如何以新的色彩表來使用 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 + 寫入該檔案使用 Image 物件位元已被色彩減少的半色調調色盤。GDI + 並色彩轉換從 32 位元-每像素 (32 BPP) 時它將影像寫入檔案因為使用 GDI + 32 BPP 圖形引擎進行至影像的所有修改。

雖然 GDI + 支援建立的 影像] 及 [點陣圖 的各種不同的像素格式,並因此可以載入.gif 圖像,32 BPP 圖形引擎使用 GDI + 來修改它們時,必須轉換為 32 BPP。不過,影像點陣圖,是由 修改 GDI + 會保留其原始的像素格式中,而且可以寫到檔案,使用適當的編碼器 Save 方法。這個屬性會形成基礎的技巧,可以將 圖像 儲存至自訂色彩的表格.gif 檔案。

您可以撰寫與 GIF 編碼器未經修改的 點陣圖,並保持不變 點陣圖 色彩表 ; 因此,您可以使用這個方法,要將新的色彩表資料儲存.gif 檔案。

則方法會將影像資料從原始的 影像 物件複製到暫時的 點陣圖 物件。如 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 + 影像 物件。
  • 目標檔案的檔案名稱。
  • 類別識別項 (CLSID) 的 GIF 編碼器。
  • .gif 檔案的色彩數。
  • 旗標,指出是否需要透明的色彩。
如需詳細資訊,有關 GDI + GetEncoderClsid 函式和來下載範例程式碼詳細資訊,請參閱 MSDN 說明的說明文件,在下列連結:

平台 SDK: GDI +-Image::Save (檔案名稱、 clsidEncoder、 encoderParams)
http://msdn.microsoft.com/en-us/library/ms533843.aspx
函式首先建立 點陣圖 物件的有 PixelFormat8BPPIndexed 的像素格式,因為,是建立具有 nColors,.gif 檔案會被儲存的物件,然後建立使用自訂的色彩調色盤。.gif 檔案會從 點陣圖 物件 ColorPalette 取得大小和其色彩表的特定項目。範例程式碼會建立灰階供示範之用,因為該演算法易於擴充透過各種色彩資料表大小。

若要建立.gif 檔案,您必須初始化 8 BPP 點陣圖 物件與影像定義要寫入至檔案。將範例程式碼迴圈一個中央組是用來色彩將連入影像轉換成基本上是黑色和白色電視色彩空間。

供示範之用來源影像像素是藉由存取的 點陣圖 物件,它是一份來源影像 GetPixel 方法。因為 圖像 類別不實作 GetPixel 方法,是製作 點陣圖 複本。

您可以使用其他技術來存取像素例如藉由使用具有 Windows GDI DIB 區段LockBits 方法或 Interop 像素的直接存取。當您將一個點陣圖 Gdi + HDC 從複製到 GDI DIB 區段記憶體網域控制站使用 BitBlt 函數時,GBitBlt 函式會使用色彩比對的能力的 GDI。

建立 點陣圖 複本之後,使用具有所提供的 GIF CLSID 中的 [Save 方法將點陣圖寫入目標檔案。

具有少於 256 色的 GIF 檔案

在 GDI + 版本 1.0 GIF 轉碼器編碼只 GDI + 影像 是 8 BPP。所有其他的 影像 格式會轉換之前先編碼方式。這個程式碼會使用 8 BPP 點陣圖 格式寫入因為 GIF 轉碼器會辨識 8 BPP ColorPalette.Count 屬性所包含少於 256 色的 點陣圖 物件具有少於 256 色.gif 檔案。

有關更多 GIF 轉碼器像素格式,請參閱本文 < References > 一節。

將影像的像素定義複製到 8 BPP 點陣圖,處理迴圈中程式碼會考慮調色盤大小當程式碼會計算一個像素的索引值。GIF 轉碼器限制調色盤的大小,並會限制影像定義,以索引值,因此可以建立具有少於 256 色.gif 檔案並會與調色盤大小 (也就是潛在 GIF 色彩資料表),相容。

GIF 透明度

在將範例程式碼中 ColorPalette 建立常式會設定為 GIF 透明色彩,以示範 [透明度] 功能使用第一個項目。程式碼的運作方式是設定為零的 Alpha 元件 色彩 項目。本文中的範例程式碼是僅供示範,因此,透明色彩是任意的選擇,並可能有未預期的結果完全依賴來源 影像

GIF 編碼器識別第一個色彩在 ColorPalette 具有 Alpha 值為透明色彩的零。也就是說透明的色彩並不需要是 ColorPalette 中的第一個項目。在條件,先前的所有項目包含具有非零值的 Alpha 元件,它可以是任何一種可能的 256 色,在調色盤中。具有零的 Alpha 元件值任何較新項目都會被忽略。具有非零的 Alpha 元件的所有項目會被視為不透明的。

注意: 使用這個程式碼覆寫現有檔案時您可能會看到結果檔案的大小有問題。這是由於 GDI + 版本 1.0,並不會截斷檔案中有錯誤。

有關更多影像檔案大小,請參閱本文 < References > 一節。雖然那里參考文件討論 System.Drawing 命名空間 點陣圖 的類別的.NET Framework 使用,在主題適用於 GDI + 因為 System.Drawing 由 GDI + 來實作。

GIF/LZW 授權問題

Microsoft 已取得授權從 Unisys 使用.gif 檔案格式及 Unisys 擁有美國及數種 Microsoft 產品的外部索引專利涵蓋其他 LZW 技術。不過,此授權不會擴充協力廠商開發人員使用 Microsoft 開發產品或工具套件來開發應用程式。身為協力廠商開發人員,您需要判斷是否必須取得授權從 Unisys 使用 GIF 格式或 LZW 技術。

如需有關 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: 點陣圖類別的 Save 方法並不截斷檔案大小
雖然在這個連結中參考的文章討論 System.Drawing 命名空間 點陣圖 的類別的.NET Framework 使用,在主題適用於 GDI + 因為 System.Drawing 由 GDI + 來實作。

名詞解釋

BPP
每 1 像素-位元數位元用來表示的數位化影像中每個像素色彩值 ; 描述影像中的每個像素色彩定義的實體配置。常見和一般參考的像素格式包括 32 BPP、 24 BPP、 16 BPP、 8 BPP、 4 BPP、 1 BPP。
8 BPP
影像像素格式,以包含在一個位元組的八位元表示。位元組值作為索引中包含實際的紅-綠-藍 (RGB) 色彩定義在色彩表中。因為索引的大小的一個位元組色彩表是限制為 256 色。
GIF
圖形交換格式-由 CompuServe 建立以 streamable 影像檔案格式。
RGB
紅色、 綠色和藍色-每通常表示為一個位元組並導致色彩 3 位元組三分析元。

屬性

文章編號: 315780 - 上次校閱: 2007年2月12日 - 版次: 3.5
這篇文章中的資訊適用於:
  • Microsoft GDI+ 1.0
  • Microsoft Windows XP Professional
  • the operating system: Microsoft Windows XP 64-Bit Edition
關鍵字:?
kbmt kbdswgdi2003swept kbbitmap kbgdiplus kbhowto KB315780 KbMtzh
機器翻譯
重要:本文是以 Microsoft 機器翻譯軟體翻譯而成,而非使用人工翻譯而成。Microsoft 同時提供使用者人工翻譯及機器翻譯兩個版本的文章,讓使用者可以依其使用語言使用知識庫中的所有文章。但是,機器翻譯的文章可能不盡完美。這些文章中也可能出現拼字、語意或文法上的錯誤,就像外國人在使用本國語言時可能發生的錯誤。Microsoft 不為內容的翻譯錯誤或客戶對該內容的使用所產生的任何錯誤或損害負責。Microsoft也同時將不斷地就機器翻譯軟體進行更新。
按一下這裡查看此文章的英文版本: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