スポンサーサイト
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

page top


DIB:アルファチャネル

BITMAPINFOHEADERの代わりにBITMAPV4HEADERBITMAPV5HEADERを使ったビットマップファイル(32ビット形式でアルファチャネルを持ったような)は、LoadImageでDIBを読込もうとしても上手くいかないみたい。

LoadImageでDIBを読込んだ場合、GetObjectDIBSECTIONを取得することになるけれど、この構造体がBITMAPINFOHEADERを前提に作られているので仕方がないのかな。

そんなわけで、そんなファイルでもDIBを取得したい場合、LoadImageは使えない。

ファイルを開いて、ヘッダー部分を読込む。biBitCount0の場合、画像データはJPEGかPNGで圧縮されているので今回は扱わない。

HANDLE hf = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0,
	OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hf == INVALID_HANDLE_VALUE) throw;

DWORD read;

BITMAPFILEHEADER bfh;
ReadFile(hf, &bfh, sizeof(bfh), &read, NULL); if (read != sizeof(bfh)) throw;

UINT hdrSize = bfh.bfOffBits - sizeof(BITMAPFILEHEADER);

BYTE* phdr = new BYTE[hdrSize];
ReadFile(hf, phdr, hdrSize, &read, NULL); if (read != hdrSize) throw;

BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)phdr;

if (pbmih->biBitCount == 0) throw; // jpeg or png

256色以下の場合はパレットを。

HPALETTE hPal = NULL, hOldPal;
UINT colorUse;
if (pbmih->biPlanes * pbmih->biBitCount <= 8)
{
	UINT nColors = 1 << pbmih->biBitCount;
	RGBQUAD* pColors = (RGBQUAD*)(phdr + pbmih->biSize);
	
	LOGPALETTE* pLogPal = (LOGPALETTE*)new BYTE[sizeof(LOGPALETTE) + nColors * sizeof(PALETTEENTRY)];
	pLogPal->palVersion = 0x300;
	pLogPal->palNumEntries = nColors;
	
	UINT i = 0;
	for (; (i < nColors) && (pColors + 1 <= phdr + hdrSize) ; ++i, ++pColors)
	{
		pLogPal->palPalEntry[i].peRed = pColors->rgbRed;
		pLogPal->palPalEntry[i].peGreen = pColors->rgbGreen;
		pLogPal->palPalEntry[i].peBlue = pColors->rgbBlue;
		pLogPal->palPalEntry[i].peFlags = 0;
	}
	
	hPal = CreatePalette(pLogPal);
	hOldPal = SelectPalette(hdc, hPal, false);
	RealizePalette(hdc);
	colorUse = DIB_PAL_COLORS;
	
	delete[] pLogPal;
	
	WORD* PalIndexes = (WORD*)(phdr + pbmih->biSize);
	for (UINT index = 0; index < i; ++index) PalIndexes[index] = index;
}
else colorUse = DIB_RGB_COLORS;

画像データを読込む。

UINT imgSize = bfh.bfSize - bfh.bfOffBits;
BYTE* pImg = new BYTE[imgSize];
ReadFile(hf, pImg, imgSize, &read, NULL); if (read != imgSize) throw;
CloseHandle(hf);

アルファチャネルがある場合は背景と合成。ここでは真っ白な背景と合成する。変則的なビットフィールドが設定されている場合は、処理が面倒なので扱わない。

if (pbmih->biBitCount == 32 && pbmih->biCompression == BI_BITFIELDS
	&& ((BITMAPV4HEADER*)pbmih)->bV4AlphaMask)
{
	BITMAPV4HEADER* pb4h = (BITMAPV4HEADER*)pbmih;
	if (pb4h->bV4RedMask   != 0x000000FF
		|| pb4h->bV4GreenMask != 0x0000FF00
		|| pb4h->bV4BlueMask  != 0x00FF0000
		|| pb4h->bV4AlphaMask != 0xFF000000) throw;
	
	UINT nLines = abs(pbmih->biHeight);
	
	for (UINT y = 0; y < nLines; ++y)
	{
		RGBQUAD* pPix = (RGBQUAD*)pImg + pbmih->biWidth * y;
		for (UINT x = 0; x < pbmih->biWidth; ++x, ++pPix)
		{
			double a = pPix->rgbReserved / 255.0;
			double c = 1 - a;

			// この例では元の画像を書き換えてしまう。
			// 用途によっては別のDIBに書き込む。
			pPix->rgbRed = pPix->rgbRed * a + 0xFF * c;
			pPix->rgbGreen = pPix->rgbGreen * a + 0xFF * c;
			pPix->rgbBlue = pPix->rgbBlue * a + 0xFF * c;
		}
	}
}

実際には、テーブルを作っておくなどして合成を効率化するとよい。アルファチャネルの値はRGBQUADrgbReservedにあるはずだが、reservedとなっているのを使うのが気になる場合はDWORDで走査してマスクを使って各チャネルの値を取り出せばよい。

描画。変則的なビットフィールドが設定されたファイルは正しく描画できないみたい。

SetDIBitsToDevice(hdc, 0, 0, pbmih->biWidth, pbmih->biHeight,
	0, 0, 0, pbmih->biHeight,
	pImg, (BITMAPINFO*)pbmih, colorUse);

後始末。

if (hPal)
{
	SelectPalette(hdc, hOldPal, false);
	RealizePalette(hdc);
	DeleteObject(hPal);
}

delete[] pImg;
delete[] phdr;
スポンサーサイト

TrackBack0 Comment3 page top


DIBとBI_BITFIELDS

16ビットや32ビットのビットマップでBITMAPINFOHEADERbiCompressionBI_BITFIELDSになっている場合、SetDIBitsToDeviceに渡すBITMAPINFOは、後ろにDIBSECTIONdsBitfieldsと同じ配列を持っておく。まあ、DIBSECTIONdsBmihのアドレスを渡せば連続してdsBitfieldsがあるから問題ないけれど、dsBmihをコピーして使うような場合は、dsBitfieldsも忘れないように。

DIBSECTION ds;
GetObject(hDIB, sizeof(ds), &ds);

UINT size = sizeof(BITMAPINFOHEADER);
if (ds.dsBmih.biCompression == BI_BITFIELDS) size += sizeof(DWORD) * 3;

BYTE* pbmih = new BYTE[size];
CopyMemory(pbmih, &ds.dsBmih, size);

TrackBack0 Comment0 page top




Copyright © 手づくりアプリの裏側. all rights reserved.

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。