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 "SkRecordDraw.h" | 8 #include "SkRecordDraw.h" |
9 | 9 |
10 namespace { | 10 namespace { |
11 | 11 |
12 // This is an SkRecord visitor that will draw that SkRecord to an SkCanvas. | 12 // This is an SkRecord visitor that will draw that SkRecord to an SkCanvas. |
13 class Draw : SkNoncopyable { | 13 class Draw : SkNoncopyable { |
14 public: | 14 public: |
15 explicit Draw(SkCanvas* canvas) : fCanvas(canvas), fIndex(0), fClipEmpty(fal
se) {} | 15 explicit Draw(SkCanvas* canvas) : fCanvas(canvas), fIndex(0), fClipEmpty(fal
se) {} |
16 | 16 |
17 unsigned index() const { return fIndex; } | 17 unsigned index() const { return fIndex; } |
18 void next() { ++fIndex; } | 18 void next() { ++fIndex; } |
19 | 19 |
20 template <typename T> void operator()(const T& r) { | 20 template <typename T> void operator()(const T& r) { |
21 if (!this->canSkip(r)) { | 21 if (!this->skip(r)) { |
22 this->draw(r); | 22 this->draw(r); |
23 this->updateClip<T>(); | 23 this->updateClip<T>(); |
24 } | 24 } |
25 } | 25 } |
26 | 26 |
27 private: | 27 private: |
28 // Can we skip this command right now? | 28 // Return true if we can skip this command, false if not. |
29 template <typename T> bool canSkip(const T&) const { | 29 // Update fIndex here directly to skip more than just this one command. |
| 30 template <typename T> bool skip(const T&) { |
30 // We can skip most commands if the clip is empty. Exceptions are speci
alized below. | 31 // We can skip most commands if the clip is empty. Exceptions are speci
alized below. |
31 return fClipEmpty; | 32 return fClipEmpty; |
32 } | 33 } |
33 | 34 |
34 // No base case, so we'll be compile-time checked that we implemented all po
ssibilities below. | 35 // No base case, so we'll be compile-time checked that we implemented all po
ssibilities below. |
35 template <typename T> void draw(const T&); | 36 template <typename T> void draw(const T&); |
36 | 37 |
37 // Update fClipEmpty if necessary. | 38 // Update fClipEmpty if necessary. |
38 template <typename T> void updateClip() { | 39 template <typename T> void updateClip() { |
39 // Most commands don't change the clip. Exceptions are specialized belo
w. | 40 // Most commands don't change the clip. Exceptions are specialized belo
w. |
40 } | 41 } |
41 | 42 |
42 SkCanvas* fCanvas; | 43 SkCanvas* fCanvas; |
43 unsigned fIndex; | 44 unsigned fIndex; |
44 bool fClipEmpty; | 45 bool fClipEmpty; |
45 }; | 46 }; |
46 | 47 |
| 48 // TODO(mtklein): do this specialization with template traits instead of macros |
| 49 |
47 // These commands may change the clip. | 50 // These commands may change the clip. |
48 #define UPDATE_CLIP(T) template <> void Draw::updateClip<SkRecords::T>() \ | 51 #define UPDATE_CLIP(T) template <> void Draw::updateClip<SkRecords::T>() \ |
49 { fClipEmpty = fCanvas->isClipEmpty(); } | 52 { fClipEmpty = fCanvas->isClipEmpty(); } |
50 UPDATE_CLIP(Restore); | 53 UPDATE_CLIP(Restore); |
51 UPDATE_CLIP(SaveLayer); | 54 UPDATE_CLIP(SaveLayer); |
52 UPDATE_CLIP(ClipPath); | 55 UPDATE_CLIP(ClipPath); |
53 UPDATE_CLIP(ClipRRect); | 56 UPDATE_CLIP(ClipRRect); |
54 UPDATE_CLIP(ClipRect); | 57 UPDATE_CLIP(ClipRect); |
55 UPDATE_CLIP(ClipRegion); | 58 UPDATE_CLIP(ClipRegion); |
56 #undef UPDATE_CLIP | 59 #undef UPDATE_CLIP |
57 | 60 |
58 // These commands must always run. | 61 // These commands must always run. |
59 #define CAN_SKIP(T) template <> bool Draw::canSkip(const SkRecords::T&) const {
return false; } | 62 #define SKIP(T) template <> bool Draw::skip(const SkRecords::T&) { return false;
} |
60 CAN_SKIP(Restore); | 63 SKIP(Restore); |
61 CAN_SKIP(Save); | 64 SKIP(Save); |
62 CAN_SKIP(SaveLayer); | 65 SKIP(SaveLayer); |
63 CAN_SKIP(Clear); | 66 SKIP(Clear); |
64 CAN_SKIP(PushCull); | 67 SKIP(PushCull); |
65 CAN_SKIP(PopCull); | 68 SKIP(PopCull); |
66 #undef CAN_SKIP | 69 #undef SKIP |
67 | 70 |
68 // We can skip these commands if they're intersecting with a clip that's already
empty. | 71 // We can skip these commands if they're intersecting with a clip that's already
empty. |
69 #define CAN_SKIP(T) template <> bool Draw::canSkip(const SkRecords::T& r) const
\ | 72 #define SKIP(T) template <> bool Draw::skip(const SkRecords::T& r) \ |
70 { return fClipEmpty && SkRegion::kIntersect_Op == r.op; } | 73 { return fClipEmpty && SkRegion::kIntersect_Op == r.op; } |
71 CAN_SKIP(ClipPath); | 74 SKIP(ClipPath); |
72 CAN_SKIP(ClipRRect); | 75 SKIP(ClipRRect); |
73 CAN_SKIP(ClipRect); | 76 SKIP(ClipRect); |
74 CAN_SKIP(ClipRegion); | 77 SKIP(ClipRegion); |
75 #undef CAN_SKIP | 78 #undef SKIP |
76 | 79 |
77 static bool can_skip_text(const SkCanvas& c, const SkPaint& p, SkScalar minY, Sk
Scalar maxY) { | 80 // NoOps can always be skipped and draw nothing. |
78 // If we're drawing vertical text, none of the checks we're about to do make
any sense. | 81 template <> bool Draw::skip(const SkRecords::NoOp&) { return true; } |
79 // We'll need to call SkPaint::computeFastBounds() later, so bail out if tha
t's not possible. | 82 template <> void Draw::draw(const SkRecords::NoOp&) {} |
80 if (p.isVerticalText() || !p.canComputeFastBounds()) { | |
81 return false; | |
82 } | |
83 | |
84 // Rather than checking the top and bottom font metrics, we guess. Actually
looking up the top | |
85 // and bottom metrics is slow, and this overapproximation should be good eno
ugh. | |
86 const SkScalar buffer = p.getTextSize() * 1.5f; | |
87 SkDEBUGCODE(SkPaint::FontMetrics metrics;) | |
88 SkDEBUGCODE(p.getFontMetrics(&metrics);) | |
89 SkASSERT(-buffer <= metrics.fTop); | |
90 SkASSERT(+buffer >= metrics.fBottom); | |
91 | |
92 // Let the paint adjust the text bounds. We don't care about left and right
here, so we use | |
93 // 0 and 1 respectively just so the bounds rectangle isn't empty. | |
94 SkRect bounds; | |
95 bounds.set(0, -buffer, SK_Scalar1, buffer); | |
96 SkRect adjusted = p.computeFastBounds(bounds, &bounds); | |
97 return c.quickRejectY(minY + adjusted.fTop, maxY + adjusted.fBottom); | |
98 } | |
99 | |
100 template <> bool Draw::canSkip(const SkRecords::DrawPosTextH& r) const { | |
101 return fClipEmpty || can_skip_text(*fCanvas, r.paint, r.y, r.y); | |
102 } | |
103 | |
104 template <> bool Draw::canSkip(const SkRecords::DrawPosText& r) const { | |
105 if (fClipEmpty) { | |
106 return true; | |
107 } | |
108 | |
109 // TODO(mtklein): may want to move this minY/maxY calculation into a one-tim
e pass | |
110 const unsigned points = r.paint.countText(r.text, r.byteLength); | |
111 if (points == 0) { | |
112 return true; | |
113 } | |
114 SkScalar minY = SK_ScalarInfinity, maxY = SK_ScalarNegativeInfinity; | |
115 for (unsigned i = 0; i < points; i++) { | |
116 minY = SkTMin(minY, r.pos[i].fY); | |
117 maxY = SkTMax(maxY, r.pos[i].fY); | |
118 } | |
119 | |
120 return can_skip_text(*fCanvas, r.paint, minY, maxY); | |
121 } | |
122 | 83 |
123 #define DRAW(T, call) template <> void Draw::draw(const SkRecords::T& r) { fCanv
as->call; } | 84 #define DRAW(T, call) template <> void Draw::draw(const SkRecords::T& r) { fCanv
as->call; } |
124 DRAW(Restore, restore()); | 85 DRAW(Restore, restore()); |
125 DRAW(Save, save(r.flags)); | 86 DRAW(Save, save(r.flags)); |
126 DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags)); | 87 DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags)); |
127 DRAW(PopCull, popCull()); | 88 DRAW(PopCull, popCull()); |
| 89 DRAW(PushCull, pushCull(r.rect)); |
128 DRAW(Clear, clear(r.color)); | 90 DRAW(Clear, clear(r.color)); |
129 DRAW(Concat, concat(r.matrix)); | 91 DRAW(Concat, concat(r.matrix)); |
130 DRAW(SetMatrix, setMatrix(r.matrix)); | 92 DRAW(SetMatrix, setMatrix(r.matrix)); |
131 | 93 |
132 DRAW(ClipPath, clipPath(r.path, r.op, r.doAA)); | 94 DRAW(ClipPath, clipPath(r.path, r.op, r.doAA)); |
133 DRAW(ClipRRect, clipRRect(r.rrect, r.op, r.doAA)); | 95 DRAW(ClipRRect, clipRRect(r.rrect, r.op, r.doAA)); |
134 DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA)); | 96 DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA)); |
135 DRAW(ClipRegion, clipRegion(r.region, r.op)); | 97 DRAW(ClipRegion, clipRegion(r.region, r.op)); |
136 | 98 |
137 DRAW(DrawBitmap, drawBitmap(r.bitmap, r.left, r.top, r.paint)); | 99 DRAW(DrawBitmap, drawBitmap(r.bitmap, r.left, r.top, r.paint)); |
138 DRAW(DrawBitmapMatrix, drawBitmapMatrix(r.bitmap, r.matrix, r.paint)); | 100 DRAW(DrawBitmapMatrix, drawBitmapMatrix(r.bitmap, r.matrix, r.paint)); |
139 DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap, r.center, r.dst, r.paint)); | 101 DRAW(DrawBitmapNine, drawBitmapNine(r.bitmap, r.center, r.dst, r.paint)); |
140 DRAW(DrawBitmapRectToRect, drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint,
r.flags)); | 102 DRAW(DrawBitmapRectToRect, drawBitmapRectToRect(r.bitmap, r.src, r.dst, r.paint,
r.flags)); |
141 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); | 103 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint)); |
142 DRAW(DrawOval, drawOval(r.oval, r.paint)); | 104 DRAW(DrawOval, drawOval(r.oval, r.paint)); |
143 DRAW(DrawPaint, drawPaint(r.paint)); | 105 DRAW(DrawPaint, drawPaint(r.paint)); |
144 DRAW(DrawPath, drawPath(r.path, r.paint)); | 106 DRAW(DrawPath, drawPath(r.path, r.paint)); |
145 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); | 107 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint)); |
146 DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); | 108 DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint)); |
147 DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); | 109 DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint)); |
148 DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); | 110 DRAW(DrawRRect, drawRRect(r.rrect, r.paint)); |
149 DRAW(DrawRect, drawRect(r.rect, r.paint)); | 111 DRAW(DrawRect, drawRect(r.rect, r.paint)); |
150 DRAW(DrawSprite, drawSprite(r.bitmap, r.left, r.top, r.paint)); | 112 DRAW(DrawSprite, drawSprite(r.bitmap, r.left, r.top, r.paint)); |
151 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); | 113 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); |
152 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.pa
int)); | 114 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.pa
int)); |
153 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.co
lors, | 115 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.co
lors, |
154 r.xmode.get(), r.indices, r.indexCount, r.paint)
); | 116 r.xmode.get(), r.indices, r.indexCount, r.paint)
); |
155 #undef DRAW | 117 #undef DRAW |
156 | 118 |
157 // PushCull is a bit of a oddball. We might be able to just skip until just pas
t its popCull. | 119 // Added by SkRecordAnnotateCullingPairs. |
158 template <> void Draw::draw(const SkRecords::PushCull& r) { | 120 template <> bool Draw::skip(const SkRecords::PairedPushCull& r) { |
159 if (r.popOffset != SkRecords::kUnsetPopOffset && fCanvas->quickReject(r.rect
)) { | 121 if (fCanvas->quickReject(r.base->rect)) { |
160 fIndex += r.popOffset; | 122 fIndex += r.skip; |
161 } else { | 123 return true; |
162 fCanvas->pushCull(r.rect); | |
163 } | 124 } |
| 125 return false; |
164 } | 126 } |
165 | 127 |
| 128 // Added by SkRecordBoundDrawPosTextH |
| 129 template <> bool Draw::skip(const SkRecords::BoundedDrawPosTextH& r) { |
| 130 return fClipEmpty || fCanvas->quickRejectY(r.minY, r.maxY); |
| 131 } |
| 132 |
| 133 // These draw by proxying to the commands they wrap. (All the optimization is f
or skip().) |
| 134 #define DRAW(T) template <> void Draw::draw(const SkRecords::T& r) { this->draw(
*r.base); } |
| 135 DRAW(PairedPushCull); |
| 136 DRAW(BoundedDrawPosTextH); |
| 137 #undef DRAW |
| 138 |
166 } // namespace | 139 } // namespace |
167 | 140 |
168 void SkRecordDraw(const SkRecord& record, SkCanvas* canvas) { | 141 void SkRecordDraw(const SkRecord& record, SkCanvas* canvas) { |
169 for (Draw draw(canvas); draw.index() < record.count(); draw.next()) { | 142 for (Draw draw(canvas); draw.index() < record.count(); draw.next()) { |
170 record.visit(draw.index(), draw); | 143 record.visit(draw.index(), draw); |
171 } | 144 } |
172 } | 145 } |
OLD | NEW |