From d00ef066198b40aa0b02603fe4766b31996eb835 Mon Sep 17 00:00:00 2001 From: Hal Canary Date: Tue, 5 Jun 2018 11:53:58 -0400 Subject: SkPDF: Fastpath for clipstack flattening BUG: chromium:837279 Change-Id: I79a675e7d9e713617711948e491e28babe06b1a2 Reviewed-on: https://skia-review.googlesource.com/132092 Auto-Submit: Hal Canary Commit-Queue: Ben Wagner Reviewed-by: Ben Wagner --- src/pdf/SkPDFDevice.cpp | 94 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 22 deletions(-) (limited to 'src/pdf') diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index ced94ce9a9..1cf649c1a1 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -283,49 +283,99 @@ bool apply_clip(SkClipOp op, const SkPath& u, const SkPath& v, SkPath* r) { } } -// 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. -void GraphicStackState::updateClip(const SkClipStack& clipStack, - const SkIRect& bounds) { - if (clipStack == currentEntry()->fClipStack) { - return; - } - - while (fStackDepth > 0) { - pop(); - if (clipStack == currentEntry()->fClipStack) { - return; +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; } } - push(); + *dst = currentClip; + return true; +} - currentEntry()->fClipStack = clipStack; +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); - // 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))); + clipBoundsPath.addRect(outsetBounds); if (Op(clipPath, clipBoundsPath, kIntersect_SkPathOp, &clipPath)) { - SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, fContentStream); + 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) { - fContentStream->writeText("W* n\n"); + wStream->writeText("W* n\n"); } else { - fContentStream->writeText("W n\n"); + 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. +void GraphicStackState::updateClip(const SkClipStack& clipStack, + const SkIRect& bounds) { + if (clipStack == currentEntry()->fClipStack) { + return; + } + + while (fStackDepth > 0) { + pop(); + if (clipStack == currentEntry()->fClipStack) { + return; + } + } + push(); + + currentEntry()->fClipStack = clipStack; + append_clip(clipStack, bounds, fContentStream); +} + void GraphicStackState::updateMatrix(const SkMatrix& matrix) { if (matrix == currentEntry()->fMatrix) { return; -- cgit v1.2.3