ソース
OS/2形式、Windows形式の1, 4, 8, 24, 32bit BMPに対応するように作ってみました。
あまりお目にかかる機会のないであろう、ランレベル圧縮やビットフィールドについては対応せず。
大したことやってないですが、以下概要。
Bitmapファイルの各種情報を解析、保持するために、構造体を定義。
#define MAX_PALETTES 256 #pragma pack(1) typedef struct { CHAR8 Type[2]; UINT32 FileSize; UINT32 Reserved; UINT32 Offset; } BITMAP_FILE_HEADER; typedef struct { UINT32 HeaderSize; INT16 Width; INT16 Height; UINT16 Planes; UINT16 BitCount; } BITMAP_CORE_HEADER; typedef struct { UINT32 HeaderSize; INT32 Width; INT32 Height; UINT16 Planes; UINT16 BitCount; UINT32 Compression; UINT32 ImageSize; INT32 XPixPerMeter; INT32 YPixPerMeter; UINT32 ClrUsed; UINT32 ClrImportant; } BITMAP_INFO_HEADER; typedef struct { UINT8 Red; UINT8 Green; UINT8 Blue; } BITMAP_COLOR_PALETTE; typedef struct { VOID *FileData; VOID *InfoData; VOID *PaletteData; VOID *ImageData; UINTN FileSize; BOOLEAN IsOs2; UINTN Width; UINTN Height; BOOLEAN Invert; UINTN BitCount; UINTN Compression; UINTN UsedPalette; BITMAP_COLOR_PALETTE Palette[MAX_PALETTES]; } BITMAP_INFORMATION; #pragma pack()
Bitmapファイルのヘッダを読んで、BITMAP_INFORMATION構造体へ情報を入れておきます。
OS/2形式とWindows形式ではヘッダの大きさが異なるので、Information HeaderのSize情報を使用して場合分け。
STATIC EFI_STATUS ParseFileHeader ( IN OUT BITMAP_INFORMATION *BitmapInfo ) { BITMAP_FILE_HEADER *FileHeader; if (BitmapInfo->FileSize < sizeof (BITMAP_FILE_HEADER)) { return EFI_BAD_BUFFER_SIZE; } FileHeader = (BITMAP_FILE_HEADER*) BitmapInfo->FileData; if (!(FileHeader->Type[0] == 'B' && FileHeader->Type[1] == 'M')) { return EFI_UNSUPPORTED; } BitmapInfo->FileSize = MIN (BitmapInfo->FileSize, FileHeader->FileSize); BitmapInfo->InfoData = FileHeader + 1; BitmapInfo->ImageData = (UINT8*) BitmapInfo->FileData + FileHeader->Offset; return EFI_SUCCESS; } STATIC EFI_STATUS ParseInformationHeader ( IN OUT BITMAP_INFORMATION *BitmapInfo ) { BITMAP_CORE_HEADER *CoreHeader; BITMAP_INFO_HEADER *InfoHeader; CoreHeader = (BITMAP_CORE_HEADER*) BitmapInfo->InfoData; if (BitmapInfo->FileSize < ((UINTN) BitmapInfo->ImageData - (UINTN) BitmapInfo->FileData)) { return EFI_BAD_BUFFER_SIZE; } if (CoreHeader->HeaderSize == sizeof (BITMAP_CORE_HEADER)) { BitmapInfo->IsOs2 = TRUE; BitmapInfo->Width = CoreHeader->Width; if (CoreHeader->Height >= 0) { BitmapInfo->Height = CoreHeader->Height; BitmapInfo->Invert = FALSE; } else { BitmapInfo->Height = CoreHeader->Height * (-1); BitmapInfo->Invert = TRUE; } BitmapInfo->BitCount = CoreHeader->BitCount; BitmapInfo->Compression = 0; if (BitmapInfo->BitCount > 8) { BitmapInfo->UsedPalette = 0; } else { BitmapInfo->UsedPalette = 1 << BitmapInfo->BitCount; } BitmapInfo->PaletteData = CoreHeader + 1; } else if (CoreHeader->HeaderSize == sizeof (BITMAP_INFO_HEADER)) { BitmapInfo->IsOs2 = FALSE; InfoHeader = (BITMAP_INFO_HEADER*) BitmapInfo->InfoData; BitmapInfo->Width = InfoHeader->Width; if (InfoHeader->Height >= 0) { BitmapInfo->Height = InfoHeader->Height; BitmapInfo->Invert = FALSE; } else { BitmapInfo->Height = InfoHeader->Height * (-1); BitmapInfo->Invert = TRUE; } BitmapInfo->BitCount = InfoHeader->BitCount; BitmapInfo->Compression = InfoHeader->Compression; if (BitmapInfo->BitCount > 8) { BitmapInfo->UsedPalette = 0; } else { if (InfoHeader->ClrUsed == 0) { BitmapInfo->UsedPalette = 1 << BitmapInfo->BitCount; } else { BitmapInfo->UsedPalette = InfoHeader->ClrUsed; } } BitmapInfo->PaletteData = InfoHeader + 1; } else { return EFI_UNSUPPORTED; } return EFI_SUCCESS; }
1, 4, 8bitカラーBitmapの場合、カラーパレットを利用するので、パレット情報を保持。
ここでも、OS/2形式とWindows形式でパレットの使い方が違う(パレットサイズがOS/2は24bit、Windowsは32bit)ので場合分けを挟んでいます。
STATIC EFI_STATUS GetPalette ( IN OUT BITMAP_INFORMATION *BitmapInfo ) { UINTN Index; struct { UINT8 Blue; UINT8 Green; UINT8 Red; } *PaletteDataOs2 = BitmapInfo->PaletteData; struct { UINT8 Blue; UINT8 Green; UINT8 Red; UINT8 Reserved; } *PaletteDataWin = BitmapInfo->PaletteData; if (BitmapInfo->IsOs2) { for (Index = 0; Index < BitmapInfo->UsedPalette; Index++, PaletteDataOs2++) { BitmapInfo->Palette[Index].Red = PaletteDataOs2->Red; BitmapInfo->Palette[Index].Green = PaletteDataOs2->Green; BitmapInfo->Palette[Index].Blue = PaletteDataOs2->Blue; } } else { for (Index = 0; Index < BitmapInfo->UsedPalette; Index++, PaletteDataWin++) { BitmapInfo->Palette[Index].Red = PaletteDataWin->Red; BitmapInfo->Palette[Index].Green = PaletteDataWin->Green; BitmapInfo->Palette[Index].Blue = PaletteDataWin->Blue; } } return EFI_SUCCESS; }
最後に、ピクセルの情報をEFI_GRAPHICS_OUTPUT_BLT_PIXELに変換しました。
Bitmapファイルは通常、左下から右上に向かってデータが格納されているため、Y軸をデクリメントさせて処理しています。
また、各行のサイズはDWORDアラインされている必要が有るため、途中アライメント処理を挟みました。
STATIC EFI_STATUS FillBltBufferWithPalette ( IN BITMAP_INFORMATION *BitmapInfo, IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer ) { UINT8 *BitmapData; UINTN XIndex; UINTN YIndex; UINTN PixPerByte; UINTN Pos; UINTN BitPos; UINTN BltPos; UINT8 PaletteIndex; PixPerByte = 8 / BitmapInfo->BitCount; BitmapData = BitmapInfo->ImageData; for (YIndex = BitmapInfo->Height; YIndex > 0; YIndex--) { for (XIndex = 0; XIndex < BitmapInfo->Width;) { Pos = (YIndex - 1) * ALIGN_VALUE (BitmapInfo->Width / PixPerByte, 4) + (XIndex / PixPerByte); for (BitPos = 0; BitPos < 8; BitPos += BitmapInfo->BitCount, XIndex++) { if (BitmapInfo->Invert) { BltPos = (YIndex - 1) * BitmapInfo->Width + XIndex; } else { BltPos = (BitmapInfo->Height - YIndex) * BitmapInfo->Width + XIndex; } PaletteIndex = BitFieldRead8(BitmapData[Pos], (7 - BitPos) - (BitmapInfo->BitCount - 1), (7 - BitPos)); BltBuffer[BltPos].Red = BitmapInfo->Palette[PaletteIndex].Red; BltBuffer[BltPos].Green = BitmapInfo->Palette[PaletteIndex].Green; BltBuffer[BltPos].Blue = BitmapInfo->Palette[PaletteIndex].Blue; BltBuffer[BltPos].Reserved = 0; } } } return EFI_SUCCESS; } STATIC EFI_STATUS FillBltBufferWithoutPalette ( IN BITMAP_INFORMATION *BitmapInfo, IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer ) { UINT8 *BitmapData; UINTN XIndex; UINTN YIndex; UINTN BytePerPix; UINTN Pos; UINTN BltPos; BitmapData = BitmapInfo->ImageData; BytePerPix = BitmapInfo->BitCount / 8; for (YIndex = BitmapInfo->Height; YIndex > 0; YIndex--) { for (XIndex = 0; XIndex < BitmapInfo->Width; XIndex++) { Pos = (YIndex - 1) * ALIGN_VALUE (BitmapInfo->Width * BytePerPix, 4) + XIndex * BytePerPix; if (BitmapInfo->Invert) { BltPos = (YIndex - 1) * BitmapInfo->Width + XIndex; } else { BltPos = (BitmapInfo->Height - YIndex) * BitmapInfo->Width + XIndex; } BltBuffer[BltPos].Red = BitmapData[Pos + 2]; BltBuffer[BltPos].Green = BitmapData[Pos + 1]; BltBuffer[BltPos].Blue = BitmapData[Pos + 0]; BltBuffer[BltPos].Reserved = 0; } } return EFI_SUCCESS; }
試しにNt32Pkg上でEclipseのキャプチャ画像を表示させてみるとこんな感じ。
以上、かなりざっくりと作ったので読みにくい箇所はあると思います。
ざっくり作っても動くものが作れる程度に単純な仕様で素晴らしい、と捉えるべきでしょうか。