Image* OpenTGA(File* file) { COR_GUARD("OpenTGA"); // read header byte header[18]; if (file->read(header, 18) != 18) { return 0; } // decode header int id_length = header[0]; int cm_type = header[1]; int image_type = header[2]; //int cm_first = read16_le(header + 3); int cm_length = read16_le(header + 5); int cm_entry_size = header[7]; // in bits //int x_origin = read16_le(header + 8); //int y_origin = read16_le(header + 10); int width = read16_le(header + 12); int height = read16_le(header + 14); int pixel_depth = header[16]; int image_descriptor = header[17]; bool mirrored = (image_descriptor & (1 << 4)) != 0; // left-to-right? bool flipped = (image_descriptor & (1 << 5)) == 0; // bottom-to-top? /* * image types * 0 = no image data * 1 = uncompressed, color-mapped * 2 = uncompressed, true-color * 3 = uncompressed, black and white * 9 = RLE, color-mapped * 10 = RLE, true-color * 11 = RLE, black and white */ // make sure we support the image if (image_type != 2 || (pixel_depth != 24 && pixel_depth != 32)) { return 0; } // skip image id byte unused[255]; if (file->read(unused, id_length) != id_length) { return 0; } // skip color map if (cm_type != 0) { // allocate color map int cm_entry_bytes = (cm_entry_size + 7) / 8; int cm_size = cm_entry_bytes * cm_length; auto_array<byte> color_map(new byte[cm_size]); if (file->read(color_map, cm_size) != cm_size) { return 0; } } // read image data PixelFormat format; auto_array<byte> pixels; if (pixel_depth == 24) { COR_LOG("24-bit image"); format = PF_B8G8R8; int image_size = width * height * 3; pixels = new byte[image_size]; if (file->read(pixels, image_size) != image_size) { return 0; } } else if (pixel_depth == 32) { COR_LOG("32-bit image"); format = PF_B8G8R8A8; int image_size = width * height * 4; pixels = new byte[image_size]; if (file->read(pixels, image_size) != image_size) { return 0; } } else { return 0; } // reverse each row if (mirrored) { COR_LOG("Image is mirrored"); const int bpp = pixel_depth / 8; // bytes per pixel for (int y = 0; y < height; ++y) { // points to the first pixel of the row byte* start = pixels.get() + y * width * bpp; // points to the last pixel of the row byte* end = start + (width - 1) * bpp; while (start < end) { for (int b = 0; b < bpp; ++b) { std::swap(start[b], end[b]); } start += bpp; end -= bpp; } } } // reverse rows as a whole if (flipped) { COR_LOG("Image is flipped"); const int bpp = pixel_depth / 8; // bytes per pixel const int row_size = width * bpp; auto_array<byte> temp(new byte[row_size]); // for the swap // points to the beginning of the first row byte* start = pixels.get(); // points to the beginning of the last row byte* end = start + (height - 1) * width * bpp; while (start < end) { memcpy(temp.get(), start, row_size); memcpy(start, end, row_size); memcpy(end, temp.get(), row_size); start += row_size; end -= row_size; } } return new SimpleImage(width, height, format, pixels.release()); }
std::auto_ptr<Image> BMPFormat::read(byte_source* src, ImageFactory* factory, const options_map&) { char magick[2]; if (src->read(reinterpret_cast<byte*>(magick), 2) != 2) { throw CannotReadError("imread.bmp: File is empty"); } if (magick[0] != 'B' || magick[1] != 'M') { throw CannotReadError("imread.bmp: Magick number not matched (this might not be a BMP file)"); } const uint32_t size = read32_le(*src); (void)size; (void)read16_le(*src); (void)read16_le(*src); const uint32_t offset = read32_le(*src); const uint32_t header_size = read32_le(*src); (void)header_size; const uint32_t width = read32_le(*src); const uint32_t height = read32_le(*src); const uint16_t planes = read16_le(*src); if (planes != 1){ throw NotImplementedError("imread.bmp: planes should be 1"); } const uint16_t bitsppixel = read16_le(*src); const uint32_t compression = read32_le(*src); if (compression != 0) { throw NotImplementedError("imread.bmp: Only uncompressed bitmaps are supported"); } const uint32_t imsize = read32_le(*src); (void)imsize; const uint32_t hres = read32_le(*src); (void)hres; const uint32_t vres = read32_le(*src); (void)vres; const uint32_t n_colours = read32_le(*src); const uint32_t importantcolours = read32_le(*src); (void)importantcolours; if (bitsppixel != 8 && bitsppixel != 16 && bitsppixel != 24) { std::ostringstream out; out << "imread.bmp: Bits per pixel is " << bitsppixel << ". Only 8, 16, or 24 supported."; throw CannotReadError(out.str()); } const int depth = (bitsppixel == 16 ? -1 : 3); const int nbits = (bitsppixel == 16 ? 16 : 8); std::auto_ptr<Image> output(factory->create(nbits, height, width, depth)); std::vector<byte> color_table; if (bitsppixel <= 8) { const uint32_t table_size = (n_colours == 0 ? pow2(bitsppixel) : n_colours); color_table.resize(table_size*4); src->read_check(&color_table[0], table_size*4); } src->seek_absolute(offset); const int bytes_per_row = width * (bitsppixel/8); const int padding = (4 - (bytes_per_row % 4)) % 4; byte buf[4]; for (unsigned int r = 0; r != height; ++r) { byte* rowp = output->rowp_as<byte>(height-r-1); src->read_check(rowp, bytes_per_row); if (bitsppixel == 24) flippixels(rowp, width); else if (!color_table.empty()) color_expand(color_table, rowp, width); if (src->read(buf, padding) != unsigned(padding) && r != (height - 1)) { throw CannotReadError("File ended prematurely"); } } return output; }
bool ReadInfoHeader(File* file, Header& h) { const int HEADER_READ_SIZE = 24; // read the only part of the header we need byte header[HEADER_READ_SIZE]; if (file->read(header, HEADER_READ_SIZE) != HEADER_READ_SIZE) { return false; } int size = read32_le(header + 0); int width; int height; int planes; int bpp; int compression; int image_size; if (size < 40) { // assume OS/2 bitmap if (size < 12) { return false; } h.os2 = true; width = read16_le(header + 4); height = read16_le(header + 6); planes = read16_le(header + 8); bpp = read16_le(header + 10); compression = 0; image_size = 0; } else { h.os2 = false; width = read32_le(header + 4); height = read32_le(header + 8); planes = read16_le(header + 12); bpp = read16_le(header + 14); compression = read32_le(header + 16); image_size = read32_le(header + 20); } // sanity check the info header if (planes != 1) { return false; } // adjust image_size // (if compression == 0 or 3, manually calculate image size) int line_size = 0; if (compression == 0 || compression == 3) { line_size = (width * bpp + 7) / 8; line_size = (line_size + 3) / 4 * 4; // 32-bit-aligned image_size = line_size * height; } h.width = width; h.height = height; h.bpp = bpp; h.compression = compression; h.pitch = line_size; h.image_size = image_size; // jump forward (backward in the OS/2 case :) to the palette data file->seek(size - HEADER_READ_SIZE, File::CURRENT); return true; }