Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to color blend (colorize by specified alpha value) the canvas area using pure GDI?

I'd like to color blend (colorize by specified alpha value) the area of a canvas using pure Windows GDI (so without GDI+, DirectX or similar, no OpenGL, no assembler or a 3rd party libraries).

I've created the following function and I'd like to know if there is a more efficient or easier way to do this:

procedure ColorBlend(const ACanvas: HDC; const ARect: TRect;
  const ABlendColor: TColor; const ABlendValue: Integer);
var
  DC: HDC;
  Brush: HBRUSH;
  Bitmap: HBITMAP;
  BlendFunction: TBlendFunction;
begin
  DC := CreateCompatibleDC(ACanvas);
  Bitmap := CreateCompatibleBitmap(ACanvas, ARect.Right - ARect.Left,
    ARect.Bottom - ARect.Top);
  Brush := CreateSolidBrush(ColorToRGB(ABlendColor));
  try
    SelectObject(DC, Bitmap);
    Windows.FillRect(DC, Rect(0, 0, ARect.Right - ARect.Left,
      ARect.Bottom - ARect.Top), Brush);
    BlendFunction.BlendOp := AC_SRC_OVER;
    BlendFunction.BlendFlags := 0;
    BlendFunction.AlphaFormat := 0;
    BlendFunction.SourceConstantAlpha := ABlendValue;
    Windows.AlphaBlend(ACanvas, ARect.Left, ARect.Top,
      ARect.Right - ARect.Left, ARect.Bottom - ARect.Top, DC, 0, 0,
      ARect.Right - ARect.Left, ARect.Bottom - ARect.Top, BlendFunction);
  finally
    DeleteObject(Brush);
    DeleteObject(Bitmap);
    DeleteDC(DC);
  end;
end;

For the notion of what this function should do see the following (discriminating :-) images:

enter image description hereenter image description here

And the code that can render this image to the top left side of the form in the way shown above:

uses
  PNGImage;

procedure TForm1.Button1Click(Sender: TObject);
var
  Image: TPNGImage;
begin
  Image := TPNGImage.Create;
  try
    Image.LoadFromFile('d:\6G3Eg.png');
    ColorBlend(Image.Canvas.Handle, Image.Canvas.ClipRect, $0000FF80, 175);
    Canvas.Draw(0, 0, Image);
  finally
    Image.Free;
  end;
end;

Is there a more efficient way to do this using pure GDI or Delphi VCL ?

like image 902
TLama Avatar asked May 10 '12 20:05

TLama


1 Answers

Have you tried the Canvas Drawing with AlphaBlend?

something like

Canvas.Draw(Arect.Left, ARect.Top, ABitmap, AAlphaBlendValue);

combined with a FillRect for the blend color

Update: And here's some code, as close as possible to your interface, but pure VCL.
Might not be as efficient, but much simpler (and somewhat portable).
As Remy said, to paint on a Form in a pseudo persistent way, you'd have to use OnPaint...

procedure ColorBlend(const ACanvas: TCanvas; const ARect: TRect;
  const ABlendColor: TColor; const ABlendValue: Integer);
var
  bmp: TBitmap;
begin
  bmp := TBitmap.Create;
  try
    bmp.Canvas.Brush.Color := ABlendColor;
    bmp.Width := ARect.Right - ARect.Left;
    bmp.Height := ARect.Bottom - ARect.Top;
    bmp.Canvas.FillRect(Rect(0,0,bmp.Width, bmp.Height));
    ACanvas.Draw(ARect.Left, ARect.Top, bmp, ABlendValue);
  finally
    bmp.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Image: TPNGImage;
begin
  Image := TPNGImage.Create;
  try
    Image.LoadFromFile('d:\6G3Eg.png');
    ColorBlend(Image.Canvas, Image.Canvas.ClipRect, $0000FF80, 175);
    Canvas.Draw(0, 0, Image);
    // then for fun do it to the Form itself
    ColorBlend(Canvas, ClientRect, clYellow, 15);
  finally
    Image.Free;
  end;
end;
like image 85
Francesca Avatar answered Nov 02 '22 17:11

Francesca