As explained in the comments:
- The image is composed of a white background and some texts / signatures / lines
- It is not harmful to use gray tones for the output image
So I created a function that transforms a color image (RGB) into 16 shades of gray, and then exports to a GIF with a palette composed of 16 linearly spaced gray tones:
//Cria uma paleta com os 16 tons de cinza desejados (distribuídos linearmente)
private static readonly System.Windows.Media.Imaging.BitmapPalette Gray4Palette = new System.Windows.Media.Imaging.BitmapPalette(new List<System.Windows.Media.Color>(new System.Windows.Media.Color[] {
System.Windows.Media.Color.FromArgb(0xFF, 0x00, 0x00, 0x00),
System.Windows.Media.Color.FromArgb(0xFF, 0x11, 0x11, 0x11),
System.Windows.Media.Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
System.Windows.Media.Color.FromArgb(0xFF, 0x33, 0x33, 0x33),
System.Windows.Media.Color.FromArgb(0xFF, 0x44, 0x44, 0x44),
System.Windows.Media.Color.FromArgb(0xFF, 0x55, 0x55, 0x55),
System.Windows.Media.Color.FromArgb(0xFF, 0x66, 0x66, 0x66),
System.Windows.Media.Color.FromArgb(0xFF, 0x77, 0x77, 0x77),
System.Windows.Media.Color.FromArgb(0xFF, 0x88, 0x88, 0x88),
System.Windows.Media.Color.FromArgb(0xFF, 0x99, 0x99, 0x99),
System.Windows.Media.Color.FromArgb(0xFF, 0xAA, 0xAA, 0xAA),
System.Windows.Media.Color.FromArgb(0xFF, 0xBB, 0xBB, 0xBB),
System.Windows.Media.Color.FromArgb(0xFF, 0xCC, 0xCC, 0xCC),
System.Windows.Media.Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD),
System.Windows.Media.Color.FromArgb(0xFF, 0xEE, 0xEE, 0xEE),
System.Windows.Media.Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF)
}));
private static void SaveGifGray4(string saveLocation, Image image)
{
//Obtém um array com os pixels originais da imagem, no formato 32bpp
System.Drawing.Imaging.BitmapData data = (image as Bitmap).LockBits(new Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
int stride = data.Stride;
//Formato das componentes em originalPixels:
//0 1 2 3, 4 5 6 7, ....
//r g b a, r g b a, ....
byte[] originalPixels = new byte[stride * image.Height];
System.Runtime.InteropServices.Marshal.Copy(data.Scan0, originalPixels, 0, originalPixels.Length);
(image as Bitmap).UnlockBits(data);
//Cria o array que irá armazenar os índices da paleta
byte[] pixels = new byte[image.Width * image.Height];
//Converte a imagem de 32bpp para 16 tons de cinza
int offsetOut = 0;
for (int y = 0; y < image.Height; y++)
{
int offsetIn = y * stride;
for (int x = 0; x < image.Width; x++, offsetIn += 4, offsetOut++)
{
byte r = originalPixels[offsetIn], g = originalPixels[offsetIn + 1], b = originalPixels[offsetIn + 2];
//Converte o RGB em um tom de cinza, conforme a intensidade luminosa relativa da cor
int grayI = (((int)((0.2126f * (float)r) + (0.7152f * (float)g) + (0.0722f * (float)b)) >> 4) & 15);
//Reduz para 16 possíveis valores
pixels[offsetOut] = (byte)((255 * grayI) / 15);
}
}
//Codifica a imagem para um arquivo GIF
System.Windows.Media.Imaging.GifBitmapEncoder encoder = new System.Windows.Media.Imaging.GifBitmapEncoder();
encoder.Palette = Gray4Palette;
//Adiciona o único frame do GIF, a partir dos pixels já processados
encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(System.Windows.Media.Imaging.BitmapSource.Create(image.Width, image.Height, 96, 96, System.Windows.Media.PixelFormats.Indexed8, Gray4Palette, pixels, image.Width)));
//Grava o arquivo através de um stream
System.IO.FileStream stream = new System.IO.FileStream(saveLocation, System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None, 1024);
encoder.Save(stream);
stream.Close();
stream.Dispose();
}
Because of the stretch involving GIF compression with more control and with more options:
System.Windows.Media.Imaging.GifBitmapEncoder encoder...
You need to add three references to the project, for three% of the .NET Framework%:
- WindowsBase
- PresentationCore
- System.Xaml