如何使用 GDI + 以新的颜色表保存.gif 文件

文章翻译 文章翻译
文章编号: 315780 - 查看本文应用于的产品
展开全部 | 关闭全部

本文内容

概要

为这篇文章的一个 Microsoft Visual C#.net 版本,请参阅 319061
为这篇文章的一个 Microsoft Visual Basic.net 版本,请参阅 319591
CompuServe 图形交换格式 (GIF) 的最大为 256 色颜色表中排列的设计。若要将公共修改.gif 图像文件中,您必须更改自定义颜色表。但是,GDI + 编辑 Image 对象并将图像保存与 GIF 编码器然后询问时, 生成的.gif 文件将包含半色调颜色表。

若要使用 GIF 编码器以自定义颜色表中保存 图像,您必须使用 GDI + 未修改 图像 的 256 色副本。

更多信息

.gif 图像文件可以表示最多 256 种颜色。因为颜色是缺乏资源在该.gif 文件,优化这些颜色是通常所请求的任务。若要影响一个优化的颜色表,您必须能够在.gif 文件中设置任何任意的自定义颜色表。

GDI + 修改 图像,然后通过使用 GIF 编码器将图像写入到文件后,GDI + 写入该文件使用半色调调色板 图像 对象的位已减少的颜色。GDI + 不会从 32 位-每像素 (32 BPP) 的颜色转换时它将写入图像文件因为用 GDI + 32 BPP 图形引擎会应用到图像的所有修改。

尽管 GDI + 支持创建的 图像 和各种像素格式的 位图,并因此可以加载为.gif 图像,32 BPP 图形引擎的使用将只有到 32 BPP 转换时通过 GDI + 被修改。但是,映像位图 的是 修改的 GDI + 保留其原始的像素格式中,并可写入的文件与相应的编码器使用 Save 方法。该属性形成的一种技术,可以将 图像 保存到具有自定义颜色表的.gif 文件。

可以使用 GIF 编码器书写一个未被修改的 位图,并使 位图 颜色表不变 ; 因此,您可以使用此方法以使用新的颜色表中保存一个.gif 文件。

方法是将图像数据从原始 Image 对象复制到临时的 位图 对象。8-BPP 索引 位图,这是用于保存.gif 文件的像素格式时,会创建此临时的 位图位图 颜色表由使用 SetPalette 方法设置,然后将该图像定义被复制到临时的 位图。使用重复的定义创建临时的 位图 后,您可以使用 Save 方法保存它与 GIF 为编码器保持已经 8 BPP 颜色表。

若要写入一个.gif 图像带有自定义颜色表的文件,请按照下列步骤操作:
  1. 创建一个重复的 位图 对象,它是与源 图像 的大小相同。
  2. 位图 对象的自定义颜色表设置为所需的颜色表。
  3. 使用 LockBits 方法来获得对图像位副本的写访问权限。
  4. 通过颜色索引写入从复制中原始 图像 像素的 LockBits 中获得的内存,在副本中创建一个图像定义。
  5. 使用 UnLockBits 释放图像位。
  6. 图像 保存到文件中,通过使用 保存 和 GIF 编码器 位图 副本使用与自定义颜色表。
  7. 释放 图像位图 副本。

使用示例代码

在这篇文章中的代码示例演示了如何使用 Bitmap.Save 编写.gif 文件使用任意大小的自定义颜色表。因为对于仅演示的目的在于,代码不是针对性能优化。优化最佳的销售机会正在处理循环像素。GetPixel 是像素格式的一个方便的抽象,但它是非常慢。该代码示例就是如果您使用 LockBits 直接访问的像素格式快得多。若要提高其速度,不要使用 GetPixel 方法和 $ 颜色 类抽象。若要提高性能,重写通过使用整数数学,而不是浮点的灰度转换。

该示例函数采用以下五个参数:
  • GDI + 图像 的任何对象。
  • 目标文件的文件名。
  • GIF 编码器类标识符 (CLSID)。
  • 为该.gif 文件的颜色数。
  • 指示是否需要一个透明的颜色标志。
有关 GDI + GetEncoderClsid 函数,并为下载的代码示例,请参阅 MSDN 帮助文档在下面的链接的详细信息:

平台 SDK: GDI +-Image::Save 文件名、 clsidEncoder encoderParams)
http://msdn.microsoft.com/en-us/library/ms533843.aspx
该函数首先创建一个 位图 对象,因为这是使用 nColors,创建.gif 文件保存的对象有 PixelFormat8BPPIndexed 的像素格式,则会创建与自定义颜色的调色板。.gif 文件从 位图 对象的 ColorPalette 获取大小和它的颜色表的特定项。该代码示例创建用于演示目的的灰度,因为该算法很容易上各种颜色表大小进行扩展。

若要进行.gif 文件必须与要写入到文件的图像定义 8 BPP 位图 对象进行初始化。该代码示例在一个中央组循环是用于颜色到实质上是黑色和白色电视的颜色空间转换传入的图像。

出于演示目的的一个 位图 对象,该对象是一份源图像 GetPixel 方法的方式访问源图像像素为单位)。位图 副本因为 Image 类不实现 GetPixel 方法。

使用其他方法可以访问该像素如直接访问通过使用 Windows GDI DIB 节LockBits 方法或互操作像素。当您将位图从 Gdi + HDC GDI DIB 部分内存的域控制器使用 BitBlt 函数时,GBitBlt 函数将使用 GDI 颜色匹配的能力。

创建 位图 副本后,使用 Save 方法提供的 GIF CLSID 写入目标文件的位图。

使用少于 256 色的 GIF 文件

在 GDI + 1.0 版中的 GIF 编解码器编码只 GDI + 图像 8 BPP 的。所有其他 图像 格式将转换之前编码。此代码来编写具有少于 256 色,因为 GIF 编解码器能够识别由 ColorPalette.Count 属性中包含少于 256 种颜色的 8 BPP 位图 对象的.gif 文件使用 8 BPP 位图 格式。

GIF 编解码器像素格式有关的详细信息,请参阅本文的"References"一节。

该代码将图像的像素定义复制到 8 BPP 位图 将处理循环中将考虑调色板的大小时代码计算一个像素的索引值。GIF 编解码器在调色板的大小限制和限制图像定义以与调色板大小 (也就是潜在 GIF 颜色表),兼容,因此可以使用少于 256 色创建.gif 文件) 的索引值。

GIF 的透明度

在代码示例 ColorPalette 创建例程设置第一项是 GIF 透明的颜色来演示使用透明功能。该代码执行此任务的方法是设置为零的 Alpha 颜色 项的组件。在这篇文章中的代码示例是仅用于演示目的,因此,透明度颜色则任意选择此选项,并可能有意外的结果完全依赖于源 图像

GIF 编码器标识 ColorPalette 具有零作为透明颜色的 Alpha 值在第一种颜色。这意味着要 ColorPalette 中的第一个条目的用户不能具有透明的颜色。它可以是任何一种可能的 256 色,在调色板上了 condition 上面的所有项都包含非零值的 Alpha 组件。为零的 Alpha 分量值与任何更高版本条目都将被忽略。具有非零的 Alpha 组件的所有项都被都视为不透明。

: 时使用此代码来覆盖现有文件,您可能会看到生成的文件的大小有问题。由于 GDI + 不截断该文件的版本 1.0 中的错误的发生这种情况。

图像文件的大小有关的详细信息,请参阅本文的"References"一节。尽管存在引用该文章讨论了.net 框架的 位图System.Drawing 命名空间中的使用,该主题将适用于 GDI + 因为 System.Drawing 由 GDI + 实现也是如此。

GIF/lzw 授权问题

Microsoft 已经使用.gif 文件格式和其他 LZW 技术涵盖的 Unisys 拥有美国和许多 Microsoft 产品中的外专利 Unisys 获取许可证。但是,此许可证不会扩展到第三方开发人员使用 Microsoft 开发产品或工具包来开发应用程序。作为一个第三方开发人员,您需要确定是否必须从使用 GIF 格式或 LZW 技术 Unisys 获得许可证。

LZW 许可证和 $ GIF 有关的其他信息请单击下面文章编号,以查看 Microsoft 知识库中相应的文章:
193543信息: Unisys GIF 和 LZW 技术许可证信息

示例代码

Status SaveGIFWithNewColorTable(
  Image *pImage,
  const WCHAR* filename,
  const CLSID* clsidEncoder,
  DWORD nColors,
  BOOL fTransparent
)
{
    Status stat = GenericError;

    // GIF codec supports 256 colors maximum.
    if (nColors > 256)
        nColors = 256;
    if (nColors < 2)
        nColors = 2;

    // Make a new 8-BPP pixel indexed bitmap that is the same size as the source image.
    DWORD   dwWidth = pImage->GetWidth();
    DWORD   dwHeight = pImage->GetHeight();

    // Always use PixelFormat8BPPIndexed, because that is the color table
    // based interface to the GIF codec.
    Bitmap  bitmap(dwWidth, dwHeight, PixelFormat8BppIndexed); 

    stat = bitmap.GetLastStatus();

    if (stat != Ok)
        return stat;        // No point in continuing.

    // Allocate the new color table.
    DWORD   dwSizeColorPalette;
    dwSizeColorPalette = sizeof(ColorPalette);      // Size of core structure.
    dwSizeColorPalette += sizeof(ARGB)*(nColors-1);   // The other entries.

    // Allocate some raw space to back the ColorPalette structure pointer.
    ColorPalette *ppal = (ColorPalette *)new BYTE[dwSizeColorPalette];
    if (ppal == NULL) return OutOfMemory;

    ZeroMemory(ppal, dwSizeColorPalette);

    // Initialize a new color table with entries that are determined by
    // some optimal palette finding algorithm; for demonstration 
    // purposes, just do a grayscale. 
    if (fTransparent)
        ppal->Flags = PaletteFlagsHasAlpha;
    else
        ppal->Flags = 0; 
    ppal->Flags |= PaletteFlagsGrayScale;
    ppal->Count = nColors;
    for (UINT i = 0; i < nColors; i++)
    {
        int Alpha = 0xFF;       // Colors are opaque by default.
        int Intensity = i*0xFF/(nColors-1); // even distribution 

        // The GIF encoder makes the first entry in the palette with a
        // zero alpha the "transparent" color in the GIF.
        // For demonstration purposes, the first one is picked arbitrarily.

        if ( i == 0 && fTransparent) // Make this color index...
            Alpha = 0;          // Transparent
        
        // Create a gray scale for demonstration purposes.
        // Otherwise, use your favorite color reduction algorithm
        // and an optimum palette for that algorithm generated here.
        // For example, a color histogram, or a median cut palette.
        ppal->Entries[i] = Color::MakeARGB( Alpha, 
                                            Intensity, 
                                            Intensity, 
                                            Intensity );
    }

    // Set the palette into the new Bitmap object.
    bitmap.SetPalette(ppal);


    // Use GetPixel below to pull out the color data of Image.
    // Because GetPixel isn't defined on an Image, make a copy in a Bitmap 
    // instead. Make a new Bitmap that is the same size of the image that
    // you want to export. Otherwise, you might try to interpret the native 
    // pixel format of the image by using a LockBits call.
    // Use PixelFormat32BppARGB so that you can wrap a Graphics around it.
    Bitmap BmpCopy(dwWidth, dwHeight, PixelFormat32BppARGB); 
    stat = BmpCopy.GetLastStatus();
    if (stat == Ok)
    {
        Graphics g(&BmpCopy);

        // Transfer the Image to the Bitmap.
        stat = g.DrawImage(pImage, 0, 0, dwWidth, dwHeight);

        // g goes out of scope here and cleans up.
    }

    if (stat != Ok)
    {
        delete [] (LPBYTE) ppal;
        ppal = NULL;
        return stat;
    }

    // Lock the whole of the bitmap for writing.
    BitmapData  bitmapData;
    Rect        rect(0, 0, dwWidth, dwHeight);

    stat = bitmap.LockBits(
      &rect,
      ImageLockModeWrite,
      PixelFormat8BppIndexed,
      &bitmapData);

    if (stat == Ok)
    {
        // Write to the temporary buffer provided by LockBits.
        // Copy the pixels from the source image in this loop.
        // Because you want an index, convert RGB to the appropriate
        // palette index here.
        BYTE *pixels;
        if (bitmapData.Stride > 0)
            pixels = (BYTE*) bitmapData.Scan0;
        else
            // If the Stride is negative, Scan0 points to the last             // scanline in the buffer.
            // To normalize the loop, obtain the start of the buffer,
            // which is located (Height-1) scanlines previous.
            pixels = (BYTE*) bitmapData.Scan0 + bitmapData.Stride*(dwHeight-1);
        UINT stride = abs(bitmapData.Stride);

        // Top-down and bottom-up is not relevant to this algorithm.

        for(UINT row = 0; row < dwHeight; ++row)
        {
          for(UINT col = 0; col < dwWidth; ++col)
          {
              // Map palette indexes for a grayscale.
              // If you use some other technique to color convert,
              // put your favorite color reduction algorithm here.
              Color     pixel;
              UINT      i8BppPixel = row * stride + col;

              BmpCopy.GetPixel(col, row, &pixel);

              // Use luminance/chrominance conversion to get grayscale.
              // Basically, turn the image into black and white TV: YCrCb.
              // You do not have to to calculate Cr or Cb because you 
              // throw away the color anyway.
              // Y = Red * 0.299 + Green * 0.587 + Blue * 0.114

              // This expression is best as integer math for performance,
              // however, because GetPixel listed earlier is the slowest 
              // part of this loop, the expression is left as 
              // floating point for clarity.
              double luminance = (pixel.GetRed() * 0.299) +
                                (pixel.GetGreen() * 0.587) +
                                (pixel.GetBlue() * 0.114);

              // Gray scale is an intensity map from black to white.
              // Compute the index to the gray scale entry that  
              // approximates the luminance, and then round the index.      
              // Also, constrain the index choices by the number of colors to do
              pixels[i8BppPixel] = (BYTE)(luminance * (nColors-1)/255 +0.5);
          }
        }
    // To commit the changes, unlock the portion of the bitmap.  
        stat = bitmap.UnlockBits(&bitmapData);
    }

    // If destination work was successful, see whether the source was successful.
    if (stat == Ok) stat = BmpCopy.GetLastStatus();

    // See whether the code has been successful to this point.
    if (stat == Ok)
    {
    // Write it out to the disk.
        stat =  bitmap.Save(filename, clsidEncoder, NULL);
    }

    // Clean up after yourself.
    delete [] (LPBYTE) ppal;
    ppal = NULL;
    // BmpCopy goes away on its own when it falls out of scope.

    return stat;
}
				

有关示例代码

Microsoft 提供的编程示例只,用于说明不附带任何明示或暗示保证,其中包括但不是限于对适销性和/或针对特定用途的适用性的暗示的担保。本文假定您熟悉所演示的编程语言和工具用于创建和调试过程。Microsoft 支持专业人员可以帮助解释某个特定过程的功能,但他们不会修改这些示例以提供额外的功能或构建过程来满足您特定需要。
如果有限编程体验您可能需要联系 Microsoft 认证合作伙伴或 Microsoft 咨询服务。有关更多的信息请访问以下 Microsoft 网站:

Microsoft 认证合作伙伴-https://partner.microsoft.com/global/30000104

Microsoft 咨询服务-http://support.microsoft.com/gp/advisoryservice

有关可用的支持选项和有关如何与 Microsoft 联系的详细信息,请访问下面的 Microsoft 网站: http://support.microsoft.com/default.aspx?scid=fh;EN-US;CNTACTMS

参考

GIF 编解码器像素格式有关的其他信息,请单击下面文章编号,以查看 Microsoft 知识库中相应的文章:
318343信息: GDI + GIF 文件保存使用 8 BPP 格式
图像文件的大小有关的其他信息,请单击下面文章编号,以查看 Microsoft 知识库中相应的文章:
312119PRB: 位图类的保存方法不会截断文件大小
虽然在此链接中引用的文章讨论了.net 框架的 位图System.Drawing 命名空间中使用,该主题将适用于 GDI + 因为 System.Drawing 由 GDI + 实现也是如此。

术语表

BPP
每像素的位数位用于表示数字化的图像中的每个像素的颜色值,描述图像中的每个像素的颜色定义的物理布局。通用和一般情况下被引用的像素格式包括 32 BPP、 24 BPP、 16 BPP、 8 BPP、 4 BPP、 1 BPP。
8 位
表示为一个字节中包含的八位的图像像素格式。字节值用作索引到包含实际的红-绿-蓝 (RGB) 颜色定义的颜色表。因为索引的大小的一个字节,颜色表将仅限于 256 种颜色。
GIF
图形交换格式-由 CompuServe 创建一个可流式处理的图像文件格式。
RGB
红色、 绿色和蓝色-每个通常表示为一个字节,这导致颜色 3 字节三重。

属性

文章编号: 315780 - 最后修改: 2007年2月12日 - 修订: 3.5
这篇文章中的信息适用于:
  • Microsoft GDI+ 1.0
  • Microsoft Windows XP Professional Edition
  • the operating system: Microsoft Windows XP 64-Bit Edition
关键字:?
kbmt kbdswgdi2003swept kbbitmap kbgdiplus kbhowto KB315780 KbMtzh
机器翻译
注意:这篇文章是由无人工介入的微软自动的机器翻译软件翻译完成。微软很高兴能同时提供给您由人工翻译的和由机器翻译的文章, 以使您能使用您的语言访问所有的知识库文章。然而由机器翻译的文章并不总是完美的。它可能存在词汇,语法或文法的问题,就像是一个外国人在说中文时总是可能犯这样的错误。虽然我们经常升级机器翻译软件以提高翻译质量,但是我们不保证机器翻译的正确度,也不对由于内容的误译或者客户对它的错误使用所引起的任何直接的, 或间接的可能的问题负责。
点击这里察看该文章的英文版: 315780
Microsoft和/或其各供应商对于为任何目的而在本服务器上发布的文件及有关图形所含信息的适用性,不作任何声明。 所有该等文件及有关图形均"依样"提供,而不带任何性质的保证。Microsoft和/或其各供应商特此声明,对所有与该等信息有关的保证和条件不负任何责任,该等保证和条件包括关于适销性、符合特定用途、所有权和非侵权的所有默示保证和条件。在任何情况下,在由于使用或运行本服务器上的信息所引起的或与该等使用或运行有关的诉讼中,Microsoft和/或其各供应商就因丧失使用、数据或利润所导致的任何特别的、间接的、衍生性的损害或任何因使用而丧失所导致的之损害、数据或利润不负任何责任。

提供反馈

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com