Chromium Code Reviews| 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, |