Guest User

Untitled

a guest
Feb 19th, 2018
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.96 KB | None | 0 0
  1. /*
  2. * tilesplit
  3. * Simple tool to split a PNG into a tileset + tilemap.
  4. */
  5.  
  6. #include <assert.h>
  7. #include <stdbool.h>
  8. #include <stdint.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <png.h>
  13.  
  14.  
  15. #ifdef _MSC_VER
  16.  
  17. #define FATAL_ERROR(format, ...) \
  18. do { \
  19. fprintf(stderr, format, __VA_ARGS__); \
  20. exit(1); \
  21. } while (0)
  22.  
  23. #else
  24.  
  25. #define FATAL_ERROR(format, ...) \
  26. do { \
  27. fprintf(stderr, format, ##__VA_ARGS__); \
  28. exit(1); \
  29. } while (0)
  30.  
  31. #endif // _MSC_VER
  32.  
  33.  
  34. struct Color
  35. {
  36. unsigned char r;
  37. unsigned char g;
  38. unsigned char b;
  39. };
  40.  
  41. struct Image
  42. {
  43. unsigned int width;
  44. unsigned int height;
  45. unsigned int colorType;
  46. unsigned int bitDepth;
  47. unsigned int rowBytes;
  48. unsigned char *pixels;
  49. unsigned int numColors;
  50. struct Color palette[256];
  51. };
  52.  
  53. struct Tile
  54. {
  55. unsigned int row;
  56. unsigned int col;
  57. unsigned int hash;
  58. };
  59.  
  60. unsigned int tileWidth = 8;
  61. unsigned int outputWidth = 8;
  62. unsigned int mapBitsPerEntry = 8;
  63.  
  64. struct Image inputImage;
  65.  
  66. void *tileMap;
  67. struct Tile *tiles;
  68. unsigned int tilesCount;
  69.  
  70. void ReadPngPalette(struct Image *image, png_structp pngReader, png_infop pngInfo)
  71. {
  72. png_colorp colors;
  73. int numColors;
  74.  
  75. if (png_get_PLTE(pngReader, pngInfo, &colors, &numColors) != PNG_INFO_PLTE)
  76. FATAL_ERROR("Failed to retrieve palette\n");
  77.  
  78. if (numColors > 256)
  79. FATAL_ERROR("Images with more than 256 colors are not supported.\n");
  80.  
  81. image->numColors = numColors;
  82. for (int i = 0; i < numColors; i++)
  83. {
  84. image->palette[i].r = colors[i].red;
  85. image->palette[i].g = colors[i].green;
  86. image->palette[i].b = colors[i].blue;
  87. }
  88. }
  89.  
  90. void WritePngPalette(struct Image *image, png_structp pngReader, png_infop pngInfo)
  91. {
  92. png_colorp colors = malloc(image->numColors * sizeof(*colors));
  93.  
  94. if (colors == NULL)
  95. FATAL_ERROR("Failed to allocate PNG palette.\n");
  96.  
  97. for (int i = 0; i < image->numColors; i++)
  98. {
  99. colors[i].red = image->palette[i].r;
  100. colors[i].green = image->palette[i].g;
  101. colors[i].blue = image->palette[i].b;
  102. }
  103.  
  104. png_set_PLTE(pngReader, pngInfo, colors, image->numColors);
  105.  
  106. free(colors);
  107. }
  108.  
  109. static void ReadImageFromFile(struct Image *image, const char *path)
  110. {
  111. unsigned char sig[8];
  112. png_structp pngReader;
  113. png_infop pngInfo;
  114. FILE *file;
  115. png_byte **rowPointers;
  116. unsigned int i;
  117.  
  118. file = fopen(path, "rb");
  119. if (file == NULL)
  120. FATAL_ERROR("Failed to open \"%s\" for reading.\n", path);
  121.  
  122. if (fread(sig, 8, 1, file) != 1)
  123. FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path);
  124.  
  125. if (png_sig_cmp(sig, 0, 8))
  126. FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path);
  127.  
  128. pngReader = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  129. if (pngReader == NULL)
  130. FATAL_ERROR("Failed to create PNG read struct.\n");
  131.  
  132. pngInfo = png_create_info_struct(pngReader);
  133. if (pngInfo == NULL)
  134. FATAL_ERROR("Failed to create PNG info struct.\n");
  135.  
  136. if (setjmp(png_jmpbuf(pngReader)))
  137. FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path);
  138.  
  139. png_init_io(pngReader, file);
  140. png_set_sig_bytes(pngReader, 8);
  141. png_read_info(pngReader, pngInfo);
  142.  
  143. // Get info
  144. image->width = png_get_image_width(pngReader, pngInfo);
  145. image->height = png_get_image_height(pngReader, pngInfo);
  146. image->colorType = png_get_color_type(pngReader, pngInfo);
  147. image->bitDepth = png_get_bit_depth(pngReader, pngInfo);
  148.  
  149. if (image->colorType != PNG_COLOR_TYPE_PALETTE && image->colorType != PNG_COLOR_TYPE_GRAY)
  150. FATAL_ERROR("error: only paletted and grayscale images are supported");
  151.  
  152. printf("tile width: %u\n", tileWidth);
  153. printf("image dimensions: %ux%u\n", image->width, image->height);
  154. printf("image bit depth: %u\n", image->bitDepth);
  155.  
  156. // Read palette
  157. if (image->colorType == PNG_COLOR_TYPE_PALETTE)
  158. ReadPngPalette(image, pngReader, pngInfo);
  159.  
  160. // Read pixels
  161. image->rowBytes = png_get_rowbytes(pngReader, pngInfo);
  162. image->pixels = malloc(image->height * image->rowBytes);
  163. rowPointers = malloc(image->height * sizeof(*rowPointers));
  164.  
  165. for (i = 0; i < image->height; i++)
  166. rowPointers[i] = image->pixels + (i * image->rowBytes);
  167.  
  168. if (setjmp(png_jmpbuf(pngReader)))
  169. FATAL_ERROR("Error reading image\n");
  170.  
  171. png_read_image(pngReader, rowPointers);
  172.  
  173. free(rowPointers);
  174.  
  175. fclose(file);
  176. }
  177.  
  178. static void WriteImageToFile(struct Image *image, const char *path)
  179. {
  180. FILE *fp = fopen(path, "wb");
  181.  
  182. if (fp == NULL)
  183. FATAL_ERROR("Failed to open \"%s\" for writing.\n", path);
  184.  
  185. png_structp pngReader = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  186.  
  187. if (!pngReader)
  188. FATAL_ERROR("Failed to create PNG write struct.\n");
  189.  
  190. png_infop pngInfo = png_create_info_struct(pngReader);
  191.  
  192. if (!pngInfo)
  193. FATAL_ERROR("Failed to create PNG info struct.\n");
  194.  
  195. if (setjmp(png_jmpbuf(pngReader)))
  196. FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path);
  197.  
  198. png_init_io(pngReader, fp);
  199.  
  200. if (setjmp(png_jmpbuf(pngReader)))
  201. FATAL_ERROR("Error writing header for \"%s\".\n", path);
  202.  
  203. png_set_IHDR(pngReader, pngInfo, image->width, image->height,
  204. image->bitDepth, image->colorType, PNG_INTERLACE_NONE,
  205. PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
  206.  
  207. if (image->colorType == PNG_COLOR_TYPE_PALETTE)
  208. WritePngPalette(image, pngReader, pngInfo);
  209.  
  210. png_write_info(pngReader, pngInfo);
  211.  
  212. png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep));
  213.  
  214. if (row_pointers == NULL)
  215. FATAL_ERROR("Failed to allocate row pointers.\n");
  216.  
  217. int rowbytes = png_get_rowbytes(pngReader, pngInfo);
  218.  
  219. for (int i = 0; i < image->height; i++)
  220. row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes));
  221.  
  222. if (setjmp(png_jmpbuf(pngReader)))
  223. FATAL_ERROR("Error writing \"%s\".\n", path);
  224.  
  225. png_write_image(pngReader, row_pointers);
  226.  
  227. if (setjmp(png_jmpbuf(pngReader)))
  228. FATAL_ERROR("Error ending write of \"%s\".\n", path);
  229.  
  230. png_write_end(pngReader, NULL);
  231.  
  232. fclose(fp);
  233.  
  234. png_destroy_write_struct(&pngReader, &pngInfo);
  235. free(row_pointers);
  236. }
  237.  
  238. static unsigned char *GetTileStartAddr(struct Image *image, unsigned int row, unsigned int col)
  239. {
  240. unsigned int x = col * tileWidth;
  241. unsigned int y = row * tileWidth;
  242.  
  243. return image->pixels + (x + y * image->width) * image->bitDepth / 8;
  244. }
  245.  
  246. static void CreateTile(struct Tile *tile, unsigned int row, unsigned int col)
  247. {
  248. const size_t srcRowSize = inputImage.width * inputImage.bitDepth / 8;
  249. const size_t tileRowSize = tileWidth * inputImage.bitDepth / 8;
  250. unsigned int i, j;
  251. unsigned char *src = GetTileStartAddr(&inputImage, row, col);
  252. unsigned int hash = 0;
  253.  
  254. for (i = 0; i < tileWidth; i++)
  255. {
  256. for (j = 0; j < tileRowSize; j++)
  257. hash += src[j];
  258. src += srcRowSize;
  259. }
  260. tile->row = row;
  261. tile->col = col;
  262. tile->hash = hash;
  263. }
  264.  
  265. static int FindTile(struct Tile *tile)
  266. {
  267. unsigned int currTile;
  268.  
  269. for (currTile = 0; currTile < tilesCount; currTile++)
  270. {
  271. if (tile->hash == tiles[currTile].hash)
  272. {
  273. const size_t imageRowSize = inputImage.width * inputImage.bitDepth / 8;
  274. const size_t tileRowSize = tileWidth * inputImage.bitDepth / 8;
  275. unsigned int i;
  276. unsigned char *src = inputImage.pixels + tile->row * tileWidth * imageRowSize + tile->col * tileRowSize;
  277. unsigned char *dest = inputImage.pixels + tiles[currTile].row * tileWidth * imageRowSize + tiles[currTile].col * tileRowSize;
  278.  
  279. for (i = 0; i < tileWidth; i++)
  280. {
  281. if (memcmp(src, dest, tileRowSize) != 0)
  282. goto check_next;
  283. src += imageRowSize;
  284. dest += imageRowSize;
  285. }
  286. return currTile;
  287. }
  288. check_next:
  289. ;
  290. }
  291. return -1;
  292. }
  293.  
  294. void ReadImageTiles(const char *fname)
  295. {
  296. unsigned int row, col;
  297. unsigned int maxEntry = (1 << mapBitsPerEntry) - 1;
  298.  
  299. ReadImageFromFile(&inputImage, fname);
  300.  
  301. unsigned int numTilesX = inputImage.width / tileWidth;
  302. unsigned int numTilesY = inputImage.height / tileWidth;
  303.  
  304. tileMap = malloc(numTilesX * numTilesY * mapBitsPerEntry / 8);
  305. tiles = malloc(sizeof(*tiles) * numTilesX * numTilesY);
  306.  
  307. tilesCount = 0;
  308.  
  309. for (row = 0; row < numTilesY; row++)
  310. {
  311. for (col = 0; col < numTilesX; col++)
  312. {
  313. struct Tile tile;
  314.  
  315. CreateTile(&tile, row, col);
  316. int tileNum = FindTile(&tile);
  317.  
  318. if (tileNum == -1) // this is a new tile
  319. {
  320. tileNum = tilesCount;
  321. tiles[tilesCount++] = tile;
  322. }
  323. if (tileNum > maxEntry)
  324. FATAL_ERROR("Image has more than %u tiles\n", maxEntry);
  325. if (mapBitsPerEntry == 8)
  326. ((uint8_t *)tileMap)[row * numTilesX + col] = tileNum;
  327. else if (mapBitsPerEntry == 16)
  328. ((uint16_t *)tileMap)[row * numTilesX + col] = tileNum;
  329. else
  330. assert(0);
  331. }
  332. }
  333. }
  334.  
  335. void OutputTileSet(char *fname)
  336. {
  337. unsigned int i, j;
  338. const unsigned int outputTilesX = outputWidth;
  339. const unsigned int outputTilesY = (tilesCount + outputTilesX - 1) / outputTilesX;
  340. struct Image outputImage = inputImage;
  341.  
  342. outputImage.width = outputTilesX * tileWidth;
  343. outputImage.height = outputTilesY * tileWidth;
  344. outputImage.rowBytes = outputImage.width * outputImage.bitDepth / 8;
  345. outputImage.pixels = malloc(outputImage.rowBytes * outputImage.height);
  346.  
  347. for (i = 0; i < tilesCount; i++)
  348. {
  349. unsigned int row = i / outputTilesX;
  350. unsigned int col = i % outputTilesX;
  351. unsigned char *src = GetTileStartAddr(&inputImage, tiles[i].row, tiles[i].col);
  352. unsigned char *dest = GetTileStartAddr(&outputImage, row, col);
  353.  
  354. for (j = 0; j < tileWidth; j++)
  355. {
  356. memcpy(dest, src, tileWidth * outputImage.bitDepth / 8);
  357. src += inputImage.rowBytes;
  358. dest += outputImage.rowBytes;
  359. }
  360. }
  361.  
  362. WriteImageToFile(&outputImage, fname);
  363. }
  364.  
  365. void OutputTileMap(char *fname)
  366. {
  367. FILE *file = fopen(fname, "wb");
  368. unsigned int numEntries = (inputImage.width / tileWidth) * (inputImage.height / tileWidth);
  369. size_t size = numEntries * mapBitsPerEntry / 8;
  370.  
  371. fwrite(tileMap, size, 1, file);
  372. fclose(file);
  373. }
  374.  
  375. char *GetFileNameExtension(char *fname)
  376. {
  377. char *ext = strrchr(fname, '.');
  378.  
  379. if (ext != NULL)
  380. return ext;
  381. else
  382. return fname + strlen(fname);
  383. }
  384.  
  385. int main(int argc, char **argv)
  386. {
  387. int i;
  388. char *fname = NULL;
  389. char *outfname;
  390. char *ext;
  391.  
  392. for (i = 1; i < argc; i++)
  393. {
  394. char *arg = argv[i];
  395.  
  396. if (strcmp("--tile_width", arg) == 0)
  397. {
  398. i++;
  399. if (i >= argc || sscanf(argv[i], "%u", &tileWidth) != 1)
  400. FATAL_ERROR("error: expected number after --tile_width\n");
  401. }
  402. else if (strcmp("--map_bits", arg) == 0)
  403. {
  404. i++;
  405. if (i >= argc || sscanf(argv[i], "%u", &mapBitsPerEntry) != 1)
  406. FATAL_ERROR("error: expected number after --map_bits\n");
  407. }
  408. else if (strcmp("--output_width", arg) == 0)
  409. {
  410. i++;
  411. if (i >= argc || sscanf(argv[i], "%u", &outputWidth) != 1)
  412. FATAL_ERROR("error: expected number after --output_width\n");
  413. }
  414. else
  415. {
  416. fname = arg;
  417. }
  418. }
  419. if (fname == NULL)
  420. FATAL_ERROR("error: no input file specified\n");
  421.  
  422. outfname = malloc(strlen(fname) + 11);
  423. strcpy(outfname, fname);
  424. ext = GetFileNameExtension(outfname);
  425.  
  426. ReadImageTiles(fname);
  427.  
  428. strcpy(ext, "_tiles.png");
  429. OutputTileSet(outfname);
  430.  
  431. strcpy(ext, "_map.bin");
  432. OutputTileMap(outfname);
  433.  
  434. return 0;
  435. }
Add Comment
Please, Sign In to add comment