1//========================================================================
2//
3// ImageOutputDev.cc
4//
5// Copyright 1998-2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9//========================================================================
10//
11// Modified under the Poppler project - http://poppler.freedesktop.org
12//
13// All changes made under the Poppler project to this file are licensed
14// under GPL version 2 or later
15//
16// Copyright (C) 2005, 2007, 2011, 2018, 2019, 2021, 2022 Albert Astals Cid <aacid@kde.org>
17// Copyright (C) 2006 Rainer Keller <class321@gmx.de>
18// Copyright (C) 2008 Timothy Lee <timothy.lee@siriushk.com>
19// Copyright (C) 2008 Vasile Gaburici <gaburici@cs.umd.edu>
20// Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
21// Copyright (C) 2009 William Bader <williambader@hotmail.com>
22// Copyright (C) 2010 Jakob Voss <jakob.voss@gbv.de>
23// Copyright (C) 2012, 2013, 2017, 2018 Adrian Johnson <ajohnson@redneon.com>
24// Copyright (C) 2013 Thomas Fischer <fischer@unix-ag.uni-kl.de>
25// Copyright (C) 2013 Hib Eris <hib@hiberis.nl>
26// Copyright (C) 2017 Caolán McNamara <caolanm@redhat.com>
27// Copyright (C) 2018 Andreas Gruenbacher <agruenba@redhat.com>
28// Copyright (C) 2020 mrbax <12640-mrbax@users.noreply.gitlab.freedesktop.org>
29// Copyright (C) 2024 Fernando Herrera <fherrera@onirica.com>
30// Copyright (C) 2024 Sebastian J. Bronner <waschtl@sbronner.com>
31//
32// To see a description of the changes please see the Changelog file that
33// came with your tarball or type make ChangeLog if you are building from git
34//
35//========================================================================
36
37#include "config.h"
38#include <poppler-config.h>
39
40#include <cstdio>
41#include <cstdlib>
42#include <cstddef>
43#include <cctype>
44#include <cmath>
45#include "goo/gmem.h"
46#include "goo/NetPBMWriter.h"
47#include "goo/PNGWriter.h"
48#include "goo/TiffWriter.h"
49#include "Error.h"
50#include "GfxState.h"
51#include "Object.h"
52#include "Stream.h"
53#include "JBIG2Stream.h"
54#include "ImageOutputDev.h"
55
56ImageOutputDev::ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA)
57{
58 listImages = listImagesA;
59 if (!listImages) {
60 fileRoot = copyString(s: fileRootA);
61 fileName = (char *)gmalloc(size: strlen(s: fileRoot) + 45);
62 }
63 outputPNG = false;
64 outputTiff = false;
65 dumpJPEG = false;
66 dumpJP2 = false;
67 dumpJBIG2 = false;
68 dumpCCITT = false;
69 pageNames = pageNamesA;
70 printFilenames = false;
71 imgNum = 0;
72 pageNum = 0;
73 errorCode = 0;
74 if (listImages) {
75 printf(format: "page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio\n");
76 printf(format: "--------------------------------------------------------------------------------------------\n");
77 }
78}
79
80ImageOutputDev::~ImageOutputDev()
81{
82 if (!listImages) {
83 gfree(p: fileName);
84 gfree(p: fileRoot);
85 }
86}
87
88void ImageOutputDev::setFilename(const char *fileExt)
89{
90 if (pageNames) {
91 sprintf(s: fileName, format: "%s-%03d-%03d.%s", fileRoot, pageNum, imgNum, fileExt);
92 } else {
93 sprintf(s: fileName, format: "%s-%03d.%s", fileRoot, imgNum, fileExt);
94 }
95}
96
97// Print a floating point number between 0 - 9999 using 4 characters
98// eg '1.23', '12.3', ' 123', '1234'
99//
100// We need to be careful to handle the cases where rounding adds an
101// extra digit before the decimal. eg printf("%4.2f", 9.99999)
102// outputs "10.00" instead of "9.99".
103static void printNumber(double d)
104{
105 char buf[10];
106
107 if (d < 10.0) {
108 sprintf(s: buf, format: "%4.2f", d);
109 buf[4] = 0;
110 printf(format: "%s", buf);
111 } else if (d < 100.0) {
112 sprintf(s: buf, format: "%4.1f", d);
113 if (!isdigit(buf[3])) {
114 buf[3] = 0;
115 printf(format: " %s", buf);
116 } else {
117 printf(format: "%s", buf);
118 }
119 } else {
120 printf(format: "%4.0f", d);
121 }
122}
123
124void ImageOutputDev::listImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, bool inlineImg, ImageType imageType)
125{
126 const char *type;
127 const char *colorspace;
128 const char *enc;
129 int components, bpc;
130
131 printf(format: "%4d %5d ", pageNum, imgNum);
132 type = "";
133 switch (imageType) {
134 case imgImage:
135 type = "image";
136 break;
137 case imgStencil:
138 type = "stencil";
139 break;
140 case imgMask:
141 type = "mask";
142 break;
143 case imgSmask:
144 type = "smask";
145 break;
146 }
147 printf(format: "%-7s %5d %5d ", type, width, height);
148
149 colorspace = "-";
150 /* masks and stencils default to ncomps = 1 and bpc = 1 */
151 components = 1;
152 bpc = 1;
153 if (colorMap && colorMap->isOk()) {
154 switch (colorMap->getColorSpace()->getMode()) {
155 case csDeviceGray:
156 case csCalGray:
157 colorspace = "gray";
158 break;
159 case csDeviceRGB:
160 case csCalRGB:
161 colorspace = "rgb";
162 break;
163 case csDeviceCMYK:
164 colorspace = "cmyk";
165 break;
166 case csLab:
167 colorspace = "lab";
168 break;
169 case csICCBased:
170 colorspace = "icc";
171 break;
172 case csIndexed:
173 colorspace = "index";
174 break;
175 case csSeparation:
176 colorspace = "sep";
177 break;
178 case csDeviceN:
179 colorspace = "devn";
180 break;
181 case csPattern:
182 default:
183 colorspace = "-";
184 break;
185 }
186 components = colorMap->getNumPixelComps();
187 bpc = colorMap->getBits();
188 }
189 printf(format: "%-5s %2d %2d ", colorspace, components, bpc);
190
191 switch (str->getKind()) {
192 case strCCITTFax:
193 enc = "ccitt";
194 break;
195 case strDCT:
196 enc = "jpeg";
197 break;
198 case strJPX:
199 enc = "jpx";
200 break;
201 case strJBIG2:
202 enc = "jbig2";
203 break;
204 case strFile:
205 case strFlate:
206 case strCachedFile:
207 case strASCIIHex:
208 case strASCII85:
209 case strLZW:
210 case strRunLength:
211 case strWeird:
212 default:
213 enc = "image";
214 break;
215 }
216 printf(format: "%-5s ", enc);
217
218 printf(format: "%-3s ", interpolate ? "yes" : "no");
219
220 if (inlineImg) {
221 printf(format: "[inline] ");
222 } else if (ref->isRef()) {
223 const Ref imageRef = ref->getRef();
224 if (imageRef.gen >= 100000) {
225 printf(format: "[none] ");
226 } else {
227 printf(format: " %6d %2d ", imageRef.num, imageRef.gen);
228 }
229 } else {
230 printf(format: "[none] ");
231 }
232
233 const double *mat = state->getCTM();
234 double width2 = sqrt(x: mat[0] * mat[0] + mat[1] * mat[1]);
235 double height2 = sqrt(x: mat[2] * mat[2] + mat[3] * mat[3]);
236 double xppi = fabs(x: width * 72.0 / width2);
237 double yppi = fabs(x: height * 72.0 / height2);
238 if (xppi < 1.0) {
239 printf(format: "%5.3f ", xppi);
240 } else {
241 printf(format: "%5.0f ", xppi);
242 }
243 if (yppi < 1.0) {
244 printf(format: "%5.3f ", yppi);
245 } else {
246 printf(format: "%5.0f ", yppi);
247 }
248
249 Goffset embedSize = -1;
250 if (inlineImg) {
251 embedSize = getInlineImageLength(str, width, height, colorMap);
252 } else {
253 embedSize = str->getBaseStream()->getLength();
254 }
255
256 long long imageSize = 0;
257 if (colorMap && colorMap->isOk()) {
258 imageSize = ((long long)width * height * colorMap->getNumPixelComps() * colorMap->getBits()) / 8;
259 } else {
260 imageSize = (long long)width * height / 8; // mask
261 }
262
263 double ratio = -1.0;
264 if (imageSize > 0) {
265 ratio = 100.0 * embedSize / imageSize;
266 }
267
268 if (embedSize < 0) {
269 printf(format: " - ");
270 } else if (embedSize <= 9999) {
271 printf(format: "%4lldB", embedSize);
272 } else {
273 double d = embedSize / 1024.0;
274 if (d <= 9999.0) {
275 printNumber(d);
276 putchar(c: 'K');
277 } else {
278 d /= 1024.0;
279 if (d <= 9999.0) {
280 printNumber(d);
281 putchar(c: 'M');
282 } else {
283 d /= 1024.0;
284 printNumber(d);
285 putchar(c: 'G');
286 }
287 }
288 }
289
290 if (ratio > 9.9) {
291 printf(format: " %3.0f%%\n", ratio);
292 } else if (ratio >= 0.0) {
293 printf(format: " %3.1f%%\n", ratio);
294 } else {
295 printf(format: " - \n");
296 }
297
298 ++imgNum;
299}
300
301long ImageOutputDev::getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap)
302{
303 long len;
304
305 if (colorMap) {
306 ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits());
307 imgStr->reset();
308 for (int y = 0; y < height; y++) {
309 imgStr->getLine();
310 }
311
312 imgStr->close();
313 delete imgStr;
314 } else {
315 str->reset();
316 for (int y = 0; y < height; y++) {
317 int size = (width + 7) / 8;
318 for (int x = 0; x < size; x++) {
319 str->getChar();
320 }
321 }
322 }
323
324 EmbedStream *embedStr = (EmbedStream *)(str->getBaseStream());
325 embedStr->rewind();
326 len = 0;
327 while (embedStr->getChar() != EOF) {
328 len++;
329 }
330
331 embedStr->restore();
332
333 return len;
334}
335
336void ImageOutputDev::writeRawImage(Stream *str, const char *ext)
337{
338 FILE *f;
339 int c;
340
341 // open the image file
342 setFilename(ext);
343 ++imgNum;
344 if (!(f = fopen(filename: fileName, modes: "wb"))) {
345 error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'", fileName);
346 errorCode = 2;
347 return;
348 }
349
350 // initialize stream
351 str = str->getNextStream();
352 str->reset();
353
354 // copy the stream
355 while ((c = str->getChar()) != EOF) {
356 fputc(c: c, stream: f);
357 }
358
359 str->close();
360 fclose(stream: f);
361}
362
363void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, Stream *str, int width, int height, GfxImageColorMap *colorMap)
364{
365 FILE *f = nullptr; /* squelch bogus compiler warning */
366 ImageStream *imgStr = nullptr;
367 unsigned char *row;
368 unsigned char *rowp;
369 unsigned char *p;
370 GfxRGB rgb;
371 GfxCMYK cmyk;
372 GfxGray gray;
373 unsigned char zero[gfxColorMaxComps];
374 int invert_bits;
375
376 if (writer) {
377 setFilename(ext);
378 ++imgNum;
379 if (!(f = fopen(filename: fileName, modes: "wb"))) {
380 error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'", fileName);
381 errorCode = 2;
382 return;
383 }
384
385 if (!writer->init(f, width, height, hDPI: 72, vDPI: 72)) {
386 error(category: errIO, pos: -1, msg: "Error writing '{0:s}'", fileName);
387 errorCode = 2;
388 return;
389 }
390 }
391
392 int pixelSize = sizeof(unsigned int);
393 if (format == imgRGB48) {
394 pixelSize = 2 * sizeof(unsigned int);
395 }
396
397 row = (unsigned char *)gmallocn_checkoverflow(count: width, size: pixelSize);
398 if (!row) {
399 error(category: errIO, pos: -1, msg: "Image data for '{0:s}' is too big. {1:d} width with {2:d} bytes per pixel", fileName, width, pixelSize);
400 errorCode = 99;
401 return;
402 }
403
404 if (format != imgMonochrome) {
405 // initialize stream
406 imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits());
407 imgStr->reset();
408 } else {
409 // initialize stream
410 str->reset();
411 }
412
413 // PDF masks use 0 = draw current color, 1 = leave unchanged.
414 // We invert this to provide the standard interpretation of alpha
415 // (0 = transparent, 1 = opaque). If the colorMap already inverts
416 // the mask we leave the data unchanged.
417 invert_bits = 0xff;
418 if (colorMap) {
419 memset(s: zero, c: 0, n: sizeof(zero));
420 colorMap->getGray(x: zero, gray: &gray);
421 if (colToByte(x: gray) == 0) {
422 invert_bits = 0x00;
423 }
424 }
425
426 // for each line...
427 for (int y = 0; y < height; y++) {
428 switch (format) {
429 case imgRGB:
430 p = imgStr->getLine();
431 rowp = row;
432 for (int x = 0; x < width; ++x) {
433 if (p) {
434 colorMap->getRGB(x: p, rgb: &rgb);
435 *rowp++ = colToByte(x: rgb.r);
436 *rowp++ = colToByte(x: rgb.g);
437 *rowp++ = colToByte(x: rgb.b);
438 p += colorMap->getNumPixelComps();
439 } else {
440 *rowp++ = 0;
441 *rowp++ = 0;
442 *rowp++ = 0;
443 }
444 }
445 if (writer) {
446 writer->writeRow(row: &row);
447 }
448 break;
449
450 case imgRGB48: {
451 p = imgStr->getLine();
452 unsigned short *rowp16 = reinterpret_cast<unsigned short *>(row);
453 for (int x = 0; x < width; ++x) {
454 if (p) {
455 colorMap->getRGB(x: p, rgb: &rgb);
456 *rowp16++ = colToShort(x: rgb.r);
457 *rowp16++ = colToShort(x: rgb.g);
458 *rowp16++ = colToShort(x: rgb.b);
459 p += colorMap->getNumPixelComps();
460 } else {
461 *rowp16++ = 0;
462 *rowp16++ = 0;
463 *rowp16++ = 0;
464 }
465 }
466 if (writer) {
467 writer->writeRow(row: &row);
468 }
469 break;
470 }
471
472 case imgCMYK:
473 p = imgStr->getLine();
474 rowp = row;
475 for (int x = 0; x < width; ++x) {
476 if (p) {
477 colorMap->getCMYK(x: p, cmyk: &cmyk);
478 *rowp++ = colToByte(x: cmyk.c);
479 *rowp++ = colToByte(x: cmyk.m);
480 *rowp++ = colToByte(x: cmyk.y);
481 *rowp++ = colToByte(x: cmyk.k);
482 p += colorMap->getNumPixelComps();
483 } else {
484 *rowp++ = 0;
485 *rowp++ = 0;
486 *rowp++ = 0;
487 *rowp++ = 0;
488 }
489 }
490 if (writer) {
491 writer->writeRow(row: &row);
492 }
493 break;
494
495 case imgGray:
496 p = imgStr->getLine();
497 rowp = row;
498 for (int x = 0; x < width; ++x) {
499 if (p) {
500 colorMap->getGray(x: p, gray: &gray);
501 *rowp++ = colToByte(x: gray);
502 p += colorMap->getNumPixelComps();
503 } else {
504 *rowp++ = 0;
505 }
506 }
507 if (writer) {
508 writer->writeRow(row: &row);
509 }
510 break;
511
512 case imgMonochrome:
513 int size = (width + 7) / 8;
514 for (int x = 0; x < size; x++) {
515 row[x] = str->getChar() ^ invert_bits;
516 }
517 if (writer) {
518 writer->writeRow(row: &row);
519 }
520 break;
521 }
522 }
523
524 gfree(p: row);
525 if (format != imgMonochrome) {
526 imgStr->close();
527 delete imgStr;
528 }
529 str->close();
530 if (writer) {
531 writer->close();
532 fclose(stream: f);
533 }
534}
535
536void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool inlineImg)
537{
538 ImageFormat format;
539 EmbedStream *embedStr;
540
541 if (inlineImg) {
542 embedStr = (EmbedStream *)(str->getBaseStream());
543 // Record the stream. This determines the size.
544 getInlineImageLength(str, width, height, colorMap);
545 // Reading the stream again will return EOF at end of recording.
546 embedStr->rewind();
547 }
548
549 if (dumpJPEG && str->getKind() == strDCT) {
550 // dump JPEG file
551 writeRawImage(str, ext: "jpg");
552
553 } else if (dumpJP2 && str->getKind() == strJPX && !inlineImg) {
554 // dump JPEG2000 file
555 writeRawImage(str, ext: "jp2");
556
557 } else if (dumpJBIG2 && str->getKind() == strJBIG2 && !inlineImg) {
558 // dump JBIG2 globals stream if available
559 JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str);
560 Object *globals = jb2Str->getGlobalsStream();
561 if (globals->isStream()) {
562 FILE *f;
563 int c;
564 Stream *globalsStr = globals->getStream();
565
566 setFilename("jb2g");
567 if (!(f = fopen(filename: fileName, modes: "wb"))) {
568 error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'", fileName);
569 errorCode = 2;
570 return;
571 }
572 globalsStr->reset();
573 while ((c = globalsStr->getChar()) != EOF) {
574 fputc(c: c, stream: f);
575 }
576 globalsStr->close();
577 fclose(stream: f);
578 }
579
580 // dump JBIG2 embedded file
581 writeRawImage(str, ext: "jb2e");
582
583 } else if (dumpCCITT && str->getKind() == strCCITTFax) {
584 // write CCITT parameters
585 CCITTFaxStream *ccittStr = static_cast<CCITTFaxStream *>(str);
586 FILE *f;
587 setFilename("params");
588 if (!(f = fopen(filename: fileName, modes: "wb"))) {
589 error(category: errIO, pos: -1, msg: "Couldn't open image file '{0:s}'", fileName);
590 errorCode = 2;
591 return;
592 }
593 if (ccittStr->getEncoding() < 0) {
594 fprintf(stream: f, format: "-4 ");
595 } else if (ccittStr->getEncoding() == 0) {
596 fprintf(stream: f, format: "-1 ");
597 } else {
598 fprintf(stream: f, format: "-2 ");
599 }
600
601 if (ccittStr->getEndOfLine()) {
602 fprintf(stream: f, format: "-A ");
603 } else {
604 fprintf(stream: f, format: "-P ");
605 }
606
607 fprintf(stream: f, format: "-X %d ", ccittStr->getColumns());
608
609 if (ccittStr->getBlackIs1()) {
610 fprintf(stream: f, format: "-W ");
611 } else {
612 fprintf(stream: f, format: "-B ");
613 }
614
615 fprintf(stream: f, format: "-M\n"); // PDF uses MSB first
616
617 fclose(stream: f);
618
619 // dump CCITT file
620 writeRawImage(str, ext: "ccitt");
621
622 } else if (outputPNG && !(outputTiff && colorMap && (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)))) {
623 // output in PNG format
624
625#ifdef ENABLE_LIBPNG
626 ImgWriter *writer;
627
628 if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) {
629 writer = new PNGWriter(PNGWriter::MONOCHROME);
630 format = imgMonochrome;
631 } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) {
632 writer = new PNGWriter(PNGWriter::GRAY);
633 format = imgGray;
634 } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3))
635 && colorMap->getBits() > 8) {
636 writer = new PNGWriter(PNGWriter::RGB48);
637 format = imgRGB48;
638 } else {
639 writer = new PNGWriter(PNGWriter::RGB);
640 format = imgRGB;
641 }
642
643 writeImageFile(writer, format, ext: "png", str, width, height, colorMap);
644
645 delete writer;
646#endif
647 } else if (outputTiff) {
648 // output in TIFF format
649
650#ifdef ENABLE_LIBTIFF
651 ImgWriter *writer;
652
653 if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) {
654 writer = new TiffWriter(TiffWriter::MONOCHROME);
655 format = imgMonochrome;
656 } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) {
657 writer = new TiffWriter(TiffWriter::GRAY);
658 format = imgGray;
659 } else if (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)) {
660 writer = new TiffWriter(TiffWriter::CMYK);
661 format = imgCMYK;
662 } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3))
663 && colorMap->getBits() > 8) {
664 writer = new TiffWriter(TiffWriter::RGB48);
665 format = imgRGB48;
666 } else {
667 writer = new TiffWriter(TiffWriter::RGB);
668 format = imgRGB;
669 }
670
671 writeImageFile(writer, format, ext: "tif", str, width, height, colorMap);
672
673 delete writer;
674#endif
675 } else {
676 // output in PPM/PBM format
677 ImgWriter *writer;
678
679 if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) {
680 writer = new NetPBMWriter(NetPBMWriter::MONOCHROME);
681 format = imgMonochrome;
682 } else {
683 writer = new NetPBMWriter(NetPBMWriter::RGB);
684 format = imgRGB;
685 }
686
687 writeImageFile(writer, format, ext: format == imgRGB ? "ppm" : "pbm", str, width, height, colorMap);
688
689 delete writer;
690 }
691
692 if (inlineImg) {
693 embedStr->restore();
694 }
695
696 if (printFilenames) {
697 printf(format: "%s\n", fileName);
698 }
699}
700
701bool ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, GfxTilingPattern *tPat, const double *mat, int x0, int y0, int x1, int y1, double xStep, double yStep)
702{
703 return true;
704 // do nothing -- this avoids the potentially slow loop in Gfx.cc
705}
706
707void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg)
708{
709 if (listImages) {
710 listImage(state, ref, str, width, height, colorMap: nullptr, interpolate, inlineImg, imageType: imgStencil);
711 } else {
712 writeImage(state, ref, str, width, height, colorMap: nullptr, inlineImg);
713 }
714}
715
716void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg)
717{
718 if (listImages) {
719 listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg, imageType: imgImage);
720 } else {
721 writeImage(state, ref, str, width, height, colorMap, inlineImg);
722 }
723}
724
725void ImageOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, bool maskInvert, bool maskInterpolate)
726{
727 if (listImages) {
728 listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg: false, imageType: imgImage);
729 listImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: nullptr, interpolate: maskInterpolate, inlineImg: false, imageType: imgMask);
730 } else {
731 writeImage(state, ref, str, width, height, colorMap, inlineImg: false);
732 writeImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: nullptr, inlineImg: false);
733 }
734}
735
736void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap,
737 bool maskInterpolate)
738{
739 if (listImages) {
740 listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg: false, imageType: imgImage);
741 listImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: maskColorMap, interpolate: maskInterpolate, inlineImg: false, imageType: imgSmask);
742 } else {
743 writeImage(state, ref, str, width, height, colorMap, inlineImg: false);
744 writeImage(state, ref, str: maskStr, width: maskWidth, height: maskHeight, colorMap: maskColorMap, inlineImg: false);
745 }
746}
747

source code of poppler/utils/ImageOutputDev.cc