delphidelphi-xetbitmap

How do I draw with scanlines without loading an image first?


I'm trying to do the following:

bmp := TBitmap.Create;
bmp.Width := FWidth;
bmp.Height := FHeight;
for y := 0 to FHeight - 1 do
begin
   sl := bmp.ScanLine[y];
   for x := 0 to FWidth - 1 do
   begin
      //draw to the scanline, one pixel at a time
   end;
end;
//display the image
bmp.Free;

Unfortunately, what I end up with is an image that's completely white, except for the bottom line, which is colored appropriately. A bit of debugging shows that each time I access the ScanLine property, it's calling TBitmap.FreeImage, and going into the if (FHandle <> 0) and (FHandle <> FDIBHandle) then block, which resets the whole image, so only the changes to the last line actually take.

In every demo I've seen so far using TBitmap.ScanLine, they start out by loading an image. (Apparently this sets up various handles correctly so that this doesn't end up happening?) But I'm not trying to load an image and work on it; I'm trying to capture image data from a camera.

How can I set up the bitmap so that I can draw to the scanlines without having to load an image first?


Solution

  • You should set the PixelFormat explicitly before starting to draw. For instance,

    procedure TForm1.FormPaint(Sender: TObject);
    var
      bm: TBitmap;
      y: Integer;
      sl: PRGBQuad;
      x: Integer;
    begin
    
      bm := TBitmap.Create;
      try
        bm.SetSize(1024, 1024);
        bm.PixelFormat := pf32bit;
        for y := 0 to bm.Height - 1 do
        begin
          sl := bm.ScanLine[y];
          for x := 0 to bm.Width - 1 do
          begin
            sl.rgbBlue := 255 * x div bm.Width;
            sl.rgbRed := 255 * y div bm.Height;
            sl.rgbGreen := 255 * x div bm.Width;
            inc(sl);
          end;
        end;
    
        Canvas.Draw(0, 0, bm);
      finally
        bm.Free;
      end;
    
    end;