--- /dev/null
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+
+namespace skiagm {
+
+class CircularClipsGM : public GM {
+public:
+ CircularClipsGM() {}
+
+protected:
+ virtual SkString onShortName() {
+ return SkString("circular-clips");
+ }
+
+ virtual SkISize onISize() {
+ return SkISize::Make(800, 600);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ SkRegion::Op ops[] = {
+ SkRegion::kDifference_Op,
+ SkRegion::kIntersect_Op,
+ SkRegion::kUnion_Op,
+ SkRegion::kXOR_Op,
+ SkRegion::kReverseDifference_Op,
+ SkRegion::kReplace_Op,
+ };
+
+ SkScalar x1 = 80, x2 = 120;
+ SkScalar y = 50;
+ SkScalar r = 40;
+
+ SkPath circle1, circle2;
+ circle1.addCircle(x1, y, r, SkPath::kCW_Direction);
+ circle2.addCircle(x2, y, r, SkPath::kCW_Direction);
+ SkRect rect = SkRect::MakeLTRB(x1 - r, y - r, x2 + r, y + r);
+
+ SkPaint fillPaint;
+
+ for (size_t i = 0; i < 4; i++) {
+ circle1.toggleInverseFillType();
+ if (i % 2 == 0) {
+ circle2.toggleInverseFillType();
+ }
+
+ canvas->save();
+ for (size_t op = 0; op < SK_ARRAY_COUNT(ops); op++) {
+ canvas->save();
+
+ canvas->clipPath(circle1, SkRegion::kReplace_Op);
+ canvas->clipPath(circle2, ops[op]);
+
+ canvas->drawRect(rect, fillPaint);
+
+ canvas->restore();
+ canvas->translate(0, 2 * y);
+ }
+ canvas->restore();
+ canvas->translate(x1 + x2, 0);
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new CircularClipsGM; )
+}
}
}
+#ifdef SK_PDF_USE_PATHOPS
+/* 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) {
+ SkASSERT(invPath.isInverseFillType());
+
+ SkPath clipPath;
+ clipPath.addRect(bounds);
+
+ return Op(clipPath, invPath, kIntersect_PathOp, outPath);
+}
+
+// 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;
+}
+
+/* Uses Path Ops to calculate a vector SkPath clip from a clip stack.
+ * Returns true if successful, or false if not successful.
+ * If successful, the resulting clip is stored in outClipPath.
+ * If not successful, outClipPath is undefined, and a fallback method
+ * should be used.
+ */
+static bool get_clip_stack_path(const SkMatrix& transform,
+ const SkClipStack& clipStack,
+ const SkRegion& clipRegion,
+ SkPath* outClipPath) {
+ outClipPath->reset();
+ outClipPath->setFillType(SkPath::kInverseWinding_FillType);
+
+ 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()) {
+ outClipPath->reset();
+ outClipPath->setFillType(SkPath::kInverseWinding_FillType);
+ 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()) {
+ *outClipPath = entryPath;
+ } else {
+ SkPathOp op = region_op_to_pathops_op(clipEntry->getOp());
+ if (!Op(*outClipPath, entryPath, op, outClipPath)) {
+ return false;
+ }
+ }
+ }
+
+ if (outClipPath->isInverseFillType()) {
+ // The bounds are slightly outset to ensure this is correct in the
+ // face of floating-point accuracy and possible SkRegion bitmap
+ // approximations.
+ SkRect clipBounds = SkRect::Make(clipRegion.getBounds());
+ clipBounds.outset(SK_Scalar1, SK_Scalar1);
+ if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) {
+ return false;
+ }
+ }
+ return true;
+}
+#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.
}
push();
+ currentEntry()->fClipStack = clipStack;
+ currentEntry()->fClipRegion = clipRegion;
+
+ SkMatrix transform;
+ transform.setTranslate(translation.fX, translation.fY);
+
+#ifdef SK_PDF_USE_PATHOPS
+ SkPath clipPath;
+ if (get_clip_stack_path(transform, clipStack, clipRegion, &clipPath)) {
+ emit_clip(&clipPath, NULL, fContentStream);
+ return;
+ }
+#endif
// 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
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);
}
}
}
- currentEntry()->fClipStack = clipStack;
- currentEntry()->fClipRegion = clipRegion;
}
void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
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);
-}
-
+#ifdef SK_PDF_USE_PATHOPS
/* 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,
drawPath(d, modifiedPath, noInversePaint, NULL, true);
return true;
}
+#endif
bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
const SkPaint& p) {