首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

圖像層正常混合模式詳解(下):圖像拼接與直接繪制到 DC 的實現

     編輯:關於C語言

承接上篇,本文探討基於 ImageMixer 實現圖像拼接(多圖合成到一張畫布)和直接將位圖數據繪制到設備上下文 (DC)。

1. 圖像拼接:帶座標的 ImageMixer 重載

核心思路:根據混合起始座標 (x, y) 計算目標圖與源圖的交集區域,提取子圖數據結構後調用原 ImageMixer。

獲取子圖數據結構

BOOL GetSubBitmapData(CONST BitmapData *data, INT x, INT y, INT width, INT height, BitmapData *sub) {
    if (x < 0) { width += x; x = 0; }
    if (x + width > (INT)data->Width) width = data->Width - x;
    if (width <= 0) return FALSE;
    if (y < 0) { height += y; y = 0; }
    if (y + height > (INT)data->Height) height = data->Height - y;
    if (height <= 0) return FALSE;

    sub->Width = width;
    sub->Height = height;
    sub->Stride = data->Stride;
    sub->PixelFormat = data->PixelFormat;
    sub->Scan0 = (CHAR*)data->Scan0 + y * data->Stride + (x << 2);
    sub->Reserved = data->Reserved;
    return TRUE;
}

帶座標的混合入口

VOID ImageMixer(BitmapData *dest, INT x, INT y, CONST BitmapData *source, INT alpha) {
    BitmapData dst, src;
    if (GetSubBitmapData(dest, x, y, source->Width, source->Height, &dst)) {
        GetSubBitmapData(source, x < 0 ? -x : 0, y < 0 ? -y : 0, dst.Width, dst.Height, &src);
        ImageMixer(&dst, &src, alpha);
    }
}

圖像拼接完整示例 (BCB2007 + GDI+)

void __fastcall TForm1::Button3Click(TObject *Sender) {
    BitmapData data, dst, src;

    // 建立新畫布 768x256
    Gdiplus::Bitmap *newBmp = new Gdiplus::Bitmap(768, 256, PixelFormat32bppARGB);
    LockBitmap(newBmp, &data);

    // 目標圖放左側
    Gdiplus::Bitmap *dest = new Gdiplus::Bitmap(L"d:\\xmas_011.png");
    LockBitmap(dest, &dst);
    ImageMixer(&data, 0, 0, &dst, 255);

    // 源圖放中間
    Gdiplus::Bitmap *source = new Gdiplus::Bitmap(L"d:\\Apple.png");
    LockBitmap(source, &src);
    ImageMixer(&data, dest->GetWidth(), 0, &src, 255);

    // 目標與源混合 (不透明度 192)
    ImageMixer(&dst, &src, 192);

    // 混合結果放右側
    ImageMixer(&data, dest->GetWidth() << 1, 0, &dst, 255);

    UnlockBitmap(source, &src);
    UnlockBitmap(dest, &dst);
    UnlockBitmap(newBmp, &data);

    // 顯示拼接結果
    Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
    g->DrawImage(newBmp, 0, 0);

    delete g; delete source; delete dest; delete newBmp;
}

拼接效果圖

運行效果與上篇相同,但本質不同:上篇分多次繪制到窗口,本篇將多圖拼接到一張位圖後一次性顯示。

2. 直接繪制到 DC:ImageDraw 函數

實現將 BitmapData 直接繪制到 Windows HDC,支持 Alpha 混合、裁剪、格式轉換。

輔助函數

  • GetBitmapInfoHeader:填充 BITMAPINFOHEADER。
  • GetDCImageData:從 DC 讀取像素到 BitmapData(用於混合時獲取背景)。
  • BitBltImageData:將 BitmapData 寫入 DC。

核心 ImageDraw 實現

VOID ImageDraw(HDC DC, INT x, INT y, CONST BitmapData *data, float alpha = 1.0f) {
    BITMAPINFO bi;
    RECT r;
    INT alphaI = (INT)(alpha * 255);
    LPVOID scan0;
    BitmapData dst, src, tmp;

    // 獲取 DC 可見區域
    if (GetClipBox(DC, &r) <= NULLREGION) return;

    // 快速路徑:不透明、無 Alpha、Windows 位圖格式 (負 Stride) 直接 BitBlt
    if (alphaI >= 255 && !(data->Reserved & PixelAlphaFlag) && data->Stride < 0) {
        GetBitmapInfoHeader(data, &bi);
        BitBltImageData(DC, x, y, data, &bi);
        return;
    }

    // 調整座標到可見區域
    x -= r.left; y -= r.top;
    if (x > 0) { r.left += x; x = 0; }
    if (y > 0) { r.top += y; y = 0; }

    // 計算實際繪制區域
    tmp.Width = r.right - r.left;
    tmp.Height = r.bottom - r.top;
    tmp.Reserved = 0;
    if (!GetSubBitmapData(&tmp, x, y, data->Width, data->Height, &dst)) return;

    // 分配 32bpp 緩衝區 (正向 Stride)
    dst.Stride = dst.Width << 2;
    dst.Scan0 = scan0 = new CHAR[dst.Height * dst.Stride];

    // 計算源圖交集區域
    if (x < 0) x = -x;
    if (y < 0) y = -y;
    GetSubBitmapData(data, x, y, dst.Width, dst.Height, &src);
    GetBitmapInfoHeader(&src, &bi);

    // 需要混合時獲取 DC 背景
    if (alphaI < 255 || (data->Reserved & PixelAlphaFlag))
        GetDCImageData(DC, r.left, r.top, &dst, &bi);

    // 轉換為 Windows 位圖格式 (負 Stride, 底部優先)
    dst.Scan0 = (LPBYTE)scan0 + (dst.Height - 1) * dst.Stride;
    dst.Stride = -dst.Stride;

    // 執行混合
    ImageMixer(&dst, &src, alphaI);

    // 還原緩衝區格式並寫入 DC
    dst.Scan0 = scan0;
    BitBltImageData(DC, r.left, r.top, &dst, &bi);
    delete[] scan0;
}

調用示例:替換 GDI+ Graphics 繪制

void __fastcall TForm1::Button4Click(TObject *Sender) {
    Gdiplus::Bitmap *dest = new Gdiplus::Bitmap(L"d:\\xmas_011.png");
    Gdiplus::Bitmap *source = new Gdiplus::Bitmap(L"d:\\Apple.png");

    BitmapData dst, src;
    LockBitmap(dest, &dst);
    LockBitmap(source, &src);

    ImageDraw(Canvas->Handle, 0, 0, &dst);
    ImageDraw(Canvas->Handle, dst.Width, 0, &src);
    ImageMixer(&dst, &src, 192);
    ImageDraw(Canvas->Handle, dst.Width + src.Width, 0, &dst);

    UnlockBitmap(source, &src);
    UnlockBitmap(dest, &dst);
    delete source; delete dest;
}

顯示效果與 GDI+ Graphics::DrawImage 一致,經優化後速度可超越 GDI+。

摘自 閒人阿發伯的業余編程心得

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved