1
votes

I'm trying to figure out how to rotate my Bitmap image by an arbitrary degree amount, similar to the solution posted here. However, when I try using this solution, which converts the Bitmap to a Graphics object, and other solutions that I've Googled, I keep getting the exception:

A Graphics object cannot be created from an image that has an indexed pixel format.

I need indexed pixels so I can access each pixel and get color information in my program, but I also need the ability to be able to rotate on the fly in degree increments other than 90.

I was wondering if A) there is a solution that is friendly to indexed pixel Bitmaps, or B) if there is a way to temporarily change the Bitmap to allow it to be converted to a Graphics object and then change it back to an indexed pixel Bitmap with the rotation applied?

Thanks much in advance!

EDIT: The PixelFormat for the Bitmap I'm using is "1bppIndexed".

2
You don't need an indexed pixel format to be able to retrieve pixel data directly - the indexed format refers to pixel values being indexed references into the image's palette. Perhaps you should try using a non-indexed format...Simon MᶜKenzie
Why on Earth are you still using 8bpp images? It isn't 1993 anymore.Hans Passant
I assumed that "indexed format" meant that I could use GetPixel() and SetPixel(). I don't care about the specifics of the format as long as I can use those functions. Perhaps I'm gravely misunderstanding what "indexed format" means. :)Mac Sigler
Found a solution; posted a link to it below as an answer.Mac Sigler

2 Answers

1
votes

I presume you are using windows forms and GDI+ here? I've done a fair bit of work with this and can tell you with great confidence that you want to use 24 or 32 bit images (I can't remember which sorry). The performance is hugely improved something of the order of 10 to 100 times faster. In my app it made the difference between the user specifying the rotation in a textbox to being able to just grab the image with the mouse and rotate smoothly on the fly, zoom on the fly etc.

Assuming you follow that advice that just leave this issue of being able to access each pixel. Why do you think you can't get each pixel of a 24 or 32 bit image?

EDIT: In my application I used only 32 bit bitmaps for everything. If I got a bitmap in another format (eg opening from a file) I converted it immediately. Actually, even if I got a 32 bit format I would clone it immediately and then dispose the original bitmap as I found it kept some locks on the file. The inbuilt clone produced odd results so I ended up writing this code below. Please note that this is stuff I wrote probably 8 years ago so doesn't use any of the latest features:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace ImageCloneTest
{
    static class Program
    {
        [DllImport("kernel32", EntryPoint = "RtlMoveMemory")]
        public unsafe static extern void CopyMemory(byte* Destination, byte* Source, int Length);

        [DllImport("kernel32", EntryPoint = "RtlMoveMemory")]
        public static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);


        public static Bitmap Clone(Bitmap SourceBitmap, PixelFormat Format)
        {
            // copy image if pixel format is the same
            if (SourceBitmap.PixelFormat == Format) return Clone(SourceBitmap);

            int width = SourceBitmap.Width;
            int height = SourceBitmap.Height;

            // create new image with desired pixel format
            Bitmap bitmap = new Bitmap(width, height, Format);

            // draw source image on the new one using Graphics
            Graphics g = Graphics.FromImage(bitmap);
            //fill background in case orig bitmap contains transparent regions.
            g.FillRectangle(Brushes.White, 0, 0, bitmap.Width, bitmap.Height);
            g.DrawImage(SourceBitmap, 0, 0, width, height);
            g.Dispose();

            return bitmap;
        }

        // and with unspecified PixelFormat it works strange too
        public static Bitmap Clone(Bitmap SourceBitmap)
        {
            // get source image size
            int width = SourceBitmap.Width;
            int height = SourceBitmap.Height;
            Rectangle rect = new Rectangle(0, 0, width, height);

            // lock source bitmap data
            BitmapData srcData = SourceBitmap.LockBits(rect, ImageLockMode.ReadOnly, SourceBitmap.PixelFormat);

            // create new image
            Bitmap dstBitmap = new Bitmap(width, height, SourceBitmap.PixelFormat);

            // lock destination bitmap data
            BitmapData dstData = dstBitmap.LockBits(rect, ImageLockMode.WriteOnly, dstBitmap.PixelFormat);

            CopyMemory(dstData.Scan0, srcData.Scan0, height * srcData.Stride);

            // unlock both images
            dstBitmap.UnlockBits(dstData);
            SourceBitmap.UnlockBits(srcData);

            // copy pallete
            if (BitmapHasPalette(SourceBitmap) && BitmapHasPalette(dstBitmap))
            {
                ColorPalette srcPalette = SourceBitmap.Palette;
                ColorPalette dstPalette = dstBitmap.Palette;
                int n = srcPalette.Entries.Length;
                for (int i = 0; i < n; i++)
                {
                    dstPalette.Entries[i] = srcPalette.Entries[i];
                }
                dstBitmap.Palette = dstPalette;
            }
            return dstBitmap;
        }

        public static bool BitmapHasPalette(Bitmap SourceBitmap)
        {
            if (SourceBitmap == null) return false;
            switch (SourceBitmap.PixelFormat)
            {
                case PixelFormat.Format1bppIndexed:
                case PixelFormat.Format4bppIndexed:
                case PixelFormat.Format8bppIndexed:
                case PixelFormat.Indexed:
                    return true;
            }
            return false;
        }
    }
}
0
votes

I used Hans' solution here: https://stackoverflow.com/a/2016509/1464630

It seems I can still use the same program logic, but it's no longer in an indexed pixel format so the Graphics class doesn't complain.