blob: 53ae99022aa5d6a403242a8ecde4146ebae77bd1 [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 {
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
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;
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
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 {
214 SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted,
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, SkImageUsageType, 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, false);
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
reed8f343722015-08-13 13:32:39 -0700253
254class ImageCacheratorGM : public skiagm::GM {
reed935d6cf2015-08-18 11:16:09 -0700255 SkString fName;
256 SkImageGenerator* (*fFactory)(GrContext*, SkPicture*);
257 SkAutoTUnref<SkPicture> fPicture;
reed8f343722015-08-13 13:32:39 -0700258 SkAutoTDelete<SkImageCacherator> fCache;
reed935d6cf2015-08-18 11:16:09 -0700259 SkAutoTDelete<SkImageCacherator> fCacheSubset;
reed8f343722015-08-13 13:32:39 -0700260
261public:
reed935d6cf2015-08-18 11:16:09 -0700262 ImageCacheratorGM(const char suffix[], SkImageGenerator* (*factory)(GrContext*, SkPicture*))
263 : fFactory(factory)
264 {
265 fName.printf("image-cacherator-from-%s", suffix);
266 }
reed8f343722015-08-13 13:32:39 -0700267
268protected:
269 SkString onShortName() override {
reed935d6cf2015-08-18 11:16:09 -0700270 return fName;
reed8f343722015-08-13 13:32:39 -0700271 }
272
273 SkISize onISize() override {
reeda32cc952015-08-19 06:07:29 -0700274 return SkISize::Make(960, 450);
reed8f343722015-08-13 13:32:39 -0700275 }
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());
reed935d6cf2015-08-18 11:16:09 -0700282 }
reed8f343722015-08-13 13:32:39 -0700283
reed935d6cf2015-08-18 11:16:09 -0700284 void makeCaches(GrContext* ctx) {
285 auto gen = fFactory(ctx, fPicture);
286 SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
reed8f4fe372015-08-13 14:06:46 -0700287 fCache.reset(SkImageCacherator::NewFromGenerator(gen));
reed935d6cf2015-08-18 11:16:09 -0700288
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;
fmalita3b0d5322015-09-18 08:07:31 -0700306 cache->lockAsBitmap(&bitmap, nullptr);
reed935d6cf2015-08-18 11:16:09 -0700307 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(),
fmalita3b0d5322015-09-18 08:07:31 -0700313 kUntiled_SkImageUsageType,
314 nullptr));
reed935d6cf2015-08-18 11:16:09 -0700315 if (!texture) {
reeda32cc952015-08-19 06:07:29 -0700316 // 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);
reed935d6cf2015-08-18 11:16:09 -0700324 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,
reed7b6945b2015-09-24 00:50:58 -0700329 SkSurface::kNo_Budgeted));
reed935d6cf2015-08-18 11:16:09 -0700330 canvas->drawImage(image, x, y);
331#endif
reed8f343722015-08-13 13:32:39 -0700332 }
333
334 void drawSet(SkCanvas* canvas) const {
335 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
336 canvas->drawPicture(fPicture, &matrix, nullptr);
337
reed935d6cf2015-08-18 11:16:09 -0700338 // 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);
reed8f343722015-08-13 13:32:39 -0700346 }
347
348 void onDraw(SkCanvas* canvas) override {
reed935d6cf2015-08-18 11:16:09 -0700349 this->makeCaches(canvas->getGrContext());
350
reed8f343722015-08-13 13:32:39 -0700351 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};
reed935d6cf2015-08-18 11:16:09 -0700371DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
372DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
reedc4a83e22015-09-11 11:47:27 -0700373DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
reed935d6cf2015-08-18 11:16:09 -0700374#if SK_SUPPORT_GPU
375 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
376#endif
reed8f343722015-08-13 13:32:39 -0700377
378
379