| Index: src/pdf/SkPDFDevice.cpp
|
| diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
|
| index 84959b6c1bee9fcc648850df21074ae0d2218f5e..4adc4dae4ab5aca50edb08daa6c7e6a3112c1a74 100644
|
| --- a/src/pdf/SkPDFDevice.cpp
|
| +++ b/src/pdf/SkPDFDevice.cpp
|
| @@ -181,49 +181,19 @@ static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
|
| content->writeText(" Tm\n");
|
| }
|
|
|
| -// It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the
|
| -// later being our representation of an object in the PDF file.
|
| -struct GraphicStateEntry {
|
| - GraphicStateEntry();
|
| -
|
| - // Compare the fields we care about when setting up a new content entry.
|
| - bool compareInitialState(const GraphicStateEntry& b);
|
| -
|
| - SkMatrix fMatrix;
|
| - // We can't do set operations on Paths, though PDF natively supports
|
| - // intersect. If the clip stack does anything other than intersect,
|
| - // we have to fall back to the region. Treat fClipStack as authoritative.
|
| - // See http://code.google.com/p/skia/issues/detail?id=221
|
| - SkClipStack fClipStack;
|
| - SkRegion fClipRegion;
|
| -
|
| - // When emitting the content entry, we will ensure the graphic state
|
| - // is set to these values first.
|
| - SkColor fColor;
|
| - SkScalar fTextScaleX; // Zero means we don't care what the value is.
|
| - SkPaint::Style fTextFill; // Only if TextScaleX is non-zero.
|
| - int fShaderIndex;
|
| - int fGraphicStateIndex;
|
| -
|
| - // We may change the font (i.e. for Type1 support) within a
|
| - // ContentEntry. This is the one currently in effect, or nullptr if none.
|
| - SkPDFFont* fFont;
|
| - // In PDF, text size has no default value. It is only valid if fFont is
|
| - // not nullptr.
|
| - SkScalar fTextSize;
|
| -};
|
| -
|
| -GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK),
|
| - fTextScaleX(SK_Scalar1),
|
| - fTextFill(SkPaint::kFill_Style),
|
| - fShaderIndex(-1),
|
| - fGraphicStateIndex(-1),
|
| - fFont(nullptr),
|
| - fTextSize(SK_ScalarNaN) {
|
| +SkPDFDevice::GraphicStateEntry::GraphicStateEntry()
|
| + : fColor(SK_ColorBLACK)
|
| + , fTextScaleX(SK_Scalar1)
|
| + , fTextFill(SkPaint::kFill_Style)
|
| + , fShaderIndex(-1)
|
| + , fGraphicStateIndex(-1)
|
| + , fFont(nullptr)
|
| + , fTextSize(SK_ScalarNaN) {
|
| fMatrix.reset();
|
| }
|
|
|
| -bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& cur) {
|
| +bool SkPDFDevice::GraphicStateEntry::compareInitialState(
|
| + const GraphicStateEntry& cur) {
|
| return fColor == cur.fColor &&
|
| fShaderIndex == cur.fShaderIndex &&
|
| fGraphicStateIndex == cur.fGraphicStateIndex &&
|
| @@ -247,18 +217,18 @@ public:
|
| void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion,
|
| const SkPoint& translation);
|
| void updateMatrix(const SkMatrix& matrix);
|
| - void updateDrawingState(const GraphicStateEntry& state);
|
| + void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state);
|
|
|
| void drainStack();
|
|
|
| private:
|
| void push();
|
| void pop();
|
| - GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
|
| + SkPDFDevice::GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
|
|
|
| // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
|
| static const int kMaxStackDepth = 12;
|
| - GraphicStateEntry fEntries[kMaxStackDepth + 1];
|
| + SkPDFDevice::GraphicStateEntry fEntries[kMaxStackDepth + 1];
|
| int fStackDepth;
|
| SkWStream* fContentStream;
|
| };
|
| @@ -523,7 +493,7 @@ void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
|
| currentEntry()->fMatrix = matrix;
|
| }
|
|
|
| -void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) {
|
| +void GraphicStackState::updateDrawingState(const SkPDFDevice::GraphicStateEntry& state) {
|
| // PDF treats a shader as a color, so we only set one or the other.
|
| if (state.fShaderIndex >= 0) {
|
| if (state.fShaderIndex != currentEntry()->fShaderIndex) {
|
| @@ -589,23 +559,6 @@ SkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint
|
| SkPDFCanon* SkPDFDevice::getCanon() const { return fDocument->canon(); }
|
|
|
|
|
| -struct ContentEntry {
|
| - GraphicStateEntry fState;
|
| - SkDynamicMemoryWStream fContent;
|
| - SkAutoTDelete<ContentEntry> fNext;
|
| -
|
| - // If the stack is too deep we could get Stack Overflow.
|
| - // So we manually destruct the object.
|
| - ~ContentEntry() {
|
| - ContentEntry* val = fNext.release();
|
| - while (val != nullptr) {
|
| - ContentEntry* valNext = val->fNext.release();
|
| - // When the destructor is called, fNext is nullptr and exits.
|
| - delete val;
|
| - val = valNext;
|
| - }
|
| - }
|
| -};
|
|
|
| // A helper class to automatically finish a ContentEntry at the end of a
|
| // drawing method and maintain the state needed between set up and finish.
|
| @@ -640,7 +593,7 @@ public:
|
| SkSafeUnref(fDstFormXObject);
|
| }
|
|
|
| - ContentEntry* entry() { return fContentEntry; }
|
| + SkPDFDevice::ContentEntry* entry() { return fContentEntry; }
|
|
|
| /* Returns true when we explicitly need the shape of the drawing. */
|
| bool needShape() {
|
| @@ -678,7 +631,7 @@ public:
|
|
|
| private:
|
| SkPDFDevice* fDevice;
|
| - ContentEntry* fContentEntry;
|
| + SkPDFDevice::ContentEntry* fContentEntry;
|
| SkXfermode::Mode fXfermode;
|
| SkPDFFormXObject* fDstFormXObject;
|
| SkPath fShape;
|
| @@ -706,7 +659,6 @@ SkPDFDevice::SkPDFDevice(SkISize pageSize, SkScalar rasterDpi, SkPDFDocument* do
|
| , fPageSize(pageSize)
|
| , fContentSize(pageSize)
|
| , fExistingClipRegion(SkIRect::MakeSize(pageSize))
|
| - , fLastContentEntry(nullptr)
|
| , fClipStack(nullptr)
|
| , fFontGlyphUsage(new SkPDFGlyphSetMap)
|
| , fRasterDpi(rasterDpi)
|
| @@ -733,7 +685,6 @@ SkPDFDevice::~SkPDFDevice() {
|
|
|
| void SkPDFDevice::init() {
|
| fContentEntries.reset();
|
| - fLastContentEntry = nullptr;
|
| if (fFontGlyphUsage.get() == nullptr) {
|
| fFontGlyphUsage.reset(new SkPDFGlyphSetMap);
|
| }
|
| @@ -771,7 +722,7 @@ void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
|
| }
|
|
|
| void SkPDFDevice::internalDrawPaint(const SkPaint& paint,
|
| - ContentEntry* contentEntry) {
|
| + SkPDFDevice::ContentEntry* contentEntry) {
|
| if (!contentEntry) {
|
| return;
|
| }
|
| @@ -1441,26 +1392,6 @@ std::unique_ptr<SkStreamAsset> SkPDFDevice::content() const {
|
| : new SkMemoryStream);
|
| }
|
|
|
| -void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry,
|
| - SkWStream* data) const {
|
| - // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the
|
| - // right thing to pass here.
|
| - GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data);
|
| - while (entry != nullptr) {
|
| - SkPoint translation;
|
| - translation.iset(this->getOrigin());
|
| - translation.negate();
|
| - gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion,
|
| - translation);
|
| - gsState.updateMatrix(entry->fState.fMatrix);
|
| - gsState.updateDrawingState(entry->fState);
|
| -
|
| - entry->fContent.writeToStream(data);
|
| - entry = entry->fNext.get();
|
| - }
|
| - gsState.drainStack();
|
| -}
|
| -
|
| void SkPDFDevice::writeContent(SkWStream* out) const {
|
| if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
|
| SkPDFUtils::AppendTransform(fInitialTransform, out);
|
| @@ -1476,7 +1407,19 @@ void SkPDFDevice::writeContent(SkWStream* out) const {
|
| emit_clip(nullptr, &r, out);
|
| }
|
|
|
| - SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), out);
|
| + GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, out);
|
| + for (const auto& entry : fContentEntries) {
|
| + SkPoint translation;
|
| + translation.iset(this->getOrigin());
|
| + translation.negate();
|
| + gsState.updateClip(entry.fState.fClipStack, entry.fState.fClipRegion,
|
| + translation);
|
| + gsState.updateMatrix(entry.fState.fMatrix);
|
| + gsState.updateDrawingState(entry.fState);
|
| +
|
| + entry.fContent.writeToStream(out);
|
| + }
|
| + gsState.drainStack();
|
| }
|
|
|
| /* Draws an inverse filled path by using Path Ops to compute the positive
|
| @@ -1657,7 +1600,7 @@ void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
|
| &content.entry()->fContent);
|
| }
|
|
|
| -ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
|
| +SkPDFDevice::ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
|
| const SkRegion& clipRegion,
|
| const SkMatrix& matrix,
|
| const SkPaint& paint,
|
| @@ -1720,34 +1663,16 @@ ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
|
| return nullptr;
|
| }
|
|
|
| - ContentEntry* entry;
|
| - SkAutoTDelete<ContentEntry> newEntry;
|
| -
|
| - if (fLastContentEntry && fLastContentEntry->fContent.getOffset() == 0) {
|
| - entry = fLastContentEntry;
|
| + SkPDFDevice::ContentEntry* entry;
|
| + if (fContentEntries.back() && fContentEntries.back()->fContent.getOffset() == 0) {
|
| + entry = fContentEntries.back();
|
| + } else if (xfermode != SkXfermode::kDstOver_Mode) {
|
| + entry = fContentEntries.emplace_back();
|
| } else {
|
| - newEntry.reset(new ContentEntry);
|
| - entry = newEntry.get();
|
| + entry = fContentEntries.emplace_front();
|
| }
|
| -
|
| populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint,
|
| hasText, &entry->fState);
|
| - if (fLastContentEntry && xfermode != SkXfermode::kDstOver_Mode &&
|
| - entry->fState.compareInitialState(fLastContentEntry->fState)) {
|
| - return fLastContentEntry;
|
| - }
|
| -
|
| - if (!fLastContentEntry) {
|
| - fContentEntries.reset(entry);
|
| - fLastContentEntry = entry;
|
| - } else if (xfermode == SkXfermode::kDstOver_Mode) {
|
| - entry->fNext.reset(fContentEntries.release());
|
| - fContentEntries.reset(entry);
|
| - } else {
|
| - fLastContentEntry->fNext.reset(entry);
|
| - fLastContentEntry = entry;
|
| - }
|
| - newEntry.release();
|
| return entry;
|
| }
|
|
|
| @@ -1769,11 +1694,11 @@ void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
|
| }
|
| if (xfermode == SkXfermode::kDstOver_Mode) {
|
| SkASSERT(!dst);
|
| - if (fContentEntries->fContent.getOffset() == 0) {
|
| + if (fContentEntries.front()->fContent.getOffset() == 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.reset(fContentEntries->fNext.release());
|
| + fContentEntries.pop_front();
|
| }
|
| return;
|
| }
|
| @@ -1784,13 +1709,14 @@ void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
|
| }
|
|
|
| SkASSERT(dst);
|
| - SkASSERT(!fContentEntries->fNext.get());
|
| + 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
|
| // saved.
|
| - SkClipStack clipStack = fContentEntries->fState.fClipStack;
|
| - SkRegion clipRegion = fContentEntries->fState.fClipRegion;
|
| +
|
| + SkClipStack clipStack = fContentEntries.front()->fState.fClipStack;
|
| + SkRegion clipRegion = fContentEntries.front()->fState.fClipRegion;
|
|
|
| SkMatrix identity;
|
| identity.reset();
|
| @@ -1815,7 +1741,7 @@ void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
|
| xfermode = SkXfermode::kClear_Mode;
|
| }
|
| } else {
|
| - SkASSERT(!fContentEntries->fNext.get());
|
| + SkASSERT(fContentEntries.count() == 1);
|
| srcFormXObject.reset(createFormXObjectFromDevice());
|
| }
|
|
|
| @@ -1905,8 +1831,8 @@ void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
|
| }
|
|
|
| bool SkPDFDevice::isContentEmpty() {
|
| - if (!fContentEntries || fContentEntries->fContent.getOffset() == 0) {
|
| - SkASSERT(!fContentEntries || !fContentEntries->fNext.get());
|
| + if (!fContentEntries.front() || fContentEntries.front()->fContent.getOffset() == 0) {
|
| + SkASSERT(fContentEntries.count() <= 1);
|
| return true;
|
| }
|
| return false;
|
| @@ -1918,7 +1844,7 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
|
| const SkRegion& clipRegion,
|
| const SkPaint& paint,
|
| bool hasText,
|
| - GraphicStateEntry* entry) {
|
| + SkPDFDevice::GraphicStateEntry* entry) {
|
| NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
|
| NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
|
| NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
|
| @@ -2027,7 +1953,7 @@ int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
|
| }
|
|
|
| void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
|
| - ContentEntry* contentEntry) {
|
| + SkPDFDevice::ContentEntry* contentEntry) {
|
| SkTypeface* typeface = paint.getTypeface();
|
| if (contentEntry->fState.fFont == nullptr ||
|
| contentEntry->fState.fTextSize != paint.getTextSize() ||
|
|
|