Reputation: 1443
I have my user control inside a tab. Text inside user control is blurry. Please find the code of user control paint event below
protected override void OnPaint(PaintEventArgs e)
{
//Avoid redrawing the control unnecessarily
if(redraw)
{
using (Graphics g = Graphics.FromImage(buffer))
{
g.Clear(Color.Transparent);
draw(g, ClientRectangle);
}
}
e.Graphics.DrawImage(buffer, Point.Empty);
}
I noticed following things if I place my control inside Tab control available in VS toolbox.
1) Text drawn in user control is blurry
2) Paint event triggered three times. Due to conditional redraw, drawing logic will be executed once and existing buffer will be drawn on 2nd and 3rd time
3) If I use g.Clear(Color.White), text looks good
The line g.Clear is supposed to clear the Bitmap(buffer) and fill it with specified color. It also clears the state of the graphics object. If I use any color other than Transparent, text is not blurry.
Is there any notable difference between g.Clear(Color.White) and g.Clear(Color.Transparent)?
Note: Color.White is just an example. Any named color gets rid of the problem. Also occurs only with Tab control
Edit: Blurred and correct images are available below
Upvotes: 0
Views: 2958
Reputation: 54433
In addition to a suggestion how to solve the problem, let me correct two misconceptions..
Color.Transparent is essentially a no-op—it does absolutely nothing
Filling/clearing with Color.Transparent
will make all pixels a transparent black: (0,0,0,0)
.
Contrary to sombody's remarks this is not nothing; it is useful and a normal start when one really wants to draw onto a transparent canvas.
Since your result looks wrong with it, you better clear the canvas with the color you really want. If you want to display something like a copy of a TabPage
you should probably start by filling with the BackColor
of that page:
g.Clear(tapPage1.BackColor);
Another remark, this times by you is that :
using g.Clear(Color.Transparent) fills the bitmap with black color.
Well, as I said it does fill with transparent black (0,0,0,0). It does not fill with real black (255,0,0,0).
Transparency is not supported by all imaging software. A notably bad program is the (otherwise great) IrfanView, which to this day will display any transparency as black. Paint .Net will 'display' it 'correctly' meaning by underlaying it with a checkerboard pattern that shines trough at the transparent pixels..
To make an image 'just' transparent (alpha=0
) without clearing the RBG
channels you can use a routine like this (although I prefer Lockbits
to unsafe
), but as I said, this is not what you want..
Update
g.Clear(Color.Transparent);
in fact can make the canvas transparent; this is what happens when you use it on a bitmap..
..or it brings up the parent's color when you use it on a control. If you use it on a Form, which doesn't have a parent it makes it look black.
Upvotes: 2
Reputation: 244732
In what universe does it make sense to "clear" with a transparent color?
Color.Transparent is…transparent. Defined as the absence of color. If you fill with a transparent color, you fill with nothing. Emptiness. Transparency.
If you fill something that had text on it with a transparent color, it will have no effect and the text will remain there. Then, if you draw the same text back on top of it, you will see blurriness (at least, assuming the text is anti-aliased; if it's not, nothing will change).
Any color other than Color.Transparent will work to clear the area because they are opaque colors. Meaning that they are the opposite of transparent. You can't see through them, so they actually cover up what is behind them.
You've also tried too hard to optimize the code. When a Paint event handler is called, the item needs to be painted. The operating system already takes care of optimizing this for you. So the if (redraw)
check is neither necessary nor a good idea. Just fill with your background color, and draw the image or text on top of it.
Update: I was pointed here to code that supposedly reproduces the problem. Like Moonlight Sheng, I was unable to reproduce the described problem with that code.
This is not surprising. In the linked code, the call to g.Clear
is completely superfluous for two reasons. First, as I have already explained, clearing with Color.Transparent
is essentially a no-op—it does absolutely nothing. Second, the line immediately following fills the entire image rectangle with a solid white brush. This serves the purpose of "clearing" the drawing surface, making it all white. You could have accomplished the same thing with g.Clear(Color.White)
. The text is then drawn on top of this white background, and finally the image is drawn onto the control. This works perfectly because there are never any "transparent" areas. Whenever the image is drawn on top of the control, it covers up (therefore erasing) everything that was there previously. Absolutely no blurriness. As such, this has nothing to do with the use of Graphics.Clear
or Color.Transparent
.
I suppose that since you are overriding OnLayout
, you intend for this control to be used with either its Anchor or Dock properties set. So I set the Dock property to Fill in my test project, and re-ran the application. Again, I fail to reproduce the problem. However, I do notice that when rapidly resizing the container form, I can produce "tearing" effects or other visual artifacts. For example, when I make the form really small, I see the text drawn multiple times:
This is a race condition caused by the rapid resizing of the form. The dimensions of the client rectangle are changing too quickly, and the newly-exposed regions of the form are not being erased. Let me see if I can explain what is actually happening.
DrawString
to draw text into it. The image is too narrow to fit the entire string on one line, so it wraps the "100" onto a second line. The image is then drawn onto the form, and everything looks fine.It's very difficult to explain, and I imagine harder still to understand, unless you already know how painting works.
The important part is that there is a simple solution: set the form's ResizeRedraw
property to true
, ensuring that a Paint event gets raised every time the form is resized. Forcing a repaint in the control's OnLayout
event handler method would also work (this.Invalidate()
), like so:
public partial class MyControl : UserControl
{
Image buffer;
public MyControl()
{
InitializeComponent();
}
protected override void OnLayout(LayoutEventArgs e)
{
if (buffer != null)
{
buffer.Dispose();
buffer = null;
}
base.OnLayout(e);
this.Invalidate(); // force a repaint after the layout has changed
}
protected override void OnPaint(PaintEventArgs e)
{
if (buffer == null)
{
buffer = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
using (Graphics g = Graphics.FromImage(buffer))
{
//g.Clear(Color.Transparent); // pointless
g.FillRectangle(Brushes.White, ClientRectangle);
using (Font f = new System.Drawing.Font("Segoe UI", 12, FontStyle.Regular))
{
g.DrawString("Simple text 100", f, Brushes.Black, ClientRectangle);
}
}
}
e.Graphics.DrawImage(buffer, ClientRectangle);
base.OnPaint(e);
}
}
Anyway, this is not a bug as claimed, but simply a misunderstanding of how the painting subsystem works in Windows.
Incidentally, if you remove that call to FillRectangle
(which fills the control's client area with solid white), you would see the blurry-text effect that you describe originally. This is occurring for the exact reasons that I have already stated: the text is anti-aliased, and drawing anti-aliased text on top of anti-aliased text produces a "blurry" effect. You cannot fix this in any sensible way. Anti-aliased text absolutely must be drawn on top of a solid-color background; it cannot be drawn on a transparent background. You will need to fill with whatever your form's background color is. By default, that is SystemColors.Control
.
Upvotes: 1