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
diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp
index 1022d18..5069bb0 100644
--- a/src/core/SkStream.cpp
+++ b/src/core/SkStream.cpp
@@ -907,3 +907,35 @@
} while (!stream->isAtEnd());
return tempStream.copyToData();
}
+
+SkStreamRewindable* SkStreamRewindableFromSkStream(SkStream* stream) {
+ if (!stream) {
+ return NULL;
+ }
+ SkAutoTUnref<SkStreamRewindable> 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<SkData> 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 5b5a73a..718097d 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 @@
*/
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 8715d93..60fce0d 100644
--- a/src/pdf/SkPDFStream.cpp
+++ b/src/pdf/SkPDFStream.cpp
@@ -30,7 +30,7 @@
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 @@
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 @@
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 @@
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 3a84068..f908fbf 100644
--- a/src/pdf/SkPDFStream.h
+++ b/src/pdf/SkPDFStream.h
@@ -21,19 +21,20 @@
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 @@
size_t dataSize() const;
- SkData* getData() const { return fData.get(); }
-
void setState(State state) {
fState = state;
}
@@ -93,10 +92,13 @@
// 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<SkData> fData;
+ SkMemoryStream fMemoryStream; // Used by fDataStream when
+ // fDataStream needs to be backed
+ // by SkData.
+ SkAutoTUnref<SkStreamRewindable> fDataStream;
SkAutoTUnref<SkPDFStream> fSubstitute;
typedef SkPDFDict INHERITED;