#include #include #include #include #include #include "tgaimage.hpp" TGAColor::TGAColor() : bgra(), bytespp(1) { for (int i=0; i<4; i++) bgra[i] = 0; } TGAColor::TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A) : bgra(), bytespp(4) { bgra[0] = B; bgra[1] = G; bgra[2] = R; bgra[3] = A; } TGAColor::TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { for (int i=0; i<(int)bpp; i++) { bgra[i] = p[i]; } for (int i=bpp; i<4; i++) { bgra[i] = 0; } } unsigned char& TGAColor::operator[](const int i) { return bgra[i]; } TGAColor TGAColor::operator *(float intensity) const { TGAColor res = *this; intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; return res; } TGAImage::TGAImage() : data(NULL), width(0), height(0), bytespp(0) {} TGAImage::TGAImage(int w, int h, int bpp) : data(NULL), width(w), height(h), bytespp(bpp) { unsigned long nbytes = width*height*bytespp; data = new unsigned char[nbytes]; memset(data, 0, nbytes); } TGAImage::TGAImage(const TGAImage &img) : data(NULL), width(img.width), height(img.height), bytespp(img.bytespp) { unsigned long nbytes = width*height*bytespp; data = new unsigned char[nbytes]; memcpy(data, img.data, nbytes); } TGAImage::~TGAImage() { if (data) delete [] data; } TGAImage & TGAImage::operator =(const TGAImage &img) { if (this != &img) { if (data) delete [] data; width = img.width; height = img.height; bytespp = img.bytespp; unsigned long nbytes = width*height*bytespp; data = new unsigned char[nbytes]; memcpy(data, img.data, nbytes); } return *this; } bool TGAImage::read_tga_file(const char *filename) { if (data) delete [] data; data = NULL; std::ifstream in; in.open (filename, std::ios::binary); if (!in.is_open()) { std::cerr << "can't open file " << filename << "\n"; in.close(); return false; } TGA_Header header; in.read((char *)&header, sizeof(header)); if (!in.good()) { in.close(); std::cerr << "an error occured while reading the header\n"; return false; } width = header.width; height = header.height; bytespp = header.bitsperpixel>>3; if (width<=0 || height<=0 || (bytespp!=GRAYSCALE && bytespp!=RGB && bytespp!=RGBA)) { in.close(); std::cerr << "bad bpp (or width/height) value\n"; return false; } unsigned long nbytes = bytespp*width*height; data = new unsigned char[nbytes]; if (3==header.datatypecode || 2==header.datatypecode) { in.read((char *)data, nbytes); if (!in.good()) { in.close(); std::cerr << "an error occured while reading the data\n"; return false; } } else if (10==header.datatypecode||11==header.datatypecode) { if (!load_rle_data(in)) { in.close(); std::cerr << "an error occured while reading the data\n"; return false; } } else { in.close(); std::cerr << "unknown file format " << (int)header.datatypecode << "\n"; return false; } if (!(header.imagedescriptor & 0x20)) { flip_vertically(); } if (header.imagedescriptor & 0x10) { flip_horizontally(); } std::cerr << width << "x" << height << "/" << bytespp*8 << "\n"; in.close(); return true; } bool TGAImage::load_rle_data(std::ifstream &in) { unsigned long pixelcount = width*height; unsigned long currentpixel = 0; unsigned long currentbyte = 0; TGAColor colorbuffer; do { unsigned char chunkheader = 0; chunkheader = in.get(); if (!in.good()) { std::cerr << "an error occured while reading the data\n"; return false; } if (chunkheader<128) { chunkheader++; for (int i=0; ipixelcount) { std::cerr << "Too many pixels read\n"; return false; } } } else { chunkheader -= 127; in.read((char *)colorbuffer.bgra, bytespp); if (!in.good()) { std::cerr << "an error occured while reading the header\n"; return false; } for (int i=0; ipixelcount) { std::cerr << "Too many pixels read\n"; return false; } } } } while (currentpixel < pixelcount); return true; } bool TGAImage::write_tga_file(const char *filename, bool rle) const { unsigned char developer_area_ref[4] = {0, 0, 0, 0}; unsigned char extension_area_ref[4] = {0, 0, 0, 0}; unsigned char footer[18] = {'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E','.','\0'}; std::ofstream out; out.open (filename, std::ios::binary); if (!out.is_open()) { std::cerr << "can't open file " << filename << "\n"; out.close(); return false; } TGA_Header header; memset((void *)&header, 0, sizeof(header)); header.bitsperpixel = bytespp<<3; header.width = width; header.height = height; header.datatypecode = (bytespp==GRAYSCALE?(rle?11:3):(rle?10:2)); header.imagedescriptor = 0x20; // top-left origin out.write((char *)&header, sizeof(header)); if (!out.good()) { out.close(); std::cerr << "can't dump the tga file\n"; return false; } if (!rle) { out.write((char *)data, width*height*bytespp); if (!out.good()) { std::cerr << "can't unload raw data\n"; out.close(); return false; } } else { if (!unload_rle_data(out)) { out.close(); std::cerr << "can't unload rle data\n"; return false; } } out.write((char *)developer_area_ref, sizeof(developer_area_ref)); if (!out.good()) { std::cerr << "can't dump the tga file\n"; out.close(); return false; } out.write((char *)extension_area_ref, sizeof(extension_area_ref)); if (!out.good()) { std::cerr << "can't dump the tga file\n"; out.close(); return false; } out.write((char *)footer, sizeof(footer)); if (!out.good()) { std::cerr << "can't dump the tga file\n"; out.close(); return false; } out.close(); return true; } // TODO: it is not necessary to break a raw chunk for two equal pixels (for the matter of the resulting size) bool TGAImage::unload_rle_data(std::ofstream &out) const { const unsigned char max_chunk_length = 128; unsigned long npixels = width*height; unsigned long curpix = 0; while (curpix=width || y>=height) { return TGAColor(); } return TGAColor(data+(x+y*width)*bytespp, bytespp); } bool TGAImage::set(int x, int y, TGAColor &c) { if (!data || x<0 || y<0 || x>=width || y>=height) { return false; } memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp); return true; } bool TGAImage::set(int x, int y, const TGAColor &c) { if (!data || x<0 || y<0 || x>=width || y>=height) { return false; } memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp); return true; } int TGAImage::get_bytespp() const { return bytespp; } int TGAImage::get_width() const { return width; } int TGAImage::get_height() const { return height; } bool TGAImage::flip_horizontally() { if (!data) return false; int half = width>>1; for (int i=0; i>1; for (int j=0; j=(int)width) { errx -= width; nx += bytespp; memcpy(tdata+nscanline+nx, data+oscanline+ox, bytespp); } } erry += h; oscanline += olinebytes; while (erry>=(int)height) { if (erry>=(int)height<<1) // it means we jump over a scanline memcpy(tdata+nscanline+nlinebytes, tdata+nscanline, nlinebytes); erry -= height; nscanline += nlinebytes; } } delete [] data; data = tdata; width = w; height = h; return true; }