程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 圖像(層)正常混合模式詳解(下)

圖像(層)正常混合模式詳解(下)

編輯:關於C語言

在http://www.BkJia.com/kf/201201/116377.html一文中開始時說過,圖像的合成操作包括圖像顯示、圖像拷貝、圖像拼接以及的圖層拼合疊加等,本文在http://www.BkJia.com/kf/201201/116377.html基礎上談談圖像拼接和圖像顯示。

    圖像拼接比較簡單,只要在圖像正常混合函數ImageMixer基礎上定位圖像混合坐標就可以了。下面是一個有圖像混合坐標的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 = (INT)data->Width - x; 
    if (width <= 0) return FALSE; 
    if (y < 0) 
    { 
        height += y; 
        y = 0; 
    } 
    if (y + height > (INT)data->Height) 
        height = (INT)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); 
    } 

//--------------------------------------------------------------------------- 
// 獲取子圖數據
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 = (INT)data->Width - x;
 if (width <= 0) return FALSE;
 if (y < 0)
 {
  height += y;
  y = 0;
 }
 if (y + height > (INT)data->Height)
  height = (INT)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);
 }
}
//---------------------------------------------------------------------------

    上面的代碼中增加了一個獲取子圖像數據結構的函數GetSubBitmapData和一個有圖像混合坐標的重載函數ImageMixer,在重載函數ImageMixer中,調用GetSubBitmapData獲取了一個按圖像混合起始坐標(x,y)計算的目標圖與源圖的交集子圖像數據結構,然後將原圖像混合到目標子圖像上。通過多次這樣的混合操作就可以實現圖像的拼接。

    下面是一個使用BCB2007和GDI+實現圖像拼接的例子程序:


void __fastcall TForm1::Button3Click(TObject *Sender) 

    BitmapData data, dst, src; 
 
    // 建立新圖像  
    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); 
 
    // 目標圖與源圖混合  
    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; 

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

 // 建立新圖像
 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);

 // 目標圖與源圖混合
 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;
}
//---------------------------------------------------------------------------

    運行效果截圖如下:

 \

 

運行效果與《圖像(層)正常混合模式詳解(上)》例子運行效果截圖是一樣的(其實就是同一張圖片),但含義卻是不一樣的:《圖像(層)正常混合模式詳解(上)》例子是分多次在窗口上顯示,而上面例子卻是將源圖和目標圖拼接(多次混合)到一張圖上,然後再顯示。

    其實,上面的ImageMixer函數沒有對源圖進行混合坐標定位,也是不太完善的,不過有了GetSubBitmapData函數,對源圖進行坐標定位是很簡單的。

    下面再應用ImageMixer函數實現圖像顯示功能,代碼如下:


VOID GetBitmapInfoHeader(CONST BitmapData *data, CONST PBITMAPINFO pbi) 

    pbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
    pbi->bmiHeader.biWidth = data->Width; 
    pbi->bmiHeader.biHeight = data->Height; 
    pbi->bmiHeader.biPlanes = 1; 
    pbi->bmiHeader.biBitCount = (data->PixelFormat >> 8) & 0xff; 
    pbi->bmiHeader.biCompression = BI_RGB; 

//---------------------------------------------------------------------------  
 
VOID GetDCImageData(HDC DC, INT x, INT y, BitmapData *data, PBITMAPINFO pbi) 

    HBITMAP bitmap = CreateCompatibleBitmap(DC, data->Width, data->Height); 
    HDC memDC = CreateCompatibleDC(DC); 
    HBITMAP saveBitmap = (HBITMAP)SelectObject(memDC, bitmap); 
    BitBlt(memDC, 0, 0, data->Width, data->Height, DC, x, y, SRCCOPY); 
    SelectObject(memDC, saveBitmap); 
    DeleteDC(memDC); 
    GetDIBits(DC, bitmap, 0, data->Height, data->Scan0, pbi, DIB_RGB_COLORS); 
    DeleteObject(bitmap); 

//---------------------------------------------------------------------------  
 
VOID BitBltImageData(HDC DC, INT x, INT y, CONST BitmapData *data, PBITMAPINFO pbi) 

    HBITMAP bitmap = CreateDIBitmap(DC, &pbi->bmiHeader, CBM_INIT, data->Scan0, pbi, DIB_RGB_COLORS); 
    HDC memDC = CreateCompatibleDC(DC); 
    HBITMAP saveBitmap = (HBITMAP)SelectObject(memDC, bitmap); 
    BitBlt(DC, x, y, data->Width, data->Height, memDC, 0, 0, SRCCOPY); 
    SelectObject(memDC, saveBitmap); 
    DeleteDC(memDC); 
    DeleteObject(bitmap); 

//---------------------------------------------------------------------------  
 
VOID ImageDraw(HDC DC, INT x, INT y, CONST BitmapData *data, float alpha = 1.0f) 

    BITMAPINFO bi; 
    RECT r; 
    INT alphaI; 
    LPVOID scan0; 
    BitmapData dst, src, tmp; 
    // 獲取DC可見矩形  
    if (GetClipBox(DC, &r) <= NULLREGION) 
        return; 
    alphaI = (INT)(alpha * 255); 
    // 如果alpha=1,同時data不含alpha信息,同時data是Windows位圖格式,  
    // data圖像數據直接傳輸到DC  
    if (alphaI >= 255 && !(data->Reserved & PixelAlphaFlag) && data->Stride < 0) 
    { 
        GetBitmapInfoHeader(data, &bi); 
        BitBltImageData(DC, x, y, data, &bi); 
        return; 
    } 
    // 調整DC可見矩形左上角坐標  
    x -= r.left; 
    y -= r.top; 
    if (x > 0) 
    { 
        r.left += x; 
        x = 0; 
    } 
    if (y > 0) 
    { 
        r.top += y; 
        y = 0; 
    } 
    // 計算data傳輸到DC的實際尺寸到圖像數據dst  
    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; 
    // 按32位像素格式分配dst掃描線內存  
    dst.Stride = dst.Width << 2; 
    dst.Scan0 = scan0 = (LPVOID)new CHAR[dst.Height * dst.Stride]; 
    // 計算data與DC的交集子圖像數據  
    if (x < 0) x = -x; 
    if (y < 0) y = -y; 
    GetSubBitmapData(data, x, y, dst.Width, dst.Height, &src); 
    GetBitmapInfoHeader(&src, &bi); 
    // 如果alpha<1,或者data含Alpha信息,獲取DC原圖形到dst  
    if (alphaI < 255 || (data->Reserved & PixelAlphaFlag)); 
        GetDCImageData(DC, r.left, r.top, &dst, &bi); 
    // dst掃描線內存轉換成Windows位圖格式  
    dst.Scan0 = (LPBYTE)scan0 + (dst.Height - 1) * dst.Stride; 
    dst.Stride = -dst.Stride; 
    // 圖像混合  
    ImageMixer(&dst, &src, alphaI); 
    // 還原dst掃描線內存格式後,傳輸到DC  
    dst.Scan0 = scan0; 
    BitBltImageData(DC, r.left, r.top, &dst, &bi); 
    delete[] scan0; 

//--------------------------------------------------------------------------- 
VOID GetBitmapInfoHeader(CONST BitmapData *data, CONST PBITMAPINFO pbi)
{
 pbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 pbi->bmiHeader.biWidth = data->Width;
 pbi->bmiHeader.biHeight = data->Height;
 pbi->bmiHeader.biPlanes = 1;
 pbi->bmiHeader.biBitCount = (data->PixelFormat >> 8) & 0xff;
 pbi->bmiHeader.biCompression = BI_RGB;
}
//---------------------------------------------------------------------------

VOID GetDCImageData(HDC DC, INT x, INT y, BitmapData *data, PBITMAPINFO pbi)
{
 HBITMAP bitmap = CreateCompatibleBitmap(DC, data->Width, data->Height);
 HDC memDC = CreateCompatibleDC(DC);
 HBITMAP saveBitmap = (HBITMAP)SelectObject(memDC, bitmap);
 BitBlt(memDC, 0, 0, data->Width, data->Height, DC, x, y, SRCCOPY);
 SelectObject(memDC, saveBitmap);
 DeleteDC(memDC);
 GetDIBits(DC, bitmap, 0, data->Height, data->Scan0, pbi, DIB_RGB_COLORS);
 DeleteObject(bitmap);
}
//---------------------------------------------------------------------------

VOID BitBltImageData(HDC DC, INT x, INT y, CONST BitmapData *data, PBITMAPINFO pbi)
{
 HBITMAP bitmap = CreateDIBitmap(DC, &pbi->bmiHeader, CBM_INIT, data->Scan0, pbi, DIB_RGB_COLORS);
 HDC memDC = CreateCompatibleDC(DC);
 HBITMAP saveBitmap = (HBITMAP)SelectObject(memDC, bitmap);
 BitBlt(DC, x, y, data->Width, data->Height, memDC, 0, 0, SRCCOPY);
 SelectObject(memDC, saveBitmap);
 DeleteDC(memDC);
 DeleteObject(bitmap);
}
//---------------------------------------------------------------------------

VOID ImageDraw(HDC DC, INT x, INT y, CONST BitmapData *data, float alpha = 1.0f)
{
 BITMAPINFO bi;
 RECT r;
 INT alphaI;
 LPVOID scan0;
 BitmapData dst, src, tmp;
 // 獲取DC可見矩形
 if (GetClipBox(DC, &r) <= NULLREGION)
  return;
 alphaI = (INT)(alpha * 255);
 // 如果alpha=1,同時data不含alpha信息,同時data是Windows位圖格式,
 // data圖像數據直接傳輸到DC
 if (alphaI >= 255 && !(data->Reserved & PixelAlphaFlag) && data->Stride < 0)
 {
  GetBitmapInfoHeader(data, &bi);
  BitBltImageData(DC, x, y, data, &bi);
  return;
 }
 // 調整DC可見矩形左上角坐標
 x -= r.left;
 y -= r.top;
 if (x > 0)
 {
  r.left += x;
  x = 0;
 }
 if (y > 0)
 {
  r.top += y;
  y = 0;
 }
 // 計算data傳輸到DC的實際尺寸到圖像數據dst
 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;
 // 按32位像素格式分配dst掃描線內存
 dst.Stride = dst.Width << 2;
 dst.Scan0 = scan0 = (LPVOID)new CHAR[dst.Height * dst.Stride];
 // 計算data與DC的交集子圖像數據
 if (x < 0) x = -x;
 if (y < 0) y = -y;
 GetSubBitmapData(data, x, y, dst.Width, dst.Height, &src);
 GetBitmapInfoHeader(&src, &bi);
 // 如果alpha<1,或者data含Alpha信息,獲取DC原圖形到dst
 if (alphaI < 255 || (data->Reserved & PixelAlphaFlag));
  GetDCImageData(DC, r.left, r.top, &dst, &bi);
 // dst掃描線內存轉換成Windows位圖格式
 dst.Scan0 = (LPBYTE)scan0 + (dst.Height - 1) * dst.Stride;
 dst.Stride = -dst.Stride;
 // 圖像混合
 ImageMixer(&dst, &src, alphaI);
 // 還原dst掃描線內存格式後,傳輸到DC
 dst.Scan0 = scan0;
 BitBltImageData(DC, r.left, r.top, &dst, &bi);
 delete[] scan0;
}
//---------------------------------------------------------------------------

    ImageDraw函數實現了直接顯示BitmapData位圖數據到設備DC。其中的幾個步驟都作了注釋,這裡不再啰嗦,至於其中調用的Windows API,也請參見Windows API大全之類的書籍。

    下面將《圖像(層)正常混合模式詳解(上)》中的例子修改一下,將其中的GDI+的Graphics對象顯示位圖,改為上面的ImageDraw函數直接顯示位圖數據結構:


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; 

//--------------------------------------------------------------------------- 
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;
}
//---------------------------------------------------------------------------

    顯示效果同http://www.BkJia.com/kf/201201/116377.html中的例子運行效果,其截圖可參見上面的貼圖。顯示速度看起來也不會比GDI+的Graphics對象慢(沒測試),但如果將http://www.BkJia.com/kf/201201/116377.html中的幾個混合子函數進行一些優化,其顯示速度肯定超過會GDI+的Graphics對象。

   水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:[email protected]

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved