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 #ifdef SK_PDF_USE_PATHOPS |
| 331 /* Calculate an inverted path's equivalent non-inverted path, given the |
| 332 * canvas bounds. |
| 333 * outPath may alias with invPath (since this is supported by PathOps). |
| 334 */ |
| 335 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, |
| 336 SkPath* outPath) { |
| 337 SkASSERT(invPath.isInverseFillType()); |
| 338 |
| 339 SkPath clipPath; |
| 340 clipPath.addRect(bounds); |
| 341 |
| 342 return Op(clipPath, invPath, kIntersect_PathOp, outPath); |
| 343 } |
| 344 |
| 345 // Sanity check the numerical values of the SkRegion ops and PathOps ops |
| 346 // enums so region_op_to_pathops_op can do a straight passthrough cast. |
| 347 // If these are failing, it may be necessary to make region_op_to_pathops_op |
| 348 // do more. |
| 349 SK_COMPILE_ASSERT(SkRegion::kDifference_Op == (int)kDifference_PathOp, |
| 350 region_pathop_mismatch); |
| 351 SK_COMPILE_ASSERT(SkRegion::kIntersect_Op == (int)kIntersect_PathOp, |
| 352 region_pathop_mismatch); |
| 353 SK_COMPILE_ASSERT(SkRegion::kUnion_Op == (int)kUnion_PathOp, |
| 354 region_pathop_mismatch); |
| 355 SK_COMPILE_ASSERT(SkRegion::kXOR_Op == (int)kXOR_PathOp, |
| 356 region_pathop_mismatch); |
| 357 SK_COMPILE_ASSERT(SkRegion::kReverseDifference_Op == |
| 358 (int)kReverseDifference_PathOp, |
| 359 region_pathop_mismatch); |
| 360 |
| 361 static SkPathOp region_op_to_pathops_op(SkRegion::Op op) { |
| 362 SkASSERT(op >= 0); |
| 363 SkASSERT(op <= SkRegion::kReverseDifference_Op); |
| 364 return (SkPathOp)op; |
| 365 } |
| 366 |
| 367 /* Uses Path Ops to calculate a vector SkPath clip from a clip stack. |
| 368 * Returns true if successful, or false if not successful. |
| 369 * If successful, the resulting clip is stored in outClipPath. |
| 370 * If not successful, outClipPath is undefined, and a fallback method |
| 371 * should be used. |
| 372 */ |
| 373 static bool get_clip_stack_path(const SkMatrix& transform, |
| 374 const SkClipStack& clipStack, |
| 375 const SkRegion& clipRegion, |
| 376 SkPath* outClipPath) { |
| 377 outClipPath->reset(); |
| 378 outClipPath->setFillType(SkPath::kInverseWinding_FillType); |
| 379 |
| 380 const SkClipStack::Element* clipEntry; |
| 381 SkClipStack::Iter iter; |
| 382 iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart); |
| 383 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { |
| 384 SkPath entryPath; |
| 385 if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) { |
| 386 outClipPath->reset(); |
| 387 outClipPath->setFillType(SkPath::kInverseWinding_FillType); |
| 388 continue; |
| 389 } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) { |
| 390 entryPath.addRect(clipEntry->getRect()); |
| 391 } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) { |
| 392 entryPath = clipEntry->getPath(); |
| 393 } |
| 394 entryPath.transform(transform); |
| 395 |
| 396 if (SkRegion::kReplace_Op == clipEntry->getOp()) { |
| 397 *outClipPath = entryPath; |
| 398 } else { |
| 399 SkPathOp op = region_op_to_pathops_op(clipEntry->getOp()); |
| 400 if (!Op(*outClipPath, entryPath, op, outClipPath)) { |
| 401 return false; |
| 402 } |
| 403 } |
| 404 } |
| 405 |
| 406 if (outClipPath->isInverseFillType()) { |
| 407 // The bounds are slightly outset to ensure this is correct in the |
| 408 // face of floating-point accuracy and possible SkRegion bitmap |
| 409 // approximations. |
| 410 SkRect clipBounds = SkRect::Make(clipRegion.getBounds()); |
| 411 clipBounds.outset(SK_Scalar1, SK_Scalar1); |
| 412 if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) { |
| 413 return false; |
| 414 } |
| 415 } |
| 416 return true; |
| 417 } |
| 418 #endif |
| 419 |
330 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF | 420 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF |
331 // graphic state stack, and the fact that we can know all the clips used | 421 // graphic state stack, and the fact that we can know all the clips used |
332 // on the page to optimize this. | 422 // on the page to optimize this. |
333 void GraphicStackState::updateClip(const SkClipStack& clipStack, | 423 void GraphicStackState::updateClip(const SkClipStack& clipStack, |
334 const SkRegion& clipRegion, | 424 const SkRegion& clipRegion, |
335 const SkPoint& translation) { | 425 const SkPoint& translation) { |
336 if (clipStack == currentEntry()->fClipStack) { | 426 if (clipStack == currentEntry()->fClipStack) { |
337 return; | 427 return; |
338 } | 428 } |
339 | 429 |
340 while (fStackDepth > 0) { | 430 while (fStackDepth > 0) { |
341 pop(); | 431 pop(); |
342 if (clipStack == currentEntry()->fClipStack) { | 432 if (clipStack == currentEntry()->fClipStack) { |
343 return; | 433 return; |
344 } | 434 } |
345 } | 435 } |
346 push(); | 436 push(); |
347 | 437 |
| 438 currentEntry()->fClipStack = clipStack; |
| 439 currentEntry()->fClipRegion = clipRegion; |
| 440 |
| 441 SkMatrix transform; |
| 442 transform.setTranslate(translation.fX, translation.fY); |
| 443 |
| 444 #ifdef SK_PDF_USE_PATHOPS |
| 445 SkPath clipPath; |
| 446 if (get_clip_stack_path(transform, clipStack, clipRegion, &clipPath)) { |
| 447 emit_clip(&clipPath, NULL, fContentStream); |
| 448 return; |
| 449 } |
| 450 #endif |
348 // gsState->initialEntry()->fClipStack/Region specifies the clip that has | 451 // gsState->initialEntry()->fClipStack/Region specifies the clip that has |
349 // already been applied. (If this is a top level device, then it specifies | 452 // 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 | 453 // 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 | 454 // 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 | 455 // 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 | 456 // 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: | 457 // expands the clip and then uses any xfer mode that uses dst: |
355 // http://code.google.com/p/skia/issues/detail?id=228 ) | 458 // http://code.google.com/p/skia/issues/detail?id=228 ) |
356 SkClipStack::Iter iter; | 459 SkClipStack::Iter iter; |
357 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); | 460 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); |
358 | 461 |
359 // If the clip stack does anything other than intersect or if it uses | 462 // 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. | 463 // an inverse fill type, we have to fall back to the clip region. |
361 bool needRegion = false; | 464 bool needRegion = false; |
362 const SkClipStack::Element* clipEntry; | 465 const SkClipStack::Element* clipEntry; |
363 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { | 466 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { |
364 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers
eFilled()) { | 467 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers
eFilled()) { |
365 needRegion = true; | 468 needRegion = true; |
366 break; | 469 break; |
367 } | 470 } |
368 } | 471 } |
369 | 472 |
370 if (needRegion) { | 473 if (needRegion) { |
371 SkPath clipPath; | 474 SkPath clipPath; |
372 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); | 475 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); |
373 emit_clip(&clipPath, NULL, fContentStream); | 476 emit_clip(&clipPath, NULL, fContentStream); |
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 } |
388 case SkClipStack::Element::kPath_Type: { | 489 case SkClipStack::Element::kPath_Type: { |
389 SkPath translatedPath; | 490 SkPath translatedPath; |
390 clipEntry->getPath().transform(transform, &translatedPath); | 491 clipEntry->getPath().transform(transform, &translatedPath); |
391 emit_clip(&translatedPath, NULL, fContentStream); | 492 emit_clip(&translatedPath, NULL, fContentStream); |
392 break; | 493 break; |
393 } | 494 } |
394 default: | 495 default: |
395 SkASSERT(false); | 496 SkASSERT(false); |
396 } | 497 } |
397 } | 498 } |
398 } | 499 } |
399 currentEntry()->fClipStack = clipStack; | |
400 currentEntry()->fClipRegion = clipRegion; | |
401 } | 500 } |
402 | 501 |
403 void GraphicStackState::updateMatrix(const SkMatrix& matrix) { | 502 void GraphicStackState::updateMatrix(const SkMatrix& matrix) { |
404 if (matrix == currentEntry()->fMatrix) { | 503 if (matrix == currentEntry()->fMatrix) { |
405 return; | 504 return; |
406 } | 505 } |
407 | 506 |
408 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { | 507 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { |
409 SkASSERT(fStackDepth > 0); | 508 SkASSERT(fStackDepth > 0); |
410 SkASSERT(fEntries[fStackDepth].fClipStack == | 509 SkASSERT(fEntries[fStackDepth].fClipStack == |
(...skipping 821 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1232 emit_clip(NULL, &r, &data); | 1331 emit_clip(NULL, &r, &data); |
1233 } | 1332 } |
1234 | 1333 |
1235 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); | 1334 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); |
1236 | 1335 |
1237 // potentially we could cache this SkData, and only rebuild it if we | 1336 // potentially we could cache this SkData, and only rebuild it if we |
1238 // see that our state has changed. | 1337 // see that our state has changed. |
1239 return data.copyToData(); | 1338 return data.copyToData(); |
1240 } | 1339 } |
1241 | 1340 |
1242 /* Calculate an inverted path's equivalent non-inverted path, given the | 1341 #ifdef SK_PDF_USE_PATHOPS |
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 | 1342 /* Draws an inverse filled path by using Path Ops to compute the positive |
1256 * inverse using the current clip as the inverse bounds. | 1343 * inverse using the current clip as the inverse bounds. |
1257 * Return true if this was an inverse path and was properly handled, | 1344 * Return true if this was an inverse path and was properly handled, |
1258 * otherwise returns false and the normal drawing routine should continue, | 1345 * otherwise returns false and the normal drawing routine should continue, |
1259 * either as a (incorrect) fallback or because the path was not inverse | 1346 * either as a (incorrect) fallback or because the path was not inverse |
1260 * in the first place. | 1347 * in the first place. |
1261 */ | 1348 */ |
1262 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath, | 1349 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath, |
1263 const SkPaint& paint, bool pathIsMutable) { | 1350 const SkPaint& paint, bool pathIsMutable) { |
1264 if (!origPath.isInverseFillType()) { | 1351 if (!origPath.isInverseFillType()) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1305 bounds.outset(paint.getStrokeWidth() + SK_Scalar1, | 1392 bounds.outset(paint.getStrokeWidth() + SK_Scalar1, |
1306 paint.getStrokeWidth() + SK_Scalar1); | 1393 paint.getStrokeWidth() + SK_Scalar1); |
1307 | 1394 |
1308 if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) { | 1395 if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) { |
1309 return false; | 1396 return false; |
1310 } | 1397 } |
1311 | 1398 |
1312 drawPath(d, modifiedPath, noInversePaint, NULL, true); | 1399 drawPath(d, modifiedPath, noInversePaint, NULL, true); |
1313 return true; | 1400 return true; |
1314 } | 1401 } |
| 1402 #endif |
1315 | 1403 |
1316 bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix, | 1404 bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix, |
1317 const SkPaint& p) { | 1405 const SkPaint& p) { |
1318 SkAnnotation* annotationInfo = p.getAnnotation(); | 1406 SkAnnotation* annotationInfo = p.getAnnotation(); |
1319 if (!annotationInfo) { | 1407 if (!annotationInfo) { |
1320 return false; | 1408 return false; |
1321 } | 1409 } |
1322 SkData* urlData = annotationInfo->find(SkAnnotationKeys::URL_Key()); | 1410 SkData* urlData = annotationInfo->find(SkAnnotationKeys::URL_Key()); |
1323 if (urlData) { | 1411 if (urlData) { |
1324 handleLinkToURL(urlData, r, matrix); | 1412 handleLinkToURL(urlData, r, matrix); |
(...skipping 507 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 |