Home | History | Annotate | Download | only in gm
      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 
     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:
     42     ImagePictGM() {}
     43 
     44 protected:
     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 
     96 private:
     97     typedef skiagm::GM INHERITED;
     98 };
     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:
    111     RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
    112         fBM.lockPixels();
    113     }
    114 protected:
    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 };
    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
    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 
    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:
    204     EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
    205 };
    206 
    207 #if SK_SUPPORT_GPU
    208 class TextureGenerator : public SkImageGenerator {
    209 public:
    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:
    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 };
    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:
    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:
    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 
    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