ImageEn for Delphi and C++ Builder ImageEn for Delphi and C++ Builder

 

ImageEn Forum
Profile    Join    Active Topics    Forum FAQ    Search this forumSearch
Forum membership is Free!  Click Join to sign-up
Username:
Password:
Save Password
Forgot your Password?

 All Forums
 ImageEn Library for Delphi, C++ and .Net
 ImageEn and IEvolution Support Forum
 TIEBitmap.IECanvas.Brush.SetGradient
 New Topic  Reply to Topic
Author Previous Topic Topic Next Topic  

Fellafoo

USA
60 Posts

Posted - Mar 24 2025 :  14:06:00  Show Profile  Reply
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

xequte

38899 Posts

Posted - Mar 24 2025 :  19:25:41  Show Profile  Reply
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
Go to Top of Page

Fellafoo

USA
60 Posts

Posted - Mar 25 2025 :  09:27:25  Show Profile  Reply
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;


Go to Top of Page

xequte

38899 Posts

Posted - Mar 25 2025 :  20:34:02  Show Profile  Reply
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

Go to Top of Page

Fellafoo

USA
60 Posts

Posted - Mar 26 2025 :  10:41:06  Show Profile  Reply
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 }
Go to Top of Page

Fellafoo

USA
60 Posts

Posted - Mar 26 2025 :  11:52:06  Show Profile  Reply
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
Go to Top of Page

xequte

38899 Posts

Posted - Mar 26 2025 :  23:49:57  Show Profile  Reply


Nigel
Xequte Software
www.imageen.com
Go to Top of Page
  Previous Topic Topic Next Topic  
 New Topic  Reply to Topic
Jump To: