aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar halcanary <halcanary@google.com>2015-02-18 11:29:56 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2015-02-18 11:29:57 -0800
commit1b5c604d9d344537941b11b136348edfc39f236c (patch)
tree72110f65007d2e0af1cc525376488ed360b6a07d /src
parent0babd3c61987428a5c532f65be9d98c7ac583e0d (diff)
PDF: Add (low-memory) SkPDFBitmap class
Also: Add SkDeflateWStream and associated unit tests. SkPDFBitmap is a replacement for SkPDFImage. As of now, it only supports 8888 bitmaps (the most common case). SkPDFBitmap takes very little extra memory (aside from refing the bitmap's pixels), and its emitObject() does not cache any data. The SkPDFBitmap::Create function will check the canon for duplicates. This can reduce the size of the output PDF. Motivation: this gives another ~40% decrease in PDF memory overhead TODO: Support other ColorTypes and scrap SkPDFImage. BUG=skia:3030 Review URL: https://codereview.chromium.org/918813002
Diffstat (limited to 'src')
-rw-r--r--src/pdf/SkDeflateWStream.cpp106
-rw-r--r--src/pdf/SkDeflateWStream.h45
-rw-r--r--src/pdf/SkPDFBitmap.cpp333
-rw-r--r--src/pdf/SkPDFBitmap.h45
-rw-r--r--src/pdf/SkPDFCanon.cpp20
-rw-r--r--src/pdf/SkPDFCanon.h9
-rw-r--r--src/pdf/SkPDFImage.cpp4
7 files changed, 562 insertions, 0 deletions
diff --git a/src/pdf/SkDeflateWStream.cpp b/src/pdf/SkDeflateWStream.cpp
new file mode 100644
index 0000000000..5122588857
--- /dev/null
+++ b/src/pdf/SkDeflateWStream.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "SkDeflateWStream.h"
+// https://skia.org/dev/contrib/style#no-define-before-sktypes
+
+#ifndef SK_NO_FLATE
+
+#include "zlib.h"
+
+#define SKDEFLATEWSTREAM_INPUT_BUFFER_SIZE 4096
+#define SKDEFLATEWSTREAM_OUTPUT_BUFFER_SIZE 4224 // 4096 + 128, usually big
+ // enough to always do a
+ // single loop.
+
+// called by both write() and finalize()
+static void do_deflate(int flush,
+ z_stream* zStream,
+ SkWStream* out,
+ unsigned char* inBuffer,
+ size_t inBufferSize) {
+ zStream->next_in = inBuffer;
+ zStream->avail_in = inBufferSize;
+ unsigned char outBuffer[SKDEFLATEWSTREAM_OUTPUT_BUFFER_SIZE];
+ SkDEBUGCODE(int returnValue;)
+ do {
+ zStream->next_out = outBuffer;
+ zStream->avail_out = sizeof(outBuffer);
+ SkDEBUGCODE(returnValue =) deflate(zStream, flush);
+ SkASSERT(!zStream->msg);
+
+ out->write(outBuffer, sizeof(outBuffer) - zStream->avail_out);
+ } while (zStream->avail_in || !zStream->avail_out);
+ SkASSERT(flush == Z_FINISH
+ ? returnValue == Z_STREAM_END
+ : returnValue == Z_OK);
+}
+
+// Hide all zlib impl details.
+struct SkDeflateWStream::Impl {
+ SkWStream* fOut;
+ unsigned char fInBuffer[SKDEFLATEWSTREAM_INPUT_BUFFER_SIZE];
+ size_t fInBufferIndex;
+ z_stream fZStream;
+};
+
+SkDeflateWStream::SkDeflateWStream(SkWStream* out)
+ : fImpl(SkNEW(SkDeflateWStream::Impl)) {
+ fImpl->fOut = out;
+ fImpl->fInBufferIndex = 0;
+ if (!fImpl->fOut) {
+ return;
+ }
+ fImpl->fZStream.zalloc = Z_NULL;
+ fImpl->fZStream.zfree = Z_NULL;
+ fImpl->fZStream.opaque = Z_NULL;
+ fImpl->fZStream.data_type = Z_BINARY;
+ SkDEBUGCODE(int r =) deflateInit(&fImpl->fZStream, Z_DEFAULT_COMPRESSION);
+ SkASSERT(Z_OK == r);
+}
+
+SkDeflateWStream::~SkDeflateWStream() { this->finalize(); }
+
+void SkDeflateWStream::finalize() {
+ if (!fImpl->fOut) {
+ return;
+ }
+ do_deflate(Z_FINISH, &fImpl->fZStream, fImpl->fOut, fImpl->fInBuffer,
+ fImpl->fInBufferIndex);
+ (void)deflateEnd(&fImpl->fZStream);
+ fImpl->fOut = NULL;
+}
+
+bool SkDeflateWStream::write(const void* void_buffer, size_t len) {
+ if (!fImpl->fOut) {
+ return false;
+ }
+ const char* buffer = (const char*)void_buffer;
+ while (len > 0) {
+ size_t tocopy =
+ SkTMin(len, sizeof(fImpl->fInBuffer) - fImpl->fInBufferIndex);
+ memcpy(fImpl->fInBuffer + fImpl->fInBufferIndex, buffer, tocopy);
+ len -= tocopy;
+ buffer += tocopy;
+ fImpl->fInBufferIndex += tocopy;
+ SkASSERT(fImpl->fInBufferIndex <= sizeof(fImpl->fInBuffer));
+
+ // if the buffer isn't filled, don't call into zlib yet.
+ if (sizeof(fImpl->fInBuffer) == fImpl->fInBufferIndex) {
+ do_deflate(Z_NO_FLUSH, &fImpl->fZStream, fImpl->fOut,
+ fImpl->fInBuffer, fImpl->fInBufferIndex);
+ fImpl->fInBufferIndex = 0;
+ }
+ }
+ return true;
+}
+
+size_t SkDeflateWStream::bytesWritten() const {
+ return fImpl->fZStream.total_in + fImpl->fInBufferIndex;
+}
+
+#endif // SK_NO_ZLIB
diff --git a/src/pdf/SkDeflateWStream.h b/src/pdf/SkDeflateWStream.h
new file mode 100644
index 0000000000..bccb4db220
--- /dev/null
+++ b/src/pdf/SkDeflateWStream.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDeflateWStream_DEFINED
+#define SkDeflateWStream_DEFINED
+
+#include "SkStream.h"
+// https://skia.org/dev/contrib/style#no-define-before-sktypes
+
+#ifndef SK_NO_FLATE // Clients of this class should be guarded.
+
+/**
+ * Wrap a stream in this class to compress the information written to
+ * this stream using the Deflate algorithm. Uses Zlib's
+ * Z_DEFAULT_COMPRESSION level.
+ *
+ * See http://en.wikipedia.org/wiki/DEFLATE
+ */
+class SkDeflateWStream : public SkWStream {
+public:
+ /** Does not take ownership of the stream. */
+ SkDeflateWStream(SkWStream*);
+
+ /** The destructor calls finalize(). */
+ ~SkDeflateWStream();
+
+ /** Write the end of the compressed stream. All subsequent calls to
+ write() will fail. Subsequent calls to finalize() do nothing. */
+ void finalize();
+
+ // The SkWStream interface:
+ bool write(const void*, size_t) SK_OVERRIDE;
+ size_t bytesWritten() const SK_OVERRIDE;
+
+private:
+ struct Impl;
+ SkAutoTDelete<Impl> fImpl;
+};
+#endif // SK_NO_FLATE
+
+#endif // SkDeflateWStream_DEFINED
diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp
new file mode 100644
index 0000000000..26410d7fa3
--- /dev/null
+++ b/src/pdf/SkPDFBitmap.cpp
@@ -0,0 +1,333 @@
+/*
+ * 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) {
+#ifndef SK_NO_FLATE
+ return SkToBool(flag & SkPDFDocument::kFavorSpeedOverSize_Flags);
+#else
+ return true;
+#endif // SK_NO_FLATE
+}
+
+// write a single byte to a stream n times.
+static void fill_stream(SkWStream* out, char value, size_t n) {
+ char buffer[4096];
+ memset(buffer, value, sizeof(buffer));
+ while (n) {
+ size_t k = SkTMin(n, sizeof(buffer));
+ out->write(buffer, k);
+ n -= k;
+ }
+}
+
+static SkPMColor get_pmcolor_neighbor_avg_color(const SkBitmap& bitmap,
+ int xOrig,
+ int yOrig) {
+ SkASSERT(kN32_SkColorType == bitmap.colorType());
+ SkASSERT(bitmap.getPixels());
+ uint8_t count = 0;
+ unsigned r = 0;
+ unsigned g = 0;
+ unsigned b = 0;
+ for (int y = yOrig - 1; y <= yOrig + 1; ++y) {
+ if (y < 0 || y >= bitmap.height()) {
+ continue;
+ }
+ uint32_t* src = bitmap.getAddr32(0, y);
+ for (int x = xOrig - 1; x <= xOrig + 1; ++x) {
+ if (x < 0 || x >= bitmap.width()) {
+ continue;
+ }
+ SkPMColor pmColor = src[x];
+ U8CPU alpha = SkGetPackedA32(pmColor);
+ if (alpha != SK_AlphaTRANSPARENT) {
+ uint32_t s = SkUnPreMultiply::GetScale(alpha);
+ r += SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(pmColor));
+ g += SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(pmColor));
+ b += SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(pmColor));
+ ++count;
+ }
+ }
+ }
+ if (count == 0) {
+ return SkPackARGB32NoCheck(SK_AlphaOPAQUE, 0, 0, 0);
+ } else {
+ return SkPackARGB32NoCheck(
+ SK_AlphaOPAQUE, r / count, g / count, b / count);
+ }
+}
+
+static void pmcolor_to_rgb24(const SkBitmap& bm, SkWStream* out) {
+ SkASSERT(kN32_SkColorType == bm.colorType());
+ if (!bm.getPixels()) {
+ fill_stream(out, '\xFF', 3 * pixel_count(bm));
+ return;
+ }
+ 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 color = *src++;
+ U8CPU alpha = SkGetPackedA32(color);
+ if (alpha != SK_AlphaTRANSPARENT) {
+ uint32_t s = SkUnPreMultiply::GetScale(alpha);
+ *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedR32(color));
+ *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedG32(color));
+ *dst++ = SkUnPreMultiply::ApplyScale(s, SkGetPackedB32(color));
+ } else {
+ /* It is necessary to average the color component of
+ transparent pixels with their surrounding neighbors
+ since the PDF renderer may separately re-sample the
+ alpha and color channels when the image is not
+ displayed at its native resolution. Since an alpha
+ of zero gives no information about the color
+ component, the pathological case is a white image
+ with sharp transparency bounds - the color channel
+ goes to black, and the should-be-transparent pixels
+ are rendered as grey because of the separate soft
+ mask and color resizing. e.g.: gm/bitmappremul.cpp */
+ color = get_pmcolor_neighbor_avg_color(bm, x, y);
+ *dst++ = SkGetPackedR32(color);
+ *dst++ = SkGetPackedG32(color);
+ *dst++ = SkGetPackedB32(color);
+ }
+ }
+ out->write(scanline.get(), scanlineLength);
+ }
+}
+
+static void pmcolor_alpha_to_a8(const SkBitmap& bm, SkWStream* out) {
+ SkASSERT(kN32_SkColorType == bm.colorType());
+ if (!bm.getPixels()) {
+ fill_stream(out, '\xFF', pixel_count(bm));
+ return;
+ }
+ 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 emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
+};
+
+void PDFAlphaBitmap::emitObject(SkWStream* stream, SkPDFCatalog* catalog) {
+ SkAutoLockPixels autoLockPixels(fBitmap);
+
+ if (skip_compression(catalog->getDocumentFlags())) {
+ this->emitDict(stream, catalog, pixel_count(fBitmap),
+ /*deflate=*/false);
+ pdf_stream_begin(stream);
+ pmcolor_alpha_to_a8(fBitmap, stream);
+ pdf_stream_end(stream);
+ return;
+ }
+#ifndef SK_NO_FLATE
+ // 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());
+
+ this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
+ pdf_stream_begin(stream);
+ stream->writeStream(asset.get(), asset->getLength());
+ pdf_stream_end(stream);
+#endif // SK_NO_FLATE
+}
+
+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);
+ if (deflate) {
+ pdfDict.insertName("Filter", "FlateDecode");
+ }
+ pdfDict.insertInt("Length", length);
+ 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) {
+ SkAutoLockPixels autoLockPixels(fBitmap);
+
+ if (skip_compression(catalog->getDocumentFlags())) {
+ this->emitDict(stream, catalog, 3 * pixel_count(fBitmap),
+ /*deflate=*/false);
+ pdf_stream_begin(stream);
+ pmcolor_to_rgb24(fBitmap, stream);
+ pdf_stream_end(stream);
+ return;
+ }
+#ifndef SK_NO_FLATE
+ // 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());
+
+ this->emitDict(stream, catalog, asset->getLength(), /*deflate=*/true);
+ pdf_stream_begin(stream);
+ stream->writeStream(asset.get(), asset->getLength());
+ pdf_stream_end(stream);
+#endif // SK_NO_FLATE
+}
+
+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();
+ }
+ if (deflate) {
+ pdfDict.insertName("Filter", "FlateDecode");
+ }
+ pdfDict.insertInt("Length", length);
+ 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);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+static bool is_transparent(const SkBitmap& bm) {
+ SkAutoLockPixels autoLockPixels(bm);
+ if (NULL == bm.getPixels()) {
+ return true;
+ }
+ SkASSERT(kN32_SkColorType == bm.colorType());
+ for (int y = 0; y < bm.height(); ++y) {
+ U8CPU alpha = 0;
+ const SkPMColor* src = bm.getAddr32(0, y);
+ for (int x = 0; x < bm.width(); ++x) {
+ alpha |= SkGetPackedA32(*src++);
+ }
+ if (alpha) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// 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;
+ }
+ if (!bm.isImmutable()) {
+ SkBitmap copy;
+ if (!bm.copyTo(&copy)) {
+ return NULL;
+ }
+ copy.setImmutable();
+ bm = copy;
+ }
+
+ SkAutoMutexAcquire autoMutexAcquire(SkPDFCanon::GetBitmapMutex());
+ SkPDFCanon& canon = SkPDFCanon::GetCanon();
+ SkPDFBitmap* pdfBitmap = canon.findBitmap(bm);
+ if (pdfBitmap) {
+ return SkRef(pdfBitmap);
+ }
+ SkPDFObject* smask = NULL;
+ if (!bm.isOpaque() && !SkBitmap::ComputeIsOpaque(bm)) {
+ if (is_transparent(bm)) {
+ return NULL;
+ }
+ // PDFAlphaBitmaps 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;
+}
diff --git a/src/pdf/SkPDFBitmap.h b/src/pdf/SkPDFBitmap.h
new file mode 100644
index 0000000000..922db8f1c1
--- /dev/null
+++ b/src/pdf/SkPDFBitmap.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkPDFBitmap_DEFINED
+#define SkPDFBitmap_DEFINED
+
+#include "SkPDFTypes.h"
+#include "SkBitmap.h"
+
+/**
+ * SkPDFBitmap wraps a SkBitmap and serializes it as an image Xobject.
+ * It is designed to use a minimal amout of memory, aside from refing
+ * the bitmap's pixels, and its emitObject() does not cache any data.
+ *
+ * As of now, it only supports 8888 bitmaps (the most common case).
+ *
+ * The SkPDFBitmap::Create function will check the canon for duplicates.
+ */
+class SkPDFBitmap : public SkPDFObject {
+public:
+ // Returns NULL on unsupported bitmap;
+ // TODO(halcanary): support other bitmap colortypes and replace
+ // SkPDFImage.
+ static SkPDFBitmap* Create(const SkBitmap&, const SkIRect& subset);
+ ~SkPDFBitmap();
+ void emitObject(SkWStream*, SkPDFCatalog*) SK_OVERRIDE;
+ void addResources(SkTSet<SkPDFObject*>* resourceSet,
+ SkPDFCatalog* catalog) const SK_OVERRIDE;
+ bool equals(const SkBitmap& other) const {
+ return fBitmap.getGenerationID() == other.getGenerationID() &&
+ fBitmap.pixelRefOrigin() == other.pixelRefOrigin() &&
+ fBitmap.dimensions() == other.dimensions();
+ }
+
+private:
+ const SkBitmap fBitmap;
+ const SkAutoTUnref<SkPDFObject> fSMask;
+ SkPDFBitmap(const SkBitmap&, SkPDFObject*);
+ void emitDict(SkWStream*, SkPDFCatalog*, size_t, bool) const;
+};
+
+#endif // SkPDFBitmap_DEFINED
diff --git a/src/pdf/SkPDFCanon.cpp b/src/pdf/SkPDFCanon.cpp
index 6b52018ce4..268a53a897 100644
--- a/src/pdf/SkPDFCanon.cpp
+++ b/src/pdf/SkPDFCanon.cpp
@@ -6,6 +6,7 @@
*/
#include "SkLazyPtr.h"
+#include "SkPDFBitmap.h"
#include "SkPDFCanon.h"
#include "SkPDFFont.h"
#include "SkPDFGraphicState.h"
@@ -16,10 +17,12 @@
SK_DECLARE_STATIC_MUTEX(gSkPDFCanonFontMutex);
SK_DECLARE_STATIC_MUTEX(gSkPDFCanonShaderMutex);
SK_DECLARE_STATIC_MUTEX(gSkPDFCanonPaintMutex);
+SK_DECLARE_STATIC_MUTEX(gSkPDFCanonBitmapMutex);
SkBaseMutex& SkPDFCanon::GetFontMutex() { return gSkPDFCanonFontMutex; }
SkBaseMutex& SkPDFCanon::GetShaderMutex() { return gSkPDFCanonShaderMutex; }
SkBaseMutex& SkPDFCanon::GetPaintMutex() { return gSkPDFCanonPaintMutex; }
+SkBaseMutex& SkPDFCanon::GetBitmapMutex() { return gSkPDFCanonBitmapMutex; }
SkPDFCanon::SkPDFCanon() {}
@@ -168,3 +171,20 @@ void SkPDFCanon::removeGraphicState(SkPDFGraphicState* pdfGraphicState) {
assert_mutex_held(this, gSkPDFCanonPaintMutex);
SkAssertResult(remove_item(&fGraphicStateRecords, pdfGraphicState));
}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SkPDFBitmap* SkPDFCanon::findBitmap(const SkBitmap& bm) const {
+ assert_mutex_held(this, gSkPDFCanonBitmapMutex);
+ return find_item(fBitmapRecords, bm);
+}
+
+void SkPDFCanon::addBitmap(SkPDFBitmap* pdfBitmap) {
+ assert_mutex_held(this, gSkPDFCanonBitmapMutex);
+ fBitmapRecords.push(assert_ptr(pdfBitmap));
+}
+
+void SkPDFCanon::removeBitmap(SkPDFBitmap* pdfBitmap) {
+ assert_mutex_held(this, gSkPDFCanonBitmapMutex);
+ SkAssertResult(remove_item(&fBitmapRecords, pdfBitmap));
+}
diff --git a/src/pdf/SkPDFCanon.h b/src/pdf/SkPDFCanon.h
index 8e89424084..6a677e9a68 100644
--- a/src/pdf/SkPDFCanon.h
+++ b/src/pdf/SkPDFCanon.h
@@ -12,9 +12,11 @@
#include "SkTDArray.h"
struct SkIRect;
+class SkBitmap;
class SkMatrix;
class SkPDFFont;
class SkPDFGraphicState;
+class SkPDFBitmap;
class SkPaint;
// This class's fields and methods will eventually become part of
@@ -40,6 +42,7 @@ public:
static SkBaseMutex& GetFontMutex();
static SkBaseMutex& GetShaderMutex();
static SkBaseMutex& GetPaintMutex();
+ static SkBaseMutex& GetBitmapMutex();
// Returns exact match if there is one. If not, it returns NULL.
// If there is no exact match, but there is a related font, we
@@ -66,6 +69,10 @@ public:
void addGraphicState(SkPDFGraphicState*);
void removeGraphicState(SkPDFGraphicState*);
+ SkPDFBitmap* findBitmap(const SkBitmap&) const;
+ void addBitmap(SkPDFBitmap*);
+ void removeBitmap(SkPDFBitmap*);
+
private:
struct FontRec {
SkPDFFont* fFont;
@@ -81,5 +88,7 @@ private:
SkTDArray<SkPDFImageShader*> fImageShaderRecords;
SkTDArray<SkPDFGraphicState*> fGraphicStateRecords;
+
+ SkTDArray<SkPDFBitmap*> fBitmapRecords;
};
#endif // SkPDFCanon_DEFINED
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
index 0b6c81f77a..dab7473684 100644
--- a/src/pdf/SkPDFImage.cpp
+++ b/src/pdf/SkPDFImage.cpp
@@ -12,6 +12,7 @@
#include "SkColorPriv.h"
#include "SkData.h"
#include "SkFlate.h"
+#include "SkPDFBitmap.h"
#include "SkPDFCatalog.h"
#include "SkPixelRef.h"
#include "SkRect.h"
@@ -728,6 +729,9 @@ SkPDFObject* SkPDFCreateImageObject(
const SkBitmap& bitmap,
const SkIRect& subset,
SkPicture::EncodeBitmap encoder) {
+ if (SkPDFObject* pdfBitmap = SkPDFBitmap::Create(bitmap, subset)) {
+ return pdfBitmap;
+ }
#if 0 // reenable when we can figure out the JPEG colorspace
if (SkIRect::MakeWH(bitmap.width(), bitmap.height()) == subset) {
SkAutoTUnref<SkData> encodedData(ref_encoded_data(bitmap));