I'm having difficulties with System.Drawing bugs in C#. I've been trying to diagnose it for 5 months.
My program basically does photoshop brushes. 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.
The gray border on every line, and highly visible in the second picture, is an alpha artifact. If I draw over the same region, I can get the color as dark as (127, 127, 127) in RGB, which is perfect gray, and I don't think it's a coincidence.
This error occurs when opening/closing the dialog, undo/redo, and drawing over transparent regions.
Anyway, I'd love to get help with fixing this GDI+ issue. I've been here:
Color value with alpha of zero shows up as black
GDI+ Bug: Anti-Aliasing White On Transparency
C# Resized images have black borders
Ghost-borders ('ringing') when resizing in GDI+
How to solve grayish frame issue when Scaling a bitmap using GDI+
DrawImage() function over WinForms does not work correctly
http://www.codeproject.com/Articles/14884/BorderBug
Possibility: there is a Border Bug where resized images include pixels outside the image, which are assumed Black, and they're involved in the calculation (resulting in blackened borders)
But that doesn't explain how copying and writing the image over the source causes this effect.
Possibility: creating a new bitmap automatically premultiplies each pixel by its alpha.
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.
Using Color.ConvertFromPremultipliedAlpha and Color.ConvertToPremultipliedAlpha, with a definitely bad effect.
Here is the main file, which includes all the relevant code.
A tiny bit more discussion on my original post off-site.
Related
I'm using the ImageSharp library (available on NuGet as 1.0.0-beta0001) for image generation and manipulation in .NET Core 2.0, and have encountered something that I can't seem to find a way around.
Using this example as a basis, I'm trying to round the corners of a white image. What I'm finding is that the transparent IPath corners being punched out end up antialiasing dark, as if the transparent "color" were black or gray (whereas it really shouldn't be considered any color at all).
Here's the upper-right quadrant of the image to demonstrate what I mean:
I tried all the options for PixelBlenderMode at this part of the code and none have produced what I'm after:
img.Mutate(x => x.Fill(Rgba32.Transparent, corners, new GraphicsOptions(true)
{
BlenderMode = PixelBlenderMode.Src // enforces that any part of this shape that has color is punched out of the background
}));
The problem you are seeing is the consequence of incorrect pixel sampling. Pixels with low transparency were affecting the average value of the individual components too much.
Quoting some of the text from below link as an example as it explains the issues well.
A pixel in the white area has a color of RGBA(1.00, 1.00, 1.00, 1.00). A pixel in the red area has color RGBA(1.00, 0.00, 0.00, 0.10). If you average those numbers together, you get (1.00, 0.50, 0.50, 0.55). That’s the color a typical pixel on the boundary would have if you resize the image in the straightforward way.
But that color – a half-opaque light red – is the wrong color! You can see a faint red halo around the border, which shouldn’t be there:
http://entropymine.com/imageworsener/resizealpha/
The fix is simple, values must be premultiplied by their alpha component first before sampling.
However, I don't know whether this issue is happening due to resizing the generated image (fixed in the latest nightlies), or during the fill process. I would need to see sample code first to be sure as I do not know how you are providing the source image.
This has become a serious blocker for a program I'm working on to manipulate images that have Alpha channels.
Many of the images I have contain color information where an Alpha channel is completely transparent, and yet as soon as I try to load them into System.Drawing.Graphics, it changes anything with of Alpha of 0, into Black with an Alpha of 0.
Here is a basic sample of the issue.
I have looked around trying to find a reason, answer, or workaround, but I haven't found anything that even alludes to this issue.
Any help would be appreciated at this point.
var myTestTransparentColor = Color.FromArgb(0, 255, 128, 64);
var image = new Bitmap(135, 135, PixelFormat.Format32bppArgb);
using (var g = Graphics.FromImage(image))
{
g.Clear(myTestTransparentColor);
}
var color = image.GetPixel(0, 0);
Debug.Assert(color == myTestTransparentColor, "channels must match original");
EDIT:
After further testing I don't really see a way ahead by using System.Drawing.Graphics, so my only solution which is not really an answer, is to avoid System.Drawing.Graphics entirely. Looking through my code, it looks like I can avoid it.
Its just after years of using System.Drawing.Graphics for drawing shapes, planting text over images, I find it irritating for System.Drawing.Graphics to have a significant drawback like this.
I still would like to know if I can use System.Drawing.Graphics and keep my ARGB intact, but I guess I can live without it for now.
I think Vincent Povirk has answered my question appropriately here: Drawing PixelFormat32bppPARGB images with GDI+ uses conventional formula instead of premultiplied one
"The format of your foreground image doesn't matter (given that it has alpha) because you're setting it to a Gdiplus::Color. Color values are defined as non-premultiplied, so gdiplus multiplies the components by the alpha value when it clears the foreground image. The alternative would be for Color values to have different meaning depending on the format of the render target, and that way lies madness."
"If you really want this level of control over the rendering, you'll have to lock the bitmap bits and do it yourself."
So, I am doing it myself.
I have a difficulty as I am trying to render a character with a specific font style to the bitmap image (black and white). My question is the font is basically black and white and I am writing the character in black (against white background), however when I convert it to bitmap image I get a coloured thin outline around the bindery of my character.
Can anyone tell me where that grey color comes from while I am writing it with black color and how can i get ONLY black and white pixels?
The pixels that aren't completely black or completely white are the result of anti-aliasing. Anti-aliasing is used by default since everyone who doesn't know about it probably wants it.
I suggest two alternatives. One, create your bitmap with a one bit per pixel format, which will not give anti-aliasing a chance. Second, you can go through the resulting image after the text has been drawn pixel by pixel and adjust each pixel to either black or white based on a threshold. I.e. if the picture is darker than half then it's black, otherwise it's white. e.g. if (red+green+blue > 383) set_pixel_white() else set_pixel_black(); But you'll need be ready for some rather funny results. You may need to play with the thresholds.
PS there's a better solution, you can tweak anti-aliasing. MSDN You'll set your rendering to System.Drawing.Text.TextRenderingHint.SingleBitPerPixel or something that suits you.
I'm having a problem with masking an image in WinRT. Basically, what I need to do, is to cut out a puzzle shape out of the base image. I have the puzzle shape as a PNG black and white image, where the shape itself is white and the background black and also as a transparent shape of the puzzle piece. This is actually a port of a iOS app, where they used a CGContextClipToMask with the black and white mask to cut out the puzzle piece.
I tried using the Blit from the WriteableBitmapEx to mask the images, but I never achieved the result I wanted, the closest I got was the correctly cut out shape, but with a black background, instead of nothing. What is the correct way of cutting out this shape? Thanks for all the answers!
Indeed, WinRT/XAML in Windows 8 does not have an OpacityMask implementation of other XAML frameworks. You could use WriteableBitmap to manipulate the pixels, but it's a bit slow, especially on ARM devices. A faster solution is to use Direct2D, which has a FillOpacityMask method built right in. Since SharpDX wraps it nicely for .NET you can do that with C# too.
I don't have code, however the simplest case would be to just open PNG file in Photoshop/GIMP/any online transparency tool and just map black pixels alpha to zero.
Another example would be doing that in code directly,
WriteableBitmapEx has function to change each pixel,
all you have to do, is loop through all black pixels and change alpha to 0.
With a mobile device I take a picture of a flat light object on a dark surface. (for instance a coupon clipped out of a newspaper).
The image is then run through a brightness/contrast filter. If it is too dark, vital components are left out. If it is too bright, the writing on the coupon is lost.
This image is then converted into a bitonal image. Any pixel that is 50% or more dark is converted to black, everything else is white. (done)
I am left with a skewed bitonal image (think of a white trapezoid inside a larger rectangle with a black background).
I need to figure out how to crop the image - which when it's on a black background is easier than when it's on a white background. Then, I have to de-skew the image so it is rectangular instead of trapezoidal, while attempting to preserve aspect.
The end result should be a nicely cropped, bitonal, readable image of the coupon.
To crop your image, you can use the LockBits method and scan through all your pixels to find the first pixel with content from the top, left, right and bottom, respectively. How to use LockBits is described nicely here: https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx
Assuming your image is not rotated, and that the skewing comes from the camera held at an angle against the table where the coupon is being photographed, you should now have a skewed image of the coupon, fitting perfectly within the bounds of the cropped bitmap. You should also know the four corners of the trapezoid.
"Undistorting" an image is not as easy as you might think though. However, good people have solved this problem and you can probably port their code to your own use. Here is a link I used to explore this problem in a similar case some time ago:
http://ryoushin.com/cmerighi/en-US/2007-10-29_61/Image_Distortion_Enhancements
I also have some code stored somewhere if you can't make any sense of what you find.