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 "SkMakeUnique.h"
13 #include "SkPictureRecorder.h"
14 #include "SkSurface.h"
15
16 #if SK_SUPPORT_GPU
17 #include "GrContext.h"
18 #include "GrContextPriv.h"
19 #include "GrSurfaceContext.h"
20 #include "GrSurfaceProxy.h"
21 #include "GrTexture.h"
22 #include "GrTextureProxy.h"
23 #include "../src/image/SkImage_Gpu.h"
24 #endif
25
draw_something(SkCanvas * canvas,const SkRect & bounds)26 static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
27 SkPaint paint;
28 paint.setAntiAlias(true);
29 paint.setColor(SK_ColorRED);
30 paint.setStyle(SkPaint::kStroke_Style);
31 paint.setStrokeWidth(10);
32 canvas->drawRect(bounds, paint);
33 paint.setStyle(SkPaint::kFill_Style);
34 paint.setColor(SK_ColorBLUE);
35 canvas->drawOval(bounds, paint);
36 }
37
38 /*
39 * Exercise drawing pictures inside an image, showing that the image version is pixelated
40 * (correctly) when it is inside an image.
41 */
42 class ImagePictGM : public skiagm::GM {
43 sk_sp<SkPicture> fPicture;
44 sk_sp<SkImage> fImage0;
45 sk_sp<SkImage> fImage1;
46 public:
ImagePictGM()47 ImagePictGM() {}
48
49 protected:
onShortName()50 SkString onShortName() override {
51 return SkString("image-picture");
52 }
53
onISize()54 SkISize onISize() override {
55 return SkISize::Make(850, 450);
56 }
57
onOnceBeforeDraw()58 void onOnceBeforeDraw() override {
59 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
60 SkPictureRecorder recorder;
61 draw_something(recorder.beginRecording(bounds), bounds);
62 fPicture = recorder.finishRecordingAsPicture();
63
64 // extract enough just for the oval.
65 const SkISize size = SkISize::Make(100, 100);
66 auto srgbColorSpace = SkColorSpace::MakeSRGB();
67
68 SkMatrix matrix;
69 matrix.setTranslate(-100, -100);
70 fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
71 SkImage::BitDepth::kU8, srgbColorSpace);
72 matrix.postTranslate(-50, -50);
73 matrix.postRotate(45);
74 matrix.postTranslate(50, 50);
75 fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
76 SkImage::BitDepth::kU8, srgbColorSpace);
77 }
78
drawSet(SkCanvas * canvas) const79 void drawSet(SkCanvas* canvas) const {
80 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
81 canvas->drawPicture(fPicture, &matrix, nullptr);
82 canvas->drawImage(fImage0.get(), 150, 0);
83 canvas->drawImage(fImage1.get(), 300, 0);
84 }
85
onDraw(SkCanvas * canvas)86 void onDraw(SkCanvas* canvas) override {
87 canvas->translate(20, 20);
88
89 this->drawSet(canvas);
90
91 canvas->save();
92 canvas->translate(0, 130);
93 canvas->scale(0.25f, 0.25f);
94 this->drawSet(canvas);
95 canvas->restore();
96
97 canvas->save();
98 canvas->translate(0, 200);
99 canvas->scale(2, 2);
100 this->drawSet(canvas);
101 canvas->restore();
102 }
103
104 private:
105 typedef skiagm::GM INHERITED;
106 };
DEF_GM(return new ImagePictGM;)107 DEF_GM( return new ImagePictGM; )
108
109 ///////////////////////////////////////////////////////////////////////////////////////////////////
110
111 static std::unique_ptr<SkImageGenerator> make_pic_generator(GrContext*, sk_sp<SkPicture> pic) {
112 SkMatrix matrix;
113 matrix.setTranslate(-100, -100);
114 return SkImageGenerator::MakeFromPicture({ 100, 100 }, std::move(pic), &matrix, nullptr,
115 SkImage::BitDepth::kU8,
116 SkColorSpace::MakeSRGB());
117 }
118
119 class RasterGenerator : public SkImageGenerator {
120 public:
RasterGenerator(const SkBitmap & bm)121 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
122 fBM.lockPixels();
123 }
124 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,SkPMColor * ctable,int * ctableCount)125 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
126 SkPMColor* ctable, int* ctableCount) override {
127 SkASSERT(fBM.width() == info.width());
128 SkASSERT(fBM.height() == info.height());
129
130 if (info.colorType() == kIndex_8_SkColorType) {
131 if (SkColorTable* ct = fBM.getColorTable()) {
132 if (ctable) {
133 memcpy(ctable, ct->readColors(), ct->count() * sizeof(SkPMColor));
134 }
135 if (ctableCount) {
136 *ctableCount = ct->count();
137 }
138
139 for (int y = 0; y < info.height(); ++y) {
140 memcpy(pixels, fBM.getAddr8(0, y), fBM.width());
141 pixels = (char*)pixels + rowBytes;
142 }
143 return true;
144 } else {
145 return false;
146 }
147 } else {
148 return fBM.readPixels(info, pixels, rowBytes, 0, 0);
149 }
150 }
151 private:
152 SkBitmap fBM;
153 };
make_ras_generator(GrContext *,sk_sp<SkPicture> pic)154 static std::unique_ptr<SkImageGenerator> make_ras_generator(GrContext*, sk_sp<SkPicture> pic) {
155 SkBitmap bm;
156 bm.allocN32Pixels(100, 100);
157 SkCanvas canvas(bm);
158 canvas.clear(0);
159 canvas.translate(-100, -100);
160 canvas.drawPicture(pic);
161 return skstd::make_unique<RasterGenerator>(bm);
162 }
163
164 // so we can create a color-table
find_closest(SkPMColor c,const SkPMColor table[],int count)165 static int find_closest(SkPMColor c, const SkPMColor table[], int count) {
166 const int cr = SkGetPackedR32(c);
167 const int cg = SkGetPackedG32(c);
168 const int cb = SkGetPackedB32(c);
169
170 int minDist = 999999999;
171 int index = 0;
172 for (int i = 0; i < count; ++i) {
173 int dr = SkAbs32((int)SkGetPackedR32(table[i]) - cr);
174 int dg = SkAbs32((int)SkGetPackedG32(table[i]) - cg);
175 int db = SkAbs32((int)SkGetPackedB32(table[i]) - cb);
176 int dist = dr + dg + db;
177 if (dist < minDist) {
178 minDist = dist;
179 index = i;
180 }
181 }
182 return index;
183 }
184
make_ctable_generator(GrContext *,sk_sp<SkPicture> pic)185 static std::unique_ptr<SkImageGenerator> make_ctable_generator(GrContext*, sk_sp<SkPicture> pic) {
186 SkBitmap bm;
187 bm.allocN32Pixels(100, 100);
188 SkCanvas canvas(bm);
189 canvas.clear(0);
190 canvas.translate(-100, -100);
191 canvas.drawPicture(pic);
192
193 const SkPMColor colors[] = {
194 SkPreMultiplyColor(SK_ColorRED),
195 SkPreMultiplyColor(0),
196 SkPreMultiplyColor(SK_ColorBLUE),
197 };
198 const int count = SK_ARRAY_COUNT(colors);
199 SkImageInfo info = SkImageInfo::Make(100, 100, kIndex_8_SkColorType, kPremul_SkAlphaType);
200
201 SkBitmap bm2;
202 sk_sp<SkColorTable> ct(new SkColorTable(colors, count));
203 bm2.allocPixels(info, nullptr, ct.get());
204 for (int y = 0; y < info.height(); ++y) {
205 for (int x = 0; x < info.width(); ++x) {
206 *bm2.getAddr8(x, y) = find_closest(*bm.getAddr32(x, y), colors, count);
207 }
208 }
209 return skstd::make_unique<RasterGenerator>(bm2);
210 }
211
212 class EmptyGenerator : public SkImageGenerator {
213 public:
EmptyGenerator(const SkImageInfo & info)214 EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
215 };
216
217 #if SK_SUPPORT_GPU
218 class TextureGenerator : public SkImageGenerator {
219 public:
TextureGenerator(GrContext * ctx,const SkImageInfo & info,sk_sp<SkPicture> pic)220 TextureGenerator(GrContext* ctx, const SkImageInfo& info, sk_sp<SkPicture> pic)
221 : SkImageGenerator(info)
222 , fCtx(SkRef(ctx)) {
223
224 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));
225 if (surface) {
226 surface->getCanvas()->clear(0);
227 surface->getCanvas()->translate(-100, -100);
228 surface->getCanvas()->drawPicture(pic);
229 sk_sp<SkImage> image(surface->makeImageSnapshot());
230 fProxy = as_IB(image)->asTextureProxyRef();
231 }
232 }
233 protected:
onGenerateTexture(GrContext * ctx,const SkImageInfo & info,const SkIPoint & origin)234 sk_sp<GrTextureProxy> onGenerateTexture(GrContext* ctx, const SkImageInfo& info,
235 const SkIPoint& origin) override {
236 SkASSERT(ctx);
237 SkASSERT(ctx == fCtx.get());
238
239 if (!fProxy) {
240 return nullptr;
241 }
242
243 if (origin.fX == 0 && origin.fY == 0 &&
244 info.width() == fProxy->width() && info.height() == fProxy->height()) {
245 return fProxy;
246 }
247
248 // need to copy the subset into a new texture
249 GrSurfaceDesc desc = fProxy->desc();
250 desc.fWidth = info.width();
251 desc.fHeight = info.height();
252
253 sk_sp<GrSurfaceContext> dstContext(fCtx->contextPriv().makeDeferredSurfaceContext(
254 desc,
255 SkBackingFit::kExact,
256 SkBudgeted::kNo));
257 if (!dstContext) {
258 return nullptr;
259 }
260
261 if (!dstContext->copy(
262 fProxy.get(),
263 SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
264 SkIPoint::Make(0, 0))) {
265 return nullptr;
266 }
267
268 return dstContext->asTextureProxyRef();
269 }
270
271 private:
272 sk_sp<GrContext> fCtx;
273 sk_sp<GrTextureProxy> fProxy;
274 };
275
make_tex_generator(GrContext * ctx,sk_sp<SkPicture> pic)276 static std::unique_ptr<SkImageGenerator> make_tex_generator(GrContext* ctx, sk_sp<SkPicture> pic) {
277 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
278
279 if (!ctx) {
280 return skstd::make_unique<EmptyGenerator>(info);
281 }
282 return skstd::make_unique<TextureGenerator>(ctx, info, pic);
283 }
284 #endif
285
286 class ImageCacheratorGM : public skiagm::GM {
287 SkString fName;
288 std::unique_ptr<SkImageGenerator> (*fFactory)(GrContext*, sk_sp<SkPicture>);
289 sk_sp<SkPicture> fPicture;
290 std::unique_ptr<SkImageCacherator> fCache;
291 std::unique_ptr<SkImageCacherator> fCacheSubset;
292
293 public:
ImageCacheratorGM(const char suffix[],std::unique_ptr<SkImageGenerator> (* factory)(GrContext *,sk_sp<SkPicture>))294 ImageCacheratorGM(const char suffix[],
295 std::unique_ptr<SkImageGenerator> (*factory)(GrContext*, sk_sp<SkPicture>))
296 : fFactory(factory)
297 {
298 fName.printf("image-cacherator-from-%s", suffix);
299 }
300
301 protected:
onShortName()302 SkString onShortName() override {
303 return fName;
304 }
305
onISize()306 SkISize onISize() override {
307 return SkISize::Make(960, 450);
308 }
309
onOnceBeforeDraw()310 void onOnceBeforeDraw() override {
311 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
312 SkPictureRecorder recorder;
313 draw_something(recorder.beginRecording(bounds), bounds);
314 fPicture = recorder.finishRecordingAsPicture();
315 }
316
makeCaches(GrContext * ctx)317 void makeCaches(GrContext* ctx) {
318 auto gen = fFactory(ctx, fPicture);
319 SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
320 fCache.reset(SkImageCacherator::NewFromGenerator(std::move(gen)));
321
322 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
323
324 gen = fFactory(ctx, fPicture);
325 SkDEBUGCODE(const uint32_t genSubsetID = gen->uniqueID();)
326 fCacheSubset.reset(SkImageCacherator::NewFromGenerator(std::move(gen), &subset));
327
328 // whole caches should have the same ID as the generator. Subsets should be diff
329 SkASSERT(fCache->uniqueID() == genID);
330 SkASSERT(fCacheSubset->uniqueID() != genID);
331 SkASSERT(fCacheSubset->uniqueID() != genSubsetID);
332
333 SkASSERT(fCache->info().dimensions() == SkISize::Make(100, 100));
334 SkASSERT(fCacheSubset->info().dimensions() == SkISize::Make(50, 50));
335 }
336
draw_as_bitmap(SkCanvas * canvas,SkImageCacherator * cache,SkScalar x,SkScalar y)337 static void draw_as_bitmap(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
338 SkBitmap bitmap;
339 cache->lockAsBitmap(canvas->getGrContext(), &bitmap, nullptr,
340 canvas->imageInfo().colorSpace());
341 canvas->drawBitmap(bitmap, x, y);
342 }
343
draw_as_tex(SkCanvas * canvas,SkImageCacherator * cache,SkScalar x,SkScalar y)344 static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
345 #if SK_SUPPORT_GPU
346 sk_sp<SkColorSpace> texColorSpace;
347 sk_sp<GrTextureProxy> proxy(
348 cache->lockAsTextureProxy(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
349 canvas->imageInfo().colorSpace(), &texColorSpace,
350 nullptr, nullptr));
351 if (!proxy) {
352 // show placeholder if we have no texture
353 SkPaint paint;
354 paint.setStyle(SkPaint::kStroke_Style);
355 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(cache->info().width()),
356 SkIntToScalar(cache->info().width()));
357 canvas->drawRect(r, paint);
358 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
359 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
360 return;
361 }
362
363 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
364 sk_sp<SkImage> image(new SkImage_Gpu(canvas->getGrContext(),
365 cache->uniqueID(), kPremul_SkAlphaType,
366 std::move(proxy), std::move(texColorSpace),
367 SkBudgeted::kNo));
368 canvas->drawImage(image.get(), x, y);
369 #endif
370 }
371
drawSet(SkCanvas * canvas) const372 void drawSet(SkCanvas* canvas) const {
373 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
374 canvas->drawPicture(fPicture, &matrix, nullptr);
375
376 // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
377 // way we also can force the generateTexture call.
378
379 draw_as_tex(canvas, fCache.get(), 310, 0);
380 draw_as_tex(canvas, fCacheSubset.get(), 310+101, 0);
381
382 draw_as_bitmap(canvas, fCache.get(), 150, 0);
383 draw_as_bitmap(canvas, fCacheSubset.get(), 150+101, 0);
384 }
385
onDraw(SkCanvas * canvas)386 void onDraw(SkCanvas* canvas) override {
387 this->makeCaches(canvas->getGrContext());
388
389 canvas->translate(20, 20);
390
391 this->drawSet(canvas);
392
393 canvas->save();
394 canvas->translate(0, 130);
395 canvas->scale(0.25f, 0.25f);
396 this->drawSet(canvas);
397 canvas->restore();
398
399 canvas->save();
400 canvas->translate(0, 200);
401 canvas->scale(2, 2);
402 this->drawSet(canvas);
403 canvas->restore();
404 }
405
406 private:
407 typedef skiagm::GM INHERITED;
408 };
409 DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
410 DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
411 DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
412 #if SK_SUPPORT_GPU
413 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
414 #endif
415