LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkRecordDraw.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 304 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 235 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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           0 : void SkRecordDraw(const SkRecord& record,
      12             :                   SkCanvas* canvas,
      13             :                   SkPicture const* const drawablePicts[],
      14             :                   SkDrawable* const drawables[],
      15             :                   int drawableCount,
      16             :                   const SkBBoxHierarchy* bbh,
      17             :                   SkPicture::AbortCallback* callback) {
      18           0 :     SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
      19             : 
      20           0 :     if (bbh) {
      21             :         // Draw only ops that affect pixels in the canvas's current clip.
      22             :         // The SkRecord and BBH were recorded in identity space.  This canvas
      23             :         // is not necessarily in that same space.  getLocalClipBounds() returns us
      24             :         // this canvas' clip bounds transformed back into identity space, which
      25             :         // lets us query the BBH.
      26           0 :         SkRect query = canvas->getLocalClipBounds();
      27             : 
      28           0 :         SkTDArray<int> ops;
      29           0 :         bbh->search(query, &ops);
      30             : 
      31           0 :         SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount);
      32           0 :         for (int i = 0; i < ops.count(); i++) {
      33           0 :             if (callback && callback->abort()) {
      34           0 :                 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           0 :             record.visit(ops[i], draw);
      40             :         }
      41             :     } else {
      42             :         // Draw all ops.
      43           0 :         SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount);
      44           0 :         for (int i = 0; i < record.count(); i++) {
      45           0 :             if (callback && callback->abort()) {
      46           0 :                 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           0 :             record.visit(i, draw);
      52             :         }
      53             :     }
      54             : }
      55             : 
      56           0 : void SkRecordPartialDraw(const SkRecord& record, SkCanvas* canvas,
      57             :                          SkPicture const* const drawablePicts[], int drawableCount,
      58             :                          int start, int stop,
      59             :                          const SkMatrix& initialCTM) {
      60           0 :     SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
      61             : 
      62           0 :     stop = SkTMin(stop, record.count());
      63           0 :     SkRecords::Draw draw(canvas, drawablePicts, nullptr, drawableCount, &initialCTM);
      64           0 :     for (int i = start; i < stop; i++) {
      65           0 :         record.visit(i, draw);
      66             :     }
      67           0 : }
      68             : 
      69             : namespace SkRecords {
      70             : 
      71             : // NoOps draw nothing.
      72           0 : template <> void Draw::draw(const NoOp&) {}
      73             : 
      74             : #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
      75           0 : DRAW(Restore, restore());
      76           0 : DRAW(Save, save());
      77           0 : DRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds,
      78             :                                                  r.paint,
      79             :                                                  r.backdrop.get(),
      80             :                                                  r.saveLayerFlags)));
      81           0 : DRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
      82           0 : DRAW(Concat, concat(r.matrix));
      83           0 : DRAW(Translate, translate(r.dx, r.dy));
      84             : 
      85           0 : DRAW(ClipPath, clipPath(r.path, r.opAA.op(), r.opAA.aa()));
      86           0 : DRAW(ClipRRect, clipRRect(r.rrect, r.opAA.op(), r.opAA.aa()));
      87           0 : DRAW(ClipRect, clipRect(r.rect, r.opAA.op(), r.opAA.aa()));
      88           0 : DRAW(ClipRegion, clipRegion(r.region, r.op));
      89             : 
      90             : #ifdef SK_EXPERIMENTAL_SHADOWING
      91             : DRAW(TranslateZ, SkCanvas::translateZ(r.z));
      92             : #else
      93           0 : template <> void Draw::draw(const TranslateZ& r) { }
      94             : #endif
      95             : 
      96           0 : DRAW(DrawArc, drawArc(r.oval, r.startAngle, r.sweepAngle, r.useCenter, r.paint));
      97           0 : DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint));
      98           0 : DRAW(DrawImage, drawImage(r.image.get(), r.left, r.top, r.paint));
      99             : 
     100           0 : template <> void Draw::draw(const DrawImageLattice& r) {
     101             :     SkCanvas::Lattice lattice;
     102           0 :     lattice.fXCount = r.xCount;
     103           0 :     lattice.fXDivs = r.xDivs;
     104           0 :     lattice.fYCount = r.yCount;
     105           0 :     lattice.fYDivs = r.yDivs;
     106           0 :     lattice.fFlags = (0 == r.flagCount) ? nullptr : r.flags;
     107           0 :     lattice.fBounds = &r.src;
     108           0 :     fCanvas->drawImageLattice(r.image.get(), lattice, r.dst, r.paint);
     109           0 : }
     110             : 
     111           0 : DRAW(DrawImageRect, legacy_drawImageRect(r.image.get(), r.src, r.dst, r.paint, r.constraint));
     112           0 : DRAW(DrawImageNine, drawImageNine(r.image.get(), r.center, r.dst, r.paint));
     113           0 : DRAW(DrawOval, drawOval(r.oval, r.paint));
     114           0 : DRAW(DrawPaint, drawPaint(r.paint));
     115           0 : DRAW(DrawPath, drawPath(r.path, r.paint));
     116           0 : DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.bmode, r.paint));
     117           0 : DRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint));
     118             : 
     119             : #ifdef SK_EXPERIMENTAL_SHADOWING
     120             : DRAW(DrawShadowedPicture, drawShadowedPicture(r.picture.get(), &r.matrix, r.paint, r.params));
     121             : #else
     122           0 : template <> void Draw::draw(const DrawShadowedPicture& r) { }
     123             : #endif
     124             : 
     125           0 : DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint));
     126           0 : DRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint));
     127           0 : DRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint));
     128           0 : DRAW(DrawRRect, drawRRect(r.rrect, r.paint));
     129           0 : DRAW(DrawRect, drawRect(r.rect, r.paint));
     130           0 : DRAW(DrawRegion, drawRegion(r.region, r.paint));
     131           0 : DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint));
     132           0 : DRAW(DrawTextBlob, drawTextBlob(r.blob.get(), r.x, r.y, r.paint));
     133           0 : DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint));
     134           0 : DRAW(DrawTextRSXform, drawTextRSXform(r.text, r.byteLength, r.xforms, r.cull, r.paint));
     135           0 : DRAW(DrawAtlas, drawAtlas(r.atlas.get(),
     136             :                           r.xforms, r.texs, r.colors, r.count, r.mode, r.cull, r.paint));
     137           0 : DRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint));
     138           0 : DRAW(DrawAnnotation, drawAnnotation(r.rect, r.key.c_str(), r.value.get()));
     139             : #undef DRAW
     140             : 
     141           0 : template <> void Draw::draw(const DrawDrawable& r) {
     142           0 :     SkASSERT(r.index >= 0);
     143           0 :     SkASSERT(r.index < fDrawableCount);
     144           0 :     if (fDrawables) {
     145           0 :         SkASSERT(nullptr == fDrawablePicts);
     146           0 :         fCanvas->drawDrawable(fDrawables[r.index], r.matrix);
     147             :     } else {
     148           0 :         fCanvas->drawPicture(fDrawablePicts[r.index], r.matrix, nullptr);
     149             :     }
     150           0 : }
     151             : 
     152             : // This is an SkRecord visitor that fills an SkBBoxHierarchy.
     153             : //
     154             : // The interesting part here is how to calculate bounds for ops which don't
     155             : // have intrinsic bounds.  What is the bounds of a Save or a Translate?
     156             : //
     157             : // We answer this by thinking about a particular definition of bounds: if I
     158             : // don't execute this op, pixels in this rectangle might draw incorrectly.  So
     159             : // the bounds of a Save, a Translate, a Restore, etc. are the union of the
     160             : // bounds of Draw* ops that they might have an effect on.  For any given
     161             : // Save/Restore block, the bounds of the Save, the Restore, and any other
     162             : // non-drawing ("control") ops inside are exactly the union of the bounds of
     163             : // the drawing ops inside that block.
     164             : //
     165             : // To implement this, we keep a stack of active Save blocks.  As we consume ops
     166             : // inside the Save/Restore block, drawing ops are unioned with the bounds of
     167             : // the block, and control ops are stashed away for later.  When we finish the
     168             : // block with a Restore, our bounds are complete, and we go back and fill them
     169             : // in for all the control ops we stashed away.
     170           0 : class FillBounds : SkNoncopyable {
     171             : public:
     172           0 :     FillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[])
     173           0 :         : fNumRecords(record.count())
     174             :         , fCullRect(cullRect)
     175           0 :         , fBounds(bounds) {
     176           0 :         fCTM = SkMatrix::I();
     177           0 :         fCurrentClipBounds = fCullRect;
     178           0 :     }
     179             : 
     180           0 :     void cleanUp() {
     181             :         // If we have any lingering unpaired Saves, simulate restores to make
     182             :         // sure all ops in those Save blocks have their bounds calculated.
     183           0 :         while (!fSaveStack.isEmpty()) {
     184           0 :             this->popSaveBlock();
     185             :         }
     186             : 
     187             :         // Any control ops not part of any Save/Restore block draw everywhere.
     188           0 :         while (!fControlIndices.isEmpty()) {
     189           0 :             this->popControl(fCullRect);
     190             :         }
     191           0 :     }
     192             : 
     193           0 :     void setCurrentOp(int currentOp) { fCurrentOp = currentOp; }
     194             : 
     195             : 
     196           0 :     template <typename T> void operator()(const T& op) {
     197           0 :         this->updateCTM(op);
     198           0 :         this->updateClipBounds(op);
     199           0 :         this->trackBounds(op);
     200           0 :     }
     201             : 
     202             :     // In this file, SkRect are in local coordinates, Bounds are translated back to identity space.
     203             :     typedef SkRect Bounds;
     204             : 
     205             :     int currentOp() const { return fCurrentOp; }
     206             :     const SkMatrix& ctm() const { return fCTM; }
     207             :     const Bounds& getBounds(int index) const { return fBounds[index]; }
     208             : 
     209             :     // Adjust rect for all paints that may affect its geometry, then map it to identity space.
     210           0 :     Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const {
     211             :         // Inverted rectangles really confuse our BBHs.
     212           0 :         rect.sort();
     213             : 
     214             :         // Adjust the rect for its own paint.
     215           0 :         if (!AdjustForPaint(paint, &rect)) {
     216             :             // The paint could do anything to our bounds.  The only safe answer is the current clip.
     217           0 :             return fCurrentClipBounds;
     218             :         }
     219             : 
     220             :         // Adjust rect for all the paints from the SaveLayers we're inside.
     221           0 :         if (!this->adjustForSaveLayerPaints(&rect)) {
     222             :             // Same deal as above.
     223           0 :             return fCurrentClipBounds;
     224             :         }
     225             : 
     226             :         // Map the rect back to identity space.
     227           0 :         fCTM.mapRect(&rect);
     228             : 
     229             :         // Nothing can draw outside the current clip.
     230           0 :         if (!rect.intersect(fCurrentClipBounds)) {
     231           0 :             return Bounds::MakeEmpty();
     232             :         }
     233             : 
     234           0 :         return rect;
     235             :     }
     236             : 
     237             : private:
     238             :     struct SaveBounds {
     239             :         int controlOps;        // Number of control ops in this Save block, including the Save.
     240             :         Bounds bounds;         // Bounds of everything in the block.
     241             :         const SkPaint* paint;  // Unowned.  If set, adjusts the bounds of all ops in this block.
     242             :         SkMatrix ctm;
     243             :     };
     244             : 
     245             :     // Only Restore, SetMatrix, Concat, and Translate change the CTM.
     246           0 :     template <typename T> void updateCTM(const T&) {}
     247           0 :     void updateCTM(const Restore& op)   { fCTM = op.matrix; }
     248           0 :     void updateCTM(const SetMatrix& op) { fCTM = op.matrix; }
     249           0 :     void updateCTM(const Concat& op)    { fCTM.preConcat(op.matrix); }
     250           0 :     void updateCTM(const Translate& op) { fCTM.preTranslate(op.dx, op.dy); }
     251             : 
     252             :     // Most ops don't change the clip.
     253           0 :     template <typename T> void updateClipBounds(const T&) {}
     254             : 
     255             :     // Clip{Path,RRect,Rect,Region} obviously change the clip.  They all know their bounds already.
     256           0 :     void updateClipBounds(const ClipPath&   op) { this->updateClipBoundsForClipOp(op.devBounds); }
     257           0 :     void updateClipBounds(const ClipRRect&  op) { this->updateClipBoundsForClipOp(op.devBounds); }
     258           0 :     void updateClipBounds(const ClipRect&   op) { this->updateClipBoundsForClipOp(op.devBounds); }
     259           0 :     void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipOp(op.devBounds); }
     260             : 
     261             :     // The bounds of clip ops need to be adjusted for the paints of saveLayers they're inside.
     262           0 :     void updateClipBoundsForClipOp(const SkIRect& devBounds) {
     263           0 :         Bounds clip = SkRect::Make(devBounds);
     264             :         // We don't call adjustAndMap() because as its last step it would intersect the adjusted
     265             :         // clip bounds with the previous clip, exactly what we can't do when the clip grows.
     266           0 :         if (this->adjustForSaveLayerPaints(&clip)) {
     267           0 :             fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty();
     268             :         } else {
     269           0 :             fCurrentClipBounds = fCullRect;
     270             :         }
     271           0 :     }
     272             : 
     273             :     // Restore holds the devBounds for the clip after the {save,saveLayer}/restore block completes.
     274           0 :     void updateClipBounds(const Restore& op) {
     275             :         // This is just like the clip ops above, but we need to skip the effects (if any) of our
     276             :         // paired saveLayer (if it is one); it has not yet been popped off the save stack.  Our
     277             :         // devBounds reflect the state of the world after the saveLayer/restore block is done,
     278             :         // so they are not affected by the saveLayer's paint.
     279           0 :         const int kSavesToIgnore = 1;
     280           0 :         Bounds clip = SkRect::Make(op.devBounds);
     281           0 :         if (this->adjustForSaveLayerPaints(&clip, kSavesToIgnore)) {
     282           0 :             fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty();
     283             :         } else {
     284           0 :             fCurrentClipBounds = fCullRect;
     285             :         }
     286           0 :     }
     287             : 
     288             :     // We also take advantage of SaveLayer bounds when present to further cut the clip down.
     289           0 :     void updateClipBounds(const SaveLayer& op)  {
     290           0 :         if (op.bounds) {
     291             :             // adjustAndMap() intersects these layer bounds with the previous clip for us.
     292           0 :             fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint);
     293             :         }
     294           0 :     }
     295             : 
     296             :     // The bounds of these ops must be calculated when we hit the Restore
     297             :     // from the bounds of the ops in the same Save block.
     298           0 :     void trackBounds(const Save&)          { this->pushSaveBlock(nullptr); }
     299           0 :     void trackBounds(const SaveLayer& op)  { this->pushSaveBlock(op.paint); }
     300           0 :     void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); }
     301             : 
     302           0 :     void trackBounds(const SetMatrix&)         { this->pushControl(); }
     303           0 :     void trackBounds(const Concat&)            { this->pushControl(); }
     304           0 :     void trackBounds(const Translate&)         { this->pushControl(); }
     305           0 :     void trackBounds(const TranslateZ&)        { this->pushControl(); }
     306           0 :     void trackBounds(const ClipRect&)          { this->pushControl(); }
     307           0 :     void trackBounds(const ClipRRect&)         { this->pushControl(); }
     308           0 :     void trackBounds(const ClipPath&)          { this->pushControl(); }
     309           0 :     void trackBounds(const ClipRegion&)        { this->pushControl(); }
     310             : 
     311             : 
     312             :     // For all other ops, we can calculate and store the bounds directly now.
     313           0 :     template <typename T> void trackBounds(const T& op) {
     314           0 :         fBounds[fCurrentOp] = this->bounds(op);
     315           0 :         this->updateSaveBounds(fBounds[fCurrentOp]);
     316           0 :     }
     317             : 
     318           0 :     void pushSaveBlock(const SkPaint* paint) {
     319             :         // Starting a new Save block.  Push a new entry to represent that.
     320             :         SaveBounds sb;
     321           0 :         sb.controlOps = 0;
     322             :         // If the paint affects transparent black, the bound shouldn't be smaller
     323             :         // than the current clip bounds.
     324             :         sb.bounds =
     325           0 :             PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty();
     326           0 :         sb.paint = paint;
     327           0 :         sb.ctm = this->fCTM;
     328             : 
     329           0 :         fSaveStack.push(sb);
     330           0 :         this->pushControl();
     331           0 :     }
     332             : 
     333           0 :     static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
     334           0 :         if (paint) {
     335             :             // FIXME: this is very conservative
     336           0 :             if (paint->getImageFilter() || paint->getColorFilter()) {
     337           0 :                 return true;
     338             :             }
     339             : 
     340             :             // Unusual blendmodes require us to process a saved layer
     341             :             // even with operations outisde the clip.
     342             :             // For example, DstIn is used by masking layers.
     343             :             // https://code.google.com/p/skia/issues/detail?id=1291
     344             :             // https://crbug.com/401593
     345           0 :             switch (paint->getBlendMode()) {
     346             :                 // For each of the following transfer modes, if the source
     347             :                 // alpha is zero (our transparent black), the resulting
     348             :                 // blended alpha is not necessarily equal to the original
     349             :                 // destination alpha.
     350             :                 case SkBlendMode::kClear:
     351             :                 case SkBlendMode::kSrc:
     352             :                 case SkBlendMode::kSrcIn:
     353             :                 case SkBlendMode::kDstIn:
     354             :                 case SkBlendMode::kSrcOut:
     355             :                 case SkBlendMode::kDstATop:
     356             :                 case SkBlendMode::kModulate:
     357           0 :                     return true;
     358             :                     break;
     359             :                 default:
     360           0 :                     break;
     361             :             }
     362             :         }
     363           0 :         return false;
     364             :     }
     365             : 
     366           0 :     Bounds popSaveBlock() {
     367             :         // We're done the Save block.  Apply the block's bounds to all control ops inside it.
     368             :         SaveBounds sb;
     369           0 :         fSaveStack.pop(&sb);
     370             : 
     371           0 :         while (sb.controlOps --> 0) {
     372           0 :             this->popControl(sb.bounds);
     373             :         }
     374             : 
     375             :         // This whole Save block may be part another Save block.
     376           0 :         this->updateSaveBounds(sb.bounds);
     377             : 
     378             :         // If called from a real Restore (not a phony one for balance), it'll need the bounds.
     379           0 :         return sb.bounds;
     380             :     }
     381             : 
     382           0 :     void pushControl() {
     383           0 :         fControlIndices.push(fCurrentOp);
     384           0 :         if (!fSaveStack.isEmpty()) {
     385           0 :             fSaveStack.top().controlOps++;
     386             :         }
     387           0 :     }
     388             : 
     389           0 :     void popControl(const Bounds& bounds) {
     390           0 :         fBounds[fControlIndices.top()] = bounds;
     391           0 :         fControlIndices.pop();
     392           0 :     }
     393             : 
     394           0 :     void updateSaveBounds(const Bounds& bounds) {
     395             :         // If we're in a Save block, expand its bounds to cover these bounds too.
     396           0 :         if (!fSaveStack.isEmpty()) {
     397           0 :             fSaveStack.top().bounds.join(bounds);
     398             :         }
     399           0 :     }
     400             : 
     401             :     // FIXME: this method could use better bounds
     402           0 :     Bounds bounds(const DrawText&) const { return fCurrentClipBounds; }
     403             : 
     404           0 :     Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; }
     405           0 :     Bounds bounds(const NoOp&)  const { return Bounds::MakeEmpty(); }    // NoOps don't draw.
     406             : 
     407           0 :     Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); }
     408           0 :     Bounds bounds(const DrawRegion& op) const {
     409           0 :         SkRect rect = SkRect::Make(op.region.getBounds());
     410           0 :         return this->adjustAndMap(rect, &op.paint);
     411             :     }
     412           0 :     Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); }
     413             :     // Tighter arc bounds?
     414           0 :     Bounds bounds(const DrawArc& op) const { return this->adjustAndMap(op.oval, &op.paint); }
     415           0 :     Bounds bounds(const DrawRRect& op) const {
     416           0 :         return this->adjustAndMap(op.rrect.rect(), &op.paint);
     417             :     }
     418           0 :     Bounds bounds(const DrawDRRect& op) const {
     419           0 :         return this->adjustAndMap(op.outer.rect(), &op.paint);
     420             :     }
     421           0 :     Bounds bounds(const DrawImage& op) const {
     422           0 :         const SkImage* image = op.image.get();
     423           0 :         SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height());
     424             : 
     425           0 :         return this->adjustAndMap(rect, op.paint);
     426             :     }
     427           0 :     Bounds bounds(const DrawImageLattice& op) const {
     428           0 :         return this->adjustAndMap(op.dst, op.paint);
     429             :     }
     430           0 :     Bounds bounds(const DrawImageRect& op) const {
     431           0 :         return this->adjustAndMap(op.dst, op.paint);
     432             :     }
     433           0 :     Bounds bounds(const DrawImageNine& op) const {
     434           0 :         return this->adjustAndMap(op.dst, op.paint);
     435             :     }
     436           0 :     Bounds bounds(const DrawPath& op) const {
     437           0 :         return op.path.isInverseFillType() ? fCurrentClipBounds
     438           0 :                                            : this->adjustAndMap(op.path.getBounds(), &op.paint);
     439             :     }
     440           0 :     Bounds bounds(const DrawPoints& op) const {
     441             :         SkRect dst;
     442           0 :         dst.set(op.pts, op.count);
     443             : 
     444             :         // Pad the bounding box a little to make sure hairline points' bounds aren't empty.
     445           0 :         SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f);
     446           0 :         dst.outset(stroke/2, stroke/2);
     447             : 
     448           0 :         return this->adjustAndMap(dst, &op.paint);
     449             :     }
     450           0 :     Bounds bounds(const DrawPatch& op) const {
     451             :         SkRect dst;
     452           0 :         dst.set(op.cubics, SkPatchUtils::kNumCtrlPts);
     453           0 :         return this->adjustAndMap(dst, &op.paint);
     454             :     }
     455           0 :     Bounds bounds(const DrawVertices& op) const {
     456           0 :         return this->adjustAndMap(op.vertices->bounds(), &op.paint);
     457             :     }
     458             : 
     459           0 :     Bounds bounds(const DrawAtlas& op) const {
     460           0 :         if (op.cull) {
     461             :             // TODO: <reed> can we pass nullptr for the paint? Isn't cull already "correct"
     462             :             // for the paint (by the caller)?
     463           0 :             return this->adjustAndMap(*op.cull, op.paint);
     464             :         } else {
     465           0 :             return fCurrentClipBounds;
     466             :         }
     467             :     }
     468             : 
     469           0 :     Bounds bounds(const DrawPicture& op) const {
     470           0 :         SkRect dst = op.picture->cullRect();
     471           0 :         op.matrix.mapRect(&dst);
     472           0 :         return this->adjustAndMap(dst, op.paint);
     473             :     }
     474             : 
     475           0 :     Bounds bounds(const DrawShadowedPicture& op) const {
     476           0 :         SkRect dst = op.picture->cullRect();
     477           0 :         op.matrix.mapRect(&dst);
     478           0 :         return this->adjustAndMap(dst, op.paint);
     479             :     }
     480             : 
     481           0 :     Bounds bounds(const DrawPosText& op) const {
     482           0 :         const int N = op.paint.countText(op.text, op.byteLength);
     483           0 :         if (N == 0) {
     484           0 :             return Bounds::MakeEmpty();
     485             :         }
     486             : 
     487             :         SkRect dst;
     488           0 :         dst.set(op.pos, N);
     489           0 :         AdjustTextForFontMetrics(&dst, op.paint);
     490           0 :         return this->adjustAndMap(dst, &op.paint);
     491             :     }
     492           0 :     Bounds bounds(const DrawPosTextH& op) const {
     493           0 :         const int N = op.paint.countText(op.text, op.byteLength);
     494           0 :         if (N == 0) {
     495           0 :             return Bounds::MakeEmpty();
     496             :         }
     497             : 
     498           0 :         SkScalar left = op.xpos[0], right = op.xpos[0];
     499           0 :         for (int i = 1; i < N; i++) {
     500           0 :             left  = SkMinScalar(left,  op.xpos[i]);
     501           0 :             right = SkMaxScalar(right, op.xpos[i]);
     502             :         }
     503           0 :         SkRect dst = { left, op.y, right, op.y };
     504           0 :         AdjustTextForFontMetrics(&dst, op.paint);
     505           0 :         return this->adjustAndMap(dst, &op.paint);
     506             :     }
     507           0 :     Bounds bounds(const DrawTextOnPath& op) const {
     508           0 :         SkRect dst = op.path.getBounds();
     509             : 
     510             :         // Pad all sides by the maximum padding in any direction we'd normally apply.
     511           0 :         SkRect pad = { 0, 0, 0, 0};
     512           0 :         AdjustTextForFontMetrics(&pad, op.paint);
     513             : 
     514             :         // That maximum padding happens to always be the right pad today.
     515           0 :         SkASSERT(pad.fLeft == -pad.fRight);
     516           0 :         SkASSERT(pad.fTop  == -pad.fBottom);
     517           0 :         SkASSERT(pad.fRight > pad.fBottom);
     518           0 :         dst.outset(pad.fRight, pad.fRight);
     519             : 
     520           0 :         return this->adjustAndMap(dst, &op.paint);
     521             :     }
     522             : 
     523           0 :     Bounds bounds(const DrawTextRSXform& op) const {
     524           0 :         if (op.cull) {
     525           0 :             return this->adjustAndMap(*op.cull, nullptr);
     526             :         } else {
     527           0 :             return fCurrentClipBounds;
     528             :         }
     529             :     }
     530             : 
     531           0 :     Bounds bounds(const DrawTextBlob& op) const {
     532           0 :         SkRect dst = op.blob->bounds();
     533           0 :         dst.offset(op.x, op.y);
     534           0 :         return this->adjustAndMap(dst, &op.paint);
     535             :     }
     536             : 
     537           0 :     Bounds bounds(const DrawDrawable& op) const {
     538           0 :         return this->adjustAndMap(op.worstCaseBounds, nullptr);
     539             :     }
     540             : 
     541           0 :     Bounds bounds(const DrawAnnotation& op) const {
     542           0 :         return this->adjustAndMap(op.rect, nullptr);
     543             :     }
     544             : 
     545           0 :     static void AdjustTextForFontMetrics(SkRect* rect, const SkPaint& paint) {
     546             : #ifdef SK_DEBUG
     547           0 :         SkRect correct = *rect;
     548             : #endif
     549             :         // crbug.com/373785 ~~> xPad = 4x yPad
     550             :         // crbug.com/424824 ~~> bump yPad from 2x text size to 2.5x
     551           0 :         const SkScalar yPad = 2.5f * paint.getTextSize(),
     552           0 :                        xPad = 4.0f * yPad;
     553           0 :         rect->outset(xPad, yPad);
     554             : #ifdef SK_DEBUG
     555             :         SkPaint::FontMetrics metrics;
     556           0 :         paint.getFontMetrics(&metrics);
     557           0 :         correct.fLeft   += metrics.fXMin;
     558           0 :         correct.fTop    += metrics.fTop;
     559           0 :         correct.fRight  += metrics.fXMax;
     560           0 :         correct.fBottom += metrics.fBottom;
     561             :         // See skia:2862 for why we ignore small text sizes.
     562           0 :         SkASSERTF(paint.getTextSize() < 0.001f || rect->contains(correct),
     563             :                   "%f %f %f %f vs. %f %f %f %f\n",
     564             :                   -xPad, -yPad, +xPad, +yPad,
     565             :                   metrics.fXMin, metrics.fTop, metrics.fXMax, metrics.fBottom);
     566             : #endif
     567           0 :     }
     568             : 
     569             :     // Returns true if rect was meaningfully adjusted for the effects of paint,
     570             :     // false if the paint could affect the rect in unknown ways.
     571           0 :     static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) {
     572           0 :         if (paint) {
     573           0 :             if (paint->canComputeFastBounds()) {
     574           0 :                 *rect = paint->computeFastBounds(*rect, rect);
     575           0 :                 return true;
     576             :             }
     577           0 :             return false;
     578             :         }
     579           0 :         return true;
     580             :     }
     581             : 
     582           0 :     bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const {
     583           0 :         for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) {
     584             :             SkMatrix inverse;
     585           0 :             if (!fSaveStack[i].ctm.invert(&inverse)) {
     586           0 :                 return false;
     587             :             }
     588           0 :             inverse.mapRect(rect);
     589           0 :             if (!AdjustForPaint(fSaveStack[i].paint, rect)) {
     590           0 :                 return false;
     591             :             }
     592           0 :             fSaveStack[i].ctm.mapRect(rect);
     593             :         }
     594           0 :         return true;
     595             :     }
     596             : 
     597             :     const int fNumRecords;
     598             : 
     599             :     // We do not guarantee anything for operations outside of the cull rect
     600             :     const SkRect fCullRect;
     601             : 
     602             :     // Conservative identity-space bounds for each op in the SkRecord.
     603             :     Bounds* fBounds;
     604             : 
     605             :     // We walk fCurrentOp through the SkRecord, as we go using updateCTM()
     606             :     // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative
     607             :     // identity-space bounds of the current clip (fCurrentClipBounds).
     608             :     int fCurrentOp;
     609             :     SkMatrix fCTM;
     610             :     Bounds fCurrentClipBounds;
     611             : 
     612             :     // Used to track the bounds of Save/Restore blocks and the control ops inside them.
     613             :     SkTDArray<SaveBounds> fSaveStack;
     614             :     SkTDArray<int>   fControlIndices;
     615             : };
     616             : 
     617             : }  // namespace SkRecords
     618             : 
     619           0 : void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[]) {
     620           0 :     SkRecords::FillBounds visitor(cullRect, record, bounds);
     621           0 :     for (int curOp = 0; curOp < record.count(); curOp++) {
     622           0 :         visitor.setCurrentOp(curOp);
     623           0 :         record.visit(curOp, visitor);
     624             :     }
     625           0 :     visitor.cleanUp();
     626           0 : }
     627             : 

Generated by: LCOV version 1.13