I am currently working on a PDF generator. I need to first draw the background image and then draw a dark layer with 85 percent of transparency. I can draw it nicely, but when I want to draw two images after that, those images also get the transparency and that is not what I want.
XBrush brush = new XSolidBrush(XColor.FromArgb((int)(.85 * 255), 255, 255, 255));
DrawPageBackground(gfx,backgroundImage,page.Width.Value,page.Height.Value);
gfx.DrawRectangle(b,0,0,gfx.PageSize.Width,gfx.PageSize.Height );
gfx.DrawImage(otherImage,25,25);
Is there any reason why I can't just draw the images without the transparency? Is it just something simple that I am not doing right now?
Thanks.
I did successfully fix this issue by saving the XGraphicsState before the drawing of the background with darkened layer. After the drawing I used the XGraphicsState to restore and draw the images without any transparency. See the following piece of code.
XGraphicsState state = gfx.Save();
XBrush brush = new XSolidBrush(XColor.FromArgb((int)(.85 * 255), 255, 255, 255));
DrawPageBackground(gfx,backgroundImage,page.Width.Value,page.Height.Value);
gfx.DrawRectangle(b,0,0,gfx.PageSize.Width,gfx.PageSize.Height );
gfx.DrawImage(otherImage,25,25);
gfx.Restore(state);
The method DrawPageBackground:
private static void DrawPageBackground(XGraphics gfx, XImage image, double pageWidth, double pageHeight)
{
if (image.Size.Width > pageWidth)
gfx.DrawImage(image, CalculateDiffImageCenterToPageCenter(image,pageWidth), 0, CalculateBackgroundImageWidth(image,pageHeight), pageHeight);
else
gfx.DrawImage(image, 0, 0, CalculateBackgroundImageWidth(image, pageHeight),pageHeight);
}
This is just an helper method to draw the background image to the right scale and center it.
I confirm what I wrote in a comment: it is a bug that the transparency set for a brush was also applied to the images.
With version 1.50.3915-beta2 published earlier this week, this bug was fixed.
Using Save() and Restore() is a suitable workaround for earlier versions, but with the latest version this workaround is no longer needed.
Related
I'm implementing image scaling function. When I use HighQualityBicubic interpolation mode (which is preferred from quality perspective) I'm getting black background on resized image(only in rectangle of source image. In padding area of destination rectangle it is still transparent).
Input image is bmp with transparent background.
Output is also bmp 32 bpp.
Interesting thing when I change InterpolationMode to NearestNeighbor with some input images background is preserved as transparent after resizing and with another inputs it doesn't help.
What I'm doing wrong?
public Bitmap DrawScaledImage(Image img, int width, int height, int scaledWidth, int scaledHeight)
{
var resultImg = new Bitmap(width, height, PixelFormat.Format32bppArgb);
resultImg.SetResolution(img.HorizontalResolution, img.VerticalResolution);
using (Graphics grPhoto = Graphics.FromImage(resultImg))
{
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
grPhoto.DrawImage(img,
new Rectangle(0, 0, scaledWidth, scaledHeight), //0;0 - image is drawn in left top corner
new Rectangle(0, 0, img.Width, img.Height),
GraphicsUnit.Pixel);
}
return resultImg;
}
Update
Found funny thing:
I've picked random transparent pixel in source image and took its ARGB in debug: it is ARGB=(255, 0, 0, 0) - pretty clear
Then I've picked same pixel which was intended to be transparent in destination image after scaling and it is still ARGB=(255, 0, 0, 0) but in Paint.Net it is displayed as black.
Then I've picked another random pixel from destination image that is displayed as transparent in Paint.Net and its ARGB schema is ARGB=(0, 0, 0, 0) - what?
So maybe stupid question - why for 1st pic transparent pixel is [255;0;0;0] but for 2nd [255;0;0;0] means black and transparent is [0;0;0;0].
it looks like something went down-under. and Alfa-channel reversed its meaning from 255- transparent to 255-opaque after scaling. Any ideas?
So I found that reason is some buggy behavior saving file in BMP format after resizing with InterpolationMode = InterpolationMode.HighQualityBicubic. Here are cases for reproduction:
1. If I create brand new image with pixel format PixelFormat.Format32bppArgb make some program manipulations with pixels (e.g. paint some line) and save as bmp - no problem at all, transparency is saved and visible in Paint.Net.
2. If I open existing bmp image already with transparency scale it up/down with Graphics.DrawImage method all seems good in debug. Transparent pixels have Alpha = 0. Then I save that image as bmp. Then Open it again in code like Image.FromFile("resized.bmp") and its pixel format is not same as it was before saving: it is now Format32bppRgb however saved image had Format32bppArgb. On opened image transparent pixels now have Alpha = 255. This is the cause why opening in Paint.Net I see black instead of transparent after resizing.
I found 2 workarounds to preserve transparency after resizing:
1. Change InterpolationMode from InterpolationMode.HighQualityBicubic to InterpolationMode.NearestNeighbor. This solves transparency issue but not acceptable due to low quality method.
2. Save resized image as .png. This saves correct Alpha values and reopening image preserves it still. So it is my solution to refuse using BMP file format at all.
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'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
We have a winforms app (Framework v4) that shows an image (via PictureBox) on the screen and allows selection of a rectangular region on that image. During and after the image selection, we show the boundary of the selected area. This is currently done via a DrawRectangle call.
The problem is how to choose the color of this rectangle. Regardless of the chosen color, it is always possible that it will blend into the background (the image). Microsoft paint handles this very well by reversing the colors dynamically on the "selection rectangle". This suits very well to our application, but I have no idea how to do it in winforms.
I also looked around to see if there's a dash-style that would allow two colors to be used (so that I could specify black and white as these colors, making it visible no matter what the background colors are), but I could not find anything of this sort.
Thanks in advance for all the help.
you can use ControlPaint methods to paint a reversible rectangle/frame
ControlPaint.FillReversibleRectangle MSDN
and
ControlPaint.DrawReversibleFrame MSDN
here is a little pseudo code method example
private void DrawReversibleRectangle(int x, int y) {
// Hide the previous rectangle by calling the methods with the same parameters.
var rect = GetSelectionRectangle(this.PointToScreen(this.reversibleRectStartPoint), this.PointToScreen(this.reversibleRectEndPoint));
ControlPaint.FillReversibleRectangle(rect, Color.Black);
ControlPaint.DrawReversibleFrame(rect, Color.Black, FrameStyle.Dashed);
this.reversibleRectEndPoint = new Point(x, y);
// Draw the new rectangle by calling
rect = GetSelectionRectangle(this.PointToScreen(this.reversibleRectStartPoint), this.PointToScreen(this.reversibleRectEndPoint));
ControlPaint.FillReversibleRectangle(rect, Color.Black);
ControlPaint.DrawReversibleFrame(rect, Color.Black, FrameStyle.Dashed);
}
You mention an alternative solution would be to draw a dashed line in two colors, black and white, so that it would be visible on any background.
Fake this by drawing solid line in one color (e.g. black), then draw a dashed line in the other color (e.g. white).
Idea and code from: http://csharphelper.com/blog/2012/09/draw-two-colored-dashed-lines-that-are-visible-on-any-background-in-c/
using (Pen pen1 = new Pen(Color.Black, 2))
{
e.Graphics.DrawRectangle(pen1, rect);
}
using (Pen pen2 = new Pen(Color.White, 2))
{
pen2.DashPattern = new float[] { 5, 5 };
e.Graphics.DrawRectangle(pen2, rect);
}