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

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: Fix indents 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
« no previous file with comments | « 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,
vandebo (ex-Chrome) 2013/08/05 16:38:08 nit: put this and handleInversePath() inside the S
ducky 2013/08/05 17:54:19 Done.
335 SkPath* outPath) {
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 // 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 SkMatrix transform;
439 transform.setTranslate(translation.fX, translation.fY);
440
441 #ifdef SK_PDF_USE_PATHOPS
442 SkPath clipPath;
443 if (get_clip_stack_path(transform, clipStack, clipRegion, &clipPath)) {
444 emit_clip(&clipPath, NULL, fContentStream);
445
446 currentEntry()->fClipStack = clipStack;
447 currentEntry()->fClipRegion = clipRegion;
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; 500 currentEntry()->fClipStack = clipStack;
vandebo (ex-Chrome) 2013/08/05 16:38:08 nit: this is done in all cases, so just pull it up
ducky 2013/08/05 17:54:19 Done.
400 currentEntry()->fClipRegion = clipRegion; 501 currentEntry()->fClipRegion = clipRegion;
401 } 502 }
402 503
403 void GraphicStackState::updateMatrix(const SkMatrix& matrix) { 504 void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
404 if (matrix == currentEntry()->fMatrix) { 505 if (matrix == currentEntry()->fMatrix) {
405 return; 506 return;
406 } 507 }
407 508
408 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { 509 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
409 SkASSERT(fStackDepth > 0); 510 SkASSERT(fStackDepth > 0);
(...skipping 822 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
« no previous file with comments | « gyp/gmslides.gypi ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698