comparison mupdf-source/source/fitz/output-pnm.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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
23 #include "mupdf/fitz.h"
24
25 /*
26 * Write pixmap to PNM file (without alpha channel)
27 */
28 static void
29 pnm_write_header(fz_context *ctx, fz_band_writer *writer, fz_colorspace *cs)
30 {
31 fz_output *out = writer->out;
32 int w = writer->w;
33 int h = writer->h;
34 int n = writer->n;
35 int alpha = writer->alpha;
36
37 if (writer->s != 0)
38 fz_throw(ctx, FZ_ERROR_ARGUMENT, "PNM writer cannot cope with spot colors");
39 if (cs && !fz_colorspace_is_gray(ctx, cs) && !fz_colorspace_is_rgb(ctx, cs))
40 fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be grayscale or rgb to write as pnm");
41
42 /* Treat alpha only as greyscale */
43 if (n == 1 && alpha)
44 alpha = 0;
45 n -= alpha;
46
47 if (alpha)
48 fz_throw(ctx, FZ_ERROR_ARGUMENT, "PNM writer cannot cope with alpha");
49
50 if (n == 1)
51 fz_write_printf(ctx, out, "P5\n");
52 if (n == 3)
53 fz_write_printf(ctx, out, "P6\n");
54 fz_write_printf(ctx, out, "%d %d\n", w, h);
55 fz_write_printf(ctx, out, "255\n");
56 }
57
58 static void
59 pnm_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_start, int band_height, const unsigned char *p)
60 {
61 fz_output *out = writer->out;
62 int w = writer->w;
63 int h = writer->h;
64 int n = writer->n;
65 int len;
66 int end = band_start + band_height;
67
68 if (n != 1 && n != 3)
69 fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be grayscale or rgb to write as pnm");
70
71 if (!out)
72 return;
73
74 if (end > h)
75 end = h;
76 end -= band_start;
77
78 /* Tests show that writing single bytes out at a time
79 * is appallingly slow. We get a huge improvement
80 * by collating stuff into buffers first. */
81
82 while (end--)
83 {
84 len = w;
85 while (len)
86 {
87 int num_written = len;
88
89 switch (n)
90 {
91 case 1:
92 /* No collation required */
93 fz_write_data(ctx, out, p, num_written);
94 p += num_written;
95 break;
96 case 3:
97 fz_write_data(ctx, out, p, num_written*3);
98 p += num_written*3;
99 break;
100 }
101 len -= num_written;
102 }
103 p += stride - w*n;
104 }
105 }
106
107 fz_band_writer *fz_new_pnm_band_writer(fz_context *ctx, fz_output *out)
108 {
109 fz_band_writer *writer = fz_new_band_writer(ctx, fz_band_writer, out);
110
111 writer->header = pnm_write_header;
112 writer->band = pnm_write_band;
113
114 return writer;
115 }
116
117 void
118 fz_write_pixmap_as_pnm(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
119 {
120 fz_band_writer *writer = fz_new_pnm_band_writer(ctx, out);
121 fz_try(ctx)
122 {
123 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
124 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
125 fz_close_band_writer(ctx, writer);
126 }
127 fz_always(ctx)
128 fz_drop_band_writer(ctx, writer);
129 fz_catch(ctx)
130 fz_rethrow(ctx);
131 }
132
133 void
134 fz_save_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
135 {
136 fz_band_writer *writer = NULL;
137 fz_output *out = fz_new_output_with_path(ctx, filename, 0);
138
139 fz_var(writer);
140
141 fz_try(ctx)
142 {
143 writer = fz_new_pnm_band_writer(ctx, out);
144 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
145 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
146 fz_close_band_writer(ctx, writer);
147 fz_close_output(ctx, out);
148 }
149 fz_always(ctx)
150 {
151 fz_drop_band_writer(ctx, writer);
152 fz_drop_output(ctx, out);
153 }
154 fz_catch(ctx)
155 fz_rethrow(ctx);
156 }
157
158 /*
159 * Write pixmap to PAM file (with or without alpha channel)
160 */
161
162 static void
163 pam_write_header(fz_context *ctx, fz_band_writer *writer, fz_colorspace *cs)
164 {
165 fz_output *out = writer->out;
166 int w = writer->w;
167 int h = writer->h;
168 int n = writer->n;
169 int alpha = writer->alpha;
170
171 if (writer->s != 0)
172 fz_throw(ctx, FZ_ERROR_ARGUMENT, "PAM writer cannot cope with spot colors");
173
174 fz_write_printf(ctx, out, "P7\n");
175 fz_write_printf(ctx, out, "WIDTH %d\n", w);
176 fz_write_printf(ctx, out, "HEIGHT %d\n", h);
177 fz_write_printf(ctx, out, "DEPTH %d\n", n);
178 fz_write_printf(ctx, out, "MAXVAL 255\n");
179
180 n -= alpha;
181
182 if (n == 0 && alpha) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE\n");
183 else if (n == 1 && !alpha && fz_colorspace_is_gray(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE\n");
184 else if (n == 1 && alpha && fz_colorspace_is_gray(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE GRAYSCALE_ALPHA\n");
185 else if (n == 3 && !alpha && fz_colorspace_is_rgb(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE RGB\n");
186 else if (n == 3 && alpha && fz_colorspace_is_rgb(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE RGB_ALPHA\n");
187 else if (n == 4 && !alpha && fz_colorspace_is_cmyk(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE CMYK\n");
188 else if (n == 4 && alpha && fz_colorspace_is_cmyk(ctx, cs)) fz_write_printf(ctx, out, "TUPLTYPE CMYK_ALPHA\n");
189 else
190 fz_throw(ctx, FZ_ERROR_ARGUMENT, "pixmap must be alpha only, gray, rgb, or cmyk");
191 fz_write_printf(ctx, out, "ENDHDR\n");
192 }
193
194 static void
195 pam_write_band(fz_context *ctx, fz_band_writer *writer, int stride, int band_start, int band_height, const unsigned char *sp)
196 {
197 fz_output *out = writer->out;
198 int w = writer->w;
199 int h = writer->h;
200 int n = writer->n;
201 int alpha = writer->alpha;
202 int x, y;
203 int end = band_start + band_height;
204
205 if (!out)
206 return;
207
208 if (end > h)
209 end = h;
210 end -= band_start;
211
212 if (alpha)
213 {
214 /* Buffer must be a multiple of 2, 3 and 5 at least. */
215 /* Also, for the generic case, it must be bigger than FZ_MAX_COLORS */
216 char buffer[2*3*4*5*6];
217 char *b = buffer;
218 stride -= n * w;
219 switch (n)
220 {
221 case 2:
222 for (y = 0; y < end; y++)
223 {
224 for (x = 0; x < w; x++)
225 {
226 int a = sp[1];
227 *b++ = a ? (sp[0] * 255 + (a>>1))/a : 0;
228 *b++ = a;
229 sp += 2;
230 if (b == &buffer[sizeof(buffer)])
231 {
232 fz_write_data(ctx, out, buffer, sizeof(buffer));
233 b = buffer;
234 }
235 }
236 sp += stride;
237 }
238 if (b != buffer)
239 fz_write_data(ctx, out, buffer, b - buffer);
240 break;
241 case 4:
242 for (y = 0; y < end; y++)
243 {
244 for (x = 0; x < w; x++)
245 {
246 int a = sp[3];
247 int inva = a ? 256 * 255 / a : 0;
248 *b++ = (sp[0] * inva + 128)>>8;
249 *b++ = (sp[1] * inva + 128)>>8;
250 *b++ = (sp[2] * inva + 128)>>8;
251 *b++ = a;
252 sp += 4;
253 if (b == &buffer[sizeof(buffer)])
254 {
255 fz_write_data(ctx, out, buffer, sizeof(buffer));
256 b = buffer;
257 }
258 }
259 sp += stride;
260 }
261 if (b != buffer)
262 fz_write_data(ctx, out, buffer, b - buffer);
263 break;
264 case 5:
265 for (y = 0; y < end; y++)
266 {
267 for (x = 0; x < w; x++)
268 {
269 int a = sp[4];
270 int inva = a ? 256 * 255 / a : 0;
271 *b++ = (sp[0] * inva + 128)>>8;
272 *b++ = (sp[1] * inva + 128)>>8;
273 *b++ = (sp[2] * inva + 128)>>8;
274 *b++ = (sp[3] * inva + 128)>>8;
275 *b++ = a;
276 sp += 5;
277 if (b == &buffer[sizeof(buffer)])
278 {
279 fz_write_data(ctx, out, buffer, sizeof(buffer));
280 b = buffer;
281 }
282 }
283 sp += stride;
284 }
285 if (b != buffer)
286 fz_write_data(ctx, out, buffer, b - buffer);
287 break;
288 default:
289 for (y = 0; y < end; y++)
290 {
291 for (x = 0; x < w; x++)
292 {
293 int a = sp[n-1];
294 int inva = a ? 256 * 255 / a : 0;
295 int k;
296 for (k = 0; k < n-1; k++)
297 *b++ = (*sp++ * inva + 128)>>8;
298 *b++ = a;
299 sp++;
300 if (b >= &buffer[sizeof(buffer)] - n)
301 {
302 fz_write_data(ctx, out, buffer, b - buffer);
303 b = buffer;
304 }
305 }
306 sp += stride;
307 }
308 if (b != buffer)
309 fz_write_data(ctx, out, buffer, b - buffer);
310 break;
311 }
312 }
313 else
314 for (y = 0; y < end; y++)
315 {
316 fz_write_data(ctx, out, sp, (size_t)w * n);
317 sp += stride;
318 }
319 }
320
321 fz_band_writer *fz_new_pam_band_writer(fz_context *ctx, fz_output *out)
322 {
323 fz_band_writer *writer = fz_new_band_writer(ctx, fz_band_writer, out);
324
325 writer->header = pam_write_header;
326 writer->band = pam_write_band;
327
328 return writer;
329 }
330
331 void
332 fz_write_pixmap_as_pam(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
333 {
334 fz_band_writer *writer = fz_new_pam_band_writer(ctx, out);
335 fz_try(ctx)
336 {
337 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
338 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
339 fz_close_band_writer(ctx, writer);
340 }
341 fz_always(ctx)
342 fz_drop_band_writer(ctx, writer);
343 fz_catch(ctx)
344 fz_rethrow(ctx);
345 }
346
347 void
348 fz_save_pixmap_as_pam(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
349 {
350 fz_band_writer *writer = NULL;
351 fz_output *out = fz_new_output_with_path(ctx, filename, 0);
352
353 fz_var(writer);
354
355 fz_try(ctx)
356 {
357 writer = fz_new_pam_band_writer(ctx, out);
358 fz_write_header(ctx, writer, pixmap->w, pixmap->h, pixmap->n, pixmap->alpha, 0, 0, 0, pixmap->colorspace, pixmap->seps);
359 fz_write_band(ctx, writer, pixmap->stride, pixmap->h, pixmap->samples);
360 fz_close_band_writer(ctx, writer);
361 fz_close_output(ctx, out);
362 }
363 fz_always(ctx)
364 {
365 fz_drop_band_writer(ctx, writer);
366 fz_drop_output(ctx, out);
367 }
368 fz_catch(ctx)
369 fz_rethrow(ctx);
370 }
371
372 static fz_buffer *
373 buffer_from_bitmap(fz_context *ctx, fz_bitmap *bitmap, fz_color_params color_params, int drop,
374 void (*do_write)(fz_context *ctx, fz_output *out, fz_bitmap *bitmap))
375 {
376 fz_buffer *buf = NULL;
377 fz_output *out = NULL;
378
379 fz_var(buf);
380 fz_var(out);
381
382 fz_try(ctx)
383 {
384 buf = fz_new_buffer(ctx, 1024);
385 out = fz_new_output_with_buffer(ctx, buf);
386 do_write(ctx, out, bitmap);
387 fz_close_output(ctx, out);
388 }
389 fz_always(ctx)
390 {
391 if (drop)
392 fz_drop_bitmap(ctx, bitmap);
393 fz_drop_output(ctx, out);
394 }
395 fz_catch(ctx)
396 {
397 fz_drop_buffer(ctx, buf);
398 fz_rethrow(ctx);
399 }
400 return buf;
401 }
402
403 static fz_buffer *
404 buffer_from_pixmap(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params, int drop,
405 void (*do_write)(fz_context *ctx, fz_output *out, fz_pixmap *pix))
406 {
407 fz_buffer *buf = NULL;
408 fz_output *out = NULL;
409
410 fz_var(buf);
411 fz_var(out);
412
413 fz_try(ctx)
414 {
415 buf = fz_new_buffer(ctx, 1024);
416 out = fz_new_output_with_buffer(ctx, buf);
417 do_write(ctx, out, pix);
418 fz_close_output(ctx, out);
419 }
420 fz_always(ctx)
421 {
422 if (drop)
423 fz_drop_pixmap(ctx, pix);
424 fz_drop_output(ctx, out);
425 }
426 fz_catch(ctx)
427 {
428 fz_drop_buffer(ctx, buf);
429 fz_rethrow(ctx);
430 }
431 return buf;
432 }
433
434 fz_buffer *
435 fz_new_buffer_from_pixmap_as_pbm(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
436 {
437 fz_bitmap *bitmap = fz_new_bitmap_from_pixmap(ctx, pix, NULL);
438 return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pbm);
439 }
440
441 fz_buffer *
442 fz_new_buffer_from_image_as_pbm(fz_context *ctx, fz_image *image, fz_color_params color_params)
443 {
444 fz_bitmap *bitmap = fz_new_bitmap_from_image(ctx, image, NULL);
445 return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pbm);
446 }
447
448 fz_buffer *
449 fz_new_buffer_from_pixmap_as_pkm(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
450 {
451 fz_bitmap *bitmap = fz_new_bitmap_from_pixmap(ctx, pix, NULL);
452 return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pkm);
453 }
454
455 fz_buffer *
456 fz_new_buffer_from_image_as_pkm(fz_context *ctx, fz_image *image, fz_color_params color_params)
457 {
458 fz_bitmap *bitmap = fz_new_bitmap_from_image(ctx, image, NULL);
459 return buffer_from_bitmap(ctx, bitmap, color_params, 1, fz_write_bitmap_as_pkm);
460 }
461
462 fz_buffer *
463 fz_new_buffer_from_image_as_pnm(fz_context *ctx, fz_image *image, fz_color_params color_params)
464 {
465 fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
466 return buffer_from_pixmap(ctx, pix, color_params, 1, fz_write_pixmap_as_pnm);
467 }
468
469 fz_buffer *
470 fz_new_buffer_from_pixmap_as_pnm(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
471 {
472 return buffer_from_pixmap(ctx, pix, color_params, 0, fz_write_pixmap_as_pnm);
473 }
474
475 fz_buffer *
476 fz_new_buffer_from_image_as_pam(fz_context *ctx, fz_image *image, fz_color_params color_params)
477 {
478 fz_pixmap *pix = fz_get_pixmap_from_image(ctx, image, NULL, NULL, NULL, NULL);
479 return buffer_from_pixmap(ctx, pix, color_params, 1, fz_write_pixmap_as_pam);
480 }
481
482 fz_buffer *
483 fz_new_buffer_from_pixmap_as_pam(fz_context *ctx, fz_pixmap *pix, fz_color_params color_params)
484 {
485 return buffer_from_pixmap(ctx, pix, color_params, 0, fz_write_pixmap_as_pam);
486 }