| 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 |