Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / core / SkRecordDraw.cpp
1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7
8 #include "SkRecordDraw.h"
9 #include "SkPatchUtils.h"
10
11 void SkRecordDraw(const SkRecord& record,
12                   SkCanvas* canvas,
13                   const SkBBoxHierarchy* bbh,
14                   SkDrawPictureCallback* callback) {
15     SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
16
17     if (bbh) {
18         // Draw only ops that affect pixels in the canvas's current clip.
19         // The SkRecord and BBH were recorded in identity space.  This canvas
20         // is not necessarily in that same space.  getClipBounds() returns us
21         // this canvas' clip bounds transformed back into identity space, which
22         // lets us query the BBH.
23         SkRect query;
24         if (!canvas->getClipBounds(&query)) {
25             return;
26         }
27
28         SkTDArray<unsigned> ops;
29         bbh->search(query, &ops);
30
31         SkRecords::Draw draw(canvas);
32         for (int i = 0; i < ops.count(); i++) {
33             if (callback && callback->abortDrawing()) {
34                 return;
35             }
36             // This visit call uses the SkRecords::Draw::operator() to call
37             // methods on the |canvas|, wrapped by methods defined with the
38             // DRAW() macro.
39             record.visit<void>(ops[i], draw);
40         }
41     } else {
42         // Draw all ops.
43         SkRecords::Draw draw(canvas);
44         for (unsigned i = 0; i < record.count(); i++) {
45             if (callback && callback->abortDrawing()) {
46                 return;
47             }
48             // This visit call uses the SkRecords::Draw::operator() to call
49             // methods on the |canvas|, wrapped by methods defined with the
50             // DRAW() macro.
51             record.visit<void>(i, draw);
52         }
53     }
54 }
55
56 void SkRecordPartialDraw(const SkRecord& record,
57                          SkCanvas* canvas,
58                          const SkRect& clearRect,
59                          unsigned start, unsigned stop,
60                          const SkMatrix& initialCTM) {
61     SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
62
63     stop = SkTMin(stop, record.count());
64     SkRecords::PartialDraw draw(canvas, clearRect, initialCTM);
65     for (unsigned i = start; i < stop; i++) {
66         record.visit<void>(i, draw);
67     }
68 }
69
70 namespace SkRecords {
71
72 // FIXME: SkBitmaps are stateful, so we need to copy them to play back in multiple threads.
73 static SkBitmap shallow_copy(const SkBitmap& bitmap) {
74     return bitmap;
75 }
76
77 // NoOps draw nothing.
78 template <> void Draw::draw(const NoOp&) {}
79
80 #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
81 DRAW(Restore, restore());
82 DRAW(Save, save());
83 DRAW(SaveLayer, saveLayer(r.bounds, r.paint, r.flags));
84 DRAW(PopCull, popCull());
85 DRAW(PushCull, pushCull(r.rect));
86 DRAW(Clear, clear(r.color));
87 DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
88
89 DRAW(ClipPath, clipPath(r.path, r.op, r.doAA));
90 DRAW(ClipRRect, clipRRect(r.rrect, r.op, r.doAA));
91 DRAW(ClipRect, clipRect(r.rect, r.op, r.doAA));
92 DRAW(ClipRegion, clipRegion(r.region, r.op));
93
94 DRAW(BeginCommentGroup, beginCommentGroup(r.description));
95 DRAW(AddComment, addComment(r.key, r.value));
96 DRAW(EndCommentGroup, endCommentGroup());
97
98 DRAW(DrawBitmap, drawBitmap(shallow_copy(r.bitmap), r.left, r.top, r.paint));
99 DRAW(DrawBitmapMatrix, drawBitmapMatrix(shallow_copy(r.bitmap), r.matrix, r.paint));
100 DRAW(DrawBitmapNine, drawBitmapNine(shallow_copy(r.bitmap), r.center, r.dst, r.paint));
101 DRAW(DrawBitmapRectToRect,
102         drawBitmapRectToRect(shallow_copy(r.bitmap), r.src, r.dst, r.paint, r.flags));
103 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint));
104 DRAW(DrawImage, drawImage(r.image, r.left, r.top, r.paint));
105 DRAW(DrawImageRect, drawImageRect(r.image, r.src, r.dst, r.paint));
106 DRAW(DrawOval, drawOval(r.oval, r.paint));
107 DRAW(DrawPaint, drawPaint(r.paint));
108 DRAW(DrawPath, drawPath(r.path, r.paint));
109 DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.xmode, r.paint));
110 DRAW(DrawPicture, drawPicture(r.picture, r.matrix, r.paint));
111 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint));
112 DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint));
113 DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint));
114 DRAW(DrawRRect, drawRRect(r.rrect, r.paint));
115 DRAW(DrawRect, drawRect(r.rect, r.paint));
116 DRAW(DrawSprite, drawSprite(shallow_copy(r.bitmap), r.left, r.top, r.paint));
117 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint));
118 DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint));
119 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.paint));
120 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.colors,
121                                 r.xmode.get(), r.indices, r.indexCount, r.paint));
122 DRAW(DrawData, drawData(r.data, r.length));
123 #undef DRAW
124
125 // This is an SkRecord visitor that fills an SkBBoxHierarchy.
126 //
127 // The interesting part here is how to calculate bounds for ops which don't
128 // have intrinsic bounds.  What is the bounds of a Save or a Translate?
129 //
130 // We answer this by thinking about a particular definition of bounds: if I
131 // don't execute this op, pixels in this rectangle might draw incorrectly.  So
132 // the bounds of a Save, a Translate, a Restore, etc. are the union of the
133 // bounds of Draw* ops that they might have an effect on.  For any given
134 // Save/Restore block, the bounds of the Save, the Restore, and any other
135 // non-drawing ("control") ops inside are exactly the union of the bounds of
136 // the drawing ops inside that block.
137 //
138 // To implement this, we keep a stack of active Save blocks.  As we consume ops
139 // inside the Save/Restore block, drawing ops are unioned with the bounds of
140 // the block, and control ops are stashed away for later.  When we finish the
141 // block with a Restore, our bounds are complete, and we go back and fill them
142 // in for all the control ops we stashed away.
143 class FillBounds : SkNoncopyable {
144 public:
145     FillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHierarchy* bbh) 
146         : fCullRect(cullRect)
147         , fBounds(record.count()) {
148         // Calculate bounds for all ops.  This won't go quite in order, so we'll need
149         // to store the bounds separately then feed them in to the BBH later in order.
150         fCTM = &SkMatrix::I();
151         fCurrentClipBounds = fCullRect;
152         for (fCurrentOp = 0; fCurrentOp < record.count(); fCurrentOp++) {
153             record.visit<void>(fCurrentOp, *this);
154         }
155
156         // If we have any lingering unpaired Saves, simulate restores to make
157         // sure all ops in those Save blocks have their bounds calculated.
158         while (!fSaveStack.isEmpty()) {
159             this->popSaveBlock();
160         }
161
162         // Any control ops not part of any Save/Restore block draw everywhere.
163         while (!fControlIndices.isEmpty()) {
164             this->popControl(fCullRect);
165         }
166
167         // Finally feed all stored bounds into the BBH.  They'll be returned in this order.
168         SkASSERT(bbh);
169         bbh->insert(&fBounds, record.count());
170     }
171
172     template <typename T> void operator()(const T& op) {
173         this->updateCTM(op);
174         this->updateClipBounds(op);
175         this->trackBounds(op);
176     }
177
178 private:
179     // In this file, SkRect are in local coordinates, Bounds are translated back to identity space.
180     typedef SkRect Bounds;
181
182     struct SaveBounds {
183         int controlOps;        // Number of control ops in this Save block, including the Save.
184         Bounds bounds;         // Bounds of everything in the block.
185         const SkPaint* paint;  // Unowned.  If set, adjusts the bounds of all ops in this block.
186     };
187
188     // Only Restore and SetMatrix change the CTM.
189     template <typename T> void updateCTM(const T&) {}
190     void updateCTM(const Restore& op)   { fCTM = &op.matrix; }
191     void updateCTM(const SetMatrix& op) { fCTM = &op.matrix; }
192
193     // Most ops don't change the clip.
194     template <typename T> void updateClipBounds(const T&) {}
195
196     // Clip{Path,RRect,Rect,Region} obviously change the clip.  They all know their bounds already.
197     void updateClipBounds(const ClipPath&   op) { this->updateClipBoundsForClipOp(op.devBounds); }
198     void updateClipBounds(const ClipRRect&  op) { this->updateClipBoundsForClipOp(op.devBounds); }
199     void updateClipBounds(const ClipRect&   op) { this->updateClipBoundsForClipOp(op.devBounds); }
200     void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipOp(op.devBounds); }
201
202     // The bounds of clip ops need to be adjusted for the paints of saveLayers they're inside.
203     void updateClipBoundsForClipOp(const SkIRect& devBounds) {
204         Bounds clip = SkRect::Make(devBounds);
205         // We don't call adjustAndMap() because as its last step it would intersect the adjusted
206         // clip bounds with the previous clip, exactly what we can't do when the clip grows.
207         fCurrentClipBounds = this->adjustForSaveLayerPaints(&clip) ? clip : fCullRect;
208     }
209
210     // Restore holds the devBounds for the clip after the {save,saveLayer}/restore block completes.
211     void updateClipBounds(const Restore& op) {
212         // This is just like the clip ops above, but we need to skip the effects (if any) of our
213         // paired saveLayer (if it is one); it has not yet been popped off the save stack.  Our
214         // devBounds reflect the state of the world after the saveLayer/restore block is done,
215         // so they are not affected by the saveLayer's paint.
216         const int kSavesToIgnore = 1;
217         Bounds clip = SkRect::Make(op.devBounds);
218         fCurrentClipBounds =
219             this->adjustForSaveLayerPaints(&clip, kSavesToIgnore) ? clip : fCullRect;
220     }
221
222     // We also take advantage of SaveLayer bounds when present to further cut the clip down.
223     void updateClipBounds(const SaveLayer& op)  {
224         if (op.bounds) {
225             // adjustAndMap() intersects these layer bounds with the previous clip for us.
226             fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint);
227         }
228     }
229
230     // The bounds of these ops must be calculated when we hit the Restore
231     // from the bounds of the ops in the same Save block.
232     void trackBounds(const Save&)          { this->pushSaveBlock(NULL); }
233     void trackBounds(const SaveLayer& op)  { this->pushSaveBlock(op.paint); }
234     void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); }
235
236     void trackBounds(const SetMatrix&)         { this->pushControl(); }
237     void trackBounds(const ClipRect&)          { this->pushControl(); }
238     void trackBounds(const ClipRRect&)         { this->pushControl(); }
239     void trackBounds(const ClipPath&)          { this->pushControl(); }
240     void trackBounds(const ClipRegion&)        { this->pushControl(); }
241     void trackBounds(const PushCull&)          { this->pushControl(); }
242     void trackBounds(const PopCull&)           { this->pushControl(); }
243     void trackBounds(const BeginCommentGroup&) { this->pushControl(); }
244     void trackBounds(const AddComment&)        { this->pushControl(); }
245     void trackBounds(const EndCommentGroup&)   { this->pushControl(); }
246     void trackBounds(const DrawData&)          { this->pushControl(); }
247
248     // For all other ops, we can calculate and store the bounds directly now.
249     template <typename T> void trackBounds(const T& op) {
250         fBounds[fCurrentOp] = this->bounds(op);
251         this->updateSaveBounds(fBounds[fCurrentOp]);
252     }
253
254     void pushSaveBlock(const SkPaint* paint) {
255         // Starting a new Save block.  Push a new entry to represent that.
256         SaveBounds sb;
257         sb.controlOps = 0;
258         // If the paint affects transparent black, the bound shouldn't be smaller
259         // than the current clip bounds.
260         sb.bounds =
261             PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty();
262         sb.paint = paint;
263
264         fSaveStack.push(sb);
265         this->pushControl();
266     }
267
268     static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
269         if (paint) {
270             // FIXME: this is very conservative
271             if (paint->getImageFilter() || paint->getColorFilter()) {
272                 return true;
273             }
274
275             // Unusual Xfermodes require us to process a saved layer
276             // even with operations outisde the clip.
277             // For example, DstIn is used by masking layers.
278             // https://code.google.com/p/skia/issues/detail?id=1291
279             // https://crbug.com/401593
280             SkXfermode* xfermode = paint->getXfermode();
281             SkXfermode::Mode mode;
282             // SrcOver is ok, and is also the common case with a NULL xfermode.
283             // So we should make that the fast path and bypass the mode extraction
284             // and test.
285             if (xfermode && xfermode->asMode(&mode)) {
286                 switch (mode) {
287                     // For each of the following transfer modes, if the source
288                     // alpha is zero (our transparent black), the resulting
289                     // blended alpha is not necessarily equal to the original
290                     // destination alpha.
291                     case SkXfermode::kClear_Mode:
292                     case SkXfermode::kSrc_Mode:
293                     case SkXfermode::kSrcIn_Mode:
294                     case SkXfermode::kDstIn_Mode:
295                     case SkXfermode::kSrcOut_Mode:
296                     case SkXfermode::kDstATop_Mode:
297                     case SkXfermode::kModulate_Mode:
298                         return true;
299                         break;
300                     default:
301                         break;
302                 }
303             }
304         }
305         return false;
306     }
307
308     Bounds popSaveBlock() {
309         // We're done the Save block.  Apply the block's bounds to all control ops inside it.
310         SaveBounds sb;
311         fSaveStack.pop(&sb);
312
313         while (sb.controlOps --> 0) {
314             this->popControl(sb.bounds);
315         }
316
317         // This whole Save block may be part another Save block.
318         this->updateSaveBounds(sb.bounds);
319
320         // If called from a real Restore (not a phony one for balance), it'll need the bounds.
321         return sb.bounds;
322     }
323
324     void pushControl() {
325         fControlIndices.push(fCurrentOp);
326         if (!fSaveStack.isEmpty()) {
327             fSaveStack.top().controlOps++;
328         }
329     }
330
331     void popControl(const Bounds& bounds) {
332         fBounds[fControlIndices.top()] = bounds;
333         fControlIndices.pop();
334     }
335
336     void updateSaveBounds(const Bounds& bounds) {
337         // If we're in a Save block, expand its bounds to cover these bounds too.
338         if (!fSaveStack.isEmpty()) {
339             fSaveStack.top().bounds.join(bounds);
340         }
341     }
342
343     // FIXME: this method could use better bounds
344     Bounds bounds(const DrawText&) const { return fCurrentClipBounds; }
345
346     Bounds bounds(const Clear&) const { return fCullRect; }             // Ignores the clip.
347     Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; }
348     Bounds bounds(const NoOp&)  const { return Bounds::MakeEmpty(); }    // NoOps don't draw.
349
350     Bounds bounds(const DrawSprite& op) const {
351         const SkBitmap& bm = op.bitmap;
352         return Bounds::MakeXYWH(op.left, op.top, bm.width(), bm.height());  // Ignores the matrix.
353     }
354
355     Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); }
356     Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); }
357     Bounds bounds(const DrawRRect& op) const {
358         return this->adjustAndMap(op.rrect.rect(), &op.paint);
359     }
360     Bounds bounds(const DrawDRRect& op) const {
361         return this->adjustAndMap(op.outer.rect(), &op.paint);
362     }
363     Bounds bounds(const DrawImage& op) const {
364         const SkImage* image = op.image;
365         SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height());
366
367         return this->adjustAndMap(rect, op.paint);
368     }
369     Bounds bounds(const DrawImageRect& op) const {
370         return this->adjustAndMap(op.dst, op.paint);
371     }
372     Bounds bounds(const DrawBitmapRectToRect& op) const {
373         return this->adjustAndMap(op.dst, op.paint);
374     }
375     Bounds bounds(const DrawBitmapNine& op) const {
376         return this->adjustAndMap(op.dst, op.paint);
377     }
378     Bounds bounds(const DrawBitmap& op) const {
379         const SkBitmap& bm = op.bitmap;
380         return this->adjustAndMap(SkRect::MakeXYWH(op.left, op.top, bm.width(), bm.height()),
381                                   op.paint);
382     }
383     Bounds bounds(const DrawBitmapMatrix& op) const {
384         const SkBitmap& bm = op.bitmap;
385         SkRect dst = SkRect::MakeWH(bm.width(), bm.height());
386         op.matrix.mapRect(&dst);
387         return this->adjustAndMap(dst, op.paint);
388     }
389
390     Bounds bounds(const DrawPath& op) const {
391         return op.path.isInverseFillType() ? fCurrentClipBounds
392                                            : this->adjustAndMap(op.path.getBounds(), &op.paint);
393     }
394     Bounds bounds(const DrawPoints& op) const {
395         SkRect dst;
396         dst.set(op.pts, op.count);
397
398         // Pad the bounding box a little to make sure hairline points' bounds aren't empty.
399         SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f);
400         dst.outset(stroke/2, stroke/2);
401
402         return this->adjustAndMap(dst, &op.paint);
403     }
404     Bounds bounds(const DrawPatch& op) const {
405         SkRect dst;
406         dst.set(op.cubics, SkPatchUtils::kNumCtrlPts);
407         return this->adjustAndMap(dst, &op.paint);
408     }
409     Bounds bounds(const DrawVertices& op) const {
410         SkRect dst;
411         dst.set(op.vertices, op.vertexCount);
412         return this->adjustAndMap(dst, &op.paint);
413     }
414
415     Bounds bounds(const DrawPicture& op) const {
416         SkRect dst = op.picture->cullRect();
417         if (op.matrix) {
418             op.matrix->mapRect(&dst);
419         }
420         return this->adjustAndMap(dst, op.paint);
421     }
422
423     Bounds bounds(const DrawPosText& op) const {
424         const int N = op.paint.countText(op.text, op.byteLength);
425         if (N == 0) {
426             return Bounds::MakeEmpty();
427         }
428
429         SkRect dst;
430         dst.set(op.pos, N);
431         AdjustTextForFontMetrics(&dst, op.paint);
432         return this->adjustAndMap(dst, &op.paint);
433     }
434     Bounds bounds(const DrawPosTextH& op) const {
435         const int N = op.paint.countText(op.text, op.byteLength);
436         if (N == 0) {
437             return Bounds::MakeEmpty();
438         }
439
440         SkScalar left = op.xpos[0], right = op.xpos[0];
441         for (int i = 1; i < N; i++) {
442             left  = SkMinScalar(left,  op.xpos[i]);
443             right = SkMaxScalar(right, op.xpos[i]);
444         }
445         SkRect dst = { left, op.y, right, op.y };
446         AdjustTextForFontMetrics(&dst, op.paint);
447         return this->adjustAndMap(dst, &op.paint);
448     }
449     Bounds bounds(const DrawTextOnPath& op) const {
450         SkRect dst = op.path.getBounds();
451
452         // Pad all sides by the maximum padding in any direction we'd normally apply.
453         SkRect pad = { 0, 0, 0, 0};
454         AdjustTextForFontMetrics(&pad, op.paint);
455
456         // That maximum padding happens to always be the right pad today.
457         SkASSERT(pad.fLeft == -pad.fRight);
458         SkASSERT(pad.fTop  == -pad.fBottom);
459         SkASSERT(pad.fRight > pad.fBottom);
460         dst.outset(pad.fRight, pad.fRight);
461
462         return this->adjustAndMap(dst, &op.paint);
463     }
464
465     Bounds bounds(const DrawTextBlob& op) const {
466         SkRect dst = op.blob->bounds();
467         dst.offset(op.x, op.y);
468         return this->adjustAndMap(dst, &op.paint);
469     }
470
471     static void AdjustTextForFontMetrics(SkRect* rect, const SkPaint& paint) {
472 #ifdef SK_DEBUG
473         SkRect correct = *rect;
474 #endif
475         // crbug.com/373785 ~~> xPad = 4x yPad
476         // crbug.com/424824 ~~> bump yPad from 2x text size to 2.5x
477         const SkScalar yPad = 2.5f * paint.getTextSize(),
478                        xPad = 4.0f * yPad;
479         rect->outset(xPad, yPad);
480 #ifdef SK_DEBUG
481         SkPaint::FontMetrics metrics;
482         paint.getFontMetrics(&metrics);
483         correct.fLeft   += metrics.fXMin;
484         correct.fTop    += metrics.fTop;
485         correct.fRight  += metrics.fXMax;
486         correct.fBottom += metrics.fBottom;
487         // See skia:2862 for why we ignore small text sizes.
488         SkASSERTF(paint.getTextSize() < 0.001f || rect->contains(correct),
489                   "%f %f %f %f vs. %f %f %f %f\n",
490                   -xPad, -yPad, +xPad, +yPad,
491                   metrics.fXMin, metrics.fTop, metrics.fXMax, metrics.fBottom);
492 #endif
493     }
494
495     // Returns true if rect was meaningfully adjusted for the effects of paint,
496     // false if the paint could affect the rect in unknown ways.
497     static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) {
498         if (paint) {
499             if (paint->canComputeFastBounds()) {
500                 *rect = paint->computeFastBounds(*rect, rect);
501                 return true;
502             }
503             return false;
504         }
505         return true;
506     }
507
508     bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const {
509         for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) {
510             if (!AdjustForPaint(fSaveStack[i].paint, rect)) {
511                 return false;
512             }
513         }
514         return true;
515     }
516
517     // Adjust rect for all paints that may affect its geometry, then map it to identity space.
518     Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const {
519         // Inverted rectangles really confuse our BBHs.
520         rect.sort();
521
522         // Adjust the rect for its own paint.
523         if (!AdjustForPaint(paint, &rect)) {
524             // The paint could do anything to our bounds.  The only safe answer is the current clip.
525             return fCurrentClipBounds;
526         }
527
528         // Adjust rect for all the paints from the SaveLayers we're inside.
529         if (!this->adjustForSaveLayerPaints(&rect)) {
530             // Same deal as above.
531             return fCurrentClipBounds;
532         }
533
534         // Map the rect back to identity space.
535         fCTM->mapRect(&rect);
536
537         // Nothing can draw outside the current clip.
538         // (Only bounded ops call into this method, so oddballs like Clear don't matter here.)
539         rect.intersect(fCurrentClipBounds);
540         return rect;
541     }
542
543     // We do not guarantee anything for operations outside of the cull rect
544     const SkRect fCullRect;
545
546     // Conservative identity-space bounds for each op in the SkRecord.
547     SkAutoTMalloc<Bounds> fBounds;
548
549     // We walk fCurrentOp through the SkRecord, as we go using updateCTM()
550     // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative
551     // identity-space bounds of the current clip (fCurrentClipBounds).
552     unsigned fCurrentOp;
553     const SkMatrix* fCTM;
554     Bounds fCurrentClipBounds;
555
556     // Used to track the bounds of Save/Restore blocks and the control ops inside them.
557     SkTDArray<SaveBounds> fSaveStack;
558     SkTDArray<unsigned>   fControlIndices;
559 };
560
561 }  // namespace SkRecords
562
563 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHierarchy* bbh) {
564     SkRecords::FillBounds(cullRect, record, bbh);
565 }