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