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