blob: 5394f6a656b2375663fd5bcd10eaa46bc1fbc1b7 [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"
Robert Phillipse2f7d182016-12-15 09:23:05 -050017#include "GrContextPriv.h"
18#include "GrSurfaceContext.h"
19#include "GrSurfaceProxy.h"
reed8f343722015-08-13 13:32:39 -070020#include "GrTexture.h"
Robert Phillipse2f7d182016-12-15 09:23:05 -050021#include "GrTextureProxy.h"
reed8f343722015-08-13 13:32:39 -070022#include "../src/image/SkImage_Gpu.h"
reedd5b75632015-08-13 09:37:45 -070023#endif
24
25static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
26 SkPaint paint;
27 paint.setAntiAlias(true);
28 paint.setColor(SK_ColorRED);
29 paint.setStyle(SkPaint::kStroke_Style);
30 paint.setStrokeWidth(10);
31 canvas->drawRect(bounds, paint);
32 paint.setStyle(SkPaint::kFill_Style);
33 paint.setColor(SK_ColorBLUE);
34 canvas->drawOval(bounds, paint);
35}
36
37/*
38 * Exercise drawing pictures inside an image, showing that the image version is pixelated
39 * (correctly) when it is inside an image.
40 */
41class ImagePictGM : public skiagm::GM {
reedca2622b2016-03-18 07:25:55 -070042 sk_sp<SkPicture> fPicture;
reed9ce9d672016-03-17 10:51:11 -070043 sk_sp<SkImage> fImage0;
44 sk_sp<SkImage> fImage1;
reedd5b75632015-08-13 09:37:45 -070045public:
46 ImagePictGM() {}
47
48protected:
49 SkString onShortName() override {
50 return SkString("image-picture");
51 }
52
53 SkISize onISize() override {
54 return SkISize::Make(850, 450);
55 }
56
57 void onOnceBeforeDraw() override {
58 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
59 SkPictureRecorder recorder;
60 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -070061 fPicture = recorder.finishRecordingAsPicture();
reedd5b75632015-08-13 09:37:45 -070062
63 // extract enough just for the oval.
64 const SkISize size = SkISize::Make(100, 100);
Brian Osman138ea972016-12-16 11:55:18 -050065 auto srgbColorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named);
reedd5b75632015-08-13 09:37:45 -070066
67 SkMatrix matrix;
68 matrix.setTranslate(-100, -100);
Matt Sarette94255d2017-01-09 12:38:59 -050069 fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
70 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070071 matrix.postTranslate(-50, -50);
72 matrix.postRotate(45);
73 matrix.postTranslate(50, 50);
Matt Sarette94255d2017-01-09 12:38:59 -050074 fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
75 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070076 }
77
78 void drawSet(SkCanvas* canvas) const {
79 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
80 canvas->drawPicture(fPicture, &matrix, nullptr);
reed9ce9d672016-03-17 10:51:11 -070081 canvas->drawImage(fImage0.get(), 150, 0);
82 canvas->drawImage(fImage1.get(), 300, 0);
reedd5b75632015-08-13 09:37:45 -070083 }
84
85 void onDraw(SkCanvas* canvas) override {
86 canvas->translate(20, 20);
87
88 this->drawSet(canvas);
89
90 canvas->save();
91 canvas->translate(0, 130);
92 canvas->scale(0.25f, 0.25f);
93 this->drawSet(canvas);
94 canvas->restore();
95
96 canvas->save();
97 canvas->translate(0, 200);
98 canvas->scale(2, 2);
99 this->drawSet(canvas);
100 canvas->restore();
101 }
102
103private:
104 typedef skiagm::GM INHERITED;
105};
106DEF_GM( return new ImagePictGM; )
107
reed8f343722015-08-13 13:32:39 -0700108///////////////////////////////////////////////////////////////////////////////////////////////////
109
reed935d6cf2015-08-18 11:16:09 -0700110static SkImageGenerator* make_pic_generator(GrContext*, SkPicture* pic) {
111 SkMatrix matrix;
112 matrix.setTranslate(-100, -100);
Brian Osman138ea972016-12-16 11:55:18 -0500113 return SkImageGenerator::NewFromPicture(SkISize::Make(100, 100), pic, &matrix, nullptr,
Matt Sarette94255d2017-01-09 12:38:59 -0500114 SkImage::BitDepth::kU8,
Brian Osman138ea972016-12-16 11:55:18 -0500115 SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named));
reed935d6cf2015-08-18 11:16:09 -0700116}
117
118class RasterGenerator : public SkImageGenerator {
119public:
reedc4a83e22015-09-11 11:47:27 -0700120 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
121 fBM.lockPixels();
122 }
reed935d6cf2015-08-18 11:16:09 -0700123protected:
124 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
reedc4a83e22015-09-11 11:47:27 -0700125 SkPMColor* ctable, int* ctableCount) override {
126 SkASSERT(fBM.width() == info.width());
127 SkASSERT(fBM.height() == info.height());
128
129 if (info.colorType() == kIndex_8_SkColorType) {
130 if (SkColorTable* ct = fBM.getColorTable()) {
131 if (ctable) {
132 memcpy(ctable, ct->readColors(), ct->count() * sizeof(SkPMColor));
133 }
134 if (ctableCount) {
135 *ctableCount = ct->count();
136 }
137
138 for (int y = 0; y < info.height(); ++y) {
139 memcpy(pixels, fBM.getAddr8(0, y), fBM.width());
140 pixels = (char*)pixels + rowBytes;
141 }
142 return true;
143 } else {
144 return false;
145 }
146 } else {
147 return fBM.readPixels(info, pixels, rowBytes, 0, 0);
148 }
reed935d6cf2015-08-18 11:16:09 -0700149 }
150private:
151 SkBitmap fBM;
152};
153static SkImageGenerator* make_ras_generator(GrContext*, SkPicture* pic) {
154 SkBitmap bm;
155 bm.allocN32Pixels(100, 100);
156 SkCanvas canvas(bm);
157 canvas.clear(0);
158 canvas.translate(-100, -100);
159 canvas.drawPicture(pic);
160 return new RasterGenerator(bm);
161}
162
reedc4a83e22015-09-11 11:47:27 -0700163// so we can create a color-table
164static int find_closest(SkPMColor c, const SkPMColor table[], int count) {
165 const int cr = SkGetPackedR32(c);
166 const int cg = SkGetPackedG32(c);
167 const int cb = SkGetPackedB32(c);
168
169 int minDist = 999999999;
170 int index = 0;
171 for (int i = 0; i < count; ++i) {
172 int dr = SkAbs32((int)SkGetPackedR32(table[i]) - cr);
173 int dg = SkAbs32((int)SkGetPackedG32(table[i]) - cg);
174 int db = SkAbs32((int)SkGetPackedB32(table[i]) - cb);
175 int dist = dr + dg + db;
176 if (dist < minDist) {
177 minDist = dist;
178 index = i;
179 }
180 }
181 return index;
182}
183
184static SkImageGenerator* make_ctable_generator(GrContext*, SkPicture* pic) {
185 SkBitmap bm;
186 bm.allocN32Pixels(100, 100);
187 SkCanvas canvas(bm);
188 canvas.clear(0);
189 canvas.translate(-100, -100);
190 canvas.drawPicture(pic);
191
192 const SkPMColor colors[] = {
193 SkPreMultiplyColor(SK_ColorRED),
194 SkPreMultiplyColor(0),
195 SkPreMultiplyColor(SK_ColorBLUE),
196 };
197 const int count = SK_ARRAY_COUNT(colors);
198 SkImageInfo info = SkImageInfo::Make(100, 100, kIndex_8_SkColorType, kPremul_SkAlphaType);
199
200 SkBitmap bm2;
Hal Canarycefc4312016-11-04 16:26:16 -0400201 sk_sp<SkColorTable> ct(new SkColorTable(colors, count));
202 bm2.allocPixels(info, nullptr, ct.get());
reedc4a83e22015-09-11 11:47:27 -0700203 for (int y = 0; y < info.height(); ++y) {
204 for (int x = 0; x < info.width(); ++x) {
205 *bm2.getAddr8(x, y) = find_closest(*bm.getAddr32(x, y), colors, count);
206 }
207 }
208 return new RasterGenerator(bm2);
209}
210
reed935d6cf2015-08-18 11:16:09 -0700211class EmptyGenerator : public SkImageGenerator {
212public:
213 EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
214};
215
216#if SK_SUPPORT_GPU
217class TextureGenerator : public SkImageGenerator {
218public:
219 TextureGenerator(GrContext* ctx, const SkImageInfo& info, SkPicture* pic)
220 : SkImageGenerator(info)
Robert Phillipse2f7d182016-12-15 09:23:05 -0500221 , fCtx(SkRef(ctx)) {
222
223 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));
scroggoe6f0d6e2016-05-13 07:25:44 -0700224 if (surface) {
225 surface->getCanvas()->clear(0);
226 surface->getCanvas()->translate(-100, -100);
227 surface->getCanvas()->drawPicture(pic);
228 sk_sp<SkImage> image(surface->makeImageSnapshot());
Robert Phillipse2f7d182016-12-15 09:23:05 -0500229 fProxy = GrSurfaceProxy::MakeWrapped(sk_ref_sp(as_IB(image)->peekTexture()));
scroggoe6f0d6e2016-05-13 07:25:44 -0700230 }
reed935d6cf2015-08-18 11:16:09 -0700231 }
232protected:
Brian Osman222e9ad2016-12-14 15:42:36 -0500233 GrTexture* onGenerateTexture(GrContext* ctx, const SkImageInfo& info,
234 const SkIPoint& origin) override {
reed935d6cf2015-08-18 11:16:09 -0700235 if (ctx) {
236 SkASSERT(ctx == fCtx.get());
237 }
238
Robert Phillipse2f7d182016-12-15 09:23:05 -0500239 if (!fProxy) {
scroggoe6f0d6e2016-05-13 07:25:44 -0700240 return nullptr;
241 }
242
Robert Phillipse2f7d182016-12-15 09:23:05 -0500243 if (origin.fX == 0 && origin.fY == 0 &&
244 info.width() == fProxy->width() && info.height() == fProxy->height()) {
245 return SkSafeRef(fProxy->instantiate(fCtx->textureProvider())->asTexture());
246 }
247
reed935d6cf2015-08-18 11:16:09 -0700248 // need to copy the subset into a new texture
Robert Phillipse2f7d182016-12-15 09:23:05 -0500249 GrSurfaceDesc desc = fProxy->desc();
Brian Osman222e9ad2016-12-14 15:42:36 -0500250 desc.fWidth = info.width();
251 desc.fHeight = info.height();
reed935d6cf2015-08-18 11:16:09 -0700252
Robert Phillipse2f7d182016-12-15 09:23:05 -0500253 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 GrSurface* dstSurf = dstContext->asDeferredSurface()->instantiate(fCtx->textureProvider());
269 if (!dstSurf) {
270 return nullptr;
271 }
272
273 return SkRef(dstSurf->asTexture());
reed935d6cf2015-08-18 11:16:09 -0700274 }
275private:
Robert Phillipse2f7d182016-12-15 09:23:05 -0500276 sk_sp<GrContext> fCtx;
277 sk_sp<GrSurfaceProxy> fProxy;
reed935d6cf2015-08-18 11:16:09 -0700278};
279static SkImageGenerator* make_tex_generator(GrContext* ctx, SkPicture* pic) {
280 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
281
282 if (!ctx) {
283 return new EmptyGenerator(info);
284 }
285 return new TextureGenerator(ctx, info, pic);
286}
287#endif
reed8f343722015-08-13 13:32:39 -0700288
289class ImageCacheratorGM : public skiagm::GM {
reed935d6cf2015-08-18 11:16:09 -0700290 SkString fName;
291 SkImageGenerator* (*fFactory)(GrContext*, SkPicture*);
reedca2622b2016-03-18 07:25:55 -0700292 sk_sp<SkPicture> fPicture;
Ben Wagner145dbcd2016-11-03 14:40:50 -0400293 std::unique_ptr<SkImageCacherator> fCache;
294 std::unique_ptr<SkImageCacherator> fCacheSubset;
reed8f343722015-08-13 13:32:39 -0700295
296public:
reed935d6cf2015-08-18 11:16:09 -0700297 ImageCacheratorGM(const char suffix[], SkImageGenerator* (*factory)(GrContext*, SkPicture*))
298 : fFactory(factory)
299 {
300 fName.printf("image-cacherator-from-%s", suffix);
301 }
reed8f343722015-08-13 13:32:39 -0700302
303protected:
304 SkString onShortName() override {
reed935d6cf2015-08-18 11:16:09 -0700305 return fName;
reed8f343722015-08-13 13:32:39 -0700306 }
307
308 SkISize onISize() override {
reeda32cc952015-08-19 06:07:29 -0700309 return SkISize::Make(960, 450);
reed8f343722015-08-13 13:32:39 -0700310 }
311
312 void onOnceBeforeDraw() override {
313 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
314 SkPictureRecorder recorder;
315 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -0700316 fPicture = recorder.finishRecordingAsPicture();
reed935d6cf2015-08-18 11:16:09 -0700317 }
reed8f343722015-08-13 13:32:39 -0700318
reed935d6cf2015-08-18 11:16:09 -0700319 void makeCaches(GrContext* ctx) {
reedca2622b2016-03-18 07:25:55 -0700320 auto gen = fFactory(ctx, fPicture.get());
reed935d6cf2015-08-18 11:16:09 -0700321 SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
reed8f4fe372015-08-13 14:06:46 -0700322 fCache.reset(SkImageCacherator::NewFromGenerator(gen));
reed935d6cf2015-08-18 11:16:09 -0700323
324 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
325
reedca2622b2016-03-18 07:25:55 -0700326 gen = fFactory(ctx, fPicture.get());
reed935d6cf2015-08-18 11:16:09 -0700327 SkDEBUGCODE(const uint32_t genSubsetID = gen->uniqueID();)
328 fCacheSubset.reset(SkImageCacherator::NewFromGenerator(gen, &subset));
329
330 // whole caches should have the same ID as the generator. Subsets should be diff
331 SkASSERT(fCache->uniqueID() == genID);
332 SkASSERT(fCacheSubset->uniqueID() != genID);
333 SkASSERT(fCacheSubset->uniqueID() != genSubsetID);
334
335 SkASSERT(fCache->info().dimensions() == SkISize::Make(100, 100));
336 SkASSERT(fCacheSubset->info().dimensions() == SkISize::Make(50, 50));
337 }
338
339 static void draw_as_bitmap(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
340 SkBitmap bitmap;
Brian Osman61624f02016-12-09 14:51:59 -0500341 cache->lockAsBitmap(&bitmap, nullptr, canvas->imageInfo().colorSpace());
reed935d6cf2015-08-18 11:16:09 -0700342 canvas->drawBitmap(bitmap, x, y);
343 }
344
345 static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
346#if SK_SUPPORT_GPU
Brian Osman7992da32016-11-18 11:28:24 -0500347 sk_sp<SkColorSpace> texColorSpace;
Brian Osman7b8400d2016-11-08 17:08:54 -0500348 sk_sp<GrTexture> texture(
Brian Salomon514baff2016-11-17 15:17:07 -0500349 cache->lockAsTexture(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
Robert Phillips67c18d62017-01-20 12:44:06 -0500350 canvas->imageInfo().colorSpace(), &texColorSpace,
351 nullptr, nullptr));
reed935d6cf2015-08-18 11:16:09 -0700352 if (!texture) {
reeda32cc952015-08-19 06:07:29 -0700353 // show placeholder if we have no texture
354 SkPaint paint;
355 paint.setStyle(SkPaint::kStroke_Style);
356 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(cache->info().width()),
357 SkIntToScalar(cache->info().width()));
358 canvas->drawRect(r, paint);
359 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
360 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
reed935d6cf2015-08-18 11:16:09 -0700361 return;
362 }
363 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
bungeman6bd52842016-10-27 09:30:08 -0700364 sk_sp<SkImage> image(new SkImage_Gpu(cache->info().width(), cache->info().height(),
365 cache->uniqueID(), kPremul_SkAlphaType,
Brian Osman7992da32016-11-18 11:28:24 -0500366 std::move(texture), std::move(texColorSpace),
bungeman6bd52842016-10-27 09:30:08 -0700367 SkBudgeted::kNo));
368 canvas->drawImage(image.get(), x, y);
reed935d6cf2015-08-18 11:16:09 -0700369#endif
reed8f343722015-08-13 13:32:39 -0700370 }
371
372 void drawSet(SkCanvas* canvas) const {
373 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
374 canvas->drawPicture(fPicture, &matrix, nullptr);
375
reed935d6cf2015-08-18 11:16:09 -0700376 // 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
Ben Wagner145dbcd2016-11-03 14:40:50 -0400379 draw_as_tex(canvas, fCache.get(), 310, 0);
380 draw_as_tex(canvas, fCacheSubset.get(), 310+101, 0);
reed935d6cf2015-08-18 11:16:09 -0700381
Ben Wagner145dbcd2016-11-03 14:40:50 -0400382 draw_as_bitmap(canvas, fCache.get(), 150, 0);
383 draw_as_bitmap(canvas, fCacheSubset.get(), 150+101, 0);
reed8f343722015-08-13 13:32:39 -0700384 }
385
386 void onDraw(SkCanvas* canvas) override {
reed935d6cf2015-08-18 11:16:09 -0700387 this->makeCaches(canvas->getGrContext());
388
reed8f343722015-08-13 13:32:39 -0700389 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
406private:
407 typedef skiagm::GM INHERITED;
408};
reed935d6cf2015-08-18 11:16:09 -0700409DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
410DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
reedc4a83e22015-09-11 11:47:27 -0700411DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
reed935d6cf2015-08-18 11:16:09 -0700412#if SK_SUPPORT_GPU
413 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
414#endif