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

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: Make more things static, fix warnings when SK_PDF_USE_PATHOPS not enabled 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
« gm/circularclips.cpp ('K') | « 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 88b858f27f006a41adaf6467c36b140bd5e053ba..24bc7ac539def7bda2242041f6748ceafef4ec95 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -327,6 +327,101 @@ static void emit_clip(SkPath* clipPath, SkRect* clipRect,
}
}
+/* 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) {
edisonn 2013/07/31 18:27:37 nit: align
vandebo (ex-Chrome) 2013/07/31 18:35:17 indent (in original too).
ducky 2013/08/01 00:45:08 Done.
+ SkASSERT(invPath.isInverseFillType());
+
+ SkPath clipPath;
+ clipPath.addRect(bounds);
+
+ return Op(clipPath, invPath, kIntersect_PathOp, outPath);
+}
+
+#ifdef SK_PDF_USE_PATHOPS
+static SkPathOp region_op_to_pathops_op(SkRegion::Op op) {
vandebo (ex-Chrome) 2013/07/31 18:35:17 SkRegion::Op and SkPathOp have the same value for
ducky 2013/08/01 00:45:08 Done.
+ switch (op) {
+ case SkRegion::kDifference_Op:
+ return kDifference_PathOp;
+ case SkRegion::kIntersect_Op:
+ return kIntersect_PathOp;
+ case SkRegion::kUnion_Op:
+ return kUnion_PathOp;
+ case SkRegion::kXOR_Op:
+ return kXOR_PathOp;
+ case SkRegion::kReverseDifference_Op:
+ return kReverseDifference_PathOp;
+ default:
+ SkASSERT(false); // unrecognized op
+ }
+ return kIntersect_PathOp;
+}
+
+static SkPath get_clip_stack_path(const SkMatrix& transform,
+ const SkClipStack& clipStack,
+ const SkRegion& clipRegion) {
+ SkPath clipPath;
+
+ // Because of additive operations (especially XOR), starting an empty
vandebo (ex-Chrome) 2013/07/31 18:35:17 I don't understand the first part.
ducky 2013/08/01 00:45:08 Ok, I tried to clarify.
+ // clip with the bounds of the final clip can cause inaccuracies, since
+ // the intermediate clips may be larger than the final bounds.
+ // 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.
vandebo (ex-Chrome) 2013/07/31 18:35:17 I don't think we should use this buffer space. If
ducky 2013/08/01 00:45:08 I'm concerned about the numerical issues that were
+ SkRect clipBounds = SkRect::Make(clipRegion.getBounds());
+ clipBounds.outset(SK_Scalar1, SK_Scalar1);
+ SkRect workingBounds = clipBounds;
+ workingBounds.outset(SK_Scalar1, SK_Scalar1);
+ 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();
+ continue;
+ } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) {
+ SkRect translatedRect;
+ transform.mapRect(&translatedRect, clipEntry->getRect());
edisonn 2013/07/31 18:27:37 this can fail!
ducky 2013/08/01 00:45:08 Fixed, now the transform happens as a path, which
+ entryPath.addRect(translatedRect);
+ } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) {
+ clipEntry->getPath().transform(transform, &entryPath);
edisonn 2013/07/31 18:27:37 this can fail?
ducky 2013/08/01 00:45:08 Doesn't look like it. SkPath::transform returns vo
+ }
+
+ if (SkRegion::kReplace_Op == clipEntry->getOp()) {
+ clipPath = entryPath;
+ } else {
+ SkPathOp op = region_op_to_pathops_op(clipEntry->getOp());
vandebo (ex-Chrome) 2013/07/31 18:35:17 Do you have to check if entryPath has an inverse f
ducky 2013/08/01 00:45:08 I think PathOps handles inverses. All the GMs (som
+ if (!Op(clipPath, entryPath, op, &clipPath)) {
+ SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
+ return clipPath;
+ }
+ }
+ }
+
+ if (clipPath.isInverseFillType()) {
+ calculate_inverse_path(SkRect::Make(clipRegion.getBounds()),
+ clipPath, &clipPath);
+ }
+
+ SkPath boundsPath;
+ boundsPath.addRect(clipBounds);
+
+ if (!Op(clipPath, boundsPath, kIntersect_PathOp, &clipPath)) {
vandebo (ex-Chrome) 2013/07/31 18:35:17 Won't this last step take care of any inverse path
ducky 2013/08/01 00:45:08 Good point.
+ 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.
@@ -367,14 +462,20 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
}
}
+ SkMatrix transform;
+ transform.setTranslate(translation.fX, translation.fY);
+
if (needRegion) {
+#ifdef SK_PDF_USE_PATHOPS
+ SkPath clipPath = get_clip_stack_path(transform, clipStack, clipRegion);
+ emit_clip(&clipPath, NULL, fContentStream);
+#else
SkPath clipPath;
SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
emit_clip(&clipPath, NULL, fContentStream);
+#endif
} 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);
@@ -1239,19 +1340,6 @@ SkData* SkPDFDevice::copyContentToData() const {
return data.copyToData();
}
-/* Calculate an inverted path's equivalent non-inverted path, given the
- * canvas bounds.
- */
-static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
- SkPath* outPath) {
- SkASSERT(invPath.isInverseFillType());
-
- SkPath clipPath;
- clipPath.addRect(bounds);
-
- return Op(clipPath, invPath, kIntersect_PathOp, outPath);
-}
-
/* Draws an inverse filled path by using Path Ops to compute the positive
* inverse using the current clip as the inverse bounds.
* Return true if this was an inverse path and was properly handled,
« gm/circularclips.cpp ('K') | « gyp/gmslides.gypi ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698