選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

611 行
20 KiB

  1. /*
  2. * img2oric Convert an image to Oric Atmos colours
  3. * Copyright (c) 2008 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * Changes:
  7. * Jan 18, 2008: initial release
  8. * Jan 23, 2008: add support for inverse video on attribute change
  9. * improve Floyd-Steinberg coefficient values
  10. * Jun 14, 2008: Win32 version
  11. * Jun 18, 2008: add meaningful error messages
  12. *
  13. * This program is free software. It comes without any warranty, to
  14. * the extent permitted by applicable law. You can redistribute it
  15. * and/or modify it under the terms of the Do What The Fuck You Want
  16. * To Public License, Version 2, as published by Sam Hocevar. See
  17. * http://sam.zoy.org/wtfpl/COPYING for more details.
  18. *
  19. * To build this program on Linux:
  20. * cc -O3 -funroll-loops -W -Wall img2oric.c -o img2oric \
  21. * $(pkg-config --cflags --libs sdl) -lSDL_image -lm
  22. * To build a Windows executable:
  23. * i586-mingw32msvc-cc -O3 -funroll-loops -W -Wall \
  24. * img2oric.c -o img2oric.exe -lgdi32
  25. */
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <math.h>
  30. #if defined _WIN32
  31. # include <windows.h>
  32. # define uint8_t unsigned char
  33. #else
  34. # include <SDL_image.h>
  35. #endif
  36. /*
  37. * BMP output name and Oric output name
  38. */
  39. #define BMPFILE "output.bmp"
  40. #define ORICFILE "OUTPUT" /* ".TAP" will be appended */
  41. /*
  42. * Image dimensions and recursion depth. DEPTH = 2 is a reasonable value,
  43. * DEPTH = 3 gives good quality, and higher values may improve the results
  44. * even more but at the cost of significantly longer computation times.
  45. */
  46. #define WIDTH 240
  47. #define HEIGHT 200
  48. #define DEPTH 3
  49. /*
  50. * Error diffusion table, similar to Floyd-Steinberg. I choose not to
  51. * propagate 100% of the error, because doing so creates awful artifacts
  52. * (full lines of the same colour, massive colour bleeding) for unclear
  53. * reasons. Atkinson dithering propagates 3/4 of the error, which is even
  54. * less than our 31/32. I also choose to propagate slightly more in the
  55. * X direction to avoid banding effects due to rounding errors.
  56. * It would be interesting, for future versions of this software, to
  57. * propagate the error to the second line, too. But right now I find it far
  58. * too complex to do.
  59. *
  60. * +-------+-------+
  61. * | error |FS0/FSX|
  62. * +-------+-------+-------+
  63. * |FS1/FSX|FS2/FSX|FS3/FSX|
  64. * +-------+-------+-------+
  65. */
  66. #define FS0 15
  67. #define FS1 6
  68. #define FS2 9
  69. #define FS3 1
  70. #define FSX 32
  71. /*
  72. * The simple Oric RGB palette, made of the 8 Neugebauer primary colours. Each
  73. * colour is repeated 6 times so that we can point to the palette to paste
  74. * whole blocks of 6 pixels. It’s also organised so that palette[7-x] is the
  75. * RGB negative of palette[x], and screen command X uses palette[X & 7].
  76. */
  77. #define o 0x0000
  78. #define X 0xffff
  79. static const int palette[8][6 * 3] =
  80. {
  81. { o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o, o },
  82. { X, o, o, X, o, o, X, o, o, X, o, o, X, o, o, X, o, o },
  83. { o, X, o, o, X, o, o, X, o, o, X, o, o, X, o, o, X, o },
  84. { X, X, o, X, X, o, X, X, o, X, X, o, X, X, o, X, X, o },
  85. { o, o, X, o, o, X, o, o, X, o, o, X, o, o, X, o, o, X },
  86. { X, o, X, X, o, X, X, o, X, X, o, X, X, o, X, X, o, X },
  87. { o, X, X, o, X, X, o, X, X, o, X, X, o, X, X, o, X, X },
  88. { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X },
  89. };
  90. /*
  91. * Gamma correction tables. itoc_table and ctoi_table accept overflow and
  92. * underflow values to a reasonable extent, so that we don’t have to check
  93. * for these cases later in the code. Tests kill performance.
  94. */
  95. #define PAD 2048
  96. static int itoc_table_clip[PAD + 256 + PAD], ctoi_table_clip[PAD + 256 + PAD];
  97. static int *itoc_table = itoc_table_clip + PAD;
  98. static int *ctoi_table = ctoi_table_clip + PAD;
  99. static void init_tables(void)
  100. {
  101. int i;
  102. for(i = 0; i < PAD + 256 + PAD; i++)
  103. {
  104. double f = 1.0 * (i - PAD) / 255.999;
  105. if(f >= 0.)
  106. {
  107. itoc_table_clip[i] = (int)(65535.999 * pow(f, 1./2.2));
  108. ctoi_table_clip[i] = (int)(65535.999 * pow(f, 2.2));
  109. }
  110. else
  111. {
  112. itoc_table_clip[i] = - (int)(65535.999 * pow(-f, 1./2.2));
  113. ctoi_table_clip[i] = - (int)(65535.999 * pow(-f, 2.2));
  114. }
  115. }
  116. }
  117. static inline int itoc(int p) { return itoc_table[p / 0x100]; }
  118. static inline int ctoi(int p) { return ctoi_table[p / 0x100]; }
  119. /*
  120. * Set new background and foreground colours according to the given command.
  121. */
  122. static inline void domove(uint8_t command, uint8_t *bg, uint8_t *fg)
  123. {
  124. if((command & 0x78) == 0x00)
  125. *fg = command & 0x7;
  126. else if((command & 0x78) == 0x10)
  127. *bg = command & 0x7;
  128. }
  129. /*
  130. * Clamp pixel value to avoid colour bleeding. Deactivated because it
  131. * does not give satisfactory results.
  132. */
  133. #define CLAMP 0x1000
  134. static inline int clamp(int p)
  135. {
  136. #if 0
  137. /* FIXME: doesn’t give terribly good results on eg. eatme.png */
  138. if(p < - CLAMP) return - CLAMP;
  139. if(p > 0xffff + CLAMP) return 0xffff + CLAMP;
  140. #endif
  141. return p;
  142. }
  143. /*
  144. * Compute the perceptual error caused by replacing the input pixels "in"
  145. * with the output pixels "out". "inerr" is the diffused error that should
  146. * be applied to "in"’s first pixel. "outerr" will hold the diffused error
  147. * to apply after "in"’s last pixel upon next call. The return value does
  148. * not mean much physically; it is one part of the algorithm where you need
  149. * to play a bit in order to get appealing results. That’s how image
  150. * processing works, dude.
  151. */
  152. static inline int geterror(int const *in, int const *inerr,
  153. int const *out, int *outerr)
  154. {
  155. int tmperr[9 * 3];
  156. int i, c, ret = 0;
  157. /* 9 cells: 1 for the end of line, 8 for the errors below */
  158. memcpy(tmperr, inerr, 3 * sizeof(int));
  159. memset(tmperr + 3, 0, 8 * 3 * sizeof(int));
  160. for(i = 0; i < 6; i++)
  161. {
  162. for(c = 0; c < 3; c++)
  163. {
  164. /* Experiment shows that this is important at small depths */
  165. int a = clamp(in[i * 3 + c] + tmperr[c]);
  166. int b = out[i * 3 + c];
  167. tmperr[c] = (a - b) * FS0 / FSX;
  168. tmperr[c + (i * 3 + 3)] += (a - b) * FS1 / FSX;
  169. tmperr[c + (i * 3 + 6)] += (a - b) * FS2 / FSX;
  170. tmperr[c + (i * 3 + 9)] += (a - b) * FS3 / FSX;
  171. ret += (a - b) / 256 * (a - b) / 256;
  172. }
  173. }
  174. for(i = 0; i < 4; i++)
  175. {
  176. for(c = 0; c < 3; c++)
  177. {
  178. /* Experiment shows that this is important at large depths */
  179. int a = itoc((in[i * 3 + c] + in[i * 3 + 3 + c]
  180. + in[i * 3 + 6 + c]) / 3);
  181. int b = itoc((out[i * 3 + c] + out[i * 3 + 3 + c]
  182. + out[i * 3 + 6 + c]) / 3);
  183. ret += (a - b) / 256 * (a - b) / 256;
  184. }
  185. }
  186. /* Using the diffused error as a perceptual error component is stupid,
  187. * because that’s not what it is at all, but I found that it helped a
  188. * bit in some cases. */
  189. for(i = 0; i < 3; i++)
  190. ret += tmperr[i] / 256 * tmperr[i] / 256;
  191. memcpy(outerr, tmperr, 3 * sizeof(int));
  192. return ret;
  193. }
  194. static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg,
  195. int const *errvec, int depth, int maxerror,
  196. int *error, int *out)
  197. {
  198. int voidvec[3], nvoidvec[3], bestrgb[6 * 3], tmprgb[6 * 3], tmpvec[3];
  199. int const *voidrgb, *nvoidrgb, *vec, *rgb;
  200. int besterror, curerror, suberror, statice, voide, nvoide;
  201. int i, j, c;
  202. uint8_t command, bestcommand;
  203. /* Precompute error for the case where we change the foreground colour
  204. * and hence only print the background colour or its negative */
  205. voidrgb = palette[bg];
  206. voide = geterror(in, errvec, voidrgb, voidvec);
  207. nvoidrgb = palette[7 - bg];
  208. nvoide = geterror(in, errvec, nvoidrgb, nvoidvec);
  209. /* Precompute sub-error for the case where we print pixels (and hence
  210. * don’t change the palette). It’s not the exact error because we should
  211. * be propagating the error to the first pixel here. */
  212. if(depth > 0)
  213. {
  214. int tmp[3] = { 0, 0, 0 };
  215. bestmove(in + 6 * 3, bg, fg, tmp, depth - 1, maxerror, &statice, NULL);
  216. }
  217. /* Check every likely command:
  218. * 0-7: change foreground to 0-7
  219. * 8-15: change foreground to 0-7, print negative background
  220. * 16-23: change background to 0-7
  221. * 24-31: change background to 0-7, print negative background
  222. * 32: normal stuff
  223. * 33: inverse video stuff */
  224. besterror = 0x7ffffff;
  225. bestcommand = 0x10;
  226. memcpy(bestrgb, voidrgb, 6 * 3 * sizeof(int));
  227. for(j = 0; j < 34; j++)
  228. {
  229. static uint8_t const lookup[] =
  230. {
  231. 0x00, 0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07,
  232. 0x80, 0x84, 0x81, 0x85, 0x82, 0x86, 0x83, 0x87,
  233. 0x10, 0x14, 0x11, 0x15, 0x12, 0x16, 0x13, 0x17,
  234. 0x90, 0x94, 0x91, 0x95, 0x92, 0x96, 0x93, 0x97,
  235. 0x40, 0xc0
  236. };
  237. uint8_t newbg = bg, newfg = fg;
  238. command = lookup[j];
  239. domove(command, &newbg, &newfg);
  240. /* Keeping bg and fg is useless, because we could use standard
  241. * pixel printing instead */
  242. if((command & 0x40) == 0x00 && newbg == bg && newfg == fg)
  243. continue;
  244. /* I *think* having newfg == newbg is useless, too, but I don’t
  245. * want to miss some corner case where swapping bg and fg may be
  246. * interesting, so we continue anyway. */
  247. #if 0
  248. /* Bit 6 off and bit 5 on seems illegal */
  249. if((command & 0x60) == 0x20)
  250. continue;
  251. /* Bits 6 and 5 off and bit 3 on seems illegal */
  252. if((command & 0x68) == 0x08)
  253. continue;
  254. #endif
  255. if((command & 0xf8) == 0x00)
  256. {
  257. curerror = voide;
  258. rgb = voidrgb;
  259. vec = voidvec;
  260. }
  261. else if((command & 0xf8) == 0x80)
  262. {
  263. curerror = nvoide;
  264. rgb = nvoidrgb;
  265. vec = nvoidvec;
  266. }
  267. else if((command & 0xf8) == 0x10)
  268. {
  269. rgb = palette[newbg];
  270. curerror = geterror(in, errvec, rgb, tmpvec);
  271. vec = tmpvec;
  272. }
  273. else if((command & 0xf8) == 0x90)
  274. {
  275. rgb = palette[7 - newbg];
  276. curerror = geterror(in, errvec, rgb, tmpvec);
  277. vec = tmpvec;
  278. }
  279. else
  280. {
  281. int const *bgcolor, *fgcolor;
  282. if((command & 0x80) == 0x00)
  283. {
  284. bgcolor = palette[bg]; fgcolor = palette[fg];
  285. }
  286. else
  287. {
  288. bgcolor = palette[7 - bg]; fgcolor = palette[7 - fg];
  289. }
  290. memcpy(tmpvec, errvec, 3 * sizeof(int));
  291. curerror = 0;
  292. for(i = 0; i < 6; i++)
  293. {
  294. int vec1[3], vec2[3];
  295. int smalle1 = 0, smalle2 = 0;
  296. memcpy(vec1, tmpvec, 3 * sizeof(int));
  297. memcpy(vec2, tmpvec, 3 * sizeof(int));
  298. for(c = 0; c < 3; c++)
  299. {
  300. int delta1, delta2;
  301. delta1 = clamp(in[i * 3 + c] + tmpvec[c]) - bgcolor[c];
  302. vec1[c] = delta1 * FS0 / FSX;
  303. smalle1 += delta1 / 256 * delta1;
  304. delta2 = clamp(in[i * 3 + c] + tmpvec[c]) - fgcolor[c];
  305. vec2[c] = delta2 * FS0 / FSX;
  306. smalle2 += delta2 / 256 * delta2;
  307. }
  308. if(smalle1 < smalle2)
  309. {
  310. memcpy(tmpvec, vec1, 3 * sizeof(int));
  311. memcpy(tmprgb + i * 3, bgcolor, 3 * sizeof(int));
  312. }
  313. else
  314. {
  315. memcpy(tmpvec, vec2, 3 * sizeof(int));
  316. memcpy(tmprgb + i * 3, fgcolor, 3 * sizeof(int));
  317. command |= (1 << (5 - i));
  318. }
  319. }
  320. /* Recompute full error */
  321. curerror += geterror(in, errvec, tmprgb, tmpvec);
  322. rgb = tmprgb;
  323. vec = tmpvec;
  324. }
  325. if(curerror > besterror)
  326. continue;
  327. /* Try to avoid bad decisions now that will have a high cost
  328. * later in the line by making the next error more important than
  329. * the current error. */
  330. curerror = curerror * 3 / 4;
  331. if(depth == 0)
  332. suberror = 0; /* It’s the end of the tree */
  333. else if((command & 0x68) == 0x00)
  334. {
  335. bestmove(in + 6 * 3, newbg, newfg, vec, depth - 1,
  336. besterror - curerror, &suberror, NULL);
  337. #if 0
  338. /* Slight penalty for colour changes; they're hard to revert. The
  339. * value of 2 was determined empirically. 1.5 is not enough and
  340. * 3 is too much. */
  341. if(newbg != bg)
  342. suberror = suberror * 10 / 8;
  343. else if(newfg != fg)
  344. suberror = suberror * 9 / 8;
  345. #endif
  346. }
  347. else
  348. suberror = statice;
  349. if(curerror + suberror < besterror)
  350. {
  351. besterror = curerror + suberror;
  352. bestcommand = command;
  353. memcpy(bestrgb, rgb, 6 * 3 * sizeof(int));
  354. }
  355. }
  356. *error = besterror;
  357. if(out)
  358. memcpy(out, bestrgb, 6 * 3 * sizeof(int));
  359. return bestcommand;
  360. }
  361. int main(int argc, char *argv[])
  362. {
  363. #if defined _WIN32
  364. PBITMAPINFO pbinfo;
  365. BITMAPINFO binfo;
  366. BITMAPFILEHEADER bfheader;
  367. ULONG bisize;
  368. HANDLE hfile;
  369. HBITMAP tmp;
  370. HDC hdc;
  371. DWORD ret;
  372. #else
  373. SDL_Surface *tmp, *surface;
  374. #endif
  375. FILE *f;
  376. uint8_t *pixels;
  377. int *src, *dst, *srcl, *dstl;
  378. int stride, x, y, depth, c;
  379. if(argc < 2)
  380. {
  381. fprintf(stderr, "Error: missing argument.\n");
  382. fprintf(stderr, "Usage: img2oric <image>\n");
  383. return 1;
  384. }
  385. #if defined _WIN32
  386. tmp = (HBITMAP)LoadImage(NULL, argv[1], IMAGE_BITMAP,
  387. 0, 0, LR_LOADFROMFILE);
  388. #else
  389. tmp = IMG_Load(argv[1]);
  390. #endif
  391. if(!tmp)
  392. {
  393. fprintf(stderr, "Error: could not load image %s.\n", argv[1]);
  394. #if defined _WIN32
  395. fprintf(stderr, "Maybe try with an 8-bpp or 24-bpp BMP file?\n");
  396. #endif
  397. return 2;
  398. }
  399. f = fopen(ORICFILE ".TAP", "w");
  400. if(!f)
  401. {
  402. fprintf(stderr, "Error: could not open %s.TAP for writing.\n",
  403. ORICFILE);
  404. return 3;
  405. }
  406. fwrite("\x16\x16\x16\x16\x24", 1, 5, f);
  407. fwrite("\x00\xff\x80\x00\xbf\x3f\xa0\x00\x00", 1, 9, f);
  408. fwrite(ORICFILE, 1, strlen(ORICFILE), f);
  409. fwrite("\x00", 1, 1, f);
  410. init_tables();
  411. /* Load the image into a friendly array of fast integers. We create it
  412. * slightly bigger than the image so that we don’t have to care about
  413. * borders when propagating the error later */
  414. src = calloc((WIDTH + 1) * (HEIGHT + 1) * 3, sizeof(int));
  415. dst = calloc((WIDTH + 1) * (HEIGHT + 1) * 3, sizeof(int));
  416. stride = (WIDTH + 1) * 3;
  417. #if defined _WIN32
  418. hdc = CreateCompatibleDC(NULL);
  419. SelectObject(hdc, tmp);
  420. for(y = 0; y < HEIGHT; y++)
  421. for(x = 0; x < WIDTH; x++)
  422. {
  423. COLORREF color = GetPixel(hdc, x, y);
  424. src[y * stride + x * 3] = ctoi(GetRValue(color) * 0x101);
  425. src[y * stride + x * 3 + 1] = ctoi(GetGValue(color) * 0x101);
  426. src[y * stride + x * 3 + 2] = ctoi(GetBValue(color) * 0x101);
  427. for(c = 0; c < 3; c++)
  428. dst[y * stride + x * 3 + c] = 0;
  429. }
  430. #else
  431. /* FIXME: endianness */
  432. surface = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT, 32,
  433. 0xff0000, 0xff00, 0xff, 0x0);
  434. SDL_BlitSurface(tmp, NULL, surface, NULL);
  435. pixels = (uint8_t *)surface->pixels;
  436. for(y = 0; y < HEIGHT; y++)
  437. for(x = 0; x < WIDTH; x++)
  438. for(c = 0; c < 3; c++)
  439. {
  440. src[y * stride + x * 3 + c]
  441. = ctoi(pixels[y * surface->pitch + x * 4 + 2 - c] * 0x101);
  442. dst[y * stride + x * 3 + c] = 0;
  443. }
  444. #endif
  445. /* Let the fun begin */
  446. for(y = 0; y < HEIGHT; y++)
  447. {
  448. uint8_t bg = 0, fg = 7;
  449. fprintf(stderr, "\rProcessing... %i%%", (y * 100 + 99) / HEIGHT);
  450. for(x = 0; x < WIDTH; x += 6)
  451. {
  452. int errvec[3] = { 0, 0, 0 };
  453. int dummy, i;
  454. uint8_t command;
  455. depth = (x + DEPTH < WIDTH) ? DEPTH : (WIDTH - x) / 6 - 1;
  456. srcl = src + y * stride + x * 3;
  457. dstl = dst + y * stride + x * 3;
  458. /* Recursively compute and apply best command */
  459. command = bestmove(srcl, bg, fg, errvec, depth, 0x7fffff,
  460. &dummy, dstl);
  461. /* Propagate error */
  462. for(c = 0; c < 3; c++)
  463. {
  464. for(i = 0; i < 6; i++)
  465. {
  466. int error = srcl[i * 3 + c] - dstl[i * 3 + c];
  467. srcl[i * 3 + c + 3] =
  468. clamp(srcl[i * 3 + c + 3] + error * FS0 / FSX);
  469. srcl[i * 3 + c + stride - 3] += error * FS1 / FSX;
  470. srcl[i * 3 + c + stride] += error * FS2 / FSX;
  471. srcl[i * 3 + c + stride + 3] += error * FS3 / FSX;
  472. }
  473. for(i = -1; i < 7; i++)
  474. srcl[i * 3 + c + stride] = clamp(srcl[i * 3 + c + stride]);
  475. }
  476. /* Iterate */
  477. domove(command, &bg, &fg);
  478. /* Write byte to file */
  479. fwrite(&command, 1, 1, f);
  480. }
  481. }
  482. fclose(f);
  483. fprintf(stderr, " done.\n");
  484. /* Save everything */
  485. #if defined _WIN32
  486. for(y = 0; y < HEIGHT; y++)
  487. for(x = 0; x < WIDTH; x++)
  488. {
  489. uint8_t r = dst[y * stride + x * 3] / 0x100;
  490. uint8_t g = dst[y * stride + x * 3 + 1] / 0x100;
  491. uint8_t b = dst[y * stride + x * 3 + 2] / 0x100;
  492. SetPixel(hdc, x, y, RGB(r, g, b));
  493. }
  494. binfo.bmiHeader.biSize = sizeof(binfo.bmiHeader);
  495. binfo.bmiHeader.biBitCount = 0;
  496. GetDIBits(hdc, tmp, 0, 0, NULL, &binfo, DIB_RGB_COLORS);
  497. switch(binfo.bmiHeader.biBitCount)
  498. {
  499. case 24:
  500. bisize = sizeof(BITMAPINFOHEADER);
  501. break;
  502. case 16:
  503. case 32:
  504. bisize = sizeof(BITMAPINFOHEADER) + sizeof(DWORD) * 3;
  505. break;
  506. default:
  507. bisize = sizeof(BITMAPINFOHEADER)
  508. + sizeof(RGBQUAD) * (1 << binfo.bmiHeader.biBitCount);
  509. break;
  510. }
  511. pbinfo = (PBITMAPINFO)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, bisize);
  512. memcpy(pbinfo, &binfo, sizeof(BITMAPINFOHEADER));
  513. bfheader.bfType = 0x4D42; /* "BM" */
  514. bfheader.bfSize = sizeof(BITMAPFILEHEADER)
  515. + bisize + pbinfo->bmiHeader.biSizeImage;
  516. bfheader.bfReserved1 = 0;
  517. bfheader.bfReserved2 = 0;
  518. bfheader.bfOffBits = sizeof(BITMAPFILEHEADER) + bisize;
  519. pixels = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  520. binfo.bmiHeader.biSizeImage);
  521. GetDIBits(hdc, tmp, 0, pbinfo->bmiHeader.biHeight,
  522. pixels, pbinfo, DIB_RGB_COLORS);
  523. hfile = CreateFile(BMPFILE, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
  524. FILE_ATTRIBUTE_ARCHIVE, NULL);
  525. WriteFile(hfile, &bfheader, sizeof(BITMAPFILEHEADER), &ret, NULL);
  526. WriteFile(hfile, pbinfo, bisize, &ret, NULL);
  527. WriteFile(hfile, pixels, pbinfo->bmiHeader.biSizeImage, &ret, NULL);
  528. CloseHandle(hfile);
  529. GlobalFree(pbinfo);
  530. GlobalFree(pixels);
  531. #else
  532. for(y = 0; y < HEIGHT; y++)
  533. for(x = 0; x < WIDTH; x++)
  534. for(c = 0; c < 3; c++)
  535. pixels[y * surface->pitch + x * 4 + 2 - c]
  536. = itoc(dst[y * stride + x * 3 + c]) / 0x100;
  537. SDL_SaveBMP(surface, BMPFILE);
  538. #endif
  539. return 0;
  540. }