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 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 266 fStackDepth++; | 266 fStackDepth++; |
| 267 fEntries[fStackDepth] = fEntries[fStackDepth - 1]; | 267 fEntries[fStackDepth] = fEntries[fStackDepth - 1]; |
| 268 } | 268 } |
| 269 | 269 |
| 270 void GraphicStackState::pop() { | 270 void GraphicStackState::pop() { |
| 271 SkASSERT(fStackDepth > 0); | 271 SkASSERT(fStackDepth > 0); |
| 272 fContentStream->writeText("Q\n"); | 272 fContentStream->writeText("Q\n"); |
| 273 fStackDepth--; | 273 fStackDepth--; |
| 274 } | 274 } |
| 275 | 275 |
| 276 #ifndef SK_PDF_USE_PATHOPS | |
| 276 // This function initializes iter to be an iterator on the "stack" argument | 277 // This function initializes iter to be an iterator on the "stack" argument |
| 277 // and then skips over the leading entries as specified in prefix. It requires | 278 // and then skips over the leading entries as specified in prefix. It requires |
| 278 // and asserts that "prefix" will be a prefix to "stack." | 279 // and asserts that "prefix" will be a prefix to "stack." |
| 279 static void skip_clip_stack_prefix(const SkClipStack& prefix, | 280 static void skip_clip_stack_prefix(const SkClipStack& prefix, |
| 280 const SkClipStack& stack, | 281 const SkClipStack& stack, |
| 281 SkClipStack::Iter* iter) { | 282 SkClipStack::Iter* iter) { |
| 282 SkClipStack::B2TIter prefixIter(prefix); | 283 SkClipStack::B2TIter prefixIter(prefix); |
| 283 iter->reset(stack, SkClipStack::Iter::kBottom_IterStart); | 284 iter->reset(stack, SkClipStack::Iter::kBottom_IterStart); |
| 284 | 285 |
| 285 const SkClipStack::Element* prefixEntry; | 286 const SkClipStack::Element* prefixEntry; |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 297 SkASSERT(iterEntry->getType() == prefixEntry->getType()); | 298 SkASSERT(iterEntry->getType() == prefixEntry->getType()); |
| 298 // back up the iterator by one | 299 // back up the iterator by one |
| 299 iter->prev(); | 300 iter->prev(); |
| 300 prefixEntry = prefixIter.next(); | 301 prefixEntry = prefixIter.next(); |
| 301 break; | 302 break; |
| 302 } | 303 } |
| 303 } | 304 } |
| 304 | 305 |
| 305 SkASSERT(prefixEntry == NULL); | 306 SkASSERT(prefixEntry == NULL); |
| 306 } | 307 } |
| 308 #endif | |
| 307 | 309 |
| 308 static void emit_clip(SkPath* clipPath, SkRect* clipRect, | 310 static void emit_clip(SkPath* clipPath, SkRect* clipRect, |
| 309 SkWStream* contentStream) { | 311 SkWStream* contentStream) { |
| 310 SkASSERT(clipPath || clipRect); | 312 SkASSERT(clipPath || clipRect); |
| 311 | 313 |
| 312 SkPath::FillType clipFill; | 314 SkPath::FillType clipFill; |
| 313 if (clipPath) { | 315 if (clipPath) { |
| 314 SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream); | 316 SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream); |
| 315 clipFill = clipPath->getFillType(); | 317 clipFill = clipPath->getFillType(); |
| 316 } else { | 318 } else { |
| 317 SkPDFUtils::AppendRectangle(*clipRect, contentStream); | 319 SkPDFUtils::AppendRectangle(*clipRect, contentStream); |
| 318 clipFill = SkPath::kWinding_FillType; | 320 clipFill = SkPath::kWinding_FillType; |
| 319 } | 321 } |
| 320 | 322 |
| 321 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); | 323 NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false); |
| 322 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); | 324 NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false); |
| 323 if (clipFill == SkPath::kEvenOdd_FillType) { | 325 if (clipFill == SkPath::kEvenOdd_FillType) { |
| 324 contentStream->writeText("W* n\n"); | 326 contentStream->writeText("W* n\n"); |
| 325 } else { | 327 } else { |
| 326 contentStream->writeText("W n\n"); | 328 contentStream->writeText("W n\n"); |
| 327 } | 329 } |
| 328 } | 330 } |
| 329 | 331 |
| 332 #ifdef SK_PDF_USE_PATHOPS | |
| 333 // Sanity check the numerical values of the SkRegion ops and PathOps ops | |
| 334 // enums so region_op_to_pathops_op can do a straight passthrough cast. | |
| 335 // If these are failing, it may be necessary to make region_op_to_pathops_op | |
| 336 // do more. | |
| 337 SK_COMPILE_ASSERT(SkRegion::kDifference_Op == (int)kDifference_PathOp, | |
| 338 region_pathop_mismatch); | |
| 339 SK_COMPILE_ASSERT(SkRegion::kIntersect_Op == (int)kIntersect_PathOp, | |
| 340 region_pathop_mismatch); | |
| 341 SK_COMPILE_ASSERT(SkRegion::kUnion_Op == (int)kUnion_PathOp, | |
| 342 region_pathop_mismatch); | |
| 343 SK_COMPILE_ASSERT(SkRegion::kXOR_Op == (int)kXOR_PathOp, | |
| 344 region_pathop_mismatch); | |
| 345 SK_COMPILE_ASSERT(SkRegion::kReverseDifference_Op == | |
| 346 (int)kReverseDifference_PathOp, | |
| 347 region_pathop_mismatch); | |
| 348 | |
| 349 static SkPathOp region_op_to_pathops_op(SkRegion::Op op) { | |
| 350 SkASSERT(op >= 0); | |
| 351 SkASSERT(op <= SkRegion::kReverseDifference_Op); | |
| 352 return (SkPathOp)op; | |
| 353 } | |
| 354 | |
| 355 static SkPath get_clip_stack_path(const SkMatrix& transform, | |
| 356 const SkClipStack& clipStack, | |
| 357 const SkRegion& clipRegion) { | |
| 358 SkPath clipPath; | |
| 359 | |
| 360 // Note that this starts with a filled clip only on the final bounds, | |
| 361 // instead of the whole page. This can cause incorrectness in intermediate | |
| 362 // operations, which may work with shapes that are outside the final | |
| 363 // bounds (for example, consider XORing a square outside the final bounds - | |
| 364 // the actual result would differ from the simulated result). | |
| 365 // Therefore, to guarantee accuracy of the final clip, this starts with a | |
| 366 // empty clip 2 units out from the final bounds. After all stack operations | |
| 367 // are applied, the final clip is cropped to 1 unit out from the final | |
| 368 // bounds. The buffer space ensures there is no ambiguity on edge cases. | |
| 369 SkRect clipBounds = SkRect::Make(clipRegion.getBounds()); | |
| 370 clipBounds.outset(SK_Scalar1, SK_Scalar1); | |
| 371 SkRect workingBounds = clipBounds; | |
| 372 workingBounds.outset(SK_Scalar1, SK_Scalar1); | |
|
vandebo (ex-Chrome)
2013/08/01 14:56:14
Do you need to constrain the path bounds at the st
ducky
2013/08/01 21:22:46
Good point, I didn't realize I could just set the
| |
| 373 clipPath.addRect(workingBounds); | |
| 374 | |
| 375 const SkClipStack::Element* clipEntry; | |
| 376 SkClipStack::Iter iter; | |
| 377 iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart); | |
| 378 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { | |
| 379 SkPath entryPath; | |
| 380 if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) { | |
| 381 clipPath.reset(); | |
|
vandebo (ex-Chrome)
2013/08/01 14:56:14
Should this also add workingBounds to clipPath ?
ducky
2013/08/01 21:22:46
Yep. D'oh. Fixed, with new empty clip style.
| |
| 382 continue; | |
| 383 } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) { | |
| 384 entryPath.addRect(clipEntry->getRect()); | |
| 385 } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) { | |
| 386 entryPath = clipEntry->getPath(); | |
| 387 } | |
| 388 entryPath.transform(transform); | |
| 389 | |
| 390 if (SkRegion::kReplace_Op == clipEntry->getOp()) { | |
| 391 clipPath = entryPath; | |
|
vandebo (ex-Chrome)
2013/08/01 14:56:14
Should you intersect with workingBounds here?
ducky
2013/08/01 21:22:46
No longer necessary with new style. But the idea b
| |
| 392 } else { | |
| 393 SkPathOp op = region_op_to_pathops_op(clipEntry->getOp()); | |
| 394 if (!Op(clipPath, entryPath, op, &clipPath)) { | |
| 395 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); | |
| 396 return clipPath; | |
| 397 } | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 SkPath boundsPath; | |
| 402 boundsPath.addRect(clipBounds); | |
| 403 | |
| 404 if (!Op(clipPath, boundsPath, kIntersect_PathOp, &clipPath)) { | |
| 405 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); | |
| 406 return clipPath; | |
| 407 } | |
| 408 | |
| 409 return clipPath; | |
| 410 } | |
| 411 #endif | |
| 412 | |
| 330 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF | 413 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF |
| 331 // graphic state stack, and the fact that we can know all the clips used | 414 // graphic state stack, and the fact that we can know all the clips used |
| 332 // on the page to optimize this. | 415 // on the page to optimize this. |
| 333 void GraphicStackState::updateClip(const SkClipStack& clipStack, | 416 void GraphicStackState::updateClip(const SkClipStack& clipStack, |
| 334 const SkRegion& clipRegion, | 417 const SkRegion& clipRegion, |
| 335 const SkPoint& translation) { | 418 const SkPoint& translation) { |
| 336 if (clipStack == currentEntry()->fClipStack) { | 419 if (clipStack == currentEntry()->fClipStack) { |
| 337 return; | 420 return; |
| 338 } | 421 } |
| 339 | 422 |
| 340 while (fStackDepth > 0) { | 423 while (fStackDepth > 0) { |
| 341 pop(); | 424 pop(); |
| 342 if (clipStack == currentEntry()->fClipStack) { | 425 if (clipStack == currentEntry()->fClipStack) { |
| 343 return; | 426 return; |
| 344 } | 427 } |
| 345 } | 428 } |
| 346 push(); | 429 push(); |
| 347 | 430 |
| 431 SkMatrix transform; | |
| 432 transform.setTranslate(translation.fX, translation.fY); | |
| 433 | |
| 434 #ifdef SK_PDF_USE_PATHOPS | |
| 435 SkPath clipPath = get_clip_stack_path(transform, clipStack, clipRegion); | |
| 436 emit_clip(&clipPath, NULL, fContentStream); | |
| 437 #else | |
| 348 // gsState->initialEntry()->fClipStack/Region specifies the clip that has | 438 // gsState->initialEntry()->fClipStack/Region specifies the clip that has |
| 349 // already been applied. (If this is a top level device, then it specifies | 439 // already been applied. (If this is a top level device, then it specifies |
| 350 // a clip to the content area. If this is a layer, then it specifies | 440 // a clip to the content area. If this is a layer, then it specifies |
| 351 // the clip in effect when the layer was created.) There's no need to | 441 // 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 | 442 // 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 | 443 // 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: | 444 // expands the clip and then uses any xfer mode that uses dst: |
| 355 // http://code.google.com/p/skia/issues/detail?id=228 ) | 445 // http://code.google.com/p/skia/issues/detail?id=228 ) |
| 356 SkClipStack::Iter iter; | 446 SkClipStack::Iter iter; |
| 357 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); | 447 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); |
| 358 | 448 |
| 359 // If the clip stack does anything other than intersect or if it uses | 449 // 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. | 450 // an inverse fill type, we have to fall back to the clip region. |
| 361 bool needRegion = false; | 451 bool needRegion = false; |
| 362 const SkClipStack::Element* clipEntry; | 452 const SkClipStack::Element* clipEntry; |
| 363 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { | 453 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { |
| 364 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers eFilled()) { | 454 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers eFilled()) { |
| 365 needRegion = true; | 455 needRegion = true; |
| 366 break; | 456 break; |
| 367 } | 457 } |
| 368 } | 458 } |
| 369 | 459 |
| 370 if (needRegion) { | 460 if (needRegion) { |
| 371 SkPath clipPath; | 461 SkPath clipPath; |
| 372 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); | 462 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); |
| 373 emit_clip(&clipPath, NULL, fContentStream); | 463 emit_clip(&clipPath, NULL, fContentStream); |
| 374 } else { | 464 } else { |
| 375 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); | 465 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); |
| 376 SkMatrix transform; | |
| 377 transform.setTranslate(translation.fX, translation.fY); | |
| 378 const SkClipStack::Element* clipEntry; | 466 const SkClipStack::Element* clipEntry; |
| 379 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { | 467 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { |
| 380 SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op); | 468 SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op); |
| 381 switch (clipEntry->getType()) { | 469 switch (clipEntry->getType()) { |
| 382 case SkClipStack::Element::kRect_Type: { | 470 case SkClipStack::Element::kRect_Type: { |
| 383 SkRect translatedClip; | 471 SkRect translatedClip; |
| 384 transform.mapRect(&translatedClip, clipEntry->getRect()); | 472 transform.mapRect(&translatedClip, clipEntry->getRect()); |
| 385 emit_clip(NULL, &translatedClip, fContentStream); | 473 emit_clip(NULL, &translatedClip, fContentStream); |
| 386 break; | 474 break; |
| 387 } | 475 } |
| 388 case SkClipStack::Element::kPath_Type: { | 476 case SkClipStack::Element::kPath_Type: { |
| 389 SkPath translatedPath; | 477 SkPath translatedPath; |
| 390 clipEntry->getPath().transform(transform, &translatedPath); | 478 clipEntry->getPath().transform(transform, &translatedPath); |
| 391 emit_clip(&translatedPath, NULL, fContentStream); | 479 emit_clip(&translatedPath, NULL, fContentStream); |
| 392 break; | 480 break; |
| 393 } | 481 } |
| 394 default: | 482 default: |
| 395 SkASSERT(false); | 483 SkASSERT(false); |
| 396 } | 484 } |
| 397 } | 485 } |
| 398 } | 486 } |
| 487 #endif | |
| 399 currentEntry()->fClipStack = clipStack; | 488 currentEntry()->fClipStack = clipStack; |
| 400 currentEntry()->fClipRegion = clipRegion; | 489 currentEntry()->fClipRegion = clipRegion; |
| 401 } | 490 } |
| 402 | 491 |
| 403 void GraphicStackState::updateMatrix(const SkMatrix& matrix) { | 492 void GraphicStackState::updateMatrix(const SkMatrix& matrix) { |
| 404 if (matrix == currentEntry()->fMatrix) { | 493 if (matrix == currentEntry()->fMatrix) { |
| 405 return; | 494 return; |
| 406 } | 495 } |
| 407 | 496 |
| 408 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { | 497 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { |
| (...skipping 825 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1234 | 1323 |
| 1235 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); | 1324 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); |
| 1236 | 1325 |
| 1237 // potentially we could cache this SkData, and only rebuild it if we | 1326 // potentially we could cache this SkData, and only rebuild it if we |
| 1238 // see that our state has changed. | 1327 // see that our state has changed. |
| 1239 return data.copyToData(); | 1328 return data.copyToData(); |
| 1240 } | 1329 } |
| 1241 | 1330 |
| 1242 /* Calculate an inverted path's equivalent non-inverted path, given the | 1331 /* Calculate an inverted path's equivalent non-inverted path, given the |
| 1243 * canvas bounds. | 1332 * canvas bounds. |
| 1333 * outPath may alias with invPath (since this is supported by PathOps). | |
| 1244 */ | 1334 */ |
| 1245 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, | 1335 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, |
| 1246 SkPath* outPath) { | 1336 SkPath* outPath) { |
| 1247 SkASSERT(invPath.isInverseFillType()); | 1337 SkASSERT(invPath.isInverseFillType()); |
| 1248 | 1338 |
| 1249 SkPath clipPath; | 1339 SkPath clipPath; |
| 1250 clipPath.addRect(bounds); | 1340 clipPath.addRect(bounds); |
| 1251 | 1341 |
| 1252 return Op(clipPath, invPath, kIntersect_PathOp, outPath); | 1342 return Op(clipPath, invPath, kIntersect_PathOp, outPath); |
| 1253 } | 1343 } |
| (...skipping 578 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1832 } | 1922 } |
| 1833 | 1923 |
| 1834 bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y, | 1924 bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y, |
| 1835 SkCanvas::Config8888) { | 1925 SkCanvas::Config8888) { |
| 1836 return false; | 1926 return false; |
| 1837 } | 1927 } |
| 1838 | 1928 |
| 1839 bool SkPDFDevice::allowImageFilter(SkImageFilter*) { | 1929 bool SkPDFDevice::allowImageFilter(SkImageFilter*) { |
| 1840 return false; | 1930 return false; |
| 1841 } | 1931 } |
| OLD | NEW |