• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 Google LLC.
2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3 
4 #include "src/pdf/SkPDFGraphicStackState.h"
5 
6 #include "include/core/SkStream.h"
7 #include "include/pathops/SkPathOps.h"
8 #include "src/pdf/SkPDFUtils.h"
9 
to_path(const SkRect & r)10 static SkPath to_path(const SkRect& r) {
11     SkPath p;
12     p.addRect(r);
13     return p;
14 }
15 
emit_pdf_color(SkColor4f color,SkWStream * result)16 static void emit_pdf_color(SkColor4f color, SkWStream* result) {
17     SkASSERT(color.fA == 1);  // We handle alpha elsewhere.
18     SkPDFUtils::AppendColorComponentF(color.fR, result);
19     result->writeText(" ");
20     SkPDFUtils::AppendColorComponentF(color.fG, result);
21     result->writeText(" ");
22     SkPDFUtils::AppendColorComponentF(color.fB, result);
23     result->writeText(" ");
24 }
25 
rect_intersect(SkRect u,SkRect v)26 static SkRect rect_intersect(SkRect u, SkRect v) {
27     if (u.isEmpty() || v.isEmpty()) { return {0, 0, 0, 0}; }
28     return u.intersect(v) ? u : SkRect{0, 0, 0, 0};
29 }
30 
31 // Test to see if the clipstack is a simple rect, If so, we can avoid all PathOps code
32 // and speed thing up.
is_rect(const SkClipStack & clipStack,const SkRect & bounds,SkRect * dst)33 static bool is_rect(const SkClipStack& clipStack, const SkRect& bounds, SkRect* dst) {
34     SkRect currentClip = bounds;
35     SkClipStack::Iter iter(clipStack, SkClipStack::Iter::kBottom_IterStart);
36     while (const SkClipStack::Element* element = iter.next()) {
37         SkRect elementRect{0, 0, 0, 0};
38         switch (element->getDeviceSpaceType()) {
39             case SkClipStack::Element::DeviceSpaceType::kEmpty:
40                 break;
41             case SkClipStack::Element::DeviceSpaceType::kRect:
42                 elementRect = element->getDeviceSpaceRect();
43                 break;
44             default:
45                 return false;
46         }
47         switch (element->getOp()) {
48             case kReplace_SkClipOp:
49                 currentClip = rect_intersect(bounds, elementRect);
50                 break;
51             case SkClipOp::kIntersect:
52                 currentClip = rect_intersect(currentClip, elementRect);
53                 break;
54             default:
55                 return false;
56         }
57     }
58     *dst = currentClip;
59     return true;
60 }
61 
is_complex_clip(const SkClipStack & stack)62 static bool is_complex_clip(const SkClipStack& stack) {
63     SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
64     while (const SkClipStack::Element* element = iter.next()) {
65         switch (element->getOp()) {
66             case SkClipOp::kDifference:
67             case SkClipOp::kIntersect:
68                 break;
69             default:
70                 return true;
71         }
72     }
73     return false;
74 }
75 
76 template <typename F>
apply_clip(const SkClipStack & stack,const SkRect & outerBounds,F fn)77 static void apply_clip(const SkClipStack& stack, const SkRect& outerBounds, F fn) {
78     // assumes clipstack is not complex.
79     constexpr SkRect kHuge{-30000, -30000, 30000, 30000};
80     SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
81     SkRect bounds = outerBounds;
82     while (const SkClipStack::Element* element = iter.next()) {
83         SkPath operand;
84         element->asDeviceSpacePath(&operand);
85         SkPathOp op;
86         switch (element->getOp()) {
87             case SkClipOp::kDifference: op = kDifference_SkPathOp; break;
88             case SkClipOp::kIntersect:  op = kIntersect_SkPathOp;  break;
89             default: SkASSERT(false); return;
90         }
91         if (op == kDifference_SkPathOp  ||
92             operand.isInverseFillType() ||
93             !kHuge.contains(operand.getBounds()))
94         {
95             Op(to_path(bounds), operand, op, &operand);
96         }
97         SkASSERT(!operand.isInverseFillType());
98         fn(operand);
99         if (!bounds.intersect(operand.getBounds())) {
100             return; // return early;
101         }
102     }
103 }
104 
append_clip_path(const SkPath & clipPath,SkWStream * wStream)105 static void append_clip_path(const SkPath& clipPath, SkWStream* wStream) {
106     SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, wStream);
107     SkPath::FillType clipFill = clipPath.getFillType();
108     NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
109     NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
110     if (clipFill == SkPath::kEvenOdd_FillType) {
111         wStream->writeText("W* n\n");
112     } else {
113         wStream->writeText("W n\n");
114     }
115 }
116 
append_clip(const SkClipStack & clipStack,const SkIRect & bounds,SkWStream * wStream)117 static void append_clip(const SkClipStack& clipStack,
118                         const SkIRect& bounds,
119                         SkWStream* wStream) {
120     // The bounds are slightly outset to ensure this is correct in the
121     // face of floating-point accuracy and possible SkRegion bitmap
122     // approximations.
123     SkRect outsetBounds = SkRect::Make(bounds.makeOutset(1, 1));
124 
125     SkRect clipStackRect;
126     if (is_rect(clipStack, outsetBounds, &clipStackRect)) {
127         SkPDFUtils::AppendRectangle(clipStackRect, wStream);
128         wStream->writeText("W* n\n");
129         return;
130     }
131 
132     if (is_complex_clip(clipStack)) {
133         SkPath clipPath;
134         (void)clipStack.asPath(&clipPath);
135         if (Op(clipPath, to_path(outsetBounds), kIntersect_SkPathOp, &clipPath)) {
136             append_clip_path(clipPath, wStream);
137         }
138         // If Op() fails (pathological case; e.g. input values are
139         // extremely large or NaN), emit no clip at all.
140     } else {
141         apply_clip(clipStack, outsetBounds, [wStream](const SkPath& path) {
142             append_clip_path(path, wStream);
143         });
144     }
145 }
146 
147 ////////////////////////////////////////////////////////////////////////////////
148 
updateClip(const SkClipStack * clipStack,const SkIRect & bounds)149 void SkPDFGraphicStackState::updateClip(const SkClipStack* clipStack, const SkIRect& bounds) {
150     uint32_t clipStackGenID = clipStack ? clipStack->getTopmostGenID()
151                                         : SkClipStack::kWideOpenGenID;
152     if (clipStackGenID == currentEntry()->fClipStackGenID) {
153         return;
154     }
155     while (fStackDepth > 0) {
156         this->pop();
157         if (clipStackGenID == currentEntry()->fClipStackGenID) {
158             return;
159         }
160     }
161     SkASSERT(currentEntry()->fClipStackGenID == SkClipStack::kWideOpenGenID);
162     if (clipStackGenID != SkClipStack::kWideOpenGenID) {
163         SkASSERT(clipStack);
164         this->push();
165 
166         currentEntry()->fClipStackGenID = clipStackGenID;
167         append_clip(*clipStack, bounds, fContentStream);
168     }
169 }
170 
171 
updateMatrix(const SkMatrix & matrix)172 void SkPDFGraphicStackState::updateMatrix(const SkMatrix& matrix) {
173     if (matrix == currentEntry()->fMatrix) {
174         return;
175     }
176 
177     if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
178         SkASSERT(fStackDepth > 0);
179         SkASSERT(fEntries[fStackDepth].fClipStackGenID ==
180                  fEntries[fStackDepth -1].fClipStackGenID);
181         this->pop();
182 
183         SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
184     }
185     if (matrix.getType() == SkMatrix::kIdentity_Mask) {
186         return;
187     }
188 
189     this->push();
190     SkPDFUtils::AppendTransform(matrix, fContentStream);
191     currentEntry()->fMatrix = matrix;
192 }
193 
updateDrawingState(const SkPDFGraphicStackState::Entry & state)194 void SkPDFGraphicStackState::updateDrawingState(const SkPDFGraphicStackState::Entry& state) {
195     // PDF treats a shader as a color, so we only set one or the other.
196     if (state.fShaderIndex >= 0) {
197         if (state.fShaderIndex != currentEntry()->fShaderIndex) {
198             SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream);
199             currentEntry()->fShaderIndex = state.fShaderIndex;
200         }
201     } else {
202         if (state.fColor != currentEntry()->fColor ||
203                 currentEntry()->fShaderIndex >= 0) {
204             emit_pdf_color(state.fColor, fContentStream);
205             fContentStream->writeText("RG ");
206             emit_pdf_color(state.fColor, fContentStream);
207             fContentStream->writeText("rg\n");
208             currentEntry()->fColor = state.fColor;
209             currentEntry()->fShaderIndex = -1;
210         }
211     }
212 
213     if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
214         SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
215         currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
216     }
217 
218     if (state.fTextScaleX) {
219         if (state.fTextScaleX != currentEntry()->fTextScaleX) {
220             SkScalar pdfScale = state.fTextScaleX * 100;
221             SkPDFUtils::AppendScalar(pdfScale, fContentStream);
222             fContentStream->writeText(" Tz\n");
223             currentEntry()->fTextScaleX = state.fTextScaleX;
224         }
225     }
226 }
227 
push()228 void SkPDFGraphicStackState::push() {
229     SkASSERT(fStackDepth < kMaxStackDepth);
230     fContentStream->writeText("q\n");
231     ++fStackDepth;
232     fEntries[fStackDepth] = fEntries[fStackDepth - 1];
233 }
234 
pop()235 void SkPDFGraphicStackState::pop() {
236     SkASSERT(fStackDepth > 0);
237     fContentStream->writeText("Q\n");
238     fEntries[fStackDepth] = SkPDFGraphicStackState::Entry();
239     --fStackDepth;
240 }
241 
drainStack()242 void SkPDFGraphicStackState::drainStack() {
243     if (fContentStream) {
244         while (fStackDepth) {
245             this->pop();
246         }
247     }
248     SkASSERT(fStackDepth == 0);
249 }
250 
251