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() || |