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