| Index: src/pdf/SkPDFBitmap.cpp
|
| diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
|
| index de43221630e5ed85e9db28bdf8738c3957c54ada..1978040ec93309a4527912f55dea7d0ce5b32421 100644
|
| --- a/src/pdf/SkPDFBitmap.cpp
|
| +++ b/src/pdf/SkPDFBitmap.cpp
|
| @@ -8,14 +8,33 @@
|
| #include "SkColorPriv.h"
|
| #include "SkData.h"
|
| #include "SkDeflate.h"
|
| -#include "SkImageGenerator.h"
|
| +#include "SkImage_Base.h"
|
| #include "SkJpegInfo.h"
|
| #include "SkPDFBitmap.h"
|
| #include "SkPDFCanon.h"
|
| -#include "SkPixelRef.h"
|
| #include "SkStream.h"
|
| #include "SkUnPreMultiply.h"
|
|
|
| +void image_get_ro_pixels(const SkImage* image, SkBitmap* dst) {
|
| + if(as_IB(image)->getROPixels(dst)
|
| + && dst->dimensions() == image->dimensions()) {
|
| + return;
|
| + }
|
| + // no pixels or wrong size: fill with zeros.
|
| + SkAlphaType at = image->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
|
| + dst->setInfo(SkImageInfo::MakeN32(image->width(), image->height(), at));
|
| +}
|
| +
|
| +bool image_compute_is_opaque(const SkImage* image) {
|
| + if (image->isOpaque()) {
|
| + return true;
|
| + }
|
| + // keep output PDF small at cost of possible resource use.
|
| + SkBitmap bm;
|
| + image_get_ro_pixels(image, &bm);
|
| + return SkBitmap::ComputeIsOpaque(bm);
|
| +}
|
| +
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| static void pdf_stream_begin(SkWStream* stream) {
|
| @@ -239,80 +258,6 @@ static void bitmap_alpha_to_a8(const SkBitmap& bitmap, SkWStream* out) {
|
| }
|
| }
|
|
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -
|
| -namespace {
|
| -// This SkPDFObject only outputs the alpha layer of the given bitmap.
|
| -class PDFAlphaBitmap : public SkPDFObject {
|
| -public:
|
| - PDFAlphaBitmap(const SkBitmap& bm) : fBitmap(bm) {}
|
| - ~PDFAlphaBitmap() {}
|
| - void emitObject(SkWStream*,
|
| - const SkPDFObjNumMap&,
|
| - const SkPDFSubstituteMap&) const override;
|
| -
|
| -private:
|
| - const SkBitmap fBitmap;
|
| -};
|
| -
|
| -void PDFAlphaBitmap::emitObject(SkWStream* stream,
|
| - const SkPDFObjNumMap& objNumMap,
|
| - const SkPDFSubstituteMap& substitutes) const {
|
| - SkAutoLockPixels autoLockPixels(fBitmap);
|
| - SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
|
| - fBitmap.getColorTable());
|
| -
|
| - // Write to a temporary buffer to get the compressed length.
|
| - SkDynamicMemoryWStream buffer;
|
| - SkDeflateWStream deflateWStream(&buffer);
|
| - bitmap_alpha_to_a8(fBitmap, &deflateWStream);
|
| - deflateWStream.finalize(); // call before detachAsStream().
|
| - SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
|
| -
|
| - SkPDFDict pdfDict("XObject");
|
| - pdfDict.insertName("Subtype", "Image");
|
| - pdfDict.insertInt("Width", fBitmap.width());
|
| - pdfDict.insertInt("Height", fBitmap.height());
|
| - pdfDict.insertName("ColorSpace", "DeviceGray");
|
| - pdfDict.insertInt("BitsPerComponent", 8);
|
| - pdfDict.insertName("Filter", "FlateDecode");
|
| - pdfDict.insertInt("Length", asset->getLength());
|
| - pdfDict.emitObject(stream, objNumMap, substitutes);
|
| -
|
| - pdf_stream_begin(stream);
|
| - stream->writeStream(asset.get(), asset->getLength());
|
| - pdf_stream_end(stream);
|
| -}
|
| -} // namespace
|
| -
|
| -////////////////////////////////////////////////////////////////////////////////
|
| -
|
| -namespace {
|
| -class PDFDefaultBitmap : public SkPDFBitmap {
|
| -public:
|
| - const SkAutoTUnref<SkPDFObject> fSMask;
|
| - void emitObject(SkWStream*,
|
| - const SkPDFObjNumMap&,
|
| - const SkPDFSubstituteMap&) const override;
|
| - void addResources(SkPDFObjNumMap*,
|
| - const SkPDFSubstituteMap&) const override;
|
| - PDFDefaultBitmap(const SkBitmap& bm, SkPDFObject* smask)
|
| - : SkPDFBitmap(bm), fSMask(smask) {}
|
| -};
|
| -} // namespace
|
| -
|
| -void PDFDefaultBitmap::addResources(
|
| - SkPDFObjNumMap* catalog,
|
| - const SkPDFSubstituteMap& substitutes) const {
|
| - if (fSMask.get()) {
|
| - SkPDFObject* obj = substitutes.getSubstitute(fSMask.get());
|
| - SkASSERT(obj);
|
| - if (catalog->addObject(obj)) {
|
| - obj->addResources(catalog, substitutes);
|
| - }
|
| - }
|
| -}
|
| -
|
| static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
|
| SkPDFArray* result = new SkPDFArray;
|
| result->reserve(4);
|
| @@ -343,37 +288,48 @@ static SkPDFArray* make_indexed_color_space(const SkColorTable* table) {
|
| return result;
|
| }
|
|
|
| -void PDFDefaultBitmap::emitObject(SkWStream* stream,
|
| - const SkPDFObjNumMap& objNumMap,
|
| - const SkPDFSubstituteMap& substitutes) const {
|
| - SkAutoLockPixels autoLockPixels(fBitmap);
|
| - SkASSERT(fBitmap.colorType() != kIndex_8_SkColorType ||
|
| - fBitmap.getColorTable());
|
| +static void emit_image_xobject(SkWStream* stream,
|
| + const SkImage* image,
|
| + bool alpha,
|
| + SkPDFObject* smask,
|
| + const SkPDFObjNumMap& objNumMap,
|
| + const SkPDFSubstituteMap& substitutes) {
|
| + SkBitmap bitmap;
|
| + image_get_ro_pixels(image, &bitmap); // TODO(halcanary): test
|
| + SkAutoLockPixels autoLockPixels(bitmap); // with malformed images.
|
| + SkASSERT(bitmap.colorType() != kIndex_8_SkColorType ||
|
| + bitmap.getColorTable());
|
|
|
| // Write to a temporary buffer to get the compressed length.
|
| SkDynamicMemoryWStream buffer;
|
| SkDeflateWStream deflateWStream(&buffer);
|
| - bitmap_to_pdf_pixels(fBitmap, &deflateWStream);
|
| + if (alpha) {
|
| + bitmap_alpha_to_a8(bitmap, &deflateWStream);
|
| + } else {
|
| + bitmap_to_pdf_pixels(bitmap, &deflateWStream);
|
| + }
|
| deflateWStream.finalize(); // call before detachAsStream().
|
| SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
|
|
|
| SkPDFDict pdfDict("XObject");
|
| pdfDict.insertName("Subtype", "Image");
|
| - pdfDict.insertInt("Width", fBitmap.width());
|
| - pdfDict.insertInt("Height", fBitmap.height());
|
| - if (fBitmap.colorType() == kIndex_8_SkColorType) {
|
| - SkASSERT(1 == pdf_color_component_count(fBitmap.colorType()));
|
| + pdfDict.insertInt("Width", bitmap.width());
|
| + pdfDict.insertInt("Height", bitmap.height());
|
| + if (alpha) {
|
| + pdfDict.insertName("ColorSpace", "DeviceGray");
|
| + } else if (bitmap.colorType() == kIndex_8_SkColorType) {
|
| + SkASSERT(1 == pdf_color_component_count(bitmap.colorType()));
|
| pdfDict.insertObject("ColorSpace",
|
| - make_indexed_color_space(fBitmap.getColorTable()));
|
| - } else if (1 == pdf_color_component_count(fBitmap.colorType())) {
|
| + make_indexed_color_space(bitmap.getColorTable()));
|
| + } else if (1 == pdf_color_component_count(bitmap.colorType())) {
|
| pdfDict.insertName("ColorSpace", "DeviceGray");
|
| } else {
|
| pdfDict.insertName("ColorSpace", "DeviceRGB");
|
| }
|
| - pdfDict.insertInt("BitsPerComponent", 8);
|
| - if (fSMask) {
|
| - pdfDict.insertObjRef("SMask", SkRef(fSMask.get()));
|
| + if (smask) {
|
| + pdfDict.insertObjRef("SMask", SkRef(smask));
|
| }
|
| + pdfDict.insertInt("BitsPerComponent", 8);
|
| pdfDict.insertName("Filter", "FlateDecode");
|
| pdfDict.insertInt("Length", asset->getLength());
|
| pdfDict.emitObject(stream, objNumMap, substitutes);
|
| @@ -385,26 +341,68 @@ void PDFDefaultBitmap::emitObject(SkWStream* stream,
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| -static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) {
|
| - if (bm.isImmutable()) {
|
| - return bm;
|
| +namespace {
|
| +// This SkPDFObject only outputs the alpha layer of the given bitmap.
|
| +class PDFAlphaBitmap : public SkPDFObject {
|
| +public:
|
| + PDFAlphaBitmap(const SkImage* image) : fImage(SkRef(image)) {}
|
| + ~PDFAlphaBitmap() {}
|
| + void emitObject(SkWStream* stream,
|
| + const SkPDFObjNumMap& objNumMap,
|
| + const SkPDFSubstituteMap& subs) const override {
|
| + emit_image_xobject(stream, fImage, true, nullptr, objNumMap, subs);
|
| }
|
| - bm.copyTo(copy);
|
| - copy->setImmutable();
|
| - return *copy;
|
| -}
|
| +
|
| +private:
|
| + SkAutoTUnref<const SkImage> fImage;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +namespace {
|
| +class PDFDefaultBitmap : public SkPDFObject {
|
| +public:
|
| + void emitObject(SkWStream* stream,
|
| + const SkPDFObjNumMap& objNumMap,
|
| + const SkPDFSubstituteMap& substitutes) const override {
|
| + emit_image_xobject(stream, fImage, false, fSMask, objNumMap, substitutes);
|
| + }
|
| + void addResources(SkPDFObjNumMap* catalog,
|
| + const SkPDFSubstituteMap& substitutes) const override {
|
| + if (fSMask.get()) {
|
| + SkPDFObject* obj = substitutes.getSubstitute(fSMask.get());
|
| + SkASSERT(obj);
|
| + if (catalog->addObject(obj)) {
|
| + obj->addResources(catalog, substitutes);
|
| + }
|
| + }
|
| + }
|
| + PDFDefaultBitmap(const SkImage* image, SkPDFObject* smask)
|
| + : fImage(SkRef(image)), fSMask(smask) {}
|
| +
|
| +private:
|
| + SkAutoTUnref<const SkImage> fImage;
|
| + const SkAutoTUnref<SkPDFObject> fSMask;
|
| +};
|
| +} // namespace
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
|
|
| namespace {
|
| /**
|
| - * This PDFObject assumes that its constructor was handed YUV JFIF
|
| - * Jpeg-encoded data that can be directly embedded into a PDF.
|
| + * This PDFObject assumes that its constructor was handed YUV or
|
| + * Grayscale JFIF Jpeg-encoded data that can be directly embedded
|
| + * into a PDF.
|
| */
|
| -class PDFJpegBitmap : public SkPDFBitmap {
|
| +class PDFJpegBitmap : public SkPDFObject {
|
| public:
|
| + SkISize fSize;
|
| SkAutoTUnref<SkData> fData;
|
| bool fIsYUV;
|
| - PDFJpegBitmap(const SkBitmap& bm, SkData* data, bool isYUV)
|
| - : SkPDFBitmap(bm), fData(SkRef(data)), fIsYUV(isYUV) {}
|
| + PDFJpegBitmap(SkISize size, SkData* data, bool isYUV)
|
| + : fSize(size), fData(SkRef(data)), fIsYUV(isYUV) {}
|
| void emitObject(SkWStream*,
|
| const SkPDFObjNumMap&,
|
| const SkPDFSubstituteMap&) const override;
|
| @@ -415,8 +413,8 @@ void PDFJpegBitmap::emitObject(SkWStream* stream,
|
| const SkPDFSubstituteMap& substituteMap) const {
|
| SkPDFDict pdfDict("XObject");
|
| pdfDict.insertName("Subtype", "Image");
|
| - pdfDict.insertInt("Width", fBitmap.width());
|
| - pdfDict.insertInt("Height", fBitmap.height());
|
| + pdfDict.insertInt("Width", fSize.width());
|
| + pdfDict.insertInt("Height", fSize.height());
|
| if (fIsYUV) {
|
| pdfDict.insertName("ColorSpace", "DeviceRGB");
|
| } else {
|
| @@ -435,39 +433,23 @@ void PDFJpegBitmap::emitObject(SkWStream* stream,
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
|
|
| -SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) {
|
| - SkASSERT(canon);
|
| - if (!SkColorTypeIsValid(bitmap.colorType()) ||
|
| - kUnknown_SkColorType == bitmap.colorType()) {
|
| - return nullptr;
|
| - }
|
| - SkBitmap copy;
|
| - const SkBitmap& bm = immutable_bitmap(bitmap, ©);
|
| - if (bm.drawsNothing()) {
|
| - return nullptr;
|
| - }
|
| - if (SkPDFBitmap* canonBitmap = canon->findBitmap(bm)) {
|
| - return SkRef(canonBitmap);
|
| - }
|
| -
|
| - if (bm.pixelRef() && bm.pixelRefOrigin().isZero() &&
|
| - bm.dimensions() == bm.pixelRef()->info().dimensions()) {
|
| - // Requires the bitmap to be backed by lazy pixels.
|
| - SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData());
|
| - SkJFIFInfo info;
|
| - if (data && SkIsJFIF(data, &info)) {
|
| - bool yuv = info.fType == SkJFIFInfo::kYCbCr;
|
| - SkPDFBitmap* pdfBitmap = new PDFJpegBitmap(bm, data, yuv);
|
| - canon->addBitmap(pdfBitmap);
|
| - return pdfBitmap;
|
| +SkPDFObject* SkPDFCreateBitmapObject(const SkImage* image) {
|
| + SkAutoTUnref<SkData> data(image->refEncoded());
|
| + SkJFIFInfo info;
|
| + if (data && SkIsJFIF(data, &info)) {
|
| + bool yuv = info.fType == SkJFIFInfo::kYCbCr;
|
| + if (info.fSize == image->dimensions()) { // Sanity check.
|
| + // hold on to data, not image.
|
| + #ifdef SK_PDF_IMAGE_STATS
|
| + gJpegImageObjects.fetch_add(1);
|
| + #endif
|
| + return new PDFJpegBitmap(info.fSize, data, yuv);
|
| }
|
| }
|
| -
|
| - SkPDFObject* smask = nullptr;
|
| - if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
|
| - smask = new PDFAlphaBitmap(bm);
|
| - }
|
| - SkPDFBitmap* pdfBitmap = new PDFDefaultBitmap(bm, smask);
|
| - canon->addBitmap(pdfBitmap);
|
| - return pdfBitmap;
|
| + SkPDFObject* smask =
|
| + image_compute_is_opaque(image) ? nullptr : new PDFAlphaBitmap(image);
|
| + #ifdef SK_PDF_IMAGE_STATS
|
| + gRegularImageObjects.fetch_add(1);
|
| + #endif
|
| + return new PDFDefaultBitmap(image, smask);
|
| }
|
|
|