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/gm.h"
9 #include "include/codec/SkEncodedImageFormat.h"
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkImage.h"
16 #include "include/core/SkImageInfo.h"
17 #include "include/core/SkMatrix.h"
18 #include "include/core/SkPaint.h"
19 #include "include/core/SkPicture.h"
20 #include "include/core/SkPictureRecorder.h"
21 #include "include/core/SkRect.h"
22 #include "include/core/SkRefCnt.h"
23 #include "include/core/SkShader.h"
24 #include "include/core/SkSize.h"
25 #include "include/core/SkString.h"
26 #include "include/core/SkSurface.h"
27 #include "include/core/SkTileMode.h"
28 #include "include/core/SkTypes.h"
29 #include "include/encode/SkPngEncoder.h"
30 #include "include/gpu/GpuTypes.h"
31 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
32
33 #include <utility>
34
draw_something(SkCanvas * canvas,const SkRect & bounds)35 static void draw_something(SkCanvas* canvas, const SkRect& bounds) {
36 SkPaint paint;
37 paint.setAntiAlias(true);
38 paint.setColor(SK_ColorRED);
39 paint.setStyle(SkPaint::kStroke_Style);
40 paint.setStrokeWidth(10);
41 canvas->drawRect(bounds, paint);
42 paint.setStyle(SkPaint::kFill_Style);
43 paint.setColor(SK_ColorBLUE);
44 canvas->drawOval(bounds, paint);
45 }
46
47 typedef sk_sp<SkImage> (*ImageMakerProc)(GrRecordingContext*, SkPicture*, const SkImageInfo&);
48
make_raster(GrRecordingContext *,SkPicture * pic,const SkImageInfo & info)49 static sk_sp<SkImage> make_raster(GrRecordingContext*,
50 SkPicture* pic,
51 const SkImageInfo& info) {
52 auto surface(SkSurfaces::Raster(info));
53 surface->getCanvas()->clear(0);
54 surface->getCanvas()->drawPicture(pic);
55 return surface->makeImageSnapshot();
56 }
57
make_texture(GrRecordingContext * ctx,SkPicture * pic,const SkImageInfo & info)58 static sk_sp<SkImage> make_texture(GrRecordingContext* ctx,
59 SkPicture* pic,
60 const SkImageInfo& info) {
61 if (!ctx) {
62 return nullptr;
63 }
64 auto surface(SkSurfaces::RenderTarget(ctx, skgpu::Budgeted::kNo, info));
65 if (!surface) {
66 return nullptr;
67 }
68 surface->getCanvas()->clear(0);
69 surface->getCanvas()->drawPicture(pic);
70 return surface->makeImageSnapshot();
71 }
72
make_pict_gen(GrRecordingContext *,SkPicture * pic,const SkImageInfo & info)73 static sk_sp<SkImage> make_pict_gen(GrRecordingContext*,
74 SkPicture* pic,
75 const SkImageInfo& info) {
76 return SkImages::DeferredFromPicture(sk_ref_sp(pic),
77 info.dimensions(),
78 nullptr,
79 nullptr,
80 SkImages::BitDepth::kU8,
81 SkColorSpace::MakeSRGB());
82 }
83
make_encode_gen(GrRecordingContext * ctx,SkPicture * pic,const SkImageInfo & info)84 static sk_sp<SkImage> make_encode_gen(GrRecordingContext* ctx,
85 SkPicture* pic,
86 const SkImageInfo& info) {
87 sk_sp<SkImage> src(make_raster(ctx, pic, info));
88 if (!src) {
89 return nullptr;
90 }
91 sk_sp<SkData> encoded = SkPngEncoder::Encode(nullptr, src.get(), {});
92 if (!encoded) {
93 return nullptr;
94 }
95 return SkImages::DeferredFromEncodedData(std::move(encoded));
96 }
97
98 const ImageMakerProc gProcs[] = {
99 make_raster,
100 make_texture,
101 make_pict_gen,
102 make_encode_gen,
103 };
104
105 /*
106 * Exercise drawing pictures inside an image, showing that the image version is pixelated
107 * (correctly) when it is inside an image.
108 */
109 class ImageShaderGM : public skiagm::GM {
110 sk_sp<SkPicture> fPicture;
111
112 public:
ImageShaderGM()113 ImageShaderGM() {}
114
115 protected:
getName() const116 SkString getName() const override { return SkString("image-shader"); }
117
getISize()118 SkISize getISize() override { return SkISize::Make(850, 450); }
119
onOnceBeforeDraw()120 void onOnceBeforeDraw() override {
121 const SkRect bounds = SkRect::MakeWH(100, 100);
122 SkPictureRecorder recorder;
123 draw_something(recorder.beginRecording(bounds), bounds);
124 fPicture = recorder.finishRecordingAsPicture();
125 }
126
testImage(SkCanvas * canvas,SkImage * image)127 void testImage(SkCanvas* canvas, SkImage* image) {
128 SkAutoCanvasRestore acr(canvas, true);
129
130 canvas->drawImage(image, 0, 0);
131 canvas->translate(0, 120);
132
133 const SkTileMode tile = SkTileMode::kRepeat;
134 const SkMatrix localM = SkMatrix::Translate(-50, -50);
135 SkPaint paint;
136 paint.setShader(image->makeShader(tile, tile, SkSamplingOptions(), &localM));
137 paint.setAntiAlias(true);
138 canvas->drawCircle(50, 50, 50, paint);
139 }
140
onDraw(SkCanvas * canvas)141 void onDraw(SkCanvas* canvas) override {
142 canvas->translate(20, 20);
143
144 const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
145
146 for (size_t i = 0; i < std::size(gProcs); ++i) {
147 sk_sp<SkImage> image(gProcs[i](canvas->recordingContext(), fPicture.get(), info));
148 if (image) {
149 this->testImage(canvas, image.get());
150 }
151 canvas->translate(120, 0);
152 }
153 }
154
155 private:
156 using INHERITED = skiagm::GM;
157 };
DEF_GM(return new ImageShaderGM;)158 DEF_GM( return new ImageShaderGM; )
159
160 //////////////////////////////////////////////////////////////////////////////////////////////
161
162 #include "tools/ToolUtils.h"
163
164 static sk_sp<SkImage> make_checker_img(int w, int h, SkColor c0, SkColor c1, int size) {
165 SkBitmap bm = ToolUtils::create_checkerboard_bitmap(w, h, c0, c1, size);
166 bm.setImmutable();
167 return bm.asImage();
168 }
169
170 DEF_SIMPLE_GM(drawimage_sampling, canvas, 500, 500) {
171 constexpr int N = 256;
172 constexpr float kScale = 1.0f/6;
173 const SkRect dst = {0, 0, kScale*N, kScale*N};
174
175 auto img = make_checker_img(N, N, SK_ColorBLACK, SK_ColorWHITE, 7)->withDefaultMipmaps();
176 const SkRect src = SkRect::MakeIWH(img->width(), img->height());
177
178 SkMatrix mx = SkMatrix::RectToRect(src, dst);
179
180 SkPaint paint;
181
182 for (auto mm : {SkMipmapMode::kNone, SkMipmapMode::kNearest, SkMipmapMode::kLinear}) {
183 for (auto fm : {SkFilterMode::kNearest, SkFilterMode::kLinear}) {
184 SkSamplingOptions sampling(fm, mm);
185
186 canvas->save();
187
188 canvas->save();
189 canvas->concat(mx);
190 canvas->drawImage(img.get(), 0, 0, sampling);
191 canvas->restore();
192
193 canvas->translate(dst.width() + 4, 0);
194
195 paint.setShader(img->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, sampling, &mx));
196 canvas->drawRect(dst, paint);
197
198 canvas->translate(dst.width() + 4, 0);
199
200 canvas->drawImageRect(img.get(), src, dst, sampling, nullptr,
201 SkCanvas::kFast_SrcRectConstraint);
202 canvas->restore();
203
204 canvas->translate(0, dst.height() + 8);
205 }
206 }
207
208 }
209
210 // Test case for skbug.com/12685 (texture-backed image shaders silently fail drawing to CPU canvas)
211 DEF_SIMPLE_GM(textureimage_and_shader, canvas, 100, 50) {
212 canvas->clear(SK_ColorGREEN);
213
214 sk_sp<SkImage> image;
215 if (canvas->getSurface()) {
216 image = canvas->getSurface()->makeImageSnapshot();
217 canvas->clear(SK_ColorRED);
218 } else {
219 auto greenSurface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(50, 50));
220 greenSurface->getCanvas()->clear(SK_ColorGREEN);
221 image = greenSurface->makeImageSnapshot();
222 }
223
224 // At this point, 'image' contains a green image. If our original canvas is GPU-backed, then
225 // the snapped image will be a (GPU) texture. We will try to draw that image to a non-GPU
226 // surface, to ensure that we get automatic read-back. If all goes well, we will get a pure
227 // green result. If either draw fails, we'll get red (most likely).
228
229 auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(50, 50));
230
231 // First, use drawImage:
232 surface->getCanvas()->clear(SK_ColorRED);
233 surface->getCanvas()->drawImage(image, 0, 0);
234 canvas->drawImage(surface->makeImageSnapshot(), 0, 0);
235
236 // Now, use an image shader:
237 SkPaint paint;
238 paint.setShader(image->makeShader(SkSamplingOptions()));
239 surface->getCanvas()->clear(SK_ColorRED);
240 surface->getCanvas()->drawPaint(paint);
241 canvas->drawImage(surface->makeImageSnapshot(), 50, 0);
242 }
243