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