Changeset View
Changeset View
Standalone View
Standalone View
source/blender/imbuf/intern/bmp.c
| Show First 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | |||||
| #define CHECK_HEADER_FIELD(_mem, _field) ((_mem[0] == _field[0]) && (_mem[1] == _field[1])) | #define CHECK_HEADER_FIELD(_mem, _field) ((_mem[0] == _field[0]) && (_mem[1] == _field[1])) | ||||
| #define CHECK_HEADER_FIELD_BMP(_mem) \ | #define CHECK_HEADER_FIELD_BMP(_mem) \ | ||||
| (CHECK_HEADER_FIELD(_mem, "BM") || CHECK_HEADER_FIELD(_mem, "BA") || \ | (CHECK_HEADER_FIELD(_mem, "BM") || CHECK_HEADER_FIELD(_mem, "BA") || \ | ||||
| CHECK_HEADER_FIELD(_mem, "CI") || CHECK_HEADER_FIELD(_mem, "CP") || \ | CHECK_HEADER_FIELD(_mem, "CI") || CHECK_HEADER_FIELD(_mem, "CP") || \ | ||||
| CHECK_HEADER_FIELD(_mem, "IC") || CHECK_HEADER_FIELD(_mem, "PT")) | CHECK_HEADER_FIELD(_mem, "IC") || CHECK_HEADER_FIELD(_mem, "PT")) | ||||
| static bool checkbmp(const uchar *mem, const size_t size) | static bool checkbmp(const uchar *mem, const size_t size) | ||||
| { | { | ||||
| if (size < BMP_FILEHEADER_SIZE) { | if (size < (BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER))) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (!CHECK_HEADER_FIELD_BMP(mem)) { | if (!CHECK_HEADER_FIELD_BMP(mem)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| bool ok = false; | bool ok = false; | ||||
| Show All 22 Lines | |||||
| bool imb_is_a_bmp(const uchar *buf, size_t size) | bool imb_is_a_bmp(const uchar *buf, size_t size) | ||||
| { | { | ||||
| return checkbmp(buf, size); | return checkbmp(buf, size); | ||||
| } | } | ||||
| static size_t imb_bmp_calc_row_size_in_bytes(size_t x, size_t depth) | static size_t imb_bmp_calc_row_size_in_bytes(size_t x, size_t depth) | ||||
| { | { | ||||
| if (depth <= 8) { | /* https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#calculating-surface-stride | ||||
| return (depth * x + 31) / 32 * 4; | */ | ||||
| } | return (((x * depth) + 31) & ~31) >> 3; | ||||
| return (depth >> 3) * x; | |||||
| } | } | ||||
| ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) | ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) | ||||
| { | { | ||||
| ImBuf *ibuf = NULL; | ImBuf *ibuf = NULL; | ||||
| BMPINFOHEADER bmi; | BMPINFOHEADER bmi; | ||||
| int x, y, depth, ibuf_depth, skip; | int ibuf_depth; | ||||
| const uchar *bmp; | const uchar *bmp; | ||||
| uchar *rect; | uchar *rect; | ||||
| ushort col; | ushort col; | ||||
| double xppm, yppm; | |||||
| bool top_to_bottom = false; | bool top_to_bottom = false; | ||||
| (void)size; /* unused */ | |||||
| if (checkbmp(mem, size) == 0) { | if (checkbmp(mem, size) == 0) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); | colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); | ||||
| const size_t pixel_data_offset = LITTLE_LONG(*(int *)(mem + 10)); | /* For systems where an int needs to be 4 bytes aligned. */ | ||||
| bmp = mem + pixel_data_offset; | memcpy(&bmi, mem + BMP_FILEHEADER_SIZE, sizeof(bmi)); | ||||
| if (CHECK_HEADER_FIELD_BMP(mem)) { | const size_t palette_offset = (size_t)BMP_FILEHEADER_SIZE + LITTLE_LONG(bmi.biSize); | ||||
| /* skip fileheader */ | const int depth = LITTLE_SHORT(bmi.biBitCount); | ||||
| mem += BMP_FILEHEADER_SIZE; | const int xppm = LITTLE_LONG(bmi.biXPelsPerMeter); | ||||
| const int yppm = LITTLE_LONG(bmi.biYPelsPerMeter); | |||||
| const int x = LITTLE_LONG(bmi.biWidth); | |||||
| int y = LITTLE_LONG(bmi.biHeight); | |||||
| /* Negative height means bitmap is stored top-to-bottom. */ | |||||
| if (y < 0) { | |||||
| y = -y; | |||||
| top_to_bottom = true; | |||||
| } | } | ||||
| else { | |||||
| /* Validate and cross-check offsets and sizes. */ | |||||
| if (x < 1 || | |||||
| !(depth == 1 || depth == 4 || depth == 8 || depth == 16 || depth == 24 || depth == 32)) { | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* for systems where an int needs to be 4 bytes aligned */ | const size_t pixel_data_offset = (size_t)LITTLE_LONG(*(int *)(mem + 10)); | ||||
| memcpy(&bmi, mem, sizeof(bmi)); | const size_t header_bytes = BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER); | ||||
| const size_t num_actual_data_bytes = size - pixel_data_offset; | |||||
| skip = LITTLE_LONG(bmi.biSize); | |||||
| x = LITTLE_LONG(bmi.biWidth); | |||||
| y = LITTLE_LONG(bmi.biHeight); | |||||
| depth = LITTLE_SHORT(bmi.biBitCount); | |||||
| xppm = LITTLE_LONG(bmi.biXPelsPerMeter); | |||||
| yppm = LITTLE_LONG(bmi.biYPelsPerMeter); | |||||
| const size_t row_size_in_bytes = imb_bmp_calc_row_size_in_bytes(x, depth); | const size_t row_size_in_bytes = imb_bmp_calc_row_size_in_bytes(x, depth); | ||||
| const size_t num_expected_data_bytes = row_size_in_bytes * y; | const size_t num_expected_data_bytes = row_size_in_bytes * y; | ||||
| const size_t num_actual_data_bytes = size - pixel_data_offset; | if (num_actual_data_bytes < num_expected_data_bytes || num_actual_data_bytes > size || | ||||
| if (num_actual_data_bytes < num_expected_data_bytes) { | pixel_data_offset < header_bytes || pixel_data_offset > (size - num_expected_data_bytes) || | ||||
| palette_offset < header_bytes || palette_offset > pixel_data_offset) { | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| if (depth <= 8) { | if (depth <= 8) { | ||||
| ibuf_depth = 24; | ibuf_depth = 24; | ||||
| } | } | ||||
| else { | else { | ||||
| ibuf_depth = depth; | ibuf_depth = depth; | ||||
| } | } | ||||
| if (y < 0) { | bmp = mem + pixel_data_offset; | ||||
| /* Negative height means bitmap is stored top-to-bottom... */ | |||||
| y = -y; | |||||
| top_to_bottom = true; | |||||
| } | |||||
| #if 0 | #if 0 | ||||
| printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, depth, bmi.biBitCount); | printf("palette_offset: %d, x: %d y: %d, depth: %d\n", palette_offset, x, y, depth); | ||||
| #endif | #endif | ||||
| if (flags & IB_test) { | if (flags & IB_test) { | ||||
| ibuf = IMB_allocImBuf(x, y, ibuf_depth, 0); | ibuf = IMB_allocImBuf(x, y, ibuf_depth, 0); | ||||
| } | } | ||||
| else { | else { | ||||
| ibuf = IMB_allocImBuf(x, y, ibuf_depth, IB_rect); | ibuf = IMB_allocImBuf(x, y, ibuf_depth, IB_rect); | ||||
| if (!ibuf) { | if (!ibuf) { | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| rect = (uchar *)ibuf->rect; | rect = (uchar *)ibuf->rect; | ||||
| if (depth <= 8) { | if (depth <= 8) { | ||||
| const char(*palette)[4] = (void *)(mem + skip); | const char(*palette)[4] = (const char(*)[4])(mem + palette_offset); | ||||
| const int startmask = ((1 << depth) - 1) << 8; | const int startmask = ((1 << depth) - 1) << 8; | ||||
| for (size_t i = y; i > 0; i--) { | for (size_t i = y; i > 0; i--) { | ||||
| int index; | int index; | ||||
| int bitoffs = 8; | int bitoffs = 8; | ||||
| int bitmask = startmask; | int bitmask = startmask; | ||||
| int nbytes = 0; | int nbytes = 0; | ||||
| const char *pcol; | const char *pcol; | ||||
| if (top_to_bottom) { | if (top_to_bottom) { | ||||
| ▲ Show 20 Lines • Show All 200 Lines • Show Last 20 Lines | |||||