output-pnm.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. // Copyright (C) 2004-2025 Artifex Software, Inc.
  2. //
  3. // This file is part of MuPDF.
  4. //
  5. // MuPDF is free software: you can redistribute it and/or modify it under the
  6. // terms of the GNU Affero General Public License as published by the Free
  7. // Software Foundation, either version 3 of the License, or (at your option)
  8. // any later version.
  9. //
  10. // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
  11. // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  12. // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. // details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
  17. //
  18. // Alternative licensing terms are available from the licensor.
  19. // For commercial licensing, see <https://www.artifex.com/> or contact
  20. // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
  21. // CA 94129, USA, for further information.
  22. #include "mupdf/fitz.h"
  23. /*
  24. * Write pixmap to PNM file (without alpha channel)
  25. */
  26. static void
  27. pnm_write_header(fz_context *ctx, fz_band_writer *writer, fz_colorspace *cs)
  28. {
  29. fz_output *out = writer->out;
  30. int w = writer->w;
  31. int h = writer->h;
  32. int n = writer->n;
  33. int alpha = writer->alpha;
  34. if (writer->s != 0)
  35. fz_throw(ctx, FZ_ERROR_ARGUMENT, "PNM writer cannot cope with spot colors");
  36. if (cs && !fz_colorspace_is_gray(ctx, cs) && !fz_colorspace_is_rgb(ctx, cs))
  37. fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be grayscale or rgb to write as pnm");
  38. /* Treat alpha only as greyscale */
  39. if (n == 1 && alpha)
  40. alpha = 0;
  41. n -= alpha;
  42. if (alpha)
  43. fz_throw(ctx, FZ_ERROR_ARGUMENT, "PNM writer cannot cope with alpha");
  44. if (n == 1)
  45. fz_write_printf(ctx, out, "P5\n");
  46. if (n == 3)
  47. fz_write_printf(ctx, out, "P6\n");
  48. fz_write_printf(ctx, out, "%d %d\n", w, h);
  49. fz_write_printf(ctx, out, "255\n");
  50. }
  51. static void
  52. pnm_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_start, int band_height, const unsigned char *p)
  53. {
  54. fz_output *out = writer->out;
  55. int w = writer->w;
  56. int h = writer->h;
  57. int n = writer->n;
  58. int len;
  59. int end = band_start + band_height;
  60. if (n != 1 && n != 3)
  61. fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be grayscale or rgb to write as pnm");
  62. if (!out)
  63. return;
  64. if (end > h)
  65. end = h;
  66. end -= band_start;
  67. /* Tests show that writing single bytes out at a time
  68. * is appallingly slow. We get a huge improvement
  69. * by collating stuff into buffers first. */
  70. while (end--)
  71. {
  72. len = w;
  73. while (len)
  74. {
  75. int num_written = len;
  76. switch (n)
  77. {
  78. case 1:
  79. /* No collation required */
  80. fz_write_data(ctx, out, p, num_written);
  81. p += num_written;
  82. break;
  83. case 3:
  84. fz_write_data(ctx, out, p, num_written*3);
  85. p += num_written*3;
  86. break;
  87. }
  88. len -= num_written;
  89. }
  90. p += stride - w*n;
  91. }
  92. }
  93. fz_band_writer *fz_new_pnm_band_writer(fz_context *ctx, fz_output *out)
  94. {
  95. fz_band_writer *writer = fz_new_band_writer(ctx, fz_band_writer, out);
  96. writer->header = pnm_write_header;
  97. writer->band = pnm_write_band;
  98. return writer;
  99. }
  100. void
  101. fz_write_pixmap_as_pnm(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
  102. {
  103. fz_band_writer *writer = fz_new_pnm_band_writer(ctx, out);
  104. fz_try(ctx)
  105. {
  106. fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
  107. fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
  108. fz_close_band_writer(ctx, writer);
  109. }
  110. fz_always(ctx)
  111. fz_drop_band_writer(ctx, writer);
  112. fz_catch(ctx)
  113. fz_rethrow(ctx);
  114. }
  115. void
  116. fz_save_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
  117. {
  118. fz_band_writer *writer = NULL;
  119. fz_output *out = fz_new_output_with_path(ctx, filename, 0);
  120. fz_var(writer);
  121. fz_try(ctx)
  122. {
  123. writer = fz_new_pnm_band_writer(ctx, out);
  124. fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
  125. fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
  126. fz_close_band_writer(ctx, writer);
  127. fz_close_output(ctx, out);
  128. }
  129. fz_always(ctx)
  130. {
  131. fz_drop_band_writer(ctx, writer);
  132. fz_drop_output(ctx, out);
  133. }
  134. fz_catch(ctx)
  135. fz_rethrow(ctx);
  136. }
  137. /*
  138. * Write pixmap to PAM file (with or without alpha channel)
  139. */
  140. static void
  141. pam_write_header(fz_context *ctx, fz_band_writer *writer, fz_colorspace *cs)
  142. {
  143. fz_output *out = writer->out;
  144. int w = writer->w;
  145. int h = writer->h;
  146. int n = writer->n;
  147. int alpha = writer->alpha;
  148. if (writer->s != 0)
  149. fz_throw(ctx, FZ_ERROR_ARGUMENT, "PAM writer cannot cope with spot colors");
  150. fz_write_printf(ctx, out, "P7\n");
  151. fz_write_printf(ctx, out, "WIDTH %d\n", w);
  152. fz_write_printf(ctx, out, "HEIGHT %d\n", h);
  153. fz_write_printf(ctx, out, "DEPTH %d\n", n);
  154. fz_write_printf(ctx, out, "MAXVAL 255\n");
  155. n -= alpha;
  156. if (n == 0 && alpha) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE\n");
  157. else if (n == 1 && !alpha && fz_colorspace_is_gray(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE\n");
  158. else if (n == 1 && alpha && fz_colorspace_is_gray(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE_ALPHA\n");
  159. else if (n == 3 && !alpha && fz_colorspace_is_rgb(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE RGB\n");
  160. else if (n == 3 && alpha && fz_colorspace_is_rgb(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE RGB_ALPHA\n");
  161. else if (n == 4 && !alpha && fz_colorspace_is_cmyk(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE CMYK\n");
  162. else if (n == 4 && alpha && fz_colorspace_is_cmyk(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE CMYK_ALPHA\n");
  163. else
  164. fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be alpha only, gray, rgb, or cmyk");
  165. fz_write_printf(ctx, out, "ENDHDR\n");
  166. }
  167. static void
  168. pam_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_start, int band_height, const unsigned char *sp)
  169. {
  170. fz_output *out = writer->out;
  171. int w = writer->w;
  172. int h = writer->h;
  173. int n = writer->n;
  174. int alpha = writer->alpha;
  175. int x, y;
  176. int end = band_start + band_height;
  177. if (!out)
  178. return;
  179. if (end > h)
  180. end = h;
  181. end -= band_start;
  182. if (alpha)
  183. {
  184. /* Buffer must be a multiple of 2, 3 and 5 at least. */
  185. /* Also, for the generic case, it must be bigger than FZ_MAX_COLORS */
  186. char buffer[2*3*4*5*6];
  187. char *b = buffer;
  188. stride -= n * w;
  189. switch (n)
  190. {
  191. case 2:
  192. for (y = 0; y < end; y++)
  193. {
  194. for (x = 0; x < w; x++)
  195. {
  196. int a = sp[1];
  197. *b++ = a ? (sp[0] * 255 + (a>>1))/a : 0;
  198. *b++ = a;
  199. sp += 2;
  200. if (b == &buffer[sizeof(buffer)])
  201. {
  202. fz_write_data(ctx, out, buffer, sizeof(buffer));
  203. b = buffer;
  204. }
  205. }
  206. sp += stride;
  207. }
  208. if (b != buffer)
  209. fz_write_data(ctx, out, buffer, b - buffer);
  210. break;
  211. case 4:
  212. for (y = 0; y < end; y++)
  213. {
  214. for (x = 0; x < w; x++)
  215. {
  216. int a = sp[3];
  217. int inva = a ? 256 * 255 / a : 0;
  218. *b++ = (sp[0] * inva + 128)>>8;
  219. *b++ = (sp[1] * inva + 128)>>8;
  220. *b++ = (sp[2] * inva + 128)>>8;
  221. *b++ = a;
  222. sp += 4;
  223. if (b == &buffer[sizeof(buffer)])
  224. {
  225. fz_write_data(ctx, out, buffer, sizeof(buffer));
  226. b = buffer;
  227. }
  228. }
  229. sp += stride;
  230. }
  231. if (b != buffer)
  232. fz_write_data(ctx, out, buffer, b - buffer);
  233. break;
  234. case 5:
  235. for (y = 0; y < end; y++)
  236. {
  237. for (x = 0; x < w; x++)
  238. {
  239. int a = sp[4];
  240. int inva = a ? 256 * 255 / a : 0;
  241. *b++ = (sp[0] * inva + 128)>>8;
  242. *b++ = (sp[1] * inva + 128)>>8;
  243. *b++ = (sp[2] * inva + 128)>>8;
  244. *b++ = (sp[3] * inva + 128)>>8;
  245. *b++ = a;
  246. sp += 5;
  247. if (b == &buffer[sizeof(buffer)])
  248. {
  249. fz_write_data(ctx, out, buffer, sizeof(buffer));
  250. b = buffer;
  251. }
  252. }
  253. sp += stride;
  254. }
  255. if (b != buffer)
  256. fz_write_data(ctx, out, buffer, b - buffer);
  257. break;
  258. default:
  259. for (y = 0; y < end; y++)
  260. {
  261. for (x = 0; x < w; x++)
  262. {
  263. int a = sp[n-1];
  264. int inva = a ? 256 * 255 / a : 0;
  265. int k;
  266. for (k = 0; k < n-1; k++)
  267. *b++ = (*sp++ * inva + 128)>>8;
  268. *b++ = a;
  269. sp++;
  270. if (b >= &buffer[sizeof(buffer)] - n)
  271. {
  272. fz_write_data(ctx, out, buffer, b - buffer);
  273. b = buffer;
  274. }
  275. }
  276. sp += stride;
  277. }
  278. if (b != buffer)
  279. fz_write_data(ctx, out, buffer, b - buffer);
  280. break;
  281. }
  282. }
  283. else
  284. for (y = 0; y < end; y++)
  285. {
  286. fz_write_data(ctx, out, sp, (size_t)w * n);
  287. sp += stride;
  288. }
  289. }
  290. fz_band_writer *fz_new_pam_band_writer(fz_context *ctx, fz_output *out)
  291. {
  292. fz_band_writer *writer = fz_new_band_writer(ctx, fz_band_writer, out);
  293. writer->header = pam_write_header;
  294. writer->band = pam_write_band;
  295. return writer;
  296. }
  297. void
  298. fz_write_pixmap_as_pam(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
  299. {
  300. fz_band_writer *writer = fz_new_pam_band_writer(ctx, out);
  301. fz_try(ctx)
  302. {
  303. fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
  304. fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
  305. fz_close_band_writer(ctx, writer);
  306. }
  307. fz_always(ctx)
  308. fz_drop_band_writer(ctx, writer);
  309. fz_catch(ctx)
  310. fz_rethrow(ctx);
  311. }
  312. void
  313. fz_save_pixmap_as_pam(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
  314. {
  315. fz_band_writer *writer = NULL;
  316. fz_output *out = fz_new_output_with_path(ctx, filename, 0);
  317. fz_var(writer);
  318. fz_try(ctx)
  319. {
  320. writer = fz_new_pam_band_writer(ctx, out);
  321. fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
  322. fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
  323. fz_close_band_writer(ctx, writer);
  324. fz_close_output(ctx, out);
  325. }
  326. fz_always(ctx)
  327. {
  328. fz_drop_band_writer(ctx, writer);
  329. fz_drop_output(ctx, out);
  330. }
  331. fz_catch(ctx)
  332. fz_rethrow(ctx);
  333. }
  334. static fz_buffer *
  335. buffer_from_bitmap(fz_context *ctx, fz_bitmap *bitmap, fz_color_params color_params, int drop,
  336. void (*do_write)(fz_context *ctx, fz_output *out, fz_bitmap *bitmap))
  337. {
  338. fz_buffer *buf = NULL;
  339. fz_output *out = NULL;
  340. fz_var(buf);
  341. fz_var(out);
  342. fz_try(ctx)
  343. {
  344. buf = fz_new_buffer(ctx, 1024);
  345. out = fz_new_output_with_buffer(ctx, buf);
  346. do_write(ctx, out, bitmap);
  347. fz_close_output(ctx, out);
  348. }
  349. fz_always(ctx)
  350. {
  351. if (drop)
  352. fz_drop_bitmap(ctx, bitmap);
  353. fz_drop_output(ctx, out);
  354. }
  355. fz_catch(ctx)
  356. {
  357. fz_drop_buffer(ctx, buf);
  358. fz_rethrow(ctx);
  359. }
  360. return buf;
  361. }
  362. static fz_buffer *
  363. buffer_from_pixmap(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params, int drop,
  364. void (*do_write)(fz_context *ctx, fz_output *out, fz_pixmap *pix))
  365. {
  366. fz_buffer *buf = NULL;
  367. fz_output *out = NULL;
  368. fz_var(buf);
  369. fz_var(out);
  370. fz_try(ctx)
  371. {
  372. buf = fz_new_buffer(ctx, 1024);
  373. out = fz_new_output_with_buffer(ctx, buf);
  374. do_write(ctx, out, pix);
  375. fz_close_output(ctx, out);
  376. }
  377. fz_always(ctx)
  378. {
  379. if (drop)
  380. fz_drop_pixmap(ctx, pix);
  381. fz_drop_output(ctx, out);
  382. }
  383. fz_catch(ctx)
  384. {
  385. fz_drop_buffer(ctx, buf);
  386. fz_rethrow(ctx);
  387. }
  388. return buf;
  389. }
  390. fz_buffer *
  391. fz_new_buffer_from_pixmap_as_pbm(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
  392. {
  393. fz_bitmap *bitmap = fz_new_bitmap_from_pixmap(ctx, pix, NULL);
  394. return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pbm);
  395. }
  396. fz_buffer *
  397. fz_new_buffer_from_image_as_pbm(fz_context *ctx, fz_image *image, fz_color_params color_params)
  398. {
  399. fz_bitmap *bitmap = fz_new_bitmap_from_image(ctx, image, NULL);
  400. return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pbm);
  401. }
  402. fz_buffer *
  403. fz_new_buffer_from_pixmap_as_pkm(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
  404. {
  405. fz_bitmap *bitmap = fz_new_bitmap_from_pixmap(ctx, pix, NULL);
  406. return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pkm);
  407. }
  408. fz_buffer *
  409. fz_new_buffer_from_image_as_pkm(fz_context *ctx, fz_image *image, fz_color_params color_params)
  410. {
  411. fz_bitmap *bitmap = fz_new_bitmap_from_image(ctx, image, NULL);
  412. return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pkm);
  413. }
  414. fz_buffer *
  415. fz_new_buffer_from_image_as_pnm(fz_context *ctx, fz_image *image, fz_color_params color_params)
  416. {
  417. fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
  418. return buffer_from_pixmap(ctx, pix, color_params, 1, fz_write_pixmap_as_pnm);
  419. }
  420. fz_buffer *
  421. fz_new_buffer_from_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
  422. {
  423. return buffer_from_pixmap(ctx, pix, color_params, 0, fz_write_pixmap_as_pnm);
  424. }
  425. fz_buffer *
  426. fz_new_buffer_from_image_as_pam(fz_context *ctx, fz_image *image, fz_color_params color_params)
  427. {
  428. fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
  429. return buffer_from_pixmap(ctx, pix, color_params, 1, fz_write_pixmap_as_pam);
  430. }
  431. fz_buffer *
  432. fz_new_buffer_from_pixmap_as_pam(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
  433. {
  434. return buffer_from_pixmap(ctx, pix, color_params, 0, fz_write_pixmap_as_pam);
  435. }