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
draw_something(SkCanvas * canvas,const SkRect & bounds)21 static 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 */
37 class ImagePictGM : public skiagm::GM {
38 SkAutoTUnref<SkPicture> fPicture;
39 SkAutoTUnref<SkImage> fImage0;
40 SkAutoTUnref<SkImage> fImage1;
41 public:
ImagePictGM()42 ImagePictGM() {}
43
44 protected:
onShortName()45 SkString onShortName() override {
46 return SkString("image-picture");
47 }
48
onISize()49 SkISize onISize() override {
50 return SkISize::Make(850, 450);
51 }
52
onOnceBeforeDraw()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
drawSet(SkCanvas * canvas) const71 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
onDraw(SkCanvas * canvas)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
96 private:
97 typedef skiagm::GM INHERITED;
98 };
DEF_GM(return new ImagePictGM;)99 DEF_GM( return new ImagePictGM; )
100
101 ///////////////////////////////////////////////////////////////////////////////////////////////////
102
103 static 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
109 class RasterGenerator : public SkImageGenerator {
110 public:
RasterGenerator(const SkBitmap & bm)111 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
112 fBM.lockPixels();
113 }
114 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,SkPMColor * ctable,int * ctableCount)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 }
141 private:
142 SkBitmap fBM;
143 };
make_ras_generator(GrContext *,SkPicture * pic)144 static 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
find_closest(SkPMColor c,const SkPMColor table[],int count)155 static 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
make_ctable_generator(GrContext *,SkPicture * pic)175 static 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
202 class EmptyGenerator : public SkImageGenerator {
203 public:
EmptyGenerator(const SkImageInfo & info)204 EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
205 };
206
207 #if SK_SUPPORT_GPU
208 class TextureGenerator : public SkImageGenerator {
209 public:
TextureGenerator(GrContext * ctx,const SkImageInfo & info,SkPicture * pic)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 }
222 protected:
onGenerateTexture(GrContext * ctx,const SkIRect * subset)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 }
240 private:
241 SkAutoTUnref<GrContext> fCtx;
242 SkAutoTUnref<GrTexture> fTexture;
243 };
make_tex_generator(GrContext * ctx,SkPicture * pic)244 static 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
254 class 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
261 public:
ImageCacheratorGM(const char suffix[],SkImageGenerator * (* factory)(GrContext *,SkPicture *))262 ImageCacheratorGM(const char suffix[], SkImageGenerator* (*factory)(GrContext*, SkPicture*))
263 : fFactory(factory)
264 {
265 fName.printf("image-cacherator-from-%s", suffix);
266 }
267
268 protected:
onShortName()269 SkString onShortName() override {
270 return fName;
271 }
272
onISize()273 SkISize onISize() override {
274 return SkISize::Make(960, 450);
275 }
276
onOnceBeforeDraw()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
makeCaches(GrContext * ctx)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
draw_as_bitmap(SkCanvas * canvas,SkImageCacherator * cache,SkScalar x,SkScalar y)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
draw_as_tex(SkCanvas * canvas,SkImageCacherator * cache,SkScalar x,SkScalar y)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
drawSet(SkCanvas * canvas) const334 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
onDraw(SkCanvas * canvas)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
368 private:
369 typedef skiagm::GM INHERITED;
370 };
371 DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
372 DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
373 DEF_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