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 "GrPictureUtils.h"
9
10#include "SkPaintPriv.h"
11#include "SkRecord.h"
12#include "SkRecords.h"
13
14SkPicture::AccelData::Key GrAccelData::ComputeAccelDataKey() {
15    static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain();
16
17    return gGPUID;
18}
19
20// SkRecord visitor to gather saveLayer/restore information.
21class CollectLayers {
22public:
23    CollectLayers(const SkPicture* pict, GrAccelData* accelData)
24        : fPictureID(pict->uniqueID())
25        , fCTM(&SkMatrix::I())
26        , fSaveLayersInStack(0)
27        , fAccelData(accelData) {
28
29        pict->cullRect().roundOut(&fCurrentClipBounds);
30
31        if (NULL == pict->fRecord.get()) {
32            return;
33        }
34
35        for (fCurrentOp = 0; fCurrentOp < pict->fRecord->count(); ++fCurrentOp) {
36            pict->fRecord->visit<void>(fCurrentOp, *this);
37        }
38
39        while (!fSaveStack.isEmpty()) {
40            this->popSaveBlock();
41        }
42    }
43
44    template <typename T> void operator()(const T& op) {
45        this->updateCTM(op);
46        this->updateClipBounds(op);
47        this->trackSaveLayers(op);
48    }
49
50private:
51
52    class SaveInfo {
53    public:
54        SaveInfo() { }
55        SaveInfo(int opIndex, bool isSaveLayer, const SkPaint* paint, const SkIRect& bounds)
56            : fStartIndex(opIndex)
57            , fIsSaveLayer(isSaveLayer)
58            , fHasNestedSaveLayer(false)
59            , fPaint(paint)
60            , fBounds(bounds) {
61
62        }
63
64        int            fStartIndex;
65        bool           fIsSaveLayer;
66        bool           fHasNestedSaveLayer;
67        const SkPaint* fPaint;
68        SkIRect        fBounds;
69    };
70
71    uint32_t            fPictureID;
72    unsigned int        fCurrentOp;
73    const SkMatrix*     fCTM;
74    SkIRect             fCurrentClipBounds;
75    int                 fSaveLayersInStack;
76    SkTDArray<SaveInfo> fSaveStack;
77    GrAccelData*        fAccelData;
78
79    template <typename T> void updateCTM(const T&) { /* most ops don't change the CTM */ }
80    void updateCTM(const SkRecords::Restore& op)   { fCTM = &op.matrix; }
81    void updateCTM(const SkRecords::SetMatrix& op) { fCTM = &op.matrix; }
82
83    template <typename T> void updateClipBounds(const T&) { /* most ops don't change the clip */ }
84    // Each of these devBounds fields is the state of the device bounds after the op.
85    // So Restore's devBounds are those bounds saved by its paired Save or SaveLayer.
86    void updateClipBounds(const SkRecords::Restore& op)    { fCurrentClipBounds = op.devBounds; }
87    void updateClipBounds(const SkRecords::ClipPath& op)   { fCurrentClipBounds = op.devBounds; }
88    void updateClipBounds(const SkRecords::ClipRRect& op)  { fCurrentClipBounds = op.devBounds; }
89    void updateClipBounds(const SkRecords::ClipRect& op)   { fCurrentClipBounds = op.devBounds; }
90    void updateClipBounds(const SkRecords::ClipRegion& op) { fCurrentClipBounds = op.devBounds; }
91    void updateClipBounds(const SkRecords::SaveLayer& op)  {
92        if (op.bounds) {
93            fCurrentClipBounds.intersect(this->adjustAndMap(*op.bounds, op.paint));
94        }
95    }
96
97    template <typename T> void trackSaveLayers(const T& op) {
98        /* most ops aren't involved in saveLayers */
99    }
100    void trackSaveLayers(const SkRecords::Save& s) { this->pushSaveBlock(); }
101    void trackSaveLayers(const SkRecords::SaveLayer& sl) { this->pushSaveLayerBlock(sl.paint); }
102    void trackSaveLayers(const SkRecords::Restore& r) { this->popSaveBlock(); }
103    void trackSaveLayers(const SkRecords::DrawPicture& dp) {
104        // For sub-pictures, we wrap their layer information within the parent
105        // picture's rendering hierarchy
106        const GrAccelData* childData = GPUOptimize(dp.picture);
107
108        for (int i = 0; i < childData->numSaveLayers(); ++i) {
109            const GrAccelData::SaveLayerInfo& src = childData->saveLayerInfo(i);
110
111            this->updateStackForSaveLayer();
112
113            // TODO: need to store an SkRect in GrAccelData::SaveLayerInfo?
114            SkRect srcRect = SkRect::MakeXYWH(SkIntToScalar(src.fOffset.fX),
115                                              SkIntToScalar(src.fOffset.fY),
116                                              SkIntToScalar(src.fSize.width()),
117                                              SkIntToScalar(src.fSize.height()));
118            SkIRect newClip(fCurrentClipBounds);
119            newClip.intersect(this->adjustAndMap(srcRect, dp.paint));
120
121            GrAccelData::SaveLayerInfo& dst = fAccelData->addSaveLayerInfo();
122
123            dst.fValid = true;
124            // If src.fPicture is NULL the layer is in dp.picture; otherwise
125            // it belongs to a sub-picture.
126            dst.fPicture = src.fPicture ? src.fPicture : static_cast<const SkPicture*>(dp.picture);
127            dst.fPicture->ref();
128            dst.fSize = SkISize::Make(newClip.width(), newClip.height());
129            dst.fOffset = SkIPoint::Make(newClip.fLeft, newClip.fTop);
130            dst.fOriginXform = *fCTM;
131            dst.fOriginXform.postConcat(src.fOriginXform);
132            if (src.fPaint) {
133                dst.fPaint = SkNEW_ARGS(SkPaint, (*src.fPaint));
134            }
135            dst.fSaveLayerOpID = src.fSaveLayerOpID;
136            dst.fRestoreOpID = src.fRestoreOpID;
137            dst.fHasNestedLayers = src.fHasNestedLayers;
138            dst.fIsNested = fSaveLayersInStack > 0 || src.fIsNested;
139        }
140    }
141
142    void pushSaveBlock() {
143        fSaveStack.push(SaveInfo(fCurrentOp, false, NULL, SkIRect::MakeEmpty()));
144    }
145
146    // Inform all the saveLayers already on the stack that they now have a
147    // nested saveLayer inside them
148    void updateStackForSaveLayer() {
149        for (int index = fSaveStack.count() - 1; index >= 0; --index) {
150            if (fSaveStack[index].fHasNestedSaveLayer) {
151                break;
152            }
153            fSaveStack[index].fHasNestedSaveLayer = true;
154            if (fSaveStack[index].fIsSaveLayer) {
155                break;
156            }
157        }
158    }
159
160    void pushSaveLayerBlock(const SkPaint* paint) {
161        this->updateStackForSaveLayer();
162
163        fSaveStack.push(SaveInfo(fCurrentOp, true, paint, fCurrentClipBounds));
164        ++fSaveLayersInStack;
165    }
166
167    void popSaveBlock() {
168        if (fSaveStack.count() <= 0) {
169            SkASSERT(false);
170            return;
171        }
172
173        SaveInfo si;
174        fSaveStack.pop(&si);
175
176        if (!si.fIsSaveLayer) {
177            return;
178        }
179
180        --fSaveLayersInStack;
181
182        GrAccelData::SaveLayerInfo& slInfo = fAccelData->addSaveLayerInfo();
183
184        slInfo.fValid = true;
185        SkASSERT(NULL == slInfo.fPicture);  // This layer is in the top-most picture
186        slInfo.fSize = SkISize::Make(si.fBounds.width(), si.fBounds.height());
187        slInfo.fOffset = SkIPoint::Make(si.fBounds.fLeft, si.fBounds.fTop);
188        slInfo.fOriginXform = *fCTM;
189        if (si.fPaint) {
190            slInfo.fPaint = SkNEW_ARGS(SkPaint, (*si.fPaint));
191        }
192        slInfo.fSaveLayerOpID = si.fStartIndex;
193        slInfo.fRestoreOpID = fCurrentOp;
194        slInfo.fHasNestedLayers = si.fHasNestedSaveLayer;
195        slInfo.fIsNested = fSaveLayersInStack > 0;
196    }
197
198    // Returns true if rect was meaningfully adjusted for the effects of paint,
199    // false if the paint could affect the rect in unknown ways.
200    static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) {
201        if (paint) {
202            if (paint->canComputeFastBounds()) {
203                *rect = paint->computeFastBounds(*rect, rect);
204                return true;
205            }
206            return false;
207        }
208        return true;
209    }
210
211    // Adjust rect for all paints that may affect its geometry, then map it to device space.
212    SkIRect adjustAndMap(SkRect rect, const SkPaint* paint) const {
213        // Inverted rectangles really confuse our BBHs.
214        rect.sort();
215
216        // Adjust the rect for its own paint.
217        if (!AdjustForPaint(paint, &rect)) {
218            // The paint could do anything to our bounds.  The only safe answer is the current clip.
219            return fCurrentClipBounds;
220        }
221
222        // Adjust rect for all the paints from the SaveLayers we're inside.
223        for (int i = fSaveStack.count() - 1; i >= 0; i--) {
224            if (!AdjustForPaint(fSaveStack[i].fPaint, &rect)) {
225                // Same deal as above.
226                return fCurrentClipBounds;
227            }
228        }
229
230        // Map the rect back to device space.
231        fCTM->mapRect(&rect);
232        SkIRect devRect;
233        rect.roundOut(&devRect);
234
235        // Nothing can draw outside the current clip.
236        // (Only bounded ops call into this method, so oddballs like Clear don't matter here.)
237        devRect.intersect(fCurrentClipBounds);
238        return devRect;
239    }
240};
241
242
243// GPUOptimize is only intended to be called within the context of SkGpuDevice's
244// EXPERIMENTAL_optimize method.
245const GrAccelData* GPUOptimize(const SkPicture* pict) {
246    if (NULL == pict || pict->cullRect().isEmpty()) {
247        return NULL;
248    }
249
250    SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey();
251
252    const GrAccelData* existing =
253                            static_cast<const GrAccelData*>(pict->EXPERIMENTAL_getAccelData(key));
254    if (existing) {
255        return existing;
256    }
257
258    SkAutoTUnref<GrAccelData> data(SkNEW_ARGS(GrAccelData, (key)));
259
260    pict->EXPERIMENTAL_addAccelData(data);
261
262    CollectLayers collector(pict, data);
263
264    return data;
265}
266