初投稿させていただきます!
2015年新卒入社のyoshi.kと申します。
普段の業務では、おもに自社製品である超画像(iOS版画像ビューア)の改修などを行っていますが、画像の描画は基本的にUIKitのUIImageに任せっきりです。
そこで今回、勉強も兼ねてBMPフォーマットの画像を自力で描画してみました。
BMPファイルとは
正式名称はMicrosoft Windows Bitmap Imageと呼ばれます
基本的には無圧縮のため、他の画像フォーマットよりも容量が大きくなりがちです。
BMPは大きく分けて次のような構造になっています。
- ヘッダ: どんなファイルであるかという情報が含まれる
- 画像データ: 1つのピクセルごとにRGB情報が割り当てられる
もう少し詳しくは次のようになっています。
名前 | サイズ | 説明 |
---|---|---|
ヘッダファイル | 14 byte | ファイルの一般的な情報 |
情報ヘッダ | 40 byte or 12 byte | ファイルの詳細な情報 |
カラーパレット | 可変(色数 × 3 byte or 4 byte) | 色の定義 ない場合もある |
画像データ | 可変 | 1ピクセルごとのデータ |
詳細については、「BMP ファイルフォーマット」などでググってください。
今回のBMPフォーマット画像描画は以下を前提としました。制約多いですね; ;
- 無圧縮
- カラーパレットなし
- Windows Bitmap形式
- 24-bit color
プログラム
プログラムが汚くてごめんなさい。
今回画像の描画に必要な情報は、幅、高さ、各ピクセル毎のRGBです。
まず、幅については情報ヘッダの 5 〜 8 byte、高さは 9 〜 12 byteを読み取れば取得できます。
typedef struct {
unsigned int width;
unsigned int height;
} ImageSize;
ImageSize read_bitmap_size(FILE *fp) {
unsigned int size;
ImageSize imgSize;
fseek(fp, sizeof(char) * 14, SEEK_SET);
fread(&size, sizeof(int), 1, fp);
fread(&imgSize, sizeof(int), 2, fp);
fseek(fp, sizeof(char) * size + 14, SEEK_SET);
return imgSize;
}
次にRGBです。
1ピクセルごとに、青、緑、赤の順で色データが並びます。赤、緑、青じゃないんですね。
24-bit colorの場合、色ごとに1 byteずつ割り当てられるので、1ピクセルで計3 byte使います。
typedef struct {
unsigned char b;
unsigned char g;
unsigned char r;
} Color;
Color read_rgb(FILE *fp) {
Color biColor;
fread(&biColor, sizeof(char), 3, fp);
return biColor;
}
画像データは、必ず1行あたり4 byteで単位で記録されます。
画像データが4 byteで割り切れない場合には、余った部分に意味のないデータ(パディング)が置かれるので、パディングをスキップする処理が必要です。
void skip_padding(int padding, FILE *fp) {
if (padding == 4) {
return;
}
fseek(fp, sizeof(char) * padding * 3, SEEK_CUR);
}
最後に、取得した情報を元に1ピクセルごとに点を描画すれば終わりです。
今回はCocoa Applicationとして作成しましたが、何気にCocoa Applicationを作るのは初めてです。
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
FILE *fp;
open_bitmap((char *)[kFilePath UTF8String], &fp);
ImageSize imageSize = read_bitmap_size(fp);
int padding = 4 - imageSize.width % 4;
for(NSInteger y = 0; y < imageSize.height; y++) {
for(NSInteger x = 0; x < imageSize.width; x++) {
Color rgb = read_rgb(fp);
NSColor *color = [NSColor colorWithDeviceRed:(CGFloat)rgb.r / 255
green:(CGFloat)rgb.g / 255
blue:(CGFloat)rgb.b / 255
alpha:1.0];
[color set];
NSRect rect = NSMakeRect(x, y, x, y);
NSFrameRect(rect);
}
skip_padding(padding, fp);
}
close_bitmap(&fp);
}
結果
プレビューアプリ | 作成物 |
---|---|
ちゃんと描画されました!
やたー。
まとめ
今回は、決まった欲しい情報だけを抜き出して描画するプログラムになってしまいました。
次回以降、もう少しデータの内容について深掘りしていければと思います。
また、別のフォーマットについてもチャレンジしていきたいです。