工業儀表盤虛擬化需實現指針平滑旋轉、刻度盤縮放漫游、背景透明疊加。傳統逐像素旋轉效率低,利用GDI世界變換(World Transform)可硬件加速實現仿射變換。
GDI使用XFORM結構體表示2D仿射變換矩陣:
typedef struct tagXFORM {
FLOAT eM11; // 水平縮放/旋轉余弦
FLOAT eM12; // 水平傾斜/旋轉正弦
FLOAT eM21; // 垂直傾斜/-旋轉正弦
FLOAT eM22; // 垂直縮放/旋轉余弦
FLOAT eDx; // 水平平移
FLOAT eDy; // 垂直平移
} XFORM;
坐標變換公式:
x' = x * eM11 + y * eM21 + eDx
y' = x * eM12 + y * eM22 + eDy
TBitmap載入,設置Transparent = true、TransparentColor = clFuchsia (或Alpha通道)void DrawRotatedNeedle(TCanvas* Canvas, TBitmap* NeedleBmp,
TPoint Center, float AngleDeg, float Scale = 1.0f) {
// 1. 保存原始圖形模式
int oldMode = SetGraphicsMode(Canvas->Handle, GM_ADVANCED);
// 2. 構建變換矩陣:平移到原點 → 旋轉 → 縮放 → 平移回目標中心
XFORM xform = { 0 };
float rad = AngleDeg * M_PI / 180.0f;
float cosA = cos(rad) * Scale;
float sinA = sin(rad) * Scale;
xform.eM11 = cosA;
xform.eM12 = sinA;
xform.eM21 = -sinA;
xform.eM22 = cosA;
xform.eDx = Center.x - (NeedleBmp->Width / 2.0f) * cosA + (NeedleBmp->Height / 2.0f) * sinA;
xform.eDy = Center.y - (NeedleBmp->Width / 2.0f) * sinA - (NeedleBmp->Height / 2.0f) * cosA;
// 3. 應用變換
SetWorldTransform(Canvas->Handle, &xform);
// 4. 繪制位圖(相對原點(0,0)繪制,變換會自動映射)
Canvas->Draw(0, 0, NeedleBmp);
// 5. 恢復身份變換
XFORM identity = { 1, 0, 0, 1, 0, 0 };
SetWorldTransform(Canvas->Handle, &identity);
SetGraphicsMode(Canvas->Handle, oldMode);
}
void __fastcall TForm1::PaintBox1Paint(TObject *Sender) {
TRect rect = PaintBox1->ClientRect;
TBitmap* backBuf = new TBitmap();
backBuf->SetSize(rect.Width(), rect.Height());
backBuf->Canvas->Brush->Color = clWhite;
backBuf->Canvas->FillRect(rect);
// 繪制刻度盤背景
backBuf->Canvas->Draw(0, 0, BackgroundBmp);
// 繪制旋轉指針
float angle = CalculateAngle(currentValue); // 數值映射到角度
DrawRotatedNeedle(backBuf->Canvas, NeedleBmp, CenterPoint, angle);
// 一次性輸出到屏幕
PaintBox1->Canvas->Draw(0, 0, backBuf);
delete backBuf;
}
SetGraphicsMode(hdc, GM_ADVANCED);
SetWorldTransform(hdc, &xform);
// 啟用高質量模式 (Windows Vista+)
SetStretchBltMode(hdc, HALFTONE);
SetBrushOrgEx(hdc, 0, 0, NULL);
保存歷史幀位圖,按時間衰減Alpha值疊加繪製,模擬機械儀表慣性。
GDI坐標原點左上角,Y軸向下;數學坐標Y軸向上。旋轉角度需按順時針方向為正,或通過eM12/eM21符號調整。
class TGaugeNeedle {
TBitmap* FNeedleBmp;
TPoint FCenter;
float FMinAngle, FMaxAngle; // 量程對應角度范圍
float FZeroAngle; // 零點角度偏移
public:
void SetValue(float value, float minVal, float maxVal);
void PaintTo(TCanvas* Canvas);
// 內部處理角度插值、變換矩陣構建、雙緩衝
};