Categories
Delphi

How to split an image with Alpha into a bitmap and mask image in GDI+

The following code provides a routine for splitting out a source image with an Alpha channel into two images, the first holding the RGB information and the second as a Grayscale mask image.
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 SplitAlpha(Source: TGPBitmap; var Bitmap: TGPBitmap;
  var Mask: TGPBitmap): boolean;

  procedure SetGrayScale(palette: PColorPalette);
  var
    i: integer;
  begin
    for i := 0 to Palette.Count - 1 do
    begin
      palette.Entries[i] := MakeColor(i, i, i);
    end;
  end;
var
  SourcePixelFormat: PixelFormat;
  w, h: cardinal;
  x, y: cardinal;
  sbmd: TBitmapData;
  bbmd: TBitmapData;
  mbmd: TBitmapData;
  r: TGPRect;
  sImageStart: PByteArray;
  spixelpos
    : integer;
  sPixel: pARGB;
  bImageStart: PByteArray;
  bpixelpos: integer;
  bPixel: PRGBTriple;
  mImageStart: PByteArray;
  mpixelpos: integer;
  mPixel: pByte;
  Palette: pColorPalette;
  PaletteSize: cardinal;
begin
  Bitmap := nil;
  Mask := nil;
  if not assigned(Source) then raise EImageProcessError.create('No sourceimage assigned');
  if (source.GetWidth = 0) or (source.GetHeight = 0) then raise  EImageProcessError.create('Height and width of source image must be greater than zero');

  SourcePixelFormat := source.GetPixelFormat;
  if not IsAlphaPixelFormat(SourcePixelFormat) then raise EImageProcessError.create('Source image does not have alpha channel');

  w := Source.GetWidth;
  h := source.GetHeight;

  Bitmap := TGPBitmap.Create(w, h, PixelFormat32bppRGB);
  Mask := TGPBitmap.Create(w, h, PixelFormat8bppIndexed);

  PaletteSize := mask.GetPaletteSize;
  Palette := AllocMem(PaletteSize);

  mask.GetPalette(Palette, PaletteSize);
  SetGrayScale(Palette);
  mask.SetPalette(Palette);
  FreeMem(Palette, PaletteSize);

  r := MakeRect(0, 0, integer(w), integer(h));
  source.LockBits(r, ImageLockModeRead, PixelFormat32bppARGB, sbmd);
  bitmap.LockBits(r, ImageLockModeWrite, PixelFormat32bppRGB, bbmd);
  mask.LockBits(r, ImageLockModeWrite, PixelFormat8bppIndexed, mbmd);

  sImageStart := sbmd.Scan0;
  bImageStart := bbmd.Scan0;
  mImageStart := mbmd.Scan0;
  for y := 0 to sbmd.Height - 1 do
  begin
    for x := 0 to sbmd.Width - 1 do
    begin
      spixelpos := (y * cardinal(sbmd.Stride)) +
        (x * 4);
      sPixel := @sImageStart[sPixelPos];
      bpixelpos := (y * cardinal(bbmd.Stride))
        + (x * 4);
      bPixel := @bImageStart[bPixelPos];
      mpixelpos := (y * cardinal(mbmd.Stride))
        + (x);
      mPixel := @mImageStart[mPixelPos];
      bPixel.rgbtRed := (spixel^
        and
        $FF0000) shr 16;
      bPixel.rgbtGreen := (spixel^ and $00FF00)
        shr
        8;
      bPixel.rgbtBlue := (spixel^ and $0000FF);
      mpixel^ := (spixel^ and ALPHA_MASK)
        shr
        ALPHA_SHIFT;
    end;
  end;
  source.UnlockBits(sbmd);
  bitmap.UnlockBits(bbmd);
  mask.UnlockBits(mbmd);
  result := true;
end;