Open Source Tomb Raider Engine
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

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. }