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 |