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