2 * Copyright 2014 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "SkRecordOpts.h"
10 #include "SkRecordPattern.h"
11 #include "SkRecords.h"
12 #include "SkTDArray.h"
14 using namespace SkRecords;
16 void SkRecordOptimize(SkRecord* record) {
17 // TODO(mtklein): fuse independent optimizations to reduce number of passes?
18 SkRecordNoopSaveRestores(record);
19 SkRecordAnnotateCullingPairs(record);
20 SkRecordReduceDrawPosTextStrength(record); // Helpful to run this before BoundDrawPosTextH.
21 SkRecordBoundDrawPosTextH(record);
24 // Most of the optimizations in this file are pattern-based. These are all defined as structs with:
25 // - a Pattern typedef
26 // - a bool onMatch(SkRceord*, Pattern*, unsigned begin, unsigned end) method,
27 // which returns true if it made changes and false if not.
29 // Run a pattern-based optimization once across the SkRecord, returning true if it made any changes.
30 // It looks for spans which match Pass::Pattern, and when found calls onMatch() with the pattern,
31 // record, and [begin,end) span of the commands that matched.
32 template <typename Pass>
33 static bool apply(Pass* pass, SkRecord* record) {
34 typename Pass::Pattern pattern;
36 unsigned begin, end = 0;
38 while (pattern.search(record, &begin, &end)) {
39 changed |= pass->onMatch(record, &pattern, begin, end);
44 // Turns logical no-op Save-[non-drawing command]*-Restore patterns into actual no-ops.
45 struct SaveRestoreNooper {
46 // Star matches greedily, so we also have to exclude Save and Restore.
47 typedef Pattern3<Is<Save>,
48 Star<Not<Or3<Is<Save>,
54 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
55 // If restore doesn't revert both matrix and clip, this isn't safe to noop away.
56 if (pattern->first<Save>()->flags != SkCanvas::kMatrixClip_SaveFlag) {
60 // The entire span between Save and Restore (inclusively) does nothing.
61 for (unsigned i = begin; i < end; i++) {
62 record->replace<NoOp>(i);
67 void SkRecordNoopSaveRestores(SkRecord* record) {
68 SaveRestoreNooper pass;
69 while (apply(&pass, record)); // Run until it stops changing things.
72 // Replaces DrawPosText with DrawPosTextH when all Y coordinates are equal.
73 struct StrengthReducer {
74 typedef Pattern1<Is<DrawPosText> > Pattern;
76 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
77 SkASSERT(end == begin + 1);
78 DrawPosText* draw = pattern->first<DrawPosText>();
80 const unsigned points = draw->paint.countText(draw->text, draw->byteLength);
82 return false; // No point (ha!).
85 const SkScalar firstY = draw->pos[0].fY;
86 for (unsigned i = 1; i < points; i++) {
87 if (draw->pos[i].fY != firstY) {
88 return false; // Needs full power of DrawPosText.
91 // All ys are the same. We can replace DrawPosText with DrawPosTextH.
93 // draw->pos is points SkPoints, [(x,y),(x,y),(x,y),(x,y), ... ].
94 // We're going to squint and look at that as 2*points SkScalars, [x,y,x,y,x,y,x,y, ...].
95 // Then we'll rearrange things so all the xs are in order up front, clobbering the ys.
96 SK_COMPILE_ASSERT(sizeof(SkPoint) == 2 * sizeof(SkScalar), SquintingIsNotSafe);
97 SkScalar* scalars = &draw->pos[0].fX;
98 for (unsigned i = 0; i < 2*points; i += 2) {
99 scalars[i/2] = scalars[i];
102 // Extend lifetime of draw to the end of the loop so we can copy its paint.
103 Adopted<DrawPosText> adopted(draw);
104 SkNEW_PLACEMENT_ARGS(record->replace<DrawPosTextH>(begin, adopted),
106 (draw->text, draw->byteLength, scalars, firstY, draw->paint));
110 void SkRecordReduceDrawPosTextStrength(SkRecord* record) {
111 StrengthReducer pass;
112 apply(&pass, record);
115 // Tries to replace DrawPosTextH with BoundedDrawPosTextH, which knows conservative upper and lower
116 // bounds to use with SkCanvas::quickRejectY.
118 typedef Pattern1<Is<DrawPosTextH> > Pattern;
120 bool onMatch(SkRecord* record, Pattern* pattern, unsigned begin, unsigned end) {
121 SkASSERT(end == begin + 1);
122 DrawPosTextH* draw = pattern->first<DrawPosTextH>();
124 // If we're drawing vertical text, none of the checks we're about to do make any sense.
125 // We'll need to call SkPaint::computeFastBounds() later, so bail if that's not possible.
126 if (draw->paint.isVerticalText() || !draw->paint.canComputeFastBounds()) {
130 // Rather than checking the top and bottom font metrics, we guess. Actually looking up the
131 // top and bottom metrics is slow, and this overapproximation should be good enough.
132 const SkScalar buffer = draw->paint.getTextSize() * 1.5f;
133 SkDEBUGCODE(SkPaint::FontMetrics metrics;)
134 SkDEBUGCODE(draw->paint.getFontMetrics(&metrics);)
135 SkASSERT(-buffer <= metrics.fTop);
136 SkASSERT(+buffer >= metrics.fBottom);
138 // Let the paint adjust the text bounds. We don't care about left and right here, so we use
139 // 0 and 1 respectively just so the bounds rectangle isn't empty.
141 bounds.set(0, draw->y - buffer, SK_Scalar1, draw->y + buffer);
142 SkRect adjusted = draw->paint.computeFastBounds(bounds, &bounds);
144 Adopted<DrawPosTextH> adopted(draw);
145 SkNEW_PLACEMENT_ARGS(record->replace<BoundedDrawPosTextH>(begin, adopted),
147 (&adopted, adjusted.fTop, adjusted.fBottom));
151 void SkRecordBoundDrawPosTextH(SkRecord* record) {
153 apply(&pass, record);
156 // Replaces PushCull with PairedPushCull, which lets us skip to the paired PopCull when the canvas
157 // can quickReject the cull rect.
158 // There's no efficient way (yet?) to express this one as a pattern, so we write a custom pass.
159 class CullAnnotator {
161 // Do nothing to most ops.
162 template <typename T> void operator()(T*) {}
164 void operator()(PushCull* push) {
165 Pair pair = { fIndex, push };
166 fPushStack.push(pair);
169 void operator()(PopCull* pop) {
170 Pair push = fPushStack.top();
173 SkASSERT(fIndex > push.index);
174 unsigned skip = fIndex - push.index;
176 Adopted<PushCull> adopted(push.command);
177 SkNEW_PLACEMENT_ARGS(fRecord->replace<PairedPushCull>(push.index, adopted),
178 PairedPushCull, (&adopted, skip));
181 void apply(SkRecord* record) {
182 for (fRecord = record, fIndex = 0; fIndex < record->count(); fIndex++) {
183 fRecord->mutate(fIndex, *this);
193 SkTDArray<Pair> fPushStack;
197 void SkRecordAnnotateCullingPairs(SkRecord* record) {