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 #ifndef SkRecordDraw_DEFINED | 8 #ifndef SkRecordDraw_DEFINED |
9 #define SkRecordDraw_DEFINED | 9 #define SkRecordDraw_DEFINED |
10 | 10 |
11 #include "SkBBoxHierarchy.h" | 11 #include "SkBBoxHierarchy.h" |
12 #include "SkCanvas.h" | 12 #include "SkCanvas.h" |
13 #include "SkDrawPictureCallback.h" | 13 #include "SkDrawPictureCallback.h" |
14 #include "SkMatrix.h" | 14 #include "SkMatrix.h" |
| 15 #include "SkPatchUtils.h" |
15 #include "SkRecord.h" | 16 #include "SkRecord.h" |
16 | 17 |
17 // Fill a BBH to be used by SkRecordDraw to accelerate playback. | 18 // Fill a BBH to be used by SkRecordDraw to accelerate playback. |
18 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord&, SkBBoxHierarchy
*); | 19 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord&, SkBBoxHierarchy
*); |
19 | 20 |
20 // Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::D
raw. | 21 // Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::D
raw. |
21 void SkRecordDraw(const SkRecord&, SkCanvas*, const SkBBoxHierarchy*, SkDrawPict
ureCallback*); | 22 void SkRecordDraw(const SkRecord&, SkCanvas*, const SkBBoxHierarchy*, SkDrawPict
ureCallback*); |
22 | 23 |
23 // Draw a portion of an SkRecord into an SkCanvas while replacing clears with dr
awRects. | 24 // Draw a portion of an SkRecord into an SkCanvas while replacing clears with dr
awRects. |
24 // When drawing a portion of an SkRecord the CTM on the passed in canvas must be | 25 // When drawing a portion of an SkRecord the CTM on the passed in canvas must be |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 p.setColor(c.color); | 68 p.setColor(c.color); |
68 DrawRect drawRect(p, fClearRect); | 69 DrawRect drawRect(p, fClearRect); |
69 this->INHERITED::operator()(drawRect); | 70 this->INHERITED::operator()(drawRect); |
70 } | 71 } |
71 | 72 |
72 private: | 73 private: |
73 const SkRect fClearRect; | 74 const SkRect fClearRect; |
74 typedef Draw INHERITED; | 75 typedef Draw INHERITED; |
75 }; | 76 }; |
76 | 77 |
| 78 // This is an SkRecord visitor that fills an SkBBoxHierarchy. |
| 79 // |
| 80 // The interesting part here is how to calculate bounds for ops which don't |
| 81 // have intrinsic bounds. What is the bounds of a Save or a Translate? |
| 82 // |
| 83 // We answer this by thinking about a particular definition of bounds: if I |
| 84 // don't execute this op, pixels in this rectangle might draw incorrectly. So |
| 85 // the bounds of a Save, a Translate, a Restore, etc. are the union of the |
| 86 // bounds of Draw* ops that they might have an effect on. For any given |
| 87 // Save/Restore block, the bounds of the Save, the Restore, and any other |
| 88 // non-drawing ("control") ops inside are exactly the union of the bounds of |
| 89 // the drawing ops inside that block. |
| 90 // |
| 91 // To implement this, we keep a stack of active Save blocks. As we consume ops |
| 92 // inside the Save/Restore block, drawing ops are unioned with the bounds of |
| 93 // the block, and control ops are stashed away for later. When we finish the |
| 94 // block with a Restore, our bounds are complete, and we go back and fill them |
| 95 // in for all the control ops we stashed away. |
| 96 class FillBounds : SkNoncopyable { |
| 97 public: |
| 98 FillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHierarchy*
bbh); |
| 99 |
| 100 void setCurrentOp(unsigned currentOp) { fCurrentOp = currentOp; } |
| 101 void cleanUp(); |
| 102 |
| 103 template <typename T> void operator()(const T& op) { |
| 104 this->updateCTM(op); |
| 105 this->updateClipBounds(op); |
| 106 this->trackBounds(op); |
| 107 } |
| 108 |
| 109 // In FillBounds, SkRect are in local coordinates, Bounds are translated bac
k to identity space. |
| 110 typedef SkRect Bounds; |
| 111 |
| 112 unsigned currentOp() const { return fCurrentOp; } |
| 113 const SkMatrix& ctm() const { return *fCTM; } |
| 114 const Bounds& currentClipBounds() const { return fCurrentClipBounds; } |
| 115 |
| 116 Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const; |
| 117 const Bounds& getBounds(unsigned index) const { return fBounds[index]; } |
| 118 |
| 119 private: |
| 120 struct SaveBounds { |
| 121 int controlOps; // Number of control ops in this Save block, incl
uding the Save. |
| 122 Bounds bounds; // Bounds of everything in the block. |
| 123 const SkPaint* paint; // Unowned. If set, adjusts the bounds of all op
s in this block. |
| 124 }; |
| 125 |
| 126 static void AdjustTextForFontMetrics(SkRect* rect, const SkPaint& paint); |
| 127 |
| 128 template <typename T> void updateCTM(const T&) {} |
| 129 template <typename T> void updateClipBounds(const T&) {} |
| 130 template <typename T> void trackBounds(const T& op) { |
| 131 fBounds[fCurrentOp] = this->bounds(op); |
| 132 this->updateSaveBounds(fBounds[fCurrentOp]); |
| 133 } |
| 134 template <typename T> Bounds bounds(const T&) const; |
| 135 |
| 136 void updateClipBoundsForClipOp(const SkIRect& devBounds); |
| 137 |
| 138 void pushSaveBlock(const SkPaint* paint); |
| 139 Bounds popSaveBlock(); |
| 140 void pushControl(); |
| 141 void popControl(const Bounds& bounds); |
| 142 void updateSaveBounds(const Bounds& bounds); |
| 143 |
| 144 bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const; |
| 145 |
| 146 const unsigned int fNumRecords; |
| 147 |
| 148 // The BBH being filled in |
| 149 SkBBoxHierarchy* fBBH; |
| 150 |
| 151 // We do not guarantee anything for operations outside of the cull rect |
| 152 const SkRect fCullRect; |
| 153 |
| 154 // Conservative identity-space bounds for each op in the SkRecord. |
| 155 SkAutoTMalloc<Bounds> fBounds; |
| 156 |
| 157 // We walk fCurrentOp through the SkRecord, as we go using updateCTM() |
| 158 // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative |
| 159 // identity-space bounds of the current clip (fCurrentClipBounds). |
| 160 unsigned fCurrentOp; |
| 161 const SkMatrix* fCTM; |
| 162 Bounds fCurrentClipBounds; |
| 163 |
| 164 // Used to track the bounds of Save/Restore blocks and the control ops insid
e them. |
| 165 SkTDArray<SaveBounds> fSaveStack; |
| 166 SkTDArray<unsigned> fControlIndices; |
| 167 }; |
| 168 |
| 169 // Only Restore and SetMatrix change the CTM. |
| 170 template <> inline void FillBounds::updateCTM(const Restore& op) { fCTM = &op.
matrix; } |
| 171 template <> inline void FillBounds::updateCTM(const SetMatrix& op) { fCTM = &op.
matrix; } |
| 172 |
| 173 // Clip{Path,RRect,Rect,Region} obviously change the clip. They all know their
bounds already. |
| 174 template <> inline void FillBounds::updateClipBounds(const ClipPath& op) { thi
s->updateClipBoundsForClipOp(op.devBounds); } |
| 175 template <> inline void FillBounds::updateClipBounds(const ClipRRect& op) { thi
s->updateClipBoundsForClipOp(op.devBounds); } |
| 176 template <> inline void FillBounds::updateClipBounds(const ClipRect& op) { thi
s->updateClipBoundsForClipOp(op.devBounds); } |
| 177 template <> inline void FillBounds::updateClipBounds(const ClipRegion& op) { thi
s->updateClipBoundsForClipOp(op.devBounds); } |
| 178 |
| 179 // Restore holds the devBounds for the clip after the {save,saveLayer}/restore b
lock completes. |
| 180 template <> inline void FillBounds::updateClipBounds(const Restore& op) { |
| 181 // This is just like the clip ops above, but we need to skip the effects (if
any) of our |
| 182 // paired saveLayer (if it is one); it has not yet been popped off the save
stack. Our |
| 183 // devBounds reflect the state of the world after the saveLayer/restore bloc
k is done, |
| 184 // so they are not affected by the saveLayer's paint. |
| 185 const int kSavesToIgnore = 1; |
| 186 Bounds clip = SkRect::Make(op.devBounds); |
| 187 fCurrentClipBounds = |
| 188 this->adjustForSaveLayerPaints(&clip, kSavesToIgnore) ? clip : fCullRect
; |
| 189 } |
| 190 |
| 191 // We also take advantage of SaveLayer bounds when present to further cut the cl
ip down. |
| 192 template <> inline void FillBounds::updateClipBounds(const SaveLayer& op) { |
| 193 if (op.bounds) { |
| 194 // adjustAndMap() intersects these layer bounds with the previous clip f
or us. |
| 195 fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint); |
| 196 } |
| 197 } |
| 198 |
| 199 template <> inline void FillBounds::trackBounds(const Save&) { this->pu
shSaveBlock(NULL); } |
| 200 template <> inline void FillBounds::trackBounds(const SaveLayer& op) { this->pu
shSaveBlock(op.paint); } |
| 201 template <> inline void FillBounds::trackBounds(const Restore&) { fBounds[fCurre
ntOp] = this->popSaveBlock(); } |
| 202 |
| 203 template <> inline void FillBounds::trackBounds(const SetMatrix&) { this
->pushControl(); } |
| 204 template <> inline void FillBounds::trackBounds(const ClipRect&) { this
->pushControl(); } |
| 205 template <> inline void FillBounds::trackBounds(const ClipRRect&) { this
->pushControl(); } |
| 206 template <> inline void FillBounds::trackBounds(const ClipPath&) { this
->pushControl(); } |
| 207 template <> inline void FillBounds::trackBounds(const ClipRegion&) { this
->pushControl(); } |
| 208 template <> inline void FillBounds::trackBounds(const PushCull&) { this
->pushControl(); } |
| 209 template <> inline void FillBounds::trackBounds(const PopCull&) { this
->pushControl(); } |
| 210 template <> inline void FillBounds::trackBounds(const BeginCommentGroup&) { this
->pushControl(); } |
| 211 template <> inline void FillBounds::trackBounds(const AddComment&) { this
->pushControl(); } |
| 212 template <> inline void FillBounds::trackBounds(const EndCommentGroup&) { this
->pushControl(); } |
| 213 template <> inline void FillBounds::trackBounds(const DrawData&) { this
->pushControl(); } |
| 214 |
| 215 // FIXME: this method could use better bounds |
| 216 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawText&) const
{ return fCurrentClipBounds; } |
| 217 |
| 218 template <> inline FillBounds::Bounds FillBounds::bounds(const Clear&) const { r
eturn fCullRect; } // Ignores the clip. |
| 219 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawPaint&) const
{ return fCurrentClipBounds; } |
| 220 template <> inline FillBounds::Bounds FillBounds::bounds(const NoOp&) const { r
eturn Bounds::MakeEmpty(); } // NoOps don't draw. |
| 221 |
| 222 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawSprite& op) c
onst { |
| 223 // Ignores the matrix. |
| 224 const SkBitmap& bm = op.bitmap; |
| 225 return Bounds::MakeXYWH(SkIntToScalar(op.left), SkIntToScalar(op.top), |
| 226 SkIntToScalar(bm.width()), SkIntToScalar(bm.height()
)); |
| 227 } |
| 228 |
| 229 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawRect& op) con
st { return this->adjustAndMap(op.rect, &op.paint); } |
| 230 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawOval& op) con
st { return this->adjustAndMap(op.oval, &op.paint); } |
| 231 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawRRect& op) co
nst { |
| 232 return this->adjustAndMap(op.rrect.rect(), &op.paint); |
| 233 } |
| 234 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawDRRect& op) c
onst { |
| 235 return this->adjustAndMap(op.outer.rect(), &op.paint); |
| 236 } |
| 237 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawImage& op) co
nst { |
| 238 const SkImage* image = op.image; |
| 239 SkRect rect = SkRect::MakeXYWH(SkIntToScalar(op.left), SkIntToScalar(op.top)
, |
| 240 SkIntToScalar(image->width()), SkIntToScalar(
image->height())); |
| 241 |
| 242 return this->adjustAndMap(rect, op.paint); |
| 243 } |
| 244 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawImageRect& op
) const { |
| 245 return this->adjustAndMap(op.dst, op.paint); |
| 246 } |
| 247 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawBitmapRectToR
ect& op) const { |
| 248 return this->adjustAndMap(op.dst, op.paint); |
| 249 } |
| 250 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawBitmapNine& o
p) const { |
| 251 return this->adjustAndMap(op.dst, op.paint); |
| 252 } |
| 253 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawBitmap& op) c
onst { |
| 254 const SkBitmap& bm = op.bitmap; |
| 255 return this->adjustAndMap(SkRect::MakeXYWH(SkIntToScalar(op.left), |
| 256 SkIntToScalar(op.top), |
| 257 SkIntToScalar(bm.width()), |
| 258 SkIntToScalar(bm.height())), |
| 259 op.paint); |
| 260 } |
| 261 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawBitmapMatrix&
op) const { |
| 262 const SkBitmap& bm = op.bitmap; |
| 263 SkRect dst = SkRect::MakeWH(SkIntToScalar(bm.width()), |
| 264 SkIntToScalar(bm.height())); |
| 265 op.matrix.mapRect(&dst); |
| 266 return this->adjustAndMap(dst, op.paint); |
| 267 } |
| 268 |
| 269 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawPath& op) con
st { |
| 270 return op.path.isInverseFillType() ? fCurrentClipBounds |
| 271 : this->adjustAndMap(op.path.getBounds(),
&op.paint); |
| 272 } |
| 273 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawPoints& op) c
onst { |
| 274 SkRect dst; |
| 275 dst.set(op.pts, op.count); |
| 276 |
| 277 // Pad the bounding box a little to make sure hairline points' bounds aren't
empty. |
| 278 SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f); |
| 279 dst.outset(stroke/2, stroke/2); |
| 280 |
| 281 return this->adjustAndMap(dst, &op.paint); |
| 282 } |
| 283 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawPatch& op) co
nst { |
| 284 SkRect dst; |
| 285 dst.set(op.cubics, SkPatchUtils::kNumCtrlPts); |
| 286 return this->adjustAndMap(dst, &op.paint); |
| 287 } |
| 288 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawVertices& op)
const { |
| 289 SkRect dst; |
| 290 dst.set(op.vertices, op.vertexCount); |
| 291 return this->adjustAndMap(dst, &op.paint); |
| 292 } |
| 293 |
| 294 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawPicture& op)
const { |
| 295 SkRect dst = op.picture->cullRect(); |
| 296 if (op.matrix) { |
| 297 op.matrix->mapRect(&dst); |
| 298 } |
| 299 return this->adjustAndMap(dst, op.paint); |
| 300 } |
| 301 |
| 302 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawPosText& op)
const { |
| 303 const int N = op.paint.countText(op.text, op.byteLength); |
| 304 if (N == 0) { |
| 305 return Bounds::MakeEmpty(); |
| 306 } |
| 307 |
| 308 SkRect dst; |
| 309 dst.set(op.pos, N); |
| 310 AdjustTextForFontMetrics(&dst, op.paint); |
| 311 return this->adjustAndMap(dst, &op.paint); |
| 312 } |
| 313 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawPosTextH& op)
const { |
| 314 const int N = op.paint.countText(op.text, op.byteLength); |
| 315 if (N == 0) { |
| 316 return Bounds::MakeEmpty(); |
| 317 } |
| 318 |
| 319 SkScalar left = op.xpos[0], right = op.xpos[0]; |
| 320 for (int i = 1; i < N; i++) { |
| 321 left = SkMinScalar(left, op.xpos[i]); |
| 322 right = SkMaxScalar(right, op.xpos[i]); |
| 323 } |
| 324 SkRect dst = { left, op.y, right, op.y }; |
| 325 AdjustTextForFontMetrics(&dst, op.paint); |
| 326 return this->adjustAndMap(dst, &op.paint); |
| 327 } |
| 328 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawTextOnPath& o
p) const { |
| 329 SkRect dst = op.path.getBounds(); |
| 330 |
| 331 // Pad all sides by the maximum padding in any direction we'd normally apply
. |
| 332 SkRect pad = { 0, 0, 0, 0}; |
| 333 AdjustTextForFontMetrics(&pad, op.paint); |
| 334 |
| 335 // That maximum padding happens to always be the right pad today. |
| 336 SkASSERT(pad.fLeft == -pad.fRight); |
| 337 SkASSERT(pad.fTop == -pad.fBottom); |
| 338 SkASSERT(pad.fRight > pad.fBottom); |
| 339 dst.outset(pad.fRight, pad.fRight); |
| 340 |
| 341 return this->adjustAndMap(dst, &op.paint); |
| 342 } |
| 343 |
| 344 template <> inline FillBounds::Bounds FillBounds::bounds(const DrawTextBlob& op)
const { |
| 345 SkRect dst = op.blob->bounds(); |
| 346 dst.offset(op.x, op.y); |
| 347 return this->adjustAndMap(dst, &op.paint); |
| 348 } |
| 349 |
77 } // namespace SkRecords | 350 } // namespace SkRecords |
78 | 351 |
79 #endif//SkRecordDraw_DEFINED | 352 #endif//SkRecordDraw_DEFINED |
OLD | NEW |