Jump to content
vshvetsov

How to draw a semitransparent ellipse on a canvas?

Recommended Posts

Hi,

I have this code for drawing an ellipse on a canvas:

Canvas->Pen->Color = my_Color;

Canvas->Brush->Color = my_Color;
Canvas->Ellipse(my_rect);

Ellipse is drawn over a background image. How can I draw it semitransparently?

Sorry for posting such a question that seems to be explained in the available documentation. 
Spent several hours reading and trying, didn't succeed. Please help.
Edited by vshvetsov

Share this post


Link to post
45 minutes ago, vshvetsov said:

Ellipse is drawn over a background image. How can I draw it semitransparently?

You can't, well not directly anyway.  You can draw it opaquely onto an in-memory bitmap first, and then draw that bitmap onto your target Canvas with alpha/transparency applied to it as needed.

Share this post


Link to post

Thank you. I tried to do it, but I didn't succeed. I created a bitmap:

 

bitmap = new Graphics::TBitmap(w, h);

 

and drew a circle in it. Then I drew it in TCanvas:

 

Canvas->Draw(x, y, bitmap, opacity);

 

The problem is that the whole rectangle is drawn as translucent, not just the ellipse. 
To remove the margins around the ellipse, I set: 

bitmap->Transparent = true; 
bitmap->TransparentColor = clWhite; // the color of margins, can also be determined automaticaly 
                                    // as a color of the left-most pixel

Then the ellipse is drawn without margins, as needed, but it is not translucent any more: 
Canvas->Draw() ignores the opacity value.
I don't understand how to get a translucent ellipse without margins...

 

Share this post


Link to post
1 hour ago, vshvetsov said:

Then I drew it in TCanvas:

 

Canvas->Draw(x, y, bitmap, opacity);

I was thinking more along the lines of creating a 32bit Bitmap with an alpha channel and then using the Win32 AlphaBlend() API to draw the bitmap onto the Canvas (I forgot that TCanvas.Draw() has an opacity parameter).

Edited by Remy Lebeau

Share this post


Link to post

I've attached a pretty old unit with numerous functions for managing semi-transparent VCL.Graphics.TBitmap controls.

However, unless your graphics needs are minimal, I strongly recommend you consider using an open source graphics library.

Two graphics libraries for Delphi I can recommend:

Graphics32: Multiple developers contributed over the last 20yrs and is widely used.

Image32: Recently developed by me. IMHO it's a lot simpler than Graphics32 while almost as fully featured.

 

 

AlphaBitmaps.pas

  • Like 2

Share this post


Link to post
7 hours ago, angusj said:

ITwo graphics libraries for Delphi I can recommend:

Graphics32: Multiple developers contributed over the last 20yrs and is widely used.

Image32: Recently developed by me. IMHO it's a lot simpler than Graphics32 while almost as fully featured.

Thanks for the tips.
1) I am programming in C++. The VCL provides C++ header files for using classes and functions. 
How will I use the Image32 (or Graphics32) library?
2) Can I use Image32 in a 64-bit Windows application?
Edited by vshvetsov

Share this post


Link to post
14 hours ago, vshvetsov said:

1) I am programming in C++. The VCL provides C++ header files for using classes and functions. 
How will I use the Image32 (or Graphics32) library?

While I can't guarantee it, this is likely to help:

http://www.angusj.com/delphi/image32/Videos/cpp.mkv

 

14 hours ago, vshvetsov said:

2) Can I use Image32 in a 64-bit Windows application?

Absolutely!

Edit: The 32 in the library name simply refers to image bits per pixel WRT how images are stored in memory. (And yes the library can read and write images to file using different bpp.)

Edited by angusj

Share this post


Link to post

 

On 3/1/2023 at 5:07 AM, angusj said:

While I can't guarantee it, this is likely to help:

http://www.angusj.com/delphi/image32/Videos/cpp.mkv

 

Absolutely!

Edit: The 32 in the library name simply refers to image bits per pixel WRT how images are stored in memory. (And yes the library can read and write images to file using different bpp.)

Hi, Angus! Thank you for your answers.

Before using libraries I tried to do smth manually. I would be grateful, if you could explain me some things. 

I draw a rather colorful image on the form. In fact, this is a map of the city, on top of which I draw lines, polygons, etc. I want to highlight the circle on the map, giving it some shade, for example, blue.

I created a memory bitmap, set PixelFormat = pf32bit and AlphaFormat = afDefined, filled it with clBlack and drew a circle with clBlue brush. Then I manually (using ScanLine property) set the reserved byte of black pixels to 0, and of blue pixels - to some alpha-value. Then I drew this bitmap on a form using AlphaBlend() API function.

The result is close to expected, but there are oddities. The circle does not become completely transparent with alpha=0. The result is also highly dependent on the color of the circle. For example, if we take gray instead of blue, the circle is almost white, opaque, even with alpha=0. I fill the bitmap with black around the circle because that's the only color that becomes fully transparent at alpha=0. Why is this happening?

 

 

Share this post


Link to post
6 hours ago, vshvetsov said:

Why is this happening?

I strongly suspect that this is because you haven't pre-multiplied the image before using AlphaBlend.

 

Quote

Note that the APIs use premultiplied alpha, which means that the red, green and blue channel values in the bitmap must be premultiplied with the alpha channel value. For example, if the alpha channel value is x, the red, green and blue channels must be multiplied by x and divided by 0xff prior to the call.

https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-blendfunction

 

Here's a (rather poor) example of using AlphaBlend where the image is premultiplied.

There's also a PremultiplyAlpha function in my AlphaBitmaps.pas unit.

Edited by angusj

Share this post


Link to post
13 hours ago, angusj said:

I strongly suspect that this is because you haven't pre-multiplied the image before using AlphaBlend.

Exactly. After pre-multiplication everything was fixed, thank you very much.

A new question has arisen. It turns out that in the structure TRGBQuad the order of colors is reversed in comparison to the VCL type TColor. With a simple reinterpretation of TColor as TRGBQuad, blue becomes red, and vice versa. TColor is widely used in a VCL-application. As I understand it, your TColorEntry type in AlphaBitmaps.pas is compatible with TRGBQuad, that is, the same problem should arise with TColor. I rearrange the bytes manually. Is there any standard conversion function?

Share this post


Link to post

You can easily do it with Direct2D

 

// ...

uses Winapi.D2D1, Vcl.Direct2D;

// ...

procedure TForm1.Button3Click(Sender: TObject);
begin
  if (TDirect2DCanvas.Supported) then
  begin
    var D2DCanvas := TDirect2DCanvas.Create(Canvas, ClientRect);
    try
      D2DCanvas.Font.Assign(Font);
      D2DCanvas.RenderTarget.BeginDraw;
      D2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);

      D2DCanvas.Brush.Color := clRed;

      D2DCanvas.Brush.Handle.SetOpacity(0.5);
      D2DCanvas.Ellipse(10, 10, 200, 200);
      D2DCanvas.RenderTarget.EndDraw;
    finally
      D2DCanvas.Free;
    end;
  end
end;

 

  • Like 1

Share this post


Link to post
On 3/5/2023 at 5:39 AM, vshvetsov said:

A new question has arisen. It turns out that in the structure TRGBQuad the order of colors is reversed in comparison to the VCL type TColor. With a simple reinterpretation of TColor as TRGBQuad, blue becomes red, and vice versa.

TColor is an enum representing an integer, and so it is subject to endian.  TRGBQuad is not.  You should be using the ColorToRGB() function to convert an TColor value into an integer value, and then using the Get(R|G|B)Value() functions to obtain the individual R/G/B values of that integer.

 

The PremultiplyAlpha() function is operating on a bitmap's raw R/G/B pixel bytes directly, it is not going through TColor at all.

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×