Guest User

Untitled

a guest
Jul 22nd, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.89 KB | None | 0 0
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include "smzxconv.h"
  4.  
  5. typedef struct {
  6. int r;
  7. int g;
  8. int b;
  9. int c;
  10. } rgba_accum;
  11.  
  12. typedef struct {
  13. mzx_glyph g;
  14. unsigned short d;
  15. } glyph_dist;
  16.  
  17. struct _smzx_converter {
  18. int w, h;
  19. int iw, ih;
  20. int tsiz, isiz;
  21. int chroff, chrskip, chrlen;
  22. int chrprelen;
  23. int clroff, clrlen;
  24. rgba_color *src;
  25. unsigned char *dest;
  26. unsigned char *hist;
  27. glyph_dist *tgly;
  28. glyph_dist *cset;
  29. };
  30.  
  31. smzx_converter *smzx_convert_init (int w, int h, int chroff, int chrskip,
  32. int chrlen, int clroff, int clrlen) {
  33. smzx_converter *c;
  34. int chrprelen = chrlen;
  35. int iw = w * 4;
  36. int ih = h * 14;
  37. int tsiz = w * h;
  38. int isiz = iw * ih;
  39. int setsize = tsiz;
  40. if ((chrskip > chroff) && (chrskip < chroff + chrlen)) chrprelen--;
  41. if (setsize < chroff + chrlen) setsize = chroff + chrlen;
  42. c = malloc(sizeof(smzx_converter));
  43. if (!c) return NULL;
  44. c->w = w;
  45. c->h = h;
  46. c->iw = iw;
  47. c->ih = ih;
  48. c->tsiz = tsiz;
  49. c->isiz = isiz;
  50. c->chroff = chroff;
  51. c->chrskip = chrskip;
  52. c->chrlen = chrlen;
  53. c->chrprelen = chrprelen;
  54. c->clroff = clroff;
  55. c->clrlen = clrlen;
  56. c->src = malloc(sizeof(rgba_color) * isiz);
  57. if (!c->src) {
  58. free(c);
  59. return NULL;
  60. }
  61. c->dest = malloc(isiz);
  62. if (!c->dest) {
  63. free(c->src);
  64. free(c);
  65. return NULL;
  66. }
  67. c->hist = malloc(isiz);
  68. if (!c->hist) {
  69. free(c->dest);
  70. free(c->src);
  71. free(c);
  72. return NULL;
  73. }
  74. c->tgly = malloc(sizeof(glyph_dist) * setsize);
  75. if (!c->tgly) {
  76. free(c->hist);
  77. free(c->dest);
  78. free(c->src);
  79. free(c);
  80. return NULL;
  81. }
  82. c->cset = malloc(sizeof(glyph_dist) * setsize);
  83. if (!c->cset) {
  84. free(c->tgly);
  85. free(c->hist);
  86. free(c->dest);
  87. free(c->src);
  88. free(c);
  89. return NULL;
  90. }
  91. return c;
  92. }
  93.  
  94. static int ccmp (const void *av, const void *bv) {
  95. const unsigned char *a = av;
  96. const unsigned char *b = bv;
  97. if (*a < *b) return -1;
  98. if (*a > *b) return 1;
  99. return 0;
  100. }
  101.  
  102. static int gdist (const mzx_glyph a, const mzx_glyph b) {
  103. int i, x, y, res;
  104. static int init = 0;
  105. static int dist[256][256];
  106. const unsigned char swap[4] = {0, 2, 1, 3};
  107. if (!init) {
  108. for (y = 0; y < 4; y++)
  109. for (x = 0; x < 4; x++)
  110. dist[x][y] = abs(swap[x] - swap[y]);
  111. for (y = 0; y < 256; y++) {
  112. for (x = 0; x < 256; x++) {
  113. dist[x][y] = dist[x&3][y&3] + dist[(x>>2)&3][(y>>2)&3]
  114. + dist[(x>>4)&3][(y>>4)&3] + dist[x>>6][y>>6];
  115. }
  116. }
  117. init = 1;
  118. }
  119. res = 0;
  120. for (i = 0; i < 14; i++)
  121. res += dist[a[i]][b[i]];
  122. return res;
  123. }
  124.  
  125. static int grdist (const mzx_glyph a, const mzx_glyph b) {
  126. int i, x, y, res;
  127. static int init = 0;
  128. static int dist[256][256];
  129. const unsigned char swap[4] = {0, 2, 1, 3};
  130. if (!init) {
  131. for (y = 0; y < 4; y++)
  132. for (x = 0; x < 4; x++)
  133. dist[x][y] = abs(swap[x] - swap[y]);
  134. for (y = 0; y < 256; y++) {
  135. for (x = 0; x < 256; x++) {
  136. dist[x][y] = dist[x&3][y&3] + dist[(x>>2)&3][(y>>2)&3]
  137. + dist[(x>>4)&3][(y>>4)&3] + dist[x>>6][y>>6];
  138. }
  139. }
  140. init = 1;
  141. }
  142. res = 0;
  143. for (i = 0; i < 14; i++)
  144. res += dist[a[i]][b[i]^0xFF];
  145. return res;
  146. }
  147.  
  148. static int gpdist (const mzx_glyph a, const mzx_glyph b) {
  149. int res, rev;
  150. res = gdist(a, b);
  151. rev = grdist(a, b);
  152. return (res < rev) ? res : rev;
  153. }
  154.  
  155. static int gcmp (const void *av, const void *bv) {
  156. const mzx_glyph blank = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  157. const glyph_dist *a = av;
  158. const glyph_dist *b = bv;
  159. int ad, bd, i;
  160. if (a->d > b->d) return -1;
  161. if (a->d < b->d) return 1;
  162. ad = gdist(a->g, blank);
  163. bd = gdist(b->g, blank);
  164. if (ad < bd) return -1;
  165. if (ad > bd) return 1;
  166. for (i = 0; i < 14; i++) {
  167. if (a->g[i] < b->g[i]) return -1;
  168. if (a->g[i] > b->g[i]) return 1;
  169. }
  170. return 0;
  171. }
  172.  
  173. int smzx_convert (smzx_converter *c, const rgba_color *img, mzx_tile *tile,
  174. mzx_glyph *chr, mzx_color *pal) {
  175. const mzx_glyph blank = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  176. const mzx_glyph full = {255, 255, 255, 255, 255, 255, 255, 255, 255,
  177. 255, 255, 255, 255, 255};
  178. const rgba_color *ipx;
  179. rgba_color *spx;
  180. unsigned char *dpx, *hpx;
  181. unsigned char gpal[16];
  182. unsigned char lut[256];
  183. unsigned char bg, fg, tmp;
  184. rgba_accum apal[256] = {{0}};
  185. int i, j, u, v, x, y;
  186. if (!c) return 0;
  187. /* Half-width copy image */
  188. for (i = 0, ipx = img, spx = c->src; i < c->isiz;
  189. i++, ipx += 2, spx++) {
  190. spx->r = (ipx[0].r * ipx[0].a + ipx[1].r * ipx[1].a) / 2040;
  191. spx->g = (ipx[0].g * ipx[0].a + ipx[1].g * ipx[1].a) / 2040;
  192. spx->b = (ipx[0].b * ipx[0].a + ipx[1].b * ipx[1].a) / 2040;
  193. spx->a = 63;
  194. }
  195. /* Grayscale image */
  196. for (i = 0, spx = c->src, dpx = c->dest; i < c->isiz;
  197. i++, spx++, dpx++)
  198. *dpx = (spx->r * 30 + spx->g * 59 + spx->b * 11) * spx->a
  199. / 6300;
  200. /* Create histogram-based grayscale palette */
  201. memcpy(c->hist, c->dest, c->isiz);
  202. qsort(c->hist, c->isiz, 1, ccmp);
  203. for (i = 0; i < c->clrlen; i++)
  204. gpal[i] = (c->hist[i*(c->isiz-1)/(c->clrlen-1)] * 2
  205. + (c->hist[0] * (c->clrlen - 1 - i)
  206. + c->hist[c->isiz-1] * i) / (c->clrlen - 1)) / 3;
  207. memmove(gpal + c->clroff, gpal, c->clrlen);
  208. /* Find min/max values for each tile and assign tile colors accordingly */
  209. for (v = 0; v < c->h; v++) {
  210. for (u = 0; u < c->w; u++) {
  211. bg = 255;
  212. fg = 0;
  213. dpx = c->dest + u * 4 + v * 14 * c->iw;
  214. for (y = 0; y < 14; y++) {
  215. for (x = 0; x < 4; x++) {
  216. tmp = dpx[x+y*c->iw];
  217. if (bg > tmp) bg = tmp;
  218. if (fg < tmp) fg = tmp;
  219. }
  220. }
  221. for (i = c->clroff; i < c->clroff + c->clrlen; i++)
  222. if (gpal[i] <= bg) tmp = i;
  223. tile[u+v*c->w].clr = tmp << 4;
  224. for (i = c->clroff + c->clrlen - 1; i >= c->clroff;
  225. i--)
  226. if (gpal[i] >= fg) tmp = i;
  227. tile[u+v*c->w].clr |= tmp;
  228. }
  229. }
  230. /* Create characters for each tile (no dithering) */
  231. for (y = 0, dpx = c->dest, hpx = c->hist; y < c->ih; y++) {
  232. v = y / 14;
  233. for (x = 0; x < c->iw; x++, dpx++, hpx++) {
  234. u = x / 4;
  235. tmp = tile[u+v*c->w].clr;
  236. bg = tmp >> 4;
  237. fg = tmp & 15;
  238. tmp = (gpal[bg] + gpal[fg]) / 2;
  239. if (*dpx >= tmp) {
  240. tmp = (gpal[bg] + gpal[fg] * 5) / 6;
  241. if (*dpx >= tmp) *hpx = (fg << 4) | fg;
  242. else *hpx = (bg << 4) | fg;
  243. } else {
  244. tmp = (gpal[bg] * 5 + gpal[fg]) / 6;
  245. if (*dpx >= tmp) *hpx = (fg << 4) | bg;
  246. else *hpx = (bg << 4) | bg;
  247. }
  248. if (gpal[bg] == gpal[fg]) *hpx = (bg << 4) | bg;
  249. }
  250. }
  251. /* Convert characters to SMZX format */
  252. for (v = 0; v < c->h; v++) {
  253. for (u = 0; u < c->w; u++) {
  254. hpx = c->hist + u * 4 + v * 14 * c->iw;
  255. tmp = tile[u+v*c->w].clr;
  256. bg = tmp >> 4;
  257. fg = tmp & 15;
  258. lut[(bg<<4)|bg] = 0;
  259. lut[(fg<<4)|bg] = 2;
  260. lut[(bg<<4)|fg] = 1;
  261. lut[(fg<<4)|fg] = 3;
  262. for (y = 0; y < 14; y++, hpx += c->iw)
  263. c->tgly[u+v*c->w].g[y] = (lut[hpx[0]] << 6)
  264. | (lut[hpx[1]] << 4)
  265. | (lut[hpx[2]] << 2) | lut[hpx[3]];
  266. }
  267. }
  268. /* Reduce character set to specified size */
  269. memcpy(c->cset, c->tgly, sizeof(glyph_dist) * c->tsiz);
  270. for (i = 0; i < c->tsiz; i++) {
  271. c->cset[i].d = 32767;
  272. if (gdist(c->cset[i].g, blank) > gdist(c->cset[i].g, full))
  273. for (j = 0; j < 14; j++)
  274. c->cset[i].g[j] ^= 0xFF;
  275. }
  276. qsort(c->cset, c->tsiz, sizeof(glyph_dist), gcmp);
  277. for (i = 0; i < c->tsiz - 1; i++) {
  278. for (j = i + 1; j < c->tsiz; j++) {
  279. x = gpdist(c->cset[i].g, c->cset[j].g);
  280. if (x < c->cset[j].d) c->cset[j].d = x;
  281. }
  282. }
  283. qsort(c->cset, c->tsiz, sizeof(glyph_dist), gcmp);
  284. memmove(c->cset + c->chroff, c->cset,
  285. sizeof(glyph_dist) * c->chrprelen);
  286. if (c->chrprelen < c->chrlen) {
  287. memmove(c->cset + c->chrskip + 1, c->cset + c->chrskip,
  288. sizeof(glyph_dist) * (c->chroff + c->chrprelen
  289. - c->chrskip));
  290. memset(c->cset + c->chrskip, 0, sizeof(glyph_dist));
  291. }
  292. for (i = 0; i < c->chrlen; i++)
  293. memcpy(chr[i], c->cset[i+c->chroff].g, sizeof(mzx_glyph));
  294. /* Map tiles to characters from reduced set */
  295. for (i = u = 0; i < c->tsiz; i++) {
  296. x = gdist(c->tgly[i].g, c->cset->g) + 1;
  297. for (j = c->chroff; j < c->chroff + c->chrlen; j++) {
  298. if (j == c->chrskip) continue;
  299. y = gdist(c->tgly[i].g, c->cset[j].g);
  300. if (y < x) {
  301. x = y;
  302. tile[i].chr = j;
  303. u = 0;
  304. }
  305. y = grdist(c->tgly[i].g, c->cset[j].g);
  306. if (y < x) {
  307. x = y;
  308. tile[i].chr = j;
  309. u = 1;
  310. }
  311. }
  312. if (u) tile[i].clr = (tile[i].clr << 4) | (tile[i].clr >> 4);
  313. }
  314. /* Draw resulting grayscale image */
  315. for (v = 0; v < c->h; v++) {
  316. for (u = 0; u < c->w; u++) {
  317. hpx = c->hist + u * 4 + v * 14 * c->iw;
  318. tmp = tile[u+v*c->w].clr;
  319. bg = tmp >> 4;
  320. fg = tmp & 15;
  321. lut[0] = (bg << 4) | bg;
  322. lut[2] = (fg << 4) | bg;
  323. lut[1] = (bg << 4) | fg;
  324. lut[3] = (fg << 4) | fg;
  325. for (y = 0; y < 14; y++, hpx += c->iw) {
  326. hpx[0] = lut[c->cset[tile[u+v*c->w].chr].g[y]>>6];
  327. hpx[1] = lut[(c->cset[tile[u+v*c->w].chr].g[y]>>4)&3];
  328. hpx[2] = lut[(c->cset[tile[u+v*c->w].chr].g[y]>>2)&3];
  329. hpx[3] = lut[c->cset[tile[u+v*c->w].chr].g[y]&3];
  330. }
  331. }
  332. }
  333. /* Calculate average color of pixels assigned to each index */
  334. for (i = 0, spx = c->src, dpx = c->dest, hpx = c->hist; i < c->isiz;
  335. i++, spx++, dpx++, hpx++) {
  336. apal[*hpx].r += spx->r;
  337. apal[*hpx].g += spx->g;
  338. apal[*hpx].b += spx->b;
  339. apal[*hpx].c += *dpx;
  340. }
  341. for (i = 0; i < 256; i++) {
  342. bg = i >> 4;
  343. fg = i & 15;
  344. if (bg < c->clroff) continue;
  345. if (fg < c->clroff) continue;
  346. if (bg >= c->clroff + c->clrlen) continue;
  347. if (fg >= c->clroff + c->clrlen) continue;
  348. tmp = (gpal[bg] + gpal[fg] * 2) / 3;
  349. if (apal[i].c) {
  350. apal[i].r = apal[i].r * tmp / apal[i].c;
  351. apal[i].g = apal[i].g * tmp / apal[i].c;
  352. apal[i].b = apal[i].b * tmp / apal[i].c;
  353. if (apal[i].r > 63) apal[i].r = 63;
  354. if (apal[i].g > 63) apal[i].g = 63;
  355. if (apal[i].b > 63) apal[i].b = 63;
  356. } else
  357. apal[i].r = apal[i].g = apal[i].b = tmp;
  358. pal[i].r = apal[i].r;
  359. pal[i].g = apal[i].g;
  360. pal[i].b = apal[i].b;
  361. }
  362. return 1;
  363. }
  364.  
  365. void smzx_convert_free (smzx_converter *c) {
  366. if (!c) return;
  367. free(c->src);
  368. free(c->dest);
  369. free(c->hist);
  370. free(c->tgly);
  371. free(c->cset);
  372. free(c);
  373. }
Add Comment
Please, Sign In to add comment