为什么StretchDIBits不显示任何内容?

时间:2016-05-16 11:23:22

标签: delphi winapi

我想用两种颜色绘制一个200%拉伸的单色位图:纯黑色和纯白色。

我使用以下代码,但没有显示任何内容 如果我用SRCCOPY替换SRCPAINT,我会得到一个白色矩形,但仍然没有随机的2x2块被绘制为应该发生的。

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowCell(Form1.Canvas);  //Using another canvas does not help.
end;

procedure ShowCell(Canvas: TCanvas);
const
  cHeight = 100;
  cWidth = 50;    //50 * 8 = 400 pixels
var
  bmpinfo: PBitmapInfo;
  color: PRGBQUAD;
  i: Integer;
  x,y,h: integer;
  DataBuffer: array[0..cHeight-1,0..cWidth-1] of byte;
  ScanLineWidth: integer;
  Cell: TLifeCell;
  Coordinate: TCoordinate;
begin
  GetMem(bmpinfo, SizeOf(TBitmapInfo) + SizeOf(TRGBQUAD)*2);
  color:= @bmpinfo^.bmiColors[0];
  color^.rgbRed:= 255;
  color^.rgbBlue:= 255;
  color^.rgbGreen:= 255;
  color^.rgbReserved:= 0;
  Inc(color);
  color^.rgbRed:= 0;
  color^.rgbBlue:= 0;
  color^.rgbGreen:= 0;
  color^.rgbReserved:= 0;

  with bmpinfo.bmiHeader do begin
    biSize:= SizeOf(bmpinfo.bmiHeader);
    biWidth:= cWidth*8;   //8 pixels per byte
    biHeight:= cHeight;
    biPlanes:= 1;
    biBitCount:= 1;
    biCompression:= BI_RGB;
    biSizeImage:= 0;
    biXPelsPerMeter:= 0;
    biYPelsPerMeter:= 0;
    biClrUsed:= 0;
    biClrImportant:= 0;
  end;

  ScanlineWidth:= cWidth div 8;
  if (ScanlineWidth mod 4) <> 0 then Inc(ScanlineWidth, 4 - ScanlineWidth mod 4);

  for x:= 0 to cwidth-1 do begin
    for y:= 0 to cheight-1 do begin
      DataBuffer[x][y]:= Random(255);
    end;
  end;

    StretchDIBits(Canvas.Handle,  0, 0, cHeight*2, cWidth*2*8, 0, 0, cHeight, cWidth*8,
             @DataBuffer, bmpinfo^, DIB_RGB_COLORS, SRCCOPY);
  FreeMem(bmpinfo);
end;

我在这里做错了什么?

2 个答案:

答案 0 :(得分:1)

它对我有一些修正 - 循环中的cwidth / cheight和主要 - 交换了StretchDiBits函数的width和height参数。 GetLastError报告了错误的参数值吗? (在我的情况下 - 不是)

  for x:= 0 to cwidth-1 do begin
    for y:= 0 to cheight-1 do begin
      DataBuffer[x][y]:= Random(255);
    end;
  end;

  StretchDIBits(Canvas.Handle, 0,0,cWidth*2*8,cHeight*2,0,0,cwidth*8,cHeight,@DataBuffer,
    bmpinfo^, DIB_RGB_COLORS, SRCCOPY);

另一个可能的问题 - 您定义了cWidth(数据缓冲区宽度),与ScanlineWidth计算无关。

答案 1 :(得分:1)

有很多错误:

  • 位图声明与StretchDIBits调用不匹配。
  • Bitmap is upside down
  • 循环有x和y颠倒
  • 未检查状态代码(最后)
  • 出于性能原因,位图的宽度应为4(或8)字节的倍数

位图声明与StretchDIBits调用不匹配
问题是位图的声明必须与StretchDIBits的参数匹配。如果这些不匹配,您将收到无声错误,并且不会显示任何内容。

以下是问题专栏:

with bmpinfo.bmiHeader do begin
  biSize:= SizeOf(bmpinfo.bmiHeader);
  biWidth:= cWidth*8;   //8 pixels per byte   must match srcWidth.
  biHeight:= cHeight;   // must match srcHeight below.

StretchDIBits(Canvas.Handle,0,0,cWidth*2*8,cHeight*2  
                           ,0,0,cwidth*8,cHeight,   //srcWidth,srcHeight 
              @DataBuffer, bmpinfo^, DIB_RGB_COLORS, SRCCOPY);

如果srcWidthsrcHeight参数超出位图的尺寸,则呼叫将失败。
在问题高度和宽度反转的StretchDIBits调用中,使位图太大并强制出错,从而阻止显示。

位图颠倒
因为IBM在位图格式上有肮脏的手,逻辑会从窗口中消失,而位图的默认设置是颠倒的。

  

BITMAPINFOHEADER
   biHeight 位图的高度(以像素为单位)。如果biHeight为正,则位图是自下而上的DIB,其原点是左下角。如果biHeight是负数,则位图是自上而下的DIB,其原点是左上角。

除非您希望数据颠倒,否则最好让biHeight为负数,如下所示:

with bmpinfo.bmiHeader do begin
  biSize:= SizeOf(bmpinfo.bmiHeader);
  biWidth:= cWidth*8;   //8 pixels per byte   must match srcWidth.
  biHeight:= -cHeight;   // "-" = TopDown: must match srcHeight below.

循环x和y颠倒
在循环中,请注意缓冲区中的x和y是相反的。

  for y:= 0 to cHeight-1 do begin
    for x:= 0 to cWidth-1 do begin   //fill scanlines in the inner loop.
      DataBuffer[y][x]:= Random(256);  //y,x must be reversed!
    end; {for x}
  end; {for y}

未检查状态代码
如果我打扰检查StretchDIBits的返回值,那么我可以省去自己的麻烦。我会知道有一个错误。

  

如果函数成功,则返回值是复制的扫描行数。请注意,此值对于镜像内容可能为负。

     

如果函数失败,或者没有复制扫描行,则返回值为0.

Success:= StretchDIBits(.....
Assert(Success <> 0,'StretchDIBits error, check your arguments');  

出于性能原因,位图的宽度应为4字节的倍数
如果要使用(32位)整数写入位图缓冲区,最好确保位图宽度是4个字节的倍数,否则由于未对齐的写入而导致延迟。
如果使用64位Int64写入,请将其设为8个字节的倍数。

Windows仅强制执行2字节对齐。这是因为位图需要与16位Windows位图保持兼容。

  

bmWidthBytes 每条扫描行中的字节数。此值必须可被2整除,因为系统假定位图的位值形成一个字对齐的数组