4
votes

Question

What's the best way to draw a bitmap with a per-pixel alpha onto a control's Canvas?

My bitmap data is stored in a 2d array of 32-bit pixel values.

T32BitPixel = packed record
    Blue  : byte;
    Green : byte;
    Red   : byte;
    Alpha : byte;
end;

My control is a descendent of TCustomTransparentControl.

Background

For a GUI I'm building I need to draw semi-transparent controls over other controls and textured backgrounds. The control graphics are created using AggPasMod (a port of Anti-Grain Geometry).

TCustomTransparentControl.Canvas.Handle provides access to the device context for drawing but I'm not sure how to blit the pixel data from there.

1
Are your pixels stored really that way or it's just a typo ? Usually the pixels of 32-bit bitmaps are stored as B,G,R,A not R,G,B,A.TLama
Re (Edit): First use something a bit more different, 220 is close to 255 for a visual test, use something like, f.i., 128. TLama's answer assumes that you've properly setup the alpha channel. You have not. After you setup pixel data, set AlphaFormat to afPremultiplied to let VCL properly calculate R,G,B from the alpha channel if you've got a recent Delphi version. If not, see AC_SRC_ALPHA in BLENDFUNCTIONs documentation for how it is done.Sertac Akyuz
@Shannon - Read my comment again, set afPremultiplied after you've setup pixel data. Move the Bitmap.AlphaFormat := afPreMultiplied; line just before BlendFunction.BlendOp :=.. line.Sertac Akyuz
@SertacAkyuz: oops!! Sorry I missed an important detail. Setting 'afPremultiplied' indeed does the trick!! Thanks for your help!Shannon Matthews
@Shannon - You're welcome! Consider rolling back the question since the 'edit'ed part addresses a different issue than the originally asked question.Sertac Akyuz

1 Answers

7
votes

Assuming, you have your pixel array composed like the image rows and pixels in them, I would do it this way. The Canvas parameter is a target canvas, the X and Y are coordinates, where the bitmap will be rendered in the target canvas and the Pixels is the pixel array:

type
  TPixel = packed record
    B: Byte;
    G: Byte;
    R: Byte;
    A: Byte;
  end;
  TPixelArray = array of array of TPixel;

procedure RenderBitmap(Canvas: TCanvas; X, Y: Integer; Pixels: TPixelArray);
var
  I: Integer;
  Size: Integer;
  Bitmap: TBitmap;
  BlendFunction: TBlendFunction;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.Width := Length(Pixels[0]);
    Bitmap.Height := Length(Pixels);
    Size := Bitmap.Width * SizeOf(TPixel);

    for I := 0 to Bitmap.Height - 1 do
      Move(Pixels[I][0], Bitmap.ScanLine[I]^, Size);

    BlendFunction.BlendOp := AC_SRC_OVER;
    BlendFunction.BlendFlags := 0;
    BlendFunction.SourceConstantAlpha := 255;
    BlendFunction.AlphaFormat := AC_SRC_ALPHA;
    AlphaBlend(Canvas.Handle, X, Y, Bitmap.Width, Bitmap.Height,
      Bitmap.Canvas.Handle, 0, 0, Bitmap.Width, Bitmap.Height, BlendFunction);
  finally
    Bitmap.Free;
  end;
end;