Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
