Any possibility to use Graphics.DrawString on a 1bpp Bitmap? - c#

The question is quite self explicative, I would like to use a 1bpp Bitmap because will help me (a lot) talking with a device that accept 1bbp bitmaps (well, 160x43 byte array that is a 160x43 bitmap image with 1bpp format).
While C# allows me to create 1bpp bitmaps, I would like to work on it with a Graphics object. But the program seems to behave strangely when I do any operation on this.
Is possible to do some graphic operations on those type of bitmaps?
my code snippet is quite short:
Bitmap bwImage = new Bitmap(160, 43, System.Drawing.Imaging.PixelFormat.Format1bppIndexed);
using (Graphics g = Graphics.FromImage(bwImage))
{
g.FillRectangle(Brushes.White, new Rectangle(0, 0, 160, 43));
g.DrawString("ciao sono francesco", Font, Brushes.Black, new RectangleF(0f, 0f, 159f, 42f));
}
When I do anything related to bitmaps after those calls. My bitmaps doesn't change (obviusly I'm talking about other bitmaps). Like if GDI is totally dead
Any ideas?

The Graphics class and the Graphics.FromImage method do not support indexed bitmaps. Please refere to the documentation for the Graphics.FromImage method on MSDN. A workaround for your problem would be to do all graphic operations on a supported bitmap format such as PixelFormat.Format32bppRgb and then convert the bitmap to a indexed bitmap. The conversion is straightforward. You will find a example of such a conversion here.
Hope, this helps.

Related

Is there a benefit to using Graphics.DrawImage(Image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit) when the image is a Bitmap?

Unless I miss something bitmaps are quantized (pixel-oriented). So what happens when someone tries the following:
public void Foo(Bitmap image)
{
var destinationRect = new Rectangle(0, 0, 50, 50);
var resultingSubimage = new Bitmap(destinationRect.Width, destinationRect.Height, PixelFormat.Format24bppRgb);
using (var graphics = Graphics.FromImage(resultingSubimage))
{
graphics.DrawImage(image, destinationRect, new RectangleF(30.3245F /*x*/, 23.234234F /*y*/, 50F, 50F), GraphicsUnit.Pixel);
// vs graphics.DrawImage(image, destinationRect, new Rectangle(30 /*x*/, 23 /*y*/, 50, 50), GraphicsUnit.Pixel);
}
}
notice that the x and y fields are decimals which point to a sub-pixel point. But is there even such a thing as a sub-pixel point for bitmaps? What's going on under the hood? Sorry if this has been answered elsewhere but the online documentation for both Graphics.DrawImage() and for the underlying p/invoke function 'GdipDrawImageRectRect' do not shed any light in this.
It is not well known, that if you draw an image, e.g.:
graphics.DrawImage(image, top, left);
the image will be scaled. This is because DrawImage looks at the dpi setting of the image (e.g. 72dpi from photoshop), and scales the image to match the destination display (typically 96dpi).
If you want to draw an image without any scaling, you must explicitly give DrawImage the size of the image:
graphics.DrawImage(img, top, left, img.Width, img.Height);
By calling, DrawImage with the the destination rectangle that matches the original image's pixel size, you are avoiding a resampling/rescaling.
Bonus Reading
.NET: What does Graphics.DrawImageUnscaled do?
Define "benefit". What overload would you use instead? I.e. "benefit" as compared to what?
It is most assuredly not the case that the overload is completely useless when dealing with Bitmap objects. Firstly, the GraphicsUnit value determines how the coordinates passed to the method are interpreted and of course one might pass something other than GraphicsUnit.Pixel. For example, suppose you are using GraphicsUnit.Inch and the resolution of the image is 120 dpi. Then each pixel is only 1/120th of an inch, and for per-pixel precision, your floating point values would be multiples of that (i.e. multiples of 0.0083333333333333), and not integer values.
Secondly, the Graphics object can be configured to do sub-pixel sampling in a variety of ways, and in such cases, a fractional pixel value could have meaning, even if the units being described were pixels.
You ask "what's going on under the hood", but I'm afraid that part is too broad a question for Stack Overflow. The Graphics object uses GDI+ as the underlying mechanism when using it on a Windows platform; the answer to what specifically happens with different configurations of the Graphics object would require a lengthy treatise.
If you want that level of detail, the right place to start would be the MSDN documentation for the GDI+ in the native Windows API. For most parts of Graphics, there's a one-for-one correspondence between the .NET API and the native one.
By the way, to be clear: your coordinates in this scenario are float values. I would be cautious about using the word "decimal" here, because .NET/C# has an actual decimal type, and you're definitely not using that. :)

Use Bitmap.ConvertFormat in C# (PixelFormat from 24 bits to 16 bits RGB555 with dithering)

Any easy way? I am trying to convert the PixelFormat from 24 bits image to 16 bits RGB555 with dithering (for a portable device). I tested already a lot of approaches:
AForge.NET
FreeImage
http://www.codeproject.com/Articles/66341/A-Simple-Yet-Quite-Powerful-Palette-Quantizer-in-C
They all work poorly.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms536306(v=vs.85).aspx
I found a GDI+ wrapper, but this function is missing.
Thanks!
You can convert the image by using Bitmap class in the constructor pass the parameters for the conversion format and it will render the image with the PixelFormat you have mentioned.
Some code:
public static Bitmap ConvertTo16bpp(Image img) {
//you can also use Image.FromFile(fileName);//fileName can be absolute path of the image.
var bmp = new Bitmap(img.Width, img.Height,System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
using (var gr = Graphics.FromImage(bmp))
gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height));
return bmp;
}
Suggestions
If you have some simple requirements you would not probably need AForge.NET (An excellent library for image processing) and you may go with a simple solution,but that's your decision to make.
Links
system.drawing.imaging
system.drawing.imaging.pixelformat
Convert-24-bit-bmp-to-16-bit (Solution Adpated from)

C# Converting 32bpp image to 8bpp

I'm trying to convert a 32bpp screenshot image to an 8bpp (or 4bpp, or 1bpp) format using C#. I've already looked at several stackoverflow answers on similar subjects and most suggest variations using the following code:
public static Bitmap Convert(Bitmap oldbmp)
{
Bitmap newbmp = new Bitmap(oldbmp.Width, oldbmp.Height, PixelFormat.Format8bppIndexed);
Graphics gr = Graphics.FromImage(newbmp);
gr.PageUnit = GraphicsUnit.Pixel;
gr.DrawImageUnscaled(oldbmp, 0, 0);
return newbmp;
}
However, when this executes, I get a the exception: A graphics object cannot be created from an image that has an indexed pixel format. I understand that 8, 4 and 1bpp images have colour table mappings rather than the actual colour pixels themselves (as in 32 or 16bpp images) so I assume I'm missing some conversion step somewhere, but I'm fairly new to C# (coming from a C++ background) and would prefer to be able do this using native C# calls rather than resorting to PInvoking BitBlt and GetDIBits etc. Anybody able to help me solve this? Thanks.
EDIT: I should point out that I need this to be backwardly compatible to .NET framework 2.0
GDI+ in general has very poor support for indexed pixel formats. There is no simple way to convert an image with 65536 or 16 million colors into one that only has 2, 16 or 256. Colors have to be removed from the source image and that is a lossy conversion that can have very poor results. There are multiple algorithms available to accomplish this, none of them are perfect for every kind of image. This is a job for a graphics editor.
There is one trick I found. GDI+ has an image encoder for GIF files. That's a graphics format that has only 256 colors, the encoder must limit the number of colors. It uses a dithering algorithm that's suitable for photos. It does have a knack for generating a grid pattern, you'll be less than thrilled when it does. Use it like this:
public static Image Convert(Bitmap oldbmp) {
using (var ms = new MemoryStream()) {
oldbmp.Save(ms, ImageFormat.Gif);
ms.Position = 0;
return Image.FromStream(ms);
}
}
The returned image has a 8bpp pixel format with the Palette entries calculated by the encoder. You can cast it to Bitmap if necessary. By far the best thing to do is to simply not bother with indexed formats. They date from the stone age of computing back when memory was severely constrained. Or use a professional graphics editor.
AForge library is doing it perfectly using Grayscale.
var bmp8bpp = Grayscale.CommonAlgorithms.BT709.Apply(bmp);
This class is the base class for image grayscaling [...]
The filter accepts 24, 32, 48 and 64 bpp color images and produces 8
(if source is 24 or 32 bpp image) or 16 (if source is 48 or 64 bpp
image) bpp grayscale image.
Negative stride signifies the image is bottom-up (inverted). Just use the absolute of the stride if you dont care. I know that works for 24bpp images, unaware if it works for others.
You can use System.Windows.Media.Imaging in PresentationCore Assembly take a look at here for more information

Load a bitmap from file in RGB format (without alpha)

i simply want to load a .BMP file and get the Bitmap object in 24bit RGB format (or 32bit in RGB format).
All methods I tried return a Bitmap/Image object with PixelFormat = Format32bppArgb. Even if of course BMPs don't have alpha.
new Bitmap(System.Drawing.Image.FromFile(fileName, true));
new Bitmap(fileName);
I currently solve the problem by copying the first object to another in memory bitmap at 24bit RBG.
Is there a single method to do it?
Thanks
As far as I can tell it is not possible to specify the PixelFormat for loading bitmaps using the classes in System.Drawing. To convert the bitmap check this question: Converting Bitmap PixelFormats in C#
This is currently the top answer there:
Bitmap orig = new Bitmap(#"c:\temp\24bpp.bmp");
Bitmap clone = new Bitmap(orig.Width, orig.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (Graphics gr = Graphics.FromImage(clone)) {
gr.DrawImage(orig, new Rectangle(0, 0, clone.Width, clone.Height));
}
// Dispose orig as necessary..
One option would be to put that in a function which takes a filename and PixelFormat. That hides the ugly code, which has its ups and downs since it also hides the fact it is probably not that efficient.
According to the linked SO question using the Clone method not always works.
You can clone it to a RGB format one:
var bitmapInRgbFormat = loadedBitmap.Clone(new Rectangle(0, 0, loadedBitmap.Width, loadedBitmap.Height), PixelFormat.Format32bppRgb)
Exactly what do you mean by copying the first object to another?
To achieve what you want to do, meaning loading an image and converting it to 24 bit, just get the graphics context of a second bitmap that matches the size and that is RGB, and paint the original bitmap onto this one.

8bpp Bitmap format on the Compact Framework

I am messing around with Conway's Game of Life - http://en.wikipedia.org/wiki/Conway's_Game_of_Life
I started out coding algorithmns for winforms and now want to port my work onto windows mobile 6.1 (compact framework). I came across an article by Jon Skeet where he compared several different algorithmns for calculating next generations in the game. He used an array of bytes to store a cells state (alive or dead) and then he would copy this array to an 8bpp bitmap. For each new generation, he works out the state of each byte, then copies the array to a bitmap, then draws that bitmap to a picturebox.
void CreateInitialImage()
{
bitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
ColorPalette palette = bitmap.Palette;
palette.Entries[0] = Color.Black;
palette.Entries[1] = Color.White;
bitmap.Palette = palette;
}
public Image Render()
{
Rectangle rect = new Rectangle(0, 0, Width, Height);
BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, bitmap.PixelFormat);
Marshal.Copy(Data, 0, bmpData.Scan0, Data.Length);
bitmap.UnlockBits(bmpData);
return bitmap;
}
His code above is beautifully simple and very fast to render. Jon is using Windows Forms but now I want to port my own version of this onto Windows Mobile 6.1 (Compact Framework) but . . . .there is no way to format a bitmap to 8bpp in the cf.
Can anyone suggest a way of rendering an array of bytes to a drawable image in the CF. This array is created in code on the fly (it is NOT loaded from an image file on disk). I basically need to store an array of cells represented by bytes, they are either alive or dead and I then need to draw that array as an image. The game is particularly slow on the CF so I need to implement clever optimised algoritmns but also need to render as fast as possible and the above solution would be pretty dam perfect if only it was available on the compact framework.
Many thanks for any help
Any suggestions?
You could have a look at GDI+ for CF. It's basically a wrapper for most of the GDI implemented in WinCE. Here's a link to the source code and a writeup: http://community.opennetcf.com/articles/cf/archive/2007/10/31/using-gdi-on-windows-mobile.aspx
I think ImagingFactoryClass.CreateBitmapFromBuffer() looks like a good place to start.
Ok, how about this:
use the Bitmap.Save() method to save to a MemoryStream instead of a file;
when you save to the MemoryStream, you get to name the ImageFormat as "GIF" (this is equivalent to 8bpp in .Net, according to this: http://support.microsoft.com/kb/318343)
use MemoryStream.Write() to change whatever data you want in the image, or copy the data using MemoryStream.ToArray() if that jives better.
After you change the MemoryStream, you'll probably have to copy it back into the Bitmap, or make a new Bitmap. If you do make a new Bitmap, be sure to Dispose() the old one, to avoid memory leaks.
Hi Rocjoe and thanks again for the help, I have tried the following
Image bmp = new Bitmap(10, 10);
byte[] array = ImageToByteArray(bmp);
public byte[] ImageToByteArray(Image img)
{
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Gif );
return ms.ToArray();
}
The array coming back has over 870 bytes in it, It seems to hold all sorts of header info, padding and what have you. so again it does not work...

Categories