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 "SkImageGenerator.h"
     12 #include "SkImage_Base.h"
     13 #include "SkMakeUnique.h"
     14 #include "SkPictureRecorder.h"
     15 #include "SkSurface.h"
     16 
     17 #include "GrContext.h"
     18 #include "GrContextPriv.h"
     19 #include "GrSurfaceContext.h"
     20 #include "GrTextureProxy.h"
     21 #include "../src/image/SkImage_Gpu.h"
     22 
     23 static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
     24     SkPaint paint;
     25     paint.setAntiAlias(true);
     26     paint.setColor(SK_ColorRED);
     27     paint.setStyle(SkPaint::kStroke_Style);
     28     paint.setStrokeWidth(10);
     29     canvas->drawRect(bounds, paint);
     30     paint.setStyle(SkPaint::kFill_Style);
     31     paint.setColor(SK_ColorBLUE);
     32     canvas->drawOval(bounds, paint);
     33 }
     34 
     35 /*
     36  *  Exercise drawing pictures inside an image, showing that the image version is pixelated
     37  *  (correctly) when it is inside an image.
     38  */
     39 class ImagePictGM : public skiagm::GM {
     40     sk_sp<SkPicture> fPicture;
     41     sk_sp<SkImage>   fImage0;
     42     sk_sp<SkImage>   fImage1;
     43 public:
     44     ImagePictGM() {}
     45 
     46 protected:
     47     SkString onShortName() override {
     48         return SkString("image-picture");
     49     }
     50 
     51     SkISize onISize() override {
     52         return SkISize::Make(850, 450);
     53     }
     54 
     55     void onOnceBeforeDraw() override {
     56         const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
     57         SkPictureRecorder recorder;
     58         draw_something(recorder.beginRecording(bounds), bounds);
     59         fPicture = recorder.finishRecordingAsPicture();
     60 
     61         // extract enough just for the oval.
     62         const SkISize size = SkISize::Make(100, 100);
     63         auto srgbColorSpace = SkColorSpace::MakeSRGB();
     64 
     65         SkMatrix matrix;
     66         matrix.setTranslate(-100, -100);
     67         fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
     68                                            SkImage::BitDepth::kU8, srgbColorSpace);
     69         matrix.postTranslate(-50, -50);
     70         matrix.postRotate(45);
     71         matrix.postTranslate(50, 50);
     72         fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
     73                                            SkImage::BitDepth::kU8, srgbColorSpace);
     74     }
     75 
     76     void drawSet(SkCanvas* canvas) const {
     77         SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
     78         canvas->drawPicture(fPicture, &matrix, nullptr);
     79         canvas->drawImage(fImage0.get(), 150, 0);
     80         canvas->drawImage(fImage1.get(), 300, 0);
     81     }
     82 
     83     void onDraw(SkCanvas* canvas) override {
     84         canvas->translate(20, 20);
     85 
     86         this->drawSet(canvas);
     87 
     88         canvas->save();
     89         canvas->translate(0, 130);
     90         canvas->scale(0.25f, 0.25f);
     91         this->drawSet(canvas);
     92         canvas->restore();
     93 
     94         canvas->save();
     95         canvas->translate(0, 200);
     96         canvas->scale(2, 2);
     97         this->drawSet(canvas);
     98         canvas->restore();
     99     }
    100 
    101 private:
    102     typedef skiagm::GM INHERITED;
    103 };
    104 DEF_GM( return new ImagePictGM; )
    105 
    106 ///////////////////////////////////////////////////////////////////////////////////////////////////
    107 
    108 static std::unique_ptr<SkImageGenerator> make_pic_generator(GrContext*, sk_sp<SkPicture> pic) {
    109     SkMatrix matrix;
    110     matrix.setTranslate(-100, -100);
    111     return SkImageGenerator::MakeFromPicture({ 100, 100 }, std::move(pic), &matrix, nullptr,
    112                                             SkImage::BitDepth::kU8,
    113                                             SkColorSpace::MakeSRGB());
    114 }
    115 
    116 class RasterGenerator : public SkImageGenerator {
    117 public:
    118     RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm)
    119     {}
    120 
    121 protected:
    122     bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
    123                      const Options&) override {
    124         SkASSERT(fBM.width() == info.width());
    125         SkASSERT(fBM.height() == info.height());
    126         return fBM.readPixels(info, pixels, rowBytes, 0, 0);
    127     }
    128 private:
    129     SkBitmap fBM;
    130 };
    131 static std::unique_ptr<SkImageGenerator> make_ras_generator(GrContext*, sk_sp<SkPicture> pic) {
    132     SkBitmap bm;
    133     bm.allocN32Pixels(100, 100);
    134     SkCanvas canvas(bm);
    135     canvas.clear(0);
    136     canvas.translate(-100, -100);
    137     canvas.drawPicture(pic);
    138     return skstd::make_unique<RasterGenerator>(bm);
    139 }
    140 
    141 class EmptyGenerator : public SkImageGenerator {
    142 public:
    143     EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
    144 };
    145 
    146 class TextureGenerator : public SkImageGenerator {
    147 public:
    148     TextureGenerator(GrContext* ctx, const SkImageInfo& info, sk_sp<SkPicture> pic)
    149         : SkImageGenerator(info)
    150         , fCtx(SkRef(ctx)) {
    151 
    152         sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, info, 0,
    153                                                              kTopLeft_GrSurfaceOrigin, nullptr));
    154         if (surface) {
    155             surface->getCanvas()->clear(0);
    156             surface->getCanvas()->translate(-100, -100);
    157             surface->getCanvas()->drawPicture(pic);
    158             sk_sp<SkImage> image(surface->makeImageSnapshot());
    159             fProxy = as_IB(image)->asTextureProxyRef(fCtx.get());
    160         }
    161     }
    162 protected:
    163     sk_sp<GrTextureProxy> onGenerateTexture(GrRecordingContext* ctx, const SkImageInfo& info,
    164                                             const SkIPoint& origin,
    165                                             bool willBeMipped) override {
    166         SkASSERT(ctx);
    167         SkASSERT(ctx == fCtx.get());
    168 
    169         if (!fProxy) {
    170             return nullptr;
    171         }
    172 
    173         if (origin.fX == 0 && origin.fY == 0 &&
    174             info.width() == fProxy->width() && info.height() == fProxy->height()) {
    175             return fProxy;
    176         }
    177 
    178         // need to copy the subset into a new texture
    179         GrSurfaceDesc desc;
    180         desc.fWidth = info.width();
    181         desc.fHeight = info.height();
    182         desc.fConfig = fProxy->config();
    183 
    184         GrMipMapped mipMapped = willBeMipped ? GrMipMapped::kYes : GrMipMapped::kNo;
    185 
    186         sk_sp<GrSurfaceContext> dstContext(fCtx->priv().makeDeferredSurfaceContext(
    187                 fProxy->backendFormat(), desc, fProxy->origin(), mipMapped, SkBackingFit::kExact,
    188                 SkBudgeted::kYes));
    189         if (!dstContext) {
    190             return nullptr;
    191         }
    192 
    193         if (!dstContext->copy(
    194                             fProxy.get(),
    195                             SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
    196                             SkIPoint::Make(0, 0))) {
    197             return nullptr;
    198         }
    199 
    200         return dstContext->asTextureProxyRef();
    201     }
    202 
    203 private:
    204     sk_sp<GrContext>      fCtx;
    205     sk_sp<GrTextureProxy> fProxy;
    206 };
    207 
    208 static std::unique_ptr<SkImageGenerator> make_tex_generator(GrContext* ctx, sk_sp<SkPicture> pic) {
    209     const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
    210 
    211     if (!ctx) {
    212         return skstd::make_unique<EmptyGenerator>(info);
    213     }
    214     return skstd::make_unique<TextureGenerator>(ctx, info, pic);
    215 }
    216 
    217 class ImageCacheratorGM : public skiagm::GM {
    218     SkString                         fName;
    219     std::unique_ptr<SkImageGenerator> (*fFactory)(GrContext*, sk_sp<SkPicture>);
    220     sk_sp<SkPicture>                 fPicture;
    221     sk_sp<SkImage>                   fImage;
    222     sk_sp<SkImage>                   fImageSubset;
    223 
    224 public:
    225     ImageCacheratorGM(const char suffix[],
    226                       std::unique_ptr<SkImageGenerator> (*factory)(GrContext*, sk_sp<SkPicture>))
    227         : fFactory(factory)
    228     {
    229         fName.printf("image-cacherator-from-%s", suffix);
    230     }
    231 
    232 protected:
    233     SkString onShortName() override {
    234         return fName;
    235     }
    236 
    237     SkISize onISize() override {
    238         return SkISize::Make(960, 450);
    239     }
    240 
    241     void onOnceBeforeDraw() override {
    242         const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
    243         SkPictureRecorder recorder;
    244         draw_something(recorder.beginRecording(bounds), bounds);
    245         fPicture = recorder.finishRecordingAsPicture();
    246     }
    247 
    248     void makeCaches(GrContext* ctx) {
    249         auto gen = fFactory(ctx, fPicture);
    250         fImage = SkImage::MakeFromGenerator(std::move(gen));
    251 
    252         const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
    253 
    254         gen = fFactory(ctx, fPicture);
    255         fImageSubset = SkImage::MakeFromGenerator(std::move(gen), &subset);
    256 
    257         SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
    258         SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
    259     }
    260 
    261     static void draw_as_bitmap(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
    262         SkBitmap bitmap;
    263         as_IB(image)->getROPixels(&bitmap);
    264         canvas->drawBitmap(bitmap, x, y);
    265     }
    266 
    267     static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
    268         sk_sp<GrTextureProxy> proxy(as_IB(image)->asTextureProxyRef(
    269                 canvas->getGrContext(), GrSamplerState::ClampBilerp(), nullptr));
    270         if (!proxy) {
    271             // show placeholder if we have no texture
    272             SkPaint paint;
    273             paint.setStyle(SkPaint::kStroke_Style);
    274             SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(image->width()),
    275                                         SkIntToScalar(image->width()));
    276             canvas->drawRect(r, paint);
    277             canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
    278             canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
    279             return;
    280         }
    281 
    282         // No API to draw a GrTexture directly, so we cheat and create a private image subclass
    283         sk_sp<SkImage> texImage(new SkImage_Gpu(sk_ref_sp(canvas->getGrContext()),
    284                                                 image->uniqueID(), kPremul_SkAlphaType,
    285                                                 std::move(proxy), image->refColorSpace()));
    286         canvas->drawImage(texImage.get(), x, y);
    287     }
    288 
    289     void drawSet(SkCanvas* canvas) const {
    290         SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
    291         canvas->drawPicture(fPicture, &matrix, nullptr);
    292 
    293         // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
    294         // way we also can force the generateTexture call.
    295 
    296         draw_as_tex(canvas, fImage.get(), 310, 0);
    297         draw_as_tex(canvas, fImageSubset.get(), 310+101, 0);
    298 
    299         draw_as_bitmap(canvas, fImage.get(), 150, 0);
    300         draw_as_bitmap(canvas, fImageSubset.get(), 150+101, 0);
    301     }
    302 
    303     void onDraw(SkCanvas* canvas) override {
    304         this->makeCaches(canvas->getGrContext());
    305 
    306         canvas->translate(20, 20);
    307 
    308         this->drawSet(canvas);
    309 
    310         canvas->save();
    311         canvas->translate(0, 130);
    312         canvas->scale(0.25f, 0.25f);
    313         this->drawSet(canvas);
    314         canvas->restore();
    315 
    316         canvas->save();
    317         canvas->translate(0, 200);
    318         canvas->scale(2, 2);
    319         this->drawSet(canvas);
    320         canvas->restore();
    321     }
    322 
    323 private:
    324     typedef skiagm::GM INHERITED;
    325 };
    326 DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
    327 DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
    328 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
    329