Erase to Transparency - c#

So I'm trying to make a 32x32 block back to transparent but everytime I try to set it to transparent it just keeps what ever is already there, I want to erase what's on the image to transparent, here's my code I tried.
public Bitmap erase_tile(Bitmap bitmap, int x, int y)
{
Graphics device = Graphics.FromImage(bitmap);
Brush brush = new SolidBrush(Color.FromArgb(0, Color.White));
device.FillRectangle(brush, new Rectangle(x * 32, y * 32, 32, 32));
return bitmap;
}

All transparency is going to be accomplished via functionality on the Bitmap class. The Graphics class is geared toward drawing, and drawing Color.Transparent is essentially a no-op.
You can use Bitmap.SetPixel() with Color.Transparent to set individual pixels.
Or you can do something like this, where you use Graphics to paint a dummy color which you will then instruct the bitmap to use as the transparent color.
using (var graphics = Graphics.FromImage(bmp))
{
graphics.FillRectangle(Brushes.Red, 0, 0, 64, 64);
graphics.FillRectangle(Brushes.Magenta, 16, 16, 32, 32);
}
bmp.MakeTransparent(Color.Magenta);

While I was searching for the same solution, I wasn't able to find the exact answer, so after some experiments I came across SetCompositingMode and it made the trick (refer to Using Compositing Mode to Control Alpha Blending for full details).
Here is a working code in C++ to demonstrate the approach (it needs some tuning to be re-used in C#):
void SetTransparent(Gdiplus::Image* image, IN INT x, IN INT y, IN INT width, IN INT height)
{
Gdiplus::Graphics graph(image);
graph.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
Gdiplus::SolidBrush transparent(0);
graph.FillRectangle(&transparent, x, y, width, height);
}

Related

Get Rectangle's color after FillRectangles

How can I get the color of a Rectangle after creating it?
I'm using this code to create them :
SolidBrush sb = new SolidBrush(Color.Red);
Graphics g = panel1.CreateGraphics();
Rectangle[] rects = { new Rectangle(0, 0, 15, 15), new Rectangle(16, 16, 15, 15), new Rectangle(16, 0, 15, 15), new Rectangle(0, 16, 15, 15) };
g.FillRectangles(sb,rects);
And now I want to get the color of the 3rd rectangle
rects[2] = ....
Is it possible to get this? And it should return Color.Red.
You can find the position of center pixel of your rectangle, then you can use GetPixel method to get information about such as color.
Color pixelColor = myBitmap.GetPixel(50, 50);
The FillRectangles draws the rectangles with the brush (color) you passed. There is no reference from any rectangle to any color. It just executes a drawing command to a Graphics object.
A Rectangle doesn't have a color. So, nope, you cannot. If you explain why you would need it, there might be other solutions to get the desired results.
If you store the image, you only store the result (i.e. the grid of pixels). There is no information on how you created this image.
E.g. based on the bitmap image alone, you cannot differentiate between two adjacent squares (new Rectangle(0, 0, 15, 15), new Rectangle(15, 0, 15, 15) and a single rectangle that has twice the width (new Rectangle(0, 0, 30, 15)).
So the short answer to your question is no, you cannot do that.
Unless you store your information (the rectangles you drew) separately and then use that to find the appropriate pixel on the image (and this only works in simple cases - if you overlapped an earlier rectangle, it's going to be impossible)
But if you're going to store the rectangle information anyway, you might as well store its color and then you don't need to reverse engineer the image anymore. So the answer remains that you cannot do this based on an image alone.
You could write a new Class, for example:
class ColorRectangles
{
public Color color;
public Rectangle[] rects;
public ColorRectangle(Rectangle[] rects, Color color) : base(x, y, width, height)
{
this.color = color
this.rects = rects;
}
}
and then set the Rectangles Color with g.FillRectangles(new SolidBrush(Rectangle.color), Rectangle.rects);

Remove the black background color of a bitmap

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 :-)

Graphics.DrawImage alternatives for large images

I am trying to draw a crosshair ("plus sign") with inverted colors over an image to show the location of a selected point within the image. This is how I do it:
private static void DrawInvertedCrosshair(Graphics g, Image img, PointF location, float length, float width)
{
float halfLength = length / 2f;
float halfWidth = width / 2f;
Rectangle absHorizRect = Rectangle.Round(new RectangleF(location.X - halfLength, location.Y - halfWidth, length, width));
Rectangle absVertRect = Rectangle.Round(new RectangleF(location.X - halfWidth, location.Y - halfLength, width, length));
ImageAttributes attributes = new ImageAttributes();
float[][] invertMatrix =
{
new float[] {-1, 0, 0, 0, 0 },
new float[] { 0, -1, 0, 0, 0 },
new float[] { 0, 0, -1, 0, 0 },
new float[] { 0, 0, 0, 1, 0 },
new float[] { 1, 1, 1, 0, 1 }
};
ColorMatrix matrix = new ColorMatrix(invertMatrix);
attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(img, absHorizRect, absHorizRect.X, absHorizRect.Y, absHorizRect.Width, absHorizRect.Height, GraphicsUnit.Pixel, attributes);
g.DrawImage(img, absVertRect, absVertRect.X, absVertRect.Y, absVertRect.Width, absVertRect.Height, GraphicsUnit.Pixel, attributes);
}
It works as expected, however, it is really slow. I want the user to be able to move the selected location around with their mouse by setting the location to the cursor's location whenever it moves. Unfortunately, on my computer, it can update only around once per second for big images.
So, I am looking for an alternative to using Graphics.DrawImage to invert a region of an image. Are there any ways to do this with speeds proportional to the selected region area rather than the entire image area?
Sounds to me you are focusing on the wrong problem. Painting the image is slow, not painting the "cross-hairs".
Large images can certainly be very expensive when you don't help. And System.Drawing makes it very easy to not help. Two basic things you want to do to make the image paint faster, getting it more than 20 times faster is quite achievable:
avoid forcing the image painting code to rescale the image. Instead do it just once so the image can be drawn directly one-to-one without any rescaling. Best time to do so is when you load the image. Possibly again in the control's Resize event handler.
pay attention to the pixel format of the image. The fastest one by a long shot is the pixel format that's directly compatible with the way the image needs to be stored in the video adapter. So the image data can be directly copied to video RAM without having to adjust each individual pixel. That format is PixelFormat.Format32bppPArgb on 99% of all modern machines. Makes a huge difference, it is ten times faster than all the other ones.
A simple helper method that accomplishes both without otherwise dealing with the aspect ratio:
private static Bitmap Resample(Image img, Size size) {
var bmp = new Bitmap(size.Width, size.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(img, new Rectangle(Point.Empty, size));
}
return bmp;
}
Draw the image once on Graphics g, then draw the crosshair on Graphics g directly instead of the image. You can optionally keep track of the places the user clicked so as to save them either in the image or elsewhere as needed.

How to create a simple glass effect

I am currently painting a light blue, partly transparent overlay over owner-drawn objects to indicate certain state. It's OK but I thought that it would be even nicer if I could at some sort of glass effect to further establish the idea that the particular object has "something" overlaid over the top of it.
I thought that some glass streaks, for example, in addition to the blue transparency would lend a nice effect.
I've Googled around for GDI+ (and others) algorithms to do simple things painting like this but have come up empty. Links to any (fairly simple) algorithms in any language would be appreciated. I prefer .NET but can figure out the painting from pseudo-code on up.
Sorry, shoul've also specified that I need to target WinXP and using .NET version 2.0 - So unable to use WPF or Vista/Win7 goodies.
I've not done this myself but, have used codeproject source to render a sample...Try this:
http://www.codeproject.com/KB/GDI-plus/Image-Glass-Reflection.aspx
public static Image DrawReflection(Image _Image, Color _BackgroundColor, int _Reflectivity)
{
// Calculate the size of the new image
int height = (int)(_Image.Height + (_Image.Height * ((float)_Reflectivity / 255)));
Bitmap newImage = new Bitmap(_Image.Width, height, PixelFormat.Format24bppRgb);
newImage.SetResolution(_Image.HorizontalResolution, _Image.VerticalResolution);
using (Graphics graphics = Graphics.FromImage(newImage))
{
// Initialize main graphics buffer
graphics.Clear(_BackgroundColor);
graphics.DrawImage(_Image, new Point(0, 0));
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Rectangle destinationRectangle = new Rectangle(0, _Image.Size.Height,
_Image.Size.Width, _Image.Size.Height);
// Prepare the reflected image
int reflectionHeight = (_Image.Height * _Reflectivity) / 255;
Image reflectedImage = new Bitmap(_Image.Width, reflectionHeight);
// Draw just the reflection on a second graphics buffer
using (Graphics gReflection = Graphics.FromImage(reflectedImage))
{
gReflection.DrawImage(_Image,
new Rectangle(0, 0, reflectedImage.Width, reflectedImage.Height),
0, _Image.Height - reflectedImage.Height, reflectedImage.Width,
reflectedImage.Height, GraphicsUnit.Pixel);
}
reflectedImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
Rectangle imageRectangle =
new Rectangle(destinationRectangle.X, destinationRectangle.Y,
destinationRectangle.Width,
(destinationRectangle.Height * _Reflectivity) / 255);
// Draw the image on the original graphics
graphics.DrawImage(reflectedImage, imageRectangle);
// Finish the reflection using a gradiend brush
LinearGradientBrush brush = new LinearGradientBrush(imageRectangle,
Color.FromArgb(255 - _Reflectivity, _BackgroundColor),
_BackgroundColor, 90, false);
graphics.FillRectangle(brush, imageRectangle);
}
return newImage;
}
I was actually able to achieve a basic glass effect by overlaying my image with a rectangle about one third the size of the image below that contains a gradient fill of white that starts at 25% opacity and goes to 75% opacity. This is single bit of painting produces a glassy "streak" that I was happy with. The same idea could be repeated a number of times with a variety of rect widths to produce several "streaks" that will give the illusion of a glass overlay.
You could try the Aero Glass function, if you are using Vista or Windows 7.
These might be helpful:
http://msdn.microsoft.com/en-us/library/aa969537%28VS.85%29.aspx#blurbehind
http://msdn.microsoft.com/en-us/library/ms748975.aspx

TextRenderer.DrawText in Bitmap vs OnPaintBackground

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);
}
}

Categories

Resources