在.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 != )
{
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 = ;
private const int BI_RGB = ; [StructLayout(LayoutKind.Sequential, Pack = )]
private struct RGBQUAD
{
internal byte Blue;
internal byte Green;
internal byte Red;
internal byte Reserved;
} [StructLayout(LayoutKind.Sequential, Pack = )]
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 = )]
private struct BITMAPINFO
{
internal BITMAPINFOHEADER Header;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = )]
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 = ;
private int m_Height = ;
private int m_Stride = ;
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(, , 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(, , 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 = ; m_Height = ; m_Stride = ; m_Pointer = null;
if (Suppress == true) GC.SuppressFinalize(this);
}
} #endregion #region PrivateMethod private void AllocateBitmap(int Width, int Height)
{
if (Width <= ) throw new ArgumentOutOfRangeException("Width", Width, "Width must be >=0");
if (Height <= ) 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); ;
BmpInfo.Header.Planes = ;
BmpInfo.Header.Compression = BI_RGB; // 创建DIBSection必须用不压缩的格式
BmpInfo.Header.XPelsPerMeter = ; // CreateDIBSection does not use the BITMAPINFOHEADER parameters biXPelsPerMeter or biYPelsPerMeter and will not provide resolution information in the BITMAPINFO structure.
BmpInfo.Header.YPelsPerMeter = ;
BmpInfo.Header.ClrUsed = ;
BmpInfo.Header.SizeImage = ;
BmpInfo.Header.ClrImportant = ;
BmpInfo.Header.SizeImage = ;
BmpInfo.Palette = new RGBQUAD[];
for (int X = ; X < ; 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 = ;
}
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, );
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, , , BmpInfo.Palette);
m_Width = Width; m_Height = Height; m_Stride = (int)((m_Width + ) & 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 = ; X < ; X++) Pal.Entries[X] = Color.FromArgb(, 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 != )
IsGray = false;
else
{
for (int X = ; 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(, , , ));
Bmp.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Bmp.Graphics.FillEllipse(SB, new Rectangle(, , , ));
SB.Dispose();
Canvas.Invalidate();

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

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

希望朋友们喜欢我的文章。

***************************作者: laviewpbt   时间: 2013.7.13   联系QQ:  33184777  转载请保留本行信息*************************

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

  1. 无法从带有索引像素格式的图像创建graphics对象(转)

    大家在用 .NET 做图片水印功能的时候, 很可能会遇到 “无法从带有索引像素格式的图像创建graphics对象”这个错误,对应的英文错误提示是“A Graphics object cannot be ...

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

    大家在用 .NET 做图片水印功能的时候, 很可能会遇到 “无法从带有索引像素格式的图像创建graphics对象”这个错误,对应的英文错误提示是“A Graphics object cannot be ...

  3. .Net给图片加水印,并解决“无法从带有索引像素格式的图像创建Graphics对象”问题

    using (Image img = Image.FromFile(savePath)) { //如果原图片是索引像素格式之列的,则需要转换 if (img.PixelFormat!=null) { ...

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

    最近编写了一个验证码识别软件.其中对png.jpg图片进行二值化处理时,出现了错误:具有索引像素格式的图像不支持SetPixel解决方案.从字面上来看,这说明我对一个具有索引色的图片进行了直接RGB颜 ...

  5. IntelliJ IDEA创建文件时自动填入作者时间 定制格式

    IntelliJ IDEA创建文件时自动填入作者时间 定制格式 学习了:https://blog.csdn.net/Hi_Boy_/article/details/78205483 学习了:http: ...

  6. .net下灰度模式图像

    .net下灰度模式图像在创建Graphics时出现:无法从带有索引像素格式的图像创建graphics对象 问题的解决方案. Posted on 2013-07-13 14:23 Imageshop 阅 ...

  7. 改变MyEclipse创建JSP时默认的pageEncoding编码

    如何改变MyEclipse创建JSP时默认的pageEncoding编码 有时我们需要改变MyEclipse创建JSP时默认的pageEncoding编码,因为也许它默认的编码不是我们想要的,比如我们 ...

  8. Python图像处理丨基于OpenCV和像素处理的图像灰度化处理

    摘要:本篇文章讲解图像灰度化处理的知识,结合OpenCV调用cv2.cvtColor()函数实现图像灰度操作,使用像素处理方法对图像进行灰度化处理. 本文分享自华为云社区<[Python图像处理 ...

  9. Ubuntu 16.04下使用Eclipse:创建工程时卡死的解决方法

    问题如下: Ubuntu 16.04下使用Eclipse创建工程时出现卡顿和卡死,新建一个MapReduce项目卡了一下午,鼠标变成了圆圈进度条转了一下午,还关不掉. 当我直接去关闭新建项目的窗口时, ...

随机推荐

  1. CSS3和jQuery实现的自定义美化Checkbox

    效果图: 是不是比默认的好看多了,个人的审美观应该还是可以的. 当然我们可以在这里查看DEMO演示. 接下来我们一起来看看实现这款美化版Checkbox的源代码.主要思路是利用隐藏原来的checkbo ...

  2. 面积(area)

    题目描述 编程计算由"*"号围成的下列图形的面积.面积计算方法是统计*号所围成的闭合曲线中点的数目.如图所示,在10*10的二维数组中,“*”围住了15个点,因此面积为15. 0 ...

  3. Hibernate-chapter one

    ======什么是Hibernate???====== Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的 ...

  4. MySQL中进行模糊搜索的一些问题

    在搜索数据库中的数据时,SQL 通配符可以替代一个或多个字符.SQL 通配符必须与 LIKE 运算符一起使用.在 SQL 中,可使用以下通配符:通配符 描述       % 替代一个或多个字符     ...

  5. TinyMCE 官方插件一览表(不完全)

    TinyMCE 官方插件一览表:advlist(Advanced List Plugin):项目编号.toolbar:bullist.autolink:自动加链接.lists:This list pl ...

  6. CSS3 Transitions, Transforms和Animation的使用

    一.前言 CSS3动画相关的几个属性是:transition, transform, animation:分别理解为过渡,变换,动画.虽意义相近,但具体的功能和在CSS3中承担的工作有一定的差异. t ...

  7. ks

    http://www.codeproject.com/Articles/207820/The-Repository-Pattern-with-EF-code-first-Dependen

  8. list集合的排序Comparator和Collections.sort

    一个例子 package sortt; import java.util.ArrayList; import java.util.Collections; import java.util.Compa ...

  9. ios 开发之单例模式

    在iOS开发中,有很多地方都选择使用单例模式.有很多时候必须要创建一个对象,并且不能创建多个,用单例就为了防止创建多个对象.单例模式的意思就是某一个类有且只有一个实例.单例模式确保某一个类只有一个实例 ...

  10. 【代码笔记】iOS-推荐收听,左右两个tableView

    一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController ...