tgaimage.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #include <iostream>
  2. #include <fstream>
  3. #include <string.h>
  4. #include <time.h>
  5. #include <math.h>
  6. #include "tgaimage.h"
  7. TGAImage::TGAImage() : data(NULL), width(0), height(0), bytespp(0) {}
  8. TGAImage::TGAImage(int w, int h, int bpp) : data(NULL), width(w), height(h), bytespp(bpp) {
  9. unsigned long nbytes = width*height*bytespp;
  10. data = new unsigned char[nbytes];
  11. memset(data, 0, nbytes);
  12. }
  13. TGAImage::TGAImage(const TGAImage &img) : data(NULL), width(img.width), height(img.height), bytespp(img.bytespp) {
  14. unsigned long nbytes = width*height*bytespp;
  15. data = new unsigned char[nbytes];
  16. memcpy(data, img.data, nbytes);
  17. }
  18. TGAImage::~TGAImage() {
  19. if (data) delete [] data;
  20. }
  21. TGAImage & TGAImage::operator =(const TGAImage &img) {
  22. if (this != &img) {
  23. if (data) delete [] data;
  24. width = img.width;
  25. height = img.height;
  26. bytespp = img.bytespp;
  27. unsigned long nbytes = width*height*bytespp;
  28. data = new unsigned char[nbytes];
  29. memcpy(data, img.data, nbytes);
  30. }
  31. return *this;
  32. }
  33. bool TGAImage::read_tga_file(const char *filename) {
  34. if (data) delete [] data;
  35. data = NULL;
  36. std::ifstream in;
  37. in.open (filename, std::ios::binary);
  38. if (!in.is_open()) {
  39. std::cerr << "can't open file " << filename << "\n";
  40. in.close();
  41. return false;
  42. }
  43. TGA_Header header;
  44. in.read((char *)&header, sizeof(header));
  45. if (!in.good()) {
  46. in.close();
  47. std::cerr << "an error occured while reading the header\n";
  48. return false;
  49. }
  50. width = header.width;
  51. height = header.height;
  52. bytespp = header.bitsperpixel>>3;
  53. if (width<=0 || height<=0 || (bytespp!=GRAYSCALE && bytespp!=RGB && bytespp!=RGBA)) {
  54. in.close();
  55. std::cerr << "bad bpp (or width/height) value\n";
  56. return false;
  57. }
  58. unsigned long nbytes = bytespp*width*height;
  59. data = new unsigned char[nbytes];
  60. if (3==header.datatypecode || 2==header.datatypecode) {
  61. in.read((char *)data, nbytes);
  62. if (!in.good()) {
  63. in.close();
  64. std::cerr << "an error occured while reading the data\n";
  65. return false;
  66. }
  67. } else if (10==header.datatypecode||11==header.datatypecode) {
  68. if (!load_rle_data(in)) {
  69. in.close();
  70. std::cerr << "an error occured while reading the data\n";
  71. return false;
  72. }
  73. } else {
  74. in.close();
  75. std::cerr << "unknown file format " << (int)header.datatypecode << "\n";
  76. return false;
  77. }
  78. if (!(header.imagedescriptor & 0x20)) {
  79. flip_vertically();
  80. }
  81. if (header.imagedescriptor & 0x10) {
  82. flip_horizontally();
  83. }
  84. std::cerr << width << "x" << height << "/" << bytespp*8 << "\n";
  85. in.close();
  86. return true;
  87. }
  88. bool TGAImage::load_rle_data(std::ifstream &in) {
  89. unsigned long pixelcount = width*height;
  90. unsigned long currentpixel = 0;
  91. unsigned long currentbyte = 0;
  92. TGAColor colorbuffer;
  93. do {
  94. unsigned char chunkheader = 0;
  95. chunkheader = in.get();
  96. if (!in.good()) {
  97. std::cerr << "an error occured while reading the data\n";
  98. return false;
  99. }
  100. if (chunkheader<128) {
  101. chunkheader++;
  102. for (int i=0; i<chunkheader; i++) {
  103. in.read((char *)colorbuffer.bgra, bytespp);
  104. if (!in.good()) {
  105. std::cerr << "an error occured while reading the header\n";
  106. return false;
  107. }
  108. for (int t=0; t<bytespp; t++)
  109. data[currentbyte++] = colorbuffer.bgra[t];
  110. currentpixel++;
  111. if (currentpixel>pixelcount) {
  112. std::cerr << "Too many pixels read\n";
  113. return false;
  114. }
  115. }
  116. } else {
  117. chunkheader -= 127;
  118. in.read((char *)colorbuffer.bgra, bytespp);
  119. if (!in.good()) {
  120. std::cerr << "an error occured while reading the header\n";
  121. return false;
  122. }
  123. for (int i=0; i<chunkheader; i++) {
  124. for (int t=0; t<bytespp; t++)
  125. data[currentbyte++] = colorbuffer.bgra[t];
  126. currentpixel++;
  127. if (currentpixel>pixelcount) {
  128. std::cerr << "Too many pixels read\n";
  129. return false;
  130. }
  131. }
  132. }
  133. } while (currentpixel < pixelcount);
  134. return true;
  135. }
  136. bool TGAImage::write_tga_file(const char *filename, bool rle) const {
  137. unsigned char developer_area_ref[4] = {0, 0, 0, 0};
  138. unsigned char extension_area_ref[4] = {0, 0, 0, 0};
  139. unsigned char footer[18] = {'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E','.','\0'};
  140. std::ofstream out;
  141. out.open (filename, std::ios::binary);
  142. if (!out.is_open()) {
  143. std::cerr << "can't open file " << filename << "\n";
  144. out.close();
  145. return false;
  146. }
  147. TGA_Header header;
  148. memset((void *)&header, 0, sizeof(header));
  149. header.bitsperpixel = bytespp<<3;
  150. header.width = width;
  151. header.height = height;
  152. header.datatypecode = (bytespp==GRAYSCALE?(rle?11:3):(rle?10:2));
  153. header.imagedescriptor = 0x20; // top-left origin
  154. out.write((char *)&header, sizeof(header));
  155. if (!out.good()) {
  156. out.close();
  157. std::cerr << "can't dump the tga file\n";
  158. return false;
  159. }
  160. if (!rle) {
  161. out.write((char *)data, width*height*bytespp);
  162. if (!out.good()) {
  163. std::cerr << "can't unload raw data\n";
  164. out.close();
  165. return false;
  166. }
  167. } else {
  168. if (!unload_rle_data(out)) {
  169. out.close();
  170. std::cerr << "can't unload rle data\n";
  171. return false;
  172. }
  173. }
  174. out.write((char *)developer_area_ref, sizeof(developer_area_ref));
  175. if (!out.good()) {
  176. std::cerr << "can't dump the tga file\n";
  177. out.close();
  178. return false;
  179. }
  180. out.write((char *)extension_area_ref, sizeof(extension_area_ref));
  181. if (!out.good()) {
  182. std::cerr << "can't dump the tga file\n";
  183. out.close();
  184. return false;
  185. }
  186. out.write((char *)footer, sizeof(footer));
  187. if (!out.good()) {
  188. std::cerr << "can't dump the tga file\n";
  189. out.close();
  190. return false;
  191. }
  192. out.close();
  193. return true;
  194. }
  195. // TODO: it is not necessary to break a raw chunk for two equal pixels (for the matter of the resulting size)
  196. bool TGAImage::unload_rle_data(std::ofstream &out) const {
  197. const unsigned char max_chunk_length = 128;
  198. unsigned long npixels = width*height;
  199. unsigned long curpix = 0;
  200. while (curpix<npixels) {
  201. unsigned long chunkstart = curpix*bytespp;
  202. unsigned long curbyte = curpix*bytespp;
  203. unsigned char run_length = 1;
  204. bool raw = true;
  205. while (curpix+run_length<npixels && run_length<max_chunk_length) {
  206. bool succ_eq = true;
  207. for (int t=0; succ_eq && t<bytespp; t++) {
  208. succ_eq = (data[curbyte+t]==data[curbyte+t+bytespp]);
  209. }
  210. curbyte += bytespp;
  211. if (1==run_length) {
  212. raw = !succ_eq;
  213. }
  214. if (raw && succ_eq) {
  215. run_length--;
  216. break;
  217. }
  218. if (!raw && !succ_eq) {
  219. break;
  220. }
  221. run_length++;
  222. }
  223. curpix += run_length;
  224. out.put(raw?run_length-1:run_length+127);
  225. if (!out.good()) {
  226. std::cerr << "can't dump the tga file\n";
  227. return false;
  228. }
  229. out.write((char *)(data+chunkstart), (raw?run_length*bytespp:bytespp));
  230. if (!out.good()) {
  231. std::cerr << "can't dump the tga file\n";
  232. return false;
  233. }
  234. }
  235. return true;
  236. }
  237. TGAColor TGAImage::get(int x, int y) const {
  238. if (!data || x<0 || y<0 || x>=width || y>=height) {
  239. return TGAColor(128.f,128.f,128.f,255.f);
  240. }
  241. return TGAColor(data+(x+y*width)*bytespp, bytespp);
  242. }
  243. bool TGAImage::set(int x, int y, TGAColor &c) {
  244. if (!data || x<0 || y<0 || x>=width || y>=height) {
  245. return false;
  246. }
  247. memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp);
  248. return true;
  249. }
  250. bool TGAImage::set(int x, int y, const TGAColor &c) {
  251. if (!data || x<0 || y<0 || x>=width || y>=height) {
  252. return false;
  253. }
  254. memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp);
  255. return true;
  256. }
  257. int TGAImage::get_bytespp() {
  258. return bytespp;
  259. }
  260. int TGAImage::get_width() {
  261. return width;
  262. }
  263. int TGAImage::get_height() {
  264. return height;
  265. }
  266. bool TGAImage::flip_horizontally() {
  267. if (!data) return false;
  268. int half = width>>1;
  269. for (int i=0; i<half; i++) {
  270. for (int j=0; j<height; j++) {
  271. TGAColor c1 = get(i, j);
  272. TGAColor c2 = get(width-1-i, j);
  273. set(i, j, c2);
  274. set(width-1-i, j, c1);
  275. }
  276. }
  277. return true;
  278. }
  279. bool TGAImage::flip_vertically() {
  280. if (!data) return false;
  281. unsigned long bytes_per_line = width*bytespp;
  282. unsigned char *line = new unsigned char[bytes_per_line];
  283. int half = height>>1;
  284. for (int j=0; j<half; j++) {
  285. unsigned long l1 = j*bytes_per_line;
  286. unsigned long l2 = (height-1-j)*bytes_per_line;
  287. memmove((void *)line, (void *)(data+l1), bytes_per_line);
  288. memmove((void *)(data+l1), (void *)(data+l2), bytes_per_line);
  289. memmove((void *)(data+l2), (void *)line, bytes_per_line);
  290. }
  291. delete [] line;
  292. return true;
  293. }
  294. unsigned char *TGAImage::buffer() {
  295. return data;
  296. }
  297. void TGAImage::clear() {
  298. memset((void *)data, 0, width*height*bytespp);
  299. }
  300. bool TGAImage::scale(int w, int h) {
  301. if (w<=0 || h<=0 || !data) return false;
  302. unsigned char *tdata = new unsigned char[w*h*bytespp];
  303. int nscanline = 0;
  304. int oscanline = 0;
  305. int erry = 0;
  306. unsigned long nlinebytes = w*bytespp;
  307. unsigned long olinebytes = width*bytespp;
  308. for (int j=0; j<height; j++) {
  309. int errx = width-w;
  310. int nx = -bytespp;
  311. int ox = -bytespp;
  312. for (int i=0; i<width; i++) {
  313. ox += bytespp;
  314. errx += w;
  315. while (errx>=(int)width) {
  316. errx -= width;
  317. nx += bytespp;
  318. memcpy(tdata+nscanline+nx, data+oscanline+ox, bytespp);
  319. }
  320. }
  321. erry += h;
  322. oscanline += olinebytes;
  323. while (erry>=(int)height) {
  324. if (erry>=(int)height<<1) // it means we jump over a scanline
  325. memcpy(tdata+nscanline+nlinebytes, tdata+nscanline, nlinebytes);
  326. erry -= height;
  327. nscanline += nlinebytes;
  328. }
  329. }
  330. delete [] data;
  331. data = tdata;
  332. width = w;
  333. height = h;
  334. return true;
  335. }