Graphics on indexed image - c#

I am getting error:
"A Graphics object cannot be created from an image that has an indexed
pixel format."
in function:
public static void AdjustImage(ImageAttributes imageAttributes, Image image)
{
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
Graphics g = Graphics.FromImage(image);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
g.Dispose();
}
I would like to ask you, how can I fix it?

Refering to this, it can be solved by creating a blank bitmap with the same dimensions and the correct PixelFormat and the draw on that bitmap.
// The original bitmap with the wrong pixel format.
// You can check the pixel format with originalBmp.PixelFormat
Bitmap originalBmp = (Bitmap)Image.FromFile("YourFileName.gif");
// Create a blank bitmap with the same dimensions
Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);
// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(tempBitmap))
{
// Draw the original bitmap onto the graphics of the new bitmap
g.DrawImage(originalBmp, 0, 0);
// Use g to do whatever you like
g.DrawLine(...);
}
// Use tempBitmap as you would have used originalBmp embedded in it
return tempBitmap;

The simplest way is to create a new image like this:
Bitmap EditableImg = new Bitmap(IndexedImg);
It creates a new image exactly like the original was with all its contents.

Overall, if you want to work with indexed images and actually preserve their colour depth and palette, this will always mean writing explicit checks and special code for them. Graphics simply can't work with them, because it manipulates colours, and the actual pixels of indexed images contain no colours, just indices.
For anyone still seeing this all these years later... the valid way to paint an image onto an existing (8-bit) indexed image is this:
Go over all the pixels of the image you want to paste and, for each colour, find the closest match on the target image's colour palette, and save its index into a byte array.
Open the backing bytes array of the indexed image using LockBits, and paste your matched bytes onto it, at the desired location, by looping over the relevant indices using the height and image stride.
It's not an easy task, but it's certainly possible. If the pasted image is also indexed, and contains more than 256 pixels, you can speed up the process by doing the colour matching on the palette instead of on the actual image data, then getting the backing bytes from the other indexed image, and remapping them using the created mapping.
Note that all of this only applies to eight bit. If your image is four-bit or one-bit, the simplest way to handle it is to convert it to 8-bit first so you can handle it as one byte per pixel, and convert it back afterwards.
For more information on that, see How can I work with 1-bit and 4-bit images?

Though the accepted answer works, it creates a new 32bpp ARGB image from the indexed bitmap.
To manipulate indexed bitmaps directly you can use this library (alert: shameless self promotion). Its GetReadWriteBitmapData extension allows creating a writable managed accessor even for indexed pixel formats.
And then you can use one of the DrawInto methods that can be used similarly to Graphics.DrawImage. Of course, as the target bitmap is indexed, the drawing operation must quantize the pixels using the target palette colors but there are a sort of overloads that can use dithering to preserve more image details.
Usage example (see more examples in the links above):
using (IReadWriteBitmapData indexedTarget = myIndexedBitmap.GetReadWriteBitmapData())
using (IReadableBitmapData source = someTrueColorBitmap.GetReadableBitmapData())
{
// or DrawIntoAsync if you want to use async-await
source.DrawInto(indexedTarget, targetRect, OrderedDitherer.Bayer8x8);
}
Image examples:
All images below had been created with PixelFormat.Format8bppIndexed format with the default palette, and a 256x256 icon and an alpha gradient rainbow were drawn on top of each other. Note that blending is used as much as possible with the available palette.
Image
Description
No dithering
Ordered Bayer8x8 dithering
Floyd-Steinberg error diffusion dithering
Disclaimer: Of course, the library has also some limitations compared to Graphics, for example there are no shape-drawing methods. But in worst case you still can use the accepted answer, and then call the ConvertPixelFormat method in the end if you need to produce an indexed result.

Related

A Graphics object cannot be created from an image that has an indexed pixel format [duplicate]

I am getting error:
"A Graphics object cannot be created from an image that has an indexed
pixel format."
in function:
public static void AdjustImage(ImageAttributes imageAttributes, Image image)
{
Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
Graphics g = Graphics.FromImage(image);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(image, rect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, imageAttributes);
g.Dispose();
}
I would like to ask you, how can I fix it?
Refering to this, it can be solved by creating a blank bitmap with the same dimensions and the correct PixelFormat and the draw on that bitmap.
// The original bitmap with the wrong pixel format.
// You can check the pixel format with originalBmp.PixelFormat
Bitmap originalBmp = (Bitmap)Image.FromFile("YourFileName.gif");
// Create a blank bitmap with the same dimensions
Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);
// From this bitmap, the graphics can be obtained, because it has the right PixelFormat
using(Graphics g = Graphics.FromImage(tempBitmap))
{
// Draw the original bitmap onto the graphics of the new bitmap
g.DrawImage(originalBmp, 0, 0);
// Use g to do whatever you like
g.DrawLine(...);
}
// Use tempBitmap as you would have used originalBmp embedded in it
return tempBitmap;
The simplest way is to create a new image like this:
Bitmap EditableImg = new Bitmap(IndexedImg);
It creates a new image exactly like the original was with all its contents.
Overall, if you want to work with indexed images and actually preserve their colour depth and palette, this will always mean writing explicit checks and special code for them. Graphics simply can't work with them, because it manipulates colours, and the actual pixels of indexed images contain no colours, just indices.
For anyone still seeing this all these years later... the valid way to paint an image onto an existing (8-bit) indexed image is this:
Go over all the pixels of the image you want to paste and, for each colour, find the closest match on the target image's colour palette, and save its index into a byte array.
Open the backing bytes array of the indexed image using LockBits, and paste your matched bytes onto it, at the desired location, by looping over the relevant indices using the height and image stride.
It's not an easy task, but it's certainly possible. If the pasted image is also indexed, and contains more than 256 pixels, you can speed up the process by doing the colour matching on the palette instead of on the actual image data, then getting the backing bytes from the other indexed image, and remapping them using the created mapping.
Note that all of this only applies to eight bit. If your image is four-bit or one-bit, the simplest way to handle it is to convert it to 8-bit first so you can handle it as one byte per pixel, and convert it back afterwards.
For more information on that, see How can I work with 1-bit and 4-bit images?
Though the accepted answer works, it creates a new 32bpp ARGB image from the indexed bitmap.
To manipulate indexed bitmaps directly you can use this library (alert: shameless self promotion). Its GetReadWriteBitmapData extension allows creating a writable managed accessor even for indexed pixel formats.
And then you can use one of the DrawInto methods that can be used similarly to Graphics.DrawImage. Of course, as the target bitmap is indexed, the drawing operation must quantize the pixels using the target palette colors but there are a sort of overloads that can use dithering to preserve more image details.
Usage example (see more examples in the links above):
using (IReadWriteBitmapData indexedTarget = myIndexedBitmap.GetReadWriteBitmapData())
using (IReadableBitmapData source = someTrueColorBitmap.GetReadableBitmapData())
{
// or DrawIntoAsync if you want to use async-await
source.DrawInto(indexedTarget, targetRect, OrderedDitherer.Bayer8x8);
}
Image examples:
All images below had been created with PixelFormat.Format8bppIndexed format with the default palette, and a 256x256 icon and an alpha gradient rainbow were drawn on top of each other. Note that blending is used as much as possible with the available palette.
Image
Description
No dithering
Ordered Bayer8x8 dithering
Floyd-Steinberg error diffusion dithering
Disclaimer: Of course, the library has also some limitations compared to Graphics, for example there are no shape-drawing methods. But in worst case you still can use the accepted answer, and then call the ConvertPixelFormat method in the end if you need to produce an indexed result.

C# Resizing Bitmap without changing the Pixelformat

I have a bitmap in BGR format and resolution 1920*1200 and want to scale it without changing the pixelformat.
private Bitmap rescale(Size size, Bitmap origin)
{
Bitmap scaled = new Bitmap(origin, size);
return scaled;
}
the problem is, that scaled turns out to be a RGBA bitmap(all alphas at 255), which is not only useless to me, but also troubles me as later I am doing a AbsDiff (from EMGU) on different images and then, the alpha value always turns out zero..
Is there a way to not change the pixelformat when scaling or to do AbsDiff without the alpha-values? Because like this, when I load the image later, it is invisible..
Bitmap scaled = new Bitmap(origin, size);
There are a lot of implicit assumptions built into that constructor call. You'll get:
A bitmap with the 32bppPArgb pixel format. Meant to help the programmer fall into the pit of success, it is the most optimal pixel format on modern PCs. Compatible with the pixel format of the video adapter frame buffer, it can be blitted without any conversion. It is ten times faster than all the other ones.
The resolution is set to the video adapter DPI. This is usually a bit less optimal although it is pretty hard to argue that it should use the resolution of the source image after rescaling it. You might want to modify that.
A transparent background. That matters if the source bitmap has transparency or has pixels with the alpha channel set to a value < 255. Usually fine, if the source bitmap was transparent then the new one will be as well. Not so fine with alpha, rescaling the bitmap is pretty likely to affect that negatively. YMMV.
Bilinear interpolation of the source image. That is fairly modest, you might favor InterpolationMode.HighQualityBicubic for a better result, especially when you shrink it by more than 50%. Or NearestNeighbor if speed is your concern or the source image is very small and you enlarge it with the intention to keep the pixels visible as-is.
Clearly you are unhappy, the first bullet is the source of your complaint. Writing it out with all details tweakable:
public static Bitmap RescaleImage(Image source, Size size) {
// 1st bullet, pixel format
var bmp = new Bitmap(size.Width, size.Height, source.PixelFormat);
// 2nd bullet, resolution
bmp.SetResolution(source.HorizontalResolution, source.VerticalResolution);
using (var gr = Graphics.FromImage(bmp)) {
// 3rd bullet, background
gr.Clear(Color.Transparent);
// 4th bullet, interpolation
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
gr.DrawImage(source, new Rectangle(0, 0, size.Width, size.Height));
}
return bmp;
}
-
The (admittedly sort of ugly) way I would do this is to create a new Bitmap with the original Pixelformat and the new Size and draw the original bitmap onto it (untested):
private Bitmap rescale(Size size, Bitmap origin)
{
Bitmap rescaled = new Bitmap(size.Width, size.Height, origin.PixelFormat);
using(Graphics g = Graphics.FromImage(rescaled))
{
g.DrawImage(origin, 0, 0, size.Width, size.Height);
}
return rescaled;
}
This seems to be what you need:
http://www.codeproject.com/Tips/552141/Csharp-Image-resize-convert-and-save
and
Bitmap(Image, Size) constructor
public Bitmap(
Image original,
Size newSize
)
This is a constructor that takes a new size. The image is automatically resized to that size.

Creating a custom Image display in WPF as an optimization mechanism

I am doing some image processing on a custom class representing 16 bit gray-scale images.
The intensity of the pixels is stored in a single dimension ushort array: ushort[] data
I also have the width, the height, the dpi, the stride, if necessary.
My motivation is the following: I show the results of certain operations quite fast, but the conversion from the array to the bitmapsource to the image object is too lengthy, so I was thinking of an Image object which drew its "source" directly from the array. I could thus write a method "update()" instead of doing multiple conversions.
1/ Is this possible?
2/ Would it be faster?
3/ How would I go about doing it?
The way I currently draw the image is with the following code (there's a bit more to that, but essetially that the heart of it)
BitmapSource bmps = BitmapSource.Create(Width, Height, Dpi, Dpi, PixelFormats.Gray16, null,
data, stride);
image.Source=bmps;
Thank you!
Instead of using a separate array to store pixels and then creating a BitmapSource to show the results, I think it would be better using a WriteableBitmap, this way you can store pixel data (16-bit greyscale values) directly in its BackBuffer. Thus you can code something like this:
// You create "bmp" just once and then update its content when needed
var bmp = new WriteableBitmap(640, 480, 96, 96, PixelFormats.Gray16, null);
var imgRect = new Int32Rect(0, 0, bmp.PixelWidth, bmp.PixelHeight);
Then you update the image:
bmp.Lock();
ApplySomeFilter(bmp.BackBuffer, ImageFilter.Blur); // this is just an example ;)
bmp.AddDirtyRect(imgRect);
bmp.Unlock();
The ApplySomeFilter method can use unsafe code to modify pixel data of the WriteableBitmap.
For example, if ApplySomeFilter is defined like this:
unsafe public void ApplySomeFilter(void* imgBuffer, ImageFilter filter)
{
// code that modifies pixels goes here
}
then you can call it this way:
ApplySomeFilter(bmp.BackBuffer.ToPointer(), ImageFilter.Blur);

Sprite/Texture Atlas: GDI+ Bitmap.MakeTransparent for color-key with OpenTK

I am writing a support class for sprite/texture atlas functionality, using C# with OpenTK.
Most functionality is working fine thus far (simple 2D tiles on an orthographic view).
My problem relates to unexpected display results when calling the GDI+ Bitmap.MakeTransparent() method to set a color (Magenta / 0xFFFF00FF) for use as a color-key.
It would seem that I am using incorrect pixel format parameters for the bitmap.LockBits() and GL.TexImage2D() calls. My code was based on examples which indeed worked, but which had in common that the rectangle passed to LockBits() was for the entire image.
The calls which pertain to this process are:
<!-- language: C# -->
Bitmap bitmap = new Bitmap(filename);
bitmap.MakeTransparent(Color.Magenta);
GL.GenTextures(1, out texture_id);
GL.BindTexture(TextureTarget.Texture2D, texture_id);
// "rect" is initialized for one of:
// - the dimensions of the entire image
// (0, 0, bitmap.width, bitmap.height)
// - the dimensions for a sub-rectangle within the image (for one tile)
// (tile_x * tile_width, tile_y * tile_height, tile_width, tile_height)
// I observe different behaviors for a sub-rectangle,
// as compared to the entire image, when in combination with
// the .MakeTransparent() call.
//
// This code is in a load_tile() method, and the plan was to make
// multiple calls per image file, one per tile to extract as a GL texture.
// Without transparency, that worked fine.
Rectangle rect = new Rectangle(xx, yy, width, height);
BitmapData data = bitmap.LockBits(rect,
ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format32bppRgb);
// In the absence of calling bitmap.MakeTransparent(),
// images loaded and displayed as expected with Format24bppRgb.
// With MakeTransparent() and Format32bppRgb, the results seem to be OS-dependent.
// (At first I thought the "correct" combination to be found,
// but then found that the results were "right" only under Windows 7.)
GL.TexImage2D(
OpenTK.Graphics.OpenGL.TextureTarget.Texture2D, // texture_target,
0, // level,
OpenTK.Graphics.OpenGL.PixelInternalFormat.Rgba, // internal_format
data.Width, data.Height, // width, height,
0, // border,
OpenTK.Graphics.OpenGL.PixelFormat.Bgra, // pixel_format
OpenTK.Graphics.OpenGL.PixelType.UnsignedByte, // pixel_type
data.Scan0 // pixels
);
// Certainly the internal_format and pixel_format arguments are pertinent,
// but other combinations I have tried produced various undesired display results.
// After reading various (OpenGL, OpenTK, and GDI+) docs, still I am not enlightened..
bitmap.UnlockBits(data);
I have tested a small demo using the code above on different boxen, and observe these results:
Windows 7 box: magenta pixels act as transparent (the desired result)
Windows XP box: magenta pixels rendered as black
Ubuntu Linux box: magenta pixels rendered as magenta
This surprises me, as I anticipated that (GDI+ and OpenGL and the OpenTK bindings) would act the same on different boxes.
To the extent that I have absorbed the GDI+ and OpenGL / OpenTK API documentation, I think my puzzlement relates to these two points:
What is a correct way of calling MakeTransparent() + LockBits() + GL.TexImage2D(), so as to result in the specified color being rendered as transparent?
Why do I see strange display results (as if the "stride" was mis-calculated) for certain pixel format parameter combinations, when LockBits() is called for a sub-rectangle rather than the entire image?
Update:
I have whittled down my code into a small project on Github:
https://github.com/sglasby/OpenGL_Transparent_Sprite
Also, I stumbled upon a parameter combination that works
(arg 3 of LockBits() is Format32bppPArgb),
though it is not clear why it works, given that the documentation implies another pixelformat is wanted:
http://msdn.microsoft.com/en-us/library/8517ckds.aspx
(which states that the bitmap will be in Format32bppArgb after calling MakeTransparent).
While this is a separate issue to your question, in most cases you should actually use premultiplied-alpha (Format32bppPArgb). If this format is working correctly, then understanding why Format32bppArgb does not work is mostly an academic exercise.
I ran your example project on Win7 with an Intel 2000HD and got the following results:
Format32bppPArgb works correctly
Format32bppRgb works correctly
Format32bppArgb is scrambled
On further investigation, this does not appear to be linked to OpenGL, but rather to the way Bitmap.LockBits works.
Check the values of data.Stride on the debugger for each approach:
Format32bppPArgb has a stride of 128 (4x the bitmap width, correct)
Format32bppRgb has a stride of 128 (4x the bitmap width, correct)
Format32bppArgb has a stride of 512 (16x the bitmap width, ?)
MSDN does not turn up something useful here. At this point, I cannot tell why this is happening. I'll update this answer if I manage to uncover anything.
Edit: lo and behold, if you force the correct stride when unpacking the data, the output looks correct:
GL.PixelStore(PixelStoreParameter.UnpackRowLength, data.Width * 4); // 4x for 32bpp

Copy one Bitmap onto a larger Bitmap using without using Graphics.DrawImage

This is a follow up from Rendering to a single Bitmap object from multiple threads
What im trying to achieve is to take a bitmap of say 50x50 pixels and draw it onto a larger bitmap(100x100 pixels) at any point on the larger image, using the bitmaps LockBits function or any other but NOT graphics.DrawImage. My reasons for not wanting to use DrawImage is stated in the other thread.
I have managed to get something by using Marshal.Copy from the source BitmapData to the dest BitmapData but its creating a tiled, stretched image horizontally.
You could manipulate the image in memory without relying on any system calls. If you dig into the underlying format of a .BMP file you could build your own Device Independant Bitmap class that truly "understands" the low level format of a .BMP.
For example a 8 bit per pixel image is essentially a 2 dimensional array of bytes (each byte is 1 pixel) plus a simple color table. Roughly speaking (and this is very very rough):
byte[,] bColors = new byte[3,256]; // 256 RGB colors
byte[,] bImage = new byte[25,50]; // 25 x 50 pixels
The trick is (as always) getting a hold of the raw pixel data, doing the processing, and then updating the raw pixel data with your changes.
In the past I've approached this by converting a GDI HBITMAP into a 24bpp DIB, doing my funky image processing on the raw pixels (3 bytes per pixels makes this easier), then converting the DIB back into a HBITMAP. This was all using just classic GDI (pre GDI+ even, let alone C#).
Using that approach you could design a control structure to allow multiple writers to different sections of your much bigger image.
However... the lowlevel BitBlt GDI calls are likely to be way more efficient that anything you can do. If I were you I'd make certain that just doing 50 or 100 bitblt's in a row would be too slow (you'd likely need to do this in c++).
The most annoying challenges with dealing with DIB's are:
Converting a DIB to an actual "image" ready for display and
Converting an actual "image" into a DIB
Saving a DIB as something other than a .BMP
Core references when I started learning the "horror" that images actually are:
http://msdn.microsoft.com/en-us/library/dd183562(VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd144879(VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd162973(VS.85).aspx
How you go about getting to/from .NET Image's... well... that's a good question :)
This should work just fine using LockBits/BitmapData, if you are using a 32bpp [P]ARGB pixel format. The trick is that you will have to copy the data one row at a time so that it aligns in the correct places. You should be able to do this using something like:
Rectangle srcArea = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(srcArea, ImageLockMode.ReadOnly, destBitmap.PixelFormat);
Rectangle destArea = new Rectangle(25, 25, srcBitmap.Width, srcBitmap.Height);
BitmapData destData = destBitmap.LockBits(destArea, ImageLockMode.WriteOnly, destBitmap.PixelFormat);
IntPtr srcPtr = srcData.Scan0;
IntPtr destPtr = destData.Scan0;
byte[] buffer = new byte[srcData.Stride];
for (int i = 0; i < srcData.Height; ++i)
{
Marshal.Copy(srcPtr, buffer, 0, buffer.Length);
Marshal.Copy(buffer, 0, destPtr, buffer.Length);
srcPtr += srcData.Stride;
destPtr += destData.Stride;
}
srcBitmap.UnlockBits(srcData);
destBitmap.UnlockBits(destData);
As a warning, this code won't work as is because I am not sure what the right incantations are for incrementing IntPtr's. I've done this same type of thing before, but in C++. Also, I don't know if there is a way to directly copy the data instead of using an intermediate buffer.
An additional caveat: the LockBits call srcBitmap and the sizing of the buffer assume that srcBitmap will be completely enclosed in destBitmap. If this is not the case (some part of the bitmap will be cropped off) the area locked and the size of the buffer will need to be adjusted.
If you are not using a 32bpp pixel format (ie 24bpp), it will be more difficult. The stride of your source BitmapData may include some amount of padding that should not be copied. You could work around this by calculating the amount of actual pixel data in a source row, and copy this amount. Indexed pixel formats would be even more work.
I would recommend taking a look at the internal bitmap memory structure.
The best approach, I think, would be to not try to set the BitmapData directly. Instead, I would make a single, shared byte array of the appropriate size, and set the byte array directly from your smaller images.
When you compose your larger image, you can take the final byte array and directly make a Bitmap from the byte data.
This has the advantage of allowing you to control the memory management, thread the operations, etc, as you seemed to want to do in your original post. It should be very fast for the data access, as well.

Categories

Resources