Bitmap: Dで手軽にビットマップを扱うクラス
こちらは未完成です。
バグを見つけた方はご指摘下さると嬉しいです。
リソースやファイルからビットマップを読み込んで、
DIBSectionを作り出します。
デバイスコンテキストを持っているので、
GDIで描画できます。
ファイル書き出し機能もおまけで付けました。
module bitmap; //------------------------------------------------------------------------------------------// import win32.windows; import std.c.string; import std.stream; import std.utf; //------------------------------------------------------------------------------------------// pragma(lib, "gdi32.lib"); static if (WINVER < 0x0500) { const DWORD NOMIRRORBITMAP = 0x80000000, CAPTUREBLT = 0x40000000; } //------------------------------------------------------------------------------------------// const uint RASTER = SRCCOPY | CAPTUREBLT; //------------------------------------------------------------------------------------------// class BitmapException : Exception { this(string message) { super(message); } } //------------------------------------------------------------------------------------------// interface Image { // Methods public: void Create(int w, int h, ushort bitCount=32, uint clrUsed=0); void Create(BITMAPINFO* bi); void Create(Image image); void Load(ushort rsrcID); void Load(string filename); void Save(string filename); void Dispose(); void Draw(HDC hDC, int left=0, int top=0, uint dwRaster=RASTER); void Draw(HDC hDC, int left=0, int top=0, int width=0, int height=0, int sx=0, int sy=0, int sw=0, int sh=0, uint dwRaster=RASTER); // Properties public: int Width(); int Height(); ushort BitCount(); uint ColorUsed(); uint Size(); HDC hDC(); BITMAPINFO* BmpInfo(); HANDLE pBits(); } //------------------------------------------------------------------------------------------// class Bitmap : Image { // Attributes protected: int m_width = 0; int m_height = 0; ushort m_bitCount = 0; uint m_clrUsed = 0; int m_lineLength = 0; HDC m_hDC = null; HBITMAP m_hBitmap = null; HPALETTE m_hPalette = null; BITMAPINFO* m_lpBI = null; HANDLE m_pbits = null; // Methods public: // コンストラクタ this() { } this(int width, int height, ushort bitCount=32, uint clrUsed=0) { Create(width, height, bitCount, clrUsed); } this(BITMAPINFO* bi) { Create(bi); } this(Image image) { Create(image); } this(ushort rsrcID) { Load(rsrcID); } this(string filename) { Load(filename); } // デストラクタ ~this() { Dispose(); } // ビットマップを解放する void Dispose() { m_width = 0; m_height = 0; m_bitCount = 0; m_clrUsed = 0; m_lineLength = 0; DeleteDC(m_hDC); m_hDC = null; /// DCの解放がオブジェクトの解放より先らしい DeleteObject(m_hBitmap); m_hBitmap = null; /// m_pbitsはここで解放される DeleteObject(m_hPalette); m_hPalette = null; m_lpBI = null; m_pbits = null; } // ビットマップを描画する void Draw(HDC hDC, int left = 0, int top = 0, uint dwRaster = RASTER) { Draw(hDC, left, top, 0, 0, 0, 0, 0, 0, dwRaster); } // ビットマップを描画する void Draw(HDC hDC, int left = 0, int top = 0, int width = 0, int height = 0, int sx = 0, int sy = 0, int sw = 0, int sh = 0, uint dwRaster = RASTER) { // パレットの選択 HPALETTE hOldPalette = null; if (m_hPalette) { hOldPalette = SelectPalette(hDC, m_hPalette, false); if (hOldPalette is null) { throw new BitmapException(r"パレット選択エラー"); } RealizePalette(hDC); } if (width == 0 || height == 0) { // 描画 BitBlt(hDC, left, top, m_width, m_height, m_hDC, 0, 0, dwRaster); } else { // 描画元画像サイズのデフォルトを指定 if (sw == 0) sw = m_width; if (sh == 0) sh = m_height; // ビットマップ伸縮モードの設定 auto oldSthMode = SetStretchBltMode(hDC, COLORONCOLOR); /// 縮小・拡大ともこちらのモードが最適 if (oldSthMode == 0) { throw new BitmapException(r"ビットマップ伸縮モード設定エラー"); } // 描画 StretchDIBits(hDC, left, top, width, height, sx, sy, sw, sh, m_pbits, m_lpBI, DIB_RGB_COLORS, dwRaster); // ビットマップ伸縮モードの設定解除 SetStretchBltMode(hDC, oldSthMode); } // パレットの選択解除 if (hOldPalette) { SelectPalette(hDC, hOldPalette, false); } } // 空のビットマップを作成する void Create(int width=1, int height=1, ushort bitCount=32, uint clrUsed=0) { try { Dispose(); // データをメンバ変数に記憶 /// ここで m_width, m_height, m_bitCount, m_clrUsed, m_lineLength が初期化される StoreData(width, height, bitCount, clrUsed); // ヘッダ情報の作成 size_t bmpInfoSize = BITMAPINFOHEADER.sizeof + RGBQUAD.sizeof * m_clrUsed; m_lpBI = cast(BITMAPINFO*)new ubyte[bmpInfoSize]; m_lpBI.bmiHeader.biSize = BITMAPINFOHEADER.sizeof; m_lpBI.bmiHeader.biWidth = m_width; m_lpBI.bmiHeader.biHeight = m_height; m_lpBI.bmiHeader.biPlanes = 1; m_lpBI.bmiHeader.biBitCount = m_bitCount; m_lpBI.bmiHeader.biCompression = BI_RGB; m_lpBI.bmiHeader.biSizeImage = this.Size; m_lpBI.bmiHeader.biXPelsPerMeter = 0; m_lpBI.bmiHeader.biYPelsPerMeter = 0; m_lpBI.bmiHeader.biClrUsed = m_clrUsed; m_lpBI.bmiHeader.biClrImportant = 0; // 対応している形式かどうか Check(); // パレットデータの作成 CreatePalette(); // 互換DCの作成 CreateCompatibleDC(); // DIBSectionの生成 CreateDIBSection(); } catch (Object o) { Dispose(); throw o; } } // ヘッダ情報からビットマップを作成する void Create(BITMAPINFO* bmi) { try { // 空のビットマップを作成 /// ここで m_lpBI が初期化される Create(bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, bmi.bmiHeader.biBitCount, bmi.bmiHeader.biClrUsed); // ヘッダ情報をコピー m_lpBI.bmiHeader.biXPelsPerMeter = bmi.bmiHeader.biXPelsPerMeter; m_lpBI.bmiHeader.biYPelsPerMeter = bmi.bmiHeader.biYPelsPerMeter; m_lpBI.bmiHeader.biClrImportant = bmi.bmiHeader.biClrImportant; // パレットデータをコピー memcpy(m_lpBI + BITMAPINFOHEADER.sizeof, bmi + bmi.bmiHeader.biSize, RGBQUAD.sizeof * m_clrUsed); } catch (Object o) { Dispose(); throw o; } } // イメージオブジェクトからビットマップを作成する void Create(Image image) { try { Dispose(); // ヘッダ情報からビットマップを作成 Create(image.BmpInfo); // ピクセルデータのコピー image.Draw(this.hDC); } catch (Object o) { Dispose(); throw o; } } // リソースから読み込む void Load(ushort rsrcID) { try { Dispose(); // リソースの読み込み auto hRsrc = FindResource(GetModuleHandle(null), MAKEINTRESOURCE(rsrcID), RT_BITMAP); if(hRsrc is null) { throw new BitmapException(r"リソースが見つかりませんでした"); } auto hBmp = LoadResource(GetModuleHandle(null), hRsrc); if(hBmp is null) { throw new BitmapException(r"リソースの読み込みに失敗しました"); } // ヘッダ情報およびパレット情報の読み込み /// 仮にヘッダのバージョンがV4形式やV5形式でもこれで対応できます。 m_lpBI = cast(BITMAPINFO*)LockResource(hBmp); if(m_lpBI is null) { throw new BitmapException(r"ヘッダ情報の読み込みに失敗しました"); } // ヘッダ情報のうちよく使うデータをメンバ変数に記憶 StoreData(m_lpBI.bmiHeader.biWidth, m_lpBI.bmiHeader.biHeight, m_lpBI.bmiHeader.biBitCount, m_lpBI.bmiHeader.biClrUsed); // 対応している形式かどうか Check(); // パレットデータの作成 CreatePalette(); // 互換DCの作成 CreateCompatibleDC(); // DIBSectionの生成 CreateDIBSection(); // ピクセルデータの読み込み void* source = m_lpBI + m_lpBI.bmiHeader.biSize + RGBQUAD.sizeof * m_clrUsed; memcpy(m_pbits, source, this.Size); } catch (Object o) { Dispose(); throw o; } finally { /// リソースやリソースロックの解放はしなくてよいらしい /// http://www.microsoft.com/japan/msdn/library/ja/jpwinui/html/Toppage_Resource.asp } } // ファイルから読み込む void Load(string filename) { File file; try { Dispose(); file = new File(filename, FileMode.In); // ファイルヘッダの読み込み BITMAPFILEHEADER bmpfh; file.readExact(&bmpfh, bmpfh.sizeof); if (bmpfh.bfType != 0x4D42) { throw new BitmapException(r"ビットマップちゃうやん"); } // ヘッダ情報およびパレット情報の読み込み /// 仮にヘッダのバージョンがV4形式やV5形式でもこれで対応できます。 size_t size = bmpfh.bfOffBits - bmpfh.sizeof; m_lpBI = cast(BITMAPINFO*)new ubyte[size]; file.readExact(m_lpBI, size); if(m_lpBI is null) { throw new BitmapException(r"ヘッダ情報の読み込みに失敗しました"); } // ヘッダ情報のうちよく使うデータをメンバ変数に記憶 StoreData(m_lpBI.bmiHeader.biWidth, m_lpBI.bmiHeader.biHeight, m_lpBI.bmiHeader.biBitCount, m_lpBI.bmiHeader.biClrUsed); // 対応している形式かどうか Check(); // パレットデータの作成 CreatePalette(); // 互換DCの作成 CreateCompatibleDC(); // DIBSectionの生成 CreateDIBSection(); // ピクセルデータの読み込み file.readExact(m_pbits, this.Size); } catch (Object o) { Dispose(); throw o; } finally { if ( file ) file.close(); } } // ファイルに書き出す void Save(string filename) { File file; try { file = new std.stream.File(filename, FileMode.OutNew); // ファイルヘッダの書き出し BITMAPFILEHEADER bmpfh; bmpfh.bfType = 0x4d42; /// `BM` bmpfh.bfOffBits = bmpfh.sizeof + m_lpBI.bmiHeader.biSize + RGBQUAD.sizeof * m_clrUsed; bmpfh.bfReserved1 = 0; bmpfh.bfReserved2 = 0; bmpfh.bfSize = bmpfh.bfOffBits + m_lpBI.bmiHeader.biSizeImage; file.writeExact(&bmpfh, bmpfh.sizeof); // ヘッダ情報およびパレット情報の書き出し file.writeExact(m_lpBI, m_lpBI.bmiHeader.biSize + RGBQUAD.sizeof * m_clrUsed); // ピクセルデータの書き出し file.writeExact(m_pbits, this.Size); } catch (Object o) { throw o; } finally { if ( file ) file.close(); } } protected: // データをメンバ変数に記憶 void StoreData(int width, int height, ushort bitCount, uint clrUsed) { m_width = width; m_height = height; m_bitCount = bitCount; /// 1, 4, 8, (16,) 24, 32, ... m_clrUsed = (bitCount > 8) ? 0 : (clrUsed > 0) ? clrUsed : (1 << bitCount); m_lineLength = (((width * bitCount) + 31) & ~31) / 8; /// 4バイト境界に揃える } // 対応している形式かどうか調べる void Check() { if ( m_lpBI.bmiHeader.biSize != BITMAPINFOHEADER.sizeof && m_lpBI.bmiHeader.biSize != BITMAPV4HEADER.sizeof && m_lpBI.bmiHeader.biSize != BITMAPV5HEADER.sizeof ) { throw new BitmapException(r"対応していないヘッダ形式です"); } if ( m_lpBI.bmiHeader.biCompression == BI_RGB ) { switch ( m_bitCount ) { case 1: case 4: case 8: case 16: case 24: case 32: return; default: throw new BitmapException(r"対応していないビット深度です"); } } else if ( m_lpBI.bmiHeader.biCompression == BI_BITFIELDS ) { switch ( m_bitCount ) { case 16: case 32: return; default: throw new BitmapException(r"対応していないビット深度です"); } } else { throw new BitmapException(r"圧縮ビットマップには非対応です"); } } // パレットデータを作成する void CreatePalette() { if ( m_bitCount > 8 ) { return; /// フルカラーの場合は必要なし } try { // 色数に応じて必要なメモリを確保 auto lpLogPal = cast(LOGPALETTE*)new ubyte[LOGPALETTE.sizeof + PALETTEENTRY.sizeof * m_clrUsed]; lpLogPal.palVersion = 0x300; lpLogPal.palNumEntries = cast(ushort)m_clrUsed; for ( size_t i = 0; i < m_clrUsed; i++ ) { lpLogPal.palPalEntry[i].peRed = m_lpBI.bmiColors[i].rgbRed; lpLogPal.palPalEntry[i].peGreen = m_lpBI.bmiColors[i].rgbGreen; lpLogPal.palPalEntry[i].peBlue = m_lpBI.bmiColors[i].rgbBlue; } m_hPalette = .CreatePalette(lpLogPal); if (m_hPalette is null) { throw new BitmapException(r"パレットデータの作成に失敗しました"); } } catch (Object o) { DeleteObject(m_hPalette); m_hPalette = null; throw o; } } // デスクトップと互換性のあるデバイスコンテキストを作成する void CreateCompatibleDC() { HDC hDesktopDC; try { hDesktopDC = GetDC(null); m_hDC = .CreateCompatibleDC(hDesktopDC); if (m_hDC is null) { throw new BitmapException(r"デバイスコンテキストが作成できませんでした"); } } catch (Object o) { DeleteDC(m_hDC); m_hDC = null; throw o; } finally { ReleaseDC(null, hDesktopDC); } } // DIBSectionを生成する void CreateDIBSection() { try { // DIBSectionの作成 m_hBitmap = .CreateDIBSection(m_hDC, m_lpBI, DIB_RGB_COLORS, cast(void**)&m_pbits, null, 0); if (m_hBitmap is null) { throw new BitmapException(r"DIBSectionの生成に失敗しました"); } // ビットマップオブジェクトをデバイスコンテキストにセット HGDIOBJ hOldBmp = SelectObject(m_hDC, m_hBitmap); DeleteObject(hOldBmp); } catch (Object o) { DeleteObject(m_hBitmap); m_hBitmap = null; throw o; } } // Properties public: int Width() { return m_width; } int Height() { return m_height; } ushort BitCount() { return m_bitCount; } uint ColorUsed() { return m_clrUsed; } uint Size() { return m_lineLength * m_height; } HDC hDC() { return m_hDC; } HPALETTE hPalette() { return m_hPalette; } HBITMAP hBitmap() { return m_hBitmap; } BITMAPINFO* BmpInfo() { return m_lpBI; } HANDLE pBits() { return m_pbits; } }
- ライセンスは暫定的にNYSLで。
- ご質問・ご指摘等 歓迎します