For Bitmap, there is a MakeTransparent method, is there one similar for changing one color to another?
// This sets Color.White to transparent
Bitmap myBitmap = new Bitmap(sr.Stream);
myBitmap.MakeTransparent(System.Drawing.Color.White);
Is there something that can do something like this?
Bitmap myBitmap = new Bitmap(sr.Stream);
myBitmap.ChangeColor(System.Drawing.Color.Black, System.Drawing.Color.Gray);
Through curiosity to Yorye Nathan's comment, This is an extension that I created by modifying http://msdn.microsoft.com/en-GB/library/ms229672(v=vs.90).aspx.
It can turn all pixels in a bitmap from one colour to another.
public static class BitmapExt
{
public static void ChangeColour(this Bitmap bmp, byte inColourR, byte inColourG, byte inColourB, byte outColourR, byte outColourG, byte outColourB)
{
// Specify a pixel format.
PixelFormat pxf = PixelFormat.Format24bppRgb;
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData =
bmp.LockBits(rect, ImageLockMode.ReadWrite,
pxf);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
// int numBytes = bmp.Width * bmp.Height * 3;
int numBytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[numBytes];
// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, numBytes);
// Manipulate the bitmap
for (int counter = 0; counter < rgbValues.Length; counter += 3)
{
if (rgbValues[counter] == inColourR &&
rgbValues[counter + 1] == inColourG &&
rgbValues[counter + 2] == inColourB)
{
rgbValues[counter] = outColourR;
rgbValues[counter + 1] = outColourG;
rgbValues[counter + 2] = outColourB;
}
}
// Copy the RGB values back to the bitmap
Marshal.Copy(rgbValues, 0, ptr, numBytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
}
}
called by bmp.ChangeColour(0,128,0,0,0,0);
Lifting the code from this answer:
public static class BitmapExtensions
{
public static Bitmap ChangeColor(this Bitmap image, Color fromColor, Color toColor)
{
ImageAttributes attributes = new ImageAttributes();
attributes.SetRemapTable(new ColorMap[]
{
new ColorMap()
{
OldColor = fromColor,
NewColor = toColor,
}
}, ColorAdjustType.Bitmap);
using (Graphics g = Graphics.FromImage(image))
{
g.DrawImage(
image,
new Rectangle(Point.Empty, image.Size),
0, 0, image.Width, image.Height,
GraphicsUnit.Pixel,
attributes);
}
return image;
}
}
While I haven't benchmarked it, this should be faster than any solution that's doing GetPixel/SetPixel in a loop. It's also a bit more straightforward.
You can use SetPixel for that:
private void ChangeColor(Bitmap s, System.Drawing.Color source, System.Drawing.Color target)
{
for (int x = 0; x < s.Width; x++)
{
for (int y = 0; y < s.Height; y++)
{
if (s.GetPixel(x, y) == source)
s.SetPixel(x, y, target);
}
}
}
GetPixel and SetPixel are wrappers around gdiplus.dll functions GdipBitmapGetPixel and GdipBitmapSetPixel accordingly
Remarks:
Depending on the format of the bitmap, GdipBitmapGetPixel might not
return the same value as was set by GdipBitmapSetPixel. For example,
if you call GdipBitmapSetPixel on a Bitmap object whose pixel format
is 32bppPARGB, the pixel's RGB components are premultiplied. A
subsequent call to GdipBitmapGetPixel might return a different value
because of rounding. Also, if you call GdipBitmapSetPixel on a Bitmap
object whose color depth is 16 bits per pixel, information could be
lost during the conversion from 32 to 16 bits, and a subsequent call
to GdipBitmapGetPixel might return a different value.
You need a library that provides a way to modify the color space of an image without having to work with pixels. LeadTools has a pretty extensive image library that you can use that supports color space modifications, including swapping colors.
Related
I have a performance problem.
For a insole model configurator, we have a piece to upload and many material images to fusion with the piece image.
I should replace every white pixel on the piece image by the corresponding pixel on the material image.
As the material image is not a mono color, I cant replace simply all white by another mono color.
Image sizes are the same. So I simply take a pixel if the color is not transparent from the piece image and with the same X and Z coordinates on the material images, I take a pixel and I set the pixel of the piece image.
But as there are many materials, it takes 5 minutes today.
Is there a mor optimised way to do this ?
Here is my method :
//For every material image, calls the fusion method below.
foreach (string material in System.IO.Directory.GetFiles(materialsPath))
{
var result = FillWhiteImages(whiteImagesFolder, whiteImagesFolder + "\\" + System.IO.Path.GetFileName(whiteFilePath), material);
}
private static void FusionWhiteImagesWithMaterials(string whiteImageFolder, string file, string materialImageFile)
{
if (file.ToLower().EndsWith(".db") || materialImageFile.ToLower().EndsWith(".db"))
return;
List<CustomPixel> lstColoredPixels = new List<CustomPixel>();
try
{
Bitmap image = new Bitmap(file);
for (int y = 0; y < image.Height; ++y)
{
for (int x = 0; x < image.Width; ++x)
{
if (image.GetPixel(x, y).A > 0)
{
lstColoredPixels.Add(new CustomPixel(x, y));
}
}
}
Bitmap bmpTemp = new Bitmap(materialImageFile);
Bitmap target = new Bitmap(bmpTemp, new Size(image.Size.Width, image.Size.Height));
for (int y = 0; y < target.Height; y++)
{
for (int x = 0; x < target.Width; x++)
{
Color clr = image.GetPixel(x, y);
if (clr.A > 0)
{
if (clr.R > 200 && clr.G > 200 && clr.B > 200)
image.SetPixel(x, y, target.GetPixel(x, y));
else
image.SetPixel(x, y, Color.Gray);
}
}
}
...
image.Save(...);
}
catch (Exception ex)
{
}
}
//I reduced image sizes to keep on the screen. Real image sizes are 500x1240 px.
Replacing the white is one possibility, but it's not a very pretty one. Based on the images you have there, the ideal solution for this is to get the pattern with the correct alpha applied, and then paint the visible black lines over it. This is actually a process with some more steps:
Extract the alpha from the foot shape image
Extract the black lines from the foot shape image
Apply the alpha to the pattern image
Paint the black lines over the alpha-adjusted pattern image
The way I'd approach this is to extract the data of both images as ARGB byte arrays, meaning, each pixel is four bytes, in the order B, G, R, A. Then, for each pixel, we simply copy the alpha byte from the foot shape image into the alpha byte of the pattern image, so you end up with the pattern image, with the transparency of the foot shape applied to it.
Now, in a new byte array of the same size, which starts with pure 00 bytes (meaning, since A,R,G and B are all zero, transparent black), we construct the black line. Pixels can be considered "black" if they're both not white, and visible. So the ideal result, including smooth fades, is to adjust the alpha of this new image to the minimum value of the alpha and the inverse of the brightness. Since it's grayscale, any of the R, G, B will do for brightness. To get the inverse as byte value, we just take (255 - brightness).
Note, if you need to apply this to a load of images, you probably want to extract the bytes, dimensions and stride of the foot pattern image only once in advance, and keep them in variables to give to the alpha-replacing process. In fact, since the black lines image won't change either, a preprocessing step to generate that should speed things up even more.
public static void BakeImages(String whiteFilePath, String materialsFolder, String resultFolder)
{
Int32 width;
Int32 height;
Int32 stride;
// extract bytes of shape & alpha image
Byte[] shapeImageBytes;
using (Bitmap shapeImage = new Bitmap(whiteFilePath))
{
width = shapeImage.Width;
height = shapeImage.Height;
// extract bytes of shape & alpha image
shapeImageBytes = GetImageData(shapeImage, out stride, PixelFormat.Format32bppArgb);
}
using (Bitmap blackImage = ExtractBlackImage(shapeImageBytes, width, height, stride))
{
//For every material image, calls the fusion method below.
foreach (String materialImagePath in Directory.GetFiles(materialsFolder))
{
using (Bitmap patternImage = new Bitmap(materialImagePath))
using (Bitmap result = ApplyAlphaToImage(shapeImageBytes, width, height, stride, patternImage))
{
if (result == null)
continue;
// paint black lines image onto alpha-adjusted pattern image.
using (Graphics g = Graphics.FromImage(result))
g.DrawImage(blackImage, 0, 0);
result.Save(Path.Combine(resultFolder, Path.GetFileNameWithoutExtension(materialImagePath) + ".png"), ImageFormat.Png);
}
}
}
}
The black lines image:
public static Bitmap ExtractBlackImage(Byte[] shapeImageBytes, Int32 width, Int32 height, Int32 stride)
{
// Create black lines image.
Byte[] imageBytesBlack = new Byte[shapeImageBytes.Length];
// Line start offset is set to 3 to immediately get the alpha component.
Int32 lineOffsImg = 3;
for (Int32 y = 0; y < height; y++)
{
Int32 curOffs = lineOffsImg;
for (Int32 x = 0; x < width; x++)
{
// copy either alpha or inverted brightness (whichever is lowest)
// from the shape image onto black lines image as alpha, effectively
// only retaining the visible black lines from the shape image.
// I use curOffs - 1 (red) because it's the simplest operation.
Byte alpha = shapeImageBytes[curOffs];
Byte invBri = (Byte) (255 - shapeImageBytes[curOffs - 1]);
imageBytesBlack[curOffs] = Math.Min(alpha, invBri);
// Adjust offset to next pixel.
curOffs += 4;
}
// Adjust line offset to next line.
lineOffsImg += stride;
}
// Make the black lines images out of the byte array.
return BuildImage(imageBytesBlack, width, height, stride, PixelFormat.Format32bppArgb);
}
The processing to apply the foot image's transparency to the pattern image:
public static Bitmap ApplyAlphaToImage(Byte[] alphaImageBytes, Int32 width, Int32 height, Int32 stride, Bitmap texture)
{
Byte[] imageBytesPattern;
if (texture.Width != width || texture.Height != height)
return null;
// extract bytes of pattern image. Stride should be the same.
Int32 patternStride;
imageBytesPattern = ImageUtils.GetImageData(texture, out patternStride, PixelFormat.Format32bppArgb);
if (patternStride != stride)
return null;
// Line start offset is set to 3 to immediately get the alpha component.
Int32 lineOffsImg = 3;
for (Int32 y = 0; y < height; y++)
{
Int32 curOffs = lineOffsImg;
for (Int32 x = 0; x < width; x++)
{
// copy alpha from shape image onto pattern image.
imageBytesPattern[curOffs] = alphaImageBytes[curOffs];
// Adjust offset to next pixel.
curOffs += 4;
}
// Adjust line offset to next line.
lineOffsImg += stride;
}
// Make a image out of the byte array, and return it.
return BuildImage(imageBytesPattern, width, height, stride, PixelFormat.Format32bppArgb);
}
The helper function to extract the bytes from an image:
public static Byte[] GetImageData(Bitmap sourceImage, out Int32 stride, PixelFormat desiredPixelFormat)
{
Int32 width = sourceImage.Width;
Int32 height = sourceImage.Height;
BitmapData sourceData = sourceImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, desiredPixelFormat);
stride = sourceData.Stride;
Byte[] data = new Byte[stride * height];
Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
sourceImage.UnlockBits(sourceData);
return data;
}
The helper function to make a new image from a byte array:
public static Bitmap BuildImage(Byte[] sourceData, Int32 width, Int32 height, Int32 stride, PixelFormat pixelFormat)
{
Bitmap newImage = new Bitmap(width, height, pixelFormat);
BitmapData targetData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, newImage.PixelFormat);
// Get actual data width.
Int32 newDataWidth = ((Image.GetPixelFormatSize(pixelFormat) * width) + 7) / 8;
Int32 targetStride = targetData.Stride;
Int64 scan0 = targetData.Scan0.ToInt64();
// Copy per line, copying only data and ignoring any possible padding.
for (Int32 y = 0; y < height; ++y)
Marshal.Copy(sourceData, y * stride, new IntPtr(scan0 + y * targetStride), newDataWidth);
newImage.UnlockBits(targetData);
return newImage;
}
The result in my test tool:
As you see, the black lines are preserved on top of the pattern.
GetPixel/SetPixel are notoriously slow due to locking and other overhead accessing the pixels. To improve performance you will need to use some unmanaged coding to access the data directly.
This answer should shows an example on how to improve speed when working with bitmaps.
Here is some (untested!) code adapted from that anwer:
public static unsafe Image MergeBitmaps(Bitmap mask, Bitmap background)
{
Debug.Assert(mask.PixelFormat == PixelFormat.Format32bppArgb);
BitmapData maskData = mask.LockBits(new Rectangle(0, 0, mask.Width, mask.Height),
ImageLockMode.ReadWrite, mask.PixelFormat);
BitmapData backgroundData = background.LockBits(new Rectangle(0, 0, background.Width, background.Height),
ImageLockMode.ReadWrite, background.PixelFormat);
try
{
byte bytesPerPixel = 4;
/*This time we convert the IntPtr to a ptr*/
byte* maskScan0 = (byte*)maskData.Scan0.ToPointer();
byte* backgroundScan0 = (byte*)backgroundData.Scan0.ToPointer();
for (int i = 0; i < maskData.Height; ++i)
{
for (int j = 0; j < maskData.Width; ++j)
{
byte* maskPtr = maskScan0 + i * maskData.Stride + j * bytesPerPixel;
byte* backPtr = backgroundScan0 + i * backgroundData.Stride + j * bytesPerPixel;
//maskPtr is a pointer to the first byte of the 4-byte color data
//maskPtr[0] = blueComponent;
//maskPtr[1] = greenComponent;
//maskPtr[2] = redComponent;
//maskPtr[3] = alphaComponent;
if (maskPtr[3] > 0 )
{
if (maskPtr[2] > 200 &&
maskPtr[1] > 200 &&
maskPtr[0] > 200)
{
maskPtr[3] = 255;
maskPtr[2] = backPtr[2];
maskPtr[1] = backPtr[1];
maskPtr[0] = backPtr[0];
}
else
{
maskPtr[3] = 255;
maskPtr[2] = 128;
maskPtr[1] = 128;
maskPtr[0] = 128;
}
}
}
}
return mask;
}
finally
{
mask.UnlockBits(maskData);
background.UnlockBits(backgroundData);
}
}
}
I found this solution, it is much more faster.
But it uses too much resources.
Parallel programing in C# came to my help :
//I called my method in a parallel foreach
Parallel.ForEach(System.IO.Directory.GetFiles(materialsPath), filling =>
{
var result = FillWhiteImages(whiteImagesFolder, whiteImagesFolder + "\\" + System.IO.Path.GetFileName(whiteFilePath), filling);
});
//Instead of a classic foreach loop like this.
foreach (string material in System.IO.Directory.GetFiles(materialsPath))
{
var result = FillWhiteImages(whiteImagesFolder, whiteImagesFolder + "\\" + System.IO.Path.GetFileName(whiteFilePath), material);
}
I'm trying to make color picker using LockBits so when I move the cursor over picturebox it shows color located at cursor position. Approach with GetPixel works however I'm interested how to do this using LockBits.
My try, unfortunately shows white all the time:
void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Bitmap bmp = new Bitmap(pictureBox1.Image);
// we will try to get the pixel using raw data and make color from it
BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
// default format is 32bpp argb ( 4 bytes per pixel)
unsafe
{
byte* scanline = (byte*)data.Scan0;
for(int y = 0; y < data.Height; y++)
{
// row
for (int x = 0; x < data.Width; x+=4)
{
int r = scanline[x];
int g = scanline[x+1];
int b = scanline[x+2];
//int a = scanline[x+3];
Color color = Color.FromArgb(255, r, g, b);
pictureBox2.BackColor = color;
}
}
}
bmp.UnlockBits(data);
//Color color = bmp.GetPixel(e.X, e.Y);
}
Here is the solution..:
unsafe Color getPixel(Bitmap bmp, int x, int y)
{
BitmapData bmData = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
// not a complete check, but a start on how to use different pixelformats
int pixWidth = bmp.PixelFormat == PixelFormat.Format24bppRgb ? 3 :
bmp.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 4;
IntPtr scan0 = bmData.Scan0;
int stride = bmData.Stride;
byte* p = (byte*)scan0.ToPointer() + y * stride;
int px = x * pixWidth;
byte alpha = (byte) (pixWidth == 4 ? p[px + 3] : 255);
Color color = Color.FromArgb(alpha , p[px + 2], p[px + 1], p[px + 0]);
bmp.UnlockBits(bmData);
return color;
}
This is how you could call it:
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
panel2.BackColor = getPixel((Bitmap)panel1.BackgroundImage, e.X, e.Y);
}
Of course you can use any sort of Bitmap source, like Label.Image ot PictureBox.Image...
The color channels are called ARGB but actually ordered BGRA.
Note that stride is the physical width of a bitmap pixel row, including possible etra bytes to fill to a multiple of 4 bytes.
Also note that other solutions using lockbit work without pointers..
As noted in the comments above this is actually slower than using GetPixel, since setting up the unsafe access vector eats up any gain, even when reading out several pixels.
I can't quite seem to wrap my brain around images and converting them from a byte[] of raw RGB colors to a BitMap. I found one solution that allows me to convert an RGB 24bpp byte[] to a BitMap using SetPixel, but I have read that using LockBits is much faster, so I am trying to figure out how to do it that way.
Using the SetPixel method, I am getting an inverted image using:
public static Bitmap CreateBitmap24bppRgb(byte[] bmpData, int width, int height)
{
var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
var pos = 0;
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
bmp.SetPixel(x, y, Color.FromArgb(bmpData[pos], bmpData[pos + 1], bmpData[pos + 2]));
pos += 3;
}
}
return bmp;
}
Which I can't quite seem to figure out how to invert. But when I try to use LockBits, the image is just black, and I am unsure what I am doing wrong, it seems quite straight forward.
public static Bitmap CreateBitmap24bppRgb(byte[] data, int width, int height)
{
var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
//Create a BitmapData and Lock all pixels to be written
var bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
//Copy the data from the byte array into BitmapData.Scan0
for (int y = 0; y < bmp.Height - 1; y++)
{
Marshal.Copy(data, y * bmp.Width, bmpData.Scan0 bmpData.Stride);
}
//Unlock the pixels
bmp.UnlockBits(bmpData);
return bmp;
}
I'm just curious what is going wrong here?
If you're creating a new bitmap, instead of modifying an existing one, there's no reason to use LockBits or Marshal.Copy.
Just go with the Bitmap constructor that takes a pointer to pixel data.
public static Bitmap CreateBitmap24bppRgb(byte[] data, int width, int height)
{
GCHandle pin = GCHandle.Alloc(data, GCHandleType.Pinned);
var bmp = new Bitmap(width, height,
(width * 3 + 3) / 4 * 4,
PixelFormat.Format24bppRgb,
Marshal.UnsafeAddrOfPinnedArrayElement(data, 0));
bmp = (Bitmap)bmp.Clone(); // workaround the requirement that the memory address stay valid
// the clone step can also crop and/or change PixelFormat, if desired
GCHandle.Free(pin);
return bmp;
}
(or use an unsafe block, pinned keyword, and a pointer)
I need to create an image in memory (can be huge image!) and to extract from it byte array in the size of width x height. Each byte must have value of 0-255 (256 gray scale values: 0 for white and 255 for black).
The part of creating the image is easy, here is a simple example of my code:
img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
drawing.Clear(Color.Black);// paint the background
drawing.DrawString(text, font, Brushes.White, 0, 0);
Problem is to convert it to "my" special gray scale byte array. When I'm using any pixel format other then Format8bppIndexed, the byte array I'm getting from the bitmap is not in the size I need (width*length) so I need a conversion that takes too much time. When I'm using Format8bppIndexed I'm getting the byte array very fast and in the right size, but each byte/pixel is 0-15.
Changing the bitmap palette has no affect:
var pal = img.Palette;
for (int i = 1; i < 256; i++){
pal.Entries[i] = Color.FromArgb(255, 255, 255);
}
img.Palette = pal;
Any idea how to do it?
Edit: Full code:
// assume font can be Times New Roman, size 7500!
static private Bitmap DrawText(String text, Font font)
{
//first, create a dummy bitmap just to get a graphics object
var img = new Bitmap(1, 1);
var drawing = Graphics.FromImage(img);
//measure the string to see how big the image needs to be
var textSize = drawing.MeasureString(text, font);
//free up the dummy image and old graphics object
img.Dispose();
drawing.Dispose();
//create a new image of the right size (must be multiple of 4)
int width = (int) (textSize.Width/4) * 4;
int height = (int)(textSize.Height / 4) * 4;
img = new Bitmap(width, height);
drawing = Graphics.FromImage(img);
// paint the background
drawing.Clear(Color.Black);
drawing.DrawString(text, font, Brushes.White, 0, 0);
var bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
var newBitmap = new Bitmap(width, height, bmpData.Stride, PixelFormat.Format8bppIndexed, bmpData.Scan0);
drawing.Dispose();
return newBitmap;
}
private static byte[] GetGrayscleBytesFastest(Bitmap bitmap)
{
BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int numbytes = bmpdata.Stride * bitmap.Height;
byte[] bytedata = new byte[numbytes];
IntPtr ptr = bmpdata.Scan0;
Marshal.Copy(ptr, bytedata, 0, numbytes);
bitmap.UnlockBits(bmpdata);
return bytedata;
}
You probably want to do this in two steps. First, create a 16bpp grayscale copy of your original image as described in Convert an image to grayscale.
Then, create your 8bpp image with the appropriate color table and draw the 16bpp grayscale image onto that image. That will do the conversion for you, converting the 16-bit grayscale values to your 256 different colors.
You should then have an 8bpp image with your 256 different shades of gray. You can then call LockBits to get access to the bitmap bits, which will be index values in the range 0 to 255.
I have solved this problem with ImageSharp
I calculate the gray value from the rgb values and then add it to the array.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
private static byte[] GetImageData(byte[] imageData)
{
using (var image = Image.Load<Rgba32>(imageData))
{
var buffer = new byte[image.Width * image.Height];
var index = 0;
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
Span<Rgba32> pixelRow = accessor.GetRowSpan(y);
for (int x = 0; x < pixelRow.Length; x++)
{
ref Rgba32 pixel = ref pixelRow[x];
buffer[index] = (byte)((pixel.R + pixel.G + pixel.B) / 3);
index++;
}
}
});
return buffer;
}
}
i am creating png image which painted on my base, from the base i can save a png image, for your reference
Graphics g = e.Graphics;
....
g.DrawLine(pen, new Point(x, y), new Point(x1, y1));
.....
base.OnPaint(e);
using (var bmp = new Bitmap(500, 50))
{
base.DrawToBitmap(bmp, new Rectangle(0, 0, 500, 50));
bmp.Save(outPath);
}
this is single color transparency image, now how do i can inverse this image like png filled with any color and the real image portion should be transparent, is there any possibilities?
bit detail : so transparent will go nontransparent and where there is fill will go to transparent
There's a faster way if you're willing to use unsafe code:
private unsafe void Invert(Bitmap bmp)
{
int w = bmp.Width, h = bmp.Height;
BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int* bytes = (int*)data.Scan0;
for ( int i = w*h-1; i >= 0; i-- )
bytes[i] = ~bytes[i];
bmp.UnlockBits(data);
}
Note that this doesn't care about the colors and will invert those as well. If you wish to use a specific color, then the code will have to be modified a bit.
EDIT (thanks for Thomas notation)
public void ApplyInvert()
{
byte A, R, G, B;
Color pixelColor;
for (int y = 0; y < bitmapImage.Height; y++)
{
for (int x = 0; x < bitmapImage.Width; x++)
{
pixelColor = bitmapImage.GetPixel(x, y);
A = (byte)(255 - pixelColor.A);
R = pixelColor.R;
G = pixelColor.G;
B = pixelColor.B;
bitmapImage.SetPixel(x, y, Color.FromArgb((int)A, (int)R, (int)G, (int)B));
}
}
}
from here : Image Processing in C#: Inverting an image
For anyone who wants a fast method for inverting Bitmap colors without using unsafe:
public static void BitmapInvertColors(Bitmap bitmapImage)
{
var bitmapRead = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
var bitmapLength = bitmapRead.Stride * bitmapRead.Height;
var bitmapBGRA = new byte[bitmapLength];
Marshal.Copy(bitmapRead.Scan0, bitmapBGRA, 0, bitmapLength);
bitmapImage.UnlockBits(bitmapRead);
for (int i = 0; i < bitmapLength; i += 4)
{
bitmapBGRA[i] = (byte)(255 - bitmapBGRA[i]);
bitmapBGRA[i + 1] = (byte)(255 - bitmapBGRA[i + 1]);
bitmapBGRA[i + 2] = (byte)(255 - bitmapBGRA[i + 2]);
// [i + 3] = ALPHA.
}
var bitmapWrite = bitmapImage.LockBits(new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb);
Marshal.Copy(bitmapBGRA, 0, bitmapWrite.Scan0, bitmapLength);
bitmapImage.UnlockBits(bitmapWrite);
}
Bitmap GetPixel and SetPixel are extremly slow, this method works by copying the Bitmap pixels into a byte array, which you can then loop through and change, before finally copying the pixels back.
When you say invert the transparent sections into color, are you storing the real colors in the PNG image just set to full transparency? A lot of programs will optimize a png by removing the color data from transparency so you can't reverse it.
Colors can be converted to transparency
But transparency (without underlying colors) cannot be converted to color.
If your lucky your PNG will be non optimized and still have the original color data intact, but if your doing this from user input then it won't work for a high percentage of cases.