Flutter Engine
The Flutter Engine
SkPDFGraphicStackState.cpp
Go to the documentation of this file.
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
5
10#include "include/core/SkRect.h"
14#include "src/pdf/SkPDFUtils.h"
16
18 SkASSERT(color.fA == 1); // We handle alpha elsewhere.
20 result->writeText(" ");
22 result->writeText(" ");
24 result->writeText(" ");
25}
26
28 if (u.isEmpty() || v.isEmpty()) { return {0, 0, 0, 0}; }
29 return u.intersect(v) ? u : SkRect{0, 0, 0, 0};
30}
31
32// Test to see if the clipstack is a simple rect, If so, we can avoid all PathOps code
33// and speed thing up.
34static bool is_rect(const SkClipStack& clipStack, const SkRect& bounds, SkRect* dst) {
35 SkRect currentClip = bounds;
37 while (const SkClipStack::Element* element = iter.next()) {
38 SkRect elementRect{0, 0, 0, 0};
39 switch (element->getDeviceSpaceType()) {
41 break;
43 elementRect = element->getDeviceSpaceRect();
44 break;
45 default:
46 return false;
47 }
48 if (element->isReplaceOp()) {
49 currentClip = rect_intersect(bounds, elementRect);
50 } else if (element->getOp() == SkClipOp::kIntersect) {
51 currentClip = rect_intersect(currentClip, elementRect);
52 } else {
53 return false;
54 }
55 }
56 *dst = currentClip;
57 return true;
58}
59
60// TODO: When there's no expanding clip ops, this function may not be necessary anymore.
61static bool is_complex_clip(const SkClipStack& stack) {
63 while (const SkClipStack::Element* element = iter.next()) {
64 if (element->isReplaceOp() ||
65 (element->getOp() != SkClipOp::kDifference &&
66 element->getOp() != SkClipOp::kIntersect)) {
67 return true;
68 }
69 }
70 return false;
71}
72
73template <typename F>
74static void apply_clip(const SkClipStack& stack, const SkRect& outerBounds, F fn) {
75 // assumes clipstack is not complex.
76 constexpr SkRect kHuge{-30000, -30000, 30000, 30000};
78 SkRect bounds = outerBounds;
79 while (const SkClipStack::Element* element = iter.next()) {
80 SkPath operand;
81 element->asDeviceSpacePath(&operand);
82 SkPathOp op;
83 switch (element->getOp()) {
86 default: SkASSERT(false); return;
87 }
88 if (op == kDifference_SkPathOp ||
89 operand.isInverseFillType() ||
90 !kHuge.contains(operand.getBounds()))
91 {
92 Op(SkPath::Rect(bounds), operand, op, &operand);
93 }
94 SkASSERT(!operand.isInverseFillType());
95 fn(operand);
96 if (!bounds.intersect(operand.getBounds())) {
97 return; // return early;
98 }
99 }
100}
101
102static void append_clip_path(const SkPath& clipPath, SkWStream* wStream) {
104 SkPathFillType clipFill = clipPath.getFillType();
107 if (clipFill == SkPathFillType::kEvenOdd) {
108 wStream->writeText("W* n\n");
109 } else {
110 wStream->writeText("W n\n");
111 }
112}
113
114static void append_clip(const SkClipStack& clipStack,
115 const SkIRect& bounds,
116 SkWStream* wStream) {
117 // The bounds are slightly outset to ensure this is correct in the
118 // face of floating-point accuracy and possible SkRegion bitmap
119 // approximations.
120 SkRect outsetBounds = SkRect::Make(bounds.makeOutset(1, 1));
121
122 SkRect clipStackRect;
123 if (is_rect(clipStack, outsetBounds, &clipStackRect)) {
124 SkPDFUtils::AppendRectangle(clipStackRect, wStream);
125 wStream->writeText("W* n\n");
126 return;
127 }
128
129 if (is_complex_clip(clipStack)) {
131 SkClipStack_AsPath(clipStack, &clipPath);
132 if (Op(clipPath, SkPath::Rect(outsetBounds), kIntersect_SkPathOp, &clipPath)) {
133 append_clip_path(clipPath, wStream);
134 }
135 // If Op() fails (pathological case; e.g. input values are
136 // extremely large or NaN), emit no clip at all.
137 } else {
138 apply_clip(clipStack, outsetBounds, [wStream](const SkPath& path) {
139 append_clip_path(path, wStream);
140 });
141 }
142}
143
144////////////////////////////////////////////////////////////////////////////////
145
147 uint32_t clipStackGenID = clipStack ? clipStack->getTopmostGenID()
149 if (clipStackGenID == currentEntry()->fClipStackGenID) {
150 return;
151 }
152 while (fStackDepth > 0) {
153 this->pop();
154 if (clipStackGenID == currentEntry()->fClipStackGenID) {
155 return;
156 }
157 }
159 if (clipStackGenID != SkClipStack::kWideOpenGenID) {
160 SkASSERT(clipStack);
161 this->push();
162
163 currentEntry()->fClipStackGenID = clipStackGenID;
164 append_clip(*clipStack, bounds, fContentStream);
165 }
166}
167
168
170 if (matrix == currentEntry()->fMatrix) {
171 return;
172 }
173
176 SkASSERT(fEntries[fStackDepth].fClipStackGenID ==
177 fEntries[fStackDepth -1].fClipStackGenID);
178 this->pop();
179
181 }
182 if (matrix.getType() == SkMatrix::kIdentity_Mask) {
183 return;
184 }
185
186 this->push();
189}
190
192 // PDF treats a shader as a color, so we only set one or the other.
193 if (state.fShaderIndex >= 0) {
194 if (state.fShaderIndex != currentEntry()->fShaderIndex) {
196 currentEntry()->fShaderIndex = state.fShaderIndex;
197 }
198 } else if (state.fColor != currentEntry()->fColor || currentEntry()->fShaderIndex >= 0) {
202 fContentStream->writeText("rg\n");
203 currentEntry()->fColor = state.fColor;
205 }
206
207 if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
209 currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
210 }
211
212 if (state.fTextScaleX) {
213 if (state.fTextScaleX != currentEntry()->fTextScaleX) {
214 SkScalar pdfScale = state.fTextScaleX * 100;
216 fContentStream->writeText(" Tz\n");
217 currentEntry()->fTextScaleX = state.fTextScaleX;
218 }
219 }
220}
221
225 ++fStackDepth;
227}
228
233 --fStackDepth;
234}
235
237 if (fContentStream) {
238 while (fStackDepth) {
239 this->pop();
240 }
241 }
242 SkASSERT(fStackDepth == 0);
243}
SkMatrix fMatrix
Definition: FillRRectOp.cpp:74
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SkClipStack_AsPath(const SkClipStack &cs, SkPath *path)
static void apply_clip(const SkClipStack &stack, const SkRect &outerBounds, F fn)
static void emit_pdf_color(SkColor4f color, SkWStream *result)
static bool is_rect(const SkClipStack &clipStack, const SkRect &bounds, SkRect *dst)
static SkRect rect_intersect(SkRect u, SkRect v)
static bool is_complex_clip(const SkClipStack &stack)
static void append_clip_path(const SkPath &clipPath, SkWStream *wStream)
static void append_clip(const SkClipStack &clipStack, const SkIRect &bounds, SkWStream *wStream)
#define NOT_IMPLEMENTED(condition, assertion)
Definition: SkPDFUtils.h:51
SkPathOp
Definition: SkPathOps.h:22
@ kDifference_SkPathOp
subtract the op path from the first path
Definition: SkPathOps.h:23
@ kIntersect_SkPathOp
intersect the two paths
Definition: SkPathOps.h:24
SkPathFillType
Definition: SkPathTypes.h:11
@ kEmpty
This element makes the clip empty (regardless of previous elements).
@ kRect
This element combines a device space round-rect with the current clip.
const Element * next()
uint32_t getTopmostGenID() const
static const uint32_t kWideOpenGenID
Definition: SkClipStack.h:387
@ kIdentity_Mask
identity SkMatrix; all bits clear
Definition: SkMatrix.h:192
TypeMask getType() const
Definition: SkMatrix.h:207
@ kFill_Style
set to fill geometry
Definition: SkPaint.h:193
Definition: SkPath.h:59
bool isInverseFillType() const
Definition: SkPath.h:244
static SkPath Rect(const SkRect &, SkPathDirection=SkPathDirection::kCW, unsigned startIndex=0)
Definition: SkPath.cpp:3586
const SkRect & getBounds() const
Definition: SkPath.cpp:430
bool writeText(const char text[])
Definition: SkStream.h:247
DlColor color
float SkScalar
Definition: extension.cpp:12
AtkStateType state
GAsyncResult * result
void ApplyGraphicState(int objectIndex, SkWStream *content)
Definition: SkPDFUtils.cpp:254
void EmitPath(const SkPath &path, SkPaint::Style paintStyle, bool doConsumeDegerates, SkWStream *content, SkScalar tolerance=0.25f)
Definition: SkPDFUtils.cpp:132
void AppendRectangle(const SkRect &rect, SkWStream *content)
Definition: SkPDFUtils.cpp:118
void AppendScalar(SkScalar value, SkWStream *stream)
Definition: SkPDFUtils.h:98
void AppendTransform(const SkMatrix &, SkWStream *)
Definition: SkPDFUtils.cpp:399
void ApplyPattern(int objectIndex, SkWStream *content)
Definition: SkPDFUtils.cpp:259
void AppendColorComponentF(float value, SkWStream *wStream)
Definition: SkPDFUtils.h:92
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
Optional< SkRect > bounds
Definition: SkRecords.h:189
clipPath(r.path, r.opAA.op(), r.opAA.aa())) DRAW(ClipRRect
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
dst
Definition: cp.py:12
Definition: SkMD5.cpp:120
Definition: SkRect.h:32
Entry fEntries[kMaxStackDepth+1]
void updateMatrix(const SkMatrix &matrix)
void updateDrawingState(const Entry &state)
SkDynamicMemoryWStream * fContentStream
static constexpr int kMaxStackDepth
void updateClip(const SkClipStack *clipStack, const SkIRect &bounds)
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
bool isEmpty() const
Definition: SkRect.h:693