OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 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 "SkRecordOpts.h" | 8 #include "SkRecordOpts.h" |
9 | 9 |
| 10 #include "SkRecordTraits.h" |
10 #include "SkRecords.h" | 11 #include "SkRecords.h" |
11 #include "SkTDArray.h" | 12 #include "SkTDArray.h" |
12 | 13 |
13 void SkRecordOptimize(SkRecord* record) { | 14 void SkRecordOptimize(SkRecord* record) { |
14 // TODO(mtklein): fuse independent optimizations to reduce number of passes? | 15 // TODO(mtklein): fuse independent optimizations to reduce number of passes? |
15 SkRecordNoopSaveRestores(record); | 16 SkRecordNoopSaveRestores(record); |
16 SkRecordAnnotateCullingPairs(record); | 17 SkRecordAnnotateCullingPairs(record); |
17 SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before Bo
undDrawPosTextH. | 18 SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before Bo
undDrawPosTextH. |
18 SkRecordBoundDrawPosTextH(record); | 19 SkRecordBoundDrawPosTextH(record); |
19 } | 20 } |
(...skipping 13 matching lines...) Expand all Loading... |
33 unsigned fIndex; | 34 unsigned fIndex; |
34 }; | 35 }; |
35 | 36 |
36 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual
no-ops. | 37 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual
no-ops. |
37 // TODO(mtklein): state machine diagram | 38 // TODO(mtklein): state machine diagram |
38 class SaveRestoreNooper : public Common { | 39 class SaveRestoreNooper : public Common { |
39 public: | 40 public: |
40 explicit SaveRestoreNooper(SkRecord* record) | 41 explicit SaveRestoreNooper(SkRecord* record) |
41 : Common(record), fSave(kInactive), fChanged(false) {} | 42 : Common(record), fSave(kInactive), fChanged(false) {} |
42 | 43 |
43 // Most drawing commands reset to inactive state without nooping anything. | 44 // Drawing commands reset state to inactive without nooping. |
44 template <typename T> | 45 template <typename T> |
45 void operator()(T*) { fSave = kInactive; } | 46 SK_WHEN(SkRecords::IsDraw<T>, void) operator()(T*) { fSave = kInactive; } |
| 47 |
| 48 // Most non-drawing commands can be ignored. |
| 49 template <typename T> |
| 50 SK_WHEN(!SkRecords::IsDraw<T>, void) operator()(T*) {} |
| 51 |
| 52 void operator()(SkRecords::Save* r) { |
| 53 fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kIn
active; |
| 54 } |
| 55 |
| 56 void operator()(SkRecords::Restore* r) { |
| 57 if (fSave != kInactive) { |
| 58 // Remove everything between the save and restore, inclusive on both
sides. |
| 59 fChanged = true; |
| 60 for (unsigned i = fSave; i <= this->index(); i++) { |
| 61 fRecord->replace<SkRecords::NoOp>(i); |
| 62 } |
| 63 fSave = kInactive; |
| 64 } |
| 65 } |
46 | 66 |
47 bool changed() const { return fChanged; } | 67 bool changed() const { return fChanged; } |
48 | 68 |
49 private: | 69 private: |
50 static const unsigned kInactive = ~0; | 70 static const unsigned kInactive = ~0; |
51 unsigned fSave; | 71 unsigned fSave; |
52 bool fChanged; | 72 bool fChanged; |
53 }; | 73 }; |
54 | 74 |
55 // If the command doesn't draw anything, that doesn't reset the state back to in
active. | |
56 // TODO(mtklein): do this with some sort of template-based trait mechanism inste
ad of macros | |
57 #define DOESNT_DRAW(T) template <> void SaveRestoreNooper::operator()(SkRecords:
:T*) {} | |
58 DOESNT_DRAW(NoOp) | |
59 DOESNT_DRAW(Concat) | |
60 DOESNT_DRAW(SetMatrix) | |
61 DOESNT_DRAW(ClipRect) | |
62 DOESNT_DRAW(ClipRRect) | |
63 DOESNT_DRAW(ClipPath) | |
64 DOESNT_DRAW(ClipRegion) | |
65 DOESNT_DRAW(PairedPushCull) | |
66 DOESNT_DRAW(PushCull) | |
67 DOESNT_DRAW(PopCull) | |
68 #undef DOESNT_DRAW | |
69 | |
70 template <> | |
71 void SaveRestoreNooper::operator()(SkRecords::Save* r) { | |
72 fSave = SkCanvas::kMatrixClip_SaveFlag == r->flags ? this->index() : kInacti
ve; | |
73 } | |
74 | |
75 template <> | |
76 void SaveRestoreNooper::operator()(SkRecords::Restore* r) { | |
77 if (fSave != kInactive) { | |
78 // Remove everything between the save and restore, inclusive on both sid
es. | |
79 fChanged = true; | |
80 for (unsigned i = fSave; i <= this->index(); i++) { | |
81 fRecord->replace<SkRecords::NoOp>(i); | |
82 } | |
83 fSave = kInactive; | |
84 } | |
85 } | |
86 | |
87 | |
88 // Tries to replace PushCull with PairedPushCull, which lets us skip to the pair
ed PopCull | 75 // Tries to replace PushCull with PairedPushCull, which lets us skip to the pair
ed PopCull |
89 // when the canvas can quickReject the cull rect. | 76 // when the canvas can quickReject the cull rect. |
90 class CullAnnotator : public Common { | 77 class CullAnnotator : public Common { |
91 public: | 78 public: |
92 explicit CullAnnotator(SkRecord* record) : Common(record) {} | 79 explicit CullAnnotator(SkRecord* record) : Common(record) {} |
93 | 80 |
94 // Do nothing to most ops. | 81 // Do nothing to most ops. |
95 template <typename T> | 82 template <typename T> void operator()(T*) {} |
96 void operator()(T*) {} | 83 |
| 84 void operator()(SkRecords::PushCull* push) { |
| 85 Pair pair = { this->index(), push }; |
| 86 fPushStack.push(pair); |
| 87 } |
| 88 |
| 89 void operator()(SkRecords::PopCull* pop) { |
| 90 Pair push = fPushStack.top(); |
| 91 fPushStack.pop(); |
| 92 |
| 93 SkASSERT(this->index() > push.index); |
| 94 unsigned skip = this->index() - push.index; |
| 95 |
| 96 SkRecords::Adopted<SkRecords::PushCull> adopted(push.command); |
| 97 SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::PairedPushCull>(push.in
dex, adopted), |
| 98 SkRecords::PairedPushCull, (&adopted, skip)); |
| 99 } |
97 | 100 |
98 private: | 101 private: |
99 struct Pair { | 102 struct Pair { |
100 unsigned index; | 103 unsigned index; |
101 SkRecords::PushCull* command; | 104 SkRecords::PushCull* command; |
102 }; | 105 }; |
103 | 106 |
104 SkTDArray<Pair> fPushStack; | 107 SkTDArray<Pair> fPushStack; |
105 }; | 108 }; |
106 | 109 |
107 template <> | |
108 void CullAnnotator::operator()(SkRecords::PushCull* push) { | |
109 Pair pair = { this->index(), push }; | |
110 fPushStack.push(pair); | |
111 } | |
112 | |
113 template <> | |
114 void CullAnnotator::operator()(SkRecords::PopCull* pop) { | |
115 Pair push = fPushStack.top(); | |
116 fPushStack.pop(); | |
117 | |
118 SkASSERT(this->index() > push.index); | |
119 unsigned skip = this->index() - push.index; | |
120 | |
121 SkRecords::Adopted<SkRecords::PushCull> adopted(push.command); | |
122 SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::PairedPushCull>(push.index,
adopted), | |
123 SkRecords::PairedPushCull, (&adopted, skip)); | |
124 } | |
125 | |
126 // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal. | 110 // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal. |
127 class StrengthReducer : public Common { | 111 class StrengthReducer : public Common { |
128 public: | 112 public: |
129 explicit StrengthReducer(SkRecord* record) : Common(record) {} | 113 explicit StrengthReducer(SkRecord* record) : Common(record) {} |
130 | 114 |
131 // Do nothing to most ops. | 115 // Do nothing to most ops. |
132 template <typename T> | 116 template <typename T> void operator()(T*) {} |
133 void operator()(T*) {} | |
134 }; | |
135 | 117 |
136 template <> | 118 void operator()(SkRecords::DrawPosText* r) { |
137 void StrengthReducer::operator()(SkRecords::DrawPosText* r) { | 119 const unsigned points = r->paint.countText(r->text, r->byteLength); |
138 const unsigned points = r->paint.countText(r->text, r->byteLength); | 120 if (points == 0) { |
139 if (points == 0) { | 121 // No point (ha!). |
140 // No point (ha!). | |
141 return; | |
142 } | |
143 | |
144 const SkScalar firstY = r->pos[0].fY; | |
145 for (unsigned i = 1; i < points; i++) { | |
146 if (r->pos[i].fY != firstY) { | |
147 // Needs the full strength of DrawPosText. | |
148 return; | 122 return; |
149 } | 123 } |
| 124 |
| 125 const SkScalar firstY = r->pos[0].fY; |
| 126 for (unsigned i = 1; i < points; i++) { |
| 127 if (r->pos[i].fY != firstY) { |
| 128 // Needs the full strength of DrawPosText. |
| 129 return; |
| 130 } |
| 131 } |
| 132 // All ys are the same. We can replace DrawPosText with DrawPosTextH. |
| 133 |
| 134 // r->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ]. |
| 135 // We're going to squint and look at that as 2*points SkScalars, [x,y,x,
y,x,y,x,y, ...]. |
| 136 // Then we'll rearrange things so all the xs are in order up front, clob
bering the ys. |
| 137 SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNo
tSafe); |
| 138 SkScalar* scalars = &r->pos[0].fX; |
| 139 for (unsigned i = 0; i < 2*points; i += 2) { |
| 140 scalars[i/2] = scalars[i]; |
| 141 } |
| 142 |
| 143 // Extend lifetime of r to the end of the method so we can copy its part
s. |
| 144 SkRecords::Adopted<SkRecords::DrawPosText> adopted(r); |
| 145 SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::DrawPosTextH>(this->ind
ex(), adopted), |
| 146 SkRecords::DrawPosTextH, |
| 147 (r->text, r->byteLength, scalars, firstY, r->paint)
); |
150 } | 148 } |
151 // All ys are the same. We can replace DrawPosText with DrawPosTextH. | 149 }; |
152 | |
153 // r->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ]. | |
154 // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,
y,x,y, ...]. | |
155 // Then we'll rearrange things so all the xs are in order up front, clobberi
ng the ys. | |
156 SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSaf
e); | |
157 SkScalar* scalars = &r->pos[0].fX; | |
158 for (unsigned i = 0; i < 2*points; i += 2) { | |
159 scalars[i/2] = scalars[i]; | |
160 } | |
161 | |
162 // Extend lifetime of r to the end of the method so we can copy its parts. | |
163 SkRecords::Adopted<SkRecords::DrawPosText> adopted(r); | |
164 SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::DrawPosTextH>(this->index()
, adopted), | |
165 SkRecords::DrawPosTextH, | |
166 (r->text, r->byteLength, scalars, firstY, r->paint)); | |
167 } | |
168 | |
169 | 150 |
170 // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservat
ive upper and lower | 151 // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservat
ive upper and lower |
171 // bounds to use with SkCanvas::quickRejectY. | 152 // bounds to use with SkCanvas::quickRejectY. |
172 class TextBounder : public Common { | 153 class TextBounder : public Common { |
173 public: | 154 public: |
174 explicit TextBounder(SkRecord* record) : Common(record) {} | 155 explicit TextBounder(SkRecord* record) : Common(record) {} |
175 | 156 |
176 // Do nothing to most ops. | 157 // Do nothing to most ops. |
177 template <typename T> | 158 template <typename T> void operator()(T*) {} |
178 void operator()(T*) {} | 159 |
| 160 void operator()(SkRecords::DrawPosTextH* r) { |
| 161 // If we're drawing vertical text, none of the checks we're about to do
make any sense. |
| 162 // We'll need to call SkPaint::computeFastBounds() later, so bail if tha
t's not possible. |
| 163 if (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) { |
| 164 return; |
| 165 } |
| 166 |
| 167 // Rather than checking the top and bottom font metrics, we guess. Actu
ally looking up the |
| 168 // top and bottom metrics is slow, and this overapproximation should be
good enough. |
| 169 const SkScalar buffer = r->paint.getTextSize() * 1.5f; |
| 170 SkDEBUGCODE(SkPaint::FontMetrics metrics;) |
| 171 SkDEBUGCODE(r->paint.getFontMetrics(&metrics);) |
| 172 SkASSERT(-buffer <= metrics.fTop); |
| 173 SkASSERT(+buffer >= metrics.fBottom); |
| 174 |
| 175 // Let the paint adjust the text bounds. We don't care about left and r
ight here, so we use |
| 176 // 0 and 1 respectively just so the bounds rectangle isn't empty. |
| 177 SkRect bounds; |
| 178 bounds.set(0, r->y - buffer, SK_Scalar1, r->y + buffer); |
| 179 SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds); |
| 180 |
| 181 SkRecords::Adopted<SkRecords::DrawPosTextH> adopted(r); |
| 182 SkNEW_PLACEMENT_ARGS( |
| 183 fRecord->replace<SkRecords::BoundedDrawPosTextH>(this->index(),
adopted), |
| 184 SkRecords::BoundedDrawPosTextH, |
| 185 (&adopted, adjusted.fTop, adjusted.fBottom)); |
| 186 } |
179 }; | 187 }; |
180 | 188 |
181 template <> | |
182 void TextBounder::operator()(SkRecords::DrawPosTextH* r) { | |
183 // If we're drawing vertical text, none of the checks we're about to do make
any sense. | |
184 // We'll need to call SkPaint::computeFastBounds() later, so bail if that's
not possible. | |
185 if (r->paint.isVerticalText() || !r->paint.canComputeFastBounds()) { | |
186 return; | |
187 } | |
188 | |
189 // Rather than checking the top and bottom font metrics, we guess. Actually
looking up the | |
190 // top and bottom metrics is slow, and this overapproximation should be good
enough. | |
191 const SkScalar buffer = r->paint.getTextSize() * 1.5f; | |
192 SkDEBUGCODE(SkPaint::FontMetrics metrics;) | |
193 SkDEBUGCODE(r->paint.getFontMetrics(&metrics);) | |
194 SkASSERT(-buffer <= metrics.fTop); | |
195 SkASSERT(+buffer >= metrics.fBottom); | |
196 | |
197 // Let the paint adjust the text bounds. We don't care about left and right
here, so we use | |
198 // 0 and 1 respectively just so the bounds rectangle isn't empty. | |
199 SkRect bounds; | |
200 bounds.set(0, r->y - buffer, SK_Scalar1, r->y + buffer); | |
201 SkRect adjusted = r->paint.computeFastBounds(bounds, &bounds); | |
202 | |
203 SkRecords::Adopted<SkRecords::DrawPosTextH> adopted(r); | |
204 SkNEW_PLACEMENT_ARGS(fRecord->replace<SkRecords::BoundedDrawPosTextH>(this->
index(), adopted), | |
205 SkRecords::BoundedDrawPosTextH, | |
206 (&adopted, adjusted.fTop, adjusted.fBottom)); | |
207 } | |
208 | 189 |
209 template <typename Pass> | 190 template <typename Pass> |
210 static void run_pass(Pass& pass, SkRecord* record) { | 191 static void run_pass(Pass& pass, SkRecord* record) { |
211 for (; pass.index() < record->count(); pass.next()) { | 192 for (; pass.index() < record->count(); pass.next()) { |
212 record->mutate(pass.index(), pass); | 193 record->mutate(pass.index(), pass); |
213 } | 194 } |
214 } | 195 } |
215 | 196 |
216 } // namespace | 197 } // namespace |
217 | 198 |
(...skipping 15 matching lines...) Expand all Loading... |
233 | 214 |
234 void SkRecordReduceDrawPosTextStrength(SkRecord* record) { | 215 void SkRecordReduceDrawPosTextStrength(SkRecord* record) { |
235 StrengthReducer reducer(record); | 216 StrengthReducer reducer(record); |
236 run_pass(reducer, record); | 217 run_pass(reducer, record); |
237 } | 218 } |
238 | 219 |
239 void SkRecordBoundDrawPosTextH(SkRecord* record) { | 220 void SkRecordBoundDrawPosTextH(SkRecord* record) { |
240 TextBounder bounder(record); | 221 TextBounder bounder(record); |
241 run_pass(bounder, record); | 222 run_pass(bounder, record); |
242 } | 223 } |
OLD | NEW |