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 8.3KB

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