draw text with transparent background on wxBitmap

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: draw text with transparent background on wxBitmap

Post by doublemax »

ollydbg23 wrote: Thu Nov 24, 2022 1:56 am Hi, doublemax, thanks, can you show the code?
I only added drawing of the background. The rest is directly from your repo.
Attachments
checkers.png
checkers.png (214 Bytes) Viewed 2120 times
svg_panel.h
(1.01 KiB) Downloaded 166 times
svg_panel.cpp
(5.33 KiB) Downloaded 172 times
Use the source, Luke!
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

doublemax wrote: Thu Nov 24, 2022 8:04 am
ollydbg23 wrote: Thu Nov 24, 2022 1:56 am Hi, doublemax, thanks, can you show the code?
I only added drawing of the background. The rest is directly from your repo.
Hi, doublemax, my question still remains. I just looked at your code, for drawing the text, they are still drawn on the decice context, what I need is draw on the wxBitmap. Because someone in the wx-dev forum would like to draw svg text for the toolbar icon.

Thanks.
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

ONEEYEMAN wrote: Thu Nov 24, 2022 3:07 am ollydbg,
If I copy the files will they conflict with the internal library SVG parser?

Thank you.
No, I don't have conflict here for wx 3.2.1 supplied by msys2' pacman.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: draw text with transparent background on wxBitmap

Post by doublemax »

ollydbg23 wrote: Sat Nov 26, 2022 2:50 am Hi, doublemax, my question still remains. I just looked at your code, for drawing the text, they are still drawn on the decice context, what I need is draw on the wxBitmap. Because someone in the wx-dev forum would like to draw svg text for the toolbar icon.
Yeah, sorry about that, didn't check the code again, i thought that version already rendered into the bitmap.

The problem is that wxDC under Windows can't handle transparency, in this particular case, you can't draw into the parts of a bitmap that are transparent. You'll need to change the code to use wxGraphicsContext instead (just for the text rendering).
Use the source, Luke!
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

Hi, thanks for the reply.

I just follow your advice and use the wxGraphicsContext class to draw the text, but unluckily, the font is still missing, see my change of the function OnPaint(). Please note that the code I used was followed from the way mentioned here: Clearing a transparent wxGraphicsContext? (Or this discussion wxGraphicsContex Text Drawing, they are mainly the same method)

Code: Select all

void SVGPanel::OnPaint(wxPaintEvent &event)
{
	wxAutoBufferedPaintDC dc(this);

	dc.SetBrush( *wxWHITE_BRUSH );
	dc.SetPen( *wxWHITE_PEN );
	dc.DrawRectangle( 0, 0, GetClientSize().x, GetClientSize().y );

	if( m_svg_image!=NULL )
	{
		double svg_ratio = m_svg_image->width / m_svg_image->height;
		double panel_ratio = (double)GetClientSize().x / GetClientSize().y;

		int render_width = 0;
		int render_height = 0;
		if( svg_ratio > panel_ratio )
		{
			render_width = GetClientSize().x;
			render_height = svg_ratio * GetClientSize().y;
		} else {
			render_width = (double)GetClientSize().y * svg_ratio;
			render_height = GetClientSize().y;
		}

		if (render_width == 0 || render_height == 0)
            return;

		if( !m_bitmap.IsOk() || render_width != m_bitmap.GetWidth() || render_height != m_bitmap.GetHeight() )
		{
			m_bitmap.Create(render_width, render_height, 32);
			m_bitmap.UseAlpha(true);

			NSVGrasterizer *rast = nsvgCreateRasterizer();
			unsigned char *image_buffer = (unsigned char *)malloc(render_width*render_height*4);

			float scale = (float)render_width / m_svg_image->width;
			m_Scale = scale;

			//wxLogDebug("rasterizing image %d x %d (scale=%.2f)", render_width, render_height, scale);
			//printf("rasterizing image %d x %d (scale=%.2f)", render_width, render_height, scale);
			nsvgRasterize(rast, m_svg_image, 0,0,scale, image_buffer, render_width, render_height, render_width*4);

			PixelData bmdata(m_bitmap);
			PixelData::Iterator dst(bmdata);

			unsigned char *source_data = image_buffer;
			for(int y=0; y<m_bitmap.GetHeight(); y++)
			{
				dst.MoveTo(bmdata, 0, y);
				for(int x=0; x<m_bitmap.GetWidth(); x++)
				{
					const unsigned char alpha = source_data[3];
					dst.Blue() = source_data[2] * alpha / 255;
					dst.Green() = source_data[1] * alpha / 255;
					dst.Red() = source_data[0] * alpha / 255;
					dst.Alpha() = alpha;
					dst++;
					source_data += 4;
				}
			}
			nsvgDeleteRasterizer(rast);
			free(image_buffer);

			// here the m_bitmap(created by m_bitmap.Create(render_width, render_height, 32)) is ready
			// but it does not contains any text label information, so we have to use wxMemoryDC to
			// draw text on the bitmap
			wxMemoryDC memDC(m_bitmap);
			// Clearing a transparent wxGraphicsContext? - wxWidgets Discussion Forum — https://forums.wxwidgets.org/viewtopic.php?t=48970&p=210477
			wxGraphicsContext* gc = wxGraphicsContext::Create(memDC);

            gc->SetPen(*wxRED_PEN);
            gc->SetBrush(*wxTRANSPARENT_BRUSH);

            for (TextLabel &a : m_TextLabels)
            {
                wxFont font(wxFontInfo(a.fontSize*m_Scale/1.3333).FaceName(a.family).AntiAliased(true));
                gc->SetFont(font, *wxBLACK);

                //gc->SetTextForeground(*wxRED);
                //gc->SetTextBackground(*wxGREEN);

                // size calculation
                wxDouble  w, h;
                wxDouble descent;
                gc->GetTextExtent(a.label, &w, &h, &descent);

                gc->DrawText(a.label, a.x*m_Scale, a.y*m_Scale - h + descent);
            }
            delete gc;
            memDC.SelectObject(wxNullBitmap);
		}

		if( m_bitmap.IsOk() )
		{
			dc.DrawBitmap( m_bitmap, 0, 0, true );
			//m_bitmap.SaveFile( "d:\\_nano_svg_test.png", wxBITMAP_TYPE_PNG );
		}

#if 0 // disable drawing the text labels on DC, we just need to build it on bitmap
        // show text labels on top of the bitmap
		for (TextLabel &a : m_TextLabels)
        {
            // The coordinates refer to the top-left corner of the rectangle bounding the string.
            // See GetTextExtent() for how to get the dimensions of a text string,
            // which can be used to position the text more precisely

            // for svg text, its x,y is defined as: x: The x coordinate of the starting point of the text baseline.
            // so generally the left bottom corner of the first character

            //wxFont font(a.fontSize*4*g_Scale, wxROMAN, wxNORMAL, wxLIGHT, false, _T("Times New Roman"));

            // 2022-04-10 point size is in pt unit, we have to convert it to px unit
            // One point(pt) is the equivalent of 1.333(3) pixels(px)

            wxFont font(wxFontInfo(a.fontSize*m_Scale/1.3333).FaceName(a.family).AntiAliased(true));
            dc.SetFont(font);

            // size calculation
            wxCoord  w, h;
            wxCoord descent;
            dc.GetTextExtent(a.label, &w, &h, &descent);

            dc.DrawText(a.label, a.x*m_Scale, a.y*m_Scale - h + descent);
        } //for (TextLabel &a : m_TextLabels)
#endif
	} //if( m_svg_image!=NULL )
};
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: draw text with transparent background on wxBitmap

Post by doublemax »

This worked for me:

Code: Select all

  // draw text on top
  wxGraphicsContext *gc = wxGraphicsContext::Create( m_bitmap );
  gc->SetAntialiasMode( wxANTIALIAS_NONE );
  for (TextLabel &a : m_TextLabels)
  {
    // The coordinates refer to the top-left corner of the rectangle bounding the string.
    // See GetTextExtent() for how to get the dimensions of a text string,
    // which can be used to position the text more precisely

    // for svg text, its x,y is defined as: x: The x coordinate of the starting point of the text baseline.
    // so generally the left bottom corner of the first character

    //wxFont font(a.fontSize*4*g_Scale, wxROMAN, wxNORMAL, wxLIGHT, false, _T("Times New Roman"));

    // 2022-04-10 point size is in pt unit, we have to convert it to px unit
    // One point(pt) is the equivalent of 1.333(3) pixels(px)
    wxGraphicsFont gfont = gc->CreateFont( wxFont(wxFontInfo(a.fontSize*m_Scale/1.3333).FaceName(a.family)), *wxRED );
    gc->SetFont(gfont);

    // size calculation
    wxDouble  w, h;
    wxDouble descent;
    gc->GetTextExtent(a.label, &w, &h, &descent);

    gc->DrawText(a.label, a.x*m_Scale, 12 + a.y*m_Scale - h + descent);
  } //for (TextLabel &a : m_TextLabels)
  delete gc;
}

if( m_bitmap.IsOk() )
{
  dc.DrawBitmap( m_bitmap, 0, 0, true );
}
Without the wxANTIALIAS_NONE you get some ugly black edged around the text.
Use the source, Luke!
User avatar
doublemax@work
Super wx Problem Solver
Super wx Problem Solver
Posts: 474
Joined: Wed Jul 29, 2020 6:06 pm
Location: NRW, Germany

Re: draw text with transparent background on wxBitmap

Post by doublemax@work »

Code: Select all

12 + a.y*m_Scale
You can remove the "12" here, i just added that because i wanted the text to overlap the drawing for testing.
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

doublemax@work wrote: Mon Nov 28, 2022 7:55 am

Code: Select all

12 + a.y*m_Scale
You can remove the "12" here, i just added that because i wanted the text to overlap the drawing for testing.

Hi, doublemax, thanks for your help, you solved the problem!
With your new code, the wxGraphicsContext's font rendering on wxBitmap works OK now.
Yes, I do remove the "12" to let the font a litter upper like the original screen shot.
doublemax wrote: Mon Nov 28, 2022 6:36 am ...
...
Without the wxANTIALIAS_NONE you get some ugly black edged around the text.
In my testing, whether I have the line here or just comment the line out has no effect here, in both of the two cases, the text(font) shows correctly.

Code: Select all

gc->SetAntialiasMode( wxANTIALIAS_NONE );
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

ollydbg23 wrote: Mon Nov 28, 2022 8:05 am ...
doublemax wrote: Mon Nov 28, 2022 6:36 am ...
...
Without the wxANTIALIAS_NONE you get some ugly black edged around the text.
In my testing, whether I have the line here or just comment the line out has no effect here, in both of the two cases, the text(font) shows correctly.

Code: Select all

gc->SetAntialiasMode( wxANTIALIAS_NONE );
Here are some news:

In one of my PCs, I see that if the function call statement below is missing, than the edge of the text is ugly as doublemax said:

Code: Select all

gc->SetAntialiasMode( wxANTIALIAS_NONE );
See the ugly image shot of the text rendering, it shows blue colors around the edge of the font. This happens in one of my PCs, in another PC, I don't see the issue.
2022-11-30 20 14 09.png
2022-11-30 20 14 09.png (1.86 KiB) Viewed 1969 times
So, for consistence, we need this function call.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: draw text with transparent background on wxBitmap

Post by doublemax »

ollydbg23 wrote: Thu Dec 01, 2022 3:41 am In one of my PCs, I see that if the function call statement below is missing, than the edge of the text is ugly as doublemax said:
Interesting. What's the difference between these PCs, on which one does it happen, and on which one does it not?
Use the source, Luke!
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

doublemax wrote: Thu Dec 01, 2022 7:27 am
ollydbg23 wrote: Thu Dec 01, 2022 3:41 am In one of my PCs, I see that if the function call statement below is missing, than the edge of the text is ugly as doublemax said:
Interesting. What's the difference between these PCs, on which one does it happen, and on which one does it not?
When comment out this line:

Code: Select all

//gc->SetAntialiasMode( wxANTIALIAS_NONE );
The ugly edge happens on a PC with Win7 64bit with GeForce MX 9** or Intel HD Graphics 5** notebook. I can switch either the graphics driver to use, but in both of the cases, the ugly edge will happen.

The ugly edge doesn't happens on a Remote desktop PC(I run the program by remote desktop client) running Win7 64bit with GeForce MX 7**.
New Pagodi
Super wx Problem Solver
Super wx Problem Solver
Posts: 469
Joined: Tue Jun 20, 2006 6:47 pm
Contact:

Re: draw text with transparent background on wxBitmap

Post by New Pagodi »

It might be the case that the video on remote desktop is probably being compressed and the compression has the effect of smoothing out the rough edges.
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

New Pagodi wrote: Thu Dec 01, 2022 8:55 pm It might be the case that the video on remote desktop is probably being compressed and the compression has the effect of smoothing out the rough edges.
Hi, New Pagodi, I haven't got chance to test(sit) on the remote desktop, so I don't know whether it has smooth issue.

But I see that the font edge is definitely depend on the size of the window it shows. My full test code is on https://github.com/asmwarrior/SvgPanel with sample code.

Here is the two image shot of the result:
looks good
looks good
2022-12-08 18 32 39.png (51.65 KiB) Viewed 1745 times
And if I resize the window, the font got bad!
2022-12-08 18 33 39.png
2022-12-08 18 33 39.png (47.58 KiB) Viewed 1745 times
I'm not sure why. This is test on my PC(not the remote desktop PC).


EDIT 2022-12-09:

About how to generate the two screen shots, I forgot to mention that the line:

Code: Select all

gc->SetAntialiasMode(wxANTIALIAS_NONE);
is comment out for those two screen shots.

As doublemax said, it looks like the bigger font will automatically have wxANTIALIAS_NONE enabled, while the small font doesn't.
Last edited by ollydbg23 on Fri Dec 09, 2022 1:54 am, edited 1 time in total.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: draw text with transparent background on wxBitmap

Post by doublemax »

It's possible that anti-aliasing is not used at bigger font sizes.
Use the source, Luke!
ollydbg23
Super wx Problem Solver
Super wx Problem Solver
Posts: 438
Joined: Fri Dec 12, 2008 10:31 am

Re: draw text with transparent background on wxBitmap

Post by ollydbg23 »

doublemax wrote: Thu Dec 08, 2022 12:55 pm It's possible that anti-aliasing is not used at bigger font sizes.
Well, I think this is not correct.
In my two screen shot, the later window is smaller than the first one. But the edge issue happens in the later window.
Post Reply