T O P I C R E V I E W |
Fellafoo |
Posted - Mar 24 2025 : 14:06:00 I'm trying to prototype drawing a gradient fill to a temporary bitmap which I can then render to a canvas using RenderToCanvasWithAlpha. For example...
ieBmp := TIEBitmap.Create(320, 240, clBlack, 255);
//ieBmp.AlphaChannel.Fill(clBlack); { <-- Necessary? }
try
with ieBmp.IECanvas do begin
SmoothingMode := iesmBestPerformance;
Brush.Color := clPurple;
Brush.Transparency := 255;
Brush.BackColor := clWhite;
Brush.BackTransparency := 25;
Brush.FillMode := iefmFillModeAlternate;
Brush.SetGradient(gpgTopLeft, ieBmp.Width, ieBmp.Height);
Rectangle(GDICanvas.ClipRect);
ieBmp.AlphaChannel.Assign(ieBmp);
ieBmp.SyncAlphaChannel();
ieBmp.RenderToCanvasWithAlpha(
Cnv,
Rect.Left, Rect.Bottom, ieBmp.Width, ieBmp.Height,
0, 0, ieBmp.Width, ieBmp.Height,
255, rfFastLinear, ielNormal, cOne
);
end;
finally
ieBmp.Free;
end;
I'm not sure how to handle the alpha channel. If I don't assign it, the result is not transparent.

If I do assign it, the result is blended but not what I'd expect (i.e. washed out).

Thank you for any insight you can provide.
MFM |
6 L A T E S T R E P L I E S (Newest First) |
xequte |
Posted - Mar 26 2025 : 23:49:57 
Nigel Xequte Software www.imageen.com
|
Fellafoo |
Posted - Mar 26 2025 : 11:52:06 I haven't compared the performance between methods, but the following also works by copying the current canvas, rendering the gradient over it, then copying it back to the canvas.
procedure GradientOverBitmap(var ieBMP: TIEBitmap; Dir: TIEGDIPlusGradient; ClrBeg, ClrEnd: TColor; OpaBeg, OpaEnd: Byte);
begin
{ Draw gradient over bitmap }
with ieBmp.IECanvas do begin
Pen.Style := psClear; { No outline }
//Pen.Mode := pmNop;
Brush.Color := ClrBeg;
Brush.Transparency := OpaBeg;
Brush.BackColor := ClrEnd;
Brush.BackTransparency := OpaEnd;
Brush.SetGradient(Dir, ieBmp.Width, ieBmp.Height);
Rectangle(GDICanvas.ClipRect);
end;
end; { GradientOverBitmap }
{ Create temporary bitmap of clipping region
Note: Canvas origin is in lower-left corner }
ieBmp := TIEBitmap.Create(ClipSz.cx, ClipSz.cy, ie24RGB);
try
{ Copy region from the HDC to temporary bitmap }
BitBlt(ieBmp.VclBitmap.Canvas.Handle, 0, 0, ClipSz.cx, ClipSz.cy, aDC, ClipBox.Left, ClipBox.Bottom, SRCCOPY);
{ Draw gradient fill over copy of HDC }
GradientOverBitmap(ieBmp, gpgTopLeft, clPurple, clWhite, 255, 25);
{ Copy modified area back to HDC }
BitBlt(aDC, ClipBox.Left, ClipBox.Bottom, ClipSz.cx, ClipSz.cy, ieBmp.VclBitmap.Canvas.Handle, 0, 0, SRCCOPY);
finally
ieBmp.Free;
end;
MFM |
Fellafoo |
Posted - Mar 26 2025 : 10:41:06 Thank you for your help on this Nigel. I was able to boil the logic down to the following function.
function GradientBitmap(Dir: TIEGDIPlusGradient; Width, Height: Integer; ClrBeg, ClrEnd: TColor; OpaBeg, OpaEnd: Byte): TIEBitmap;
var
r: TRect;
begin
Result := nil;
Result := TIEBitmap.Create(Width, Height, clWhite); { No alpha yet, use 24 bit }
try
r := Result.IECanvas.GDICanvas.ClipRect;
InflateRect(r, 1, 1); { Fill may smooth edges }
{ Create color gradient }
Result.IECanvas.GradientFillRect(r, ClrBeg, ClrEnd, Dir);
{ Create and sync alpha gradient if necessary }
if ((OpaBeg <> 255) and (OpaEnd <> 255)) then begin
Result.AlphaChannel.PixelFormat := ie24RGB; { Gradient works best w/ 24 bit }
Result.AlphaChannel.IECanvas.GradientFillRect(r, RGB(OpaBeg, OpaBeg, OpaBeg), RGB(OpaEnd, OpaEnd, OpaEnd), Dir);
Result.SyncAlphaChannel();{ Convert to 8 bit }
end;
except
Result.Free;
end;
end; { GradientBitmap } |
xequte |
Posted - Mar 25 2025 : 20:34:02 There are a few errors in your code, which i have noted below:
var
ieBmp, ieBmpAlpha: TIEBitmap;
begin
ieBmp := TIEBitmap.Create(320, 240, ie24RGB ); // No alpha yet, so use 24bit
ieBmpAlpha := TIEBitmap.Create(320, 240, ie24RGB); // Gradient works best with 24bit (we'll convert it to 8bit gray scale later)
try
with ieBmp.IECanvas do begin
Brush.Color := clPurple;
Brush.BackColor := clWhite;
Brush.SetGradient(gpgTopLeft, GDICanvas.ClipRect);
Rectangle(GDICanvas.ClipRect);
end;
with ieBmpAlpha.IECanvas do begin
Brush.Color := clWhite; // Gradient should be white to black
Brush.BackColor := clBlack;
Brush.SetGradient(gpgTopLeft, GDICanvas.ClipRect);
Rectangle(GDICanvas.ClipRect);
end;
ieBmp.AlphaChannel.Assign(ieBmpAlpha);
ieBmp.SyncAlphaChannel(); // Now convert alpha to ie8g
ieBmp.RenderToCanvasWithAlpha(
cvs,
0, 0, ieBmp.Width, ieBmp.Height,
0, 0, ieBmp.Width, ieBmp.Height,
255, rfFastLinear, ielNormal );
finally
ieBmp.Free;
ieBmpAlpha.Free;
end;
end;
But it is still quite pale if you look at the alpha you see why:

But remember that the foreground does not need to be a gradient, we can just fill it with purple:
var
ieBmp, ieBmpAlpha: TIEBitmap;
begin
ImageEnView1.BackgroundStyle := iebsChessboard;
ImageEnView1.IEBitmap.Allocate(320, 240, ie24RGB);
ieBmp := TIEBitmap.Create( 320, 240, clWhite ); // No alpha yet, so use 24bit
ieBmpAlpha := TIEBitmap.Create( 320, 240, clWhite ); // Gradient works best with 24bit (we'll convert it to 8bit gray scale later)
try
// Fill image with purple
with ieBmp.IECanvas do
begin
Brush.Color := clPurple;
FillRect(GDICanvas.ClipRect);
end;
// Draw gradient to our alpha channnel
with ieBmpAlpha.IECanvas do
GradientFillRect( GDICanvas.ClipRect, clWhite, clBlack, gpgTopLeft );
ieBmp.AlphaChannel.Assign(ieBmpAlpha);
ieBmp.SyncAlphaChannel(); // Now convert alpha to ie8g
ieBmp.RenderToCanvasWithAlpha(
cvs,
0, 0, ieBmp.Width, ieBmp.Height,
0, 0, ieBmp.Width, ieBmp.Height,
255, rfFastLinear, ielNormal );
finally
ieBmp.Free;
ieBmpAlpha.Free;
end;

Which you can simplify by drawing directly to the alpha channel:
var
ieBmp: TIEBitmap;
r: TRect;
begin
ieBmp := TIEBitmap.Create(320, 240, clWhite );
try
r := ieBmp.IECanvas.GDICanvas.ClipRect;
InflateRect( r, 1, 1 ); // Because Fill may smooth edges
// Fill image with purple
ieBmp.IECanvas.Brush.Color := clPurple;
ieBmp.IECanvas.FillRect( r );
// Draw gradient to our alpha channnel
ieBmp.AlphaChannel.PixelFormat := ie24RGB; // Temporary promote the alpha channel to 24bit to get smoother gradient
ieBmp.AlphaChannel.IECanvas.GradientFillRect( r, clWhite, clBlack, gpgTopLeft );
ieBmp.SyncAlphaChannel(); // Now revert alpha to ie8g
ieBmp.RenderToCanvasWithAlpha(
cvs,
0, 0, ieBmp.Width, ieBmp.Height,
0, 0, ieBmp.Width, ieBmp.Height,
255, rfFastLinear, ielNormal );
finally
ieBmp.Free;
end;
end;
However you don't get much purple because of the gradient you have selected, here is gpgDiagonal:

Nigel Xequte Software www.imageen.com
|
Fellafoo |
Posted - Mar 25 2025 : 09:27:25 If I use GradientFill and AlphaFill, the results are what I'm looking for, but this method is limited to horizontal and vertical gradients.
ieBmp := TIEBitmap.Create(320, 240, clBlack, 255);
try
ieBmp.GradientFill(clPurple, clWhite, gdVertical);
ieBmp.AlphaFill(255, 25, gdVertical);
ieBmp.RenderToCanvasWithAlpha(
Cnv,
Rect.Left, Rect.Bottom, ieBmp.Width, ieBmp.Height,
0, 0, ieBmp.Width, ieBmp.Height,
255, rfFastLinear, ielNormal, cOne
);
finally
ieBmp.Free;
end;

I tried the following, but the results are similar to what I was getting previously.
ieBmp := TIEBitmap.Create(320, 240, ie32RGB);
ieBmpAlpha := TIEBitmap.Create(320, 240, ieg8);
try
with ieBmp.IECanvas do begin
Brush.Color := clPurple;
Brush.BackColor := clWhite;
Brush.SetGradient(gpgTopLeft, GDICanvas.ClipRect);
Rectangle(GDICanvas.ClipRect);
end;
with ieBmpAlpha.IECanvas do begin
Brush.Color := 255;
Brush.BackColor := 25;
Brush.SetGradient(gpgTopLeft, GDICanvas.ClipRect);
Rectangle(GDICanvas.ClipRect);
end;
ieBmp.AlphaChannel.Assign(ieBmpAlpha);
ieBmp.RenderToCanvasWithAlpha(
Cnv,
Rect.Left, Rect.Bottom, ieBmp.Width, ieBmp.Height,
0, 0, ieBmp.Width, ieBmp.Height,
255, rfFastLinear, ielNormal, cOne
);
finally
ieBmp.Free;
ieBmpAlpha.Free;
end;
 |
xequte |
Posted - Mar 24 2025 : 19:25:41 Hi
Don't forget that the alpha channel needs to be in the range from black (0=transparent) to white (255=opaque).
You are assigning from the the purple color to the alpha channel (which will be converted to a gray color by SyncAlphaChannel) so you never use a truly opaque (i.e. white) for your content.
You would be best to draw a second gradient to the alpha channel in the range black to white.
Take a look at the visualizer when debugging the object, and select the Alpha channel tab to see what i mean:
http://www.imageen.com/help/Debugging_Visualizers.html

Nigel Xequte Software www.imageen.com
|
|
|