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