Open Source Tomb Raider Engine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pcx.cpp 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*!
  2. * \file src/utils/pcx.cpp
  3. * \brief PCX image reader
  4. *
  5. * Based on official technical documentation from ZSoft:
  6. * http://bespin.org/~qz/pc-gpe/pcx.txt
  7. *
  8. * \author xythobuz
  9. */
  10. #include <fstream>
  11. #include "global.h"
  12. #include "utils/pcx.h"
  13. #ifdef DEBUG
  14. #include "Console.h"
  15. #define pcxPrint getConsole().print
  16. #else
  17. void pcxPrint(...) { }
  18. #endif
  19. int pcxCheck(const char *filename) {
  20. assert(filename != NULL);
  21. assert(filename[0] != '\0');
  22. std::ifstream file(filename, std::ios::in | std::ios::binary);
  23. // Read raw PCX header, 128 bytes
  24. unsigned char *header = new unsigned char[128];
  25. // Basic validation
  26. if (!file.read((char *)(&header[0]), 128)) {
  27. pcxPrint("File not big enough for valid PCX header!");
  28. delete [] header;
  29. return -1;
  30. }
  31. if (header[0] != 0x0A) {
  32. pcxPrint("Magic number at file start is wrong (0x%X != 0x0A)", header[0]);
  33. delete [] header;
  34. return -2;
  35. }
  36. if ((header[1] != 0) && ((header[1] < 2) || (header[1] > 5))) {
  37. // Valid: 0, 2, 3, 4, 5
  38. pcxPrint("Unknown PCX file format version (%d)", header[1]);
  39. delete [] header;
  40. return -3;
  41. }
  42. if ((header[2] != 0) && (header[2] != 1)) {
  43. pcxPrint("Unknown PCX file encoding (%d)", header[2]);
  44. delete [] header;
  45. return -4;
  46. }
  47. if (header[3] != 8) {
  48. pcxPrint("Only supporting 8bit (%dbit)", header[3]);
  49. delete [] header;
  50. return -5;
  51. }
  52. if (header[64] != 0) {
  53. pcxPrint("Reserved field is used (%d != 0)", header[64]);
  54. delete [] header;
  55. return -6;
  56. }
  57. delete [] header;
  58. return 0;
  59. }
  60. int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height, TextureManager::ColorMode *mode, unsigned int *bpp) {
  61. assert(filename != NULL);
  62. assert(filename[0] != '\0');
  63. assert(image != NULL);
  64. assert(width != NULL);
  65. assert(height != NULL);
  66. assert(mode != NULL);
  67. assert(bpp != NULL);
  68. std::ifstream file(filename, std::ios::in | std::ios::binary);
  69. // Read raw PCX header, 128 bytes
  70. unsigned char *header = new unsigned char[128];
  71. // Basic validation
  72. if (!file.read((char *)(&header[0]), 128)) {
  73. pcxPrint("File not big enough for valid PCX header!");
  74. delete [] header;
  75. return -1;
  76. }
  77. if (header[0] != 0x0A) {
  78. pcxPrint("Magic number at file start is wrong (0x%X != 0x0A)", header[0]);
  79. delete [] header;
  80. return -2;
  81. }
  82. if ((header[1] != 0) && ((header[1] < 2) || (header[1] > 5))) {
  83. // Valid: 0, 2, 3, 4, 5
  84. pcxPrint("Unknown PCX file format version (%d)", header[1]);
  85. delete [] header;
  86. return -3;
  87. }
  88. if ((header[2] != 0) && (header[2] != 1)) {
  89. pcxPrint("Unknown PCX file encoding (%d)", header[2]);
  90. delete [] header;
  91. return -4;
  92. }
  93. if (header[3] != 8) {
  94. pcxPrint("Only supporting 8bit (%dbit)", header[3]);
  95. delete [] header;
  96. return -5;
  97. }
  98. if (header[64] != 0) {
  99. pcxPrint("Reserved field is used (%d != 0)", header[64]);
  100. delete [] header;
  101. return -6;
  102. }
  103. // Read header informations
  104. bool versionFive = (header[1] == 5);
  105. bool compressed = (header[2] == 1);
  106. //unsigned char bitsPerPixel = header[3];
  107. unsigned int xMin = header[4] | (header[5] << 8);
  108. unsigned int yMin = header[6] | (header[7] << 8);
  109. unsigned int xMax = header[8] | (header[9] << 8);
  110. unsigned int yMax = header[10] | (header[11] << 8);
  111. //unsigned int hDPI = header[12] | (header[13] << 8);
  112. //unsigned int vDPI = header[14] | (header[15] << 8);
  113. //unsigned char *colormap = header + 16;
  114. unsigned char nPlanes = header[65];
  115. unsigned int bytesPerLine = header[66] | (header[67] << 8);
  116. //unsigned int paletteInfo = header[68] | (header[69] << 8);
  117. //unsigned int hScreenSize = header[70] | (header[71] << 8); // Only in some versionFive files
  118. //unsigned int vScreenSize = header[72] | (header[73] << 8); // Only in some versionFive files
  119. delete [] header;
  120. // Calculations
  121. *width = xMax - xMin + 1;
  122. *height = yMax - yMin + 1;
  123. unsigned long totalBytes = nPlanes * bytesPerLine; // total bytes per scan line
  124. unsigned long imageSize = totalBytes * *height;
  125. unsigned char *buffer = new unsigned char[imageSize];
  126. unsigned long b = 0;
  127. // Read encoded pixel data
  128. for (unsigned long i = 0; i < imageSize;) {
  129. unsigned int n = 1; // Run-length-encoding assumes 1
  130. int c = file.get();
  131. if (!file) {
  132. pcxPrint("Could not read data (%lu%s)", i, (file.eof() ? " EOF" : ""));
  133. delete [] buffer;
  134. return -7;
  135. }
  136. // Run-Length-Encoding
  137. if (compressed) {
  138. if ((c & 0xC0) == 0xC0) {
  139. n = c & 0x3F;
  140. c = file.get();
  141. if (!file) {
  142. pcxPrint("Could not read data rle (%lu%s)", i, (file.eof() ? " EOF" : ""));
  143. delete [] buffer;
  144. return -8;
  145. }
  146. }
  147. }
  148. for (unsigned int j = 0; j < n; j++)
  149. buffer[b++] = (unsigned char)c;
  150. i += n;
  151. }
  152. // Read color palette
  153. unsigned char *palette = NULL;
  154. if (versionFive) {
  155. int c = file.get();
  156. if ((c == 12) && file) {
  157. palette = new unsigned char[768];
  158. for (unsigned int i = 0; i < 768; i++) {
  159. palette[i] = (unsigned char)file.get();
  160. if (!file) {
  161. pcxPrint("Could not read 256 color palette (%d)", i);
  162. delete [] buffer;
  163. delete [] palette;
  164. return -9;
  165. }
  166. }
  167. }
  168. }
  169. // Bring buffer into preferred format
  170. unsigned long size = *width * *height * 4;
  171. *image = new unsigned char[size];
  172. for (unsigned int y = 0; y < *height; y++) {
  173. for (unsigned int x = 0; x < *width; x++) {
  174. unsigned long baseIndex = (x + (y * *width)) * 4;
  175. unsigned char alpha = 255, red = 0, green = 0, blue = 0;
  176. if (palette != NULL) {
  177. if (nPlanes == 1) {
  178. red = palette[buffer[(y * totalBytes) + x] * 3];
  179. green = palette[(buffer[(y * totalBytes) + x] * 3) + 1];
  180. blue = palette[(buffer[(y * totalBytes) + x] * 3) + 2];
  181. } else {
  182. pcxPrint("Unsupported number of planes (%d)", nPlanes);
  183. delete [] buffer;
  184. delete [] palette;
  185. delete [] *image;
  186. *image = NULL;
  187. return -10;
  188. }
  189. } else {
  190. if ((nPlanes == 3) || (nPlanes == 4)) {
  191. red = buffer[(y * totalBytes) + x];
  192. green = buffer[(y * totalBytes) + *width + x];
  193. blue = buffer[(y * totalBytes) + (2 * *width) + x];
  194. if (nPlanes == 4)
  195. alpha = buffer[(y * totalBytes) + (3 * *width) + x];
  196. } else if (nPlanes == 1) {
  197. red = green = blue = buffer[(y * totalBytes) + x];
  198. } else {
  199. pcxPrint("Unsupported number of planes (%d)", nPlanes);
  200. delete [] buffer;
  201. delete [] palette;
  202. delete [] *image;
  203. *image = NULL;
  204. return -11;
  205. }
  206. }
  207. (*image)[baseIndex + 0] = red;
  208. (*image)[baseIndex + 1] = green;
  209. (*image)[baseIndex + 2] = blue;
  210. (*image)[baseIndex + 3] = alpha;
  211. }
  212. }
  213. *mode = TextureManager::RGBA;
  214. *bpp = 32;
  215. delete [] buffer;
  216. delete [] palette;
  217. return 0;
  218. }