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

.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的方式创建位图对象吗,然后从GDIHDC中创建对应的Graphics。经过实践,这种方法是可以行的。

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

  unsafe class GrayBitmap{#region GDIAPIprivate 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 PrivateVariableprivate 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 Propertypublic 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 Constructorpublic 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 PublicMethodpublic 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 PrivateMethodprivate 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对象的坐标系统(起点坐标在左上角),建立一个倒序的DIBBmpInfo.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();


心细的朋友可以在测试中会发现,通过这种方式绘制的颜色可能和指定的颜色有所不同,比如上面我们要求绘制白色的椭圆,但是实际绘制的颜色是RGB(252,252,252)的,但是并不是所有的颜色都有误差,引起这个的原因估计还是GDI+的内部的一些机制上的问题吧。

工程完整代码:http://files.cnblogs.com/Imageshop/GrayModeBitmap.rar

无法从带有索引像素格式的图像创建graphics对象 问题的解决方案。相关推荐

  1. 无法从带有索引像素格式的图像创建 Graphics 对象

    在使用Graphics.FromImage方法的时候,如果传入的System.Drawing.Image图像是索引像素格式,那么将会引发异常并提示以下消息:"无法从带有索引像素格式的图像创建 ...

  2. 绘图操作时报错“无法从带有索引像素格式的图像创建 Graphics 对象”

    如下代码会出现"无法从带有索引像素格式的图像创建 Graphics 对象"异常 Bitmap bitmap = new Bitmap(fileName); Graphics gra ...

  3. 对索引像素格式的图片进行Setpixel(具有索引像素格式的图像不支持SetPixel)解决方案

    对索引像素格式的图片进行Setpixel(具有索引像素格式的图像不支持SetPixel)解决方案 参考文章: (1)对索引像素格式的图片进行Setpixel(具有索引像素格式的图像不支持SetPixe ...

  4. c#_具有索引像素格式的图像不支持 SetPixel

    首先呢我们要明白各种图片格式,不同的图片格式成像也是不同的,对于索引类型的图片,其文件头部有一个颜色表,这个表按照一定的规律存储了所有的可能在这张图片中出现的颜色.它的每一个点的像素值(ARGB)并不 ...

  5. Python数字图像处理---1.1图像的像素格式与图像读写

    目录 前言 图像像素格式 图像读写 前言 本专栏面向所有希望或有兴趣从事数字图像处理工作.学习或研究的朋友,编程语言采用了当下最火的Python语言. Python是一种跨平台的计算机设计语言,也是一 ...

  6. C语言数字图像处理---1.1图像的像素格式与图像读写

    前言 本专栏面向所有希望或有兴趣从事数字图像处理工作.学习或研究的朋友,不懂MATLAB和OPENCV没关系,仅需要基础的C语言知识,便可以通过本专栏内容轻松入门数字图像处理方向.目前市面上的数字图像 ...

  7. 【Android 内存优化】自定义组件长图组件 ( 获取图像宽高 | 计算解码区域 | 设置图像解码属性 复用 像素格式 | 图像绘制 )

    文章目录 一.获取图像真实宽高 二.计算解码区域 三.设置解码参数 内存复用 像素格式 四.图像绘制 五.执行效果 六.源码及资源下载 官方文档 API : BitmapRegionDecoder 在 ...

  8. [译]在cocos2d里面如何使用Texture Packer和像素格式来优化 spritesheet

    免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作 ...

  9. 在 cocos2d 里面如何使用 Texture Packer 和像素格式来优化 spritesheet

    原文链接:http://www.cnblogs.com/andyque/archive/2011/03/18/1988097.html 免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网 ...

最新文章

  1. 苏教版四下用计算机计算,苏教版四年级下册数学单元测试-4.用计算器计算 (含答案)...
  2. python画柱形图-Python绘制精美图表之双柱形图
  3. C/C++ 程序设计员应聘常见面试试题深入剖析
  4. ad域 禁用账号_通过AD域控平台统一批量管理公司电脑的桌面
  5. linux ls只显示文件名或者文件夹名
  6. FI模块组织机构配置文档
  7. 反弹shell与正向shell的区别
  8. 中石油训练赛 - 腿部挂件(可持久化字典树)
  9. 2362:Square 能否拼接为正方形 . DFS+回溯
  10. 基于递归的前序二叉树遍历实现
  11. 计算机维护费可以跨年吗,航天信息服务费可以跨年抵扣吗
  12. PM981/PM981a安装黑苹果-VM Ware教程
  13. icom对讲机写频线定义_自制电台通用写频线(完整版).pdf
  14. JAVA - 银行卡认证
  15. LaTeX数学表达式
  16. wamp5工具安装介绍
  17. 计算机电子科学技术产品,关于电子信息科学技术发展现状的思考
  18. Golang 数组的一些自问自答
  19. 利用pyecharts绘制新浪微博传播图(文末附完整代码地址)
  20. u盘打开提示格式化怎么办?有用的方法来了

热门文章

  1. opencv实现图片中文字识别并切割
  2. OpenResty的现状趋势使用学习方法
  3. java双重for循环流程图_Java的for循环
  4. 给在读研究生的一封信(转)
  5. LTE学习之路(1)——移动通信技术发展历程
  6. speedoffice使用方法-Word怎么修改行间距
  7. 01-在线挑战详细攻略-《我很简单,请不要欺负我》 http://bbs.ichunqiu.com/thread-1783-1-1.html (出处: i春秋社区-分享你的技术,为安全加点温度)
  8. 一款帮助用户管理桌面的Mac桌面管理软件
  9. 【C++】优先级队列priority_queue/仿函数(函数对象)
  10. ANR问题的分析与解决思路