| Index: src/pdf/SkPDFBitmap.cpp
|
| diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c2d3cfd2d56124adbcd2888bfa1e6b3baabf25e7
|
| --- /dev/null
|
| +++ b/src/pdf/SkPDFBitmap.cpp
|
| @@ -0,0 +1,258 @@
|
| +/*
|
| + * Copyright 2015 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "SkColorPriv.h"
|
| +#include "SkDeflateWStream.h"
|
| +#include "SkPDFBitmap.h"
|
| +#include "SkPDFCanon.h"
|
| +#include "SkPDFCatalog.h"
|
| +#include "SkPDFDocument.h"
|
| +#include "SkStream.h"
|
| +#include "SkUnPreMultiply.h"
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +static void pdf_stream_begin(SkWStream* stream) {
|
| + static const char streamBegin[] = " stream\n";
|
| + stream->write(streamBegin, strlen(streamBegin));
|
| +}
|
| +
|
| +static void pdf_stream_end(SkWStream* stream) {
|
| + static const char streamEnd[] = "\nendstream";
|
| + stream->write(streamEnd, strlen(streamEnd));
|
| +}
|
| +
|
| +static size_t pixel_count(const SkBitmap& bm) {
|
| + return SkToSizeT(bm.width()) * SkToSizeT(bm.height());
|
| +}
|
| +
|
| +static bool skip_compression(SkPDFDocument::Flags flag) {
|
| + return SkToBool(flag & SkPDFDocument::kFavorSpeedOverSize_Flags);
|
| +}
|
| +
|
| +static bool is_better_compressed(const SkStreamAsset* asset,
|
| + const SkBitmap& bitmap) {
|
| + return asset->getLength() + strlen("/Filter /FlateDecode\n")
|
| + <= pixel_count(bitmap);
|
| +}
|
| +
|
| +
|
| +
|
| +static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
|
| + SkASSERT(kN32_SkColorType == bm.colorType());
|
| + size_t scanlineLength = 3 * bm.width();
|
| + SkAutoTMalloc<uint8_t> scanline(scanlineLength);
|
| + for (int y = 0; y < bm.height(); ++y) {
|
| + uint8_t* dst = scanline.get();
|
| + const SkPMColor* src = bm.getAddr32(0, y);
|
| + for (int x = 0; x < bm.width(); ++x) {
|
| + SkPMColor pmColor = *src++;
|
| + uint32_t s = SkUnPreMultiply::GetScale(SkGetPackedA32(pmColor));
|
| + *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
|
| + *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
|
| + *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
|
| + }
|
| + out->write(scanline.get(), scanlineLength);
|
| + }
|
| +}
|
| +
|
| +static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
|
| + SkASSERT(kN32_SkColorType == bm.colorType());
|
| + size_t scanlineLength = bm.width();
|
| + SkAutoTMalloc<uint8_t> scanline(scanlineLength);
|
| + for (int y = 0; y < bm.height(); ++y) {
|
| + uint8_t* dst = scanline.get();
|
| + const SkPMColor* src = bm.getAddr32(0, y);
|
| + for (int x = 0; x < bm.width(); ++x) {
|
| + *dst++ = SkGetPackedA32(*src++);
|
| + }
|
| + out->write(scanline.get(), scanlineLength);
|
| + }
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +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*, SkPDFCatalog*) SK_OVERRIDE;
|
| + void addResources(SkTSet<SkPDFObject*>*, SkPDFCatalog*) const SK_OVERRIDE {}
|
| +
|
| +private:
|
| + const SkBitmap fBitmap;
|
| + void emitFast(SkWStream*, SkPDFCatalog*) const;
|
| + void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
|
| +};
|
| +
|
| +void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
|
| + SkASSERT(kN32_SkColorType == fBitmap.colorType());
|
| + SkAutoLockPixels autoLockPixels(fBitmap);
|
| + SkASSERT(fBitmap.getPixels());
|
| +
|
| +#ifdef SK_HAS_ZLIB
|
| + if (skip_compression(catalog->getDocumentFlags())) {
|
| + this->emitFast(stream, catalog);
|
| + return;
|
| + }
|
| +
|
| + // Write to a temporary buffer to get the compressed length.
|
| + SkDynamicMemoryWStream buffer;
|
| + SkDeflateWStream deflateWStream(&buffer);
|
| + pmcolor_alpha_to_a8(fBitmap, &deflateWStream);
|
| + deflateWStream.finalize(); // call before detachAsStream().
|
| + SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
|
| +
|
| + if (is_better_compressed(asset, fBitmap)) {
|
| + this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
|
| + pdf_stream_begin(stream);
|
| + stream->writeStream(asset.get(), asset->getLength());
|
| + pdf_stream_end(stream);
|
| + } else {
|
| + this->emitFast(stream, catalog);
|
| + }
|
| +#else
|
| + this->emitFast(stream, catalog);
|
| +#endif
|
| +}
|
| +void PDFAlphaBitmap::emitFast(SkWStream* stream,
|
| + SkPDFCatalog* catalog) const {
|
| + this->emitDict(stream, catalog, pixel_count(fBitmap), /*deflate=*/false);
|
| + pdf_stream_begin(stream);
|
| + pmcolor_alpha_to_a8(fBitmap, stream);
|
| + pdf_stream_end(stream);
|
| +}
|
| +
|
| +void PDFAlphaBitmap::emitDict(SkWStream* stream,
|
| + SkPDFCatalog* catalog,
|
| + size_t length,
|
| + bool deflate) const {
|
| + 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.insertInt("Length", length);
|
| + if (deflate) {
|
| + pdfDict.insertName("Filter", "FlateDecode");
|
| + }
|
| + pdfDict.emitObject(stream, catalog);
|
| +}
|
| +} // namespace
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +void SkPDFBitmap::addResources(SkTSet<SkPDFObject*>* resourceSet,
|
| + SkPDFCatalog* catalog) const {
|
| + if (fSMask.get()) {
|
| + resourceSet->add(fSMask.get());
|
| + }
|
| +}
|
| +
|
| +void SkPDFBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
|
| + SkASSERT(kN32_SkColorType == fBitmap.colorType());
|
| + SkAutoLockPixels autoLockPixels(fBitmap);
|
| + SkASSERT(fBitmap.getPixels());
|
| +
|
| +#ifdef SK_HAS_ZLIB
|
| + if (skip_compression(catalog->getDocumentFlags())) {
|
| + this->emitFast(stream, catalog);
|
| + return;
|
| + }
|
| + // Write to a temporary buffer to get the compressed length.
|
| + SkDynamicMemoryWStream buffer;
|
| + SkDeflateWStream deflateWStream(&buffer);
|
| + pmcolor_to_rgb24(fBitmap, &deflateWStream);
|
| + deflateWStream.finalize(); // call before detachAsStream().
|
| + SkAutoTDelete<SkStreamAsset> asset(buffer.detachAsStream());
|
| +
|
| + if (is_better_compressed(asset, fBitmap)) {
|
| + this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
|
| + pdf_stream_begin(stream);
|
| + stream->writeStream(asset.get(), asset->getLength());
|
| + pdf_stream_end(stream);
|
| + } else {
|
| + // probably a very small bitmap: output_pmcolor() is cheap.
|
| + this->emitFast(stream, catalog);
|
| + }
|
| +#else
|
| + this->emitFast(stream, catalog);
|
| +#endif
|
| +}
|
| +
|
| +void SkPDFBitmap::emitFast(SkWStream* stream, SkPDFCatalog* catalog) const {
|
| + this->emitDict(stream, catalog, 3 * pixel_count(fBitmap),
|
| + /*deflate=*/false);
|
| + pdf_stream_begin(stream);
|
| + pmcolor_to_rgb24(fBitmap, stream);
|
| + pdf_stream_end(stream);
|
| +}
|
| +
|
| +void SkPDFBitmap::emitDict(SkWStream* stream,
|
| + SkPDFCatalog* catalog,
|
| + size_t length,
|
| + bool deflate) const {
|
| + 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);
|
| + if (fSMask) {
|
| + pdfDict.insert("SMask", new SkPDFObjRef(fSMask))->unref();
|
| + }
|
| + pdfDict.insertInt("Length", length);
|
| + if (deflate) {
|
| + pdfDict.insertName("Filter", "FlateDecode");
|
| + }
|
| + pdfDict.emitObject(stream, catalog);
|
| +}
|
| +
|
| +SkPDFBitmap::SkPDFBitmap(const SkBitmap& bm, SkPDFObject* smask)
|
| + : fBitmap(bm), fSMask(smask) {}
|
| +
|
| +SkPDFBitmap::~SkPDFBitmap() {
|
| + SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
|
| + SkPDFCanon::GetCanon().removeBitmap(this);
|
| +}
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +// TODO(halcanary): SkPDFBitmap::Create should take a SkPDFCanon* parameter.
|
| +SkPDFBitmap* SkPDFBitmap::Create(const SkBitmap& bitmap,
|
| + const SkIRect& subset) {
|
| + if (kN32_SkColorType != bitmap.colorType()) {
|
| + // TODO(halcanary): support other colortypes.
|
| + return NULL;
|
| + }
|
| + SkBitmap bm;
|
| + // Should extractSubset be done by the SkPDFDevice?
|
| + if (!bitmap.extractSubset(&bm, subset)) {
|
| + return NULL;
|
| + }
|
| + if (bm.drawsNothing()) {
|
| + return NULL;
|
| + }
|
| + SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
|
| + SkPDFCanon& canon = SkPDFCanon::GetCanon();
|
| + SkPDFBitmap* pdfBitmap = canon.findBitmap(bm);
|
| + if (pdfBitmap) {
|
| + return SkRef(pdfBitmap);
|
| + }
|
| + SkPDFObject* smask = NULL;
|
| + if (!bitmap.isOpaque() && !SkBitmap::ComputeIsOpaque(bitmap)) {
|
| + // PDFAlphaBitmap do not get directly canonicalized (they
|
| + // are refed by the SkPDFBitmap).
|
| + smask = SkNEW_ARGS(PDFAlphaBitmap, (bm));
|
| + }
|
| + pdfBitmap = SkNEW_ARGS(SkPDFBitmap, (bm, smask));
|
| + canon.addBitmap(pdfBitmap);
|
| + return pdfBitmap;
|
| +}
|
|
|