Use Path Ops to generate PDF clips
R=vandebo@chromium.org, edisonn@google.com, caryclark@google.com Author: richardlin@chromium.org Review URL: https://chromiumcodereview.appspot.com/21161003 git-svn-id: http://skia.googlecode.com/svn/trunk@10633 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
b265741cc1
commit
d2623a1a0b
78
gm/circularclips.cpp
Normal file
78
gm/circularclips.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* 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; )
|
||||||
|
}
|
@ -20,6 +20,7 @@
|
|||||||
'../gm/blurquickreject.cpp',
|
'../gm/blurquickreject.cpp',
|
||||||
'../gm/blurrect.cpp',
|
'../gm/blurrect.cpp',
|
||||||
'../gm/circles.cpp',
|
'../gm/circles.cpp',
|
||||||
|
'../gm/circularclips.cpp',
|
||||||
'../gm/colorfilterimagefilter.cpp',
|
'../gm/colorfilterimagefilter.cpp',
|
||||||
'../gm/colormatrix.cpp',
|
'../gm/colormatrix.cpp',
|
||||||
'../gm/colortype.cpp',
|
'../gm/colortype.cpp',
|
||||||
|
@ -293,8 +293,10 @@ private:
|
|||||||
*/
|
*/
|
||||||
void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
|
void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
|
||||||
|
|
||||||
|
#ifdef SK_PDF_USE_PATHOPS
|
||||||
bool handleInversePath(const SkDraw& d, const SkPath& origPath,
|
bool handleInversePath(const SkDraw& d, const SkPath& origPath,
|
||||||
const SkPaint& paint, bool pathIsMutable);
|
const SkPaint& paint, bool pathIsMutable);
|
||||||
|
#endif
|
||||||
bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
|
bool handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
|
||||||
const SkPaint& paint);
|
const SkPaint& paint);
|
||||||
bool handlePointAnnotation(const SkPoint* points, size_t count,
|
bool handlePointAnnotation(const SkPoint* points, size_t count,
|
||||||
|
@ -327,6 +327,96 @@ static void emit_clip(SkPath* clipPath, SkRect* clipRect,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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
|
// TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
|
||||||
// graphic state stack, and the fact that we can know all the clips used
|
// graphic state stack, and the fact that we can know all the clips used
|
||||||
// on the page to optimize this.
|
// on the page to optimize this.
|
||||||
@ -345,6 +435,19 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
|
|||||||
}
|
}
|
||||||
push();
|
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
|
// gsState->initialEntry()->fClipStack/Region specifies the clip that has
|
||||||
// already been applied. (If this is a top level device, then it specifies
|
// 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
|
// a clip to the content area. If this is a layer, then it specifies
|
||||||
@ -373,8 +476,6 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
|
|||||||
emit_clip(&clipPath, NULL, fContentStream);
|
emit_clip(&clipPath, NULL, fContentStream);
|
||||||
} else {
|
} else {
|
||||||
skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
|
skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
|
||||||
SkMatrix transform;
|
|
||||||
transform.setTranslate(translation.fX, translation.fY);
|
|
||||||
const SkClipStack::Element* clipEntry;
|
const SkClipStack::Element* clipEntry;
|
||||||
for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
|
for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
|
||||||
SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
|
SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
|
||||||
@ -396,8 +497,6 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentEntry()->fClipStack = clipStack;
|
|
||||||
currentEntry()->fClipRegion = clipRegion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
|
void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
|
||||||
@ -1239,19 +1338,7 @@ SkData* SkPDFDevice::copyContentToData() const {
|
|||||||
return data.copyToData();
|
return data.copyToData();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate an inverted path's equivalent non-inverted path, given the
|
#ifdef SK_PDF_USE_PATHOPS
|
||||||
* 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
|
/* Draws an inverse filled path by using Path Ops to compute the positive
|
||||||
* inverse using the current clip as the inverse bounds.
|
* inverse using the current clip as the inverse bounds.
|
||||||
* Return true if this was an inverse path and was properly handled,
|
* Return true if this was an inverse path and was properly handled,
|
||||||
@ -1312,6 +1399,7 @@ bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath,
|
|||||||
drawPath(d, modifiedPath, noInversePaint, NULL, true);
|
drawPath(d, modifiedPath, noInversePaint, NULL, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
|
bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
|
||||||
const SkPaint& p) {
|
const SkPaint& p) {
|
||||||
|
Loading…
Reference in New Issue
Block a user