Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1373)

Unified Diff: src/pdf/SkPDFDevice.cpp

Issue 21161003: Use Path Ops to generate PDF clips (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Fix review comments Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « gyp/gmslides.gypi ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/pdf/SkPDFDevice.cpp
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index eda3616a1f7681a1fcd7c2c9895a1c6a8ee74781..73144e3493889b844ca421c8d1f29aef5b0e7680 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -273,6 +273,7 @@ void GraphicStackState::pop() {
fStackDepth--;
}
+#ifndef SK_PDF_USE_PATHOPS
// This function initializes iter to be an iterator on the "stack" argument
// and then skips over the leading entries as specified in prefix. It requires
// and asserts that "prefix" will be a prefix to "stack."
@@ -304,6 +305,7 @@ static void skip_clip_stack_prefix(const SkClipStack& prefix,
SkASSERT(prefixEntry == NULL);
}
+#endif
static void emit_clip(SkPath* clipPath, SkRect* clipRect,
SkWStream* contentStream) {
@@ -327,6 +329,87 @@ static void emit_clip(SkPath* clipPath, SkRect* clipRect,
}
}
+#ifdef SK_PDF_USE_PATHOPS
+// Sanity check the numerical values of the SkRegion ops and PathOps ops
+// enums so region_op_to_pathops_op can do a straight passthrough cast.
+// If these are failing, it may be necessary to make region_op_to_pathops_op
+// do more.
+SK_COMPILE_ASSERT(SkRegion::kDifference_Op == (int)kDifference_PathOp,
+ region_pathop_mismatch);
+SK_COMPILE_ASSERT(SkRegion::kIntersect_Op == (int)kIntersect_PathOp,
+ region_pathop_mismatch);
+SK_COMPILE_ASSERT(SkRegion::kUnion_Op == (int)kUnion_PathOp,
+ region_pathop_mismatch);
+SK_COMPILE_ASSERT(SkRegion::kXOR_Op == (int)kXOR_PathOp,
+ region_pathop_mismatch);
+SK_COMPILE_ASSERT(SkRegion::kReverseDifference_Op ==
+ (int)kReverseDifference_PathOp,
+ region_pathop_mismatch);
+
+static SkPathOp region_op_to_pathops_op(SkRegion::Op op) {
+ SkASSERT(op >= 0);
+ SkASSERT(op <= SkRegion::kReverseDifference_Op);
+ return (SkPathOp)op;
+}
+
+static SkPath get_clip_stack_path(const SkMatrix& transform,
+ const SkClipStack& clipStack,
+ const SkRegion& clipRegion) {
+ SkPath clipPath;
+
+ // Note that this starts with a filled clip only on the final bounds,
+ // instead of the whole page. This can cause incorrectness in intermediate
+ // operations, which may work with shapes that are outside the final
+ // bounds (for example, consider XORing a square outside the final bounds -
+ // the actual result would differ from the simulated result).
+ // Therefore, to guarantee accuracy of the final clip, this starts with a
+ // empty clip 2 units out from the final bounds. After all stack operations
+ // are applied, the final clip is cropped to 1 unit out from the final
+ // bounds. The buffer space ensures there is no ambiguity on edge cases.
+ SkRect clipBounds = SkRect::Make(clipRegion.getBounds());
+ clipBounds.outset(SK_Scalar1, SK_Scalar1);
+ SkRect workingBounds = clipBounds;
+ workingBounds.outset(SK_Scalar1, SK_Scalar1);
vandebo (ex-Chrome) 2013/08/01 14:56:14 Do you need to constrain the path bounds at the st
ducky 2013/08/01 21:22:46 Good point, I didn't realize I could just set the
+ clipPath.addRect(workingBounds);
+
+ const SkClipStack::Element* clipEntry;
+ SkClipStack::Iter iter;
+ iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
+ for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
+ SkPath entryPath;
+ if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) {
+ clipPath.reset();
vandebo (ex-Chrome) 2013/08/01 14:56:14 Should this also add workingBounds to clipPath ?
ducky 2013/08/01 21:22:46 Yep. D'oh. Fixed, with new empty clip style.
+ continue;
+ } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) {
+ entryPath.addRect(clipEntry->getRect());
+ } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) {
+ entryPath = clipEntry->getPath();
+ }
+ entryPath.transform(transform);
+
+ if (SkRegion::kReplace_Op == clipEntry->getOp()) {
+ clipPath = entryPath;
vandebo (ex-Chrome) 2013/08/01 14:56:14 Should you intersect with workingBounds here?
ducky 2013/08/01 21:22:46 No longer necessary with new style. But the idea b
+ } else {
+ SkPathOp op = region_op_to_pathops_op(clipEntry->getOp());
+ if (!Op(clipPath, entryPath, op, &clipPath)) {
+ SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
+ return clipPath;
+ }
+ }
+ }
+
+ SkPath boundsPath;
+ boundsPath.addRect(clipBounds);
+
+ if (!Op(clipPath, boundsPath, kIntersect_PathOp, &clipPath)) {
+ SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
+ return clipPath;
+ }
+
+ return clipPath;
+}
+#endif
+
// TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
// graphic state stack, and the fact that we can know all the clips used
// on the page to optimize this.
@@ -345,6 +428,13 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
}
push();
+ SkMatrix transform;
+ transform.setTranslate(translation.fX, translation.fY);
+
+#ifdef SK_PDF_USE_PATHOPS
+ SkPath clipPath = get_clip_stack_path(transform, clipStack, clipRegion);
+ emit_clip(&clipPath, NULL, fContentStream);
+#else
// gsState->initialEntry()->fClipStack/Region specifies the clip that has
// already been applied. (If this is a top level device, then it specifies
// a clip to the content area. If this is a layer, then it specifies
@@ -373,8 +463,6 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
emit_clip(&clipPath, NULL, fContentStream);
} else {
skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
- SkMatrix transform;
- transform.setTranslate(translation.fX, translation.fY);
const SkClipStack::Element* clipEntry;
for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
@@ -396,6 +484,7 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
}
}
}
+#endif
currentEntry()->fClipStack = clipStack;
currentEntry()->fClipRegion = clipRegion;
}
@@ -1241,6 +1330,7 @@ SkData* SkPDFDevice::copyContentToData() const {
/* Calculate an inverted path's equivalent non-inverted path, given the
* canvas bounds.
+ * outPath may alias with invPath (since this is supported by PathOps).
*/
static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
SkPath* outPath) {
« no previous file with comments | « gyp/gmslides.gypi ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698