// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "core/fxcodec/lbmp/fx_bmp.h" #include #include #include "core/fxcrt/fx_system.h" #include "third_party/base/logging.h" #include "third_party/base/ptr_util.h" static_assert(sizeof(BmpFileHeader) == 14, "BmpFileHeader should have a size of 14"); namespace { const size_t kBmpCoreHeaderSize = 12; const size_t kBmpInfoHeaderSize = 40; uint8_t HalfRoundUp(uint8_t value) { uint16_t value16 = value; return static_cast((value16 + 1) / 2); } } // namespace BMPDecompressor::BMPDecompressor() : context_ptr(nullptr), next_in(nullptr), header_offset(0), width(0), height(0), compress_flag(0), components(0), src_row_bytes(0), out_row_bytes(0), bitCounts(0), color_used(0), imgTB_flag(false), pal_num(0), pal_type(0), data_size(0), img_data_offset(0), img_ifh_size(0), row_num(0), col_num(0), dpi_x(0), dpi_y(0), mask_red(0), mask_green(0), mask_blue(0), avail_in(0), skip_size(0), decode_status(BMP_D_STATUS_HEADER) {} BMPDecompressor::~BMPDecompressor() {} void BMPDecompressor::Error() { longjmp(jmpbuf, 1); } void BMPDecompressor::ReadScanline(uint32_t row_num, const std::vector& row_buf) { auto* p = reinterpret_cast(context_ptr); p->m_pDelegate->BmpReadScanline(row_num, row_buf); } bool BMPDecompressor::GetDataPosition(uint32_t rcd_pos) { auto* p = reinterpret_cast(context_ptr); return p->m_pDelegate->BmpInputImagePositionBuf(rcd_pos); } int32_t BMPDecompressor::ReadHeader() { uint32_t skip_size_org = skip_size; if (decode_status == BMP_D_STATUS_HEADER) { BmpFileHeader* pBmp_header = nullptr; if (!ReadData(reinterpret_cast(&pBmp_header), sizeof(BmpFileHeader))) { return 2; } pBmp_header->bfType = FXWORD_GET_LSBFIRST(reinterpret_cast(&pBmp_header->bfType)); pBmp_header->bfOffBits = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_header->bfOffBits)); data_size = FXDWORD_GET_LSBFIRST(reinterpret_cast(&pBmp_header->bfSize)); if (pBmp_header->bfType != BMP_SIGNATURE) { Error(); NOTREACHED(); } if (avail_in < sizeof(uint32_t)) { skip_size = skip_size_org; return 2; } img_ifh_size = FXDWORD_GET_LSBFIRST(static_cast(next_in + skip_size)); pal_type = 0; static_assert(sizeof(BmpCoreHeader) == kBmpCoreHeaderSize, "BmpCoreHeader has wrong size"); static_assert(sizeof(BmpInfoHeader) == kBmpInfoHeaderSize, "BmpInfoHeader has wrong size"); switch (img_ifh_size) { case kBmpCoreHeaderSize: { pal_type = 1; BmpCoreHeader* pBmp_core_header = nullptr; if (!ReadData(reinterpret_cast(&pBmp_core_header), img_ifh_size)) { skip_size = skip_size_org; return 2; } width = FXWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_core_header->bcWidth)); height = FXWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_core_header->bcHeight)); bitCounts = FXWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_core_header->bcBitCount)); compress_flag = BMP_RGB; imgTB_flag = false; } break; case kBmpInfoHeaderSize: { BmpInfoHeader* pBmp_info_header = nullptr; if (!ReadData(reinterpret_cast(&pBmp_info_header), img_ifh_size)) { skip_size = skip_size_org; return 2; } width = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biWidth)); int32_t signed_height = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biHeight)); bitCounts = FXWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biBitCount)); compress_flag = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biCompression)); color_used = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biClrUsed)); dpi_x = static_cast(FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biXPelsPerMeter))); dpi_y = static_cast(FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biYPelsPerMeter))); SetHeight(signed_height); } break; default: { if (img_ifh_size > std::min(kBmpInfoHeaderSize, sizeof(BmpInfoHeader))) { BmpInfoHeader* pBmp_info_header = nullptr; if (!ReadData(reinterpret_cast(&pBmp_info_header), img_ifh_size)) { skip_size = skip_size_org; return 2; } uint16_t biPlanes; width = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biWidth)); int32_t signed_height = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biHeight)); bitCounts = FXWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biBitCount)); compress_flag = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biCompression)); color_used = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biClrUsed)); biPlanes = FXWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biPlanes)); dpi_x = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biXPelsPerMeter)); dpi_y = FXDWORD_GET_LSBFIRST( reinterpret_cast(&pBmp_info_header->biYPelsPerMeter)); SetHeight(signed_height); if (compress_flag == BMP_RGB && biPlanes == 1 && color_used == 0) break; } Error(); NOTREACHED(); } } if (width > BMP_MAX_WIDTH || compress_flag > BMP_BITFIELDS) { Error(); NOTREACHED(); } switch (bitCounts) { case 1: case 4: case 8: case 16: case 24: { if (color_used > 1U << bitCounts) { Error(); NOTREACHED(); } } case 32: break; default: Error(); NOTREACHED(); } src_row_bytes = BMP_WIDTHBYTES(width, bitCounts); switch (bitCounts) { case 1: case 4: case 8: out_row_bytes = BMP_WIDTHBYTES(width, 8); components = 1; break; case 16: case 24: out_row_bytes = BMP_WIDTHBYTES(width, 24); components = 3; break; case 32: out_row_bytes = src_row_bytes; components = 4; break; } out_row_buffer.clear(); if (out_row_bytes <= 0) { Error(); NOTREACHED(); } out_row_buffer.resize(out_row_bytes); SaveDecodingStatus(BMP_D_STATUS_PAL); } if (decode_status == BMP_D_STATUS_PAL) { skip_size_org = skip_size; if (compress_flag == BMP_BITFIELDS) { if (bitCounts != 16 && bitCounts != 32) { Error(); NOTREACHED(); } uint32_t* mask; if (ReadData(reinterpret_cast(&mask), 3 * sizeof(uint32_t)) == nullptr) { skip_size = skip_size_org; return 2; } mask_red = FXDWORD_GET_LSBFIRST(reinterpret_cast(&mask[0])); mask_green = FXDWORD_GET_LSBFIRST(reinterpret_cast(&mask[1])); mask_blue = FXDWORD_GET_LSBFIRST(reinterpret_cast(&mask[2])); if (mask_red & mask_green || mask_red & mask_blue || mask_green & mask_blue) { Error(); NOTREACHED(); } header_offset = std::max(header_offset, 26 + img_ifh_size); SaveDecodingStatus(BMP_D_STATUS_DATA_PRE); return 1; } else if (bitCounts == 16) { mask_red = 0x7C00; mask_green = 0x03E0; mask_blue = 0x001F; } pal_num = 0; if (bitCounts < 16) { pal_num = 1 << bitCounts; if (color_used != 0) pal_num = color_used; uint8_t* src_pal_ptr = nullptr; uint32_t src_pal_size = pal_num * (pal_type ? 3 : 4); if (ReadData(&src_pal_ptr, src_pal_size) == nullptr) { skip_size = skip_size_org; return 2; } palette.resize(pal_num); int32_t src_pal_index = 0; if (pal_type == BMP_PAL_OLD) { while (src_pal_index < pal_num) { palette[src_pal_index++] = BMP_PAL_ENCODE( 0x00, src_pal_ptr[2], src_pal_ptr[1], src_pal_ptr[0]); src_pal_ptr += 3; } } else { while (src_pal_index < pal_num) { palette[src_pal_index++] = BMP_PAL_ENCODE( src_pal_ptr[3], src_pal_ptr[2], src_pal_ptr[1], src_pal_ptr[0]); src_pal_ptr += 4; } } } header_offset = std::max(header_offset, 14 + img_ifh_size + pal_num * (pal_type ? 3 : 4)); SaveDecodingStatus(BMP_D_STATUS_DATA_PRE); } return 1; } bool BMPDecompressor::ValidateFlag() const { switch (compress_flag) { case BMP_RGB: case BMP_BITFIELDS: case BMP_RLE8: case BMP_RLE4: return true; default: return false; } } int32_t BMPDecompressor::DecodeImage() { if (decode_status == BMP_D_STATUS_DATA_PRE) { avail_in = 0; if (!GetDataPosition(header_offset)) { decode_status = BMP_D_STATUS_TAIL; Error(); NOTREACHED(); } row_num = 0; SaveDecodingStatus(BMP_D_STATUS_DATA); } if (decode_status != BMP_D_STATUS_DATA || !ValidateFlag()) { Error(); NOTREACHED(); } switch (compress_flag) { case BMP_RGB: case BMP_BITFIELDS: return DecodeRGB(); case BMP_RLE8: return DecodeRLE8(); case BMP_RLE4: return DecodeRLE4(); default: return 0; } } bool BMPDecompressor::ValidateColorIndex(uint8_t val) { if (val >= pal_num) { Error(); NOTREACHED(); } return true; } int32_t BMPDecompressor::DecodeRGB() { uint8_t* des_buf = nullptr; while (row_num < height) { size_t idx = 0; if (!ReadData(&des_buf, src_row_bytes)) return 2; SaveDecodingStatus(BMP_D_STATUS_DATA); switch (bitCounts) { case 1: { for (uint32_t col = 0; col < width; ++col) out_row_buffer[idx++] = des_buf[col >> 3] & (0x80 >> (col % 8)) ? 0x01 : 0x00; } break; case 4: { for (uint32_t col = 0; col < width; ++col) { out_row_buffer[idx++] = (col & 0x01) ? (des_buf[col >> 1] & 0x0F) : ((des_buf[col >> 1] & 0xF0) >> 4); } } break; case 16: { uint16_t* buf = (uint16_t*)des_buf; uint8_t blue_bits = 0; uint8_t green_bits = 0; uint8_t red_bits = 0; for (int32_t i = 0; i < 16; i++) { if ((mask_blue >> i) & 0x01) blue_bits++; if ((mask_green >> i) & 0x01) green_bits++; if ((mask_red >> i) & 0x01) red_bits++; } green_bits += blue_bits; red_bits += green_bits; if (blue_bits > 8 || green_bits < 8 || red_bits < 8) return 2; blue_bits = 8 - blue_bits; green_bits -= 8; red_bits -= 8; for (uint32_t col = 0; col < width; ++col) { *buf = FXWORD_GET_LSBFIRST(reinterpret_cast(buf)); out_row_buffer[idx++] = static_cast((*buf & mask_blue) << blue_bits); out_row_buffer[idx++] = static_cast((*buf & mask_green) >> green_bits); out_row_buffer[idx++] = static_cast((*buf++ & mask_red) >> red_bits); } } break; case 8: case 24: case 32: std::copy(des_buf, des_buf + src_row_bytes, out_row_buffer.begin()); idx += src_row_bytes; break; } for (uint8_t byte : out_row_buffer) { if (!ValidateColorIndex(byte)) return 0; } ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++), out_row_buffer); } SaveDecodingStatus(BMP_D_STATUS_TAIL); return 1; } int32_t BMPDecompressor::DecodeRLE8() { uint8_t* first_byte_ptr = nullptr; uint8_t* second_byte_ptr = nullptr; col_num = 0; while (true) { uint32_t skip_size_org = skip_size; if (!ReadData(&first_byte_ptr, 1)) return 2; switch (*first_byte_ptr) { case RLE_MARKER: { if (!ReadData(&first_byte_ptr, 1)) { skip_size = skip_size_org; return 2; } switch (*first_byte_ptr) { case RLE_EOL: { if (row_num >= height) { SaveDecodingStatus(BMP_D_STATUS_TAIL); Error(); NOTREACHED(); } ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++), out_row_buffer); col_num = 0; std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0); SaveDecodingStatus(BMP_D_STATUS_DATA); continue; } case RLE_EOI: { if (row_num < height) { ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++), out_row_buffer); } SaveDecodingStatus(BMP_D_STATUS_TAIL); return 1; } case RLE_DELTA: { uint8_t* delta_ptr; if (!ReadData(&delta_ptr, 2)) { skip_size = skip_size_org; return 2; } col_num += delta_ptr[0]; size_t bmp_row_num_next = row_num + delta_ptr[1]; if (col_num >= out_row_bytes || bmp_row_num_next >= height) { Error(); NOTREACHED(); } while (row_num < bmp_row_num_next) { std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0); ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++), out_row_buffer); } } break; default: { int32_t avail_size = out_row_bytes - col_num; if (!avail_size || static_cast(*first_byte_ptr) > avail_size) { Error(); NOTREACHED(); } if (!ReadData(&second_byte_ptr, *first_byte_ptr & 1 ? *first_byte_ptr + 1 : *first_byte_ptr)) { skip_size = skip_size_org; return 2; } std::copy(second_byte_ptr, second_byte_ptr + *first_byte_ptr, out_row_buffer.begin() + col_num); for (size_t i = col_num; i < col_num + *first_byte_ptr; ++i) { if (!ValidateColorIndex(out_row_buffer[i])) return 0; } col_num += *first_byte_ptr; } } } break; default: { int32_t avail_size = out_row_bytes - col_num; if (!avail_size || static_cast(*first_byte_ptr) > avail_size) { Error(); NOTREACHED(); } if (!ReadData(&second_byte_ptr, 1)) { skip_size = skip_size_org; return 2; } std::fill(out_row_buffer.begin() + col_num, out_row_buffer.begin() + col_num + *first_byte_ptr, *second_byte_ptr); if (!ValidateColorIndex(out_row_buffer[col_num])) return 0; col_num += *first_byte_ptr; } } } Error(); NOTREACHED(); } int32_t BMPDecompressor::DecodeRLE4() { uint8_t* first_byte_ptr = nullptr; uint8_t* second_byte_ptr = nullptr; col_num = 0; while (true) { uint32_t skip_size_org = skip_size; if (!ReadData(&first_byte_ptr, 1)) return 2; switch (*first_byte_ptr) { case RLE_MARKER: { if (!ReadData(&first_byte_ptr, 1)) { skip_size = skip_size_org; return 2; } switch (*first_byte_ptr) { case RLE_EOL: { if (row_num >= height) { SaveDecodingStatus(BMP_D_STATUS_TAIL); Error(); NOTREACHED(); } ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++), out_row_buffer); col_num = 0; std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0); SaveDecodingStatus(BMP_D_STATUS_DATA); continue; } case RLE_EOI: { if (row_num < height) { ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++), out_row_buffer); } SaveDecodingStatus(BMP_D_STATUS_TAIL); return 1; } case RLE_DELTA: { uint8_t* delta_ptr; if (!ReadData(&delta_ptr, 2)) { skip_size = skip_size_org; return 2; } col_num += delta_ptr[0]; size_t bmp_row_num_next = row_num + delta_ptr[1]; if (col_num >= out_row_bytes || bmp_row_num_next >= height) { Error(); NOTREACHED(); } while (row_num < bmp_row_num_next) { std::fill(out_row_buffer.begin(), out_row_buffer.end(), 0); ReadScanline(imgTB_flag ? row_num++ : (height - 1 - row_num++), out_row_buffer); } } break; default: { int32_t avail_size = out_row_bytes - col_num; if (!avail_size) { Error(); NOTREACHED(); } uint8_t size = HalfRoundUp(*first_byte_ptr); if (static_cast(*first_byte_ptr) > avail_size) { if (size + (col_num >> 1) > src_row_bytes) { Error(); NOTREACHED(); } *first_byte_ptr = avail_size - 1; } if (!ReadData(&second_byte_ptr, size & 1 ? size + 1 : size)) { skip_size = skip_size_org; return 2; } for (uint8_t i = 0; i < *first_byte_ptr; i++) { uint8_t color = (i & 0x01) ? (*second_byte_ptr++ & 0x0F) : (*second_byte_ptr & 0xF0) >> 4; if (!ValidateColorIndex(color)) return 0; out_row_buffer[col_num++] = color; } } } } break; default: { int32_t avail_size = out_row_bytes - col_num; if (!avail_size) { Error(); NOTREACHED(); } if (static_cast(*first_byte_ptr) > avail_size) { uint8_t size = HalfRoundUp(*first_byte_ptr); if (size + (col_num >> 1) > src_row_bytes) { Error(); NOTREACHED(); } *first_byte_ptr = avail_size - 1; } if (!ReadData(&second_byte_ptr, 1)) { skip_size = skip_size_org; return 2; } for (uint8_t i = 0; i < *first_byte_ptr; i++) { uint8_t second_byte = *second_byte_ptr; second_byte = i & 0x01 ? (second_byte & 0x0F) : (second_byte & 0xF0) >> 4; if (!ValidateColorIndex(second_byte)) return 0; out_row_buffer[col_num++] = second_byte; } } } } Error(); NOTREACHED(); } uint8_t* BMPDecompressor::ReadData(uint8_t** des_buf, uint32_t data_size) { if (avail_in < skip_size + data_size) return nullptr; *des_buf = next_in + skip_size; skip_size += data_size; return *des_buf; } void BMPDecompressor::SaveDecodingStatus(int32_t status) { decode_status = status; next_in += skip_size; avail_in -= skip_size; skip_size = 0; } void BMPDecompressor::SetInputBuffer(uint8_t* src_buf, uint32_t src_size) { next_in = src_buf; avail_in = src_size; skip_size = 0; } uint32_t BMPDecompressor::GetAvailInput(uint8_t** avail_buf) { if (avail_buf) { *avail_buf = nullptr; if (avail_in > 0) *avail_buf = next_in; } return avail_in; } void BMPDecompressor::SetHeight(int32_t signed_height) { if (signed_height >= 0) { height = signed_height; return; } if (signed_height == std::numeric_limits::min()) { Error(); NOTREACHED(); } height = -signed_height; imgTB_flag = true; }