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
21static 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 */
37class ImagePictGM : public skiagm::GM {
38    SkAutoTUnref<SkPicture> fPicture;
39    SkAutoTUnref<SkImage>   fImage0;
40    SkAutoTUnref<SkImage>   fImage1;
41public:
42    ImagePictGM() {}
43
44protected:
45    SkString onShortName() override {
46        return SkString("image-picture");
47    }
48
49    SkISize onISize() override {
50        return SkISize::Make(850, 450);
51    }
52
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
71    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
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
96private:
97    typedef skiagm::GM INHERITED;
98};
99DEF_GM( return new ImagePictGM; )
100
101///////////////////////////////////////////////////////////////////////////////////////////////////
102
103static 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
109class RasterGenerator : public SkImageGenerator {
110public:
111    RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
112        fBM.lockPixels();
113    }
114protected:
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    }
141private:
142    SkBitmap fBM;
143};
144static 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
155static 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
175static 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
202class EmptyGenerator : public SkImageGenerator {
203public:
204    EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
205};
206
207#if SK_SUPPORT_GPU
208class TextureGenerator : public SkImageGenerator {
209public:
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    }
222protected:
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    }
240private:
241    SkAutoTUnref<GrContext> fCtx;
242    SkAutoTUnref<GrTexture> fTexture;
243};
244static 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
254class 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
261public:
262    ImageCacheratorGM(const char suffix[], SkImageGenerator* (*factory)(GrContext*, SkPicture*))
263        : fFactory(factory)
264    {
265        fName.printf("image-cacherator-from-%s", suffix);
266    }
267
268protected:
269    SkString onShortName() override {
270        return fName;
271    }
272
273    SkISize onISize() override {
274        return SkISize::Make(960, 450);
275    }
276
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
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
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
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
334    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
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
368private:
369    typedef skiagm::GM INHERITED;
370};
371DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
372DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
373DEF_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