当前位置: 移动技术网 > IT编程>开发语言>.net > .net下灰度模式图像在创建Graphics时出现:无法从带有索引像素格式的图像创建graphics对象 问题的解决方案。

.net下灰度模式图像在创建Graphics时出现:无法从带有索引像素格式的图像创建graphics对象 问题的解决方案。

2018年10月27日  | 移动技术网IT编程  | 我要评论

逗组词,无锡世茂首府,江户川乱步奖

    在.net下,如果你加载了一副8位的灰度图像,然后想向其中绘制一些线条、或者填充一些矩形、椭圆等,都需要先创建grahpics.fromimage创建grahphics对象,而此时会出现:无法从带有索引像素格式的图像创建graphics对象 这个错误,让我们的后续工作无法完成。本文叙述了一种另外的方法来实现它。

      我们通过reflector发编译.net framework的相关函数后发现,fromimage的实现过程如下:

public static graphics fromimage(image image)
{
    if (image == null)
    {
        throw new argumentnullexception("image");
    }
    if ((image.pixelformat & pixelformat.indexed) != pixelformat.undefined)
    {
        throw new exception(sr.getstring("gdipluscannotcreategraphicsfromindexedpixelformat"));
    }
    intptr zero = intptr.zero;
    int status = safenativemethods.gdip.gdipgetimagegraphicscontext(new handleref(image, image.nativeimage), out zero);
    if (status != 0)
    {
        throw safenativemethods.gdip.statusexception(status);
    }
    return new graphics(zero) { backingimage = image };
}
     而在msdn中,对gdipgetimagegraphicscontext函数的描述有如下部分:

     this constructor also fails if the image uses one of the following pixel formats:

pixelformatundefined
pixelformatdontcare
pixelformat1bppindexed
pixelformat4bppindexed
pixelformat8bppindexed
pixelformat16bppgrayscale
pixelformat16bppargb1555
     因此,.net是判断当图像为索引模式时,直接返回错误,而不是通过判断gdipgetimagegraphicscontext的返回值来实现的。

     针对这个事实,我们其实觉得也无可厚非,graphics对象是用来干什么的,是用来向对应的image中添加线条,路径、实体图形、图像数据等的,而普通的索引图像,其矩阵的内容并不是实际的颜色值,而只是个索引,真正的颜色值在调色板中,因此,一些绘制的过程用在索引图像上存在着众多的不适。

     但是有个特列,那就是灰度图像,严格的说,灰度图像完全符合索引图像的格式,可以认为是索引图像的一种特例。但是我也可以认为他不属于索引图像一类:即他的图像数据总的值可以认为就是其颜色值,我们可以抛开其调色板中的数据。所以在photoshop中把索引模式和灰度模式作为两个模式来对待。

      真是有这个特殊性,一些画线、填充路径等等的过程应该可以在灰度图像中予以实现,单gdi+为了规避过多的判断,未对该模式进行特殊处理。

      但是,在一些特殊的场合,对灰度进行上述操作很有用途和意义。比如:在高级的图像设计中,有着选区的概念,而选区的实质上就是一副灰度图像,如果我们创建一个椭圆选区,设计上就是在灰度图像上填充了一个椭圆。如果能借助gdi+提供的优质的抗锯齿填充模式加上丰富自由的填充函数,那么就可以创建出多种多样的选区了。可.net的一个无法创建graphics让我们此路不通。

      有没有办法呢,其实也是有的,熟悉gdi+平板化api的人还知道有gdipcreatefromhdc函数,该函数可以从hdc中创建graphics。因此我的想法就是利用gdi的方式创建位图对象吗,然后从gdi的hdc中创建对应的graphics。经过实践,这种方法是可以行的。


  为此,我用gdi结合gdi+的方式创建了一个graybitmap类,该类的主要代码如下:

  unsafe class graybitmap
    {

        #region gdiapi

        private const int dib_rgb_colors = 0;
        private const int bi_rgb = 0;

        [structlayout(layoutkind.sequential, pack = 1)]
        private struct rgbquad
        {
            internal byte blue;
            internal byte green;
            internal byte red;
            internal byte reserved;
        }

        [structlayout(layoutkind.sequential, pack = 1)]
        private struct bitmapinfoheader
        {
            internal uint size;
            internal int width;
            internal int height;
            internal ushort planes;
            internal ushort bitcount;
            internal uint compression;
            internal uint sizeimage;
            internal int xpelspermeter;
            internal int ypelspermeter;
            internal uint clrused;
            internal uint clrimportant;
        }
        [structlayout(layoutkind.sequential, pack = 1)]
        private struct bitmapinfo
        {
            internal bitmapinfoheader header;
            [marshalas(unmanagedtype.byvalarray, sizeconst = 256)]
            internal rgbquad[] palette;
        }

        [structlayout(layoutkind.sequential)]
        internal struct logpalette
        {
            internal ushort palversion;
            internal ushort palnumentries;
            [marshalas(unmanagedtype.byvalarray, sizeconst = 0x400)]
            internal byte[] palpalentry;
        }

        [dllimport("user32.dll", setlasterror = true)]
        private extern static intptr getdc(intptr hwnd);

        [dllimport("user32.dll", setlasterror = true)]
        private extern static int releasedc(intptr hwnd, intptr hdc);

        [dllimport("gdi32.dll", setlasterror = true)]
        private extern static intptr createcompatibledc(intptr hdc);

        [dllimport("gdi32.dll", setlasterror = true)]
        private static extern uint setdibcolortable(intptr hdc, int un1, int un2, rgbquad[] pcrgbquad);

        [dllimport("gdi32.dll", setlasterror = true)]
        private static extern intptr createdibsection(intptr hdc, ref bitmapinfo bmpinfo, uint iusage, out byte* ppvbits, intptr hsection, uint dwoffset);

        [dllimport("gdi32.dll", setlasterror = true)]
        private extern static boolean deletedc(intptr hdc);

        [dllimport("gdi32.dll", setlasterror = true)]
        private extern static intptr selectobject(intptr hdc, intptr object);
 
        [dllimport("gdi32.dll", setlasterror = true)]
        private static extern bool deleteobject(intptr object);


     
        #endregion

        #region privatevariable

        private int m_width = 0;
        private int m_height = 0;
        private int m_stride = 0;
        private intptr m_hdc = intptr.zero;
        private graphics m_graphics = null;
        private intptr m_handle = intptr.zero;
        private byte* m_pointer = null;
        private bitmap m_bitmap = null;
        private bool disposed = false;

        #endregion

        #region property

        public int width { get { return m_width; } }
        public int height { get { return m_height; } }
        public int stride { get { return m_stride; } }
        public intptr handle { get { return m_handle; } }
        public intptr hdc { get { return m_hdc; } }
        public graphics graphics { get { return m_graphics; } }
        public byte* pointer { get { return m_pointer; } }
        public bitmap bitmap { get { return m_bitmap; } }

        #endregion

        #region constructor

        public graybitmap(int width, int height)
        {
            allocatebitmap(width, height);
        }

        public graybitmap(string filename)
        {
            bitmap bmp = (bitmap)bitmap.fromfile(filename);
            if (isgraybitmap(bmp) == false)
            { 
                bmp.dispose();
                throw new exception("wrong pixelformat");
            }
            else
            {
                allocatebitmap(bmp.width, bmp.height);
                bitmapdata bmpdata = new bitmapdata();
                bmpdata.scan0 = (intptr)m_pointer;
                bmpdata.stride = m_stride;                                  // 把image对象的数据拷贝到dibseciton中去
                bmp.lockbits(new rectangle(0, 0, bmp.width, bmp.height), imagelockmode.readwrite | imagelockmode.userinputbuffer, bmp.pixelformat, bmpdata);
                bmp.unlockbits(bmpdata);
                bmp.dispose();
            }
        }

        public graybitmap(bitmap bmp)
        {
            if (isgraybitmap(bmp) == false)
                throw new exception("wrong pixelformat");
            else
            {
                allocatebitmap(bmp.width, bmp.height);
                bitmapdata bmpdata = new bitmapdata();
                bmpdata.scan0 = (intptr)m_pointer;
                bmpdata.stride = m_stride;                                  // 把image对象的数据拷贝到dibseciton中去
                bmp.lockbits(new rectangle(0, 0, bmp.width, bmp.height), imagelockmode.readwrite | imagelockmode.userinputbuffer, bmp.pixelformat, bmpdata);
                bmp.unlockbits(bmpdata);
            }
        }

        ~graybitmap()
        {
            dispose();
        }

        #endregion

        #region publicmethod

        public static graybitmap fromfile(string filename)
        {
            graybitmap bmp = new graybitmap(filename);
            return bmp;
        }

        public void dispose()
        {
            dispose(true);
        }

        protected void dispose(bool suppress = true)
        {
            if (disposed == false)
            {
                disposed = true;
                if (m_hdc != intptr.zero) deletedc(m_hdc); m_hdc = intptr.zero;
                if (m_graphics != null) m_graphics.dispose(); m_graphics = null;
                if (m_bitmap != null) m_bitmap.dispose(); m_bitmap = null;
                if (m_handle != intptr.zero) deleteobject(m_handle); m_handle = intptr.zero;
                m_width = 0; m_height = 0; m_stride = 0; m_pointer = null;
                if (suppress == true) gc.suppressfinalize(this);
            }
        }

        #endregion

        #region privatemethod

        private void allocatebitmap(int width, int height)
        {
            if (width <= 0) throw new argumentoutofrangeexception("width", width, "width must be >=0");
            if (height <= 0) throw new argumentoutofrangeexception("height", height, "height must be >=0");

            bitmapinfo bmpinfo = new bitmapinfo();
            bmpinfo.header.size = (uint)sizeof(bitmapinfoheader);
            bmpinfo.header.width = width;
            bmpinfo.header.height = -height;                                    // 为了和gdi对象的坐标(起点坐标在左上角),建立一个倒序的dib
            bmpinfo.header.bitcount = (ushort)8; ;
            bmpinfo.header.planes = 1;
            bmpinfo.header.compression = bi_rgb;                                //  创建dibsection必须用不压缩的格式
            bmpinfo.header.xpelspermeter = 0;                                   // createdibsection does not use the bitmapinfoheader parameters bixpelspermeter or biypelspermeter and will not provide resolution information in the bitmapinfo structure.
            bmpinfo.header.ypelspermeter = 0;
            bmpinfo.header.clrused = 0;
            bmpinfo.header.sizeimage = 0;
            bmpinfo.header.clrimportant = 0;
            bmpinfo.header.sizeimage = 0;
            bmpinfo.palette = new rgbquad[256];
            for (int x = 0; x <256 ; x++)                                      //   for (byte x=0;x<=255;x++)  用这个代码试试,呵呵
            {
                bmpinfo.palette[x].red = (byte)x;
                bmpinfo.palette[x].green = (byte)x;
                bmpinfo.palette[x].blue = (byte)x;
                bmpinfo.palette[x].reserved = 255;
            }
            intptr screecdc = getdc(intptr.zero);
            m_hdc = createcompatibledc(screecdc);
            releasedc(intptr.zero, screecdc);
            m_handle = createdibsection(hdc, ref bmpinfo, dib_rgb_colors, out m_pointer, intptr.zero, 0);
            if (m_handle == intptr.zero)
            {
                deletedc(m_hdc);
                m_hdc = intptr.zero;
                throw new outofmemoryexception("createdibsection function failed,this may be caused by user input too large size of image.");
            }
            else
            {
                selectobject(m_hdc, m_handle);
                setdibcolortable(m_hdc, 0, 256, bmpinfo.palette);
                m_width = width; m_height = height; m_stride = (int)((m_width + 3) & 0xfffffffc);
                m_graphics = graphics.fromhdc(m_hdc);
                m_bitmap = new bitmap(m_width, m_height, m_stride, pixelformat.format8bppindexed, (intptr)m_pointer);
                colorpalette pal = m_bitmap.palette;
                for (int x = 0; x < 256; x++) pal.entries[x] = color.fromargb(255, x, x, x);       // 设置灰度图像的调色板  
                m_bitmap.palette = pal;
            }
        }

        private bool isgraybitmap(bitmap bmp)
        {
            bool isgray;
            if (bmp.pixelformat == pixelformat.format8bppindexed)
            {
                isgray = true;
                if (bmp.palette.entries.length != 256)
                    isgray = false;
                else
                {
                    for (int x = 0; x < bmp.palette.entries.length; x++)
                    {
                        if (bmp.palette.entries[x].r != bmp.palette.entries[x].g || bmp.palette.entries[x].r != bmp.palette.entries[x].b || bmp.palette.entries[x].b != bmp.palette.entries[x].g)
                        {
                            isgray = false;
                            break;
                        }
                    }
                }
            }
            else
            {
                isgray = false;
            }
            return isgray;
        }
        #endregion

    }
  正如上面所述,我们用gdi的方式(createdibsection)创建灰度图像,然后从hdc中创建graphics,从而可以顺利的调用graphics的任何绘制函数了。

  比如填充椭圆:

    solidbrush sb = new solidbrush(color.fromargb(255, 255, 255, 255));
    bmp.graphics.smoothingmode = system.drawing.drawing2d.smoothingmode.antialias;
    bmp.graphics.fillellipse(sb, new rectangle(100, 100, 200, 300));
    sb.dispose();
    canvas.invalidate();

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网