Delphi 如何使用GDI通过像素强度将位图转换为灰度?

Delphi 如何使用GDI通过像素强度将位图转换为灰度?,delphi,winapi,bitmap,gdi,grayscale,Delphi,Winapi,Bitmap,Gdi,Grayscale,我正在寻找如何使用GDI(而不是GDI+)将32位位图转换为灰度的简单解决方案。是否有可能,例如通过更改位图的托盘或其他方式 当然,在Delphi中有很多类似的例子,但是我正在寻找一个WinAPI函数,它可以不经过行的迭代就完成这项工作。我还没有发现任何一个GDI函数可以完成这项工作。正如David在评论中提到的,最简单的方法是扫描每一行并计算像素颜色。你要找的可能是公式 该公式几乎没有变化,在下面的示例中,我使用了由推荐的公式,见第2.5.1节。正如我在某处发现的,这个公式甚至被著名的Adob

我正在寻找如何使用GDI(而不是GDI+)将32位位图转换为灰度的简单解决方案。是否有可能,例如通过更改位图的托盘或其他方式


当然,在Delphi中有很多类似的例子,但是我正在寻找一个WinAPI函数,它可以不经过行的迭代就完成这项工作。

我还没有发现任何一个GDI函数可以完成这项工作。正如David在评论中提到的,最简单的方法是扫描每一行并计算像素颜色。你要找的可能是公式

该公式几乎没有变化,在下面的示例中,我使用了由推荐的公式,见第2.5.1节。正如我在某处发现的,这个公式甚至被著名的Adobe Photoshop使用。以下代码示例支持并期望仅将24位像素格式位图作为输入:

procedure BitmapGrayscale(ABitmap: TBitmap);
type
  PPixelRec = ^TPixelRec;
  TPixelRec = packed record
    B: Byte;
    G: Byte;
    R: Byte;
  end;
var
  X: Integer;
  Y: Integer;
  Gray: Byte;
  Pixel: PPixelRec;
begin
  for Y := 0 to ABitmap.Height - 1 do
  begin
    Pixel := ABitmap.ScanLine[Y];
    for X := 0 to ABitmap.Width - 1 do
    begin
      Gray := Round((0.299 * Pixel.R) + (0.587 * Pixel.G) + (0.114 * Pixel.B));
      Pixel.R := Gray;
      Pixel.G := Gray;
      Pixel.B := Gray;
      Inc(Pixel);
    end;
  end;
end;

您可以创建一个调色板DIB部分,每像素8位,256色,并将调色板初始化为灰色{0,0,0},{1,1}。。。{255,255,255}

此位图中的单个GDI
BitBlt
将灰显原始图像。下面是代码段(在C++、ATL和WTL中),但是你应该知道这个想法。
CWindowDC DesktopDc(空);
CDC-BitmapDc;
atlvirify(BitmapDc.CreateCompatibleDC(DesktopDc));
CBitmap位图;
CTempBuffer-pBitmapInfo;
常量大小\u T nBitmapInfoSize=sizeof(BITMAPINFO)+256*sizeof(RGBQUAD);
pBitmapInfo.AllocateBytes(nBitmapInfoSize);
零内存(pBitmapInfo、nBitmapInfoSize);
pBitmapInfo->bmiHeader.biSize=pBitmapInfo->bmiHeader的大小;
pBitmapInfo->bmiHeader.biWidth=320;
pBitmapInfo->bmiHeader.biHeight=240;
pBitmapInfo->bmiHeader.biPlanes=1;
pBitmapInfo->bmiHeader.biBitCount=8;
pBitmapInfo->bmiHeader.biCompression=BI_RGB;
pBitmapInfo->bmiHeader.biSizeImage=240*320;
pBitmapInfo->bmiHeader.biclrued=256;
pBitmapInfo->bmiHeader.biClrImportant=256;
对于(大小\u T nIndex=0;nIndex<256;nIndex++)
{
pBitmapInfo->bmiColors[nIndex].rgbreed=(字节)nIndex;
pBitmapInfo->bmiColors[nIndex].rgbGreen=(字节)nIndex;
pBitmapInfo->bmiColors[nIndex].rgbBlue=(字节)nIndex;
}
Attach(CreateDIBSection(DesktopDc,pBitmapInfo,0,DIB_RGB_COLORS,NULL,0));
atlvirify(位图);
BitmapDc。选择位图(位图);
////////////////////////////////////////////////
//这就是它变灰的原因:
ATLVERIFY(BitBlt(BitmapDc,0,0,320,240,DesktopDc,0,0,SRCCOPY));
////////////////////////////////////////////////
ATLVERIFY(BitBlt(DesktopDc,0,240,320,240,BitmapDc,0,0,SRCCOPY));

我不知道哪一个GDI函数可以做到这一点,也不知道调色板转换,但是您提到的链接中的第三个函数可以做到这一点。我猜你害怕性能,不是吗?这不仅仅是关于性能,我正在寻找一个更简单的解决方案(可能通过转换调色板或其他方式)。32位位图不包含调色板。答案是迭代扫描线感谢TLama,似乎真的没有WinAPI函数。位图.PixelFormat:=pf24Bit;//需要进行灰度缩放GDI是否允许放弃半色调调色板中保留的系统颜色?这将生成一个完全灰度调色板,具有256个灰度,没有系统颜色。@RomanR:我不明白初始位图(要转换的位图)在这里起作用。请澄清一下好吗?@UliGerhardt:假设它被选入DC,并作为第6个参数传递给BitBlt调用。在这里,它是桌面图片,所以您不会将其视为位图。
CWindowDC DesktopDc(NULL);
CDC BitmapDc;
ATLVERIFY(BitmapDc.CreateCompatibleDC(DesktopDc));
CBitmap Bitmap;
CTempBuffer<BITMAPINFO> pBitmapInfo;
const SIZE_T nBitmapInfoSize = sizeof (BITMAPINFO) + 256 * sizeof (RGBQUAD);
pBitmapInfo.AllocateBytes(nBitmapInfoSize);
ZeroMemory(pBitmapInfo, nBitmapInfoSize);
pBitmapInfo->bmiHeader.biSize = sizeof pBitmapInfo->bmiHeader;
pBitmapInfo->bmiHeader.biWidth = 320;
pBitmapInfo->bmiHeader.biHeight = 240;
pBitmapInfo->bmiHeader.biPlanes = 1;
pBitmapInfo->bmiHeader.biBitCount = 8;
pBitmapInfo->bmiHeader.biCompression = BI_RGB;
pBitmapInfo->bmiHeader.biSizeImage = 240 * 320;
pBitmapInfo->bmiHeader.biClrUsed = 256;
pBitmapInfo->bmiHeader.biClrImportant = 256;
for(SIZE_T nIndex = 0; nIndex < 256; nIndex++)
{
    pBitmapInfo->bmiColors[nIndex].rgbRed = (BYTE) nIndex;
    pBitmapInfo->bmiColors[nIndex].rgbGreen = (BYTE) nIndex;
    pBitmapInfo->bmiColors[nIndex].rgbBlue = (BYTE) nIndex;
}
Bitmap.Attach(CreateDIBSection(DesktopDc, pBitmapInfo, 0, DIB_RGB_COLORS, NULL, 0));
ATLVERIFY(Bitmap);
BitmapDc.SelectBitmap(Bitmap);
////////////////////////////////////////////////
// This is what greys it out:
ATLVERIFY(BitBlt(BitmapDc, 0, 0, 320, 240, DesktopDc, 0, 0, SRCCOPY));
////////////////////////////////////////////////
ATLVERIFY(BitBlt(DesktopDc, 0, 240, 320, 240, BitmapDc, 0, 0, SRCCOPY));