| //======================================================================== |
| // |
| // 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 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> |
| // |
| // 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 <stdio.h> |
| #include <stdlib.h> |
| #include <stddef.h> |
| #include <ctype.h> |
| #include <math.h> |
| #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; |
| imgNum = 0; |
| pageNum = 0; |
| ok = true; |
| 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 = mat[0] + mat[2]; |
| double height2 = mat[1] + mat[3]; |
| double xppi = fabs(width*72.0/width2) + 0.5; |
| double yppi = fabs(height*72.0/height2) + 0.5; |
| 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); |
| 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); |
| return; |
| } |
| |
| if (!writer->init(f, width, height, 72, 72)) { |
| error(errIO, -1, "Error writing '{0:s}'", fileName); |
| return; |
| } |
| } |
| |
| if (format != imgMonochrome) { |
| // initialize stream |
| imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), |
| colorMap->getBits()); |
| imgStr->reset(); |
| } else { |
| // initialize stream |
| str->reset(); |
| } |
| |
| int pixelSize = sizeof(unsigned int); |
| if (format == imgRGB48) |
| pixelSize = 2*sizeof(unsigned int); |
| |
| row = (unsigned char *) gmallocn(width, pixelSize); |
| |
| // 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); |
| 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); |
| 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(); |
| } |
| |
| bool ImageOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx, Catalog *cat, Object *str, |
| const double *pmat, int paintType, int tilingType, Dict *resDict, |
| const double *mat, const double *bbox, |
| 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, 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, str, 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); |
| } |
| } |