From e322482f4d82bc704e40b8c89843f2ea5b6ddc04 Mon Sep 17 00:00:00 2001 From: halcanary Date: Mon, 14 Jul 2014 09:12:12 -0700 Subject: Move SkPDFStream back to SkStream to save memory. SkPDFStream stores data as a SkStreamRewindable to minimize deep duplication and memory overhead. SkStreamToStreamRewindable function to deal with fact that SkTypeface returns a SkStream. BUG=skia:2743 R=djsollen@google.com, mtklein@google.com, bungeman@google.com, reed@google.com Author: halcanary@google.com Review URL: https://codereview.chromium.org/387863005 --- src/core/SkStream.cpp | 32 ++++++++++++++++++++++++++++++++ src/core/SkStreamPriv.h | 9 +++++++++ src/pdf/SkPDFStream.cpp | 34 ++++++++++++++++++++++------------ src/pdf/SkPDFStream.h | 22 ++++++++++++---------- 4 files changed, 75 insertions(+), 22 deletions(-) diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp index 1022d183d9..5069bb0a7e 100644 --- a/src/core/SkStream.cpp +++ b/src/core/SkStream.cpp @@ -907,3 +907,35 @@ SkData* SkCopyStreamToData(SkStream* stream) { } while (!stream->isAtEnd()); return tempStream.copyToData(); } + +SkStreamRewindable* SkStreamRewindableFromSkStream(SkStream* stream) { + if (!stream) { + return NULL; + } + SkAutoTUnref dupStream(stream->duplicate()); + if (dupStream) { + return dupStream.detach(); + } + stream->rewind(); + if (stream->hasLength()) { + size_t length = stream->getLength(); + if (stream->hasPosition()) { // If stream has length, but can't rewind. + length -= stream->getPosition(); + } + SkAutoMalloc allocMemory(length); + SkDEBUGCODE(size_t read =) stream->read(allocMemory.get(), length); + SkASSERT(length == read); + SkAutoTUnref data( + SkData::NewFromMalloc(allocMemory.detach(), length)); + return SkNEW_ARGS(SkMemoryStream, (data.get())); + } + SkDynamicMemoryWStream tempStream; + const size_t bufferSize = 4096; + char buffer[bufferSize]; + do { + size_t bytesRead = stream->read(buffer, bufferSize); + tempStream.write(buffer, bytesRead); + } while (!stream->isAtEnd()); + return tempStream.detachAsStream(); // returns a SkBlockMemoryStream, + // cheaper than copying to SkData +} diff --git a/src/core/SkStreamPriv.h b/src/core/SkStreamPriv.h index 5b5a73adef..718097d1cc 100644 --- a/src/core/SkStreamPriv.h +++ b/src/core/SkStreamPriv.h @@ -10,6 +10,7 @@ class SkAutoMalloc; class SkStream; +class SkStreamRewindable; class SkData; /** @@ -34,4 +35,12 @@ size_t SkCopyStreamToStorage(SkAutoMalloc* storage, SkStream* stream); */ SkData *SkCopyStreamToData(SkStream* stream); +/** + * Attempt to convert this stream to a StreamRewindable in the + * cheapest possible manner (calling duplicate() if possible, and + * otherwise allocating memory for a copy). The position of the + * input stream is left in an indeterminate state. + */ +SkStreamRewindable* SkStreamRewindableFromSkStream(SkStream* stream); + #endif // SkStreamPriv_DEFINED diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp index 8715d9376b..60fce0d6f1 100644 --- a/src/pdf/SkPDFStream.cpp +++ b/src/pdf/SkPDFStream.cpp @@ -30,7 +30,7 @@ SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) { SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream) : SkPDFDict(), fState(kUnused_State) { - this->setData(pdfStream.fData.get()); + this->setData(pdfStream.fDataStream.get()); bool removeLength = true; // Don't uncompress an already compressed stream, but we could. if (pdfStream.fState == kCompressed_State) { @@ -57,9 +57,8 @@ void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog, this->INHERITED::emitObject(stream, catalog, false); stream->writeText(" stream\n"); - if (fData.get()) { - stream->write(fData->data(), fData->size()); - } + stream->writeStream(fDataStream.get(), fDataStream->getLength()); + SkAssertResult(fDataStream->rewind()); stream->writeText("\nendstream"); } @@ -79,22 +78,31 @@ size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) { SkPDFStream::SkPDFStream() : fState(kUnused_State) {} void SkPDFStream::setData(SkData* data) { - fData.reset(SkSafeRef(data)); + fMemoryStream.setData(data); + if (&fMemoryStream != fDataStream.get()) { + fDataStream.reset(SkRef(&fMemoryStream)); + } } void SkPDFStream::setData(SkStream* stream) { // Code assumes that the stream starts at the beginning and is rewindable. + if (&fMemoryStream == fDataStream.get()) { + SkASSERT(&fMemoryStream != stream); + fMemoryStream.setData(NULL); + } + SkASSERT(0 == fMemoryStream.getLength()); if (stream) { - SkASSERT(stream->getPosition() == 0); - fData.reset(SkCopyStreamToData(stream)); - SkAssertResult(stream->rewind()); + // SkStreamRewindableFromSkStream will try stream->duplicate(). + fDataStream.reset(SkStreamRewindableFromSkStream(stream)); + SkASSERT(fDataStream.get()); } else { - fData.reset(NULL); + fDataStream.reset(SkRef(&fMemoryStream)); } } size_t SkPDFStream::dataSize() const { - return fData.get() ? fData->size() : 0; + SkASSERT(fDataStream->hasLength()); + return fDataStream->getLength(); } bool SkPDFStream::populate(SkPDFCatalog* catalog) { @@ -102,9 +110,11 @@ bool SkPDFStream::populate(SkPDFCatalog* catalog) { if (!skip_compression(catalog) && SkFlate::HaveFlate()) { SkDynamicMemoryWStream compressedData; - SkAssertResult(SkFlate::Deflate(fData.get(), &compressedData)); + SkAssertResult( + SkFlate::Deflate(fDataStream.get(), &compressedData)); + SkAssertResult(fDataStream->rewind()); if (compressedData.getOffset() < this->dataSize()) { - fData.reset(compressedData.copyToData()); + this->setData(compressedData.detachAsStream()); insertName("Filter", "FlateDecode"); } fState = kCompressed_State; diff --git a/src/pdf/SkPDFStream.h b/src/pdf/SkPDFStream.h index 3a84068e3d..f908fbf2f3 100644 --- a/src/pdf/SkPDFStream.h +++ b/src/pdf/SkPDFStream.h @@ -21,19 +21,20 @@ class SkPDFCatalog; A stream object in a PDF. Note, all streams must be indirect objects (via SkObjRef). - TODO(vandebo): SkStream should be replaced by SkStreamRewindable when that - is feasible. */ class SkPDFStream : public SkPDFDict { SK_DECLARE_INST_COUNT(SkPDFStream) public: /** Create a PDF stream. A Length entry is automatically added to the - * stream dictionary. The stream may be retained (stream->ref() may be - * called) so its contents must not be changed after calling this. - * @param data The data part of the stream. + * stream dictionary. + * @param data The data part of the stream. Will be ref()ed. */ explicit SkPDFStream(SkData* data); - /** Deprecated constructor. */ + + /** Create a PDF stream. A Length entry is automatically added to the + * stream dictionary. + * @param stream The data part of the stream. Will be duplicate()d. + */ explicit SkPDFStream(SkStream* stream); virtual ~SkPDFStream(); @@ -79,8 +80,6 @@ protected: size_t dataSize() const; - SkData* getData() const { return fData.get(); } - void setState(State state) { fState = state; } @@ -93,10 +92,13 @@ private: // Indicates what form (or if) the stream has been requested. State fState; - // Mutex guards fState, fData, and fSubstitute in public interface. + // Mutex guards fState, fDataStream, and fSubstitute in public interface. SkMutex fMutex; - SkAutoTUnref fData; + SkMemoryStream fMemoryStream; // Used by fDataStream when + // fDataStream needs to be backed + // by SkData. + SkAutoTUnref fDataStream; SkAutoTUnref fSubstitute; typedef SkPDFDict INHERITED; -- cgit v1.2.3