文章編號: 319061 - 上次校閱: 2006年12月11日 - 版次: 4.7

如何以新的色彩表來使用 Visual C# 儲存.gif 檔案

系統提示本文適用於您使用的作業系統之外的作業系統。與您不相關的文章內容已停用。

在此頁中

全部展開 | 全部摺疊

結論

CompuServe 圖形交換格式 (GIF) 被設計的排列在色彩表中的 256 色,最多。若要以便.gif 影像檔的常見修改您必須變更自訂色彩表。不過,System.Drawing 編輯 Image 物件,並再要求您將圖像儲存與 GIF 編碼器時, 產生.gif 檔案中包含半色調色彩表。

如果要將 影像 儲存自訂色彩資料表藉由使用 GIF 編碼器,您必須使用 System.Drawing 未修改 影像 的 256 色的複本。

了解由 System.DrawingGDI + 所撰寫的.gif 檔案

.gif 影像檔可以表達上限為 256 色。因為色彩.gif 檔案中的珍貴資源最佳化這些色彩是常要求的工作。若要影響最佳化的色表,您必須能夠在.gif 檔中設定任何任意的自訂色彩表。

System.Drawing 命名空間是主要周圍的包裝函式 GDI +,因此本文參考至命名空間做為 GDI + 除非 System.Drawing 命名空間的特定的行為會討論,在這種情況下 System.Drawing 用一詞。

GDI + 會修改 影像,並再將影像寫入檔案使用 GIF 編碼器之後,GDI + 寫入該檔案使用 Image 物件位元已被色彩減少的半色調調色盤。GDI + 不會從每像素 (32 BPP) 32 位元的色彩轉換時它將影像寫入檔案因為使用 GDI + 32 BPP 圖形引擎進行至影像的所有修改。

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

撰寫自訂的色彩表的.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 + 影像 物件。
  • 目標檔案的檔案名稱。
  • .gif 檔案的色彩數。
  • 旗標,指出是否需要透明的色彩。
函式首先會建立 點陣圖 物件具有 PixelFormat.Format8BPPIndexed 的像素格式,因為該物件,以建立 nColors.gif 檔案儲存。接下來,會建立使用自訂的色彩調色盤。.gif 檔案會從 點陣圖 物件 ColorPalette 取得大小和其色彩表的特定項目。範例程式碼會建立灰階供示範之用,因為該演算法易於擴充透過各種色彩資料表大小。

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

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

您可以使用其他技術來存取像素例如直接存取與 Unmanaged 機器碼使用 LockBits() 方法或 Interop,藉由使用 Windows GDI DIB 區段 像素。當您使用 BitBlt 函數來將點陣圖 Gdi + HDC 從複製到 GDI DIB 區段記憶體網域控制站 GBitBlt 函數使用色彩調配的 GDI 的能力。

建立 點陣圖 複本之後,使用與 ImageFormat.Gif 物件中的 [Save 方法寫入目標檔案中的點陣圖。



具有少於 256 色的 GIF 檔案

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

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

不幸的是,[ColorPaletteSystem.Drawing 命名空間,在.NET Framework 的無法起始化類別與無關的 點陣圖 物件。這是只有 System.Drawing.Bitmap 類別對於加在.NET Framework 的限制 ; 然而,本文中採用的使用方法,點陣圖 物件必須有新的 ColorPalette 物件,其中包含較少的色彩,比預設的 256 ColorPalette

若要達到此目的程式碼範例會定義名為 GetColorPalette 函式。這個函式會建立暫時的 點陣圖 物件具有色彩深度靠近所要求的色彩數目。函式再參考 [調色盤] 屬性,然後將它傳回給呼叫者。這會使用其中幾個可能的色彩數建立新 ColorPalette: 256 色、 16 色或兩種色彩 (單色)。雖然您可以在小於 256 色.gif 檔中建立色彩表,色彩表受限於是二的次方的大小。

當您限制色彩資料表大小,以二的次方時,您最小化浪費的空間。結果的色彩資料表,在這個範例中是 8 色 (2 x 2 x 2)。與範例] 碼.gif 檔案會用建立 16 色的色彩表因為,是一個 點陣圖 該 accomodates 六個最小 PixelFormat 色彩。

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

GIF 透明度

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

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

GIF/LZW 授權問題

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

如需有關 LZW 使用權,且 GIF 的額外資訊,按一下下面的文件編號,檢視 「 Microsoft 知識庫 」 中的發行項:
193543? (http://support.microsoft.com/kb/193543/EN-US/ ) 資訊: Unisys GIF 及 LZW 技術授權資訊

範例程式碼

protected ColorPalette GetColorPalette( uint nColors )
{
    // Assume monochrome image.
    PixelFormat     bitscolordepth = PixelFormat.Format1bppIndexed;
    ColorPalette    palette;    // The Palette we are stealing
    Bitmap          bitmap;     // The source of the stolen palette

    // Determine number of colors.
    if (nColors > 2)
        bitscolordepth = PixelFormat.Format4bppIndexed;
    if (nColors > 16)
        bitscolordepth = PixelFormat.Format8bppIndexed;

    // Make a new Bitmap object to get its Palette.
    bitmap = new Bitmap( 1, 1, bitscolordepth );

    palette = bitmap.Palette;   // Grab the palette
    
    bitmap.Dispose();           // cleanup the source Bitmap

    return palette;             // Send the palette back
}

				
protected void SaveGIFWithNewColorTable(
    Image       image,
    string      filename,
    uint        nColors,
    bool        fTransparent
    )
{

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

    // Make a new 8-BPP indexed bitmap that is the same size as the source image.
    int   Width = image.Width;
    int   Height = image.Height;

    // Always use PixelFormat8bppIndexed because that is the color
    // table-based interface to the GIF codec.
    Bitmap  bitmap = new Bitmap(Width, 
                            Height, 
                            PixelFormat.Format8bppIndexed); 

    // Create a color palette big enough to hold the colors you want.
    ColorPalette pal = GetColorPalette(nColors);

    // Initialize a new color table with entries that are determined
    // by some optimal palette-finding algorithm; for demonstration 
    // purposes, use a grayscale.
    for (uint i = 0; i < nColors; i++)
    {
        uint Alpha = 0xFF;                      // Colors are opaque.
        uint Intensity = i*0xFF/(nColors-1);    // Even distribution. 

        // The GIF encoder makes the first entry in the palette
        // that has a ZERO alpha the transparent color in the GIF.
        // Pick the first one arbitrarily, for demonstration purposes.

        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.
        pal.Entries[i] = Color.FromArgb( (int)Alpha, 
                                        (int)Intensity, 
                                        (int)Intensity, 
                                        (int)Intensity );
    }

    // Set the palette into the new Bitmap object.
    bitmap.Palette = pal;


    // 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 as the
    // image that you want to export. Or, try to
    // interpret the native pixel format of the image by using a LockBits
    // call. Use PixelFormat32BppARGB so you can wrap a Graphics  
    // around it.
    Bitmap BmpCopy = new Bitmap(Width, 
                            Height, 
                            PixelFormat.Format32bppArgb); 
    {
        Graphics g = Graphics.FromImage(BmpCopy);

        g.PageUnit = GraphicsUnit.Pixel;

        // Transfer the Image to the Bitmap
        g.DrawImage(image, 0, 0, Width, Height);

        // g goes out of scope and is marked for garbage collection.
        // Force it, just to keep things clean.
        g.Dispose();
    }

    // Lock a rectangular portion of the bitmap for writing.
    BitmapData  bitmapData;
    Rectangle   rect = new Rectangle(0, 0, Width, Height);

    bitmapData = bitmap.LockBits(
        rect,
        ImageLockMode.WriteOnly,
        PixelFormat.Format8bppIndexed);

    // Write to the temporary buffer that is 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.
    IntPtr pixels = bitmapData.Scan0;

    unsafe 
    { 
        // Get the pointer to the image bits.
        // This is the unsafe operation.
        byte *  pBits;
        if (bitmapData.Stride > 0)
            pBits = (byte *)pixels.ToPointer();
        else
            // If the Stide is negative, Scan0 points to the last 
            // scanline in the buffer. To normalize the loop, obtain
            // a pointer to the front of the buffer that is located 
            // (Height-1) scanlines previous.
            pBits = (byte *)pixels.ToPointer() + bitmapData.Stride*(Height-1);
        uint stride = (uint)Math.Abs(bitmapData.Stride);

        for ( uint row = 0; row < Height; ++row )
        {
            for ( uint col = 0; col < Width; ++col )
            {
                // Map palette indexes for a gray scale.
                // If you use some other technique to color convert,
                // put your favorite color reduction algorithm here.
                Color     pixel;    // The source pixel.

                // The destination pixel.
                // The pointer to the color index byte of the
                // destination; this real pointer causes this
                // code to be considered unsafe.
                byte *    p8bppPixel = pBits + row*stride + col;

                pixel = BmpCopy.GetPixel((int)col, (int)row);

                // Use luminance/chrominance conversion to get grayscale.
                // Basically, turn the image into black and white TV.
                // Do not calculate Cr or Cb because you 
                // discard 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.R *0.299) +
                    (pixel.G *0.587) +
                    (pixel.B *0.114);

                // Gray scale is an intensity map from black to white.
                // Compute the index to the grayscale entry that
                // approximates the luminance, and then round the index.
                // Also, constrain the index choices by the number of
                // colors to do, and then set that pixel's index to the 
                // byte value.
                *p8bppPixel = (byte)(luminance * (nColors-1)/255 +0.5);

            } /* end loop for col */ 
        } /* end loop for row */ 
    } /* end unsafe */ 

    // To commit the changes, unlock the portion of the bitmap.  
    bitmap.UnlockBits(bitmapData);

    bitmap.Save(filename, ImageFormat.Gif);

    // Bitmap goes out of scope here and is also marked for
    // garbage collection.
    // Pal is referenced by bitmap and goes away.
    // BmpCopy goes out of scope here and is marked for garbage
    // collection. Force it, because it is probably quite large.
    // The same applies to bitmap.
    BmpCopy.Dispose();
    bitmap.Dispose();

}

				

關於範例程式碼

Microsoft 僅,為了說明提供程式設計範例,不提供任何明示或默示的保證,包括但不是限於適售性以及適合某特定用途之默示擔保責任。本文假設您已熟悉我們所示範的程式設計語言,以及用來建立和偵錯程序的工具。Microsoft 技術支援工程師可以協助解釋特定程序的功能,但它們不會修改這些範例以提供附加功能或建構程序,以符合您的特定需求。
如果您有限制程式設計經驗,您可以連絡 Microsoft 認證合作夥伴或 Microsoft 諮詢服務。如需詳細資訊請造訪下列 Microsoft] 網站:

Microsoft 認證合作夥伴-https://partner.microsoft.com/global/30000104 (https://partner.microsoft.com/global/30000104)

Microsoft 摘要報告服務-http://support.microsoft.com/gp/advisoryservice (http://support.microsoft.com/gp/advisoryservice)

如需有關可用的支援選項,以及有關如何連絡 Microsoft 的詳細資訊,請造訪下列 Microsoft 網站: http://support.microsoft.com/default.aspx?scid=fh;EN-US;CNTACTMS (http://support.microsoft.com/default.aspx?scid=fh;en-us;cntactms)

疑難排解

使用此程式碼覆寫現有檔案時,您可能會看到什麼似乎有問題的產生的檔案大小。這是由於 GDIPlus 1.0 版,並不會截斷檔案中有錯誤。如需有關圖像檔大小的詳細資訊,請參閱 "References" 一節。

範例程式碼也使用該關鍵字 不安全。它會因為程式碼會將像素定義從來源點陣圖移至目的點陣圖使用 位元組 值的指標。真實的指標只可用於所標示不安全,而且,以 / unsafe 編譯器選項編譯的程式碼。這種方式所撰寫的任何程式碼具有安全性 impliciations。在特別這類的 Managed 程式碼可能必須要求的使用權限,而且可能會失敗如果不受信任執行。

?考

如需關於 GIF 轉碼器的像素格式的詳細資訊,按一下下面的文件編號,檢視 「 Microsoft 知識庫 」 中的發行項:
318343? (http://support.microsoft.com/kb/318343/EN-US/ ) 資訊: GDI + GIF 檔案會儲存使用 8 BPP 格式
取得更多資訊有關影像檔案大小按一下面的文件編號,檢視 「 Microsoft 知識庫 」 中的發行項:
312119? (http://support.microsoft.com/kb/312119/EN-US/ ) PRB: 點陣圖類別的 Save 方法並不截斷檔案大小

如需有關使用 Microsoft Visual C++ 這項技術的詳細資訊,按一下下面的文件編號,檢視 「 Microsoft 知識庫 」 中的發行項:
315780? (http://support.microsoft.com/kb/315780/EN-US/ ) HOWTO: 使用資料表建立新的色彩儲存為 GIF,藉由使用 GDI +

名詞解釋

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


這篇文章中的資訊適用於:
  • Microsoft .NET Framework Software Development Kit 1.0
  • Microsoft Visual C# .NET 2002 Standard Edition
  • Microsoft Visual C# 2005 Express Edition
  • Microsoft Windows XP Professional
  • the operating system: Microsoft Windows XP 64-Bit Edition
關鍵字:?
kbmt kbdswgdi2003swept kbhowtomaster KB319061 KbMtzh
機器翻譯機器翻譯
重要:本文是以 Microsoft 機器翻譯軟體翻譯而成,而非使用人工翻譯而成。Microsoft 同時提供使用者人工翻譯及機器翻譯兩個版本的文章,讓使用者可以依其使用語言使用知識庫中的所有文章。但是,機器翻譯的文章可能不盡完美。這些文章中也可能出現拼字、語意或文法上的錯誤,就像外國人在使用本國語言時可能發生的錯誤。Microsoft 不為內容的翻譯錯誤或客戶對該內容的使用所產生的任何錯誤或損害負責。Microsoft也同時將不斷地就機器翻譯軟體進行更新。
按一下這裡查看此文章的英文版本:319061? (http://support.microsoft.com/kb/319061/en-us/ )
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。