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

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: Use wide-open inverse clip 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 clipPath.setFillType(SkPath::kInverseWinding_FillType);
360
361 const SkClipStack::Element* clipEntry;
362 SkClipStack::Iter iter;
363 iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
364 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
365 SkPath entryPath;
366 if (SkClipStack::Element::kEmpty_Type == clipEntry->getType()) {
367 clipPath.reset();
368 clipPath.setFillType(SkPath::kInverseWinding_FillType);
369 continue;
370 } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) {
371 entryPath.addRect(clipEntry->getRect());
372 } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) {
373 entryPath = clipEntry->getPath();
374 }
375 entryPath.transform(transform);
376
377 if (SkRegion::kReplace_Op == clipEntry->getOp()) {
378 clipPath = entryPath;
379 } else {
380 SkPathOp op = region_op_to_pathops_op(clipEntry->getOp());
381 if (!Op(clipPath, entryPath, op, &clipPath)) {
382 SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
vandebo (ex-Chrome) 2013/08/02 15:58:09 Here we could fall back to the old code.
ducky 2013/08/02 19:03:33 Done.
383 return clipPath;
384 }
385 }
386 }
387
388 // Intersect the results with the clip bounds from the region to handle
vandebo (ex-Chrome) 2013/08/02 15:58:09 Don't we only need to do this chunk of code if cli
ducky 2013/08/02 19:03:33 Good point.
389 // an inverse fill. The bounds are slightly outset to ensure this is
390 // correct in the face of floating-point accuracy and possible
391 // SkRegion bitmap approximations.
392 SkRect clipBounds = SkRect::Make(clipRegion.getBounds());
393 clipBounds.outset(SK_Scalar1, SK_Scalar1);
394 SkPath boundsPath;
395 boundsPath.addRect(clipBounds);
396
397 if (!Op(clipPath, boundsPath, kIntersect_PathOp, &clipPath)) {
398 SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
399 return clipPath;
400 }
401
402 return clipPath;
403 }
404 #endif
405
330 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF 406 // TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
331 // graphic state stack, and the fact that we can know all the clips used 407 // graphic state stack, and the fact that we can know all the clips used
332 // on the page to optimize this. 408 // on the page to optimize this.
333 void GraphicStackState::updateClip(const SkClipStack& clipStack, 409 void GraphicStackState::updateClip(const SkClipStack& clipStack,
334 const SkRegion& clipRegion, 410 const SkRegion& clipRegion,
335 const SkPoint& translation) { 411 const SkPoint& translation) {
336 if (clipStack == currentEntry()->fClipStack) { 412 if (clipStack == currentEntry()->fClipStack) {
337 return; 413 return;
338 } 414 }
339 415
340 while (fStackDepth > 0) { 416 while (fStackDepth > 0) {
341 pop(); 417 pop();
342 if (clipStack == currentEntry()->fClipStack) { 418 if (clipStack == currentEntry()->fClipStack) {
343 return; 419 return;
344 } 420 }
345 } 421 }
346 push(); 422 push();
347 423
424 SkMatrix transform;
425 transform.setTranslate(translation.fX, translation.fY);
426
427 #ifdef SK_PDF_USE_PATHOPS
428 SkPath clipPath = get_clip_stack_path(transform, clipStack, clipRegion);
429 emit_clip(&clipPath, NULL, fContentStream);
430 #else
348 // gsState->initialEntry()->fClipStack/Region specifies the clip that has 431 // gsState->initialEntry()->fClipStack/Region specifies the clip that has
349 // already been applied. (If this is a top level device, then it specifies 432 // 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 433 // 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 434 // 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 435 // 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 436 // 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: 437 // expands the clip and then uses any xfer mode that uses dst:
355 // http://code.google.com/p/skia/issues/detail?id=228 ) 438 // http://code.google.com/p/skia/issues/detail?id=228 )
356 SkClipStack::Iter iter; 439 SkClipStack::Iter iter;
357 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 440 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
358 441
359 // If the clip stack does anything other than intersect or if it uses 442 // 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. 443 // an inverse fill type, we have to fall back to the clip region.
361 bool needRegion = false; 444 bool needRegion = false;
362 const SkClipStack::Element* clipEntry; 445 const SkClipStack::Element* clipEntry;
363 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 446 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
364 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers eFilled()) { 447 if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInvers eFilled()) {
365 needRegion = true; 448 needRegion = true;
366 break; 449 break;
367 } 450 }
368 } 451 }
369 452
370 if (needRegion) { 453 if (needRegion) {
371 SkPath clipPath; 454 SkPath clipPath;
372 SkAssertResult(clipRegion.getBoundaryPath(&clipPath)); 455 SkAssertResult(clipRegion.getBoundaryPath(&clipPath));
373 emit_clip(&clipPath, NULL, fContentStream); 456 emit_clip(&clipPath, NULL, fContentStream);
374 } else { 457 } else {
375 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter); 458 skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
376 SkMatrix transform;
377 transform.setTranslate(translation.fX, translation.fY);
378 const SkClipStack::Element* clipEntry; 459 const SkClipStack::Element* clipEntry;
379 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) { 460 for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
380 SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op); 461 SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
381 switch (clipEntry->getType()) { 462 switch (clipEntry->getType()) {
382 case SkClipStack::Element::kRect_Type: { 463 case SkClipStack::Element::kRect_Type: {
383 SkRect translatedClip; 464 SkRect translatedClip;
384 transform.mapRect(&translatedClip, clipEntry->getRect()); 465 transform.mapRect(&translatedClip, clipEntry->getRect());
385 emit_clip(NULL, &translatedClip, fContentStream); 466 emit_clip(NULL, &translatedClip, fContentStream);
386 break; 467 break;
387 } 468 }
388 case SkClipStack::Element::kPath_Type: { 469 case SkClipStack::Element::kPath_Type: {
389 SkPath translatedPath; 470 SkPath translatedPath;
390 clipEntry->getPath().transform(transform, &translatedPath); 471 clipEntry->getPath().transform(transform, &translatedPath);
391 emit_clip(&translatedPath, NULL, fContentStream); 472 emit_clip(&translatedPath, NULL, fContentStream);
392 break; 473 break;
393 } 474 }
394 default: 475 default:
395 SkASSERT(false); 476 SkASSERT(false);
396 } 477 }
397 } 478 }
398 } 479 }
480 #endif
399 currentEntry()->fClipStack = clipStack; 481 currentEntry()->fClipStack = clipStack;
400 currentEntry()->fClipRegion = clipRegion; 482 currentEntry()->fClipRegion = clipRegion;
401 } 483 }
402 484
403 void GraphicStackState::updateMatrix(const SkMatrix& matrix) { 485 void GraphicStackState::updateMatrix(const SkMatrix& matrix) {
404 if (matrix == currentEntry()->fMatrix) { 486 if (matrix == currentEntry()->fMatrix) {
405 return; 487 return;
406 } 488 }
407 489
408 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) { 490 if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
(...skipping 825 matching lines...) Expand 10 before | Expand all | Expand 10 after
1234 1316
1235 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data); 1317 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), &data);
1236 1318
1237 // potentially we could cache this SkData, and only rebuild it if we 1319 // potentially we could cache this SkData, and only rebuild it if we
1238 // see that our state has changed. 1320 // see that our state has changed.
1239 return data.copyToData(); 1321 return data.copyToData();
1240 } 1322 }
1241 1323
1242 /* Calculate an inverted path's equivalent non-inverted path, given the 1324 /* Calculate an inverted path's equivalent non-inverted path, given the
1243 * canvas bounds. 1325 * canvas bounds.
1326 * outPath may alias with invPath (since this is supported by PathOps).
1244 */ 1327 */
1245 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath, 1328 static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
1246 SkPath* outPath) { 1329 SkPath* outPath) {
1247 SkASSERT(invPath.isInverseFillType()); 1330 SkASSERT(invPath.isInverseFillType());
1248 1331
1249 SkPath clipPath; 1332 SkPath clipPath;
1250 clipPath.addRect(bounds); 1333 clipPath.addRect(bounds);
1251 1334
1252 return Op(clipPath, invPath, kIntersect_PathOp, outPath); 1335 return Op(clipPath, invPath, kIntersect_PathOp, outPath);
1253 } 1336 }
(...skipping 578 matching lines...) Expand 10 before | Expand all | Expand 10 after
1832 } 1915 }
1833 1916
1834 bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y, 1917 bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
1835 SkCanvas::Config8888) { 1918 SkCanvas::Config8888) {
1836 return false; 1919 return false;
1837 } 1920 }
1838 1921
1839 bool SkPDFDevice::allowImageFilter(SkImageFilter*) { 1922 bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
1840 return false; 1923 return false;
1841 } 1924 }
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