| //======================================================================== |
| // |
| // ImageOutputDev.cc |
| // |
| // Copyright 1998-2003 Glyph & Cog, LLC |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2005, 2007, 2011, 2018, 2019, 2021, 2022 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2006 Rainer Keller <class321@gmx.de> |
| // Copyright (C) 2008 Timothy Lee <timothy.lee@siriushk.com> |
| // Copyright (C) 2008 Vasile Gaburici <gaburici@cs.umd.edu> |
| // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org> |
| // Copyright (C) 2009 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2010 Jakob Voss <jakob.voss@gbv.de> |
| // Copyright (C) 2012, 2013, 2017, 2018 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2013 Thomas Fischer <fischer@unix-ag.uni-kl.de> |
| // Copyright (C) 2013 Hib Eris <hib@hiberis.nl> |
| // Copyright (C) 2017 Caolán McNamara <caolanm@redhat.com> |
| // Copyright (C) 2018 Andreas Gruenbacher <agruenba@redhat.com> |
| // Copyright (C) 2020 mrbax <12640-mrbax@users.noreply.gitlab.freedesktop.org> |
| // Copyright (C) 2024 Fernando Herrera <fherrera@onirica.com> |
| // Copyright (C) 2024 Sebastian J. Bronner <waschtl@sbronner.com> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include "config.h" |
| #include <poppler-config.h> |
| |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstddef> |
| #include <cctype> |
| #include <cmath> |
| #include "goo/gmem.h" |
| #include "goo/NetPBMWriter.h" |
| #include "goo/PNGWriter.h" |
| #include "goo/TiffWriter.h" |
| #include "Error.h" |
| #include "GfxState.h" |
| #include "Object.h" |
| #include "Stream.h" |
| #include "JBIG2Stream.h" |
| #include "ImageOutputDev.h" |
| |
| ImageOutputDev::ImageOutputDev(char *fileRootA, bool pageNamesA, bool listImagesA) |
| { |
| listImages = listImagesA; |
| if (!listImages) { |
| fileRoot = copyString(fileRootA); |
| fileName = (char *)gmalloc(strlen(fileRoot) + 45); |
| } |
| outputPNG = false; |
| outputTiff = false; |
| dumpJPEG = false; |
| dumpJP2 = false; |
| dumpJBIG2 = false; |
| dumpCCITT = false; |
| pageNames = pageNamesA; |
| printFilenames = false; |
| imgNum = 0; |
| pageNum = 0; |
| errorCode = 0; |
| if (listImages) { |
| printf("page num type width height color comp bpc enc interp object ID x-ppi y-ppi size ratio\n"); |
| printf("--------------------------------------------------------------------------------------------\n"); |
| } |
| } |
| |
| ImageOutputDev::~ImageOutputDev() |
| { |
| if (!listImages) { |
| gfree(fileName); |
| gfree(fileRoot); |
| } |
| } |
| |
| void ImageOutputDev::setFilename(const char *fileExt) |
| { |
| if (pageNames) { |
| sprintf(fileName, "%s-%03d-%03d.%s", fileRoot, pageNum, imgNum, fileExt); |
| } else { |
| sprintf(fileName, "%s-%03d.%s", fileRoot, imgNum, fileExt); |
| } |
| } |
| |
| // Print a floating point number between 0 - 9999 using 4 characters |
| // eg '1.23', '12.3', ' 123', '1234' |
| // |
| // We need to be careful to handle the cases where rounding adds an |
| // extra digit before the decimal. eg printf("%4.2f", 9.99999) |
| // outputs "10.00" instead of "9.99". |
| static void printNumber(double d) |
| { |
| char buf[10]; |
| |
| if (d < 10.0) { |
| sprintf(buf, "%4.2f", d); |
| buf[4] = 0; |
| printf("%s", buf); |
| } else if (d < 100.0) { |
| sprintf(buf, "%4.1f", d); |
| if (!isdigit(buf[3])) { |
| buf[3] = 0; |
| printf(" %s", buf); |
| } else { |
| printf("%s", buf); |
| } |
| } else { |
| printf("%4.0f", d); |
| } |
| } |
| |
| void ImageOutputDev::listImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, bool inlineImg, ImageType imageType) |
| { |
| const char *type; |
| const char *colorspace; |
| const char *enc; |
| int components, bpc; |
| |
| printf("%4d %5d ", pageNum, imgNum); |
| type = ""; |
| switch (imageType) { |
| case imgImage: |
| type = "image"; |
| break; |
| case imgStencil: |
| type = "stencil"; |
| break; |
| case imgMask: |
| type = "mask"; |
| break; |
| case imgSmask: |
| type = "smask"; |
| break; |
| } |
| printf("%-7s %5d %5d ", type, width, height); |
| |
| colorspace = "-"; |
| /* masks and stencils default to ncomps = 1 and bpc = 1 */ |
| components = 1; |
| bpc = 1; |
| if (colorMap && colorMap->isOk()) { |
| switch (colorMap->getColorSpace()->getMode()) { |
| case csDeviceGray: |
| case csCalGray: |
| colorspace = "gray"; |
| break; |
| case csDeviceRGB: |
| case csCalRGB: |
| colorspace = "rgb"; |
| break; |
| case csDeviceCMYK: |
| colorspace = "cmyk"; |
| break; |
| case csLab: |
| colorspace = "lab"; |
| break; |
| case csICCBased: |
| colorspace = "icc"; |
| break; |
| case csIndexed: |
| colorspace = "index"; |
| break; |
| case csSeparation: |
| colorspace = "sep"; |
| break; |
| case csDeviceN: |
| colorspace = "devn"; |
| break; |
| case csPattern: |
| default: |
| colorspace = "-"; |
| break; |
| } |
| components = colorMap->getNumPixelComps(); |
| bpc = colorMap->getBits(); |
| } |
| printf("%-5s %2d %2d ", colorspace, components, bpc); |
| |
| switch (str->getKind()) { |
| case strCCITTFax: |
| enc = "ccitt"; |
| break; |
| case strDCT: |
| enc = "jpeg"; |
| break; |
| case strJPX: |
| enc = "jpx"; |
| break; |
| case strJBIG2: |
| enc = "jbig2"; |
| break; |
| case strFile: |
| case strFlate: |
| case strCachedFile: |
| case strASCIIHex: |
| case strASCII85: |
| case strLZW: |
| case strRunLength: |
| case strWeird: |
| default: |
| enc = "image"; |
| break; |
| } |
| printf("%-5s ", enc); |
| |
| printf("%-3s ", interpolate ? "yes" : "no"); |
| |
| if (inlineImg) { |
| printf("[inline] "); |
| } else if (ref->isRef()) { |
| const Ref imageRef = ref->getRef(); |
| if (imageRef.gen >= 100000) { |
| printf("[none] "); |
| } else { |
| printf(" %6d %2d ", imageRef.num, imageRef.gen); |
| } |
| } else { |
| printf("[none] "); |
| } |
| |
| const double *mat = state->getCTM(); |
| double width2 = sqrt(mat[0] * mat[0] + mat[1] * mat[1]); |
| double height2 = sqrt(mat[2] * mat[2] + mat[3] * mat[3]); |
| double xppi = fabs(width * 72.0 / width2); |
| double yppi = fabs(height * 72.0 / height2); |
| if (xppi < 1.0) { |
| printf("%5.3f ", xppi); |
| } else { |
| printf("%5.0f ", xppi); |
| } |
| if (yppi < 1.0) { |
| printf("%5.3f ", yppi); |
| } else { |
| printf("%5.0f ", yppi); |
| } |
| |
| Goffset embedSize = -1; |
| if (inlineImg) { |
| embedSize = getInlineImageLength(str, width, height, colorMap); |
| } else { |
| embedSize = str->getBaseStream()->getLength(); |
| } |
| |
| long long imageSize = 0; |
| if (colorMap && colorMap->isOk()) { |
| imageSize = ((long long)width * height * colorMap->getNumPixelComps() * colorMap->getBits()) / 8; |
| } else { |
| imageSize = (long long)width * height / 8; // mask |
| } |
| |
| double ratio = -1.0; |
| if (imageSize > 0) { |
| ratio = 100.0 * embedSize / imageSize; |
| } |
| |
| if (embedSize < 0) { |
| printf(" - "); |
| } else if (embedSize <= 9999) { |
| printf("%4lldB", embedSize); |
| } else { |
| double d = embedSize / 1024.0; |
| if (d <= 9999.0) { |
| printNumber(d); |
| putchar('K'); |
| } else { |
| d /= 1024.0; |
| if (d <= 9999.0) { |
| printNumber(d); |
| putchar('M'); |
| } else { |
| d /= 1024.0; |
| printNumber(d); |
| putchar('G'); |
| } |
| } |
| } |
| |
| if (ratio > 9.9) { |
| printf(" %3.0f%%\n", ratio); |
| } else if (ratio >= 0.0) { |
| printf(" %3.1f%%\n", ratio); |
| } else { |
| printf(" - \n"); |
| } |
| |
| ++imgNum; |
| } |
| |
| long ImageOutputDev::getInlineImageLength(Stream *str, int width, int height, GfxImageColorMap *colorMap) |
| { |
| long len; |
| |
| if (colorMap) { |
| ImageStream *imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); |
| imgStr->reset(); |
| for (int y = 0; y < height; y++) { |
| imgStr->getLine(); |
| } |
| |
| imgStr->close(); |
| delete imgStr; |
| } else { |
| str->reset(); |
| for (int y = 0; y < height; y++) { |
| int size = (width + 7) / 8; |
| for (int x = 0; x < size; x++) { |
| str->getChar(); |
| } |
| } |
| } |
| |
| EmbedStream *embedStr = (EmbedStream *)(str->getBaseStream()); |
| embedStr->rewind(); |
| len = 0; |
| while (embedStr->getChar() != EOF) { |
| len++; |
| } |
| |
| embedStr->restore(); |
| |
| return len; |
| } |
| |
| void ImageOutputDev::writeRawImage(Stream *str, const char *ext) |
| { |
| FILE *f; |
| int c; |
| |
| // open the image file |
| setFilename(ext); |
| ++imgNum; |
| if (!(f = fopen(fileName, "wb"))) { |
| error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); |
| errorCode = 2; |
| return; |
| } |
| |
| // initialize stream |
| str = str->getNextStream(); |
| str->reset(); |
| |
| // copy the stream |
| while ((c = str->getChar()) != EOF) { |
| fputc(c, f); |
| } |
| |
| str->close(); |
| fclose(f); |
| } |
| |
| void ImageOutputDev::writeImageFile(ImgWriter *writer, ImageFormat format, const char *ext, Stream *str, int width, int height, GfxImageColorMap *colorMap) |
| { |
| FILE *f = nullptr; /* squelch bogus compiler warning */ |
| ImageStream *imgStr = nullptr; |
| unsigned char *row; |
| unsigned char *rowp; |
| unsigned char *p; |
| GfxRGB rgb; |
| GfxCMYK cmyk; |
| GfxGray gray; |
| unsigned char zero[gfxColorMaxComps]; |
| int invert_bits; |
| |
| if (writer) { |
| setFilename(ext); |
| ++imgNum; |
| if (!(f = fopen(fileName, "wb"))) { |
| error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); |
| errorCode = 2; |
| return; |
| } |
| |
| if (!writer->init(f, width, height, 72, 72)) { |
| error(errIO, -1, "Error writing '{0:s}'", fileName); |
| errorCode = 2; |
| return; |
| } |
| } |
| |
| int pixelSize = sizeof(unsigned int); |
| if (format == imgRGB48) { |
| pixelSize = 2 * sizeof(unsigned int); |
| } |
| |
| row = (unsigned char *)gmallocn_checkoverflow(width, pixelSize); |
| if (!row) { |
| error(errIO, -1, "Image data for '{0:s}' is too big. {1:d} width with {2:d} bytes per pixel", fileName, width, pixelSize); |
| errorCode = 99; |
| return; |
| } |
| |
| if (format != imgMonochrome) { |
| // initialize stream |
| imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); |
| imgStr->reset(); |
| } else { |
| // initialize stream |
| str->reset(); |
| } |
| |
| // PDF masks use 0 = draw current color, 1 = leave unchanged. |
| // We invert this to provide the standard interpretation of alpha |
| // (0 = transparent, 1 = opaque). If the colorMap already inverts |
| // the mask we leave the data unchanged. |
| invert_bits = 0xff; |
| if (colorMap) { |
| memset(zero, 0, sizeof(zero)); |
| colorMap->getGray(zero, &gray); |
| if (colToByte(gray) == 0) { |
| invert_bits = 0x00; |
| } |
| } |
| |
| // for each line... |
| for (int y = 0; y < height; y++) { |
| switch (format) { |
| case imgRGB: |
| p = imgStr->getLine(); |
| rowp = row; |
| for (int x = 0; x < width; ++x) { |
| if (p) { |
| colorMap->getRGB(p, &rgb); |
| *rowp++ = colToByte(rgb.r); |
| *rowp++ = colToByte(rgb.g); |
| *rowp++ = colToByte(rgb.b); |
| p += colorMap->getNumPixelComps(); |
| } else { |
| *rowp++ = 0; |
| *rowp++ = 0; |
| *rowp++ = 0; |
| } |
| } |
| if (writer) { |
| writer->writeRow(&row); |
| } |
| break; |
| |
| case imgRGB48: { |
| p = imgStr->getLine(); |
| unsigned short *rowp16 = reinterpret_cast<unsigned short *>(row); |
| for (int x = 0; x < width; ++x) { |
| if (p) { |
| colorMap->getRGB(p, &rgb); |
| *rowp16++ = colToShort(rgb.r); |
| *rowp16++ = colToShort(rgb.g); |
| *rowp16++ = colToShort(rgb.b); |
| p += colorMap->getNumPixelComps(); |
| } else { |
| *rowp16++ = 0; |
| *rowp16++ = 0; |
| *rowp16++ = 0; |
| } |
| } |
| if (writer) { |
| writer->writeRow(&row); |
| } |
| break; |
| } |
| |
| case imgCMYK: |
| p = imgStr->getLine(); |
| rowp = row; |
| for (int x = 0; x < width; ++x) { |
| if (p) { |
| colorMap->getCMYK(p, &cmyk); |
| *rowp++ = colToByte(cmyk.c); |
| *rowp++ = colToByte(cmyk.m); |
| *rowp++ = colToByte(cmyk.y); |
| *rowp++ = colToByte(cmyk.k); |
| p += colorMap->getNumPixelComps(); |
| } else { |
| *rowp++ = 0; |
| *rowp++ = 0; |
| *rowp++ = 0; |
| *rowp++ = 0; |
| } |
| } |
| if (writer) { |
| writer->writeRow(&row); |
| } |
| break; |
| |
| case imgGray: |
| p = imgStr->getLine(); |
| rowp = row; |
| for (int x = 0; x < width; ++x) { |
| if (p) { |
| colorMap->getGray(p, &gray); |
| *rowp++ = colToByte(gray); |
| p += colorMap->getNumPixelComps(); |
| } else { |
| *rowp++ = 0; |
| } |
| } |
| if (writer) { |
| writer->writeRow(&row); |
| } |
| break; |
| |
| case imgMonochrome: |
| int size = (width + 7) / 8; |
| for (int x = 0; x < size; x++) { |
| row[x] = str->getChar() ^ invert_bits; |
| } |
| if (writer) { |
| writer->writeRow(&row); |
| } |
| break; |
| } |
| } |
| |
| gfree(row); |
| if (format != imgMonochrome) { |
| imgStr->close(); |
| delete imgStr; |
| } |
| str->close(); |
| if (writer) { |
| writer->close(); |
| fclose(f); |
| } |
| } |
| |
| void ImageOutputDev::writeImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool inlineImg) |
| { |
| ImageFormat format; |
| EmbedStream *embedStr; |
| |
| if (inlineImg) { |
| embedStr = (EmbedStream *)(str->getBaseStream()); |
| // Record the stream. This determines the size. |
| getInlineImageLength(str, width, height, colorMap); |
| // Reading the stream again will return EOF at end of recording. |
| embedStr->rewind(); |
| } |
| |
| if (dumpJPEG && str->getKind() == strDCT) { |
| // dump JPEG file |
| writeRawImage(str, "jpg"); |
| |
| } else if (dumpJP2 && str->getKind() == strJPX && !inlineImg) { |
| // dump JPEG2000 file |
| writeRawImage(str, "jp2"); |
| |
| } else if (dumpJBIG2 && str->getKind() == strJBIG2 && !inlineImg) { |
| // dump JBIG2 globals stream if available |
| JBIG2Stream *jb2Str = static_cast<JBIG2Stream *>(str); |
| Object *globals = jb2Str->getGlobalsStream(); |
| if (globals->isStream()) { |
| FILE *f; |
| int c; |
| Stream *globalsStr = globals->getStream(); |
| |
| setFilename("jb2g"); |
| if (!(f = fopen(fileName, "wb"))) { |
| error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); |
| errorCode = 2; |
| return; |
| } |
| globalsStr->reset(); |
| while ((c = globalsStr->getChar()) != EOF) { |
| fputc(c, f); |
| } |
| globalsStr->close(); |
| fclose(f); |
| } |
| |
| // dump JBIG2 embedded file |
| writeRawImage(str, "jb2e"); |
| |
| } else if (dumpCCITT && str->getKind() == strCCITTFax) { |
| // write CCITT parameters |
| CCITTFaxStream *ccittStr = static_cast<CCITTFaxStream *>(str); |
| FILE *f; |
| setFilename("params"); |
| if (!(f = fopen(fileName, "wb"))) { |
| error(errIO, -1, "Couldn't open image file '{0:s}'", fileName); |
| errorCode = 2; |
| return; |
| } |
| if (ccittStr->getEncoding() < 0) { |
| fprintf(f, "-4 "); |
| } else if (ccittStr->getEncoding() == 0) { |
| fprintf(f, "-1 "); |
| } else { |
| fprintf(f, "-2 "); |
| } |
| |
| if (ccittStr->getEndOfLine()) { |
| fprintf(f, "-A "); |
| } else { |
| fprintf(f, "-P "); |
| } |
| |
| fprintf(f, "-X %d ", ccittStr->getColumns()); |
| |
| if (ccittStr->getBlackIs1()) { |
| fprintf(f, "-W "); |
| } else { |
| fprintf(f, "-B "); |
| } |
| |
| fprintf(f, "-M\n"); // PDF uses MSB first |
| |
| fclose(f); |
| |
| // dump CCITT file |
| writeRawImage(str, "ccitt"); |
| |
| } else if (outputPNG && !(outputTiff && colorMap && (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)))) { |
| // output in PNG format |
| |
| #ifdef ENABLE_LIBPNG |
| ImgWriter *writer; |
| |
| if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { |
| writer = new PNGWriter(PNGWriter::MONOCHROME); |
| format = imgMonochrome; |
| } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { |
| writer = new PNGWriter(PNGWriter::GRAY); |
| format = imgGray; |
| } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) |
| && colorMap->getBits() > 8) { |
| writer = new PNGWriter(PNGWriter::RGB48); |
| format = imgRGB48; |
| } else { |
| writer = new PNGWriter(PNGWriter::RGB); |
| format = imgRGB; |
| } |
| |
| writeImageFile(writer, format, "png", str, width, height, colorMap); |
| |
| delete writer; |
| #endif |
| } else if (outputTiff) { |
| // output in TIFF format |
| |
| #ifdef ENABLE_LIBTIFF |
| ImgWriter *writer; |
| |
| if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { |
| writer = new TiffWriter(TiffWriter::MONOCHROME); |
| format = imgMonochrome; |
| } else if (colorMap->getColorSpace()->getMode() == csDeviceGray || colorMap->getColorSpace()->getMode() == csCalGray) { |
| writer = new TiffWriter(TiffWriter::GRAY); |
| format = imgGray; |
| } else if (colorMap->getColorSpace()->getMode() == csDeviceCMYK || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 4)) { |
| writer = new TiffWriter(TiffWriter::CMYK); |
| format = imgCMYK; |
| } else if ((colorMap->getColorSpace()->getMode() == csDeviceRGB || colorMap->getColorSpace()->getMode() == csCalRGB || (colorMap->getColorSpace()->getMode() == csICCBased && colorMap->getNumPixelComps() == 3)) |
| && colorMap->getBits() > 8) { |
| writer = new TiffWriter(TiffWriter::RGB48); |
| format = imgRGB48; |
| } else { |
| writer = new TiffWriter(TiffWriter::RGB); |
| format = imgRGB; |
| } |
| |
| writeImageFile(writer, format, "tif", str, width, height, colorMap); |
| |
| delete writer; |
| #endif |
| } else { |
| // output in PPM/PBM format |
| ImgWriter *writer; |
| |
| if (!colorMap || (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1)) { |
| writer = new NetPBMWriter(NetPBMWriter::MONOCHROME); |
| format = imgMonochrome; |
| } else { |
| writer = new NetPBMWriter(NetPBMWriter::RGB); |
| format = imgRGB; |
| } |
| |
| writeImageFile(writer, format, format == imgRGB ? "ppm" : "pbm", str, width, height, colorMap); |
| |
| delete writer; |
| } |
| |
| if (inlineImg) { |
| embedStr->restore(); |
| } |
| |
| if (printFilenames) { |
| printf("%s\n", fileName); |
| } |
| } |
| |
| bool 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) |
| { |
| return true; |
| // do nothing -- this avoids the potentially slow loop in Gfx.cc |
| } |
| |
| void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str, int width, int height, bool invert, bool interpolate, bool inlineImg) |
| { |
| if (listImages) { |
| listImage(state, ref, str, width, height, nullptr, interpolate, inlineImg, imgStencil); |
| } else { |
| writeImage(state, ref, str, width, height, nullptr, inlineImg); |
| } |
| } |
| |
| void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) |
| { |
| if (listImages) { |
| listImage(state, ref, str, width, height, colorMap, interpolate, inlineImg, imgImage); |
| } else { |
| writeImage(state, ref, str, width, height, colorMap, inlineImg); |
| } |
| } |
| |
| void 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) |
| { |
| if (listImages) { |
| listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); |
| listImage(state, ref, maskStr, maskWidth, maskHeight, nullptr, maskInterpolate, false, imgMask); |
| } else { |
| writeImage(state, ref, str, width, height, colorMap, false); |
| writeImage(state, ref, maskStr, maskWidth, maskHeight, nullptr, false); |
| } |
| } |
| |
| void ImageOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, Stream *maskStr, int maskWidth, int maskHeight, GfxImageColorMap *maskColorMap, |
| bool maskInterpolate) |
| { |
| if (listImages) { |
| listImage(state, ref, str, width, height, colorMap, interpolate, false, imgImage); |
| listImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate, false, imgSmask); |
| } else { |
| writeImage(state, ref, str, width, height, colorMap, false); |
| writeImage(state, ref, maskStr, maskWidth, maskHeight, maskColorMap, false); |
| } |
| } |