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 "SkImageGenerator.h"
12 #include "SkImage_Base.h"
13 #include "SkMakeUnique.h"
14 #include "SkPictureRecorder.h"
15 #include "SkSurface.h"
16
17 #if SK_SUPPORT_GPU
18 #include "GrContext.h"
19 #include "GrContextPriv.h"
20 #include "GrSurfaceContext.h"
21 #include "GrTextureProxy.h"
22 #include "../src/image/SkImage_Gpu.h"
23 #endif
24
draw_something(SkCanvas * canvas,const SkRect & bounds)25 static 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 */
41 class ImagePictGM : public skiagm::GM {
42 sk_sp<SkPicture> fPicture;
43 sk_sp<SkImage> fImage0;
44 sk_sp<SkImage> fImage1;
45 public:
ImagePictGM()46 ImagePictGM() {}
47
48 protected:
onShortName()49 SkString onShortName() override {
50 return SkString("image-picture");
51 }
52
onISize()53 SkISize onISize() override {
54 return SkISize::Make(850, 450);
55 }
56
onOnceBeforeDraw()57 void onOnceBeforeDraw() override {
58 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
59 SkPictureRecorder recorder;
60 draw_something(recorder.beginRecording(bounds), bounds);
61 fPicture = recorder.finishRecordingAsPicture();
62
63 // extract enough just for the oval.
64 const SkISize size = SkISize::Make(100, 100);
65 auto srgbColorSpace = SkColorSpace::MakeSRGB();
66
67 SkMatrix matrix;
68 matrix.setTranslate(-100, -100);
69 fImage0 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
70 SkImage::BitDepth::kU8, srgbColorSpace);
71 matrix.postTranslate(-50, -50);
72 matrix.postRotate(45);
73 matrix.postTranslate(50, 50);
74 fImage1 = SkImage::MakeFromPicture(fPicture, size, &matrix, nullptr,
75 SkImage::BitDepth::kU8, srgbColorSpace);
76 }
77
drawSet(SkCanvas * canvas) const78 void drawSet(SkCanvas* canvas) const {
79 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
80 canvas->drawPicture(fPicture, &matrix, nullptr);
81 canvas->drawImage(fImage0.get(), 150, 0);
82 canvas->drawImage(fImage1.get(), 300, 0);
83 }
84
onDraw(SkCanvas * canvas)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
103 private:
104 typedef skiagm::GM INHERITED;
105 };
DEF_GM(return new ImagePictGM;)106 DEF_GM( return new ImagePictGM; )
107
108 ///////////////////////////////////////////////////////////////////////////////////////////////////
109
110 static std::unique_ptr<SkImageGenerator> make_pic_generator(GrContext*, sk_sp<SkPicture> pic) {
111 SkMatrix matrix;
112 matrix.setTranslate(-100, -100);
113 return SkImageGenerator::MakeFromPicture({ 100, 100 }, std::move(pic), &matrix, nullptr,
114 SkImage::BitDepth::kU8,
115 SkColorSpace::MakeSRGB());
116 }
117
118 class RasterGenerator : public SkImageGenerator {
119 public:
RasterGenerator(const SkBitmap & bm)120 RasterGenerator(const SkBitmap& bm) : SkImageGenerator(bm.info()), fBM(bm)
121 {}
122
123 protected:
onGetPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const Options &)124 bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
125 const Options&) override {
126 SkASSERT(fBM.width() == info.width());
127 SkASSERT(fBM.height() == info.height());
128 return fBM.readPixels(info, pixels, rowBytes, 0, 0);
129 }
130 private:
131 SkBitmap fBM;
132 };
make_ras_generator(GrContext *,sk_sp<SkPicture> pic)133 static std::unique_ptr<SkImageGenerator> make_ras_generator(GrContext*, sk_sp<SkPicture> pic) {
134 SkBitmap bm;
135 bm.allocN32Pixels(100, 100);
136 SkCanvas canvas(bm);
137 canvas.clear(0);
138 canvas.translate(-100, -100);
139 canvas.drawPicture(pic);
140 return skstd::make_unique<RasterGenerator>(bm);
141 }
142
143 class EmptyGenerator : public SkImageGenerator {
144 public:
EmptyGenerator(const SkImageInfo & info)145 EmptyGenerator(const SkImageInfo& info) : SkImageGenerator(info) {}
146 };
147
148 #if SK_SUPPORT_GPU
149 class TextureGenerator : public SkImageGenerator {
150 public:
TextureGenerator(GrContext * ctx,const SkImageInfo & info,sk_sp<SkPicture> pic)151 TextureGenerator(GrContext* ctx, const SkImageInfo& info, sk_sp<SkPicture> pic)
152 : SkImageGenerator(info)
153 , fCtx(SkRef(ctx)) {
154
155 sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, info, 0,
156 kTopLeft_GrSurfaceOrigin, nullptr));
157 if (surface) {
158 surface->getCanvas()->clear(0);
159 surface->getCanvas()->translate(-100, -100);
160 surface->getCanvas()->drawPicture(pic);
161 sk_sp<SkImage> image(surface->makeImageSnapshot());
162 fProxy = as_IB(image)->asTextureProxyRef();
163 }
164 }
165 protected:
onGenerateTexture(GrContext * ctx,const SkImageInfo & info,const SkIPoint & origin,SkTransferFunctionBehavior,bool willBeMipped)166 sk_sp<GrTextureProxy> onGenerateTexture(GrContext* ctx, const SkImageInfo& info,
167 const SkIPoint& origin,
168 SkTransferFunctionBehavior,
169 bool willBeMipped) override {
170 SkASSERT(ctx);
171 SkASSERT(ctx == fCtx.get());
172
173 if (!fProxy) {
174 return nullptr;
175 }
176
177 if (origin.fX == 0 && origin.fY == 0 &&
178 info.width() == fProxy->width() && info.height() == fProxy->height()) {
179 return fProxy;
180 }
181
182 // need to copy the subset into a new texture
183 GrSurfaceDesc desc;
184 desc.fOrigin = fProxy->origin();
185 desc.fWidth = info.width();
186 desc.fHeight = info.height();
187 desc.fConfig = fProxy->config();
188
189 GrMipMapped mipMapped = willBeMipped ? GrMipMapped::kYes : GrMipMapped::kNo;
190
191 sk_sp<GrSurfaceContext> dstContext(fCtx->contextPriv().makeDeferredSurfaceContext(
192 desc,
193 mipMapped,
194 SkBackingFit::kExact,
195 SkBudgeted::kYes));
196 if (!dstContext) {
197 return nullptr;
198 }
199
200 if (!dstContext->copy(
201 fProxy.get(),
202 SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height()),
203 SkIPoint::Make(0, 0))) {
204 return nullptr;
205 }
206
207 return dstContext->asTextureProxyRef();
208 }
209
210 private:
211 sk_sp<GrContext> fCtx;
212 sk_sp<GrTextureProxy> fProxy;
213 };
214
make_tex_generator(GrContext * ctx,sk_sp<SkPicture> pic)215 static std::unique_ptr<SkImageGenerator> make_tex_generator(GrContext* ctx, sk_sp<SkPicture> pic) {
216 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
217
218 if (!ctx) {
219 return skstd::make_unique<EmptyGenerator>(info);
220 }
221 return skstd::make_unique<TextureGenerator>(ctx, info, pic);
222 }
223 #endif
224
225 class ImageCacheratorGM : public skiagm::GM {
226 SkString fName;
227 std::unique_ptr<SkImageGenerator> (*fFactory)(GrContext*, sk_sp<SkPicture>);
228 sk_sp<SkPicture> fPicture;
229 sk_sp<SkImage> fImage;
230 sk_sp<SkImage> fImageSubset;
231
232 public:
ImageCacheratorGM(const char suffix[],std::unique_ptr<SkImageGenerator> (* factory)(GrContext *,sk_sp<SkPicture>))233 ImageCacheratorGM(const char suffix[],
234 std::unique_ptr<SkImageGenerator> (*factory)(GrContext*, sk_sp<SkPicture>))
235 : fFactory(factory)
236 {
237 fName.printf("image-cacherator-from-%s", suffix);
238 }
239
240 protected:
onShortName()241 SkString onShortName() override {
242 return fName;
243 }
244
onISize()245 SkISize onISize() override {
246 return SkISize::Make(960, 450);
247 }
248
onOnceBeforeDraw()249 void onOnceBeforeDraw() override {
250 const SkRect bounds = SkRect::MakeXYWH(100, 100, 100, 100);
251 SkPictureRecorder recorder;
252 draw_something(recorder.beginRecording(bounds), bounds);
253 fPicture = recorder.finishRecordingAsPicture();
254 }
255
makeCaches(GrContext * ctx)256 void makeCaches(GrContext* ctx) {
257 auto gen = fFactory(ctx, fPicture);
258 fImage = SkImage::MakeFromGenerator(std::move(gen));
259
260 const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
261
262 gen = fFactory(ctx, fPicture);
263 fImageSubset = SkImage::MakeFromGenerator(std::move(gen), &subset);
264
265 SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
266 SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
267 }
268
draw_as_bitmap(SkCanvas * canvas,SkImage * image,SkScalar x,SkScalar y)269 static void draw_as_bitmap(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
270 SkBitmap bitmap;
271 as_IB(image)->getROPixels(&bitmap, canvas->imageInfo().colorSpace());
272 canvas->drawBitmap(bitmap, x, y);
273 }
274
draw_as_tex(SkCanvas * canvas,SkImage * image,SkScalar x,SkScalar y)275 static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
276 #if SK_SUPPORT_GPU
277 sk_sp<SkColorSpace> texColorSpace;
278 sk_sp<GrTextureProxy> proxy(as_IB(image)->asTextureProxyRef(
279 canvas->getGrContext(), GrSamplerState::ClampBilerp(),
280 canvas->imageInfo().colorSpace(), &texColorSpace, nullptr));
281 if (!proxy) {
282 // show placeholder if we have no texture
283 SkPaint paint;
284 paint.setStyle(SkPaint::kStroke_Style);
285 SkRect r = SkRect::MakeXYWH(x, y, SkIntToScalar(image->width()),
286 SkIntToScalar(image->width()));
287 canvas->drawRect(r, paint);
288 canvas->drawLine(r.left(), r.top(), r.right(), r.bottom(), paint);
289 canvas->drawLine(r.left(), r.bottom(), r.right(), r.top(), paint);
290 return;
291 }
292
293 // No API to draw a GrTexture directly, so we cheat and create a private image subclass
294 sk_sp<SkImage> texImage(new SkImage_Gpu(canvas->getGrContext(), image->uniqueID(),
295 kPremul_SkAlphaType, std::move(proxy),
296 std::move(texColorSpace), SkBudgeted::kNo));
297 canvas->drawImage(texImage.get(), x, y);
298 #endif
299 }
300
drawSet(SkCanvas * canvas) const301 void drawSet(SkCanvas* canvas) const {
302 SkMatrix matrix = SkMatrix::MakeTrans(-100, -100);
303 canvas->drawPicture(fPicture, &matrix, nullptr);
304
305 // Draw the tex first, so it doesn't hit a lucky cache from the raster version. This
306 // way we also can force the generateTexture call.
307
308 draw_as_tex(canvas, fImage.get(), 310, 0);
309 draw_as_tex(canvas, fImageSubset.get(), 310+101, 0);
310
311 draw_as_bitmap(canvas, fImage.get(), 150, 0);
312 draw_as_bitmap(canvas, fImageSubset.get(), 150+101, 0);
313 }
314
onDraw(SkCanvas * canvas)315 void onDraw(SkCanvas* canvas) override {
316 this->makeCaches(canvas->getGrContext());
317
318 canvas->translate(20, 20);
319
320 this->drawSet(canvas);
321
322 canvas->save();
323 canvas->translate(0, 130);
324 canvas->scale(0.25f, 0.25f);
325 this->drawSet(canvas);
326 canvas->restore();
327
328 canvas->save();
329 canvas->translate(0, 200);
330 canvas->scale(2, 2);
331 this->drawSet(canvas);
332 canvas->restore();
333 }
334
335 private:
336 typedef skiagm::GM INHERITED;
337 };
338 DEF_GM( return new ImageCacheratorGM("picture", make_pic_generator); )
339 DEF_GM( return new ImageCacheratorGM("raster", make_ras_generator); )
340 #if SK_SUPPORT_GPU
341 DEF_GM( return new ImageCacheratorGM("texture", make_tex_generator); )
342 #endif
343