將來源位圖 (x, y) 像素映射到目標位圖 (y, H-1-x) (順時針 90°) 或 (W-1-y, x) (逆時針 90°)。使用記憶體位圖緩衝區 避免重疊複製錯誤,最後一次性繪回目標。
void __fastcall RotateBitmap90CW(Graphics::TBitmap* Src, Graphics::TBitmap* Dest)
{
if (!Src || !Dest || Src->Empty) return;
int srcW = Src->Width;
int srcH = Src->Height;
// 1. 建立目標緩衝區 (寬高互換)
Graphics::TBitmap* Buffer = new Graphics::TBitmap();
try {
Buffer->Width = srcH;
Buffer->Height = srcW;
Buffer->PixelFormat = Src->PixelFormat; // 保持像素格式
// 2. 像素級複製 (順時針 90°: (x,y) -> (y, srcH-1-x))
for (int y = 0; y < srcH; ++y) {
for (int x = 0; x < srcW; ++x) {
TColor color = Src->Canvas->Pixels[x][y]; // 取得像素顏色
Buffer->Canvas->Pixels[y][srcH - 1 - x] = color; // 寫入緩衝區
}
}
// 3. 指派給目標位圖
Dest->Assign(Buffer); // 複製像素、調色盤、格式
}
__finally {
delete Buffer;
}
}
// 使用範例:旋轉 Image1 的位圖並顯示回 Image1
void __fastcall TForm1::ButtonRotateClick(TObject *Sender)
{
Graphics::TBitmap* Rotated = new Graphics::TBitmap();
try {
RotateBitmap90CW(Image1->Picture->Bitmap, Rotated);
Image1->Picture->Assign(Rotated); // 更新顯示
}
__finally {
delete Rotated;
}
}
void __fastcall RotateBitmap90CW_Fast(Graphics::TBitmap* Src, Graphics::TBitmap* Dest)
{
if (!Src || !Dest || Src->Empty) return;
int srcW = Src->Width;
int srcH = Src->Height;
Graphics::TBitmap* Buffer = new Graphics::TBitmap();
try {
Buffer->Width = srcH;
Buffer->Height = srcW;
Buffer->PixelFormat = Src->PixelFormat;
// 針對 24/32-bit 格式優化
if (Src->PixelFormat == pf24bit || Src->PixelFormat == pf32bit) {
int bytesPerPixel = (Src->PixelFormat == pf24bit) ? 3 : 4;
for (int y = 0; y < srcH; ++y) {
// 來源掃描線
BYTE* srcLine = (BYTE*)Src->ScanLine[y];
// 目標掃描線 (順時針: 目標行 = y, 目標列 = srcH-1-x)
for (int x = 0; x < srcW; ++x) {
BYTE* srcPixel = srcLine + x * bytesPerPixel;
BYTE* dstLine = (BYTE*)Buffer->ScanLine[y];
BYTE* dstPixel = dstLine + (srcH - 1 - x) * bytesPerPixel;
// 複製像素 (含 Alpha)
memcpy(dstPixel, srcPixel, bytesPerPixel);
}
}
} else {
// 其他格式退回 Pixels[][]
for (int y = 0; y < srcH; ++y)
for (int x = 0; x < srcW; ++x)
Buffer->Canvas->Pixels[y][srcH - 1 - x] = Src->Canvas->Pixels[x][y];
}
Dest->Assign(Buffer);
}
__finally {
delete Buffer;
}
}
| 轉換 | 目標座標 (x', y') | 目標尺寸 |
|---|---|---|
| 順時針 90° | (y, H-1-x) | (H, W) |
| 逆時針 90° | (W-1-y, x) | (H, W) |
| 180° | (W-1-x, H-1-y) | (W, H) |
| 水平鏡像 | (W-1-x, y) | (W, H) |
| 垂直鏡像 | (x, H-1-y) | (W, H) |
#include
#pragma comment(lib, "gdiplus.lib")
void RotateGdiplus(Gdiplus::Bitmap* src, Gdiplus::Bitmap** dst, float angle)
{
int w = src->GetWidth();
int h = src->GetHeight();
*dst = new Gdiplus::Bitmap(h, w, src->GetPixelFormat()); // 90° 交換寬高
Gdiplus::Graphics g(*dst);
g.TranslateTransform(h/2.0f, w/2.0f); // 移至中心
g.RotateTransform(angle); // 旋轉
g.TranslateTransform(-w/2.0f, -h/2.0f); // 移回
g.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
g.DrawImage(src, 0, 0, w, h);
}
PixelFormat 一致 (pf24bit/pf32bit),Assign 會自動轉換,手動 ScanLine 複製需自行處理格式。Pixels[][] 存取會捨棄 Alpha,必須用 ScanLine 複製 4 位元組。Pixels[][] 極慢 (每次存取觸發 GDI 鎖定/解鎖),>1000x1000 必須用 ScanLine 或 GDI+。W*H*4 bytes,大圖分塊處理或使用檔案映射。