57

私のアルゴリズムでは、情報出力を作成する必要があります。ブール行列をbmpファイルに書き込む必要があります。それは単色画像でなければならず、そのような要素のマトリックスが真である場合、ピクセルは白です。主な問題は、bmpヘッダーとこれを作成する方法です。

4

12 に答える 12

62

これがうまくいくかどうかを確認してください...このコードでは、赤、緑、青と呼ばれる3つの2次元配列がありました。それぞれのサイズは[幅][高さ]で、各要素はピクセルに対応していました。これが理にかなっていることを願っています。

FILE *f;
unsigned char *img = NULL;
int filesize = 54 + 3*w*h;  //w is your image width, h is image height, both int

img = (unsigned char *)malloc(3*w*h);
memset(img,0,3*w*h);

for(int i=0; i<w; i++)
{
    for(int j=0; j<h; j++)
    {
        x=i; y=(h-1)-j;
        r = red[i][j]*255;
        g = green[i][j]*255;
        b = blue[i][j]*255;
        if (r > 255) r=255;
        if (g > 255) g=255;
        if (b > 255) b=255;
        img[(x+y*w)*3+2] = (unsigned char)(r);
        img[(x+y*w)*3+1] = (unsigned char)(g);
        img[(x+y*w)*3+0] = (unsigned char)(b);
    }
}

unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};
unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};
unsigned char bmppad[3] = {0,0,0};

bmpfileheader[ 2] = (unsigned char)(filesize    );
bmpfileheader[ 3] = (unsigned char)(filesize>> 8);
bmpfileheader[ 4] = (unsigned char)(filesize>>16);
bmpfileheader[ 5] = (unsigned char)(filesize>>24);

bmpinfoheader[ 4] = (unsigned char)(       w    );
bmpinfoheader[ 5] = (unsigned char)(       w>> 8);
bmpinfoheader[ 6] = (unsigned char)(       w>>16);
bmpinfoheader[ 7] = (unsigned char)(       w>>24);
bmpinfoheader[ 8] = (unsigned char)(       h    );
bmpinfoheader[ 9] = (unsigned char)(       h>> 8);
bmpinfoheader[10] = (unsigned char)(       h>>16);
bmpinfoheader[11] = (unsigned char)(       h>>24);

f = fopen("img.bmp","wb");
fwrite(bmpfileheader,1,14,f);
fwrite(bmpinfoheader,1,40,f);
for(int i=0; i<h; i++)
{
    fwrite(img+(w*(h-i-1)*3),3,w,f);
    fwrite(bmppad,1,(4-(w*3)%4)%4,f);
}

free(img);
fclose(f);
于 2010-04-16T17:07:47.777 に答える
37

ビットマップ(BMP)画像生成用のクリーンなCコード

ビットマップ画像


このコードは、 stdio.h以外のライブラリを使用していません。そのため、C ++、C#、Javaなど、Cファミリの他の言語に簡単に組み込むことができます。


#include <stdio.h>

const int BYTES_PER_PIXEL = 3; /// red, green, & blue
const int FILE_HEADER_SIZE = 14;
const int INFO_HEADER_SIZE = 40;

void generateBitmapImage(unsigned char* image, int height, int width, char* imageFileName);
unsigned char* createBitmapFileHeader(int height, int stride);
unsigned char* createBitmapInfoHeader(int height, int width);


int main ()
{
    int height = 361;
    int width = 867;
    unsigned char image[height][width][BYTES_PER_PIXEL];
    char* imageFileName = (char*) "bitmapImage.bmp";

    int i, j;
    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            image[i][j][2] = (unsigned char) ( i * 255 / height );             ///red
            image[i][j][1] = (unsigned char) ( j * 255 / width );              ///green
            image[i][j][0] = (unsigned char) ( (i+j) * 255 / (height+width) ); ///blue
        }
    }

    generateBitmapImage((unsigned char*) image, height, width, imageFileName);
    printf("Image generated!!");
}


void generateBitmapImage (unsigned char* image, int height, int width, char* imageFileName)
{
    int widthInBytes = width * BYTES_PER_PIXEL;

    unsigned char padding[3] = {0, 0, 0};
    int paddingSize = (4 - (widthInBytes) % 4) % 4;

    int stride = (widthInBytes) + paddingSize;

    FILE* imageFile = fopen(imageFileName, "wb");

    unsigned char* fileHeader = createBitmapFileHeader(height, stride);
    fwrite(fileHeader, 1, FILE_HEADER_SIZE, imageFile);

    unsigned char* infoHeader = createBitmapInfoHeader(height, width);
    fwrite(infoHeader, 1, INFO_HEADER_SIZE, imageFile);

    int i;
    for (i = 0; i < height; i++) {
        fwrite(image + (i*widthInBytes), BYTES_PER_PIXEL, width, imageFile);
        fwrite(padding, 1, paddingSize, imageFile);
    }

    fclose(imageFile);
}

unsigned char* createBitmapFileHeader (int height, int stride)
{
    int fileSize = FILE_HEADER_SIZE + INFO_HEADER_SIZE + (stride * height);

    static unsigned char fileHeader[] = {
        0,0,     /// signature
        0,0,0,0, /// image file size in bytes
        0,0,0,0, /// reserved
        0,0,0,0, /// start of pixel array
    };

    fileHeader[ 0] = (unsigned char)('B');
    fileHeader[ 1] = (unsigned char)('M');
    fileHeader[ 2] = (unsigned char)(fileSize      );
    fileHeader[ 3] = (unsigned char)(fileSize >>  8);
    fileHeader[ 4] = (unsigned char)(fileSize >> 16);
    fileHeader[ 5] = (unsigned char)(fileSize >> 24);
    fileHeader[10] = (unsigned char)(FILE_HEADER_SIZE + INFO_HEADER_SIZE);

    return fileHeader;
}

unsigned char* createBitmapInfoHeader (int height, int width)
{
    static unsigned char infoHeader[] = {
        0,0,0,0, /// header size
        0,0,0,0, /// image width
        0,0,0,0, /// image height
        0,0,     /// number of color planes
        0,0,     /// bits per pixel
        0,0,0,0, /// compression
        0,0,0,0, /// image size
        0,0,0,0, /// horizontal resolution
        0,0,0,0, /// vertical resolution
        0,0,0,0, /// colors in color table
        0,0,0,0, /// important color count
    };

    infoHeader[ 0] = (unsigned char)(INFO_HEADER_SIZE);
    infoHeader[ 4] = (unsigned char)(width      );
    infoHeader[ 5] = (unsigned char)(width >>  8);
    infoHeader[ 6] = (unsigned char)(width >> 16);
    infoHeader[ 7] = (unsigned char)(width >> 24);
    infoHeader[ 8] = (unsigned char)(height      );
    infoHeader[ 9] = (unsigned char)(height >>  8);
    infoHeader[10] = (unsigned char)(height >> 16);
    infoHeader[11] = (unsigned char)(height >> 24);
    infoHeader[12] = (unsigned char)(1);
    infoHeader[14] = (unsigned char)(BYTES_PER_PIXEL*8);

    return infoHeader;
}
于 2017-12-13T04:45:15.700 に答える
29

他のライブラリを使用せずに、BMPファイル形式を確認できます。私は過去にそれを実装しました、そしてそれはあまり多くの仕事なしで行うことができます。

ビットマップ-ファイル構造

各ビットマップファイルには、ビットマップファイルヘッダー、ビットマップ情報ヘッダー、カラーテーブル、およびビットマップビットを定義するバイト配列が含まれています。ファイルの形式は次のとおりです。

BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD aColors [];
BYTE aBitmapBits [];

...詳細については、ファイル形式を参照してください

于 2010-04-16T16:13:19.100 に答える
12

これはhttps://en.wikipedia.org/wiki/User:Evercat/Buddhabrot.cからコピーされたサンプルコードです

void drawbmp (char * filename) {

unsigned int headers[13];
FILE * outfile;
int extrabytes;
int paddedsize;
int x; int y; int n;
int red, green, blue;

extrabytes = 4 - ((WIDTH * 3) % 4);                 // How many bytes of padding to add to each
                                                    // horizontal line - the size of which must
                                                    // be a multiple of 4 bytes.
if (extrabytes == 4)
   extrabytes = 0;

paddedsize = ((WIDTH * 3) + extrabytes) * HEIGHT;

// Headers...
// Note that the "BM" identifier in bytes 0 and 1 is NOT included in these "headers".
                     
headers[0]  = paddedsize + 54;      // bfSize (whole file size)
headers[1]  = 0;                    // bfReserved (both)
headers[2]  = 54;                   // bfOffbits
headers[3]  = 40;                   // biSize
headers[4]  = WIDTH;  // biWidth
headers[5]  = HEIGHT; // biHeight

// Would have biPlanes and biBitCount in position 6, but they're shorts.
// It's easier to write them out separately (see below) than pretend
// they're a single int, especially with endian issues...

headers[7]  = 0;                    // biCompression
headers[8]  = paddedsize;           // biSizeImage
headers[9]  = 0;                    // biXPelsPerMeter
headers[10] = 0;                    // biYPelsPerMeter
headers[11] = 0;                    // biClrUsed
headers[12] = 0;                    // biClrImportant

outfile = fopen(filename, "wb");

//
// Headers begin...
// When printing ints and shorts, we write out 1 character at a time to avoid endian issues.
//

fprintf(outfile, "BM");

for (n = 0; n <= 5; n++)
{
   fprintf(outfile, "%c", headers[n] & 0x000000FF);
   fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8);
   fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16);
   fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24);
}

// These next 4 characters are for the biPlanes and biBitCount fields.

fprintf(outfile, "%c", 1);
fprintf(outfile, "%c", 0);
fprintf(outfile, "%c", 24);
fprintf(outfile, "%c", 0);

for (n = 7; n <= 12; n++)
{
   fprintf(outfile, "%c", headers[n] & 0x000000FF);
   fprintf(outfile, "%c", (headers[n] & 0x0000FF00) >> 8);
   fprintf(outfile, "%c", (headers[n] & 0x00FF0000) >> 16);
   fprintf(outfile, "%c", (headers[n] & (unsigned int) 0xFF000000) >> 24);
}

//
// Headers done, now write the data...
//

for (y = HEIGHT - 1; y >= 0; y--)     // BMP image format is written from bottom to top...
{
   for (x = 0; x <= WIDTH - 1; x++)
   {

      red = reduce(redcount[x][y] + COLOUR_OFFSET) * red_multiplier;
      green = reduce(greencount[x][y] + COLOUR_OFFSET) * green_multiplier;
      blue = reduce(bluecount[x][y] + COLOUR_OFFSET) * blue_multiplier;
      
      if (red > 255) red = 255; if (red < 0) red = 0;
      if (green > 255) green = 255; if (green < 0) green = 0;
      if (blue > 255) blue = 255; if (blue < 0) blue = 0;
      
      // Also, it's written in (b,g,r) format...

      fprintf(outfile, "%c", blue);
      fprintf(outfile, "%c", green);
      fprintf(outfile, "%c", red);
   }
   if (extrabytes)      // See above - BMP lines must be of lengths divisible by 4.
   {
      for (n = 1; n <= extrabytes; n++)
      {
         fprintf(outfile, "%c", 0);
      }
   }
}

fclose(outfile);
return;
}


drawbmp(filename);
于 2015-08-15T17:33:42.930 に答える
9

行は下から上に保存され、その逆ではないことに注意してください。

さらに、スキャンラインのバイト長は4の倍数である必要があります。これを確実にするために、行の最後にフィルバイトを挿入する必要があります。

于 2010-04-16T16:18:06.283 に答える
9

これが私のために働くコードのC++バリアントです。行のパディングを考慮して、サイズの計算を変更する必要があることに注意してください。

// mimeType = "image/bmp";

unsigned char file[14] = {
    'B','M', // magic
    0,0,0,0, // size in bytes
    0,0, // app data
    0,0, // app data
    40+14,0,0,0 // start of data offset
};
unsigned char info[40] = {
    40,0,0,0, // info hd size
    0,0,0,0, // width
    0,0,0,0, // heigth
    1,0, // number color planes
    24,0, // bits per pixel
    0,0,0,0, // compression is none
    0,0,0,0, // image bits size
    0x13,0x0B,0,0, // horz resoluition in pixel / m
    0x13,0x0B,0,0, // vert resolutions (0x03C3 = 96 dpi, 0x0B13 = 72 dpi)
    0,0,0,0, // #colors in pallete
    0,0,0,0, // #important colors
    };

int w=waterfallWidth;
int h=waterfallHeight;

int padSize  = (4-(w*3)%4)%4;
int sizeData = w*h*3 + h*padSize;
int sizeAll  = sizeData + sizeof(file) + sizeof(info);

file[ 2] = (unsigned char)( sizeAll    );
file[ 3] = (unsigned char)( sizeAll>> 8);
file[ 4] = (unsigned char)( sizeAll>>16);
file[ 5] = (unsigned char)( sizeAll>>24);

info[ 4] = (unsigned char)( w   );
info[ 5] = (unsigned char)( w>> 8);
info[ 6] = (unsigned char)( w>>16);
info[ 7] = (unsigned char)( w>>24);

info[ 8] = (unsigned char)( h    );
info[ 9] = (unsigned char)( h>> 8);
info[10] = (unsigned char)( h>>16);
info[11] = (unsigned char)( h>>24);

info[20] = (unsigned char)( sizeData    );
info[21] = (unsigned char)( sizeData>> 8);
info[22] = (unsigned char)( sizeData>>16);
info[23] = (unsigned char)( sizeData>>24);

stream.write( (char*)file, sizeof(file) );
stream.write( (char*)info, sizeof(info) );

unsigned char pad[3] = {0,0,0};

for ( int y=0; y<h; y++ )
{
    for ( int x=0; x<w; x++ )
    {
        long red = lround( 255.0 * waterfall[x][y] );
        if ( red < 0 ) red=0;
        if ( red > 255 ) red=255;
        long green = red;
        long blue = red;

        unsigned char pixel[3];
        pixel[0] = blue;
        pixel[1] = green;
        pixel[2] = red;

        stream.write( (char*)pixel, 3 );
    }
    stream.write( (char*)pad, padSize );
}
于 2013-09-07T17:06:31.493 に答える
7

ralfのhtpコードをコンパイルするように編集しました(gccでは、ubuntu 16.04 ltsを実行しています)。変数を初期化するだけの問題でした。

    int w = 100; /* Put here what ever width you want */
    int h = 100; /* Put here what ever height you want */
    int red[w][h]; 
    int green[w][h];
    int blue[w][h];


    FILE *f;
    unsigned char *img = NULL;
    int filesize = 54 + 3*w*h;  //w is your image width, h is image height, both int
    if( img )
            free( img );
    img = (unsigned char *)malloc(3*w*h);
    memset(img,0,sizeof(img));
    int x;
    int y;
    int r;
    int g;
    int b;

    for(int i=0; i<w; i++)
    {
            for(int j=0; j<h; j++)
            {
                    x=i; y=(h-1)-j;
                    r = red[i][j]*255;
                    g = green[i][j]*255;
                    b = blue[i][j]*255;
                    if (r > 255) r=255;
                    if (g > 255) g=255;
                    if (b > 255) b=255;
                    img[(x+y*w)*3+2] = (unsigned char)(r);
                    img[(x+y*w)*3+1] = (unsigned char)(g);
                    img[(x+y*w)*3+0] = (unsigned char)(b);
            }
    }

    unsigned char bmpfileheader[14] = {'B','M', 0,0,0,0, 0,0, 0,0, 54,0,0,0};
    unsigned char bmpinfoheader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};
    unsigned char bmppad[3] = {0,0,0};

    bmpfileheader[ 2] = (unsigned char)(filesize    );
    bmpfileheader[ 3] = (unsigned char)(filesize>> 8);
    bmpfileheader[ 4] = (unsigned char)(filesize>>16);
    bmpfileheader[ 5] = (unsigned char)(filesize>>24);

    bmpinfoheader[ 4] = (unsigned char)(       w    );
    bmpinfoheader[ 5] = (unsigned char)(       w>> 8);
    bmpinfoheader[ 6] = (unsigned char)(       w>>16);
    bmpinfoheader[ 7] = (unsigned char)(       w>>24);
    bmpinfoheader[ 8] = (unsigned char)(       h    );
    bmpinfoheader[ 9] = (unsigned char)(       h>> 8);
    bmpinfoheader[10] = (unsigned char)(       h>>16);
    bmpinfoheader[11] = (unsigned char)(       h>>24);

    f = fopen("img.bmp","wb");
    fwrite(bmpfileheader,1,14,f);
    fwrite(bmpinfoheader,1,40,f);
    for(int i=0; i<h; i++)
    {
            fwrite(img+(w*(h-i-1)*3),3,w,f);
            fwrite(bmppad,1,(4-(w*3)%4)%4,f);
    }
    fclose(f);
于 2017-05-22T21:06:51.693 に答える
7

Minhas Kamalのコードの改良版を共有したかったのは、ほとんどのアプリケーションで十分に機能しましたが、それでもいくつかの問題があったためです。覚えておくべき2つの非常に重要なこと:

  1. コード(執筆時点)は、2つの静的配列でfree()を呼び出します。これにより、プログラムがクラッシュします。だから私はそれらの行をコメントアウトしました。
  2. ピクセルデータのピッチが常に(Width * BytesPerPixel)であると想定しないでください。ユーザーにピッチ値を指定させるのが最善です。例:Direct3Dでリソースを操作する場合、RowPitchが使用されているバイト深度の偶数倍であることが保証されることはありません。これにより、生成されたビットマップでエラーが発生する可能性があります(特に、1366x768などの奇数の解像度で)。

以下に、彼のコードに対する私の改訂を見ることができます。

const int bytesPerPixel = 4; /// red, green, blue
const int fileHeaderSize = 14;
const int infoHeaderSize = 40;

void generateBitmapImage(unsigned char *image, int height, int width, int pitch, const char* imageFileName);
unsigned char* createBitmapFileHeader(int height, int width, int pitch, int paddingSize);
unsigned char* createBitmapInfoHeader(int height, int width);



void generateBitmapImage(unsigned char *image, int height, int width, int pitch, const char* imageFileName) {

    unsigned char padding[3] = { 0, 0, 0 };
    int paddingSize = (4 - (/*width*bytesPerPixel*/ pitch) % 4) % 4;

    unsigned char* fileHeader = createBitmapFileHeader(height, width, pitch, paddingSize);
    unsigned char* infoHeader = createBitmapInfoHeader(height, width);

    FILE* imageFile = fopen(imageFileName, "wb");

    fwrite(fileHeader, 1, fileHeaderSize, imageFile);
    fwrite(infoHeader, 1, infoHeaderSize, imageFile);

    int i;
    for (i = 0; i < height; i++) {
        fwrite(image + (i*pitch /*width*bytesPerPixel*/), bytesPerPixel, width, imageFile);
        fwrite(padding, 1, paddingSize, imageFile);
    }

    fclose(imageFile);
    //free(fileHeader);
    //free(infoHeader);
}

unsigned char* createBitmapFileHeader(int height, int width, int pitch, int paddingSize) {
    int fileSize = fileHeaderSize + infoHeaderSize + (/*bytesPerPixel*width*/pitch + paddingSize) * height;

    static unsigned char fileHeader[] = {
        0,0, /// signature
        0,0,0,0, /// image file size in bytes
        0,0,0,0, /// reserved
        0,0,0,0, /// start of pixel array
    };

    fileHeader[0] = (unsigned char)('B');
    fileHeader[1] = (unsigned char)('M');
    fileHeader[2] = (unsigned char)(fileSize);
    fileHeader[3] = (unsigned char)(fileSize >> 8);
    fileHeader[4] = (unsigned char)(fileSize >> 16);
    fileHeader[5] = (unsigned char)(fileSize >> 24);
    fileHeader[10] = (unsigned char)(fileHeaderSize + infoHeaderSize);

    return fileHeader;
}

unsigned char* createBitmapInfoHeader(int height, int width) {
    static unsigned char infoHeader[] = {
        0,0,0,0, /// header size
        0,0,0,0, /// image width
        0,0,0,0, /// image height
        0,0, /// number of color planes
        0,0, /// bits per pixel
        0,0,0,0, /// compression
        0,0,0,0, /// image size
        0,0,0,0, /// horizontal resolution
        0,0,0,0, /// vertical resolution
        0,0,0,0, /// colors in color table
        0,0,0,0, /// important color count
    };

    infoHeader[0] = (unsigned char)(infoHeaderSize);
    infoHeader[4] = (unsigned char)(width);
    infoHeader[5] = (unsigned char)(width >> 8);
    infoHeader[6] = (unsigned char)(width >> 16);
    infoHeader[7] = (unsigned char)(width >> 24);
    infoHeader[8] = (unsigned char)(height);
    infoHeader[9] = (unsigned char)(height >> 8);
    infoHeader[10] = (unsigned char)(height >> 16);
    infoHeader[11] = (unsigned char)(height >> 24);
    infoHeader[12] = (unsigned char)(1);
    infoHeader[14] = (unsigned char)(bytesPerPixel * 8);

    return infoHeader;
}
于 2019-04-03T21:16:34.943 に答える
2

上記のC++関数を使用して、画像の中央で奇妙な色のスイッチが表示された場合。必ずバイナリモードでアウトストリームを開いてください。 imgFile.open(filename, std::ios_base::out | std::ios_base::binary);
そうしないと、Windowsがファイルの途中に不要な文字を挿入します。(この問題について何時間も頭を悩ませてきました)

ここで関連する質問を参照してください:ofstreamが0x0Aの前に0x0Dバイトを挿入するのはなぜですか?

于 2015-05-25T09:19:33.953 に答える
2

最高のビットマップエンコーダーは、自分で作成しないエンコーダーです。ファイル形式は、予想以上に複雑です。これは、提案されたすべての回答がモノクロ(1bpp)ビットマップを作成するのではなく、たまたま2色しか使用しない24bppファイルを書き出すという事実によって証明されています。

以下は、 WindowsImagingComponentを使用したWindowsのみのソリューションです。Windowsに付属しているものを除いて、外部のサードパーティライブラリに依存していません。

すべてのC++プログラムと同様に、いくつかのヘッダーファイルをインクルードする必要があります。そして、私たちがそれにいる間、 Windowscodecs.libにリンクします:

#include <Windows.h>
#include <comdef.h>
#include <comip.h>
#include <comutil.h>
#include <wincodec.h>

#include <vector>

#pragma comment(lib, "Windowscodecs.lib")

次に、コンテナ(ベクトル、ベクトルの!のbool!)と、便宜上いくつかのスマートポインタを宣言します。

using _com_util::CheckError;
using container = std::vector<std::vector<bool>>;

_COM_SMARTPTR_TYPEDEF(IWICImagingFactory, __uuidof(IWICImagingFactory));
_COM_SMARTPTR_TYPEDEF(IWICBitmapEncoder, __uuidof(IWICBitmapEncoder));
_COM_SMARTPTR_TYPEDEF(IWICBitmapFrameEncode, __uuidof(IWICBitmapFrameEncode));
_COM_SMARTPTR_TYPEDEF(IWICStream, __uuidof(IWICStream));
_COM_SMARTPTR_TYPEDEF(IWICPalette, __uuidof(IWICPalette));

これですべてが解決したので、すぐに実装に取り​​掛かることができます。工場、エンコーダー、フレームを入手し、すべてを準備するために必要なセットアップが少しあります。

void write_bitmap(wchar_t const* pathname, container const& data)
{
    // Create factory
    IWICImagingFactoryPtr sp_factory { nullptr };
    CheckError(sp_factory.CreateInstance(CLSID_WICImagingFactory, nullptr,
                                         CLSCTX_INPROC_SERVER));

    // Create encoder
    IWICBitmapEncoderPtr sp_encoder { nullptr };
    CheckError(sp_factory->CreateEncoder(GUID_ContainerFormatBmp, nullptr, &sp_encoder));

    // Create stream
    IWICStreamPtr sp_stream { nullptr };
    CheckError(sp_factory->CreateStream(&sp_stream));
    CheckError(sp_stream->InitializeFromFilename(pathname, GENERIC_WRITE));

    // Initialize encoder with stream
    CheckError(sp_encoder->Initialize(sp_stream, WICBitmapEncoderNoCache));

    // Create new frame
    IWICBitmapFrameEncodePtr sp_frame { nullptr };
    IPropertyBag2Ptr sp_properties { nullptr };
    CheckError(sp_encoder->CreateNewFrame(&sp_frame, &sp_properties));

    // Initialize frame with default properties
    CheckError(sp_frame->Initialize(sp_properties));

    // Set pixel format
    // SetPixelFormat() requires a pointer to non-const
    auto pf { GUID_WICPixelFormat1bppIndexed };
    CheckError(sp_frame->SetPixelFormat(&pf));
    if (!::IsEqualGUID(pf, GUID_WICPixelFormat1bppIndexed))
    {
        // Report unsupported pixel format
        CheckError(WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT);
    }

    // Set size derived from data argument
    auto const width { static_cast<UINT>(data.size()) };
    auto const height { static_cast<UINT>(data[0].size()) };
    CheckError(sp_frame->SetSize(width, height));

    // Set palette on frame. This is required since we use an indexed pixel format.
    // Only GIF files support global palettes, so make sure to set it on the frame
    // rather than the encoder.
    IWICPalettePtr sp_palette { nullptr };
    CheckError(sp_factory->CreatePalette(&sp_palette));
    CheckError(sp_palette->InitializePredefined(WICBitmapPaletteTypeFixedBW, FALSE));
    CheckError(sp_frame->SetPalette(sp_palette));

その時点ですべてがセットアップされ、データをダンプするフレームがあります。1bppファイルの場合、すべてのバイトに8ピクセルの情報が格納されます。左端のピクセルはMSBに格納され、ピクセルはLSBに格納されている右端のピクセルまで続きます。

コードは完全に重要ではありません。とにかく入力のデータレイアウトを置き換えるときは、それをニーズに合ったものに置き換えることになります。

    // Write data to frame
    auto const stride { (width * 1 + 7) / 8 };
    auto const size { height * stride };
    std::vector<unsigned char> buffer(size, 127u);
    // Convert data to match required layout. Each byte stores 8 pixels, with the
    // MSB being the leftmost, the LSB the right-most.
    for (size_t x { 0 }; x < data.size(); ++x)
    {
        for (size_t y { 0 }; y < data[x].size(); ++y)
        {
            auto shift { x % 8 };
            auto mask { 0x80 >> shift };
            auto bit { mask * data[x][y] };
            auto& value { buffer[y * stride + x / 8] };
            value &= ~mask;
            value |= bit;
        }
    }
    CheckError(sp_frame->WritePixels(height, stride,
                                     static_cast<UINT>(buffer.size()), buffer.data()));

残っているのは、フレームとエンコーダーへの変更をコミットすることです。これにより、最終的にイメージファイルがディスクに書き込まれます。

    // Commit frame
    CheckError(sp_frame->Commit());

    // Commit image
    CheckError(sp_encoder->Commit());
}

これはテストプログラムであり、最初のコマンドライン引数として渡されたファイルに画像を書き込みます。

#include <iostream>

int wmain(int argc, wchar_t* argv[])
try
{
    if (argc != 2)
    {
        return -1;
    }

    CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));


    // Create 64x64 matrix
    container data(64, std::vector<bool>(64, false));
    // Fill with arrow pointing towards the upper left
    for (size_t i { 0 }; i < data.size(); ++i)
    {
        data[0][i] = true;
        data[i][0] = true;
        data[i][i] = true;
    }
    ::write_bitmap(argv[1], data);


    ::CoUninitialize();
}
catch (_com_error const& e)
{
    std::wcout << L"Error!\n" << L"  Message: " << e.ErrorMessage() << std::endl;
}

次の画像(真の1bpp、サイズ574バイト)を生成します。

プログラム出力

于 2020-01-14T23:23:56.413 に答える
2

これが単純なc++bmp画像ファイルクラスです。

class bmp_img {
public:
    constexpr static int header_size = 14;
    constexpr static int info_header_size = 40;

    bmp_img(size_t width, size_t height, size_t bpp = 3) :
        bytes_per_pixel{ bpp }, image_px_width{ width }, image_px_height{ height }, row_width{ image_px_width * bytes_per_pixel },
        row_padding{ (4 - row_width % 4) % 4 }, row_stride{ row_width + row_padding }, file_size{ header_size + info_header_size + (image_px_height * row_stride) },
        image(image_px_height, std::vector<unsigned char>(row_width))
    {
        //header file type
        file_header[0] = 'B';
        file_header[1] = 'M';


        //header file size info
        file_header[2] = static_cast<unsigned char>(file_size);
        file_header[3] = static_cast<unsigned char>(file_size >> 8);
        file_header[4] = static_cast<unsigned char>(file_size >> 16);
        file_header[5] = static_cast<unsigned char>(file_size >> 24);

        //header offset to pixel data
        file_header[10] = header_size + info_header_size;

        //info header size
        info_header[0] = info_header_size;

        //info header image width
        info_header[4] = static_cast<unsigned char>(image_px_width);
        info_header[5] = static_cast<unsigned char>(image_px_width >> 8);
        info_header[6] = static_cast<unsigned char>(image_px_width >> 16);
        info_header[7] = static_cast<unsigned char>(image_px_width >> 24);

        //info header image height
        info_header[8] = static_cast<unsigned char>(image_px_height);
        info_header[9] = static_cast<unsigned char>(image_px_height >> 8);
        info_header[10] = static_cast<unsigned char>(image_px_height >> 16);
        info_header[11] = static_cast<unsigned char>(image_px_height >> 24);

        //info header planes
        info_header[12] = 1;

        //info header bits per pixel
        info_header[14] = 8 * bytes_per_pixel;
    }

    size_t width() const {
        return image_px_width;
    }

    size_t height() const {
        return image_px_height;
    }

    void set_pixel(size_t x, size_t y, int r, int g, int b) {
        image[y][x * bytes_per_pixel + 2] = r;
        image[y][x * bytes_per_pixel + 1] = g;
        image[y][x * bytes_per_pixel + 0] = b;
    }

    void fill(int r, int g, int b) {
        for (int y = 0; y < image_px_height; ++y) {
            for (int x = 0; x < image_px_width; ++x) {
                set_pixel(x, y, r, g, b);
            }
        }
    }

    void write_to_file(const char* file_name) const {
        std::ofstream img_file(file_name, std::ios_base::binary | std::ios_base::out);

        img_file.write((char*)file_header, header_size);
        img_file.write((char*)info_header, info_header_size);

        std::vector<char> allignment(row_padding);

        for (int y = image_px_height - 1; y >= 0; --y) {
            img_file.write((char*)image[y].data(), row_width);

            img_file.write(allignment.data(), row_padding);
        }

        img_file.close();
    }
private:
    size_t bytes_per_pixel;

    size_t image_px_width;
    size_t image_px_height;

    size_t row_width;

    size_t row_padding;

    size_t row_stride;

    size_t file_size;

    unsigned char file_header[header_size] = { 0 };
    unsigned char info_header[info_header_size] = { 0 };
    std::vector<std::vector<unsigned char>> image;
};
于 2021-08-14T04:09:37.640 に答える
1

C ++の答えである柔軟なAPIは、リトルエンディアンのシステムでコードゴルフをすることを前提としています。これはbmpネイティブy軸(下部の0)を使用することに注意してください。

#include <vector>
#include <fstream>

struct image
{   
    image(int width, int height)
    :   w(width), h(height), rgb(w * h * 3)
    {}
    uint8_t & r(int x, int y) { return rgb[(x + y*w)*3 + 2]; }
    uint8_t & g(int x, int y) { return rgb[(x + y*w)*3 + 1]; }
    uint8_t & b(int x, int y) { return rgb[(x + y*w)*3 + 0]; }

    int w, h;
    std::vector<uint8_t> rgb;
};

template<class Stream>
Stream & operator<<(Stream & out, image const& img)
{   
    uint32_t w = img.w, h = img.h;
    uint32_t pad = w * -3 & 3;
    uint32_t total = 54 + 3*w*h + pad*h;
    uint32_t head[13] = {total, 0, 54, 40, w, h, (24<<16)|1};
    char const* rgb = (char const*)img.rgb.data();

    out.write("BM", 2);
    out.write((char*)head, 52);
    for(uint32_t i=0 ; i<h ; i++)
    {   out.write(rgb + (3 * w * i), 3 * w);
        out.write((char*)&pad, pad);
    }
    return out;
}

int main()
{
    image img(100, 100);
    for(int x=0 ; x<100 ; x++)
    {   for(int y=0 ; y<100 ; y++)
        {   img.r(x,y) = x;
            img.g(x,y) = y;
            img.b(x,y) = 100-x;
        }
    }
    std::ofstream("/tmp/out.bmp") << img;
}
于 2020-07-17T02:51:02.750 に答える