NinthDesertDude Posted April 3, 2016 Share Posted April 3, 2016 (edited) Download here (too large to attach): Brush Factory (Beta) Brush Factory allows you to draw brushes with basic dynamics, like randomized scaling, rotation, and transparency. Example below. It works well on images that are absolutely opaque. But, if the image has any transparency at all, it begins to cause strange errors. Consider this. Notice the obvious gray border around the white brush stroke on the second image. That shouldn't happen. These colors are actually darker (they are not white anymore, and it's not the alpha making it look gray there). This exact type of error occurs when you simply open/close the dialog, undo/redo, and draw over transparent regions. It can be hard to see esp. when simply opening and OKing the dialog, but it's there, so don't use Brush Factory with transparency. Anyway, I'd love to get help with fixing this GDI+ issue. I've been here: http://stackoverflow.com/questions/6615340/color-value-with-alpha-of-zero-shows-up-as-black http://stackoverflow.com/questions/15956424/gdi-bug-anti-aliasing-white-on-transparency http://stackoverflow.com/questions/1861305/c-sharp-resized-images-have-black-borders http://stackoverflow.com/questions/1890605/ghost-borders-ringing-when-resizing-in-gdi http://stackoverflow.com/questions/2024757/how-to-solve-grayish-frame-issue-when-scaling-a-bitmap-using-gdi http://stackoverflow.com/questions/6744067/drawimage-function-over-winforms-does-not-work-correctly http://www.codeproject.com/Articles/14884/BorderBug I read that there is a Border Bug where resized images include pixels outside the image, which are assumed Black, and involve them in the calculation (resulting in blackened borders). I read that creating a new bitmap automatically premultiplies each pixel by its alpha, so I've tried a tested workaround that locks the bits and copies them over (to avoid multiplying by alpha), but this doesn't solve my issue. I've tried all the solutions presented here, which I summarize below: - Any combination of InterpolationMode.NearestNeighbor, SmoothingMode.None, and PixelOffsetMode.Half. - Clearing with a transparent color, Color.White, and Color.Black, along with the above attempts as well. - Workarounds for cloning bitmaps, sometimes in conjunction with other workarounds above. - Using an ImageAttributes object and setting WrapMode to TileFlipXY, sometimes in conjunction with above workarounds. I've attached the source code along with the .dll. Please try to help if you can! Otherwise, I'll have no choice but to port the entire thing to another graphics library that doesn't have these silly errors and it'll take ages. Feel free to point out any side bugs so I can fix them too; I know a few of them already but am focusing on this now. Thanks. Download here (too large to attach): Brush Factory (Beta) Edited December 9, 2016 by AnthonyScoffler Replaced dead links with permanent links on google drive 2 Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 3, 2016 Share Posted April 3, 2016 This looks like a premultiplied alpha problem. As in, something is premultiplied when it shouldn't be, or vice versa. What are you using to draw the brush stroke? Are the pixel values of the brush all white, with varying alpha? ("white" meaning red=255,green=255,blue=255) Or are they premultiplied, such that A=R=G=B, but they are less than 255? You might play around with using ColorBgra.Convert[From|To]PremultipliedAlpha(). This will involve locking your bitmap, then casting the BitmapData.Scan0 to a ColorBgra*. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
NinthDesertDude Posted April 4, 2016 Author Share Posted April 4, 2016 (edited) This looks like a premultiplied alpha problem. Yep! I've been thinking that for awhile now. It's peculiar because all images are kept as 32bppARGB. The premultiplied format is 32bppPARGB, so I'm not sure why they're being premultiplied. I read that images are premultiplied when they're created (that the alpha blend function from C++ was incorporated like that in the C# version of GDI+, which I would call a design flaw). What are you using to draw the brush stroke? //Something along the lines of this: Graphics g = new Graphics.FromImage(the_entire_image); g.DrawImage(the_brush_image, xPos, yPos); Are the pixel values of the brush all white, with varying alpha? No, which is why I think it's premultiplied alpha causing problems (it's the main symptom). The colors are darker and the more I draw, the darker they get (all the way to black). The alpha increases with each brush stroke, of course, but that's fully expected. You might play around with using ColorBgra.Convert[From|To]PremultipliedAlpha() I wrote the code correctly, but it doesn't solve my problem. I tried FromPremultipliedAlpha, which tends to increase aliasing. The other is less effective yet. I tried applying it once and twice, to the brush before/after rotation, or both. Here is the code I used. public static unsafe void UndoPremultipliedAlpha(ref Bitmap img) { if (img.PixelFormat != PixelFormat.Format32bppArgb) { return; } BitmapData bmpData = img.LockBits( new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadWrite, img.PixelFormat); //The top left pixel's address, from which the rest can be found. byte* row = (byte*)bmpData.Scan0; //Iterates through each pixel to apply the change. for (int y = 0; y < img.Height; y++) { for (int x = 0; x < img.Width; x++) { int pos = y * bmpData.Stride + x * 4; //Gets the pixel color, corrects it, and applies changes. ColorBgra col = ColorBgra.FromBgra( row[pos], row[pos + 1], row[pos + 2], row[pos + 3]); col = col.ConvertFromPremultipliedAlpha(); row[pos] = col.B; row[pos + 1] = col.G; row[pos + 2] = col.R; row[pos + 3] = col.A; } } img.UnlockBits(bmpData); } Edited April 9, 2016 by AnthonyScoffler Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.