blob: 16ee896bfa357d469181f7080b8de088d44e6838 [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"
Brian Osmandf7e0752017-04-26 16:20:28 -040011#include "SkImageGenerator.h"
Brian Osmana28e2b02017-04-24 16:44:03 -040012#include "SkImage_Base.h"
Mike Reed185130c2017-02-15 15:14:16 -050013#include "SkMakeUnique.h"
reedd5b75632015-08-13 09:37:45 -070014#include "SkPictureRecorder.h"
reed8f343722015-08-13 13:32:39 -070015#include "SkSurface.h"
reedd5b75632015-08-13 09:37:45 -070016
17#if SK_SUPPORT_GPU
18#include "GrContext.h"
Robert Phillipse2f7d182016-12-15 09:23:05 -050019#include "GrContextPriv.h"
20#include "GrSurfaceContext.h"
21#include "GrSurfaceProxy.h"
reed8f343722015-08-13 13:32:39 -070022#include "GrTexture.h"
Robert Phillipse2f7d182016-12-15 09:23:05 -050023#include "GrTextureProxy.h"
reed8f343722015-08-13 13:32:39 -070024#include "../src/image/SkImage_Gpu.h"
reedd5b75632015-08-13 09:37:45 -070025#endif
26
27static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
28 SkPaint paint;
29 paint.setAntiAlias(true);
30 paint.setColor(SK_ColorRED);
31 paint.setStyle(SkPaint::kStroke_Style);
32 paint.setStrokeWidth(10);
33 canvas->drawRect(bounds, paint);
34 paint.setStyle(SkPaint::kFill_Style);
35 paint.setColor(SK_ColorBLUE);
36 canvas->drawOval(bounds, paint);
37}
38
39/*
40 * Exercise drawing pictures inside an image, showing that the image version is pixelated
41 * (correctly) when it is inside an image.
42 */
43class ImagePictGM : public skiagm::GM {
reedca2622b2016-03-18 07:25:55 -070044 sk_sp<SkPicture> fPicture;
reed9ce9d672016-03-17 10:51:11 -070045 sk_sp<SkImage> fImage0;
46 sk_sp<SkImage> fImage1;
reedd5b75632015-08-13 09:37:45 -070047public:
48 ImagePictGM() {}
49
50protected:
51 SkString onShortName() override {
52 return SkString("image-picture");
53 }
54
55 SkISize onISize() override {
56 return SkISize::Make(850, 450);
57 }
58
59 void onOnceBeforeDraw() override {
60 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
61 SkPictureRecorder recorder;
62 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -070063 fPicture = recorder.finishRecordingAsPicture();
reedd5b75632015-08-13 09:37:45 -070064
65 // extract enough just for the oval.
66 const SkISize size = SkISize::Make(100, 100);
Matt Sarett77a7a1b2017-02-07 13:56:11 -050067 auto srgbColorSpace = SkColorSpace::MakeSRGB();
reedd5b75632015-08-13 09:37:45 -070068
69 SkMatrix matrix;
70 matrix.setTranslate(-100, -100);
Matt Sarette94255d2017-01-09 12:38:59 -050071 fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
72 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070073 matrix.postTranslate(-50, -50);
74 matrix.postRotate(45);
75 matrix.postTranslate(50, 50);
Matt Sarette94255d2017-01-09 12:38:59 -050076 fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
77 SkImage::BitDepth::kU8, srgbColorSpace);
reedd5b75632015-08-13 09:37:45 -070078 }
79
80 void drawSet(SkCanvas* canvas) const {
81 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
82 canvas->drawPicture(fPicture, &matrix, nullptr);
reed9ce9d672016-03-17 10:51:11 -070083 canvas->drawImage(fImage0.get(), 150, 0);
84 canvas->drawImage(fImage1.get(), 300, 0);
reedd5b75632015-08-13 09:37:45 -070085 }
86
87 void onDraw(SkCanvas* canvas) override {
88 canvas->translate(20, 20);
89
90 this->drawSet(canvas);
91
92 canvas->save();
93 canvas->translate(0, 130);
94 canvas->scale(0.25f, 0.25f);
95 this->drawSet(canvas);
96 canvas->restore();
97
98 canvas->save();
99 canvas->translate(0, 200);
100 canvas->scale(2, 2);
101 this->drawSet(canvas);
102 canvas->restore();
103 }
104
105private:
106 typedef skiagm::GM INHERITED;
107};
108DEF_GM( return new ImagePictGM; )
109
reed8f343722015-08-13 13:32:39 -0700110///////////////////////////////////////////////////////////////////////////////////////////////////
111
Mike Reed185130c2017-02-15 15:14:16 -0500112static std::unique_ptr<SkImageGenerator> make_pic_generator(GrContext*, sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700113 SkMatrix matrix;
114 matrix.setTranslate(-100, -100);
Mike Reed185130c2017-02-15 15:14:16 -0500115 return SkImageGenerator::MakeFromPicture({ 100, 100 }, std::move(pic), &matrix, nullptr,
Matt Sarette94255d2017-01-09 12:38:59 -0500116 SkImage::BitDepth::kU8,
Matt Sarett77a7a1b2017-02-07 13:56:11 -0500117 SkColorSpace::MakeSRGB());
reed935d6cf2015-08-18 11:16:09 -0700118}
119
120class RasterGenerator : public SkImageGenerator {
121public:
Mike Reed4edb5d22017-04-17 11:02:51 -0400122 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm)
123 {}
124
reed935d6cf2015-08-18 11:16:09 -0700125protected:
126 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
reedc4a83e22015-09-11 11:47:27 -0700127 SkPMColor* ctable, int* ctableCount) override {
128 SkASSERT(fBM.width() == info.width());
129 SkASSERT(fBM.height() == info.height());
130
131 if (info.colorType() == kIndex_8_SkColorType) {
132 if (SkColorTable* ct = fBM.getColorTable()) {
133 if (ctable) {
134 memcpy(ctable, ct->readColors(), ct->count() * sizeof(SkPMColor));
135 }
136 if (ctableCount) {
137 *ctableCount = ct->count();
138 }
139
140 for (int y = 0; y < info.height(); ++y) {
141 memcpy(pixels, fBM.getAddr8(0, y), fBM.width());
142 pixels = (char*)pixels + rowBytes;
143 }
144 return true;
145 } else {
146 return false;
147 }
148 } else {
149 return fBM.readPixels(info, pixels, rowBytes, 0, 0);
150 }
reed935d6cf2015-08-18 11:16:09 -0700151 }
152private:
153 SkBitmap fBM;
154};
Mike Reed185130c2017-02-15 15:14:16 -0500155static std::unique_ptr<SkImageGenerator> make_ras_generator(GrContext*, sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700156 SkBitmap bm;
157 bm.allocN32Pixels(100, 100);
158 SkCanvas canvas(bm);
159 canvas.clear(0);
160 canvas.translate(-100, -100);
161 canvas.drawPicture(pic);
Mike Reed185130c2017-02-15 15:14:16 -0500162 return skstd::make_unique<RasterGenerator>(bm);
reed935d6cf2015-08-18 11:16:09 -0700163}
164
reedc4a83e22015-09-11 11:47:27 -0700165// so we can create a color-table
166static int find_closest(SkPMColor c, const SkPMColor table[], int count) {
167 const int cr = SkGetPackedR32(c);
168 const int cg = SkGetPackedG32(c);
169 const int cb = SkGetPackedB32(c);
170
171 int minDist = 999999999;
172 int index = 0;
173 for (int i = 0; i < count; ++i) {
174 int dr = SkAbs32((int)SkGetPackedR32(table[i]) - cr);
175 int dg = SkAbs32((int)SkGetPackedG32(table[i]) - cg);
176 int db = SkAbs32((int)SkGetPackedB32(table[i]) - cb);
177 int dist = dr + dg + db;
178 if (dist < minDist) {
179 minDist = dist;
180 index = i;
181 }
182 }
183 return index;
184}
185
Mike Reed185130c2017-02-15 15:14:16 -0500186static std::unique_ptr<SkImageGenerator> make_ctable_generator(GrContext*, sk_sp<SkPicture> pic) {
reedc4a83e22015-09-11 11:47:27 -0700187 SkBitmap bm;
188 bm.allocN32Pixels(100, 100);
189 SkCanvas canvas(bm);
190 canvas.clear(0);
191 canvas.translate(-100, -100);
192 canvas.drawPicture(pic);
193
194 const SkPMColor colors[] = {
195 SkPreMultiplyColor(SK_ColorRED),
196 SkPreMultiplyColor(0),
197 SkPreMultiplyColor(SK_ColorBLUE),
198 };
199 const int count = SK_ARRAY_COUNT(colors);
200 SkImageInfo info = SkImageInfo::Make(100, 100, kIndex_8_SkColorType, kPremul_SkAlphaType);
201
202 SkBitmap bm2;
Mike Reed6b3155c2017-04-03 14:41:44 -0400203 bm2.allocPixels(info, SkColorTable::Make(colors, count));
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:
Robert Phillips4447b642017-03-03 11:10:18 -0500234 sk_sp<GrTextureProxy> onGenerateTexture(GrContext* ctx, const SkImageInfo& info,
235 const SkIPoint& origin) override {
236 SkASSERT(ctx);
237 SkASSERT(ctx == fCtx.get());
reed935d6cf2015-08-18 11:16:09 -0700238
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()) {
Robert Phillips4447b642017-03-03 11:10:18 -0500245 return fProxy;
Robert Phillipse2f7d182016-12-15 09:23:05 -0500246 }
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
Robert Phillips4447b642017-03-03 11:10:18 -0500268 return dstContext->asTextureProxyRef();
reed935d6cf2015-08-18 11:16:09 -0700269 }
Robert Phillips4447b642017-03-03 11:10:18 -0500270
reed935d6cf2015-08-18 11:16:09 -0700271private:
Robert Phillipse2f7d182016-12-15 09:23:05 -0500272 sk_sp<GrContext> fCtx;
Robert Phillips4447b642017-03-03 11:10:18 -0500273 sk_sp<GrTextureProxy> fProxy;
reed935d6cf2015-08-18 11:16:09 -0700274};
Robert Phillips4447b642017-03-03 11:10:18 -0500275
Mike Reed185130c2017-02-15 15:14:16 -0500276static std::unique_ptr<SkImageGenerator> make_tex_generator(GrContext* ctx, sk_sp<SkPicture> pic) {
reed935d6cf2015-08-18 11:16:09 -0700277 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
278
279 if (!ctx) {
Mike Reed185130c2017-02-15 15:14:16 -0500280 return skstd::make_unique<EmptyGenerator>(info);
reed935d6cf2015-08-18 11:16:09 -0700281 }
Mike Reed185130c2017-02-15 15:14:16 -0500282 return skstd::make_unique<TextureGenerator>(ctx, info, pic);
reed935d6cf2015-08-18 11:16:09 -0700283}
284#endif
reed8f343722015-08-13 13:32:39 -0700285
286class ImageCacheratorGM : public skiagm::GM {
reed935d6cf2015-08-18 11:16:09 -0700287 SkString fName;
Mike Reed185130c2017-02-15 15:14:16 -0500288 std::unique_ptr<SkImageGenerator> (*fFactory)(GrContext*, sk_sp<SkPicture>);
reedca2622b2016-03-18 07:25:55 -0700289 sk_sp<SkPicture> fPicture;
Brian Osmana28e2b02017-04-24 16:44:03 -0400290 sk_sp<SkImage> fImage;
291 sk_sp<SkImage> fImageSubset;
reed8f343722015-08-13 13:32:39 -0700292
293public:
Mike Reed185130c2017-02-15 15:14:16 -0500294 ImageCacheratorGM(const char suffix[],
295 std::unique_ptr<SkImageGenerator> (*factory)(GrContext*, sk_sp<SkPicture>))
reed935d6cf2015-08-18 11:16:09 -0700296 : fFactory(factory)
297 {
298 fName.printf("image-cacherator-from-%s", suffix);
299 }
reed8f343722015-08-13 13:32:39 -0700300
301protected:
302 SkString onShortName() override {
reed935d6cf2015-08-18 11:16:09 -0700303 return fName;
reed8f343722015-08-13 13:32:39 -0700304 }
305
306 SkISize onISize() override {
reeda32cc952015-08-19 06:07:29 -0700307 return SkISize::Make(960, 450);
reed8f343722015-08-13 13:32:39 -0700308 }
309
310 void onOnceBeforeDraw() override {
311 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
312 SkPictureRecorder recorder;
313 draw_something(recorder.beginRecording(bounds), bounds);
reedca2622b2016-03-18 07:25:55 -0700314 fPicture = recorder.finishRecordingAsPicture();
reed935d6cf2015-08-18 11:16:09 -0700315 }
reed8f343722015-08-13 13:32:39 -0700316
reed935d6cf2015-08-18 11:16:09 -0700317 void makeCaches(GrContext* ctx) {
Mike Reed185130c2017-02-15 15:14:16 -0500318 auto gen = fFactory(ctx, fPicture);
Brian Osmana28e2b02017-04-24 16:44:03 -0400319 fImage = SkImage::MakeFromGenerator(std::move(gen));
reed935d6cf2015-08-18 11:16:09 -0700320
321 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
322
Mike Reed185130c2017-02-15 15:14:16 -0500323 gen = fFactory(ctx, fPicture);
Brian Osmana28e2b02017-04-24 16:44:03 -0400324 fImageSubset = SkImage::MakeFromGenerator(std::move(gen), &subset);
reed935d6cf2015-08-18 11:16:09 -0700325
Brian Osmandf7e0752017-04-26 16:20:28 -0400326 SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
327 SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
reed935d6cf2015-08-18 11:16:09 -0700328 }
329
Brian Osmana28e2b02017-04-24 16:44:03 -0400330 static void draw_as_bitmap(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
reed935d6cf2015-08-18 11:16:09 -0700331 SkBitmap bitmap;
Brian Osmana28e2b02017-04-24 16:44:03 -0400332 as_IB(image)->getROPixels(&bitmap, canvas->imageInfo().colorSpace());
reed935d6cf2015-08-18 11:16:09 -0700333 canvas->drawBitmap(bitmap, x, y);
334 }
335
Brian Osmana28e2b02017-04-24 16:44:03 -0400336 static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
reed935d6cf2015-08-18 11:16:09 -0700337#if SK_SUPPORT_GPU
Brian Osman7992da32016-11-18 11:28:24 -0500338 sk_sp<SkColorSpace> texColorSpace;
Robert Phillips4f358be2017-03-23 08:21:00 -0400339 sk_sp<GrTextureProxy> proxy(
Brian Osmana28e2b02017-04-24 16:44:03 -0400340 as_IB(image)->asTextureProxyRef(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
341 canvas->imageInfo().colorSpace(), &texColorSpace,
342 nullptr));
Robert Phillips4f358be2017-03-23 08:21:00 -0400343 if (!proxy) {
reeda32cc952015-08-19 06:07:29 -0700344 // show placeholder if we have no texture
345 SkPaint paint;
346 paint.setStyle(SkPaint::kStroke_Style);
Brian Osmana28e2b02017-04-24 16:44:03 -0400347 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(image->width()),
348 SkIntToScalar(image->width()));
reeda32cc952015-08-19 06:07:29 -0700349 canvas->drawRect(r, paint);
350 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
351 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
reed935d6cf2015-08-18 11:16:09 -0700352 return;
353 }
Robert Phillips0ae6faa2017-03-21 16:22:00 -0400354
reed935d6cf2015-08-18 11:16:09 -0700355 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
Brian Osmana28e2b02017-04-24 16:44:03 -0400356 sk_sp<SkImage> texImage(new SkImage_Gpu(canvas->getGrContext(), image->uniqueID(),
357 kPremul_SkAlphaType, std::move(proxy),
358 std::move(texColorSpace), SkBudgeted::kNo));
359 canvas->drawImage(texImage.get(), x, y);
reed935d6cf2015-08-18 11:16:09 -0700360#endif
reed8f343722015-08-13 13:32:39 -0700361 }
362
363 void drawSet(SkCanvas* canvas) const {
364 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
365 canvas->drawPicture(fPicture, &matrix, nullptr);
366
reed935d6cf2015-08-18 11:16:09 -0700367 // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
368 // way we also can force the generateTexture call.
369
Brian Osmana28e2b02017-04-24 16:44:03 -0400370 draw_as_tex(canvas, fImage.get(), 310, 0);
371 draw_as_tex(canvas, fImageSubset.get(), 310+101, 0);
reed935d6cf2015-08-18 11:16:09 -0700372
Brian Osmana28e2b02017-04-24 16:44:03 -0400373 draw_as_bitmap(canvas, fImage.get(), 150, 0);
374 draw_as_bitmap(canvas, fImageSubset.get(), 150+101, 0);
reed8f343722015-08-13 13:32:39 -0700375 }
376
377 void onDraw(SkCanvas* canvas) override {
reed935d6cf2015-08-18 11:16:09 -0700378 this->makeCaches(canvas->getGrContext());
379
reed8f343722015-08-13 13:32:39 -0700380 canvas->translate(20, 20);
381
382 this->drawSet(canvas);
383
384 canvas->save();
385 canvas->translate(0, 130);
386 canvas->scale(0.25f, 0.25f);
387 this->drawSet(canvas);
388 canvas->restore();
389
390 canvas->save();
391 canvas->translate(0, 200);
392 canvas->scale(2, 2);
393 this->drawSet(canvas);
394 canvas->restore();
395 }
396
397private:
398 typedef skiagm::GM INHERITED;
399};
reed935d6cf2015-08-18 11:16:09 -0700400DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
401DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
reedc4a83e22015-09-11 11:47:27 -0700402DEF_GM( return new ImageCacheratorGM("ctable", make_ctable_generator); )
reed935d6cf2015-08-18 11:16:09 -0700403#if SK_SUPPORT_GPU
404 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
405#endif