diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp index 98525a5394..3c01156653 100644 --- a/gm/gmmain.cpp +++ b/gm/gmmain.cpp @@ -410,11 +410,9 @@ public: gm_fprintf(stdout, "(results marked with [*] will cause nonzero return value)\n"); } - static bool write_document(const SkString& path, - const SkDynamicMemoryWStream& document) { + static bool write_document(const SkString& path, SkStreamAsset* asset) { SkFILEWStream stream(path.c_str()); - SkAutoDataUnref data(document.copyToData()); - return stream.write(data->data(), data->size()); + return stream.writeStream(asset, asset->getLength()); } /** @@ -665,7 +663,7 @@ public: const char renderModeDescriptor [], const char *shortName, const BitmapAndDigest* bitmapAndDigest, - SkDynamicMemoryWStream* document) { + SkStreamAsset* document) { SkString path; bool success = false; if (gRec.fBackend == kRaster_Backend || @@ -679,12 +677,12 @@ public: if (kPDF_Backend == gRec.fBackend) { path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor, "pdf"); - success = write_document(path, *document); + success = write_document(path, document); } if (kXPS_Backend == gRec.fBackend) { path = make_filename(writePath, shortName, gRec.fName, renderModeDescriptor, "xps"); - success = write_document(path, *document); + success = write_document(path, document); } if (success) { return kEmpty_ErrorCombination; @@ -911,7 +909,7 @@ public: */ ErrorCombination compare_test_results_to_stored_expectations( GM* gm, const ConfigData& gRec, const char writePath[], - const BitmapAndDigest* actualBitmapAndDigest, SkDynamicMemoryWStream* document) { + const BitmapAndDigest* actualBitmapAndDigest, SkStreamAsset* document) { SkString shortNamePlusConfig = make_shortname_plus_config(gm->shortName(), gRec.fName); SkString nameWithExtension(shortNamePlusConfig); @@ -1025,24 +1023,10 @@ public: } static SkPicture* stream_to_new_picture(const SkPicture& src) { - - // To do in-memory commiunications with a stream, we need to: - // * create a dynamic memory stream - // * copy it into a buffer - // * create a read stream from it - // ?!?! - SkDynamicMemoryWStream storage; src.serialize(&storage); - - size_t streamSize = storage.getOffset(); - SkAutoMalloc dstStorage(streamSize); - void* dst = dstStorage.get(); - //char* dst = new char [streamSize]; - //@todo thudson 22 April 2011 when can we safely delete [] dst? - storage.copyTo(dst); - SkMemoryStream pictReadback(dst, streamSize); - SkPicture* retval = SkPicture::CreateFromStream(&pictReadback); + SkAutoTUnref pictReadback(storage.detatchAsStream()); + SkPicture* retval = SkPicture::CreateFromStream(pictReadback); return retval; } @@ -1079,13 +1063,14 @@ public: bitmap = NULL; // we don't generate a bitmap rendering of the XPS file } + SkAutoTUnref documentStream(document.detatchAsStream()); if (NULL == bitmap) { return compare_test_results_to_stored_expectations( - gm, gRec, writePath, NULL, &document); + gm, gRec, writePath, NULL, documentStream); } else { BitmapAndDigest bitmapAndDigest(*bitmap); return compare_test_results_to_stored_expectations( - gm, gRec, writePath, &bitmapAndDigest, &document); + gm, gRec, writePath, &bitmapAndDigest, documentStream); } } @@ -1093,8 +1078,6 @@ public: const ConfigData& gRec, const SkBitmap& referenceBitmap, GrSurface* gpuTarget) { - SkDynamicMemoryWStream document; - if (gRec.fBackend == kRaster_Backend || gRec.fBackend == kGPU_Backend) { const char renderModeDescriptor[] = "-deferred"; diff --git a/include/core/SkStream.h b/include/core/SkStream.h index 31ed7bc6d4..1930f3f195 100644 --- a/include/core/SkStream.h +++ b/include/core/SkStream.h @@ -280,7 +280,7 @@ public: virtual size_t getLength() const SK_OVERRIDE; - const void* getMemoryBase() SK_OVERRIDE; + virtual const void* getMemoryBase() SK_OVERRIDE; private: SkFILE* fFILE; @@ -423,7 +423,10 @@ public: */ SkData* copyToData() const; - // reset the stream to its original state + /** Reset, returning a reader stream with the current content. */ + SkStreamAsset* detatchAsStream(); + + /** Reset the stream to its original, empty, state. */ void reset(); void padToAlign4(); private: @@ -435,6 +438,10 @@ private: void invalidateCopy(); + // For access to the Block type. + friend class SkBlockMemoryStream; + friend class SkBlockMemoryRefCnt; + typedef SkWStream INHERITED; }; diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp index 91de88adac..b251905633 100644 --- a/src/core/SkStream.cpp +++ b/src/core/SkStream.cpp @@ -661,6 +661,138 @@ void SkDynamicMemoryWStream::invalidateCopy() { } } +class SkBlockMemoryRefCnt : public SkRefCnt { +public: + explicit SkBlockMemoryRefCnt(SkDynamicMemoryWStream::Block* head) : fHead(head) { } + + virtual ~SkBlockMemoryRefCnt() { + SkDynamicMemoryWStream::Block* block = fHead; + while (block != NULL) { + SkDynamicMemoryWStream::Block* next = block->fNext; + sk_free(block); + block = next; + } + } + + SkDynamicMemoryWStream::Block* const fHead; +}; + +class SkBlockMemoryStream : public SkStreamAsset { +public: + SkBlockMemoryStream(SkDynamicMemoryWStream::Block* head, size_t size) + : fBlockMemory(SkNEW_ARGS(SkBlockMemoryRefCnt, (head))), fCurrent(head) + , fSize(size) , fOffset(0), fCurrentOffset(0) { } + + SkBlockMemoryStream(SkBlockMemoryRefCnt* headRef, size_t size) + : fBlockMemory(SkRef(headRef)), fCurrent(fBlockMemory->fHead) + , fSize(size) , fOffset(0), fCurrentOffset(0) { } + + virtual size_t read(void* buffer, size_t rawCount) SK_OVERRIDE { + size_t count = rawCount; + if (fOffset + count > fSize) { + count = fSize - fOffset; + } + size_t bytesLeftToRead = count; + while (fCurrent != NULL) { + size_t bytesLeftInCurrent = fCurrent->written() - fCurrentOffset; + size_t bytesFromCurrent = bytesLeftToRead <= bytesLeftInCurrent + ? bytesLeftToRead : bytesLeftInCurrent; + if (buffer) { + memcpy(buffer, fCurrent->start() + fCurrentOffset, bytesFromCurrent); + } + if (bytesLeftToRead <= bytesFromCurrent) { + fCurrentOffset += bytesFromCurrent; + fOffset += count; + return count; + } + bytesLeftToRead -= bytesFromCurrent; + buffer = SkTAddOffset(buffer, bytesFromCurrent); + fCurrent = fCurrent->fNext; + fCurrentOffset = 0; + } + SkASSERT(false); + return 0; + } + + virtual bool isAtEnd() const SK_OVERRIDE { + return fOffset == fSize; + } + + virtual bool rewind() SK_OVERRIDE { + fCurrent = fBlockMemory->fHead; + fOffset = 0; + fCurrentOffset = 0; + return true; + } + + virtual SkBlockMemoryStream* duplicate() const SK_OVERRIDE { + return SkNEW_ARGS(SkBlockMemoryStream, (fBlockMemory.get(), fSize)); + } + + virtual size_t getPosition() const SK_OVERRIDE { + return fOffset; + } + + virtual bool seek(size_t position) SK_OVERRIDE { + // If possible, skip forward. + if (position >= fOffset) { + size_t skipAmount = position - fOffset; + return this->skip(skipAmount) == skipAmount; + } + // If possible, move backward within the current block. + size_t moveBackAmount = fOffset - position; + if (moveBackAmount <= fCurrentOffset) { + fCurrentOffset -= moveBackAmount; + fOffset -= moveBackAmount; + return true; + } + // Otherwise rewind and move forward. + return this->rewind() && this->skip(position) == position; + } + + virtual bool move(long offset) SK_OVERRIDE { + return seek(fOffset + offset); + } + + virtual SkBlockMemoryStream* fork() const SK_OVERRIDE { + SkAutoTUnref that(this->duplicate()); + that->fCurrent = this->fCurrent; + that->fOffset = this->fOffset; + that->fCurrentOffset = this->fCurrentOffset; + return that.detach(); + } + + virtual size_t getLength() const SK_OVERRIDE { + return fSize; + } + + virtual const void* getMemoryBase() SK_OVERRIDE { + if (NULL == fBlockMemory->fHead->fNext) { + return fBlockMemory->fHead->start(); + } + return NULL; + } + +private: + SkAutoTUnref const fBlockMemory; + SkDynamicMemoryWStream::Block const * fCurrent; + size_t const fSize; + size_t fOffset; + size_t fCurrentOffset; +}; + +SkStreamAsset* SkDynamicMemoryWStream::detatchAsStream() { + if (fCopy) { + SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (fCopy)); + this->reset(); + return stream; + } + SkBlockMemoryStream* stream = SkNEW_ARGS(SkBlockMemoryStream, (fHead, fBytesWritten)); + fHead = 0; + this->reset(); + return stream; +} + /////////////////////////////////////////////////////////////////////////////// void SkDebugWStream::newline() diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp index cf828430a9..b3a427f9f8 100644 --- a/tests/StreamTest.cpp +++ b/tests/StreamTest.cpp @@ -30,6 +30,7 @@ static void test_loop_stream(skiatest::Reporter* reporter, SkStream* stream, } // expect EOF + REPORTER_ASSERT(reporter, stream->isAtEnd()); size_t bytes = stream->read(tmp, 1); REPORTER_ASSERT(reporter, 0 == bytes); } @@ -81,6 +82,7 @@ static void TestWStream(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, ds.write(s, 26)); } REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26); + char* dst = new char[100 * 26 + 1]; dst[100*26] = '*'; ds.copyTo(dst); @@ -90,10 +92,42 @@ static void TestWStream(skiatest::Reporter* reporter) { } { - SkData* data = ds.copyToData(); + SkAutoTUnref stream(ds.detatchAsStream()); + REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength()); + REPORTER_ASSERT(reporter, ds.getOffset() == 0); + test_loop_stream(reporter, stream.get(), s, 26, 100); + + SkAutoTUnref stream2(stream->duplicate()); + test_loop_stream(reporter, stream2.get(), s, 26, 100); + + SkAutoTUnref stream3(stream->fork()); + REPORTER_ASSERT(reporter, stream3->isAtEnd()); + char tmp; + size_t bytes = stream->read(&tmp, 1); + REPORTER_ASSERT(reporter, 0 == bytes); + stream3->rewind(); + test_loop_stream(reporter, stream3.get(), s, 26, 100); + } + + for (i = 0; i < 100; i++) { + REPORTER_ASSERT(reporter, ds.write(s, 26)); + } + REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26); + + { + SkAutoTUnref data(ds.copyToData()); REPORTER_ASSERT(reporter, 100 * 26 == data->size()); REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0); - data->unref(); + } + + { + // Test that this works after a copyToData. + SkAutoTUnref stream(ds.detatchAsStream()); + REPORTER_ASSERT(reporter, ds.getOffset() == 0); + test_loop_stream(reporter, stream.get(), s, 26, 100); + + SkAutoTUnref stream2(stream->duplicate()); + test_loop_stream(reporter, stream2.get(), s, 26, 100); } delete[] dst;