懐かしのGDIで8bit化。
残念画質になるので、質を求めるなら自力で減色ルーチン・最適化パレット作らないとダメですね。
グレースケールなら、それほど難しくはないのですが。
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public enum BitmapCompressionMode : uint
{
BI_RGB = 0,
BI_RLE8 = 1,
BI_RLE4 = 2,
BI_BITFIELDS = 3,
BI_JPEG = 4,
BI_PNG = 5
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFOHEADER
{
public uint biSize;
public int biWidth;
public int biHeight;
public ushort biPlanes;
public ushort biBitCount;
public BitmapCompressionMode biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
}
public enum DibColorsUsage : uint
{
RGB = 0,
PAL = 1
}
[StructLayout(LayoutKind.Sequential)]
public struct BITMAPINFO
{
public BITMAPINFOHEADER bmiHeader;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U4, SizeConst = 256)]
public uint[] bmiColors;
}
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hwnd);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hdc);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern int GetDIBits(
IntPtr hdc,
IntPtr hbm,
int start,
int cLines,
IntPtr lpvBits,
ref BITMAPINFO lpbmi,
DibColorsUsage usage
);
private void button1_Click(object sender, EventArgs e)
{
using (var bmp8_src = new Bitmap("c:/test/test.bmp"))
using (var canvas = new Bitmap(bmp8_src))
using (var graph = Graphics.FromImage(canvas))
using (var pen = new Pen(Color.Black, 1))
{
//位置(10, 20)に100x80の長方形を描く
graph.DrawRectangle(pen, 10, 20, 100, 80);
using (var bmp8_dst = ConvertBmp8(canvas))
{
bmp8_dst.Save("c:/test/test2.bmp");
}
}
}
private Bitmap ConvertBmp8(Bitmap bmp)
{
//出力用8bitビットマップ
var bmp8 = new Bitmap(bmp.Width, bmp.Height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
//GetDIBitsで8bitビットマップ取得
BITMAPINFO bi = new BITMAPINFO();
bi.bmiHeader.biSize = (uint)Marshal.SizeOf(bi.bmiHeader);
bi.bmiHeader.biWidth = bmp.Width;
bi.bmiHeader.biHeight = -bmp.Height; //トップダウンにするためマイナス(上下逆転する)
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 8;
bi.bmiHeader.biCompression = BitmapCompressionMode.BI_RGB;
var hdc = CreateCompatibleDC(IntPtr.Zero);
var hBmp = bmp.GetHbitmap();
var hBmpOld = SelectObject(hdc, hBmp);
var bmData = bmp8.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
GetDIBits(hdc, hBmp, 0, bmp.Height, bmData.Scan0, ref bi, DibColorsUsage.RGB);
bmp8.UnlockBits(bmData);
SelectObject(hdc, hBmpOld);
DeleteObject(hBmp);
DeleteDC(hdc);
//パレット転送
var palette = bmp8.Palette;
for (var i = 0; i < 256; i++)
{
palette.Entries[i] = Color.FromArgb((int)(bi.bmiColors[i] | 0xff000000));
}
bmp8.Palette = palette;
return bmp8;
}
}
}