SkPDF: Fastpath for clipstack flattening

BUG: chromium:837279

Change-Id: I79a675e7d9e713617711948e491e28babe06b1a2
Reviewed-on: https://skia-review.googlesource.com/132092
Auto-Submit: Hal Canary <halcanary@google.com>
Commit-Queue: Ben Wagner <bungeman@google.com>
Reviewed-by: Ben Wagner <bungeman@google.com>
This commit is contained in:
Hal Canary 2018-06-05 11:53:58 -04:00 committed by Skia Commit-Bot
parent bd04be76ad
commit d00ef06619

View File

@ -283,6 +283,78 @@ bool apply_clip(SkClipOp op, const SkPath& u, const SkPath& v, SkPath* r) {
}
}
static SkRect rect_intersect(SkRect u, SkRect v) {
if (u.isEmpty() || v.isEmpty()) { return {0, 0, 0, 0}; }
return u.intersect(v) ? u : SkRect{0, 0, 0, 0};
}
// Test to see if the clipstack is a simple rect, If so, we can avoid all PathOps code
// and speed thing up.
static bool is_rect(const SkClipStack& clipStack, const SkRect& bounds, SkRect* dst) {
SkRect currentClip = bounds;
SkClipStack::Iter iter(clipStack, SkClipStack::Iter::kBottom_IterStart);
while (const SkClipStack::Element* element = iter.next()) {
SkRect elementRect{0, 0, 0, 0};
switch (element->getDeviceSpaceType()) {
case SkClipStack::Element::DeviceSpaceType::kEmpty:
break;
case SkClipStack::Element::DeviceSpaceType::kRect:
elementRect = element->getDeviceSpaceRect();
break;
default:
return false;
}
switch (element->getOp()) {
case kReplace_SkClipOp:
currentClip = rect_intersect(bounds, elementRect);
break;
case SkClipOp::kIntersect:
currentClip = rect_intersect(currentClip, elementRect);
break;
default:
return false;
}
}
*dst = currentClip;
return true;
}
static void append_clip(const SkClipStack& clipStack,
const SkIRect& bounds,
SkWStream* wStream) {
// The bounds are slightly outset to ensure this is correct in the
// face of floating-point accuracy and possible SkRegion bitmap
// approximations.
SkRect outsetBounds = SkRect::Make(bounds.makeOutset(1, 1));
SkRect clipStackRect;
if (is_rect(clipStack, outsetBounds, &clipStackRect)) {
SkPDFUtils::AppendRectangle(clipStackRect, wStream);
wStream->writeText("W* n\n");
return;
}
SkPath clipPath;
(void)clipStack.asPath(&clipPath);
SkPath clipBoundsPath;
clipBoundsPath.addRect(outsetBounds);
if (Op(clipPath, clipBoundsPath, kIntersect_SkPathOp, &clipPath)) {
SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, wStream);
SkPath::FillType clipFill = clipPath.getFillType();
NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
if (clipFill == SkPath::kEvenOdd_FillType) {
wStream->writeText("W* n\n");
} else {
wStream->writeText("W n\n");
}
}
// If Op() fails (pathological case; e.g. input values are
// extremely large or NaN), emit no clip at all.
}
// 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.
@ -301,29 +373,7 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
push();
currentEntry()->fClipStack = clipStack;
SkPath clipPath;
(void)clipStack.asPath(&clipPath);
// The bounds are slightly outset to ensure this is correct in the
// face of floating-point accuracy and possible SkRegion bitmap
// approximations.
SkPath clipBoundsPath;
clipBoundsPath.addRect(SkRect::Make(bounds.makeOutset(1, 1)));
if (Op(clipPath, clipBoundsPath, kIntersect_SkPathOp, &clipPath)) {
SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, fContentStream);
SkPath::FillType clipFill = clipPath.getFillType();
NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
if (clipFill == SkPath::kEvenOdd_FillType) {
fContentStream->writeText("W* n\n");
} else {
fContentStream->writeText("W n\n");
}
}
// If Op() fails (pathological case; e.g. input values are
// extremely large or NaN), emit no clip at all.
append_clip(clipStack, bounds, fContentStream);
}
void GraphicStackState::updateMatrix(const SkMatrix& matrix) {