149 lines
3.8 KiB
C++
149 lines
3.8 KiB
C++
#include <algorithm>
|
|
#include <cstring>
|
|
#include "Debug.h"
|
|
#include "Open.h"
|
|
#include "SimpleImage.h"
|
|
#include "Utility.h"
|
|
|
|
|
|
namespace corona {
|
|
|
|
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());
|
|
}
|
|
|
|
}
|