Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |