static int read_bmp_header(const unsigned char *p) { if (*p++ != 'B') return 0; if (*p++ != 'M') return 0; if (get_4(&p) != HEADER_SIZE + png.width * png.height * 4) return 0; get_4(&p); if (get_4(&p) != HEADER_SIZE) return 0; if (get_4(&p) != 40) return 0; if (get_4(&p) != png.width) return 0; if (get_4(&p) != -png.height) return 0; get_2(&p); if (get_2(&p) != 32) return 0; if (get_4(&p) != 0) return 0; if (get_4(&p) != png.width * png.height * 4) return 0; get_4(&p); get_4(&p); get_4(&p); get_4(&p); return 1; }
void ImageBMP::ReadBMP(const uint8_t* data, unsigned len, bool transparent, int& width, int& height, void*& pixels) { pixels = NULL; // BITMAPFILEHEADER structure // // 0 2 signature: 'B','M' // 2 4 file size // 6 2 reserved // 8 2 reserved // 10 4 offset to bitmap data static const unsigned BITMAPFILEHEADER_SIZE = 14; if (len < 64) { Output::Error("Not a valid BMP file."); return; } // file size is skipped because every program writes other data into // this field and not needed for correct decoding. const unsigned bits_offset = get_4(&data[10]); // BITMAPINFOHEADER structure (BMP 2.x and 3.x) // // 0 4 BITMAPINFOHEADER size // 4 4 width // 8 4 height (+ve => bottom-up, -ve => top-down) // 12 2 number of planes (must be 1) // 14 2 bits per pixel // 16 4 compression // 20 4 image size // 24 4 X pixels per meter // 28 4 Y pixels per meter // 32 4 number of palette colors used // 36 4 number of important palette colors // size ... palette (starts at 40 in BMP 2.x/3.x and 108 in BMP 4.x) width = (int) get_4(&data[BITMAPFILEHEADER_SIZE + 4]); height = (int) get_4(&data[BITMAPFILEHEADER_SIZE + 8]); // bitmap scan lines need to be aligned to 32 bit boundaries, add padding if needed int padding = (width % 4 != 0) ? 4 - width % 4 : 0; bool vflip = height > 0; if (!vflip) height = -height; const int planes = (int) get_2(&data[BITMAPFILEHEADER_SIZE + 12]); if (planes != 1) { Output::Error("BMP planes is not 1."); return; } const int depth = (int) get_2(&data[BITMAPFILEHEADER_SIZE + 14]); if (depth != 8) { Output::Error("BMP image is not 8-bit."); return; } const int compression = get_4(&data[BITMAPFILEHEADER_SIZE + 16]); static const int BI_RGB = 0; if (compression != BI_RGB) { Output::Error("BMP image is compressed."); return; } int num_colors = std::min(256U, get_4(&data[BITMAPFILEHEADER_SIZE + 32])); uint8_t (*palette)[4] = (uint8_t(*)[4]) &data[BITMAPFILEHEADER_SIZE + get_4(&data[BITMAPFILEHEADER_SIZE + 0])]; const uint8_t* src_pixels = &data[bits_offset]; // Ensure no palette entry is an exact duplicate of #0 for (int i = 1; i < num_colors; i++) { if (palette[i][0] == palette[0][0] && palette[i][1] == palette[0][1] && palette[i][2] == palette[0][2]) { palette[i][0] ^= 1; } } pixels = malloc(width * height * 4); uint8_t* dst = (uint8_t*) pixels; for (int y = 0; y < height; y++) { const uint8_t* src = src_pixels + (vflip ? height - 1 - y : y) * (width + padding); for (int x = 0; x < width; x++) { const uint8_t& pix = *src++; const uint8_t* color = palette[pix]; *dst++ = color[2]; *dst++ = color[1]; *dst++ = color[0]; *dst++ = (transparent && pix == 0) ? 0 : 255; } } }