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

圖像混合模式深度解析:正常模式完整算法與 C 實現

     編輯:關於C

引言

圖像合成是圖形處理核心操作。Photoshop 「正常」混合模式看似簡單,實則涉及 Alpha 通道、預乘、非預乘多種情況組合。本文從數學公式推導到工程落地,給出 8 種情況完整實現與優化版本。

數學公式推導

基礎公式 (無 Alpha 通道)

dstRGB = srcRGB * alpha + dstRGB * (1 - alpha)
alpha 為不透明度 [0,1]。此為特例。

通用公式 (含 Alpha 通道)

採用**預乘 Alpha (Premultiplied Alpha, PARGB)** 空間計算,避免除法與精度損失。

  1. 源像素預乘:srcRGB' = srcRGB * srcAlpha * alpha / 255
  2. 目標像素預乘:dstRGB' = dstRGB * dstAlpha / 255
  3. 混合:dstRGB'' = dstRGB' + srcRGB' - dstRGB' * srcAlpha * alpha / 255
  4. 混合後 Alpha:dstAlpha' = dstAlpha + srcAlpha * alpha - dstAlpha * srcAlpha * alpha / 255
  5. 還原非預乘:dstRGB = dstRGB'' * 255 / dstAlpha'

化簡得高效公式 (整數運算友好):


// 3-1: 目標預乘
dstRGB = dstRGB * dstAlpha / 255;
// 3-2: 混合 (核心差值公式)
dstRGB = dstRGB + (srcRGB - dstRGB) * srcAlpha * alpha / 255;
// 3-3: Alpha 混合
dstAlpha = dstAlpha + srcAlpha * alpha - dstAlpha * srcAlpha * alpha / 255;
// 3-4: 還原
dstRGB = dstRGB * 255 / dstAlpha;

當 dstAlpha = srcAlpha = 255 (不透明) 時退化為基礎公式。

8 種情況完整實現

根據 源 Alpha 有無、目標 Alpha 有無、混合比例 alpha 是否 255,共 2×2×2=8 種。代碼結構統一:遍歷像素,逐通道計算。

數據結構定義


typedef union {
    DWORD Color;
    struct { BYTE Blue, Green, Red, Alpha; };
} ARGBQuad, *PARGBQuad;

typedef struct {
    INT width, height;
    PARGBQuad dstScan0, srcScan0;
    INT dstOffset, srcOffset; // 掃描行跨度補償
} ImageCpyData, *PImageCpyData;

typedef VOID (*MixerProc)(PImageCpyData, INT);
#define PixelAlphaFlag 0x10000 // BitmapData.Reserved 高位標記 Alpha 存在

8 個混合器 (節選關鍵差異)

  • Mixer0:無源Alpha、無目標Alpha、alpha<255。簡單插值。
  • Mixer1:alpha=255 (不透明覆蓋),直接 *pd++ = *ps++ 拷貝。
  • Mixer2:無源Alpha、有目標Alpha、alpha<255。目標需預乘/還原。
  • Mixer4:有源Alpha、無目標Alpha、alpha<255。源像素按 srcAlpha * alpha 加權。
  • Mixer5:有源Alpha、無目標Alpha、alpha=255。源像素按 srcAlpha 加權。
  • Mixer6:有源Alpha、有目標Alpha、alpha<255。**最通用完整邏輯** (對應公式 3 系列)。
  • Mixer7:有源Alpha、有目標Alpha、alpha=255。完整邏輯簡化版。

混合效果圖:左目標、中源、右結果(alpha=192)

調度入口:ImageMixer


VOID ImageMixer(BitmapData *dest, CONST BitmapData *source, INT alpha)
{
    if (alpha <= 0) return;
    if (alpha > 255) alpha = 255;

    ImageCpyData data;
    // 計算寬高取較小值,處理 Stride 對齊
    data.width = min(dest->Width, source->Width);
    data.height = min(dest->Height, source->Height);
    data.dstOffset = (dest->Stride >> 2) - data.width;
    data.srcOffset = (source->Stride >> 2) - data.width;
    data.dstScan0 = (PARGBQuad)dest->Scan0;
    data.srcScan0 = (PARGBQuad)source->Scan0;

    // 索引編碼: bit0=alpha==255, bit1=destHasAlpha, bit2=srcHasAlpha
    MixerProc proc[] = {Mixer0, Mixer1, Mixer2, Mixer1, Mixer4, Mixer5, Mixer6, Mixer7};
    INT index = (alpha / 255) | ((dest->Reserved >> 16) << 1) | ((source->Reserved >> 16) << 2);
    proc[index](&data, alpha);
}

工程優化版:三路分支

實際項目中 8 路過於冗餘,保留核心三路:


VOID ImageMixer_Optimized(BitmapData *dest, CONST BitmapData *source, INT alpha)
{
    // ... 參數準備同前 ...
    if (alpha == 255 && !(source->Reserved & PixelAlphaFlag))
        Mixer1(&data, alpha);          // 不透明無源Alpha -> 直接拷貝
    else if (dest->Reserved & PixelAlphaFlag)
        Mixer6(&data, alpha);          // 目標有Alpha -> 完整邏輯
    else
        Mixer4(&data, alpha);          // 目標無Alpha、源有Alpha/半透明 -> 擴展插值
}

GDI+ 調用示例


void TestBlend()
{
    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);   // 鎖定位圖並標記 Alpha Flag
    LockBitmap(source, &src);

    ImageMixer_Optimized(&dst, &src, 192); // 75% 不透明度混合

    UnlockBitmap(source, &src);
    UnlockBitmap(dest, &dst);
    // 繪製結果...
}

總結

  1. 正常混合本質是**線性插值**,Alpha 通道引入預乘空間計算可統一邏輯。
  2. 8 種情況覆蓋所有 Alpha 組合,**Mixer6 為通解**。
  3. 工程上可根據美術資源特性 (如 UI 圖層通常目標不透明) 大幅精簡分支。
  4. 整數運算使用 +127 再 /255 實現四捨五入,提升精度。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved