var bmp = new Bitmap(16, 16);
var gBmp = System.Drawing.Graphics.FromImage(bmp );
Color col = Color.FromArgb(pdmsCol.Red, pdmsCol.Green, pdmsCol.Blue);
gBmp.FillRectangle(new SolidBrush(col), new Rectangle(0, 0, 16, 16));
mColourPopupContainer.Image = bmp;
Using the above code draws a rectangle into my control for given color. This works fine unless the color is Magenta in which case it seems to be drawn transparently. I guess this is something to do with bitmaps treating Magenta as transparent. How to I turn off this behaviour?
mColourPopupContainer is a UserControl (custom color picker).
You need to set the Form.TransparencyKey property to a color other than Magenta. This property:
Gets or sets the color that will represent transparent areas of the form.
By default it's magenta, hence your issue. If you set it to some other color that shouldn't show up anywhere else on your form, the problem should go away.
My current solution is not very nice: Detect when magenta is used and change it to not magenta.
var col = Color.FromArgb(r, g, b);
if (r==255 && g==0 && b==255 ) col = Color.FromArgb(r-1, g, b); // Don't use Megenta as it can be treated as transparent
Related
Everywhere I look online, I see people posting on how to successfully crop an image. However, I want to 'crop'/ clear a hole out of an image. I want to keep the original image, but crop out a rectangle
As you can see in the image above, I have "cropped" out the kittens face. I maintained the original image, but removed only part of it. I cannot figure out how to do that.
Assuming you want to replace the original pixel colors with transparency you run into a small problem: You can't draw or fill with transparency in GDI+.
But you can use Graphics.Clear(Color.Transparent).
To do that you restrict the region where the Graphics object will draw. Here we can use the simple cropping rectangle but you can clear more complex shapes using a GraphicsPath..
Example using a bitmap bmp:
using (Graphics g = Graphics.FromImage(bmp))
{
Rectangle crop = new Rectangle(222,222,55,55);
g.SetClip(crop);
g.Clear(Color.Transparent);
}
bmp.Save(somefilename, ImageFormat.Png);
Setting your Graphics object's CompositingMode property to CompositingMode.SourceCopy will allow your drawing operations to replace the alpha value instead of proportionally opacifying it:
public static void TestDrawTransparent()
{
//This code will, successfully, draw something transparent overwriting an opaque area.
//More precisely, it creates a 100*100 fully-opaque red square with a 50*50 semi-transparent center.
using(Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb))
{
using(Graphics g = Graphics.FromImage(bmp))
using(Brush opaqueRedBrush = new SolidBrush(Color.FromArgb(255, 255, 0, 0)))
using(Brush semiRedBrush = new SolidBrush(Color.FromArgb(128, 255, 0, 0)))
{
g.Clear(Color.Transparent);
Rectangle bigRect = new Rectangle(0, 0, 100, 100);
Rectangle smallRect = new Rectangle(25, 25, 50, 50);
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
g.FillRectangle(opaqueRedBrush, bigRect);
g.FillRectangle(semiRedBrush, smallRect);
}
bmp.Save(#"C:\FilePath\TestDrawTransparent.png", ImageFormat.Png);
}
}
In this code, I first draw a fully-opaque red square, then a semi-transparent red square "over" it. The result is a semi-transparent "hole" in the square:
And on a black background:
A zero-opacity brush works just as well, leaving a clear hole through the image (I checked).
With that in mind, you should be able to crop any shapes you want, simply by filling them with a zero-opacity brush.
I need to remove the black background color of a bitmap in C# VS2013.
It is just like that I draw some points on a canvas. The canvas is black and I just need to change the canvas to be transparent meanwhile keeping colorful points on it without any changes.
I got the solution at:
How to remove white background color from Bitmap
Bitmap capcha = new Bitmap(#"C:/image.png");
capcha.MakeTransparent(Color.Black);
But, the background still have a gray color like a fog covering the points on the image.
How to remove it ?
UPDATE
I used the code:
ImageAttribute imageAttribute = new ImageAttribute();
imageAttribute.SetGamma(0.5f, ColorAdjustType.Bitmap);
gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
0, 0, img.Width, img.Height, GraphicsUnit.Pixel, imageAttribute );
I got same thing.
More update of C# code to draw an image :
System.Drawing.Bitmap canvasImage = new System.Drawing.Bitmap(xSize, ySize, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
canvasImage.MakeTransparent(Color.Black);
Graphics g = Graphics.FromImage(canvasImage);
System.Drawing.Bitmap tempImage = myDrawImageFunction(myPoints);
g.Clear(Color.Transparent); // this only give me an empty image without any points on it. But, if I uncomment it, I will have an image with black background.
// my understanding about g.DrawImage() is to draw points on tempImage
// after clearing the background. But, the image still have a foggy covering on the image.
g.DrawImage(tempImage, new System.Drawing.PointF(x_position, y_position));
I want to have a transparent background for "tempImage" before any points are drawn on it.
The example image has a back ground that needs to be removed but the colorful points on the image need to be kept without any changes.
This will do the job:
public Color MakeTransparent(Color c, int threshold)
{ // calculate the weighed brightness:
byte val = (byte)((c.R * 0.299f + c.G * 0.587f + c.B * 0.114f));
return val < threshold ? Color.FromArgb(0, c.R, c.G, c.B) : c;
}
You could use it in a double loop over the pixels, but for fast results you should call it from the code in this post (second part!) which uses LockBits.
Change this
ModifyHue hueChanger = new ModifyHue(MaxChannel);
to the new function:
ModifyHue hueChanger = new ModifyHue(MakeTransparent);
and call it with a suitable threshold, maybe 10 or 20..:
c = hueChanger(c, 20);
The function skips the call to the system's MakeTransparent function and directly sets the alpha channel of each pixel to 0.
If you want to create a uniform color instead of a transparent one it should be easy to modify (e.g. by returning Color.FromArgb(255, 0, 0, 0) for solid black)
Do note that, while the code in the linked post takes both 24 and 32 bbp formats you should definitely not save as JPG, as this will re-introduce artifacts and the result will not work well with e.g. a TransparencyKey color..
Instead do save it as PNG, as Hans suggests!
I hope you can modify the button code to a function :-)
When rendering text into a bitmap, I find that text looks very bad when rendered on top of an area with non-opaque alpha. The problem is progressively worse as the underlying pixels become more transparent. If I had to guess I'd say that when underlying pixels are transparent, the text renderer draws any anti-aliased 'gray' pixels as solid black.
Here are some screenshots:
Text drawn on top of transparent pixels:
Text drawn on top of semi-transparent pixels:
Text drawn on opaque pixels:
Here is the code used to render the text:
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawString("Press the spacebar", Font, Brushes.Black, textLeft, textTop);
The option I used to workaround this problem was:
Graphics graphics = new Graphics();
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
There are some others useful options in TextRenderingHint
Hope it helps
There is a very simple answer to this...
g.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAliasGridFit
If you set this before you render your text, it will come out clear. In addition, this methods supports more font sizes (The default only goes up to size 56).
Thanks for reading this post.
The first output is what you get when you draw black text on a black background, probably Color.Transparent. The 2nd was drawn on an almost-black background. The 3rd was drawn on the same background it is being displayed with.
Anti-aliasing cannot work when on a transparent background. The colors used for the anti-aliasing pixels will not blend the letter shape into the background when the text is displayed with a different background. Those pixels will now become very noticeable and make the text look very bad.
Note that SmoothingMode doesn't affect text output. It will look slightly less bad if you use a lower quality TextRenderingHint and a background color that's grayish with a alpha of zero. Only TextRenderingHint.SingleBitPerPixelGridFit avoids all anti-aliasing troubles.
Getting a perfect fix for this is very difficult. Vista's glass effect on the window title bar uses very subtle shading to give the text a well defined background color. You'd need SysInternals' ZoomIt tool to really see it. DrawThemeTextEx() function with a non-zero iGlowSize.
If you're looking for something that preserves antialiasing a bit better than GDI+ does by default, you can call Graphics.Clear with a chroma key, then manually remove the chroma artifacts that result. (See Why does DrawString look so crappy? and Ugly looking text problem.)
Here's how I ultimately ended up solving a similar problem:
static Bitmap TextToBitmap(string text, Font font, Color foregroundColor)
{
SizeF textSize;
using ( var g = Graphics.FromHwndInternal(IntPtr.Zero) )
textSize = g.MeasureString(text, font);
var image = new Bitmap((int)Math.Ceiling(textSize.Width), (int)Math.Ceiling(textSize.Height));
var brush = new SolidBrush(foregroundColor);
using ( var g = Graphics.FromImage(image) )
{
g.Clear(Color.Magenta);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawString(text, font, brush, 0, 0);
g.Flush();
}
image.MakeTransparent(Color.Magenta);
// The image now has a transparent background, but around each letter are antialiasing artifacts still keyed to magenta. We need to remove those.
RemoveChroma(image, foregroundColor, Color.Magenta);
return image;
}
static unsafe void RemoveChroma(Bitmap image, Color foregroundColor, Color chroma)
{
if (image == null) throw new ArgumentNullException("image");
BitmapData data = null;
try
{
data = image.LockBits(new Rectangle(Point.Empty, image.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
for ( int y = data.Height - 1; y >= 0; --y )
{
int* row = (int*)(data.Scan0 + (y * data.Stride));
for ( int x = data.Width - 1; x >= 0; --x )
{
if ( row[x] == 0 ) continue;
Color pixel = Color.FromArgb(row[x]);
if ( (pixel != foregroundColor) &&
((pixel.B >= foregroundColor.B) && (pixel.B <= chroma.B)) &&
((pixel.G >= foregroundColor.G) && (pixel.G <= chroma.G)) &&
((pixel.R >= foregroundColor.R) && (pixel.R <= chroma.R)) )
{
row[x] = Color.FromArgb(
255 - ((int)
((Math.Abs(pixel.B - foregroundColor.B) +
Math.Abs(pixel.G - foregroundColor.G) +
Math.Abs(pixel.R - foregroundColor.R)) / 3)),
foregroundColor).ToArgb();
}
}
}
}
finally
{
if (data != null) image.UnlockBits(data);
}
}
It's a shame GDI/GDI+ doesn't do this already, but that would be sensible, wouldn't it? :)
If you aren't able to use an unsafe context, you could easily use the same logic with Bitmap.GetPixel and Bitmap.SetPixel, though it will be significantly slower.
Im making a little app to display the pictures of guests as they scan their cards.
But i want to to display blank green or red (green if the guest exists without a photo and red if they dont exist)
But i cant figure out how to create a blank colour image.
Bitmap bmRed = new Bitmap(imgbox.Width, imgbox.Height, PixelFormat.Format24bppRgb);
imgbox.Image = bmRed;
Thats the code i have at the moment and it just makes the box black.
imgbox is a PictureBox
Don't use an image - set the BackColor property of the PictureBox:
imgBox.BackColor = Color.Red;
To prevent null pointer exception, create a blank bmp
myPicBox.Image = new Bitmap(myPicBox.Width, myPicBox.Height);
Graphics graphics = Graphics.FromImage(myPicBox.Image);
Brush brush = new SolidBrush(Color.Gray);
graphics.FillRectangle(brush, new System.Drawing.Rectangle(0, 0, myPicBox.Width, myPicBox.Height));
How about setting the background color directly?
imgbox.BackColor = Color.Red;
create a graphics context and draw using it.
using(Graphics g = Graphics.FromImage(bmRed))
{
g.FillRectangle(new SolidBrush(Color.Red),0,0,imgbox.Width,imgbox.Height);
}
Single statement:
Graphics.FromImage(PicBox.Image=new bitmap(PicBox.Size)).FillRectangle(Brushes.Red,new Rectangle (Point.EMPTY,PicBox.Size));
If I use TextRenderer.DrawText() using the Graphics object provided in the OnPaintBackground my text looks perfect. If I create my own Bitmap and use the Graphics object obtained from my Bitmap my text looks terrible. It looks like it is anti-aliasing the text using black, not the bitmap's background color. I can avoid this problem if I use Graphics.DrawString(), but this method has horrible kerning problems. What should I do? How can I get TextRenderer.DrawText() to anti-alias properly using the Bitmap's contents?
Looks terrible:
Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Red);
TextFormatFlags tf = TextFormatFlags.Left;
TextRenderer.DrawText(g, #"C:\Development\Testing\blag", font, clip, Color.White,
Color.Transparent, tf);
}
Looks good, but I want to render this onto a bitmap, NOT onto the control's surface:
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.Clear(Color.Red);
TextFormatFlags tf = TextFormatFlags.Left;
TextRenderer.DrawText(e.Graphics, #"C:\Development\Testing\blag", font, clip,
Color.White, Color.Transparent, tf);
}
What is the difference?
The answer is not to use TextRenderer. TextRenderer is a wrapper for the GDI (not GDI+) implementation of text rendering, which has lots of features, but doesn't interoperate well with in-memory DCs as you have discovered.
Use Graphics.DrawString & Graphics.MeasureString, but remember to pass it StringFormat.GenericTypographic to get accurate size and positioning.
The reason TextRenderer was introduced initially was that GDI+ didn't support all the complex scripts that GDI's Uniscribe engine did. Over time however GDI+ support for complex scripts has been expanded, and these days there aren't any good reasons left to use TextRenderer (it's not even the faster of the two anymore, in fact quite the opposite it appears).
Really, though, unless you are running into serious, measurable performance issues just use Graphics.DrawString.
I believe the problem is that the clear type text rendering doesn't work if the background is transparent. A few possible solutions.
Option 1. Fill the background of your bitmap with a color.
If you do this (as Tim Robinson did above in his code example by using g.Clear(Color.Red)) clear type will do the right thing. But your bitmap won't be completely transparent which might not be acceptable. If you use Graphics.MeasureText, you can fill just the rectangle around your text, if you like.
Option 2. Set TextRenderingHint = TextRenderingHintAntiAliasGridFit
This appears to turn off clear type. The text will be rendered at a lower quality than clear type on a background, but much better than the mess clear type on no background creates.
Option 3. Fill the text rectangle with white, draw the text and then find all the non-text pixels and put them back to transparent.
using (Bitmap bmp = new Bitmap(someWidth, someHeight))
{
using (Graphics g = Graphics.FromImage(bmp))
{
// figure out where our text will go
Point textPoint = new Point(someX, someY);
Size textSize = g.MeasureString(someText, someFont).ToSize();
Rectangle textRect = new Rectangle(textPoint, textSize);
// fill that rect with white
g.FillRectangle(Brushes.White, textRect);
// draw the text
g.DrawString(someText, someFont, Brushes.Black, textPoint);
// set any pure white pixels back to transparent
for (int x = textRect.Left; x <= textRect.Left + textRect.Width; x++)
{
for (int y = textRect.Top; y <= textRect.Top + textRect.Height; y++)
{
Color c = bmp.GetPixel(x, y);
if (c.A == 255 && c.R == 255 && c.G == 255 && c.B == 255)
{
bmp.SetPixel(x, y, Color.Transparent);
}
}
}
}
}
I know, it's a horrible hack, but it appears to work.
The answer is to use a BuffersGraphicsContext. This is the same system that .NET uses internally when you set the ControlStyles.OptimizedDoubleBuffer style on a control.
See http://msdn.microsoft.com/en-us/library/b367a457.aspx for more information about double buffering in .NET.
Another possible solution: Draw the whole thing to the screen, bitmap with text on top, and then write some code to 'screen capture' that portion of the screen. Not practical in all cases but you're right, DrawString creates weird text and DrawText onto a bitmap looks horrible.
If your bitmap is not the same size as your display area, it might just be a resizing issue, where .NET scales the bitmap to the display size and you get funny looking text.
Can you test with a bitmap created at the same size as your display area?
Can you post the smallest program that suffers from this problem? I can't reproduce it like this -- the antialiasing looks fine:
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
public class Program
{
public static void Main()
{
Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb);
using (Font font = new Font("Arial", 10, GraphicsUnit.Point))
using (Graphics g = Graphics.FromImage(bmp))
{
Rectangle clip = Rectangle.FromLTRB(0, 0, 100, 100);
g.Clear(Color.Red);
TextFormatFlags tf = TextFormatFlags.Left;
TextRenderer.DrawText(g, #"C:\Development\Testing\blag", font, clip, Color.White, Color.Transparent, tf);
}
Form form = new Form();
form.BackgroundImage = bmp;
Application.Run(form);
}
}