1c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org/*
2c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org * Copyright 2014 Google Inc.
3c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org *
4c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org * Use of this source code is governed by a BSD-style license that can be
5c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org * found in the LICENSE file.
6c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org */
7c4b21e6c03a6cdb03e116b9f510eb10cf8daedb1commit-bot@chromium.org
8506db0b7d2905c6bedba9fc5d4aeaf231a9a34eacommit-bot@chromium.org#include "SkRecordDraw.h"
9ab244f045a0740fa6106ed21a4e5824cd09f84f3Florin Malita#include "SkImage.h"
10131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein#include "SkPatchUtils.h"
11506db0b7d2905c6bedba9fc5d4aeaf231a9a34eacommit-bot@chromium.org
125ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtkleinvoid SkRecordDraw(const SkRecord& record,
135ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein                  SkCanvas* canvas,
141bdfd3f4f09e47364f76d3f08177b1ce844ac786reed                  SkPicture const* const drawablePicts[],
153cb3840c9af6f70896cf5565a38d4ee03c02d767reed                  SkDrawable* const drawables[],
161bdfd3f4f09e47364f76d3f08177b1ce844ac786reed                  int drawableCount,
175ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein                  const SkBBoxHierarchy* bbh,
18783fe16b8ed1cd1cff34eacc33296874a32f293brobertphillips                  SkPicture::AbortCallback* callback) {
19c11530ea73b2a2fcb431df0f5c1887d08ac9113cMike Klein    SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
205ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein
2149f085dddff10473b6ebf832a974288300224e60bsalomon    if (bbh) {
225ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein        // Draw only ops that affect pixels in the canvas's current clip.
233e8232b6ef6b04290f0c280f88b38f8cd2c3f3ecmtklein        // The SkRecord and BBH were recorded in identity space.  This canvas
24918e144408ba218df919528f8b48c544f4767883Mike Reed        // is not necessarily in that same space.  getLocalClipBounds() returns us
253e8232b6ef6b04290f0c280f88b38f8cd2c3f3ecmtklein        // this canvas' clip bounds transformed back into identity space, which
263e8232b6ef6b04290f0c280f88b38f8cd2c3f3ecmtklein        // lets us query the BBH.
27918e144408ba218df919528f8b48c544f4767883Mike Reed        SkRect query = canvas->getLocalClipBounds();
283e8232b6ef6b04290f0c280f88b38f8cd2c3f3ecmtklein
29c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein        SkTDArray<int> ops;
30a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        bbh->search(query, &ops);
315ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein
321bdfd3f4f09e47364f76d3f08177b1ce844ac786reed        SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount);
335ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein        for (int i = 0; i < ops.count(); i++) {
34783fe16b8ed1cd1cff34eacc33296874a32f293brobertphillips            if (callback && callback->abort()) {
355ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein                return;
365ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein            }
37d239d425148e5c1445b6a99d9bc33348c41197bcdanakj            // This visit call uses the SkRecords::Draw::operator() to call
38d239d425148e5c1445b6a99d9bc33348c41197bcdanakj            // methods on the |canvas|, wrapped by methods defined with the
39d239d425148e5c1445b6a99d9bc33348c41197bcdanakj            // DRAW() macro.
40343a63d082bda969d7e8a4e09ba850e931185269mtklein            record.visit(ops[i], draw);
415ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein        }
425ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein    } else {
435ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein        // Draw all ops.
441bdfd3f4f09e47364f76d3f08177b1ce844ac786reed        SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount);
45c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein        for (int i = 0; i < record.count(); i++) {
46783fe16b8ed1cd1cff34eacc33296874a32f293brobertphillips            if (callback && callback->abort()) {
475ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein                return;
485ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein            }
49d239d425148e5c1445b6a99d9bc33348c41197bcdanakj            // This visit call uses the SkRecords::Draw::operator() to call
50d239d425148e5c1445b6a99d9bc33348c41197bcdanakj            // methods on the |canvas|, wrapped by methods defined with the
51d239d425148e5c1445b6a99d9bc33348c41197bcdanakj            // DRAW() macro.
52343a63d082bda969d7e8a4e09ba850e931185269mtklein            record.visit(i, draw);
53c11530ea73b2a2fcb431df0f5c1887d08ac9113cMike Klein        }
5473b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.org    }
5527f6b0d013572184dc980462cce8d0f3caec1c8ecommit-bot@chromium.org}
56d9ce2be6b24b1c89d13c2edb63c3462b0f5c6aa3commit-bot@chromium.org
576be2aa9a251bf6022570a03140f956655b3ef1dareedvoid SkRecordPartialDraw(const SkRecord& record, SkCanvas* canvas,
586be2aa9a251bf6022570a03140f956655b3ef1dareed                         SkPicture const* const drawablePicts[], int drawableCount,
59c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein                         int start, int stop,
604815fe5a0a497b676677fb4e4a0f05c511855490robertphillips                         const SkMatrix& initialCTM) {
6100f30bdc9e34b013da54b4406f36556c5be8d041mtklein    SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
6200f30bdc9e34b013da54b4406f36556c5be8d041mtklein
6300f30bdc9e34b013da54b4406f36556c5be8d041mtklein    stop = SkTMin(stop, record.count());
6496fcdcc219d2a0d3579719b84b28bede76efba64halcanary    SkRecords::Draw draw(canvas, drawablePicts, nullptr, drawableCount, &initialCTM);
65c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    for (int i = start; i < stop; i++) {
66343a63d082bda969d7e8a4e09ba850e931185269mtklein        record.visit(i, draw);
6700f30bdc9e34b013da54b4406f36556c5be8d041mtklein    }
6800f30bdc9e34b013da54b4406f36556c5be8d041mtklein}
6900f30bdc9e34b013da54b4406f36556c5be8d041mtklein
7027f6b0d013572184dc980462cce8d0f3caec1c8ecommit-bot@chromium.orgnamespace SkRecords {
712e0c32af0508a1e544c9953ea2fe128dbae7d429commit-bot@chromium.org
722e0c32af0508a1e544c9953ea2fe128dbae7d429commit-bot@chromium.org// NoOps draw nothing.
7327f6b0d013572184dc980462cce8d0f3caec1c8ecommit-bot@chromium.orgtemplate <> void Draw::draw(const NoOp&) {}
7473b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.org
7527f6b0d013572184dc980462cce8d0f3caec1c8ecommit-bot@chromium.org#define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
767cc49d65fc5788f72458efc8fc4156cde4cca15aMike KleinDRAW(Flush, flush());
7773b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(Restore, restore());
785f6102d07982043542343ff0a6c67b1319ac9fc7Florin MalitaDRAW(Save, save());
79da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(SaveLayer, saveLayer(SkCanvas::SaveLayerRec(r.bounds,
80da574d17f864ed70323a1c0fc6e4e969153a4c98mtklein                                                 r.paint,
81da574d17f864ed70323a1c0fc6e4e969153a4c98mtklein                                                 r.backdrop.get(),
82b34ab04884f6219b93c1f143c83a1fc60fded40cMike Klein                                                 r.clipMask.get(),
8353f77bd4fdd76525b66b7f26d1c5c550858120dfFlorin Malita                                                 r.clipMatrix,
84da574d17f864ed70323a1c0fc6e4e969153a4c98mtklein                                                 r.saveLayerFlags)));
8599bd7d817471ec903a7fe364b4e1d4477b1372b5commit-bot@chromium.orgDRAW(SetMatrix, setMatrix(SkMatrix::Concat(fInitialCTM, r.matrix)));
86e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtkleinDRAW(Concat, concat(r.matrix));
87cbdf007bc2eb85056a1a5c75c088202becba2d16mtkleinDRAW(Translate, translate(r.dx, r.dy));
8873b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.org
89ebfce6d9b42198e04288a15953f40c395a7b6139Mike ReedDRAW(ClipPath, clipPath(r.path, r.opAA.op(), r.opAA.aa()));
90ebfce6d9b42198e04288a15953f40c395a7b6139Mike ReedDRAW(ClipRRect, clipRRect(r.rrect, r.opAA.op(), r.opAA.aa()));
91ebfce6d9b42198e04288a15953f40c395a7b6139Mike ReedDRAW(ClipRect, clipRect(r.rect, r.opAA.op(), r.opAA.aa()));
9273b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(ClipRegion, clipRegion(r.region, r.op));
9373b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.org
94ac3aa245acc7b469aa2f0d0078e53401d78ac8b9bsalomonDRAW(DrawArc, drawArc(r.oval, r.startAngle, r.sweepAngle, r.useCenter, r.paint));
9573b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint));
96da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(DrawImage, drawImage(r.image.get(), r.left, r.top, r.paint));
97c573a40ed5024b463e47088d307e3164a486dba5msarett
98c573a40ed5024b463e47088d307e3164a486dba5msaretttemplate <> void Draw::draw(const DrawImageLattice& r) {
99c573a40ed5024b463e47088d307e3164a486dba5msarett    SkCanvas::Lattice lattice;
100c573a40ed5024b463e47088d307e3164a486dba5msarett    lattice.fXCount = r.xCount;
101c573a40ed5024b463e47088d307e3164a486dba5msarett    lattice.fXDivs = r.xDivs;
102c573a40ed5024b463e47088d307e3164a486dba5msarett    lattice.fYCount = r.yCount;
103c573a40ed5024b463e47088d307e3164a486dba5msarett    lattice.fYDivs = r.yDivs;
104ca8c0953e8da1def5e6c12dde6d4368b4bf16077Stan Iliev    lattice.fRectTypes = (0 == r.flagCount) ? nullptr : r.flags;
105ca8c0953e8da1def5e6c12dde6d4368b4bf16077Stan Iliev    lattice.fColors = (0 == r.flagCount) ? nullptr : r.colors;
10671df2d7bc1bbc83ad4cf005f9027df4cb3b88a9bmsarett    lattice.fBounds = &r.src;
107c573a40ed5024b463e47088d307e3164a486dba5msarett    fCanvas->drawImageLattice(r.image.get(), lattice, r.dst, r.paint);
108c573a40ed5024b463e47088d307e3164a486dba5msarett}
109c573a40ed5024b463e47088d307e3164a486dba5msarett
110da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(DrawImageRect, legacy_drawImageRect(r.image.get(), r.src, r.dst, r.paint, r.constraint));
111da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(DrawImageNine, drawImageNine(r.image.get(), r.center, r.dst, r.paint));
11273b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawOval, drawOval(r.oval, r.paint));
11373b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawPaint, drawPaint(r.paint));
11473b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawPath, drawPath(r.path, r.paint));
1157d954ad797176afedb9262fdea4507d0fc60eb9dMike ReedDRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.bmode, r.paint));
116da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint));
11773b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint));
11873b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawPosText, drawPosText(r.text, r.byteLength, r.pos, r.paint));
11973b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawPosTextH, drawPosTextH(r.text, r.byteLength, r.xpos, r.y, r.paint));
12073b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawRRect, drawRRect(r.rrect, r.paint));
12173b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawRect, drawRect(r.rect, r.paint));
12244df651ebefc284acc2f66425dff3ea0b0e14b36msarettDRAW(DrawRegion, drawRegion(r.region, r.paint));
12373b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.orgDRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint));
124da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(DrawTextBlob, drawTextBlob(r.blob.get(), r.x, r.y, r.paint));
125af57903f330a0afd0c10244d4a66f64fdbef5d1emtkleinDRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, &r.matrix, r.paint));
12645561a0b15fe045ba272c328684c3f7ae290785areedDRAW(DrawTextRSXform, drawTextRSXform(r.text, r.byteLength, r.xforms, r.cull, r.paint));
127da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(DrawAtlas, drawAtlas(r.atlas.get(),
128da574d17f864ed70323a1c0fc6e4e969153a4c98mtklein                          r.xforms, r.texs, r.colors, r.count, r.mode, r.cull, r.paint));
129e88a1cb20e6b4c9f099070112225a88693a4630bMike ReedDRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint));
1304204da25aa4c6e0b321314aa32fd9affb4865563Mike ReedDRAW(DrawShadowRec, private_draw_shadow_rec(r.path, r.rec));
131da574d17f864ed70323a1c0fc6e4e969153a4c98mtkleinDRAW(DrawAnnotation, drawAnnotation(r.rect, r.key.c_str(), r.value.get()));
13273b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.org#undef DRAW
13373b55eb7d789549ee9d6602ed96d2d9b74f3c6e3commit-bot@chromium.org
1346be2aa9a251bf6022570a03140f956655b3ef1dareedtemplate <> void Draw::draw(const DrawDrawable& r) {
1356be2aa9a251bf6022570a03140f956655b3ef1dareed    SkASSERT(r.index >= 0);
1366be2aa9a251bf6022570a03140f956655b3ef1dareed    SkASSERT(r.index < fDrawableCount);
1371bdfd3f4f09e47364f76d3f08177b1ce844ac786reed    if (fDrawables) {
13896fcdcc219d2a0d3579719b84b28bede76efba64halcanary        SkASSERT(nullptr == fDrawablePicts);
139a8db72864a43ad1fbba3c2892cf5cd88060a43efreed        fCanvas->drawDrawable(fDrawables[r.index], r.matrix);
1401bdfd3f4f09e47364f76d3f08177b1ce844ac786reed    } else {
14196fcdcc219d2a0d3579719b84b28bede76efba64halcanary        fCanvas->drawPicture(fDrawablePicts[r.index], r.matrix, nullptr);
1421bdfd3f4f09e47364f76d3f08177b1ce844ac786reed    }
1436be2aa9a251bf6022570a03140f956655b3ef1dareed}
1446be2aa9a251bf6022570a03140f956655b3ef1dareed
1455ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein// This is an SkRecord visitor that fills an SkBBoxHierarchy.
146828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein//
147828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// The interesting part here is how to calculate bounds for ops which don't
148828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// have intrinsic bounds.  What is the bounds of a Save or a Translate?
149828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein//
150828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// We answer this by thinking about a particular definition of bounds: if I
151828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// don't execute this op, pixels in this rectangle might draw incorrectly.  So
152828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// the bounds of a Save, a Translate, a Restore, etc. are the union of the
153828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// bounds of Draw* ops that they might have an effect on.  For any given
154828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// Save/Restore block, the bounds of the Save, the Restore, and any other
155828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// non-drawing ("control") ops inside are exactly the union of the bounds of
156828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// the drawing ops inside that block.
157828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein//
158828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// To implement this, we keep a stack of active Save blocks.  As we consume ops
159828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// inside the Save/Restore block, drawing ops are unioned with the bounds of
160828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// the block, and control ops are stashed away for later.  When we finish the
161828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// block with a Restore, our bounds are complete, and we go back and fill them
162828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein// in for all the control ops we stashed away.
1635ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtkleinclass FillBounds : SkNoncopyable {
1645ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtkleinpublic:
16540732b34a1bf94eb44ee4b2327eece8d97735f11mtklein    FillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[])
1664e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        : fNumRecords(record.count())
1674e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        , fCullRect(cullRect)
16840732b34a1bf94eb44ee4b2327eece8d97735f11mtklein        , fBounds(bounds) {
169e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein        fCTM = SkMatrix::I();
1704d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        fCurrentClipBounds = fCullRect;
1714e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips    }
172828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
17340732b34a1bf94eb44ee4b2327eece8d97735f11mtklein    void cleanUp() {
174828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // If we have any lingering unpaired Saves, simulate restores to make
175828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // sure all ops in those Save blocks have their bounds calculated.
176828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        while (!fSaveStack.isEmpty()) {
177828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein            this->popSaveBlock();
178828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        }
179828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
180828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // Any control ops not part of any Save/Restore block draw everywhere.
181828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        while (!fControlIndices.isEmpty()) {
1824d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips            this->popControl(fCullRect);
183828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        }
184828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    }
1855ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein
18640732b34a1bf94eb44ee4b2327eece8d97735f11mtklein    void setCurrentOp(int currentOp) { fCurrentOp = currentOp; }
18740732b34a1bf94eb44ee4b2327eece8d97735f11mtklein
18840732b34a1bf94eb44ee4b2327eece8d97735f11mtklein
189a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    template <typename T> void operator()(const T& op) {
190a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        this->updateCTM(op);
191a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        this->updateClipBounds(op);
192a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        this->trackBounds(op);
1935ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein    }
1945ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein
195533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    // In this file, SkRect are in local coordinates, Bounds are translated back to identity space.
196533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    typedef SkRect Bounds;
197533eb782edaa0b6fece6166d3001edf72ec39f11mtklein
198c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    int currentOp() const { return fCurrentOp; }
199e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein    const SkMatrix& ctm() const { return fCTM; }
200c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    const Bounds& getBounds(int index) const { return fBounds[index]; }
2014e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips
2024e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips    // Adjust rect for all paints that may affect its geometry, then map it to identity space.
2034e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips    Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const {
2044e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        // Inverted rectangles really confuse our BBHs.
2054e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        rect.sort();
2064e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips
2074e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        // Adjust the rect for its own paint.
2084e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        if (!AdjustForPaint(paint, &rect)) {
2094e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips            // The paint could do anything to our bounds.  The only safe answer is the current clip.
2104e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips            return fCurrentClipBounds;
2114e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        }
2124e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips
2134e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        // Adjust rect for all the paints from the SaveLayers we're inside.
2144e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        if (!this->adjustForSaveLayerPaints(&rect)) {
2154e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips            // Same deal as above.
2164e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips            return fCurrentClipBounds;
2174e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        }
2184e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips
2194e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        // Map the rect back to identity space.
220e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein        fCTM.mapRect(&rect);
2214e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips
2224e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        // Nothing can draw outside the current clip.
223c187a3c8849ba5e409f79ead21cd5bc82173d9f8robertphillips        if (!rect.intersect(fCurrentClipBounds)) {
224c187a3c8849ba5e409f79ead21cd5bc82173d9f8robertphillips            return Bounds::MakeEmpty();
225c187a3c8849ba5e409f79ead21cd5bc82173d9f8robertphillips        }
226c187a3c8849ba5e409f79ead21cd5bc82173d9f8robertphillips
2274e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        return rect;
2284e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips    }
2294e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips
2304e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillipsprivate:
231828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    struct SaveBounds {
232a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        int controlOps;        // Number of control ops in this Save block, including the Save.
233533eb782edaa0b6fece6166d3001edf72ec39f11mtklein        Bounds bounds;         // Bounds of everything in the block.
234a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        const SkPaint* paint;  // Unowned.  If set, adjusts the bounds of all ops in this block.
235db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco        SkMatrix ctm;
236828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    };
237828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
238cbdf007bc2eb85056a1a5c75c088202becba2d16mtklein    // Only Restore, SetMatrix, Concat, and Translate change the CTM.
2398e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    template <typename T> void updateCTM(const T&) {}
240e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein    void updateCTM(const Restore& op)   { fCTM = op.matrix; }
241e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein    void updateCTM(const SetMatrix& op) { fCTM = op.matrix; }
242e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein    void updateCTM(const Concat& op)    { fCTM.preConcat(op.matrix); }
243cbdf007bc2eb85056a1a5c75c088202becba2d16mtklein    void updateCTM(const Translate& op) { fCTM.preTranslate(op.dx, op.dy); }
244a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein
2458e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    // Most ops don't change the clip.
2468e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    template <typename T> void updateClipBounds(const T&) {}
247271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein
2488e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    // Clip{Path,RRect,Rect,Region} obviously change the clip.  They all know their bounds already.
2498e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    void updateClipBounds(const ClipPath&   op) { this->updateClipBoundsForClipOp(op.devBounds); }
2508e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    void updateClipBounds(const ClipRRect&  op) { this->updateClipBoundsForClipOp(op.devBounds); }
2518e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    void updateClipBounds(const ClipRect&   op) { this->updateClipBoundsForClipOp(op.devBounds); }
2528e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipOp(op.devBounds); }
253271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein
2548e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    // The bounds of clip ops need to be adjusted for the paints of saveLayers they're inside.
2558e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    void updateClipBoundsForClipOp(const SkIRect& devBounds) {
2568e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        Bounds clip = SkRect::Make(devBounds);
257271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein        // We don't call adjustAndMap() because as its last step it would intersect the adjusted
258271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein        // clip bounds with the previous clip, exactly what we can't do when the clip grows.
25923d8593f8127411d9d687b4565b34b4ecd6b11d3schenney        if (this->adjustForSaveLayerPaints(&clip)) {
26023d8593f8127411d9d687b4565b34b4ecd6b11d3schenney            fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty();
26123d8593f8127411d9d687b4565b34b4ecd6b11d3schenney        } else {
26223d8593f8127411d9d687b4565b34b4ecd6b11d3schenney            fCurrentClipBounds = fCullRect;
26323d8593f8127411d9d687b4565b34b4ecd6b11d3schenney        }
264271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein    }
265271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein
2668e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    // Restore holds the devBounds for the clip after the {save,saveLayer}/restore block completes.
2678e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    void updateClipBounds(const Restore& op) {
2688e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        // This is just like the clip ops above, but we need to skip the effects (if any) of our
2698e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        // paired saveLayer (if it is one); it has not yet been popped off the save stack.  Our
2708e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        // devBounds reflect the state of the world after the saveLayer/restore block is done,
2718e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        // so they are not affected by the saveLayer's paint.
2728e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        const int kSavesToIgnore = 1;
2738e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        Bounds clip = SkRect::Make(op.devBounds);
27423d8593f8127411d9d687b4565b34b4ecd6b11d3schenney        if (this->adjustForSaveLayerPaints(&clip, kSavesToIgnore)) {
27523d8593f8127411d9d687b4565b34b4ecd6b11d3schenney            fCurrentClipBounds = clip.intersect(fCullRect) ? clip : Bounds::MakeEmpty();
27623d8593f8127411d9d687b4565b34b4ecd6b11d3schenney        } else {
27723d8593f8127411d9d687b4565b34b4ecd6b11d3schenney            fCurrentClipBounds = fCullRect;
27823d8593f8127411d9d687b4565b34b4ecd6b11d3schenney        }
2798e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    }
2808e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein
281271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein    // We also take advantage of SaveLayer bounds when present to further cut the clip down.
282a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    void updateClipBounds(const SaveLayer& op)  {
283a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        if (op.bounds) {
284271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein            // adjustAndMap() intersects these layer bounds with the previous clip for us.
285271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein            fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint);
286a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        }
287a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    }
2886cfa73a29a26edf1d03bca224ad6860396308ffcmtklein
289828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    // The bounds of these ops must be calculated when we hit the Restore
290828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    // from the bounds of the ops in the same Save block.
29196fcdcc219d2a0d3579719b84b28bede76efba64halcanary    void trackBounds(const Save&)          { this->pushSaveBlock(nullptr); }
292a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    void trackBounds(const SaveLayer& op)  { this->pushSaveBlock(op.paint); }
293a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    void trackBounds(const Restore&) { fBounds[fCurrentOp] = this->popSaveBlock(); }
294828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
29568199a2d5f0cd0b9577b9695b9e095e720e96950mtklein    void trackBounds(const SetMatrix&)         { this->pushControl(); }
296e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein    void trackBounds(const Concat&)            { this->pushControl(); }
297cbdf007bc2eb85056a1a5c75c088202becba2d16mtklein    void trackBounds(const Translate&)         { this->pushControl(); }
29868199a2d5f0cd0b9577b9695b9e095e720e96950mtklein    void trackBounds(const ClipRect&)          { this->pushControl(); }
29968199a2d5f0cd0b9577b9695b9e095e720e96950mtklein    void trackBounds(const ClipRRect&)         { this->pushControl(); }
30068199a2d5f0cd0b9577b9695b9e095e720e96950mtklein    void trackBounds(const ClipPath&)          { this->pushControl(); }
30168199a2d5f0cd0b9577b9695b9e095e720e96950mtklein    void trackBounds(const ClipRegion&)        { this->pushControl(); }
302828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
303e5de130788c8637d2f7df9ddb0241b78e04d5882vjiaoblack
304828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    // For all other ops, we can calculate and store the bounds directly now.
305828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    template <typename T> void trackBounds(const T& op) {
306828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        fBounds[fCurrentOp] = this->bounds(op);
307828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        this->updateSaveBounds(fBounds[fCurrentOp]);
308828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    }
309828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
310a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    void pushSaveBlock(const SkPaint* paint) {
311828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // Starting a new Save block.  Push a new entry to represent that.
3124d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        SaveBounds sb;
3134d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        sb.controlOps = 0;
3144d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        // If the paint affects transparent black, the bound shouldn't be smaller
3154d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        // than the current clip bounds.
3164d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        sb.bounds =
3174d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips            PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds::MakeEmpty();
3184d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        sb.paint = paint;
319db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco        sb.ctm = this->fCTM;
3204d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips
321828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        fSaveStack.push(sb);
322828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        this->pushControl();
323828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    }
324828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
325d910f544439fffa6c2bcc5181b79b2811a4c130amtklein    static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
326327f905d2cb0d37c302d651d8f2b17ea56368467dneto        if (paint) {
327327f905d2cb0d37c302d651d8f2b17ea56368467dneto            // FIXME: this is very conservative
328327f905d2cb0d37c302d651d8f2b17ea56368467dneto            if (paint->getImageFilter() || paint->getColorFilter()) {
329327f905d2cb0d37c302d651d8f2b17ea56368467dneto                return true;
330327f905d2cb0d37c302d651d8f2b17ea56368467dneto            }
331327f905d2cb0d37c302d651d8f2b17ea56368467dneto
332374772bd61951f01bf84fe17bf53d8867681c9aereed            // Unusual blendmodes require us to process a saved layer
333327f905d2cb0d37c302d651d8f2b17ea56368467dneto            // even with operations outisde the clip.
334327f905d2cb0d37c302d651d8f2b17ea56368467dneto            // For example, DstIn is used by masking layers.
335327f905d2cb0d37c302d651d8f2b17ea56368467dneto            // https://code.google.com/p/skia/issues/detail?id=1291
336327f905d2cb0d37c302d651d8f2b17ea56368467dneto            // https://crbug.com/401593
337374772bd61951f01bf84fe17bf53d8867681c9aereed            switch (paint->getBlendMode()) {
338374772bd61951f01bf84fe17bf53d8867681c9aereed                // For each of the following transfer modes, if the source
339374772bd61951f01bf84fe17bf53d8867681c9aereed                // alpha is zero (our transparent black), the resulting
340374772bd61951f01bf84fe17bf53d8867681c9aereed                // blended alpha is not necessarily equal to the original
341374772bd61951f01bf84fe17bf53d8867681c9aereed                // destination alpha.
342374772bd61951f01bf84fe17bf53d8867681c9aereed                case SkBlendMode::kClear:
343374772bd61951f01bf84fe17bf53d8867681c9aereed                case SkBlendMode::kSrc:
344374772bd61951f01bf84fe17bf53d8867681c9aereed                case SkBlendMode::kSrcIn:
345374772bd61951f01bf84fe17bf53d8867681c9aereed                case SkBlendMode::kDstIn:
346374772bd61951f01bf84fe17bf53d8867681c9aereed                case SkBlendMode::kSrcOut:
347374772bd61951f01bf84fe17bf53d8867681c9aereed                case SkBlendMode::kDstATop:
348374772bd61951f01bf84fe17bf53d8867681c9aereed                case SkBlendMode::kModulate:
349374772bd61951f01bf84fe17bf53d8867681c9aereed                    return true;
350374772bd61951f01bf84fe17bf53d8867681c9aereed                    break;
351374772bd61951f01bf84fe17bf53d8867681c9aereed                default:
352374772bd61951f01bf84fe17bf53d8867681c9aereed                    break;
353327f905d2cb0d37c302d651d8f2b17ea56368467dneto            }
354327f905d2cb0d37c302d651d8f2b17ea56368467dneto        }
355327f905d2cb0d37c302d651d8f2b17ea56368467dneto        return false;
356d910f544439fffa6c2bcc5181b79b2811a4c130amtklein    }
357d910f544439fffa6c2bcc5181b79b2811a4c130amtklein
358533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds popSaveBlock() {
359828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // We're done the Save block.  Apply the block's bounds to all control ops inside it.
360828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        SaveBounds sb;
361828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        fSaveStack.pop(&sb);
362d910f544439fffa6c2bcc5181b79b2811a4c130amtklein
363828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        while (sb.controlOps --> 0) {
3644d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips            this->popControl(sb.bounds);
365828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        }
366828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
367828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // This whole Save block may be part another Save block.
3684d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        this->updateSaveBounds(sb.bounds);
369828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
370828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // If called from a real Restore (not a phony one for balance), it'll need the bounds.
3714d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips        return sb.bounds;
372828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    }
373828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
374828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    void pushControl() {
375828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        fControlIndices.push(fCurrentOp);
376828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        if (!fSaveStack.isEmpty()) {
377828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein            fSaveStack.top().controlOps++;
378828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        }
3795ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein    }
3805ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein
381533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    void popControl(const Bounds& bounds) {
382828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        fBounds[fControlIndices.top()] = bounds;
383828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        fControlIndices.pop();
384828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    }
385828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
386533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    void updateSaveBounds(const Bounds& bounds) {
387828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        // If we're in a Save block, expand its bounds to cover these bounds too.
388828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        if (!fSaveStack.isEmpty()) {
389828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein            fSaveStack.top().bounds.join(bounds);
390828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein        }
391828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    }
392828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein
3937cc49d65fc5788f72458efc8fc4156cde4cca15aMike Klein    Bounds bounds(const Flush&) const { return fCurrentClipBounds; }
3947cc49d65fc5788f72458efc8fc4156cde4cca15aMike Klein
395131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein    // FIXME: this method could use better bounds
396533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawText&) const { return fCurrentClipBounds; }
397131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein
398533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; }
399533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const NoOp&)  const { return Bounds::MakeEmpty(); }    // NoOps don't draw.
400a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein
401533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); }
40244df651ebefc284acc2f66425dff3ea0b0e14b36msarett    Bounds bounds(const DrawRegion& op) const {
40344df651ebefc284acc2f66425dff3ea0b0e14b36msarett        SkRect rect = SkRect::Make(op.region.getBounds());
40444df651ebefc284acc2f66425dff3ea0b0e14b36msarett        return this->adjustAndMap(rect, &op.paint);
40544df651ebefc284acc2f66425dff3ea0b0e14b36msarett    }
406533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); }
407ac3aa245acc7b469aa2f0d0078e53401d78ac8b9bsalomon    // Tighter arc bounds?
408ac3aa245acc7b469aa2f0d0078e53401d78ac8b9bsalomon    Bounds bounds(const DrawArc& op) const { return this->adjustAndMap(op.oval, &op.paint); }
409533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawRRect& op) const {
41062b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        return this->adjustAndMap(op.rrect.rect(), &op.paint);
41162b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    }
412533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawDRRect& op) const {
41362b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        return this->adjustAndMap(op.outer.rect(), &op.paint);
41462b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    }
41565151754b9fdb6a968d7307764c20655d1b680a0piotaixr    Bounds bounds(const DrawImage& op) const {
416da574d17f864ed70323a1c0fc6e4e969153a4c98mtklein        const SkImage* image = op.image.get();
41765151754b9fdb6a968d7307764c20655d1b680a0piotaixr        SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height());
41862b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
41965151754b9fdb6a968d7307764c20655d1b680a0piotaixr        return this->adjustAndMap(rect, op.paint);
42065151754b9fdb6a968d7307764c20655d1b680a0piotaixr    }
421c573a40ed5024b463e47088d307e3164a486dba5msarett    Bounds bounds(const DrawImageLattice& op) const {
422c573a40ed5024b463e47088d307e3164a486dba5msarett        return this->adjustAndMap(op.dst, op.paint);
423c573a40ed5024b463e47088d307e3164a486dba5msarett    }
42465151754b9fdb6a968d7307764c20655d1b680a0piotaixr    Bounds bounds(const DrawImageRect& op) const {
42565151754b9fdb6a968d7307764c20655d1b680a0piotaixr        return this->adjustAndMap(op.dst, op.paint);
42665151754b9fdb6a968d7307764c20655d1b680a0piotaixr    }
4274c21dc5ddf3b482293ed34eead876d8d61a662c3reed    Bounds bounds(const DrawImageNine& op) const {
4284c21dc5ddf3b482293ed34eead876d8d61a662c3reed        return this->adjustAndMap(op.dst, op.paint);
4294c21dc5ddf3b482293ed34eead876d8d61a662c3reed    }
430533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawPath& op) const {
43162b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        return op.path.isInverseFillType() ? fCurrentClipBounds
43262b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein                                           : this->adjustAndMap(op.path.getBounds(), &op.paint);
43362b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    }
434533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawPoints& op) const {
43562b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        SkRect dst;
43662b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        dst.set(op.pts, op.count);
43762b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
43862b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        // Pad the bounding box a little to make sure hairline points' bounds aren't empty.
43962b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        SkScalar stroke = SkMaxScalar(op.paint.getStrokeWidth(), 0.01f);
44062b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        dst.outset(stroke/2, stroke/2);
44162b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
44262b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        return this->adjustAndMap(dst, &op.paint);
44362b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    }
444533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawPatch& op) const {
445131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        SkRect dst;
446131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        dst.set(op.cubics, SkPatchUtils::kNumCtrlPts);
447131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        return this->adjustAndMap(dst, &op.paint);
448131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein    }
449533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawVertices& op) const {
450e88a1cb20e6b4c9f099070112225a88693a4630bMike Reed        return this->adjustAndMap(op.vertices->bounds(), &op.paint);
451131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein    }
45264b4c789fd1721d6804ccb5a9ceae39df03a5711mtklein
45371c3c760a83123ee0b3127b8c65c6394ce541c50reed    Bounds bounds(const DrawAtlas& op) const {
45471c3c760a83123ee0b3127b8c65c6394ce541c50reed        if (op.cull) {
45545561a0b15fe045ba272c328684c3f7ae290785areed            // TODO: <reed> can we pass nullptr for the paint? Isn't cull already "correct"
45645561a0b15fe045ba272c328684c3f7ae290785areed            // for the paint (by the caller)?
45771c3c760a83123ee0b3127b8c65c6394ce541c50reed            return this->adjustAndMap(*op.cull, op.paint);
45871c3c760a83123ee0b3127b8c65c6394ce541c50reed        } else {
45971c3c760a83123ee0b3127b8c65c6394ce541c50reed            return fCurrentClipBounds;
46071c3c760a83123ee0b3127b8c65c6394ce541c50reed        }
46171c3c760a83123ee0b3127b8c65c6394ce541c50reed    }
46264b4c789fd1721d6804ccb5a9ceae39df03a5711mtklein
4634204da25aa4c6e0b321314aa32fd9affb4865563Mike Reed    Bounds bounds(const DrawShadowRec& op) const {
4641af03d4396a9567e3ca127830676eb4fd5a76266Jim Van Verth        SkRect bounds;
4651af03d4396a9567e3ca127830676eb4fd5a76266Jim Van Verth        SkDrawShadowMetrics::GetLocalBounds(op.path, op.rec, fCTM, &bounds);
4661af03d4396a9567e3ca127830676eb4fd5a76266Jim Van Verth        return this->adjustAndMap(bounds, nullptr);
4674204da25aa4c6e0b321314aa32fd9affb4865563Mike Reed    }
4684204da25aa4c6e0b321314aa32fd9affb4865563Mike Reed
469533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawPicture& op) const {
470a8d7f0b13cd4c6d773fcf055fe17db75d260fa05robertphillips        SkRect dst = op.picture->cullRect();
471af57903f330a0afd0c10244d4a66f64fdbef5d1emtklein        op.matrix.mapRect(&dst);
472131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        return this->adjustAndMap(dst, op.paint);
473131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein    }
47462b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
475533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawPosText& op) const {
47662b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        const int N = op.paint.countText(op.text, op.byteLength);
47762b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        if (N == 0) {
478533eb782edaa0b6fece6166d3001edf72ec39f11mtklein            return Bounds::MakeEmpty();
47962b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        }
48062b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
48162b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        SkRect dst;
482937c9c7eb4e06d4d3bc495e129c7b8103a5d6c0fmtklein        dst.set(op.pos, N);
48362b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        AdjustTextForFontMetrics(&dst, op.paint);
48462b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        return this->adjustAndMap(dst, &op.paint);
48562b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    }
486533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawPosTextH& op) const {
48762b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        const int N = op.paint.countText(op.text, op.byteLength);
48862b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        if (N == 0) {
489533eb782edaa0b6fece6166d3001edf72ec39f11mtklein            return Bounds::MakeEmpty();
49062b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        }
49162b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
49262b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        SkScalar left = op.xpos[0], right = op.xpos[0];
49362b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        for (int i = 1; i < N; i++) {
49462b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein            left  = SkMinScalar(left,  op.xpos[i]);
49562b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein            right = SkMaxScalar(right, op.xpos[i]);
49662b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        }
49762b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        SkRect dst = { left, op.y, right, op.y };
49862b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        AdjustTextForFontMetrics(&dst, op.paint);
49962b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        return this->adjustAndMap(dst, &op.paint);
50062b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    }
501533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawTextOnPath& op) const {
502131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        SkRect dst = op.path.getBounds();
503131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein
5049a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        // Pad all sides by the maximum padding in any direction we'd normally apply.
505131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        SkRect pad = { 0, 0, 0, 0};
506131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        AdjustTextForFontMetrics(&pad, op.paint);
5079a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein
5089a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        // That maximum padding happens to always be the right pad today.
5099a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        SkASSERT(pad.fLeft == -pad.fRight);
5109a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        SkASSERT(pad.fTop  == -pad.fBottom);
5119a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        SkASSERT(pad.fRight > pad.fBottom);
5129a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        dst.outset(pad.fRight, pad.fRight);
5139a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein
514131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        return this->adjustAndMap(dst, &op.paint);
515131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein    }
516131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein
51745561a0b15fe045ba272c328684c3f7ae290785areed    Bounds bounds(const DrawTextRSXform& op) const {
51845561a0b15fe045ba272c328684c3f7ae290785areed        if (op.cull) {
51945561a0b15fe045ba272c328684c3f7ae290785areed            return this->adjustAndMap(*op.cull, nullptr);
52045561a0b15fe045ba272c328684c3f7ae290785areed        } else {
52145561a0b15fe045ba272c328684c3f7ae290785areed            return fCurrentClipBounds;
52245561a0b15fe045ba272c328684c3f7ae290785areed        }
52345561a0b15fe045ba272c328684c3f7ae290785areed    }
52445561a0b15fe045ba272c328684c3f7ae290785areed
525533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds bounds(const DrawTextBlob& op) const {
526131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        SkRect dst = op.blob->bounds();
527131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        dst.offset(op.x, op.y);
528131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein        return this->adjustAndMap(dst, &op.paint);
529131a22b2a22e4656f2351d42785fc3dee7a98fe2mtklein    }
53062b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
5316be2aa9a251bf6022570a03140f956655b3ef1dareed    Bounds bounds(const DrawDrawable& op) const {
53296fcdcc219d2a0d3579719b84b28bede76efba64halcanary        return this->adjustAndMap(op.worstCaseBounds, nullptr);
5336be2aa9a251bf6022570a03140f956655b3ef1dareed    }
5346be2aa9a251bf6022570a03140f956655b3ef1dareed
535f70b531daaf47db1ee95c70da9843f1dd1f418d3reed    Bounds bounds(const DrawAnnotation& op) const {
536f70b531daaf47db1ee95c70da9843f1dd1f418d3reed        return this->adjustAndMap(op.rect, nullptr);
537f70b531daaf47db1ee95c70da9843f1dd1f418d3reed    }
538343a63d082bda969d7e8a4e09ba850e931185269mtklein
53962b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    static void AdjustTextForFontMetrics(SkRect* rect, const SkPaint& paint) {
5409a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein#ifdef SK_DEBUG
5419a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        SkRect correct = *rect;
5429a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein#endif
5439a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        // crbug.com/373785 ~~> xPad = 4x yPad
5449a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        // crbug.com/424824 ~~> bump yPad from 2x text size to 2.5x
5459a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        const SkScalar yPad = 2.5f * paint.getTextSize(),
5469a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein                       xPad = 4.0f * yPad;
5479a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        rect->outset(xPad, yPad);
5489a657fa0b493ae43ed35ae04a686c5402d701420caryclark#ifdef SK_DEBUG
54962b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        SkPaint::FontMetrics metrics;
55062b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein        paint.getFontMetrics(&metrics);
5519a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        correct.fLeft   += metrics.fXMin;
5529a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        correct.fTop    += metrics.fTop;
5539a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        correct.fRight  += metrics.fXMax;
5549a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        correct.fBottom += metrics.fBottom;
555d13291a5a3d2a79b5348ecc4a8e38ab1b6da85f0mtklein        // See skia:2862 for why we ignore small text sizes.
5569a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein        SkASSERTF(paint.getTextSize() < 0.001f || rect->contains(correct),
557ed167acb7b494bf0aece05ac8a68b560782b027cmtklein                  "%f %f %f %f vs. %f %f %f %f\n",
5589a5380dd358a9c559a41beef7cb368cd7f0ca51fmtklein                  -xPad, -yPad, +xPad, +yPad,
559ed167acb7b494bf0aece05ac8a68b560782b027cmtklein                  metrics.fXMin, metrics.fTop, metrics.fXMax, metrics.fBottom);
560a19afb418027800c245ad19d651d8cf2071c9d70mtklein#endif
56162b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein    }
56262b67ae96e94fd22569b058a3bc4625b9f52ed56mtklein
563479601b9a74b5b8e424ed3d68882e161617bdcebmtklein    // Returns true if rect was meaningfully adjusted for the effects of paint,
564479601b9a74b5b8e424ed3d68882e161617bdcebmtklein    // false if the paint could affect the rect in unknown ways.
565479601b9a74b5b8e424ed3d68882e161617bdcebmtklein    static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) {
566a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein        if (paint) {
567a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein            if (paint->canComputeFastBounds()) {
568479601b9a74b5b8e424ed3d68882e161617bdcebmtklein                *rect = paint->computeFastBounds(*rect, rect);
569479601b9a74b5b8e424ed3d68882e161617bdcebmtklein                return true;
570a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein            }
571479601b9a74b5b8e424ed3d68882e161617bdcebmtklein            return false;
572479601b9a74b5b8e424ed3d68882e161617bdcebmtklein        }
573479601b9a74b5b8e424ed3d68882e161617bdcebmtklein        return true;
574479601b9a74b5b8e424ed3d68882e161617bdcebmtklein    }
575479601b9a74b5b8e424ed3d68882e161617bdcebmtklein
5768e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein    bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const {
5778e393bf70ea2aab9ca31f52c15b518436c7b6055mtklein        for (int i = fSaveStack.count() - 1 - savesToIgnore; i >= 0; i--) {
578db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco            SkMatrix inverse;
579db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco            if (!fSaveStack[i].ctm.invert(&inverse)) {
580db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco                return false;
581db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco            }
582db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco            inverse.mapRect(rect);
583271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein            if (!AdjustForPaint(fSaveStack[i].paint, rect)) {
584271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein                return false;
585271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein            }
586db64af3b178a19ecb47d2b9a373113687d8921fdsenorblanco            fSaveStack[i].ctm.mapRect(rect);
587271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein        }
588271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein        return true;
589271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein    }
590271a030f5d0d3c59715fbeffb31c761279f3f8caMike Klein
591c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    const int fNumRecords;
592a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein
5934d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips    // We do not guarantee anything for operations outside of the cull rect
5944d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips    const SkRect fCullRect;
5954d52afef5cf90a2fed3bb69db71675c6450ab397robertphillips
596533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    // Conservative identity-space bounds for each op in the SkRecord.
59740732b34a1bf94eb44ee4b2327eece8d97735f11mtklein    Bounds* fBounds;
598a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein
599a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    // We walk fCurrentOp through the SkRecord, as we go using updateCTM()
600a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative
601533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    // identity-space bounds of the current clip (fCurrentClipBounds).
602c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    int fCurrentOp;
603e9d2052e4931eeade49042a855b9c1f5ab7c84c5mtklein    SkMatrix fCTM;
604533eb782edaa0b6fece6166d3001edf72ec39f11mtklein    Bounds fCurrentClipBounds;
605a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein
606a723b576aed31a6eb2bdda6388e6bd779d04c6b0mtklein    // Used to track the bounds of Save/Restore blocks and the control ops inside them.
607828ce1f34b3471c108aee6ac2c39ddb90d0be11emtklein    SkTDArray<SaveBounds> fSaveStack;
608c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    SkTDArray<int>   fControlIndices;
6095ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein};
6105ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein
61127f6b0d013572184dc980462cce8d0f3caec1c8ecommit-bot@chromium.org}  // namespace SkRecords
6125ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein
61340732b34a1bf94eb44ee4b2327eece8d97735f11mtkleinvoid SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkRect bounds[]) {
61440732b34a1bf94eb44ee4b2327eece8d97735f11mtklein    SkRecords::FillBounds visitor(cullRect, record, bounds);
615c6ad06acefa096716f8dabed5342f9b89dc43dfemtklein    for (int curOp = 0; curOp < record.count(); curOp++) {
6164e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips        visitor.setCurrentOp(curOp);
617343a63d082bda969d7e8a4e09ba850e931185269mtklein        record.visit(curOp, visitor);
6184e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips    }
61940732b34a1bf94eb44ee4b2327eece8d97735f11mtklein    visitor.cleanUp();
6205ad6ee1b2ce54f8e59b9f5a337c688a98a4b0f2amtklein}
6214e8e3421aa919a82eb1dd287fecbd079f5a320b4robertphillips
622