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

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 review comments 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 255 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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 }
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