I'm trying to render some text onscreen in Monogame by drawing a string to a Texture2D
This is the output I currently have
this is what i want it to look like
and here is a snippet of the code
Bitmap b = new Bitmap(width, height, PixelFormat.Format32bppArgb);
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(b);
g.Clear(System.Drawing.Color.Transparent);
//Quality settings
g.SmoothingMode = SmoothingMode.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Brush brush = new SolidBrush(Color.White);
g.DrawString(text, f, brush, 0, 0);
After reading some of the other posts about the same issue I tried clearing with the colour white, didn't change anything, then I tried adding a bunch of graphics settings like this post said to and that only gave me
Which has a bunch of unwanted black dots around it, which is arguably better then the first result but still not the crisp text I need, and clearing it with a single colour wont work because this text could potentially be on an image or something that moves
EDIT: i was able to get it slightly better with TextRenderingHint.SingleBitPerPixelGridFit
Still isnt what i want but at least closer
I was finally able to fix it with the help of some people in the monogame discord by settings BlendState to Nonpremultiplied in the sprite batch where the text is drawn to
Related
I am creating an Circle on a bitmap but want to have a hole in it. After serching for half an hour I only found ways to crop an image to a circle. The hard thing is, that the hole in the middle should be transparent as the rest of the Image.
This is the base image and the yellow circle represents the transparent area that should be added.
Thanks for any kind of help.
The start is simple: Create a transparent bitmap by doing a g.Clear(Color.Transparent) and then draw/fill a circle in a color.
The next step is a bit trickier: You next want to paint the hole with transparency.
To do so you need to switch the Graphics object to the right CompositingMode; default is SourceOver but you want SourceCopy. The former overlays the alpha values creating mixed colors. The latter will do what we want: Draw the hole by copying the drawn colors including alpha right over the old ones..
Here is an example:
Bitmap bmp = new Bitmap(500, 500);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Transparent);
//g.SmoothingMode = SmoothingMode.AntiAlias;
g.CompositingMode = CompositingMode.SourceCopy;
g.FillEllipse(Brushes.DarkGreen, 100, 100, 300, 300);
g.FillEllipse(Brushes.Transparent, 200, 200, 100, 100);
}
pictureBox1.Image = bmp;
This is what is looks like in a PictureBox with a BackgroundImage:
A few notes:
You can also use a semi-transparent brush to create a 'tinted' hole; do not use anti-aliasing for this though, as it would introduce colored fringes.
We used simple circles here but with a GraphicsPath you can create and fill shapes of almost any shape and complexity..
And using a GraphicsPath would also have been an alternative to filling with transparency: By first adding the large and then the smaller, inner ellipse the path would have been created with a hole and filling it would have had the very same result! But I found the solution above more instructive..
Final note: As clarkitect noted, to save, do use a format that supports transparency. Png is always recommended..
I have an image that load a .PNG file and then combines it with other graphics I create. The thing is that I need to rotate only the .PNG file, not the whole thing. Picture a speedometer, you have a background image that goes from 0 to 200. That image remains static all the time. Now, on top of it, you have an arrow pointing to your current speed. That's the one I want to rotate.
This is what I have so far. It does display the graphics, but doesn't rotate the .PNG (the arrow)
Bitmap bitmap = new Bitmap(500, 280, PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(Color.White);
//this.Arrow = path to the .PNG
Image i = Image.FromFile(this.Arrow);
Bitmap a = new Bitmap(i.Width, i.Height);
Graphics ga = Graphics.FromImage(a);
a.SetResolution(ga.DpiX, ga.DpiY);
//It shouldn't rotate having the pivot at the (centre, centre)
//coordinates, but at the bottom of the image.
//The (21, 110) coordinates are right relative to the .PNG file
ga.TranslateTransform(21, 110);
ga.RotateTransform(45); //<--- Not rotating
ga.DrawImage(i, 0, 0);
g.DrawImage(i, new Rectangle(new Point(229, 120), new Size(i.Width, i.Height)));
g.DrawLine(new Pen(new SolidBrush(Color.Aquamarine), 1), 250, 0, 250, 280);
g.DrawLine(new Pen(new SolidBrush(Color.Aquamarine), 1), 0, 230, 500, 230);
Any ideas?
Without a good, minimal, complete code example that reliably reproduces the problem, it is impossible to know for sure what the problem is. There are several possible problems in the code you posted; the most obvious being that you are drawing the image i into the Graphics object g, rather than the image a into which you've drawn i at a 45-degree rotation.
You will likely get results closer to that desired if you change that program statement to this:
g.DrawImage(a, new Rectangle(new Point(229, 120), new Size(a.Width, a.Height)));
Other issues include:
Failing to dispose ga and g when done with them
Failing to translate ga back to the desired location after the rotation (i.e. you translate to get the rotation of the image to happen around the point (21, 110) of the image, but have left the image translated by that amount after the rotation, which may not be what you want
Using positive offsets for the previously-mentioned translation
Because of these issues (and possibly others), I don't think the above change will result in exactly the desired output, but at least you will see some evidence of rotation.
While your error does not seem to be that which I've addressed in my answer to the question commenter Keith M mentioned, I do think you would benefit from reading the code example there, as it does basically the same thing you are trying to do here.
I've been trying to figure out a way to resize a PNG image without losing color data from pixels that are completely transparent. Here is the code that I'm using to achieve this:
//sourceImage is also a bitmap read in earlier
using (var scaledBitmap = new Bitmap(desiredWidth, desiredHeight, PixelFormat.Format32bppArgb)){
using (var g = Graphics.FromImage(scaledBitmap)){
var attr = new ImageAttributes();
attr.SetWrapMode(WrapMode.TileFlipXY);
g.CompositingQuality = CompositingQuality.HighQuality;
g.CompositingMode = CompositingMode.SourceCopy;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(sourceImage, new Rectangle(0, 0, desiredWidth, desiredHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, attr);
}
scaledBitmap.Save(memoryStream, ImageFormat.Png);
//use new bitmap here
}
But the problem with this example is that anywhere that a pixel's alpha value is zero, the pixels become black.
My reputation is too low to post these images inline sadly.
Images
First Image is the image I've been using to test with. For quick reference, I've saved a copy without the alpha channel (Second Image). The entire image is a solid color and the alpha masks part of the image to create a circle.
Third Image is the resulting image from the code above. Again I've saved two copies. In Fourth Image, it is very clear that data is being lost during the conversion. What I'd expect to see is the same single color filling the whole image.
Doing some reading I discovered this in the PNG file specification:
The alpha channel can be regarded either as a mask that temporarily hides transparent parts of the image, or as a means for constructing a non-rectangular image. In the first case, the colour values of fully transparent pixels should be preserved for future use. In the second case, the transparent pixels carry no useful data and are simply there to fill out the rectangular image area required by PNG. In this case, fully transparent pixels should all be assigned the same colour value for best compression.
So it looks like it's up to the PNG encoder to decide how to handle color values where the alpha is zero. Is there any way to tell GDI+'s encoder to not zero out the color data?
The reason I need to keep the color data is for further, dynamic resizing of the image. Without a homogeneous color at the edges of the visible portion of the image, they are terribly prone to ringing artifacts. There's also a post on the Unity forums about this issue.
Please let my know if anyone else has encountered this and how you dealt with it. Also if anyone knows of any libraries that implement a C# PNG encoder that is less draconian it would be greatly appreciated.
Thanks!
While I realize this may not be sufficient for some, in my case the answer was to switch to using ImageMagick's .NET wrapper. I was also able to find a thread on their forums with the exact problem I was experiencing.
It appears that this is a common bug when resizing images and ImageMagick has fixed this issue in their library.
using ImageMagick;
using (var sourceImage = new MagickImage(texture)){ //texture is of type Stream
sourceImage.Resize(desiredWidth, desiredHeight);
sourceImage.Write(memoryStream, MagickFormat.Png);
}
// use memoryStream here
As you can see, this also greatly simplified my code due to ImageMagick's built in support for resize operations.
Without ImageMagick, you can simply use the following code and it works, don't change CompositingQuality or any other Graphics properties :
Bitmap NewImage = new Bitmap(OriginalImage.Width, OriginalImage.Height, OriginalImage.PixelFormat);
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(NewImage)) {
g.DrawImage(OriginalImage, new Rectangle(0, 0, NewWidth, NewHeight), 0, 0, OriginalImage.Width, OriginalImage.Height, GraphicsUnit.Pixel);
}
NewImage.Save("myTransparent.png");
I'm working on a Machine Learning problem at college and my first job is to convert images into black and white.
The problem is that my image has transparency, and I don't know how to remove it.
What I am trying:
public static Bitmap RemoveTransparency (Bitmap src)
{
Bitmap target = new Bitmap (src.Size.Width, src.Size.Height);
Graphics g = Graphics.FromImage (target);
g.Clear (Color.White);
g.DrawImage (src, 0, 0);
return target;
}
An example of an input image:
An example of output image after "RemoveTransparency" call:
Does anyone know what's going on? Seems like background an the letters have the same color... and my my background is black if I'm coloring to white?
Thank you!
You need to set the CompositingMode of your Graphics object to SourceOver before drawing the other image on top of it.
g.Clear(Color.White);
g.CompositingMode = CompositingMode.SourceOver;
g.DrawImage(src, 0, 0);
The default CompositingMode is SourceCopy, which is taking the transparent black (R=G=B=A=0) pixels in your src image and rendering them as black pixels. SourceOver will do alpha blending, which is what you're looking for.
See here for details: CompositingMode Enumeration
I'm developing a program that makes use of Windows 7 (and Vista) taskbar features. Right now I have a custom bitmap that will be shown in the taskbar thumbnail. The bitmap is created programmatic and shows up successfully. The only 'problem' I'm having is that want to use transparent in that image, which should show transparent in the thumbnail too. But without any success, resulting in the standard Light-Gray color.
I've seen evidence that programs successfully get transparent in the their images:
http://blog.miranda.or.at/wp-content/uploads/2010/05/taskbar.png
http://www.addictivetips.com/wp-content/uploads/2010/04/TaskbarRSS.png
http://www.addictivetips.com/wp-content/uploads/2009/10/Thumbnail1.png
Now is my question: how do I get transparent in my thumbnail image?
I'll be filling the image with the Graphics class, so anything is allowed.
I should mention that I use Windows® API Code Pack, which uses GetHbitmap to
set the image as thumbnail.
EDIT:
Just to make it complete, this is the code I'm using atm:
Bitmap bmp = new Bitmap(197, 119);
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(new SolidBrush(Color.Red), new Rectangle(0, 0, bmp.Width, bmp.Height)); // Transparent is actually light-gray;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.DrawString("Information:", fontHeader, brush, new PointF(5, 5));
bmp.MakeTransparent(Color.Red);
return bmp;
What pixel format is your Bitmap? If it has no alpha channel, you won't be able to store transparency information in your image.
Here is how to create a bitmap with an alpha channel and make it transparent by default:
Bitmap image = new Bitmap(width, height, PixelFormat.Format32bppArgb);
using(Graphics graphics = Graphics.FromImage(image))
{
graphics.Clear(Color.Transparent);
// Draw your stuff
}
You can then draw anything you want, including semi transparent stuff using the alpha channel.
Also note that if you are trying to paint transparency over existing opaque stuff (say to make a hole), you need to change compositing mode:
graphics.CompositingMode = CompositingMode.SourceCopy;
This will make whatever color you use to overwrite the one in the image rather than blending with it.
System.Drawing.Bitmap supports an alpha level. So the simplest way is
Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(Brushes.Transparent, new Rectangle(0, 0, bmp.Width, bmp.Height)); // Transparent is actually light-gray;
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
g.DrawString("Information:", fontHeader, brush, new PointF(5, 5));
however you can also have partial transparency by replacing Brushes.Transparent with
new SolidBrush(Color.FromArgb(150, 255, 255, 255));