blob: 903024b8ee364b63e3b3b808cecc8a6ab3492944 [file] [log] [blame]
reedd5b75632015-08-13 09:37:45 -07001/*
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"
reed8f343722015-08-13 13:32:39 -070011#include "SkImageCacherator.h"
reedd5b75632015-08-13 09:37:45 -070012#include "SkPictureRecorder.h"
reed8f343722015-08-13 13:32:39 -070013#include "SkSurface.h"
reedd5b75632015-08-13 09:37:45 -070014
15#if SK_SUPPORT_GPU
16#include "GrContext.h"
reed8f343722015-08-13 13:32:39 -070017#include "GrTexture.h"
18#include "../src/image/SkImage_Gpu.h"
reedd5b75632015-08-13 09:37:45 -070019#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 {
reedca2622b2016-03-18 07:25:55 -070038 sk_sp<SkPicture> fPicture;
reed9ce9d672016-03-17 10:51:11 -070039 sk_sp<SkImage> fImage0;
40 sk_sp<SkImage> fImage1;
reedd5b75632015-08-13 09:37:45 -070041public:
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);
reedca2622b2016-03-18 07:25:55 -070057 fPicture = recorder.finishRecordingAsPicture();
reedd5b75632015-08-13 09:37:45 -070058
59 // extract enough just for the oval.
60 const SkISize size = SkISize::Make(100, 100);
61
62 SkMatrix matrix;
63 matrix.setTranslate(-100, -100);
reedca2622b2016-03-18 07:25:55 -070064 fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr);
reedd5b75632015-08-13 09:37:45 -070065 matrix.postTranslate(-50, -50);
66 matrix.postRotate(45);
67 matrix.postTranslate(50, 50);
reedca2622b2016-03-18 07:25:55 -070068 fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr);
reedd5b75632015-08-13 09:37:45 -070069 }
70
71 void drawSet(SkCanvas* canvas) const {
72 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
73 canvas->drawPicture(fPicture, &matrix, nullptr);
reed9ce9d672016-03-17 10:51:11 -070074 canvas->drawImage(fImage0.get(), 150, 0);
75 canvas->drawImage(fImage1.get(), 300, 0);
reedd5b75632015-08-13 09:37:45 -070076 }
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
reed8f343722015-08-13 13:32:39 -0700101///////////////////////////////////////////////////////////////////////////////////////////////////
102
reed935d6cf2015-08-18 11:16:09 -0700103static 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:
reedc4a83e22015-09-11 11:47:27 -0700111 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
112 fBM.lockPixels();
113 }
reed935d6cf2015-08-18 11:16:09 -0700114protected:
115 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
reedc4a83e22015-09-11 11:47:27 -0700116 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 }
reed935d6cf2015-08-18 11:16:09 -0700140 }
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
reedc4a83e22015-09-11 11:47:27 -0700154// 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;
Hal Canarycefc4312016-11-04 16:26:16 -0400192 sk_sp<SkColorTable> ct(new SkColorTable(colors, count));
193 bm2.allocPixels(info, nullptr, ct.get());
reedc4a83e22015-09-11 11:47:27 -0700194 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
reed935d6cf2015-08-18 11:16:09 -0700202class 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 {
reede8f30622016-03-23 18:59:25 -0700214 auto surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));
scroggoe6f0d6e2016-05-13 07:25:44 -0700215 if (surface) {
216 surface->getCanvas()->clear(0);
217 surface->getCanvas()->translate(-100, -100);
218 surface->getCanvas()->drawPicture(pic);
219 sk_sp<SkImage> image(surface->makeImageSnapshot());
220 fTexture.reset(SkRef(as_IB(image)->peekTexture()));
221 }
reed935d6cf2015-08-18 11:16:09 -0700222 }
223protected:
bsalomon5f5527f2015-10-15 12:14:55 -0700224 GrTexture* onGenerateTexture(GrContext* ctx, const SkIRect* subset) override {
reed935d6cf2015-08-18 11:16:09 -0700225 if (ctx) {
226 SkASSERT(ctx == fCtx.get());
227 }
228
scroggoe6f0d6e2016-05-13 07:25:44 -0700229 if (!fTexture) {
230 return nullptr;
231 }
232
reed935d6cf2015-08-18 11:16:09 -0700233 if (!subset) {
234 return SkRef(fTexture.get());
235 }
236 // need to copy the subset into a new texture
237 GrSurfaceDesc desc = fTexture->desc();
238 desc.fWidth = subset->width();
239 desc.fHeight = subset->height();
240
bsalomon5ec26ae2016-02-25 08:33:02 -0800241 GrTexture* dst = fCtx->textureProvider()->createTexture(desc, SkBudgeted::kNo);
Hal Canarycefc4312016-11-04 16:26:16 -0400242 fCtx->copySurface(dst, fTexture.get(), *subset, SkIPoint::Make(0, 0));
reed935d6cf2015-08-18 11:16:09 -0700243 return dst;
244 }
245private:
Hal Canarycefc4312016-11-04 16:26:16 -0400246 sk_sp<GrContext> fCtx;
247 sk_sp<GrTexture> fTexture;
reed935d6cf2015-08-18 11:16:09 -0700248};
249static SkImageGenerator* make_tex_generator(GrContext* ctx, SkPicture* pic) {
250 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
251
252 if (!ctx) {
253 return new EmptyGenerator(info);
254 }
255 return new TextureGenerator(ctx, info, pic);
256}
257#endif
reed8f343722015-08-13 13:32:39 -0700258
259class ImageCacheratorGM : public skiagm::GM {
reed935d6cf2015-08-18 11:16:09 -0700260 SkString fName;
261 SkImageGenerator* (*fFactory)(GrContext*, SkPicture*);
reedca2622b2016-03-18 07:25:55 -0700262 sk_sp<SkPicture> fPicture;
Ben Wagner145dbcd2016-11-03 14:40:50 -0400263 std::unique_ptr<SkImageCacherator> fCache;
264 std::unique_ptr<SkImageCacherator> fCacheSubset;
reed8f343722015-08-13 13:32:39 -0700265
266public:
reed935d6cf2015-08-18 11:16:09 -0700267 ImageCacheratorGM(const char suffix[], SkImageGenerator* (*factory)(GrContext*, SkPicture*))
268 : fFactory(factory)
269 {
270 fName.printf("image-cacherator-from-%s", suffix);
271 }
reed8f343722015-08-13 13:32:39 -0700272
273protected:
274 SkString onShortName() override {
reed935d6cf2015-08-18 11:16:09 -0700275 return fName;
reed8f343722015-08-13 13:32:39 -0700276 }
277
278 SkISize onISize() override {
reeda32cc952015-08-19 06:07:29 -0700279 return SkISize::Make(960, 450);
reed8f343722015-08-13 13:32:39 -0700280 }
281
282 void onOnceBeforeDraw() override {
283 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
284 SkPictureRecorder recorder;
285 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -0700286 fPicture = recorder.finishRecordingAsPicture();
reed935d6cf2015-08-18 11:16:09 -0700287 }
reed8f343722015-08-13 13:32:39 -0700288
reed935d6cf2015-08-18 11:16:09 -0700289 void makeCaches(GrContext* ctx) {
reedca2622b2016-03-18 07:25:55 -0700290 auto gen = fFactory(ctx, fPicture.get());
reed935d6cf2015-08-18 11:16:09 -0700291 SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
reed8f4fe372015-08-13 14:06:46 -0700292 fCache.reset(SkImageCacherator::NewFromGenerator(gen));
reed935d6cf2015-08-18 11:16:09 -0700293
294 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
295
reedca2622b2016-03-18 07:25:55 -0700296 gen = fFactory(ctx, fPicture.get());
reed935d6cf2015-08-18 11:16:09 -0700297 SkDEBUGCODE(const uint32_t genSubsetID = gen->uniqueID();)
298 fCacheSubset.reset(SkImageCacherator::NewFromGenerator(gen, &subset));
299
300 // whole caches should have the same ID as the generator. Subsets should be diff
301 SkASSERT(fCache->uniqueID() == genID);
302 SkASSERT(fCacheSubset->uniqueID() != genID);
303 SkASSERT(fCacheSubset->uniqueID() != genSubsetID);
304
305 SkASSERT(fCache->info().dimensions() == SkISize::Make(100, 100));
306 SkASSERT(fCacheSubset->info().dimensions() == SkISize::Make(50, 50));
307 }
308
309 static void draw_as_bitmap(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
310 SkBitmap bitmap;
fmalita3b0d5322015-09-18 08:07:31 -0700311 cache->lockAsBitmap(&bitmap, nullptr);
reed935d6cf2015-08-18 11:16:09 -0700312 canvas->drawBitmap(bitmap, x, y);
313 }
314
315 static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
316#if SK_SUPPORT_GPU
Brian Osman7b8400d2016-11-08 17:08:54 -0500317 sk_sp<GrTexture> texture(
318 cache->lockAsTexture(canvas->getGrContext(), GrTextureParams::ClampBilerp(),
319 SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware, nullptr));
reed935d6cf2015-08-18 11:16:09 -0700320 if (!texture) {
reeda32cc952015-08-19 06:07:29 -0700321 // show placeholder if we have no texture
322 SkPaint paint;
323 paint.setStyle(SkPaint::kStroke_Style);
324 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(cache->info().width()),
325 SkIntToScalar(cache->info().width()));
326 canvas->drawRect(r, paint);
327 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
328 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
reed935d6cf2015-08-18 11:16:09 -0700329 return;
330 }
331 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
bungeman6bd52842016-10-27 09:30:08 -0700332 sk_sp<SkImage> image(new SkImage_Gpu(cache->info().width(), cache->info().height(),
333 cache->uniqueID(), kPremul_SkAlphaType,
334 std::move(texture),
335 sk_ref_sp(cache->info().colorSpace()),
336 SkBudgeted::kNo));
337 canvas->drawImage(image.get(), x, y);
reed935d6cf2015-08-18 11:16:09 -0700338#endif
reed8f343722015-08-13 13:32:39 -0700339 }
340
341 void drawSet(SkCanvas* canvas) const {
342 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
343 canvas->drawPicture(fPicture, &matrix, nullptr);
344
reed935d6cf2015-08-18 11:16:09 -0700345 // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
346 // way we also can force the generateTexture call.
347
Ben Wagner145dbcd2016-11-03 14:40:50 -0400348 draw_as_tex(canvas, fCache.get(), 310, 0);
349 draw_as_tex(canvas, fCacheSubset.get(), 310+101, 0);
reed935d6cf2015-08-18 11:16:09 -0700350
Ben Wagner145dbcd2016-11-03 14:40:50 -0400351 draw_as_bitmap(canvas, fCache.get(), 150, 0);
352 draw_as_bitmap(canvas, fCacheSubset.get(), 150+101, 0);
reed8f343722015-08-13 13:32:39 -0700353 }
354
355 void onDraw(SkCanvas* canvas) override {
reed935d6cf2015-08-18 11:16:09 -0700356 this->makeCaches(canvas->getGrContext());
357
reed8f343722015-08-13 13:32:39 -0700358 canvas->translate(20, 20);
359
360 this->drawSet(canvas);
361
362 canvas->save();
363 canvas->translate(0, 130);
364 canvas->scale(0.25f, 0.25f);
365 this->drawSet(canvas);
366 canvas->restore();
367
368 canvas->save();
369 canvas->translate(0, 200);
370 canvas->scale(2, 2);
371 this->drawSet(canvas);
372 canvas->restore();
373 }
374
375private:
376 typedef skiagm::GM INHERITED;
377};
reed935d6cf2015-08-18 11:16:09 -0700378DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
379DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
reedc4a83e22015-09-11 11:47:27 -0700380DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
reed935d6cf2015-08-18 11:16:09 -0700381#if SK_SUPPORT_GPU
382 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
383#endif