読者です 読者をやめる 読者になる 読者になる

Bitmap読み書き

下記記事にて、Windows標準ライブラリで画像を読み書きする方法を記述しました。
GDI+ --- 画像ファイル読み書き - 何でもプログラミング

Bitmapであれば更に何のライブラリも必要なく簡単に読み書きできます。

ちょっとした画像関係の動作確認レベルの時に便利です。

今回は24bitBitmapを読み書きしてみます。

ヘッダ構造体

Bitmapファイルのヘッダ部分をまとめた構造体です。

コンパイラが勝手にパディングを入れないよう、pshpack2、poppackで挟んでいます。(VisualC++専用)

#include <pshpack2.h>
struct BitmapHeader
{
    unsigned short bfType; 
    unsigned long bfSize;
    unsigned short bfReserved1;
    unsigned short bfReserved2;
    unsigned long bfOffBits;
    unsigned int biSize;
    int biWidth;
    int biHeight;
    unsigned short biPlanes;
    unsigned short biBitCount;
    unsigned int biCompression;
    unsigned int biSizeImage;
    int biXPixPerMeter;
    int biYPixPerMeter;
    unsigned int biClrUsed;
    unsigned int biClrImporant;
};
#include <poppack.h>


読み込み

Bitmapの画像データは、幅が4Byteの倍数になるよう格納されています。

今回はデータを詰めて取り出すよう実装しています。

void LoadBitmap24(std::string path, int* width, int* height, std::vector<unsigned char>* pixels)
{
    std::ifstream file(path, std::ios::binary);
    assert(file.is_open());

    BitmapHeader header;
    file.read((char*)&header, sizeof(header));
    int w = header.biWidth;
    int h = header.biHeight;
    int step = ToBitmapStep(3 * w);

    std::vector<unsigned char> originalPixels(step * h);
    file.read((char*)originalPixels.data(), originalPixels.size());

    pixels->resize(3 * w * h);
    for (int i = 0; i < h; ++i)
        memcpy(pixels->data() + i * 3 * w, originalPixels.data() + i * step, 3 * w);

    *width = w;
    *height = h;
}
int ToBitmapStep(int step)
{
    int paddings[] = { 0, 3, 2, 1 };
    return step + paddings[step % 4];
}


書き込み

ヘッダの内容は、ほとんど決められた値になっています。

画像データを、幅が4Byteの倍数になるよう調整して書き出しています。

void SaveBitmap24(std::string path, int width, int height, const unsigned char* pixels)
{
    std::ofstream file(path, std::ios::binary);
    assert(file.is_open());

    int step = ToBitmapStep(3 * width);

    BitmapHeader header;
    header.bfType = 0x4d42; // "BM"
    header.bfSize = step * height + 54;
    header.bfReserved1 = 0;
    header.bfReserved2 = 0;
    header.bfOffBits = 54;
    header.biSize = 40;
    header.biWidth = width;
    header.biHeight = height;
    header.biPlanes = 1;
    header.biBitCount = 24;
    header.biCompression = 0;
    header.biSizeImage = 0;
    header.biXPixPerMeter = 0;
    header.biYPixPerMeter = 0;
    header.biClrUsed = 0;
    header.biClrImporant = 0;
    file.write((char*)&header, sizeof(header));

    std::vector<unsigned char> bmpPixels(step * height);
    for (int i = 0; i < height; ++i)
        memcpy(bmpPixels.data() + i * step, pixels + i * 3 * width, 3 * width);
    file.write((char*)bmpPixels.data(), bmpPixels.size());
}


動作確認

Bitmapを読み込んで、赤青反転の後にBitmap保存をしてみます。

int main()
{
    int width, height;
    std::vector<byte> pixels;
    LoadBitmap24("parrots.bmp", &width, &height, &pixels);

    for (int i = 0; i < width * height; ++i)
        std::swap(pixels[3 * i], pixels[3 * i + 2]);

    SaveBitmap24("output.bmp", width, height, pixels.data());
    return 0;
}
f:id:any-programming:20170319005947j:plain f:id:any-programming:20170319010003j:plain