Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(124)

Side by Side Diff: src/pdf/SkPDFDevice.cpp

Issue 21161003: Use Path Ops to generate PDF clips (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: Make more things static, fix warnings when SK_PDF_USE_PATHOPS not enabled Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« gm/circularclips.cpp ('K') | « gyp/gmslides.gypi ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright 2011 Google Inc. 2 * Copyright 2011 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkPDFDevice.h" 8 #include "SkPDFDevice.h"
9 9
10 #include "SkAnnotation.h" 10 #include "SkAnnotation.h"
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
320 320
321 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); 321 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
322 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); 322 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
323 if (clipFill == SkPath::kEvenOdd_FillType) { 323 if (clipFill == SkPath::kEvenOdd_FillType) {
324 contentStream->writeText("W* n\n"); 324 contentStream->writeText("W* n\n");
325 } else { 325 } else {
326 contentStream->writeText("W n\n"); 326 contentStream->writeText("W n\n");
327 } 327 }
328 } 328 }
329 329
330 /* Calculate an inverted path's equivalent non-inverted path, given the
331 * canvas bounds.
332 * outPath may alias with invPath (since this is supported by PathOps).
333 */
334 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
335 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.
336 SkASSERT(invPath.isInverseFillType());
337
338 SkPath clipPath;
339 clipPath.addRect(bounds);
340
341 return Op(clipPath, invPath, kIntersect_PathOp, outPath);
342 }
343
344 #ifdef SK_PDF_USE_PATHOPS
345 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.
346 switch (op) {
347 case SkRegion::kDifference_Op:
348 return kDifference_PathOp;
349 case SkRegion::kIntersect_Op:
350 return kIntersect_PathOp;
351 case SkRegion::kUnion_Op:
352 return kUnion_PathOp;
353 case SkRegion::kXOR_Op:
354 return kXOR_PathOp;
355 case SkRegion::kReverseDifference_Op:
356 return kReverseDifference_PathOp;
357 default:
358 SkASSERT(false); // unrecognized op
359 }
360 return kIntersect_PathOp;
361 }
362
363 static SkPath get_clip_stack_path(const SkMatrix& transform,
364 const SkClipStack& clipStack,
365 const SkRegion& clipRegion) {
366 SkPath clipPath;
367
368 // 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.
369 // clip with the bounds of the final clip can cause inaccuracies, since
370 // the intermediate clips may be larger than the final bounds.
371 // Therefore, to guarantee accuracy of the final clip, this starts with a
372 // empty clip 2 units out from the final bounds. After all stack operations
373 // are applied, the final clip is cropped to 1 unit out from the final
374 // 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
375 SkRect clipBounds = SkRect::Make(clipRegion.getBounds());
376 clipBounds.outset(SK_Scalar1, SK_Scalar1);
377 SkRect workingBounds = clipBounds;
378 workingBounds.outset(SK_Scalar1, SK_Scalar1);
379 clipPath.addRect(workingBounds);
380
381 const SkClipStack::Element* clipEntry;
382 SkClipStack::Iter iter;
383 iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
384 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
385 SkPath entryPath;
386 if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) {
387 clipPath.reset();
388 continue;
389 } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) {
390 SkRect translatedRect;
391 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
392 entryPath.addRect(translatedRect);
393 } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) {
394 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
395 }
396
397 if (SkRegion::kReplace_Op == clipEntry->getOp()) {
398 clipPath = entryPath;
399 } else {
400 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
401 if (!Op(clipPath, entryPath, op, &clipPath)) {
402 SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
403 return clipPath;
404 }
405 }
406 }
407
408 if (clipPath.isInverseFillType()) {
409 calculate_inverse_path(SkRect::Make(clipRegion.getBounds()),
410 clipPath, &clipPath);
411 }
412
413 SkPath boundsPath;
414 boundsPath.addRect(clipBounds);
415
416 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.
417 SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
418 return clipPath;
419 }
420
421 return clipPath;
422 }
423 #endif
424
330 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF 425 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
331 // graphic state stack, and the fact that we can know all the clips used 426 // graphic state stack, and the fact that we can know all the clips used
332 // on the page to optimize this. 427 // on the page to optimize this.
333 void GraphicStackState::updateClip(const SkClipStack& clipStack, 428 void GraphicStackState::updateClip(const SkClipStack& clipStack,
334 const SkRegion& clipRegion, 429 const SkRegion& clipRegion,
335 const SkPoint& translation) { 430 const SkPoint& translation) {
336 if (clipStack == currentEntry()->fClipStack) { 431 if (clipStack == currentEntry()->fClipStack) {
337 return; 432 return;
338 } 433 }
339 434
(...skipping 11 matching lines...) Expand all
351 // the clip in effect when the layer was created.) There's no need to 446 // the clip in effect when the layer was created.) There's no need to
352 // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the 447 // reapply that clip; SKCanvas's SkDrawIter will draw anything outside the
353 // initial clip on the parent layer. (This means there's a bug if the user 448 // initial clip on the parent layer. (This means there's a bug if the user
354 // expands the clip and then uses any xfer mode that uses dst: 449 // expands the clip and then uses any xfer mode that uses dst:
355 // http://code.google.com/p/skia/issues/detail?id=228 ) 450 // http://code.google.com/p/skia/issues/detail?id=228 )
356 SkClipStack::Iter iter; 451 SkClipStack::Iter iter;
357 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 452 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
358 453
359 // If the clip stack does anything other than intersect or if it uses 454 // If the clip stack does anything other than intersect or if it uses
360 // an inverse fill type, we have to fall back to the clip region. 455 // an inverse fill type, we have to fall back to the clip region.
361 bool needRegion = false; 456 bool needRegion = false;
vandebo (ex-Chrome) 2013/07/31 18:35:17 If we have path ops turned on, what do you think o
ducky 2013/08/01 00:45:08 Sure - added. Though, this could potentially precl
362 const SkClipStack::Element* clipEntry; 457 const SkClipStack::Element* clipEntry;
363 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 458 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
364 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers eFilled()) { 459 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers eFilled()) {
365 needRegion = true; 460 needRegion = true;
366 break; 461 break;
367 } 462 }
368 } 463 }
369 464
465 SkMatrix transform;
466 transform.setTranslate(translation.fX, translation.fY);
467
370 if (needRegion) { 468 if (needRegion) {
469 #ifdef SK_PDF_USE_PATHOPS
470 SkPath clipPath = get_clip_stack_path(transform, clipStack, clipRegion);
471 emit_clip(&clipPath, NULL, fContentStream);
472 #else
371 SkPath clipPath; 473 SkPath clipPath;
372 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); 474 SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
373 emit_clip(&clipPath, NULL, fContentStream); 475 emit_clip(&clipPath, NULL, fContentStream);
476 #endif
374 } else { 477 } else {
375 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 478 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
376 SkMatrix transform;
377 transform.setTranslate(translation.fX, translation.fY);
378 const SkClipStack::Element* clipEntry; 479 const SkClipStack::Element* clipEntry;
379 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 480 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
380 SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op); 481 SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
381 switch (clipEntry->getType()) { 482 switch (clipEntry->getType()) {
382 case SkClipStack::Element::kRect_Type: { 483 case SkClipStack::Element::kRect_Type: {
383 SkRect translatedClip; 484 SkRect translatedClip;
384 transform.mapRect(&translatedClip, clipEntry->getRect()); 485 transform.mapRect(&translatedClip, clipEntry->getRect());
385 emit_clip(NULL, &translatedClip, fContentStream); 486 emit_clip(NULL, &translatedClip, fContentStream);
386 break; 487 break;
387 } 488 }
(...skipping 844 matching lines...) Expand 10 before | Expand all | Expand 10 after
1232 emit_clip(NULL, &r, &data); 1333 emit_clip(NULL, &r, &data);
1233 } 1334 }
1234 1335
1235 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); 1336 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data);
1236 1337
1237 // potentially we could cache this SkData, and only rebuild it if we 1338 // potentially we could cache this SkData, and only rebuild it if we
1238 // see that our state has changed. 1339 // see that our state has changed.
1239 return data.copyToData(); 1340 return data.copyToData();
1240 } 1341 }
1241 1342
1242 /* Calculate an inverted path's equivalent non-inverted path, given the
1243 * canvas bounds.
1244 */
1245 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
1246 SkPath* outPath) {
1247 SkASSERT(invPath.isInverseFillType());
1248
1249 SkPath clipPath;
1250 clipPath.addRect(bounds);
1251
1252 return Op(clipPath, invPath, kIntersect_PathOp, outPath);
1253 }
1254
1255 /* Draws an inverse filled path by using Path Ops to compute the positive 1343 /* Draws an inverse filled path by using Path Ops to compute the positive
1256 * inverse using the current clip as the inverse bounds. 1344 * inverse using the current clip as the inverse bounds.
1257 * Return true if this was an inverse path and was properly handled, 1345 * Return true if this was an inverse path and was properly handled,
1258 * otherwise returns false and the normal drawing routine should continue, 1346 * otherwise returns false and the normal drawing routine should continue,
1259 * either as a (incorrect) fallback or because the path was not inverse 1347 * either as a (incorrect) fallback or because the path was not inverse
1260 * in the first place. 1348 * in the first place.
1261 */ 1349 */
1262 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath, 1350 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath,
1263 const SkPaint& paint, bool pathIsMutable) { 1351 const SkPaint& paint, bool pathIsMutable) {
1264 if (!origPath.isInverseFillType()) { 1352 if (!origPath.isInverseFillType()) {
(...skipping 567 matching lines...) Expand 10 before | Expand all | Expand 10 after
1832 } 1920 }
1833 1921
1834 bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y, 1922 bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
1835 SkCanvas::Config8888) { 1923 SkCanvas::Config8888) {
1836 return false; 1924 return false;
1837 } 1925 }
1838 1926
1839 bool SkPDFDevice::allowImageFilter(SkImageFilter*) { 1927 bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
1840 return false; 1928 return false;
1841 } 1929 }
OLDNEW
« gm/circularclips.cpp ('K') | « gyp/gmslides.gypi ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698