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 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 Loading... | |
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 Loading... | |
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 } |
OLD | NEW |