Chromium Code Reviews| Index: src/pdf/SkPDFBitmap.cpp |
| diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp |
| index 765016dcf2d0d06f05b25cca2747649513ad0ef5..42a5841b390eee67e84b6b02efe1e8bf0ec1ea77 100644 |
| --- a/src/pdf/SkPDFBitmap.cpp |
| +++ b/src/pdf/SkPDFBitmap.cpp |
| @@ -6,10 +6,13 @@ |
| */ |
| #include "SkColorPriv.h" |
| +#include "SkData.h" |
| #include "SkFlate.h" |
| +#include "SkImageGenerator.h" |
| #include "SkPDFBitmap.h" |
| #include "SkPDFCanon.h" |
| #include "SkPDFCatalog.h" |
| +#include "SkPixelRef.h" |
| #include "SkStream.h" |
| #include "SkUnPreMultiply.h" |
| @@ -248,7 +251,6 @@ public: |
| private: |
| const SkBitmap fBitmap; |
| - void emitDict(SkWStream*, SkPDFCatalog*, size_t) const; |
| }; |
| void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| @@ -263,15 +265,6 @@ void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| deflateWStream.finalize(); // call before detachAsStream(). |
| SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| - this->emitDict(stream, catalog, asset->getLength()); |
| - pdf_stream_begin(stream); |
| - stream->writeStream(asset.get(), asset->getLength()); |
| - pdf_stream_end(stream); |
| -} |
| - |
| -void PDFAlphaBitmap::emitDict(SkWStream* stream, |
| - SkPDFCatalog* catalog, |
| - size_t length) const { |
| SkPDFDict pdfDict("XObject"); |
| pdfDict.insertName("Subtype", "Image"); |
| pdfDict.insertInt("Width", fBitmap.width()); |
| @@ -279,14 +272,29 @@ void PDFAlphaBitmap::emitDict(SkWStream* stream, |
| pdfDict.insertName("ColorSpace", "DeviceGray"); |
| pdfDict.insertInt("BitsPerComponent", 8); |
| pdfDict.insertName("Filter", "FlateDecode"); |
| - pdfDict.insertInt("Length", length); |
| + pdfDict.insertInt("Length", asset->getLength()); |
| pdfDict.emitObject(stream, catalog); |
| + |
| + pdf_stream_begin(stream); |
| + stream->writeStream(asset.get(), asset->getLength()); |
| + pdf_stream_end(stream); |
| } |
| } // namespace |
| //////////////////////////////////////////////////////////////////////////////// |
| -void SkPDFBitmap::addResources(SkPDFCatalog* catalog) const { |
| +namespace { |
| +class PDFBitmap : public SkPDFBitmap { |
|
mtklein
2015/03/25 21:36:53
Oh come on.
hal.canary
2015/04/17 18:57:13
Done.
|
| +public: |
| + const SkAutoTUnref<SkPDFObject> fSMask; |
| + void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; |
| + void addResources(SkPDFCatalog*) const SK_OVERRIDE; |
| + PDFBitmap(const SkBitmap& bm, SkPDFObject* smask) |
| + : SkPDFBitmap(bm), fSMask(smask) {} |
| +}; |
| +} // namespace |
| + |
| +void PDFBitmap::addResources(SkPDFCatalog* catalog) const { |
| if (fSMask.get()) { |
| if (catalog->addObject(fSMask.get())) { |
| fSMask->addResources(catalog); |
| @@ -294,24 +302,6 @@ void SkPDFBitmap::addResources(SkPDFCatalog* catalog) const { |
| } |
| } |
| -void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| - 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_to_pdf_pixels(fBitmap, &deflateWStream); |
| - deflateWStream.finalize(); // call before detachAsStream(). |
| - SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| - |
| - this->emitDict(stream, catalog, asset->getLength()); |
| - pdf_stream_begin(stream); |
| - stream->writeStream(asset.get(), asset->getLength()); |
| - pdf_stream_end(stream); |
| -} |
| - |
| static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { |
| SkPDFArray* result = SkNEW(SkPDFArray); |
| result->reserve(4); |
| @@ -342,9 +332,18 @@ static SkPDFArray* make_indexed_color_space(const SkColorTable* table) { |
| return result; |
| } |
| -void SkPDFBitmap::emitDict(SkWStream* stream, |
| - SkPDFCatalog* catalog, |
| - size_t length) const { |
| +void PDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| + 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_to_pdf_pixels(fBitmap, &deflateWStream); |
| + deflateWStream.finalize(); // call before detachAsStream(). |
| + SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream()); |
| + |
| SkPDFDict pdfDict("XObject"); |
| pdfDict.insertName("Subtype", "Image"); |
| pdfDict.insertInt("Width", fBitmap.width()); |
| @@ -363,15 +362,13 @@ void SkPDFBitmap::emitDict(SkWStream* stream, |
| pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref(); |
| } |
| pdfDict.insertName("Filter", "FlateDecode"); |
| - pdfDict.insertInt("Length", length); |
| + pdfDict.insertInt("Length", asset->getLength()); |
| pdfDict.emitObject(stream, catalog); |
| -} |
| - |
| -SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, |
| - SkPDFObject* smask) |
| - : fBitmap(bm), fSMask(smask) {} |
| -SkPDFBitmap::~SkPDFBitmap() {} |
| + pdf_stream_begin(stream); |
| + stream->writeStream(asset.get(), asset->getLength()); |
| + pdf_stream_end(stream); |
| +} |
| //////////////////////////////////////////////////////////////////////////////// |
| @@ -384,6 +381,55 @@ static const SkBitmap& immutable_bitmap(const SkBitmap& bm, SkBitmap* copy) { |
| return *copy; |
| } |
| +namespace { |
| +/** |
| + * This PDFObject assumes that its constructor was handed YUV JFIF |
| + * Jpeg-encoded data that can be directly embedded into a PDF. |
| + */ |
| +class PDFJpegBitmap : public SkPDFBitmap { |
| +public: |
| + SkAutoTUnref<SkData> fData; |
| + PDFJpegBitmap(const SkBitmap& bm, SkData* data) |
| + : SkPDFBitmap(bm), fData(SkRef(data)) {} |
| + void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE; |
| +}; |
| + |
| +void PDFJpegBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) { |
| + SkPDFDict pdfDict("XObject"); |
| + pdfDict.insertName("Subtype", "Image"); |
| + pdfDict.insertInt("Width", fBitmap.width()); |
| + pdfDict.insertInt("Height", fBitmap.height()); |
| + pdfDict.insertName("ColorSpace", "DeviceRGB"); |
| + pdfDict.insertInt("BitsPerComponent", 8); |
| + pdfDict.insertName("Filter", "DCTDecode"); |
| + pdfDict.insertInt("ColorTransform", 0); |
| + pdfDict.insertInt("Length", SkToInt(fData->size())); |
| + pdfDict.emitObject(stream, catalog); |
| + pdf_stream_begin(stream); |
| + stream->write(fData->data(), fData->size()); |
| + pdf_stream_end(stream); |
| +} |
| +} // namespace |
| + |
| +//////////////////////////////////////////////////////////////////////////////// |
| + |
| +static bool is_jfif_yuv_jpeg(SkData* data) { |
| + const uint8_t bytesZeroToThree[] = {0xFF, 0xD8, 0xFF, 0xE0}; |
| + const uint8_t bytesSixToTen[] = {'J', 'F', 'I', 'F', 0}; |
| + // 0 1 2 3 4 5 6 7 8 9 10 |
| + // FF D8 FF E0 ?? ?? 'J' 'F' 'I' 'F' 00 ... |
| + if (data->size() < 11 || |
| + 0 != memcmp(data->bytes(), bytesZeroToThree, |
| + sizeof(bytesZeroToThree)) || |
| + 0 != memcmp(data->bytes() + 6, bytesSixToTen, sizeof(bytesSixToTen))) { |
| + return false; |
| + } |
| + SkAutoTDelete<SkImageGenerator> gen(SkImageGenerator::NewFromData(data)); |
| + SkISize sizes[3]; |
| + // Only YUV JPEG allows access to YUV planes. |
| + return gen && gen->getYUV8Planes(sizes, NULL, NULL, NULL); |
| +} |
| + |
| SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) { |
| SkASSERT(canon); |
| if (!SkColorTypeIsValid(bitmap.colorType()) || |
| @@ -398,11 +444,23 @@ SkPDFBitmap* SkPDFBitmap::Create(SkPDFCanon* canon, const SkBitmap& bitmap) { |
| 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()); |
| + if (data && is_jfif_yuv_jpeg(data)) { |
| + SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFJpegBitmap, (bm, data)); |
| + canon->addBitmap(pdfBitmap); |
| + return pdfBitmap; |
| + } |
| + } |
| + |
| SkPDFObject* smask = NULL; |
| if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) { |
| smask = SkNEW_ARGS(PDFAlphaBitmap, (bm)); |
| } |
| - SkPDFBitmap* pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask)); |
| + SkPDFBitmap* pdfBitmap = SkNEW_ARGS(PDFBitmap, (bm, smask)); |
| canon->addBitmap(pdfBitmap); |
| return pdfBitmap; |
| } |