Categories
Delphi

How to combine an image and mask into an Alpha Image in GDI+

The following code provides a routine for combining an image and a mask image into
an image with Alpha information.

The routine uses the GDI+ bitmap's Lockbits method to provide fast access to the
image data.

This routine is available in the GDIPExtensions unit which is part of GDI+ Controls.

function CombineImageMask(Bitmap: TGPBitmap; Mask: TGPBitmap; var AlphaRGBImage: TGPBitmap): boolean;
var
  w, h: cardinal;
  x, y: cardinal;
  obmd: TBitmapData;
  bbmd: TBitmapData;
  mbmd: TBitmapData;
  r: TGPRect;
  oImageStart: PByteArray;
  opixelpos: integer;
  oPixel: pARGB;
  bImageStart
    : PByteArray;
  bpixelpos: integer;
  bPixel: pARGB;
  mImageStart: PByteArray;
  mpixelpos
    : integer;
  mPixel: pARGB;
  alphalevel: cardinal;
begin
  AlphaRGBImage := nil;
  if not assigned(Bitmap) then
    raise EImageProcessError.create('No Bitmap assigned');
  if
    not assigned(Mask) then
    raise EImageProcessError.create('No Mask assigned');
  if (Bitmap.GetWidth = 0) or (Bitmap.GetHeight = 0) then
    raise
      EImageProcessError.create('Height and width of image must be greater than zero');
  if (Bitmap.GetWidth <> Mask.GetWidth)
    or (Bitmap.GetHeight <> Mask.GetWidth) then
    raise
      EImageProcessError.create('The hieght and width of both the image and mask should be the same');
  w := Bitmap.GetWidth;
  h := Bitmap.GetHeight;
  AlphaRGBImage := TGPBitmap.Create(w, h, PixelFormat32bppARGB);
  r := MakeRect(0, 0, integer(w), integer(h));
  bitmap.LockBits(r, ImageLockModeRead, PixelFormat32bppRGB, bbmd);
  mask.LockBits(r, ImageLockModeRead, PixelFormat32bppRGB, mbmd);
  AlphaRGBImage.LockBits(r, ImageLockModeWrite, PixelFormat32bppARGB, obmd);
  bImageStart := bbmd.Scan0;
  mImageStart := mbmd.Scan0;
  oImageStart := obmd.Scan0;
  for y := 0 to obmd.Height - 1 do
  begin
    for x := 0 to obmd.Width - 1 do
    begin
      opixelpos := (y * cardinal(obmd.Stride)) +
        (x * 4);
      oPixel := @oImageStart[oPixelPos];
      bpixelpos := (y * cardinal(bbmd.Stride))
        + (x * 4);
      bPixel := @bImageStart[bPixelPos];
      mpixelpos := (y * cardinal(mbmd.Stride))
        + (x * 4);
      mPixel := @mImageStart[mPixelPos];
      oPixel^ := bPixel^ and not ALPHA_MASK; // copy rgb
      across alphalevel := (((mpixel^ and $FF0000) shr 16) + ((mpixel^ and
        $00FF00) shr 8) + (mpixel^ and $0000FF)) div 3;
      oPixel^ := ((alphalevel and $FF) shl ALPHA_SHIFT) or oPixel^;
    end;
  end;
  AlphaRGBImage.UnlockBits(obmd);
  bitmap.UnlockBits(bbmd);
  mask.UnlockBits(mbmd);
  result := true;
end;