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 
wrap_texture(GrTexture * texture,int width,int height,SkBitmap * result)17 static 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 
draw_replacement_bitmap(GrCachedLayer * layer,SkCanvas * canvas)23 static 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.
57 class ReplaceDraw : public SkRecords::Draw {
58 public:
ReplaceDraw(SkCanvas * canvas,GrLayerCache * layerCache,SkPicture const * const drawablePicts[],int drawableCount,const SkPicture * topLevelPicture,const SkPicture * picture,const SkMatrix & initialMatrix,SkPicture::AbortCallback * callback,const unsigned * opIndices,int numIndices)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 
draw()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.
operator ()(const T & r)122     template <typename T> void operator()(const T& r) {
123         this->INHERITED::operator()(r);
124     }
operator ()(const SkRecords::DrawPicture & dp)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     }
operator ()(const SkRecords::SaveLayer & sl)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 
189 private:
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 
GrRecordReplaceDraw(const SkPicture * picture,SkCanvas * canvas,GrLayerCache * layerCache,const SkMatrix & initialMatrix,SkPicture::AbortCallback * callback)207 int 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