这是因为内置的GIF编码器无法很好地处理源文件,除非它已经是一个8 bpp图像。您必须先将PNG图像转换为256色图像,然后才能使用GIF编码器正确保存它。
public static void SaveGif(string fileName, Image image)
if (bpp < 8)
}
Color[] palette = GetColors((Bitmap)image, 256);
using (Image imageIndexed = ConvertPixelFormat(image, PixelFormat.Format8bppIndexed, palette))
}
private static Color[] GetColors(Bitmap bitmap, int maxColors)
line += data.Stride;
}
}
}
finally
}
else
}
}
return colors.Select(Color.FromArgb).ToArray();
}
private static Image ConvertPixelFormat(Image image, PixelFormat newPixelFormat, Color[] palette)
return result;
}
int transparentIndex;
Bitmap bmp;
RGBQUAD[] targetPalette = new RGBQUAD[256];
int colorCount = InitPalette(targetPalette, bpp, (image is Bitmap) ? image.Palette : null, palette, out transparentIndex);
BITMAPINFO bmi = new BITMAPINFO();
bmi.icHeader.biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER));
bmi.icHeader.biWidth = image.Width;
bmi.icHeader.biHeight = image.Height;
bmi.icHeader.biPlanes = 1;
bmi.icHeader.biBitCount = (ushort)bpp;
bmi.icHeader.biCompression = BI_RGB;
bmi.icHeader.biSizeImage = (uint)(((image.Width + 7) & 0xFFFFFFF8) * image.Height / (8 / bpp));
bmi.icHeader.biXPelsPerMeter = 0;
bmi.icHeader.biYPelsPerMeter = 0;
bmi.icHeader.biClrUsed = (uint)colorCount;
bmi.icHeader.biClrImportant = (uint)colorCount;
bmi.icColors = targetPalette;
bmp = (image as Bitmap) ?? new Bitmap(image);
IntPtr bits;
IntPtr hbmResult = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
IntPtr dcScreen = GetDC(IntPtr.Zero);
IntPtr hbmSource = bmp.GetHbitmap();
IntPtr dcSource = CreateCompatibleDC(dcScreen);
SelectObject(dcSource, hbmSource);
IntPtr dcTarget = CreateCompatibleDC(dcScreen);
SelectObject(dcTarget, hbmResult);
BitBlt(dcTarget, 0, 0, image.Width, image.Height, dcSource, 0, 0, 0x00CC0020 /*TernaryRasterOperations.SRCCOPY*/);
result = Image.FromHbitmap(hbmResult);
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
DeleteDC(dcSource);
DeleteDC(dcTarget);
ReleaseDC(IntPtr.Zero, dcScreen);
DeleteObject(hbmSource);
DeleteObject(hbmResult);
ColorPalette resultPalette = result.Palette;
bool resetPalette = false;
if (transparentIndex >= 0)
ToIndexedTransparentByArgb(result, bmp, transparentIndex);
}
if (resetPalette)
result.Palette = resultPalette;
if (!ReferenceEquals(bmp, image))
bmp.Dispose();
return result;
}
private static int InitPalette(RGBQUAD[] targetPalette, int bpp, ColorPalette originalPalette, Color[] desiredPalette, out int transparentIndex)
}
transparentIndex = -1;
bool hasBlack = false;
int colorCount = Math.Min(maxColors, sourcePalette.Length);
for (int i = 0; i < colorCount; i++)
if (transparentIndex == 0)
else if (transparentIndex != -1)
if (colorCount < maxColors && !hasBlack)
colorCount++;
return colorCount;
}
private unsafe static void ToIndexedTransparentByArgb(Bitmap target, Bitmap source, int transparentIndex)
else
lineTarget[pos] = nibbles;
break;
case 1:
pos = x >> 3;
byte mask = (byte)(128 >> (x & 7));
if (transparentIndex == 0)
lineTarget[pos] &= (byte)~mask;
else
lineTarget[pos] |= mask;
break;
}
}
}
lineSource += dataSource.Stride;
lineTarget += dataTarget.Stride;
}
}
finally
}
private static PixelFormat GetPixelFormat(int bpp)
}
以及原生类型和方法:
private const int BI_RGB = 0;
private const int DIB_RGB_COLORS = 0;
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, int iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("gdi32.dll")]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("user32.dll")]
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[StructLayout(LayoutKind.Sequential)]
private struct RGBQUAD
{
internal byte rgbBlue;
internal byte rgbGreen;
internal byte rgbRed;
internal byte rgbReserved;
internal RGBQUAD(Color color)
{
rgbRed = color.R;
rgbGreen = color.G;
rgbBlue = color.B;
rgbReserved = 0;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFO
{
public BITMAPINFOHEADER icHeader;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public RGBQUAD[] icColors;
}
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFOHEADER
{
internal uint biSize;
internal int biWidth;
internal int biHeight;
internal ushort biPlanes;
internal ushort biBitCount;
internal uint biCompression;
internal uint biSizeImage;
internal int biXPelsPerMeter;
internal int biYPelsPerMeter;
internal uint biClrUsed;
internal uint biClrImportant;
}
更新:
我的绘图库现在可以免费下载。它提供了一个SaveAsGif
扩展方法,可用于Image
类型:
using KGySoft.Drawing;
/// ...
using (var stream = new FileStream(targetPngPath, FileMode.Create))
{
// You can either use an arbitrary palette,
myPngBitmap.SaveAsGif(stream, myPngBitmap.GetColors(256));
// or, you can let the built-in encoder use dithering with a fixed palette.
// Pixel format is adjusted so transparency will be preserved.
myPngBitmap.SaveAsGif(stream, allowDithering: true);
}