• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2015 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 "gm.h"
9 #include "SkCanvas.h"
10 #include "SkImage.h"
11 #include "SkImageCacherator.h"
12 #include "SkPictureRecorder.h"
13 #include "SkSurface.h"
14 
15 #if SK_SUPPORT_GPU
16 #include "GrContext.h"
17 #include "GrTexture.h"
18 #include "../src/image/SkImage_Gpu.h"
19 #endif
20 
draw_something(SkCanvas * canvas,const SkRect & bounds)21 static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
22     SkPaint paint;
23     paint.setAntiAlias(true);
24     paint.setColor(SK_ColorRED);
25     paint.setStyle(SkPaint::kStroke_Style);
26     paint.setStrokeWidth(10);
27     canvas->drawRect(bounds, paint);
28     paint.setStyle(SkPaint::kFill_Style);
29     paint.setColor(SK_ColorBLUE);
30     canvas->drawOval(bounds, paint);
31 }
32 
33 /*
34  *  Exercise drawing pictures inside an image, showing that the image version is pixelated
35  *  (correctly) when it is inside an image.
36  */
37 class ImagePictGM : public skiagm::GM {
38     SkAutoTUnref<SkPicture> fPicture;
39     SkAutoTUnref<SkImage>   fImage0;
40     SkAutoTUnref<SkImage>   fImage1;
41 public:
ImagePictGM()42     ImagePictGM() {}
43 
44 protected:
onShortName()45     SkString onShortName() override {
46         return SkString("image-picture");
47     }
48 
onISize()49     SkISize onISize() override {
50         return SkISize::Make(850, 450);
51     }
52 
onOnceBeforeDraw()53     void onOnceBeforeDraw() override {
54         const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
55         SkPictureRecorder recorder;
56         draw_something(recorder.beginRecording(bounds), bounds);
57         fPicture.reset(recorder.endRecording());
58 
59         // extract enough just for the oval.
60         const SkISize size = SkISize::Make(100, 100);
61 
62         SkMatrix matrix;
63         matrix.setTranslate(-100, -100);
64         fImage0.reset(SkImage::NewFromPicture(fPicture, size, &matrix, nullptr));
65         matrix.postTranslate(-50, -50);
66         matrix.postRotate(45);
67         matrix.postTranslate(50, 50);
68         fImage1.reset(SkImage::NewFromPicture(fPicture, size, &matrix, nullptr));
69     }
70 
drawSet(SkCanvas * canvas) const71     void drawSet(SkCanvas* canvas) const {
72         SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
73         canvas->drawPicture(fPicture, &matrix, nullptr);
74         canvas->drawImage(fImage0, 150, 0);
75         canvas->drawImage(fImage1, 300, 0);
76     }
77 
onDraw(SkCanvas * canvas)78     void onDraw(SkCanvas* canvas) override {
79         canvas->translate(20, 20);
80 
81         this->drawSet(canvas);
82 
83         canvas->save();
84         canvas->translate(0, 130);
85         canvas->scale(0.25f, 0.25f);
86         this->drawSet(canvas);
87         canvas->restore();
88 
89         canvas->save();
90         canvas->translate(0, 200);
91         canvas->scale(2, 2);
92         this->drawSet(canvas);
93         canvas->restore();
94     }
95 
96 private:
97     typedef skiagm::GM INHERITED;
98 };
DEF_GM(return new ImagePictGM;)99 DEF_GM( return new ImagePictGM; )
100 
101 ///////////////////////////////////////////////////////////////////////////////////////////////////
102 
103 static SkImageGenerator* make_pic_generator(GrContext*, SkPicture* pic) {
104     SkMatrix matrix;
105     matrix.setTranslate(-100, -100);
106     return SkImageGenerator::NewFromPicture(SkISize::Make(100, 100), pic, &matrix, nullptr);
107 }
108 
109 class RasterGenerator : public SkImageGenerator {
110 public:
RasterGenerator(const SkBitmap & bm)111     RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
112         fBM.lockPixels();
113     }
114 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,SkPMColor * ctable,int * ctableCount)115     bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
116                      SkPMColor* ctable, int* ctableCount) override {
117         SkASSERT(fBM.width() == info.width());
118         SkASSERT(fBM.height() == info.height());
119 
120         if (info.colorType() == kIndex_8_SkColorType) {
121             if (SkColorTable* ct = fBM.getColorTable()) {
122                 if (ctable) {
123                     memcpy(ctable, ct->readColors(), ct->count() * sizeof(SkPMColor));
124                 }
125                 if (ctableCount) {
126                     *ctableCount = ct->count();
127                 }
128 
129                 for (int y = 0; y < info.height(); ++y) {
130                     memcpy(pixels, fBM.getAddr8(0, y), fBM.width());
131                     pixels = (char*)pixels + rowBytes;
132                 }
133                 return true;
134             } else {
135                 return false;
136             }
137         } else {
138             return fBM.readPixels(info, pixels, rowBytes, 0, 0);
139         }
140     }
141 private:
142     SkBitmap fBM;
143 };
make_ras_generator(GrContext *,SkPicture * pic)144 static SkImageGenerator* make_ras_generator(GrContext*, SkPicture* pic) {
145     SkBitmap bm;
146     bm.allocN32Pixels(100, 100);
147     SkCanvas canvas(bm);
148     canvas.clear(0);
149     canvas.translate(-100, -100);
150     canvas.drawPicture(pic);
151     return new RasterGenerator(bm);
152 }
153 
154 // so we can create a color-table
find_closest(SkPMColor c,const SkPMColor table[],int count)155 static int find_closest(SkPMColor c, const SkPMColor table[], int count) {
156     const int cr = SkGetPackedR32(c);
157     const int cg = SkGetPackedG32(c);
158     const int cb = SkGetPackedB32(c);
159 
160     int minDist = 999999999;
161     int index = 0;
162     for (int i = 0; i < count; ++i) {
163         int dr = SkAbs32((int)SkGetPackedR32(table[i]) - cr);
164         int dg = SkAbs32((int)SkGetPackedG32(table[i]) - cg);
165         int db = SkAbs32((int)SkGetPackedB32(table[i]) - cb);
166         int dist = dr + dg + db;
167         if (dist < minDist) {
168             minDist = dist;
169             index = i;
170         }
171     }
172     return index;
173 }
174 
make_ctable_generator(GrContext *,SkPicture * pic)175 static SkImageGenerator* make_ctable_generator(GrContext*, SkPicture* pic) {
176     SkBitmap bm;
177     bm.allocN32Pixels(100, 100);
178     SkCanvas canvas(bm);
179     canvas.clear(0);
180     canvas.translate(-100, -100);
181     canvas.drawPicture(pic);
182 
183     const SkPMColor colors[] = {
184         SkPreMultiplyColor(SK_ColorRED),
185         SkPreMultiplyColor(0),
186         SkPreMultiplyColor(SK_ColorBLUE),
187     };
188     const int count = SK_ARRAY_COUNT(colors);
189     SkImageInfo info = SkImageInfo::Make(100, 100, kIndex_8_SkColorType, kPremul_SkAlphaType);
190 
191     SkBitmap bm2;
192     SkAutoTUnref<SkColorTable> ct(new SkColorTable(colors, count));
193     bm2.allocPixels(info, nullptr, ct);
194     for (int y = 0; y < info.height(); ++y) {
195         for (int x = 0; x < info.width(); ++x) {
196             *bm2.getAddr8(x, y) = find_closest(*bm.getAddr32(x, y), colors, count);
197         }
198     }
199     return new RasterGenerator(bm2);
200 }
201 
202 class EmptyGenerator : public SkImageGenerator {
203 public:
EmptyGenerator(const SkImageInfo & info)204     EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
205 };
206 
207 #if SK_SUPPORT_GPU
208 class TextureGenerator : public SkImageGenerator {
209 public:
TextureGenerator(GrContext * ctx,const SkImageInfo & info,SkPicture * pic)210     TextureGenerator(GrContext* ctx, const SkImageInfo& info, SkPicture* pic)
211         : SkImageGenerator(info)
212         , fCtx(SkRef(ctx))
213     {
214         SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, SkBudgeted::kNo,
215                                                                    info, 0));
216         surface->getCanvas()->clear(0);
217         surface->getCanvas()->translate(-100, -100);
218         surface->getCanvas()->drawPicture(pic);
219         SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
220         fTexture.reset(SkRef(image->getTexture()));
221     }
222 protected:
onGenerateTexture(GrContext * ctx,const SkIRect * subset)223     GrTexture* onGenerateTexture(GrContext* ctx, const SkIRect* subset) override {
224         if (ctx) {
225             SkASSERT(ctx == fCtx.get());
226         }
227 
228         if (!subset) {
229             return SkRef(fTexture.get());
230         }
231         // need to copy the subset into a new texture
232         GrSurfaceDesc desc = fTexture->desc();
233         desc.fWidth = subset->width();
234         desc.fHeight = subset->height();
235 
236         GrTexture* dst = fCtx->textureProvider()->createTexture(desc, SkBudgeted::kNo);
237         fCtx->copySurface(dst, fTexture, *subset, SkIPoint::Make(0, 0));
238         return dst;
239     }
240 private:
241     SkAutoTUnref<GrContext> fCtx;
242     SkAutoTUnref<GrTexture> fTexture;
243 };
make_tex_generator(GrContext * ctx,SkPicture * pic)244 static SkImageGenerator* make_tex_generator(GrContext* ctx, SkPicture* pic) {
245     const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
246 
247     if (!ctx) {
248         return new EmptyGenerator(info);
249     }
250     return new TextureGenerator(ctx, info, pic);
251 }
252 #endif
253 
254 class ImageCacheratorGM : public skiagm::GM {
255     SkString                         fName;
256     SkImageGenerator*                (*fFactory)(GrContext*, SkPicture*);
257     SkAutoTUnref<SkPicture>          fPicture;
258     SkAutoTDelete<SkImageCacherator> fCache;
259     SkAutoTDelete<SkImageCacherator> fCacheSubset;
260 
261 public:
ImageCacheratorGM(const char suffix[],SkImageGenerator * (* factory)(GrContext *,SkPicture *))262     ImageCacheratorGM(const char suffix[], SkImageGenerator* (*factory)(GrContext*, SkPicture*))
263         : fFactory(factory)
264     {
265         fName.printf("image-cacherator-from-%s", suffix);
266     }
267 
268 protected:
onShortName()269     SkString onShortName() override {
270         return fName;
271     }
272 
onISize()273     SkISize onISize() override {
274         return SkISize::Make(960, 450);
275     }
276 
onOnceBeforeDraw()277     void onOnceBeforeDraw() override {
278         const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
279         SkPictureRecorder recorder;
280         draw_something(recorder.beginRecording(bounds), bounds);
281         fPicture.reset(recorder.endRecording());
282     }
283 
makeCaches(GrContext * ctx)284     void makeCaches(GrContext* ctx) {
285         auto gen = fFactory(ctx, fPicture);
286         SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
287         fCache.reset(SkImageCacherator::NewFromGenerator(gen));
288 
289         const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
290 
291         gen = fFactory(ctx, fPicture);
292         SkDEBUGCODE(const uint32_t genSubsetID = gen->uniqueID();)
293         fCacheSubset.reset(SkImageCacherator::NewFromGenerator(gen, &subset));
294 
295         // whole caches should have the same ID as the generator. Subsets should be diff
296         SkASSERT(fCache->uniqueID() == genID);
297         SkASSERT(fCacheSubset->uniqueID() != genID);
298         SkASSERT(fCacheSubset->uniqueID() != genSubsetID);
299 
300         SkASSERT(fCache->info().dimensions() == SkISize::Make(100, 100));
301         SkASSERT(fCacheSubset->info().dimensions() == SkISize::Make(50, 50));
302     }
303 
draw_as_bitmap(SkCanvas * canvas,SkImageCacherator * cache,SkScalar x,SkScalar y)304     static void draw_as_bitmap(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
305         SkBitmap bitmap;
306         cache->lockAsBitmap(&bitmap, nullptr);
307         canvas->drawBitmap(bitmap, x, y);
308     }
309 
draw_as_tex(SkCanvas * canvas,SkImageCacherator * cache,SkScalar x,SkScalar y)310     static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
311 #if SK_SUPPORT_GPU
312         SkAutoTUnref<GrTexture> texture(cache->lockAsTexture(canvas->getGrContext(),
313                                                              GrTextureParams::ClampBilerp(),
314                                                              nullptr));
315         if (!texture) {
316             // show placeholder if we have no texture
317             SkPaint paint;
318             paint.setStyle(SkPaint::kStroke_Style);
319             SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(cache->info().width()),
320                                         SkIntToScalar(cache->info().width()));
321             canvas->drawRect(r, paint);
322             canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
323             canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
324             return;
325         }
326         // No API to draw a GrTexture directly, so we cheat and create a private image subclass
327         SkAutoTUnref<SkImage> image(new SkImage_Gpu(cache->info().width(), cache->info().height(),
328                                                     cache->uniqueID(), kPremul_SkAlphaType, texture,
329                                                     SkBudgeted::kNo));
330         canvas->drawImage(image, x, y);
331 #endif
332     }
333 
drawSet(SkCanvas * canvas) const334     void drawSet(SkCanvas* canvas) const {
335         SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
336         canvas->drawPicture(fPicture, &matrix, nullptr);
337 
338         // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
339         // way we also can force the generateTexture call.
340 
341         draw_as_tex(canvas, fCache, 310, 0);
342         draw_as_tex(canvas, fCacheSubset, 310+101, 0);
343 
344         draw_as_bitmap(canvas, fCache, 150, 0);
345         draw_as_bitmap(canvas, fCacheSubset, 150+101, 0);
346     }
347 
onDraw(SkCanvas * canvas)348     void onDraw(SkCanvas* canvas) override {
349         this->makeCaches(canvas->getGrContext());
350 
351         canvas->translate(20, 20);
352 
353         this->drawSet(canvas);
354 
355         canvas->save();
356         canvas->translate(0, 130);
357         canvas->scale(0.25f, 0.25f);
358         this->drawSet(canvas);
359         canvas->restore();
360 
361         canvas->save();
362         canvas->translate(0, 200);
363         canvas->scale(2, 2);
364         this->drawSet(canvas);
365         canvas->restore();
366     }
367 
368 private:
369     typedef skiagm::GM INHERITED;
370 };
371 DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
372 DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
373 DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
374 #if SK_SUPPORT_GPU
375     DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
376 #endif
377 
378 
379 
380