|
Hi all,
I have tried the code below, and it fails. Can someone explain why, and how do I overcome this problem?
// Create new bitmap and Graphics: Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb); Graphics g = Graphics.FromImage(bmp); g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; // Fill a rectangle with color "{Name=d524378, ARGB=(13, 82, 67, 120)}": Color testColor = Color.FromArgb(223495032); g.FillRectangle(new SolidBrush(testColor), new Rectangle(20, 20, 20, 20)); g.Dispose(); // Read a pixel inside the filled rectangle: Color resultColor = bmp.GetPixel(28, 28); // Check if the color of that pixel is "{Name=d524378, ARGB=(13, 82, 67, 120)}": System.Diagnostics.Debug.Assert(resultColor == testColor); // Fails! the color is "{Name=d4e3a75, ARGB=(13, 78, 58, 117)}"
Thanks in advance, Yoav | | Yoav HaCohen | Two approaches. The standard on is to keep a List<Region> that you initialize with regions created from new Region(GraphicsPath) where the GraphicsPath instance contains the graphics object that you paint on the bitmap. That GraphicsPath instance is also suitable for actually painting the object on the bitmap. Keep the List<> in painting Z-order, then find out later which particular object is exposed at a certain location with the Region.IsVisible() method, testing each region in the list in succession. The last one that returns True is the one you're looking for. Beware that curved paths are approximated with small rectangles, that shouldn't be an issue for regular mouse hit testing, you are probably painting with anti-aliasing on anyway.
The other approach is decoupling the shape ID from the shape color. Keep a second bitmap around in which you paint the exact same objects, now using normalized color values without alpha and no anti-aliasing.
Hans Passant.- Marked As Answer byZhi-Xin YeMSFT, ModeratorFriday, October 10, 2008 11:51 AM
-
| | nobugz | insert this line before g.FillRectangle:
g.Clear(Color.Black);
Check the result and then replace Color.Black with Color.White. | | JohnWein | Thanks, but it does not help.
I think that System.Drawing.Drawing2D.CompositingMode.SourceCopy means that it doesn't matter which color was on the bitmap before.
By the way, if the alpha value is 255, it returns the same color, but I need that the color will be the same even when the alpha value is smaller than 255.
Is it possible that this is a bug it the .NET library (or in GDI+)? | | Yoav HaCohen | It wasn't supposed to do what you want to do. I wanted you to see that you have to account for the background when you have an alpha less than 255. Why would you expect to get the same color when the alpha is less than 255? If you want the same color, use SetPixel. | | JohnWein | You'll find the difference much greater with low alpha values. Google "premultiplied alpha" to understand what's going on. Hans Passant. | | nobugz | OK. Thank you very much. I have rewritten my code to better understanding.
I am understanding what is "premultiplied alpha". I have got the following results: When my tested color is {0,255,255,255} I get {0,0,0,0} in the result. When my tested color is {1,255,255,255} I get {1,255,255,255} in the result. When my tested color is {13,255,255,255} I get {13,255,255,255} in the result. I assume that this is a result of rounding error. Is it possible to avoid the using of premultiplied alpha? I don't use SetPixel() because I need this functionality also for FillEllipse which is hard to implement using SetPixel. By the way, I think that it is supposed to do what I want it to do, because of this line: |
| g.CompositingMode=System.Drawing.Drawing2D.CompositingMode.SourceCopy; |
Which is explained in the .NET documentation: http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.compositingmode(VS.80).aspx Thanks in advance, Yoav | //CreatenewbitmapandGraphics: | | Bitmapbmp=newBitmap(100,100,PixelFormat.Format32bppArgb); | | Graphicsg=Graphics.FromImage(bmp); | | | g.Clear(System.Drawing.Color.Transparent); | | g.CompositingMode=System.Drawing.Drawing2D.CompositingMode.SourceCopy; | | g.CompositingQuality=System.Drawing.Drawing2D.CompositingQuality.AssumeLinear; | | g.InterpolationMode=System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; | | g.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.None; | | | | //Fillarectanglewithcolor"{Name=d524378,ARGB=(13,255,255,255)}": | | ColortestColor=Color.FromArgb(13,255,255,255); | | ColornearestColor=g.GetNearestColor(testColor); | | SolidBrushbrush=newSolidBrush(nearestColor); | | | g.FillRectangle(brush,newRectangle(0,0,100,100)); | | | System.Diagnostics.Debug.Assert(nearestColor==testColor); | | g.Dispose(); | | //Readapixelinsidethefilledrectangle: | | ColorresultColor=bmp.GetPixel(29,29); | | //Checkifthecolorofthatpixelis"{Name=d524378,ARGB=(13,255,255,255)}": | | System.Diagnostics.Debug.Assert(brush.Color==nearestColor);//OK. | | System.Diagnostics.Debug.Assert(resultColor==brush.Color);//Fails!thecoloris"{ARGB=(13,254,254,254)}" | |
| | Yoav HaCohen | That wasn't a good choice of color to see the effect. Use non-saturated red, green and blue values. I'm not aware of any way to prevent GDI+ from doing this. There's probably another way to do what you're trying to do but you'll have to explain what you're trying to do first. Hans Passant. | | nobugz | I draw a list of shapes (rectangles, ellipse, etc.) on a bitmap. Each shape has an unique ID. I want to get a bitmap of the shapes IDs which are on the top. For example, it possible that an ellipse will be drawn over a rectangle and I want to know on each pixel what shape was drawn (the last one).
I have tried to draw the shapes with shapes.Color = Color.FromARGB(shape.ID) . However, the problem of the premultiplied alpha prevent me to do that.
That's why I need the exact colors (because I use their colors numbers as unique IDs).
Any other ideas to implement the IDs map, are welcome.
Thanks in advance, Yoav
- Edited byYoav HaCohen Sunday, October 05, 2008 12:55 PM
-
| | Yoav HaCohen | Two approaches. The standard on is to keep a List<Region> that you initialize with regions created from new Region(GraphicsPath) where the GraphicsPath instance contains the graphics object that you paint on the bitmap. That GraphicsPath instance is also suitable for actually painting the object on the bitmap. Keep the List<> in painting Z-order, then find out later which particular object is exposed at a certain location with the Region.IsVisible() method, testing each region in the list in succession. The last one that returns True is the one you're looking for. Beware that curved paths are approximated with small rectangles, that shouldn't be an issue for regular mouse hit testing, you are probably painting with anti-aliasing on anyway.
The other approach is decoupling the shape ID from the shape color. Keep a second bitmap around in which you paint the exact same objects, now using normalized color values without alpha and no anti-aliasing.
Hans Passant.- Marked As Answer byZhi-Xin YeMSFT, ModeratorFriday, October 10, 2008 11:51 AM
-
| | nobugz |
|