diff --git a/gn/core.gni b/gn/core.gni index a405db4cc3..f6f8852aac 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -281,7 +281,6 @@ skia_core_sources = [ "$_src/core/SkSemaphore.cpp", "$_src/core/SkSharedMutex.cpp", "$_src/core/SkSharedMutex.h", - "$_src/core/SkSinglyLinkedList.h", "$_src/core/SkSpan.h", "$_src/core/SkSpecialImage.cpp", "$_src/core/SkSpecialImage.h", diff --git a/include/core/SkStream.h b/include/core/SkStream.h index f8f0fdbbc0..f1c1a143d6 100644 --- a/include/core/SkStream.h +++ b/include/core/SkStream.h @@ -477,6 +477,13 @@ public: /** Equivalent to writeToStream() followed by reset(), but may save memory use. */ bool writeToAndReset(SkWStream* dst); + /** Equivalent to writeToStream() followed by reset(), but may save memory use. + When the dst is also a SkDynamicMemoryWStream, the implementation is constant time. */ + bool writeToAndReset(SkDynamicMemoryWStream* dst); + + /** Prepend this stream to dst, resetting this. */ + void prependToAndReset(SkDynamicMemoryWStream* dst); + /** Return the contents as SkData, and then reset the stream. */ sk_sp detachAsData(); diff --git a/src/core/SkSinglyLinkedList.h b/src/core/SkSinglyLinkedList.h deleted file mode 100644 index 1951f19ff1..0000000000 --- a/src/core/SkSinglyLinkedList.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkSinglyLinkedList_DEFINED -#define SkSinglyLinkedList_DEFINED - -#include - -#include "SkMakeUnique.h" -#include "SkTypes.h" - -template class SkSinglyLinkedList { - struct Node; -public: - SkSinglyLinkedList() {} - ~SkSinglyLinkedList() { this->reset(); } - void reset() { - SkASSERT(fHead != nullptr || nullptr == fTail); - // Use a while loop rather than recursion to avoid stack overflow. - std::unique_ptr node = std::move(fHead); - while (node) { - std::unique_ptr next = std::move(node->fNext); - SkASSERT(next || node.get() == fTail); - node = std::move(next); - } - fTail = nullptr; - } - T* back() { return fTail ? &fTail->fData : nullptr; } - T* front() { return fHead ? &fHead->fData : nullptr; } - bool empty() const { return fHead == nullptr; } - #ifdef SK_DEBUG - int count() { // O(n), debug only. - int count = 0; - for (Node* node = fHead.get(); node; node = node->fNext.get()) { - ++count; - } - return count; - } - #endif - void pop_front() { - if (fHead) { - fHead = std::move(fHead->fNext); - if (!fHead) { - fTail = nullptr; - } - } - } - template T* emplace_front(Args&&... args) { - fHead = skstd::make_unique(std::move(fHead), std::forward(args)...); - if (!fTail) { - fTail = fHead.get(); - } - return &fHead->fData; - } - template T* emplace_back(Args&&... args) { - std::unique_ptr* dst = fTail ? &fTail->fNext : &fHead; - *dst = skstd::make_unique(nullptr, std::forward(args)...); - fTail = dst->get(); - return &fTail->fData; - } - - class Iter { - public: - void operator++() { fNode = fNode->fNext.get(); } - T& operator*() { return fNode->fData; } - bool operator!=(const Iter& rhs) const { return fNode != rhs.fNode; } - Iter(Node* n) : fNode(n) {} - private: - Node* fNode; - }; - Iter begin() { return Iter(fHead.get()); } - Iter end() { return Iter(nullptr); } - - class ConstIter { - public: - void operator++() { fNode = fNode->fNext.get(); } - const T& operator*() const { return fNode->fData; } - bool operator!=(const ConstIter& rhs) const { return fNode != rhs.fNode; } - ConstIter(const Node* n) : fNode(n) {} - private: - const Node* fNode; - }; - ConstIter begin() const { return ConstIter(fHead.get()); } - ConstIter end() const { return ConstIter(nullptr); } - -private: - struct Node { - T fData; - std::unique_ptr fNext; - template - Node(std::unique_ptr n, Args&&... args) - : fData(std::forward(args)...), fNext(std::move(n)) {} - }; - std::unique_ptr fHead; - Node* fTail = nullptr; - SkSinglyLinkedList(const SkSinglyLinkedList&) = delete; - SkSinglyLinkedList& operator=(const SkSinglyLinkedList&) = delete; -}; -#endif // SkSinglyLinkedList_DEFINED diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp index b80efbd9e8..7245d7524b 100644 --- a/src/core/SkStream.cpp +++ b/src/core/SkStream.cpp @@ -555,6 +555,43 @@ bool SkDynamicMemoryWStream::write(const void* buffer, size_t count) { return true; } +bool SkDynamicMemoryWStream::writeToAndReset(SkDynamicMemoryWStream* dst) { + SkASSERT(dst); + SkASSERT(dst != this); + if (0 == this->bytesWritten()) { + return true; + } + if (0 == dst->bytesWritten()) { + *dst = std::move(*this); + return true; + } + dst->fTail->fNext = fHead; + dst->fBytesWrittenBeforeTail += fBytesWrittenBeforeTail + dst->fTail->written(); + dst->fTail = fTail; + fHead = fTail = nullptr; + fBytesWrittenBeforeTail = 0; + return true; +} + +void SkDynamicMemoryWStream::prependToAndReset(SkDynamicMemoryWStream* dst) { + SkASSERT(dst); + SkASSERT(dst != this); + if (0 == this->bytesWritten()) { + return; + } + if (0 == dst->bytesWritten()) { + *dst = std::move(*this); + return; + } + fTail->fNext = dst->fHead; + dst->fHead = fHead; + dst->fBytesWrittenBeforeTail += fBytesWrittenBeforeTail + fTail->written(); + fHead = fTail = nullptr; + fBytesWrittenBeforeTail = 0; + return; +} + + bool SkDynamicMemoryWStream::read(void* buffer, size_t offset, size_t count) { if (offset + count > this->bytesWritten()) { return false; // test does not partially modify @@ -676,9 +713,7 @@ void SkDynamicMemoryWStream::validate() const { const Block* block = fHead; while (block) { if (block->fNext) { - SkASSERT(block->avail() == 0); bytes += block->written(); - SkASSERT(bytes == SkAlign4(bytes)); // see padToAlign4() } block = block->fNext; } diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 6ed4e639e1..bff984dc7e 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -171,6 +171,8 @@ void remove_color_filter(SkPaint* paint) { } } +SkPDFDevice::GraphicStackState::GraphicStackState(SkDynamicMemoryWStream* s) : fContentStream(s) { +} void SkPDFDevice::GraphicStackState::drainStack() { if (fContentStream) { @@ -408,9 +410,7 @@ public: const SkPaint& paint, bool hasText = false) : fDevice(device) - , fContentEntry(nullptr) , fBlendMode(SkBlendMode::kSrcOver) - , fDstFormXObject(nullptr) , fClipStack(clipStack) { if (matrix.hasPerspective()) { @@ -418,14 +418,14 @@ public: return; } fBlendMode = paint.getBlendMode(); - fContentEntry = + fContentStream = fDevice->setUpContentEntry(clipStack, matrix, paint, hasText, &fDstFormXObject); } ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, bool hasText = false) : ScopedContentEntry(dev, &dev->cs(), dev->ctm(), paint, hasText) {} ~ScopedContentEntry() { - if (fContentEntry) { + if (fContentStream) { SkPath* shape = &fShape; if (shape->isEmpty()) { shape = nullptr; @@ -434,8 +434,8 @@ public: } } - SkDynamicMemoryWStream* entry() { return fContentEntry; } - SkDynamicMemoryWStream* stream() { return fContentEntry; } + explicit operator bool() const { return fContentStream != nullptr; } + SkDynamicMemoryWStream* stream() { return fContentStream; } /* Returns true when we explicitly need the shape of the drawing. */ bool needShape() { @@ -472,8 +472,8 @@ public: } private: - SkPDFDevice* fDevice; - SkDynamicMemoryWStream* fContentEntry; + SkPDFDevice* fDevice = nullptr; + SkDynamicMemoryWStream* fContentStream = nullptr; SkBlendMode fBlendMode; sk_sp fDstFormXObject; SkPath fShape; @@ -502,7 +502,7 @@ void SkPDFDevice::reset() { fXObjectResources = std::vector>(); fShaderResources = std::vector>(); fFontResources = std::vector>(); - fContentEntries.reset(); + fContent.reset(); fActiveStackState = GraphicStackState(); } @@ -610,7 +610,7 @@ void SkPDFDevice::drawPoints(SkCanvas::PointMode mode, } ScopedContentEntry content(this, *paint); - if (!content.entry()) { + if (!content) { return; } SkDynamicMemoryWStream* contentStream = content.stream(); @@ -695,7 +695,7 @@ void SkPDFDevice::drawRect(const SkRect& rect, } ScopedContentEntry content(this, paint); - if (!content.entry()) { + if (!content) { return; } SkPDFUtils::AppendRectangle(r, content.stream()); @@ -773,7 +773,7 @@ void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack, transform_shader(paint.writable(), ctm); // Since we are using identity matrix. } ScopedContentEntry content(this, &clipStack, SkMatrix::I(), *paint); - if (!content.entry()) { + if (!content) { return; } this->addSMaskGraphicState(std::move(maskDevice), content.stream()); @@ -875,7 +875,7 @@ void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack, } ScopedContentEntry content(this, &clipStack, matrix, paint); - if (!content.entry()) { + if (!content) { return; } constexpr SkScalar kToleranceScale = 0.0625f; // smaller = better conics (circles). @@ -1124,7 +1124,7 @@ void SkPDFDevice::internalDrawGlyphRun(const SkGlyphRun& glyphRun, SkPoint offse SkRect clipStackBounds = this->cs().bounds(this->bounds()); { ScopedContentEntry content(this, paint, true); - if (!content.entry()) { + if (!content) { return; } SkDynamicMemoryWStream* out = content.stream(); @@ -1300,7 +1300,7 @@ void SkPDFDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& SkMatrix matrix = SkMatrix::MakeTrans(SkIntToScalar(x), SkIntToScalar(y)); ScopedContentEntry content(this, &this->cs(), matrix, paint); - if (!content.entry()) { + if (!content) { return; } if (content.needShape()) { @@ -1330,26 +1330,22 @@ std::unique_ptr SkPDFDevice::content() { fActiveStackState.drainStack(); fActiveStackState = GraphicStackState(); } - + if (fContent.bytesWritten() == 0) { + return skstd::make_unique(); + } SkDynamicMemoryWStream buffer; if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { append_transform(fInitialTransform, &buffer); } - if (fContentEntries.back() && fContentEntries.back() == fContentEntries.front()) { - fContentEntries.front()->writeToAndReset(&buffer); - } else { - for (SkDynamicMemoryWStream& entry : fContentEntries) { - buffer.writeText("q\n"); - entry.writeToAndReset(&buffer); - buffer.writeText("Q\n"); - } + if (fNeedsExtraSave) { + buffer.writeText("q\n"); } - fContentEntries.reset(); - if (buffer.bytesWritten() > 0) { - return std::unique_ptr(buffer.detachAsStream()); - } else { - return skstd::make_unique(); + fContent.writeToAndReset(&buffer); + if (fNeedsExtraSave) { + buffer.writeText("Q\n"); } + fNeedsExtraSave = false; + return std::unique_ptr(buffer.detachAsStream()); } /* Draws an inverse filled path by using Path Ops to compute the positive @@ -1480,7 +1476,7 @@ void SkPDFDevice::drawFormXObjectWithMask(sk_sp xObject, SkPaint paint; paint.setBlendMode(mode); ScopedContentEntry content(this, nullptr, SkMatrix::I(), paint); - if (!content.entry()) { + if (!content) { return; } this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState( @@ -1526,15 +1522,17 @@ SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipSt if (treat_as_regular_pdf_blend_mode(blendMode)) { if (!fActiveStackState.fContentStream) { - fActiveStackState = GraphicStackState(fContentEntries.emplace_back()); + if (fContent.bytesWritten() != 0) { + fContent.writeText("Q\nq\n"); + fNeedsExtraSave = true; + } + fActiveStackState = GraphicStackState(&fContent); + } else { + SkASSERT(fActiveStackState.fContentStream = &fContent); } } else { fActiveStackState.drainStack(); - if (blendMode != SkBlendMode::kDstOver) { - fActiveStackState = GraphicStackState(fContentEntries.emplace_back()); - } else { - fActiveStackState = GraphicStackState(fContentEntries.emplace_front()); - } + fActiveStackState = GraphicStackState(&fContentBuffer); } SkASSERT(fActiveStackState.fContentStream); GraphicStateEntry entry; @@ -1563,14 +1561,25 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack, if (blendMode == SkBlendMode::kDstOver) { SkASSERT(!dst); - if (fContentEntries.front()->bytesWritten() == 0) { - // For DstOver, an empty content entry was inserted before the rest - // of the content entries. If nothing was drawn, it needs to be - // removed. - fContentEntries.pop_front(); + if (fContentBuffer.bytesWritten() != 0) { + if (fContent.bytesWritten() != 0) { + fContentBuffer.writeText("Q\nq\n"); + fNeedsExtraSave = true; + } + fContentBuffer.prependToAndReset(&fContent); + SkASSERT(fContentBuffer.bytesWritten() == 0); } return; } + if (fContentBuffer.bytesWritten() != 0) { + if (fContent.bytesWritten() != 0) { + fContent.writeText("Q\nq\n"); + fNeedsExtraSave = true; + } + fContentBuffer.writeToAndReset(&fContent); + SkASSERT(fContentBuffer.bytesWritten() == 0); + } + if (!dst) { SkASSERT(blendMode == SkBlendMode::kSrc || blendMode == SkBlendMode::kSrcOut); @@ -1578,7 +1587,6 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack, } SkASSERT(dst); - SkASSERT(fContentEntries.count() == 1); // Changing the current content into a form-xobject will destroy the clip // objects which is fine since the xobject will already be clipped. However // if source has shape, we need to clip it too, so a copy of the clip is @@ -1602,7 +1610,6 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack, blendMode = SkBlendMode::kClear; } } else { - SkASSERT(fContentEntries.count() == 1); srcFormXObject = this->makeFormXObjectFromDevice(); } @@ -1636,7 +1643,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack, } else if (blendMode == SkBlendMode::kSrc || blendMode == SkBlendMode::kDstATop) { ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint); - if (content.entry()) { + if (content) { this->drawFormXObject(srcFormXObject, content.stream()); } if (blendMode == SkBlendMode::kSrc) { @@ -1644,7 +1651,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack, } } else if (blendMode == SkBlendMode::kSrcATop) { ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint); - if (content.entry()) { + if (content) { this->drawFormXObject(dst, content.stream()); } } @@ -1676,11 +1683,7 @@ void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack, } bool SkPDFDevice::isContentEmpty() { - if (!fContentEntries.front() || fContentEntries.front()->bytesWritten() == 0) { - SkASSERT(fContentEntries.count() <= 1); - return true; - } - return false; + return fContent.bytesWritten() == 0 && fContentBuffer.bytesWritten() == 0; } void SkPDFDevice::populateGraphicStateEntryFromPaint( @@ -1879,7 +1882,7 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, transform_shader(&paint, ctm); // Since we are using identity matrix. } ScopedContentEntry content(this, &this->cs(), SkMatrix::I(), paint); - if (!content.entry()) { + if (!content) { return; } this->addSMaskGraphicState(std::move(maskDevice), content.stream()); @@ -1986,7 +1989,7 @@ void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset, SkIntToScalar(subset.height())); scaled.postConcat(matrix); ScopedContentEntry content(this, &this->cs(), scaled, paint); - if (!content.entry()) { + if (!content) { return; } if (content.needShape()) { diff --git a/src/pdf/SkPDFDevice.h b/src/pdf/SkPDFDevice.h index 953764e552..be50760fa2 100644 --- a/src/pdf/SkPDFDevice.h +++ b/src/pdf/SkPDFDevice.h @@ -16,7 +16,6 @@ #include "SkPaint.h" #include "SkRect.h" #include "SkRefCnt.h" -#include "SkSinglyLinkedList.h" #include "SkStream.h" #include "SkTextBlobPriv.h" #include "SkKeyedImage.h" @@ -172,9 +171,11 @@ private: std::vector> fFontResources; int fNodeId; - SkSinglyLinkedList fContentEntries; + SkDynamicMemoryWStream fContent; + SkDynamicMemoryWStream fContentBuffer; + bool fNeedsExtraSave = false; struct GraphicStackState { - GraphicStackState(SkDynamicMemoryWStream* s = nullptr) : fContentStream(s) {} + GraphicStackState(SkDynamicMemoryWStream* s = nullptr); void updateClip(const SkClipStack* clipStack, const SkIRect& bounds); void updateMatrix(const SkMatrix& matrix); void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state);