blob: 387ceb8d41623006e2998826d1f5abfd0b76b920 [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"
Mike Reed185130c2017-02-15 15:14:16 -050012#include "SkMakeUnique.h"
reedd5b75632015-08-13 09:37:45 -070013#include "SkPictureRecorder.h"
reed8f343722015-08-13 13:32:39 -070014#include "SkSurface.h"
reedd5b75632015-08-13 09:37:45 -070015
16#if SK_SUPPORT_GPU
17#include "GrContext.h"
Robert Phillipse2f7d182016-12-15 09:23:05 -050018#include "GrContextPriv.h"
19#include "GrSurfaceContext.h"
20#include "GrSurfaceProxy.h"
reed8f343722015-08-13 13:32:39 -070021#include "GrTexture.h"
Robert Phillipse2f7d182016-12-15 09:23:05 -050022#include "GrTextureProxy.h"
reed8f343722015-08-13 13:32:39 -070023#include "../src/image/SkImage_Gpu.h"
reedd5b75632015-08-13 09:37:45 -070024#endif
25
26static 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 */
42class ImagePictGM : public skiagm::GM {
reedca2622b2016-03-18 07:25:55 -070043 sk_sp<SkPicture> fPicture;
reed9ce9d672016-03-17 10:51:11 -070044 sk_sp<SkImage> fImage0;
45 sk_sp<SkImage> fImage1;
reedd5b75632015-08-13 09:37:45 -070046public:
47 ImagePictGM() {}
48
49protected:
50 SkString onShortName() override {
51 return SkString("image-picture");
52 }
53
54 SkISize onISize() override {
55 return SkISize::Make(850, 450);
56 }
57
58 void onOnceBeforeDraw() override {
59 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
60 SkPictureRecorder recorder;
61 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -070062 fPicture = recorder.finishRecordingAsPicture();
reedd5b75632015-08-13 09:37:45 -070063
64 // extract enough just for the oval.
65 const SkISize size = SkISize::Make(100, 100);
Matt Sarett77a7a1b2017-02-07 13:56:11 -050066 auto srgbColorSpace = SkColorSpace::MakeSRGB();
reedd5b75632015-08-13 09:37:45 -070067
68 SkMatrix matrix;
69 matrix.setTranslate(-100, -100);
Matt Sarette94255d2017-01-09 12:38:59 -050070 fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
71 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070072 matrix.postTranslate(-50, -50);
73 matrix.postRotate(45);
74 matrix.postTranslate(50, 50);
Matt Sarette94255d2017-01-09 12:38:59 -050075 fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
76 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070077 }
78
79 void drawSet(SkCanvas* canvas) const {
80 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
81 canvas->drawPicture(fPicture, &matrix, nullptr);
reed9ce9d672016-03-17 10:51:11 -070082 canvas->drawImage(fImage0.get(), 150, 0);
83 canvas->drawImage(fImage1.get(), 300, 0);
reedd5b75632015-08-13 09:37:45 -070084 }
85
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
104private:
105 typedef skiagm::GM INHERITED;
106};
107DEF_GM( return new ImagePictGM; )
108
reed8f343722015-08-13 13:32:39 -0700109///////////////////////////////////////////////////////////////////////////////////////////////////
110
Mike Reed185130c2017-02-15 15:14:16 -0500111static std::unique_ptr<SkImageGenerator> make_pic_generator(GrContext*, sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700112 SkMatrix matrix;
113 matrix.setTranslate(-100, -100);
Mike Reed185130c2017-02-15 15:14:16 -0500114 return SkImageGenerator::MakeFromPicture({ 100, 100 }, std::move(pic), &matrix, nullptr,
Matt Sarette94255d2017-01-09 12:38:59 -0500115 SkImage::BitDepth::kU8,
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500116 SkColorSpace::MakeSRGB());
reed935d6cf2015-08-18 11:16:09 -0700117}
118
119class RasterGenerator : public SkImageGenerator {
120public:
reedc4a83e22015-09-11 11:47:27 -0700121 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm) {
122 fBM.lockPixels();
123 }
reed935d6cf2015-08-18 11:16:09 -0700124protected:
125 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
reedc4a83e22015-09-11 11:47:27 -0700126 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 }
reed935d6cf2015-08-18 11:16:09 -0700150 }
151private:
152 SkBitmap fBM;
153};
Mike Reed185130c2017-02-15 15:14:16 -0500154static std::unique_ptr<SkImageGenerator> make_ras_generator(GrContext*, sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700155 SkBitmap bm;
156 bm.allocN32Pixels(100, 100);
157 SkCanvas canvas(bm);
158 canvas.clear(0);
159 canvas.translate(-100, -100);
160 canvas.drawPicture(pic);
Mike Reed185130c2017-02-15 15:14:16 -0500161 return skstd::make_unique<RasterGenerator>(bm);
reed935d6cf2015-08-18 11:16:09 -0700162}
163
reedc4a83e22015-09-11 11:47:27 -0700164// so we can create a color-table
165static 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
Mike Reed185130c2017-02-15 15:14:16 -0500185static std::unique_ptr<SkImageGenerator> make_ctable_generator(GrContext*, sk_sp<SkPicture> pic) {
reedc4a83e22015-09-11 11:47:27 -0700186 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;
Hal Canarycefc4312016-11-04 16:26:16 -0400202 sk_sp<SkColorTable> ct(new SkColorTable(colors, count));
203 bm2.allocPixels(info, nullptr, ct.get());
reedc4a83e22015-09-11 11:47:27 -0700204 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 }
Mike Reed185130c2017-02-15 15:14:16 -0500209 return skstd::make_unique<RasterGenerator>(bm2);
reedc4a83e22015-09-11 11:47:27 -0700210}
211
reed935d6cf2015-08-18 11:16:09 -0700212class EmptyGenerator : public SkImageGenerator {
213public:
214 EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
215};
216
217#if SK_SUPPORT_GPU
218class TextureGenerator : public SkImageGenerator {
219public:
Mike Reed185130c2017-02-15 15:14:16 -0500220 TextureGenerator(GrContext* ctx, const SkImageInfo& info, sk_sp<SkPicture> pic)
reed935d6cf2015-08-18 11:16:09 -0700221 : SkImageGenerator(info)
Robert Phillipse2f7d182016-12-15 09:23:05 -0500222 , fCtx(SkRef(ctx)) {
223
224 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));
scroggoe6f0d6e2016-05-13 07:25:44 -0700225 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());
Robert Phillips6de99042017-01-31 11:31:39 -0500230 fProxy = as_IB(image)->asTextureProxyRef();
scroggoe6f0d6e2016-05-13 07:25:44 -0700231 }
reed935d6cf2015-08-18 11:16:09 -0700232 }
233protected:
Brian Osman222e9ad2016-12-14 15:42:36 -0500234 GrTexture* onGenerateTexture(GrContext* ctx, const SkImageInfo& info,
235 const SkIPoint& origin) override {
reed935d6cf2015-08-18 11:16:09 -0700236 if (ctx) {
237 SkASSERT(ctx == fCtx.get());
238 }
239
Robert Phillipse2f7d182016-12-15 09:23:05 -0500240 if (!fProxy) {
scroggoe6f0d6e2016-05-13 07:25:44 -0700241 return nullptr;
242 }
243
Robert Phillipse2f7d182016-12-15 09:23:05 -0500244 if (origin.fX == 0 && origin.fY == 0 &&
245 info.width() == fProxy->width() && info.height() == fProxy->height()) {
246 return SkSafeRef(fProxy->instantiate(fCtx->textureProvider())->asTexture());
247 }
248
reed935d6cf2015-08-18 11:16:09 -0700249 // need to copy the subset into a new texture
Robert Phillipse2f7d182016-12-15 09:23:05 -0500250 GrSurfaceDesc desc = fProxy->desc();
Brian Osman222e9ad2016-12-14 15:42:36 -0500251 desc.fWidth = info.width();
252 desc.fHeight = info.height();
reed935d6cf2015-08-18 11:16:09 -0700253
Robert Phillipse2f7d182016-12-15 09:23:05 -0500254 sk_sp<GrSurfaceContext> dstContext(fCtx->contextPriv().makeDeferredSurfaceContext(
255 desc,
256 SkBackingFit::kExact,
257 SkBudgeted::kNo));
258 if (!dstContext) {
259 return nullptr;
260 }
261
262 if (!dstContext->copy(
263 fProxy.get(),
264 SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
265 SkIPoint::Make(0, 0))) {
266 return nullptr;
267 }
268
Robert Phillipsf200a902017-01-30 13:27:37 -0500269 GrSurface* dstSurf = dstContext->asSurfaceProxy()->instantiate(fCtx->textureProvider());
Robert Phillipse2f7d182016-12-15 09:23:05 -0500270 if (!dstSurf) {
271 return nullptr;
272 }
273
274 return SkRef(dstSurf->asTexture());
reed935d6cf2015-08-18 11:16:09 -0700275 }
276private:
Robert Phillipse2f7d182016-12-15 09:23:05 -0500277 sk_sp<GrContext> fCtx;
278 sk_sp<GrSurfaceProxy> fProxy;
reed935d6cf2015-08-18 11:16:09 -0700279};
Mike Reed185130c2017-02-15 15:14:16 -0500280static std::unique_ptr<SkImageGenerator> make_tex_generator(GrContext* ctx, sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700281 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
282
283 if (!ctx) {
Mike Reed185130c2017-02-15 15:14:16 -0500284 return skstd::make_unique<EmptyGenerator>(info);
reed935d6cf2015-08-18 11:16:09 -0700285 }
Mike Reed185130c2017-02-15 15:14:16 -0500286 return skstd::make_unique<TextureGenerator>(ctx, info, pic);
reed935d6cf2015-08-18 11:16:09 -0700287}
288#endif
reed8f343722015-08-13 13:32:39 -0700289
290class ImageCacheratorGM : public skiagm::GM {
reed935d6cf2015-08-18 11:16:09 -0700291 SkString fName;
Mike Reed185130c2017-02-15 15:14:16 -0500292 std::unique_ptr<SkImageGenerator> (*fFactory)(GrContext*, sk_sp<SkPicture>);
reedca2622b2016-03-18 07:25:55 -0700293 sk_sp<SkPicture> fPicture;
Ben Wagner145dbcd2016-11-03 14:40:50 -0400294 std::unique_ptr<SkImageCacherator> fCache;
295 std::unique_ptr<SkImageCacherator> fCacheSubset;
reed8f343722015-08-13 13:32:39 -0700296
297public:
Mike Reed185130c2017-02-15 15:14:16 -0500298 ImageCacheratorGM(const char suffix[],
299 std::unique_ptr<SkImageGenerator> (*factory)(GrContext*, sk_sp<SkPicture>))
reed935d6cf2015-08-18 11:16:09 -0700300 : fFactory(factory)
301 {
302 fName.printf("image-cacherator-from-%s", suffix);
303 }
reed8f343722015-08-13 13:32:39 -0700304
305protected:
306 SkString onShortName() override {
reed935d6cf2015-08-18 11:16:09 -0700307 return fName;
reed8f343722015-08-13 13:32:39 -0700308 }
309
310 SkISize onISize() override {
reeda32cc952015-08-19 06:07:29 -0700311 return SkISize::Make(960, 450);
reed8f343722015-08-13 13:32:39 -0700312 }
313
314 void onOnceBeforeDraw() override {
315 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
316 SkPictureRecorder recorder;
317 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -0700318 fPicture = recorder.finishRecordingAsPicture();
reed935d6cf2015-08-18 11:16:09 -0700319 }
reed8f343722015-08-13 13:32:39 -0700320
reed935d6cf2015-08-18 11:16:09 -0700321 void makeCaches(GrContext* ctx) {
Mike Reed185130c2017-02-15 15:14:16 -0500322 auto gen = fFactory(ctx, fPicture);
reed935d6cf2015-08-18 11:16:09 -0700323 SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
Mike Reed185130c2017-02-15 15:14:16 -0500324 fCache.reset(SkImageCacherator::NewFromGenerator(std::move(gen)));
reed935d6cf2015-08-18 11:16:09 -0700325
326 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
327
Mike Reed185130c2017-02-15 15:14:16 -0500328 gen = fFactory(ctx, fPicture);
reed935d6cf2015-08-18 11:16:09 -0700329 SkDEBUGCODE(const uint32_t genSubsetID = gen->uniqueID();)
Mike Reed185130c2017-02-15 15:14:16 -0500330 fCacheSubset.reset(SkImageCacherator::NewFromGenerator(std::move(gen), &subset));
reed935d6cf2015-08-18 11:16:09 -0700331
332 // whole caches should have the same ID as the generator. Subsets should be diff
333 SkASSERT(fCache->uniqueID() == genID);
334 SkASSERT(fCacheSubset->uniqueID() != genID);
335 SkASSERT(fCacheSubset->uniqueID() != genSubsetID);
336
337 SkASSERT(fCache->info().dimensions() == SkISize::Make(100, 100));
338 SkASSERT(fCacheSubset->info().dimensions() == SkISize::Make(50, 50));
339 }
340
341 static void draw_as_bitmap(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
342 SkBitmap bitmap;
Brian Osman61624f02016-12-09 14:51:59 -0500343 cache->lockAsBitmap(&bitmap, nullptr, canvas->imageInfo().colorSpace());
reed935d6cf2015-08-18 11:16:09 -0700344 canvas->drawBitmap(bitmap, x, y);
345 }
346
347 static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
348#if SK_SUPPORT_GPU
Brian Osman7992da32016-11-18 11:28:24 -0500349 sk_sp<SkColorSpace> texColorSpace;
Brian Osman7b8400d2016-11-08 17:08:54 -0500350 sk_sp<GrTexture> texture(
Brian Salomon514baff2016-11-17 15:17:07 -0500351 cache->lockAsTexture(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
Robert Phillips67c18d62017-01-20 12:44:06 -0500352 canvas->imageInfo().colorSpace(), &texColorSpace,
353 nullptr, nullptr));
reed935d6cf2015-08-18 11:16:09 -0700354 if (!texture) {
reeda32cc952015-08-19 06:07:29 -0700355 // show placeholder if we have no texture
356 SkPaint paint;
357 paint.setStyle(SkPaint::kStroke_Style);
358 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(cache->info().width()),
359 SkIntToScalar(cache->info().width()));
360 canvas->drawRect(r, paint);
361 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
362 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
reed935d6cf2015-08-18 11:16:09 -0700363 return;
364 }
365 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
bungeman6bd52842016-10-27 09:30:08 -0700366 sk_sp<SkImage> image(new SkImage_Gpu(cache->info().width(), cache->info().height(),
367 cache->uniqueID(), kPremul_SkAlphaType,
Brian Osman7992da32016-11-18 11:28:24 -0500368 std::move(texture), std::move(texColorSpace),
bungeman6bd52842016-10-27 09:30:08 -0700369 SkBudgeted::kNo));
370 canvas->drawImage(image.get(), x, y);
reed935d6cf2015-08-18 11:16:09 -0700371#endif
reed8f343722015-08-13 13:32:39 -0700372 }
373
374 void drawSet(SkCanvas* canvas) const {
375 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
376 canvas->drawPicture(fPicture, &matrix, nullptr);
377
reed935d6cf2015-08-18 11:16:09 -0700378 // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
379 // way we also can force the generateTexture call.
380
Ben Wagner145dbcd2016-11-03 14:40:50 -0400381 draw_as_tex(canvas, fCache.get(), 310, 0);
382 draw_as_tex(canvas, fCacheSubset.get(), 310+101, 0);
reed935d6cf2015-08-18 11:16:09 -0700383
Ben Wagner145dbcd2016-11-03 14:40:50 -0400384 draw_as_bitmap(canvas, fCache.get(), 150, 0);
385 draw_as_bitmap(canvas, fCacheSubset.get(), 150+101, 0);
reed8f343722015-08-13 13:32:39 -0700386 }
387
388 void onDraw(SkCanvas* canvas) override {
reed935d6cf2015-08-18 11:16:09 -0700389 this->makeCaches(canvas->getGrContext());
390
reed8f343722015-08-13 13:32:39 -0700391 canvas->translate(20, 20);
392
393 this->drawSet(canvas);
394
395 canvas->save();
396 canvas->translate(0, 130);
397 canvas->scale(0.25f, 0.25f);
398 this->drawSet(canvas);
399 canvas->restore();
400
401 canvas->save();
402 canvas->translate(0, 200);
403 canvas->scale(2, 2);
404 this->drawSet(canvas);
405 canvas->restore();
406 }
407
408private:
409 typedef skiagm::GM INHERITED;
410};
reed935d6cf2015-08-18 11:16:09 -0700411DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
412DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
reedc4a83e22015-09-11 11:47:27 -0700413DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
reed935d6cf2015-08-18 11:16:09 -0700414#if SK_SUPPORT_GPU
415 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
416#endif