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
This commit is contained in:
halcanary 2014-07-14 09:12:12 -07:00 коммит произвёл Commit bot
Родитель 9c6878be37
Коммит e322482f4d
4 изменённых файлов: 75 добавлений и 22 удалений

Просмотреть файл

@ -907,3 +907,35 @@ SkData* SkCopyStreamToData(SkStream* stream) {
} 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
}

Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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<SkData> fData;
SkMemoryStream fMemoryStream; // Used by fDataStream when
// fDataStream needs to be backed
// by SkData.
SkAutoTUnref<SkStreamRewindable> fDataStream;
SkAutoTUnref<SkPDFStream> fSubstitute;
typedef SkPDFDict INHERITED;