| 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 "SkAnnotationKeys.h" | 10 #include "SkAnnotationKeys.h" |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 174 // the passed parameters. | 174 // the passed parameters. |
| 175 content->writeText("1 0 "); | 175 content->writeText("1 0 "); |
| 176 SkPDFUtils::AppendScalar(0 - textSkewX, content); | 176 SkPDFUtils::AppendScalar(0 - textSkewX, content); |
| 177 content->writeText(" -1 "); | 177 content->writeText(" -1 "); |
| 178 SkPDFUtils::AppendScalar(x, content); | 178 SkPDFUtils::AppendScalar(x, content); |
| 179 content->writeText(" "); | 179 content->writeText(" "); |
| 180 SkPDFUtils::AppendScalar(y, content); | 180 SkPDFUtils::AppendScalar(y, content); |
| 181 content->writeText(" Tm\n"); | 181 content->writeText(" Tm\n"); |
| 182 } | 182 } |
| 183 | 183 |
| 184 // It is important to not confuse GraphicStateEntry with SkPDFGraphicState, the | 184 SkPDFDevice::GraphicStateEntry::GraphicStateEntry() |
| 185 // later being our representation of an object in the PDF file. | 185 : fColor(SK_ColorBLACK) |
| 186 struct GraphicStateEntry { | 186 , fTextScaleX(SK_Scalar1) |
| 187 GraphicStateEntry(); | 187 , fTextFill(SkPaint::kFill_Style) |
| 188 | 188 , fShaderIndex(-1) |
| 189 // Compare the fields we care about when setting up a new content entry. | 189 , fGraphicStateIndex(-1) |
| 190 bool compareInitialState(const GraphicStateEntry& b); | 190 , fFont(nullptr) |
| 191 | 191 , fTextSize(SK_ScalarNaN) { |
| 192 SkMatrix fMatrix; | |
| 193 // We can't do set operations on Paths, though PDF natively supports | |
| 194 // intersect. If the clip stack does anything other than intersect, | |
| 195 // we have to fall back to the region. Treat fClipStack as authoritative. | |
| 196 // See http://code.google.com/p/skia/issues/detail?id=221 | |
| 197 SkClipStack fClipStack; | |
| 198 SkRegion fClipRegion; | |
| 199 | |
| 200 // When emitting the content entry, we will ensure the graphic state | |
| 201 // is set to these values first. | |
| 202 SkColor fColor; | |
| 203 SkScalar fTextScaleX; // Zero means we don't care what the value is. | |
| 204 SkPaint::Style fTextFill; // Only if TextScaleX is non-zero. | |
| 205 int fShaderIndex; | |
| 206 int fGraphicStateIndex; | |
| 207 | |
| 208 // We may change the font (i.e. for Type1 support) within a | |
| 209 // ContentEntry. This is the one currently in effect, or nullptr if none. | |
| 210 SkPDFFont* fFont; | |
| 211 // In PDF, text size has no default value. It is only valid if fFont is | |
| 212 // not nullptr. | |
| 213 SkScalar fTextSize; | |
| 214 }; | |
| 215 | |
| 216 GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK), | |
| 217 fTextScaleX(SK_Scalar1), | |
| 218 fTextFill(SkPaint::kFill_Style), | |
| 219 fShaderIndex(-1), | |
| 220 fGraphicStateIndex(-1), | |
| 221 fFont(nullptr), | |
| 222 fTextSize(SK_ScalarNaN) { | |
| 223 fMatrix.reset(); | 192 fMatrix.reset(); |
| 224 } | 193 } |
| 225 | 194 |
| 226 bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& cur) { | 195 bool SkPDFDevice::GraphicStateEntry::compareInitialState( |
| 196 const GraphicStateEntry& cur) { |
| 227 return fColor == cur.fColor && | 197 return fColor == cur.fColor && |
| 228 fShaderIndex == cur.fShaderIndex && | 198 fShaderIndex == cur.fShaderIndex && |
| 229 fGraphicStateIndex == cur.fGraphicStateIndex && | 199 fGraphicStateIndex == cur.fGraphicStateIndex && |
| 230 fMatrix == cur.fMatrix && | 200 fMatrix == cur.fMatrix && |
| 231 fClipStack == cur.fClipStack && | 201 fClipStack == cur.fClipStack && |
| 232 (fTextScaleX == 0 || | 202 (fTextScaleX == 0 || |
| 233 (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill)); | 203 (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill)); |
| 234 } | 204 } |
| 235 | 205 |
| 236 class GraphicStackState { | 206 class GraphicStackState { |
| 237 public: | 207 public: |
| 238 GraphicStackState(const SkClipStack& existingClipStack, | 208 GraphicStackState(const SkClipStack& existingClipStack, |
| 239 const SkRegion& existingClipRegion, | 209 const SkRegion& existingClipRegion, |
| 240 SkWStream* contentStream) | 210 SkWStream* contentStream) |
| 241 : fStackDepth(0), | 211 : fStackDepth(0), |
| 242 fContentStream(contentStream) { | 212 fContentStream(contentStream) { |
| 243 fEntries[0].fClipStack = existingClipStack; | 213 fEntries[0].fClipStack = existingClipStack; |
| 244 fEntries[0].fClipRegion = existingClipRegion; | 214 fEntries[0].fClipRegion = existingClipRegion; |
| 245 } | 215 } |
| 246 | 216 |
| 247 void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion, | 217 void updateClip(const SkClipStack& clipStack, const SkRegion& clipRegion, |
| 248 const SkPoint& translation); | 218 const SkPoint& translation); |
| 249 void updateMatrix(const SkMatrix& matrix); | 219 void updateMatrix(const SkMatrix& matrix); |
| 250 void updateDrawingState(const GraphicStateEntry& state); | 220 void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state); |
| 251 | 221 |
| 252 void drainStack(); | 222 void drainStack(); |
| 253 | 223 |
| 254 private: | 224 private: |
| 255 void push(); | 225 void push(); |
| 256 void pop(); | 226 void pop(); |
| 257 GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; } | 227 SkPDFDevice::GraphicStateEntry* currentEntry() { return &fEntries[fStackDept
h]; } |
| 258 | 228 |
| 259 // Conservative limit on save depth, see impl. notes in PDF 1.4 spec. | 229 // Conservative limit on save depth, see impl. notes in PDF 1.4 spec. |
| 260 static const int kMaxStackDepth = 12; | 230 static const int kMaxStackDepth = 12; |
| 261 GraphicStateEntry fEntries[kMaxStackDepth + 1]; | 231 SkPDFDevice::GraphicStateEntry fEntries[kMaxStackDepth + 1]; |
| 262 int fStackDepth; | 232 int fStackDepth; |
| 263 SkWStream* fContentStream; | 233 SkWStream* fContentStream; |
| 264 }; | 234 }; |
| 265 | 235 |
| 266 void GraphicStackState::drainStack() { | 236 void GraphicStackState::drainStack() { |
| 267 while (fStackDepth) { | 237 while (fStackDepth) { |
| 268 pop(); | 238 pop(); |
| 269 } | 239 } |
| 270 } | 240 } |
| 271 | 241 |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 516 } | 486 } |
| 517 if (matrix.getType() == SkMatrix::kIdentity_Mask) { | 487 if (matrix.getType() == SkMatrix::kIdentity_Mask) { |
| 518 return; | 488 return; |
| 519 } | 489 } |
| 520 | 490 |
| 521 push(); | 491 push(); |
| 522 SkPDFUtils::AppendTransform(matrix, fContentStream); | 492 SkPDFUtils::AppendTransform(matrix, fContentStream); |
| 523 currentEntry()->fMatrix = matrix; | 493 currentEntry()->fMatrix = matrix; |
| 524 } | 494 } |
| 525 | 495 |
| 526 void GraphicStackState::updateDrawingState(const GraphicStateEntry& state) { | 496 void GraphicStackState::updateDrawingState(const SkPDFDevice::GraphicStateEntry&
state) { |
| 527 // PDF treats a shader as a color, so we only set one or the other. | 497 // PDF treats a shader as a color, so we only set one or the other. |
| 528 if (state.fShaderIndex >= 0) { | 498 if (state.fShaderIndex >= 0) { |
| 529 if (state.fShaderIndex != currentEntry()->fShaderIndex) { | 499 if (state.fShaderIndex != currentEntry()->fShaderIndex) { |
| 530 SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream); | 500 SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream); |
| 531 currentEntry()->fShaderIndex = state.fShaderIndex; | 501 currentEntry()->fShaderIndex = state.fShaderIndex; |
| 532 } | 502 } |
| 533 } else { | 503 } else { |
| 534 if (state.fColor != currentEntry()->fColor || | 504 if (state.fColor != currentEntry()->fColor || |
| 535 currentEntry()->fShaderIndex >= 0) { | 505 currentEntry()->fShaderIndex >= 0) { |
| 536 emit_pdf_color(state.fColor, fContentStream); | 506 emit_pdf_color(state.fColor, fContentStream); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 582 (layerPaint && not_supported_for_layers(*layerPaint))) { | 552 (layerPaint && not_supported_for_layers(*layerPaint))) { |
| 583 return nullptr; | 553 return nullptr; |
| 584 } | 554 } |
| 585 SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height()); | 555 SkISize size = SkISize::Make(cinfo.fInfo.width(), cinfo.fInfo.height()); |
| 586 return SkPDFDevice::Create(size, fRasterDpi, fDocument); | 556 return SkPDFDevice::Create(size, fRasterDpi, fDocument); |
| 587 } | 557 } |
| 588 | 558 |
| 589 SkPDFCanon* SkPDFDevice::getCanon() const { return fDocument->canon(); } | 559 SkPDFCanon* SkPDFDevice::getCanon() const { return fDocument->canon(); } |
| 590 | 560 |
| 591 | 561 |
| 592 struct ContentEntry { | |
| 593 GraphicStateEntry fState; | |
| 594 SkDynamicMemoryWStream fContent; | |
| 595 SkAutoTDelete<ContentEntry> fNext; | |
| 596 | |
| 597 // If the stack is too deep we could get Stack Overflow. | |
| 598 // So we manually destruct the object. | |
| 599 ~ContentEntry() { | |
| 600 ContentEntry* val = fNext.release(); | |
| 601 while (val != nullptr) { | |
| 602 ContentEntry* valNext = val->fNext.release(); | |
| 603 // When the destructor is called, fNext is nullptr and exits. | |
| 604 delete val; | |
| 605 val = valNext; | |
| 606 } | |
| 607 } | |
| 608 }; | |
| 609 | 562 |
| 610 // A helper class to automatically finish a ContentEntry at the end of a | 563 // A helper class to automatically finish a ContentEntry at the end of a |
| 611 // drawing method and maintain the state needed between set up and finish. | 564 // drawing method and maintain the state needed between set up and finish. |
| 612 class ScopedContentEntry { | 565 class ScopedContentEntry { |
| 613 public: | 566 public: |
| 614 ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw, | 567 ScopedContentEntry(SkPDFDevice* device, const SkDraw& draw, |
| 615 const SkPaint& paint, bool hasText = false) | 568 const SkPaint& paint, bool hasText = false) |
| 616 : fDevice(device), | 569 : fDevice(device), |
| 617 fContentEntry(nullptr), | 570 fContentEntry(nullptr), |
| 618 fXfermode(SkXfermode::kSrcOver_Mode), | 571 fXfermode(SkXfermode::kSrcOver_Mode), |
| (...skipping 14 matching lines...) Expand all Loading... |
| 633 if (fContentEntry) { | 586 if (fContentEntry) { |
| 634 SkPath* shape = &fShape; | 587 SkPath* shape = &fShape; |
| 635 if (shape->isEmpty()) { | 588 if (shape->isEmpty()) { |
| 636 shape = nullptr; | 589 shape = nullptr; |
| 637 } | 590 } |
| 638 fDevice->finishContentEntry(fXfermode, fDstFormXObject, shape); | 591 fDevice->finishContentEntry(fXfermode, fDstFormXObject, shape); |
| 639 } | 592 } |
| 640 SkSafeUnref(fDstFormXObject); | 593 SkSafeUnref(fDstFormXObject); |
| 641 } | 594 } |
| 642 | 595 |
| 643 ContentEntry* entry() { return fContentEntry; } | 596 SkPDFDevice::ContentEntry* entry() { return fContentEntry; } |
| 644 | 597 |
| 645 /* Returns true when we explicitly need the shape of the drawing. */ | 598 /* Returns true when we explicitly need the shape of the drawing. */ |
| 646 bool needShape() { | 599 bool needShape() { |
| 647 switch (fXfermode) { | 600 switch (fXfermode) { |
| 648 case SkXfermode::kClear_Mode: | 601 case SkXfermode::kClear_Mode: |
| 649 case SkXfermode::kSrc_Mode: | 602 case SkXfermode::kSrc_Mode: |
| 650 case SkXfermode::kSrcIn_Mode: | 603 case SkXfermode::kSrcIn_Mode: |
| 651 case SkXfermode::kSrcOut_Mode: | 604 case SkXfermode::kSrcOut_Mode: |
| 652 case SkXfermode::kDstIn_Mode: | 605 case SkXfermode::kDstIn_Mode: |
| 653 case SkXfermode::kDstOut_Mode: | 606 case SkXfermode::kDstOut_Mode: |
| (...skipping 17 matching lines...) Expand all Loading... |
| 671 /* If the shape is different than the alpha component of the content, then | 624 /* If the shape is different than the alpha component of the content, then |
| 672 * setShape should be called with the shape. In particular, images and | 625 * setShape should be called with the shape. In particular, images and |
| 673 * devices have rectangular shape. | 626 * devices have rectangular shape. |
| 674 */ | 627 */ |
| 675 void setShape(const SkPath& shape) { | 628 void setShape(const SkPath& shape) { |
| 676 fShape = shape; | 629 fShape = shape; |
| 677 } | 630 } |
| 678 | 631 |
| 679 private: | 632 private: |
| 680 SkPDFDevice* fDevice; | 633 SkPDFDevice* fDevice; |
| 681 ContentEntry* fContentEntry; | 634 SkPDFDevice::ContentEntry* fContentEntry; |
| 682 SkXfermode::Mode fXfermode; | 635 SkXfermode::Mode fXfermode; |
| 683 SkPDFFormXObject* fDstFormXObject; | 636 SkPDFFormXObject* fDstFormXObject; |
| 684 SkPath fShape; | 637 SkPath fShape; |
| 685 | 638 |
| 686 void init(const SkClipStack* clipStack, const SkRegion& clipRegion, | 639 void init(const SkClipStack* clipStack, const SkRegion& clipRegion, |
| 687 const SkMatrix& matrix, const SkPaint& paint, bool hasText) { | 640 const SkMatrix& matrix, const SkPaint& paint, bool hasText) { |
| 688 // Shape has to be flatten before we get here. | 641 // Shape has to be flatten before we get here. |
| 689 if (matrix.hasPerspective()) { | 642 if (matrix.hasPerspective()) { |
| 690 NOT_IMPLEMENTED(!matrix.hasPerspective(), false); | 643 NOT_IMPLEMENTED(!matrix.hasPerspective(), false); |
| 691 return; | 644 return; |
| 692 } | 645 } |
| 693 if (paint.getXfermode()) { | 646 if (paint.getXfermode()) { |
| 694 paint.getXfermode()->asMode(&fXfermode); | 647 paint.getXfermode()->asMode(&fXfermode); |
| 695 } | 648 } |
| 696 fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion, | 649 fContentEntry = fDevice->setUpContentEntry(clipStack, clipRegion, |
| 697 matrix, paint, hasText, | 650 matrix, paint, hasText, |
| 698 &fDstFormXObject); | 651 &fDstFormXObject); |
| 699 } | 652 } |
| 700 }; | 653 }; |
| 701 | 654 |
| 702 //////////////////////////////////////////////////////////////////////////////// | 655 //////////////////////////////////////////////////////////////////////////////// |
| 703 | 656 |
| 704 SkPDFDevice::SkPDFDevice(SkISize pageSize, SkScalar rasterDpi, SkPDFDocument* do
c, bool flip) | 657 SkPDFDevice::SkPDFDevice(SkISize pageSize, SkScalar rasterDpi, SkPDFDocument* do
c, bool flip) |
| 705 : INHERITED(SkSurfaceProps(0, kUnknown_SkPixelGeometry)) | 658 : INHERITED(SkSurfaceProps(0, kUnknown_SkPixelGeometry)) |
| 706 , fPageSize(pageSize) | 659 , fPageSize(pageSize) |
| 707 , fContentSize(pageSize) | 660 , fContentSize(pageSize) |
| 708 , fExistingClipRegion(SkIRect::MakeSize(pageSize)) | 661 , fExistingClipRegion(SkIRect::MakeSize(pageSize)) |
| 709 , fLastContentEntry(nullptr) | |
| 710 , fClipStack(nullptr) | 662 , fClipStack(nullptr) |
| 711 , fFontGlyphUsage(new SkPDFGlyphSetMap) | 663 , fFontGlyphUsage(new SkPDFGlyphSetMap) |
| 712 , fRasterDpi(rasterDpi) | 664 , fRasterDpi(rasterDpi) |
| 713 , fDocument(doc) { | 665 , fDocument(doc) { |
| 714 SkASSERT(pageSize.width() > 0); | 666 SkASSERT(pageSize.width() > 0); |
| 715 SkASSERT(pageSize.height() > 0); | 667 SkASSERT(pageSize.height() > 0); |
| 716 fLegacyBitmap.setInfo( | 668 fLegacyBitmap.setInfo( |
| 717 SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height())); | 669 SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height())); |
| 718 if (flip) { | 670 if (flip) { |
| 719 // Skia generally uses the top left as the origin but PDF | 671 // Skia generally uses the top left as the origin but PDF |
| 720 // natively has the origin at the bottom left. This matrix | 672 // natively has the origin at the bottom left. This matrix |
| 721 // corrects for that. But that only needs to be done once, we | 673 // corrects for that. But that only needs to be done once, we |
| 722 // don't do it when layering. | 674 // don't do it when layering. |
| 723 fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight)); | 675 fInitialTransform.setTranslate(0, SkIntToScalar(pageSize.fHeight)); |
| 724 fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1); | 676 fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1); |
| 725 } else { | 677 } else { |
| 726 fInitialTransform.setIdentity(); | 678 fInitialTransform.setIdentity(); |
| 727 } | 679 } |
| 728 } | 680 } |
| 729 | 681 |
| 730 SkPDFDevice::~SkPDFDevice() { | 682 SkPDFDevice::~SkPDFDevice() { |
| 731 this->cleanUp(true); | 683 this->cleanUp(true); |
| 732 } | 684 } |
| 733 | 685 |
| 734 void SkPDFDevice::init() { | 686 void SkPDFDevice::init() { |
| 735 fContentEntries.reset(); | 687 fContentEntries.reset(); |
| 736 fLastContentEntry = nullptr; | |
| 737 if (fFontGlyphUsage.get() == nullptr) { | 688 if (fFontGlyphUsage.get() == nullptr) { |
| 738 fFontGlyphUsage.reset(new SkPDFGlyphSetMap); | 689 fFontGlyphUsage.reset(new SkPDFGlyphSetMap); |
| 739 } | 690 } |
| 740 } | 691 } |
| 741 | 692 |
| 742 void SkPDFDevice::cleanUp(bool clearFontUsage) { | 693 void SkPDFDevice::cleanUp(bool clearFontUsage) { |
| 743 fGraphicStateResources.unrefAll(); | 694 fGraphicStateResources.unrefAll(); |
| 744 fXObjectResources.unrefAll(); | 695 fXObjectResources.unrefAll(); |
| 745 fFontResources.unrefAll(); | 696 fFontResources.unrefAll(); |
| 746 fShaderResources.unrefAll(); | 697 fShaderResources.unrefAll(); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 764 void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) { | 715 void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) { |
| 765 SkPaint newPaint = paint; | 716 SkPaint newPaint = paint; |
| 766 replace_srcmode_on_opaque_paint(&newPaint); | 717 replace_srcmode_on_opaque_paint(&newPaint); |
| 767 | 718 |
| 768 newPaint.setStyle(SkPaint::kFill_Style); | 719 newPaint.setStyle(SkPaint::kFill_Style); |
| 769 ScopedContentEntry content(this, d, newPaint); | 720 ScopedContentEntry content(this, d, newPaint); |
| 770 internalDrawPaint(newPaint, content.entry()); | 721 internalDrawPaint(newPaint, content.entry()); |
| 771 } | 722 } |
| 772 | 723 |
| 773 void SkPDFDevice::internalDrawPaint(const SkPaint& paint, | 724 void SkPDFDevice::internalDrawPaint(const SkPaint& paint, |
| 774 ContentEntry* contentEntry) { | 725 SkPDFDevice::ContentEntry* contentEntry) { |
| 775 if (!contentEntry) { | 726 if (!contentEntry) { |
| 776 return; | 727 return; |
| 777 } | 728 } |
| 778 SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()), | 729 SkRect bbox = SkRect::MakeWH(SkIntToScalar(this->width()), |
| 779 SkIntToScalar(this->height())); | 730 SkIntToScalar(this->height())); |
| 780 SkMatrix inverse; | 731 SkMatrix inverse; |
| 781 if (!contentEntry->fState.fMatrix.invert(&inverse)) { | 732 if (!contentEntry->fState.fMatrix.invert(&inverse)) { |
| 782 return; | 733 return; |
| 783 } | 734 } |
| 784 inverse.mapRect(&bbox); | 735 inverse.mapRect(&bbox); |
| (...skipping 649 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1434 | 1385 |
| 1435 std::unique_ptr<SkStreamAsset> SkPDFDevice::content() const { | 1386 std::unique_ptr<SkStreamAsset> SkPDFDevice::content() const { |
| 1436 SkDynamicMemoryWStream buffer; | 1387 SkDynamicMemoryWStream buffer; |
| 1437 this->writeContent(&buffer); | 1388 this->writeContent(&buffer); |
| 1438 return std::unique_ptr<SkStreamAsset>( | 1389 return std::unique_ptr<SkStreamAsset>( |
| 1439 buffer.bytesWritten() > 0 | 1390 buffer.bytesWritten() > 0 |
| 1440 ? buffer.detachAsStream() | 1391 ? buffer.detachAsStream() |
| 1441 : new SkMemoryStream); | 1392 : new SkMemoryStream); |
| 1442 } | 1393 } |
| 1443 | 1394 |
| 1444 void SkPDFDevice::copyContentEntriesToData(ContentEntry* entry, | |
| 1445 SkWStream* data) const { | |
| 1446 // TODO(ctguil): For margins, I'm not sure fExistingClipStack/Region is the | |
| 1447 // right thing to pass here. | |
| 1448 GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, data); | |
| 1449 while (entry != nullptr) { | |
| 1450 SkPoint translation; | |
| 1451 translation.iset(this->getOrigin()); | |
| 1452 translation.negate(); | |
| 1453 gsState.updateClip(entry->fState.fClipStack, entry->fState.fClipRegion, | |
| 1454 translation); | |
| 1455 gsState.updateMatrix(entry->fState.fMatrix); | |
| 1456 gsState.updateDrawingState(entry->fState); | |
| 1457 | |
| 1458 entry->fContent.writeToStream(data); | |
| 1459 entry = entry->fNext.get(); | |
| 1460 } | |
| 1461 gsState.drainStack(); | |
| 1462 } | |
| 1463 | |
| 1464 void SkPDFDevice::writeContent(SkWStream* out) const { | 1395 void SkPDFDevice::writeContent(SkWStream* out) const { |
| 1465 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { | 1396 if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) { |
| 1466 SkPDFUtils::AppendTransform(fInitialTransform, out); | 1397 SkPDFUtils::AppendTransform(fInitialTransform, out); |
| 1467 } | 1398 } |
| 1468 | 1399 |
| 1469 // If the content area is the entire page, then we don't need to clip | 1400 // If the content area is the entire page, then we don't need to clip |
| 1470 // the content area (PDF area clips to the page size). Otherwise, | 1401 // the content area (PDF area clips to the page size). Otherwise, |
| 1471 // we have to clip to the content area; we've already applied the | 1402 // we have to clip to the content area; we've already applied the |
| 1472 // initial transform, so just clip to the device size. | 1403 // initial transform, so just clip to the device size. |
| 1473 if (fPageSize != fContentSize) { | 1404 if (fPageSize != fContentSize) { |
| 1474 SkRect r = SkRect::MakeWH(SkIntToScalar(this->width()), | 1405 SkRect r = SkRect::MakeWH(SkIntToScalar(this->width()), |
| 1475 SkIntToScalar(this->height())); | 1406 SkIntToScalar(this->height())); |
| 1476 emit_clip(nullptr, &r, out); | 1407 emit_clip(nullptr, &r, out); |
| 1477 } | 1408 } |
| 1478 | 1409 |
| 1479 SkPDFDevice::copyContentEntriesToData(fContentEntries.get(), out); | 1410 GraphicStackState gsState(fExistingClipStack, fExistingClipRegion, out); |
| 1411 for (const auto& entry : fContentEntries) { |
| 1412 SkPoint translation; |
| 1413 translation.iset(this->getOrigin()); |
| 1414 translation.negate(); |
| 1415 gsState.updateClip(entry.fState.fClipStack, entry.fState.fClipRegion, |
| 1416 translation); |
| 1417 gsState.updateMatrix(entry.fState.fMatrix); |
| 1418 gsState.updateDrawingState(entry.fState); |
| 1419 |
| 1420 entry.fContent.writeToStream(out); |
| 1421 } |
| 1422 gsState.drainStack(); |
| 1480 } | 1423 } |
| 1481 | 1424 |
| 1482 /* Draws an inverse filled path by using Path Ops to compute the positive | 1425 /* Draws an inverse filled path by using Path Ops to compute the positive |
| 1483 * inverse using the current clip as the inverse bounds. | 1426 * inverse using the current clip as the inverse bounds. |
| 1484 * Return true if this was an inverse path and was properly handled, | 1427 * Return true if this was an inverse path and was properly handled, |
| 1485 * otherwise returns false and the normal drawing routine should continue, | 1428 * otherwise returns false and the normal drawing routine should continue, |
| 1486 * either as a (incorrect) fallback or because the path was not inverse | 1429 * either as a (incorrect) fallback or because the path was not inverse |
| 1487 * in the first place. | 1430 * in the first place. |
| 1488 */ | 1431 */ |
| 1489 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath, | 1432 bool SkPDFDevice::handleInversePath(const SkDraw& d, const SkPath& origPath, |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1650 SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent); | 1593 SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent); |
| 1651 | 1594 |
| 1652 // Call makeNoSmaskGraphicState() instead of | 1595 // Call makeNoSmaskGraphicState() instead of |
| 1653 // SkPDFGraphicState::MakeNoSmaskGraphicState so that the canon | 1596 // SkPDFGraphicState::MakeNoSmaskGraphicState so that the canon |
| 1654 // can deduplicate. | 1597 // can deduplicate. |
| 1655 sMaskGS = fDocument->canon()->makeNoSmaskGraphicState(); | 1598 sMaskGS = fDocument->canon()->makeNoSmaskGraphicState(); |
| 1656 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), | 1599 SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), |
| 1657 &content.entry()->fContent); | 1600 &content.entry()->fContent); |
| 1658 } | 1601 } |
| 1659 | 1602 |
| 1660 ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack, | 1603 SkPDFDevice::ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* cli
pStack, |
| 1661 const SkRegion& clipRegion, | 1604 const SkRegion& clipRegion, |
| 1662 const SkMatrix& matrix, | 1605 const SkMatrix& matrix, |
| 1663 const SkPaint& paint, | 1606 const SkPaint& paint, |
| 1664 bool hasText, | 1607 bool hasText, |
| 1665 SkPDFFormXObject** dst) { | 1608 SkPDFFormXObject** dst) { |
| 1666 *dst = nullptr; | 1609 *dst = nullptr; |
| 1667 if (clipRegion.isEmpty()) { | 1610 if (clipRegion.isEmpty()) { |
| 1668 return nullptr; | 1611 return nullptr; |
| 1669 } | 1612 } |
| 1670 | 1613 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1713 } | 1656 } |
| 1714 } | 1657 } |
| 1715 // TODO(vandebo): Figure out how/if we can handle the following modes: | 1658 // TODO(vandebo): Figure out how/if we can handle the following modes: |
| 1716 // Xor, Plus. | 1659 // Xor, Plus. |
| 1717 | 1660 |
| 1718 // Dst xfer mode doesn't draw source at all. | 1661 // Dst xfer mode doesn't draw source at all. |
| 1719 if (xfermode == SkXfermode::kDst_Mode) { | 1662 if (xfermode == SkXfermode::kDst_Mode) { |
| 1720 return nullptr; | 1663 return nullptr; |
| 1721 } | 1664 } |
| 1722 | 1665 |
| 1723 ContentEntry* entry; | 1666 SkPDFDevice::ContentEntry* entry; |
| 1724 SkAutoTDelete<ContentEntry> newEntry; | 1667 if (fContentEntries.back() && fContentEntries.back()->fContent.getOffset() =
= 0) { |
| 1725 | 1668 entry = fContentEntries.back(); |
| 1726 if (fLastContentEntry && fLastContentEntry->fContent.getOffset() == 0) { | 1669 } else if (xfermode != SkXfermode::kDstOver_Mode) { |
| 1727 entry = fLastContentEntry; | 1670 entry = fContentEntries.emplace_back(); |
| 1728 } else { | 1671 } else { |
| 1729 newEntry.reset(new ContentEntry); | 1672 entry = fContentEntries.emplace_front(); |
| 1730 entry = newEntry.get(); | |
| 1731 } | 1673 } |
| 1732 | |
| 1733 populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint, | 1674 populateGraphicStateEntryFromPaint(matrix, *clipStack, clipRegion, paint, |
| 1734 hasText, &entry->fState); | 1675 hasText, &entry->fState); |
| 1735 if (fLastContentEntry && xfermode != SkXfermode::kDstOver_Mode && | |
| 1736 entry->fState.compareInitialState(fLastContentEntry->fState)) { | |
| 1737 return fLastContentEntry; | |
| 1738 } | |
| 1739 | |
| 1740 if (!fLastContentEntry) { | |
| 1741 fContentEntries.reset(entry); | |
| 1742 fLastContentEntry = entry; | |
| 1743 } else if (xfermode == SkXfermode::kDstOver_Mode) { | |
| 1744 entry->fNext.reset(fContentEntries.release()); | |
| 1745 fContentEntries.reset(entry); | |
| 1746 } else { | |
| 1747 fLastContentEntry->fNext.reset(entry); | |
| 1748 fLastContentEntry = entry; | |
| 1749 } | |
| 1750 newEntry.release(); | |
| 1751 return entry; | 1676 return entry; |
| 1752 } | 1677 } |
| 1753 | 1678 |
| 1754 void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode, | 1679 void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode, |
| 1755 SkPDFFormXObject* dst, | 1680 SkPDFFormXObject* dst, |
| 1756 SkPath* shape) { | 1681 SkPath* shape) { |
| 1757 if (xfermode != SkXfermode::kClear_Mode && | 1682 if (xfermode != SkXfermode::kClear_Mode && |
| 1758 xfermode != SkXfermode::kSrc_Mode && | 1683 xfermode != SkXfermode::kSrc_Mode && |
| 1759 xfermode != SkXfermode::kDstOver_Mode && | 1684 xfermode != SkXfermode::kDstOver_Mode && |
| 1760 xfermode != SkXfermode::kSrcIn_Mode && | 1685 xfermode != SkXfermode::kSrcIn_Mode && |
| 1761 xfermode != SkXfermode::kDstIn_Mode && | 1686 xfermode != SkXfermode::kDstIn_Mode && |
| 1762 xfermode != SkXfermode::kSrcOut_Mode && | 1687 xfermode != SkXfermode::kSrcOut_Mode && |
| 1763 xfermode != SkXfermode::kDstOut_Mode && | 1688 xfermode != SkXfermode::kDstOut_Mode && |
| 1764 xfermode != SkXfermode::kSrcATop_Mode && | 1689 xfermode != SkXfermode::kSrcATop_Mode && |
| 1765 xfermode != SkXfermode::kDstATop_Mode && | 1690 xfermode != SkXfermode::kDstATop_Mode && |
| 1766 xfermode != SkXfermode::kModulate_Mode) { | 1691 xfermode != SkXfermode::kModulate_Mode) { |
| 1767 SkASSERT(!dst); | 1692 SkASSERT(!dst); |
| 1768 return; | 1693 return; |
| 1769 } | 1694 } |
| 1770 if (xfermode == SkXfermode::kDstOver_Mode) { | 1695 if (xfermode == SkXfermode::kDstOver_Mode) { |
| 1771 SkASSERT(!dst); | 1696 SkASSERT(!dst); |
| 1772 if (fContentEntries->fContent.getOffset() == 0) { | 1697 if (fContentEntries.front()->fContent.getOffset() == 0) { |
| 1773 // For DstOver, an empty content entry was inserted before the rest | 1698 // For DstOver, an empty content entry was inserted before the rest |
| 1774 // of the content entries. If nothing was drawn, it needs to be | 1699 // of the content entries. If nothing was drawn, it needs to be |
| 1775 // removed. | 1700 // removed. |
| 1776 fContentEntries.reset(fContentEntries->fNext.release()); | 1701 fContentEntries.pop_front(); |
| 1777 } | 1702 } |
| 1778 return; | 1703 return; |
| 1779 } | 1704 } |
| 1780 if (!dst) { | 1705 if (!dst) { |
| 1781 SkASSERT(xfermode == SkXfermode::kSrc_Mode || | 1706 SkASSERT(xfermode == SkXfermode::kSrc_Mode || |
| 1782 xfermode == SkXfermode::kSrcOut_Mode); | 1707 xfermode == SkXfermode::kSrcOut_Mode); |
| 1783 return; | 1708 return; |
| 1784 } | 1709 } |
| 1785 | 1710 |
| 1786 SkASSERT(dst); | 1711 SkASSERT(dst); |
| 1787 SkASSERT(!fContentEntries->fNext.get()); | 1712 SkASSERT(fContentEntries.count() == 1); |
| 1788 // Changing the current content into a form-xobject will destroy the clip | 1713 // Changing the current content into a form-xobject will destroy the clip |
| 1789 // objects which is fine since the xobject will already be clipped. However | 1714 // objects which is fine since the xobject will already be clipped. However |
| 1790 // if source has shape, we need to clip it too, so a copy of the clip is | 1715 // if source has shape, we need to clip it too, so a copy of the clip is |
| 1791 // saved. | 1716 // saved. |
| 1792 SkClipStack clipStack = fContentEntries->fState.fClipStack; | 1717 |
| 1793 SkRegion clipRegion = fContentEntries->fState.fClipRegion; | 1718 SkClipStack clipStack = fContentEntries.front()->fState.fClipStack; |
| 1719 SkRegion clipRegion = fContentEntries.front()->fState.fClipRegion; |
| 1794 | 1720 |
| 1795 SkMatrix identity; | 1721 SkMatrix identity; |
| 1796 identity.reset(); | 1722 identity.reset(); |
| 1797 SkPaint stockPaint; | 1723 SkPaint stockPaint; |
| 1798 | 1724 |
| 1799 sk_sp<SkPDFFormXObject> srcFormXObject; | 1725 sk_sp<SkPDFFormXObject> srcFormXObject; |
| 1800 if (isContentEmpty()) { | 1726 if (isContentEmpty()) { |
| 1801 // If nothing was drawn and there's no shape, then the draw was a | 1727 // If nothing was drawn and there's no shape, then the draw was a |
| 1802 // no-op, but dst needs to be restored for that to be true. | 1728 // no-op, but dst needs to be restored for that to be true. |
| 1803 // If there is shape, then an empty source with Src, SrcIn, SrcOut, | 1729 // If there is shape, then an empty source with Src, SrcIn, SrcOut, |
| 1804 // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop | 1730 // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop |
| 1805 // reduces to Dst. | 1731 // reduces to Dst. |
| 1806 if (shape == nullptr || xfermode == SkXfermode::kDstOut_Mode || | 1732 if (shape == nullptr || xfermode == SkXfermode::kDstOut_Mode || |
| 1807 xfermode == SkXfermode::kSrcATop_Mode) { | 1733 xfermode == SkXfermode::kSrcATop_Mode) { |
| 1808 ScopedContentEntry content(this, &fExistingClipStack, | 1734 ScopedContentEntry content(this, &fExistingClipStack, |
| 1809 fExistingClipRegion, identity, | 1735 fExistingClipRegion, identity, |
| 1810 stockPaint); | 1736 stockPaint); |
| 1811 SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst), | 1737 SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst), |
| 1812 &content.entry()->fContent); | 1738 &content.entry()->fContent); |
| 1813 return; | 1739 return; |
| 1814 } else { | 1740 } else { |
| 1815 xfermode = SkXfermode::kClear_Mode; | 1741 xfermode = SkXfermode::kClear_Mode; |
| 1816 } | 1742 } |
| 1817 } else { | 1743 } else { |
| 1818 SkASSERT(!fContentEntries->fNext.get()); | 1744 SkASSERT(fContentEntries.count() == 1); |
| 1819 srcFormXObject.reset(createFormXObjectFromDevice()); | 1745 srcFormXObject.reset(createFormXObjectFromDevice()); |
| 1820 } | 1746 } |
| 1821 | 1747 |
| 1822 // TODO(vandebo) srcFormXObject may contain alpha, but here we want it | 1748 // TODO(vandebo) srcFormXObject may contain alpha, but here we want it |
| 1823 // without alpha. | 1749 // without alpha. |
| 1824 if (xfermode == SkXfermode::kSrcATop_Mode) { | 1750 if (xfermode == SkXfermode::kSrcATop_Mode) { |
| 1825 // TODO(vandebo): In order to properly support SrcATop we have to track | 1751 // TODO(vandebo): In order to properly support SrcATop we have to track |
| 1826 // the shape of what's been drawn at all times. It's the intersection of | 1752 // the shape of what's been drawn at all times. It's the intersection of |
| 1827 // the non-transparent parts of the device and the outlines (shape) of | 1753 // the non-transparent parts of the device and the outlines (shape) of |
| 1828 // all images and devices drawn. | 1754 // all images and devices drawn. |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1898 SkXfermode::kSrcOver_Mode, false); | 1824 SkXfermode::kSrcOver_Mode, false); |
| 1899 mode = SkXfermode::kMultiply_Mode; | 1825 mode = SkXfermode::kMultiply_Mode; |
| 1900 } | 1826 } |
| 1901 drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(), | 1827 drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(), |
| 1902 &fExistingClipStack, fExistingClipRegion, mode, | 1828 &fExistingClipStack, fExistingClipRegion, mode, |
| 1903 xfermode == SkXfermode::kDstOut_Mode); | 1829 xfermode == SkXfermode::kDstOut_Mode); |
| 1904 } | 1830 } |
| 1905 } | 1831 } |
| 1906 | 1832 |
| 1907 bool SkPDFDevice::isContentEmpty() { | 1833 bool SkPDFDevice::isContentEmpty() { |
| 1908 if (!fContentEntries || fContentEntries->fContent.getOffset() == 0) { | 1834 if (!fContentEntries.front() || fContentEntries.front()->fContent.getOffset(
) == 0) { |
| 1909 SkASSERT(!fContentEntries || !fContentEntries->fNext.get()); | 1835 SkASSERT(fContentEntries.count() <= 1); |
| 1910 return true; | 1836 return true; |
| 1911 } | 1837 } |
| 1912 return false; | 1838 return false; |
| 1913 } | 1839 } |
| 1914 | 1840 |
| 1915 void SkPDFDevice::populateGraphicStateEntryFromPaint( | 1841 void SkPDFDevice::populateGraphicStateEntryFromPaint( |
| 1916 const SkMatrix& matrix, | 1842 const SkMatrix& matrix, |
| 1917 const SkClipStack& clipStack, | 1843 const SkClipStack& clipStack, |
| 1918 const SkRegion& clipRegion, | 1844 const SkRegion& clipRegion, |
| 1919 const SkPaint& paint, | 1845 const SkPaint& paint, |
| 1920 bool hasText, | 1846 bool hasText, |
| 1921 GraphicStateEntry* entry) { | 1847 SkPDFDevice::GraphicStateEntry* entry) { |
| 1922 NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false); | 1848 NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false); |
| 1923 NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false); | 1849 NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false); |
| 1924 NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false); | 1850 NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false); |
| 1925 | 1851 |
| 1926 entry->fMatrix = matrix; | 1852 entry->fMatrix = matrix; |
| 1927 entry->fClipStack = clipStack; | 1853 entry->fClipStack = clipStack; |
| 1928 entry->fClipRegion = clipRegion; | 1854 entry->fClipRegion = clipRegion; |
| 1929 entry->fColor = SkColorSetA(paint.getColor(), 0xFF); | 1855 entry->fColor = SkColorSetA(paint.getColor(), 0xFF); |
| 1930 entry->fShaderIndex = -1; | 1856 entry->fShaderIndex = -1; |
| 1931 | 1857 |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2020 int result = fXObjectResources.find(xObject); | 1946 int result = fXObjectResources.find(xObject); |
| 2021 if (result < 0) { | 1947 if (result < 0) { |
| 2022 result = fXObjectResources.count(); | 1948 result = fXObjectResources.count(); |
| 2023 fXObjectResources.push(xObject); | 1949 fXObjectResources.push(xObject); |
| 2024 xObject->ref(); | 1950 xObject->ref(); |
| 2025 } | 1951 } |
| 2026 return result; | 1952 return result; |
| 2027 } | 1953 } |
| 2028 | 1954 |
| 2029 void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID, | 1955 void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID, |
| 2030 ContentEntry* contentEntry) { | 1956 SkPDFDevice::ContentEntry* contentEntry) { |
| 2031 SkTypeface* typeface = paint.getTypeface(); | 1957 SkTypeface* typeface = paint.getTypeface(); |
| 2032 if (contentEntry->fState.fFont == nullptr || | 1958 if (contentEntry->fState.fFont == nullptr || |
| 2033 contentEntry->fState.fTextSize != paint.getTextSize() || | 1959 contentEntry->fState.fTextSize != paint.getTextSize() || |
| 2034 !contentEntry->fState.fFont->hasGlyph(glyphID)) { | 1960 !contentEntry->fState.fFont->hasGlyph(glyphID)) { |
| 2035 int fontIndex = getFontResourceIndex(typeface, glyphID); | 1961 int fontIndex = getFontResourceIndex(typeface, glyphID); |
| 2036 contentEntry->fContent.writeText("/"); | 1962 contentEntry->fContent.writeText("/"); |
| 2037 contentEntry->fContent.writeText(SkPDFResourceDict::getResourceName( | 1963 contentEntry->fContent.writeText(SkPDFResourceDict::getResourceName( |
| 2038 SkPDFResourceDict::kFont_ResourceType, | 1964 SkPDFResourceDict::kFont_ResourceType, |
| 2039 fontIndex).c_str()); | 1965 fontIndex).c_str()); |
| 2040 contentEntry->fContent.writeText(" "); | 1966 contentEntry->fContent.writeText(" "); |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2213 if (!pdfimage) { | 2139 if (!pdfimage) { |
| 2214 return; | 2140 return; |
| 2215 } | 2141 } |
| 2216 fDocument->serialize(pdfimage); // serialize images early. | 2142 fDocument->serialize(pdfimage); // serialize images early. |
| 2217 fDocument->canon()->addPDFBitmap(key, pdfimage); | 2143 fDocument->canon()->addPDFBitmap(key, pdfimage); |
| 2218 } | 2144 } |
| 2219 // TODO(halcanary): addXObjectResource() should take a sk_sp<SkPDFObject> | 2145 // TODO(halcanary): addXObjectResource() should take a sk_sp<SkPDFObject> |
| 2220 SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()), | 2146 SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()), |
| 2221 &content.entry()->fContent); | 2147 &content.entry()->fContent); |
| 2222 } | 2148 } |
| OLD | NEW |