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 "GrContext.h"
9#include "GrLayerCache.h"
10#include "GrRecordReplaceDraw.h"
11#include "SkCanvasPriv.h"
12#include "SkGrPixelRef.h"
13#include "SkImage.h"
14#include "SkRecordDraw.h"
15#include "SkRecords.h"
16
17static inline void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* result) {
18    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
19    result->setInfo(info);
20    result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref();
21}
22
23static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canvas) {
24
25    // Some image filter can totally filter away a layer (e.g., SkPictureImageFilter's with
26    // no picture).
27    if (!layer->texture()) {
28        return;
29    }
30
31    SkBitmap bm;
32    wrap_texture(layer->texture(),
33                 !layer->isAtlased() ? layer->rect().width()  : layer->texture()->width(),
34                 !layer->isAtlased() ? layer->rect().height() : layer->texture()->height(),
35                 &bm);
36
37    if (layer->isAtlased()) {
38        const SkRect src = SkRect::Make(layer->rect());
39        const SkRect dst = SkRect::Make(layer->srcIR());
40
41        SkASSERT(layer->offset().isZero());
42
43        canvas->save();
44        canvas->setMatrix(SkMatrix::I());
45        canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint());
46        canvas->restore();
47    } else {
48        canvas->drawSprite(bm,
49                           layer->srcIR().fLeft + layer->offset().fX,
50                           layer->srcIR().fTop + layer->offset().fY,
51                           layer->paint());
52    }
53}
54
55// Used by GrRecordReplaceDraw. It intercepts nested drawPicture calls and
56// also draws them with replaced layers.
57class ReplaceDraw : public SkRecords::Draw {
58public:
59    ReplaceDraw(SkCanvas* canvas, GrLayerCache* layerCache,
60                SkPicture const* const drawablePicts[], int drawableCount,
61                const SkPicture* topLevelPicture,
62                const SkPicture* picture,
63                const SkMatrix& initialMatrix,
64                SkPicture::AbortCallback* callback,
65                const unsigned* opIndices, int numIndices)
66        : INHERITED(canvas, drawablePicts, NULL, drawableCount)
67        , fCanvas(canvas)
68        , fLayerCache(layerCache)
69        , fTopLevelPicture(topLevelPicture)
70        , fPicture(picture)
71        , fInitialMatrix(initialMatrix)
72        , fCallback(callback)
73        , fIndex(0)
74        , fNumReplaced(0) {
75        fOpIndexStack.append(numIndices, opIndices);
76    }
77
78    int draw() {
79        const SkBBoxHierarchy* bbh = fPicture->fBBH.get();
80        const SkRecord* record = fPicture->fRecord.get();
81        if (NULL == record) {
82            return 0;
83        }
84
85        fNumReplaced = 0;
86
87        fOps.rewind();
88
89        if (bbh) {
90            // Draw only ops that affect pixels in the canvas's current clip.
91            // The SkRecord and BBH were recorded in identity space.  This canvas
92            // is not necessarily in that same space.  getClipBounds() returns us
93            // this canvas' clip bounds transformed back into identity space, which
94            // lets us query the BBH.
95            SkRect query = { 0, 0, 0, 0 };
96            (void)fCanvas->getClipBounds(&query);
97
98            bbh->search(query, &fOps);
99
100            for (fIndex = 0; fIndex < fOps.count(); ++fIndex) {
101                if (fCallback && fCallback->abort()) {
102                    return fNumReplaced;
103                }
104
105                record->visit<void>(fOps[fIndex], *this);
106            }
107
108        } else {
109            for (fIndex = 0; fIndex < (int) record->count(); ++fIndex) {
110                if (fCallback && fCallback->abort()) {
111                    return fNumReplaced;
112                }
113
114                record->visit<void>(fIndex, *this);
115            }
116        }
117
118        return fNumReplaced;
119    }
120
121    // Same as Draw for all ops except DrawPicture and SaveLayer.
122    template <typename T> void operator()(const T& r) {
123        this->INHERITED::operator()(r);
124    }
125    void operator()(const SkRecords::DrawPicture& dp) {
126
127        int drawPictureOffset;
128        if (fOps.count()) {
129            drawPictureOffset = fOps[fIndex];
130        } else {
131            drawPictureOffset = fIndex;
132        }
133
134        fOpIndexStack.push(drawPictureOffset);
135
136        SkAutoCanvasMatrixPaint acmp(fCanvas, &dp.matrix, dp.paint, dp.picture->cullRect());
137
138        // Draw sub-pictures with the same replacement list but a different picture
139        ReplaceDraw draw(fCanvas, fLayerCache,
140                         this->drawablePicts(), this->drawableCount(),
141                         fTopLevelPicture, dp.picture, fInitialMatrix, fCallback,
142                         fOpIndexStack.begin(), fOpIndexStack.count());
143
144        fNumReplaced += draw.draw();
145
146        fOpIndexStack.pop();
147    }
148    void operator()(const SkRecords::SaveLayer& sl) {
149
150        // For a saveLayer command, check if it can be replaced by a drawBitmap
151        // call and, if so, draw it and then update the current op index accordingly.
152        unsigned startOffset;
153        if (fOps.count()) {
154            startOffset = fOps[fIndex];
155        } else {
156            startOffset = fIndex;
157        }
158
159        fOpIndexStack.push(startOffset);
160
161        GrCachedLayer* layer = fLayerCache->findLayer(fTopLevelPicture->uniqueID(),
162                                                      fInitialMatrix,
163                                                      fOpIndexStack.begin(),
164                                                      fOpIndexStack.count());
165
166        if (layer) {
167            fNumReplaced++;
168
169            draw_replacement_bitmap(layer, fCanvas);
170
171            if (fPicture->fBBH.get()) {
172                while (fOps[fIndex] < layer->stop()) {
173                    ++fIndex;
174                }
175                SkASSERT(fOps[fIndex] == layer->stop());
176            } else {
177                fIndex = layer->stop();
178            }
179            fOpIndexStack.pop();
180            return;
181        }
182
183        // This is a fail for layer hoisting
184        this->INHERITED::operator()(sl);
185
186        fOpIndexStack.pop();
187    }
188
189private:
190    SkCanvas*                 fCanvas;
191    GrLayerCache*             fLayerCache;
192    const SkPicture*          fTopLevelPicture;
193    const SkPicture*          fPicture;
194    const SkMatrix            fInitialMatrix;
195    SkPicture::AbortCallback* fCallback;
196
197    SkTDArray<unsigned>       fOps;
198    int                       fIndex;
199    int                       fNumReplaced;
200
201    // The op code indices of all the enclosing drawPicture and saveLayer calls
202    SkTDArray<unsigned>       fOpIndexStack;
203
204    typedef Draw INHERITED;
205};
206
207int GrRecordReplaceDraw(const SkPicture* picture,
208                        SkCanvas* canvas,
209                        GrLayerCache* layerCache,
210                        const SkMatrix& initialMatrix,
211                        SkPicture::AbortCallback* callback) {
212    SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
213
214    // TODO: drawablePicts?
215    ReplaceDraw draw(canvas, layerCache, NULL, 0,
216                     picture, picture,
217                     initialMatrix, callback, NULL, 0);
218    return draw.draw();
219}
220