1 /*
2 * Copyright 2016 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 "Resources.h"
10 #include "SkCodec.h"
11 #include "SkColorSpace.h"
12 #include "SkColorSpace_Base.h"
13 #include "SkColorSpaceXform.h"
14 #include "SkColorSpaceXformPriv.h"
15 #include "SkHalf.h"
16 #include "SkImage.h"
17 #include "SkPictureRecorder.h"
18
clamp_if_necessary(const SkImageInfo & info,void * pixels)19 static void clamp_if_necessary(const SkImageInfo& info, void* pixels) {
20 if (kRGBA_F16_SkColorType != info.colorType()) {
21 return;
22 }
23
24 for (int y = 0; y < info.height(); y++) {
25 for (int x = 0; x < info.width(); x++) {
26 uint64_t pixel = ((uint64_t*) pixels)[y * info.width() + x];
27
28 Sk4f rgba = SkHalfToFloat_finite_ftz(pixel);
29 if (kUnpremul_SkAlphaType == info.alphaType()) {
30 rgba = Sk4f::Max(0.0f, Sk4f::Min(rgba, 1.0f));
31 } else {
32 SkASSERT(kPremul_SkAlphaType == info.alphaType());
33 rgba = Sk4f::Max(0.0f, Sk4f::Min(rgba, rgba[3]));
34 }
35 SkFloatToHalf_finite_ftz(rgba).store(&pixel);
36
37 ((uint64_t*) pixels)[y * info.width() + x] = pixel;
38 }
39 }
40 }
41
fix_for_colortype(SkColorSpace * colorSpace,SkColorType colorType)42 sk_sp<SkColorSpace> fix_for_colortype(SkColorSpace* colorSpace, SkColorType colorType) {
43 if (kRGBA_F16_SkColorType == colorType) {
44 return as_CSB(colorSpace)->makeLinearGamma();
45 }
46
47 return sk_ref_sp(colorSpace);
48 }
49
50 static const int kWidth = 64;
51 static const int kHeight = 64;
52
make_raster_image(SkColorType colorType)53 static sk_sp<SkImage> make_raster_image(SkColorType colorType) {
54 std::unique_ptr<SkStream> stream(GetResourceAsStream("google_chrome.ico"));
55 std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream.release()));
56
57 SkBitmap bitmap;
58 SkImageInfo info = codec->getInfo().makeWH(kWidth, kHeight)
59 .makeColorType(colorType)
60 .makeAlphaType(kPremul_SkAlphaType)
61 .makeColorSpace(fix_for_colortype(codec->getInfo().colorSpace(), colorType));
62 bitmap.allocPixels(info);
63 codec->getPixels(info, bitmap.getPixels(), bitmap.rowBytes());
64 bitmap.setImmutable();
65 return SkImage::MakeFromBitmap(bitmap);
66 }
67
make_codec_image()68 static sk_sp<SkImage> make_codec_image() {
69 sk_sp<SkData> encoded = GetResourceAsData("randPixels.png");
70 return SkImage::MakeFromEncoded(encoded);
71 }
72
draw_contents(SkCanvas * canvas)73 static void draw_contents(SkCanvas* canvas) {
74 SkPaint paint;
75 paint.setStyle(SkPaint::kStroke_Style);
76 paint.setStrokeWidth(20);
77 paint.setColor(0xFF800000);
78 canvas->drawCircle(40, 40, 35, paint);
79 paint.setColor(0xFF008000);
80 canvas->drawCircle(50, 50, 35, paint);
81 paint.setColor(0xFF000080);
82 canvas->drawCircle(60, 60, 35, paint);
83 }
84
make_picture_image()85 static sk_sp<SkImage> make_picture_image() {
86 SkPictureRecorder recorder;
87 draw_contents(recorder.beginRecording(SkRect::MakeIWH(kWidth, kHeight)));
88 return SkImage::MakeFromPicture(recorder.finishRecordingAsPicture(),
89 SkISize::Make(kWidth, kHeight), nullptr, nullptr,
90 SkImage::BitDepth::kU8,
91 SkColorSpace::MakeSRGB());
92 }
93
make_srgb_transfer_fn(const SkColorSpacePrimaries & primaries)94 static sk_sp<SkColorSpace> make_srgb_transfer_fn(const SkColorSpacePrimaries& primaries) {
95 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor);
96 SkAssertResult(primaries.toXYZD50(&toXYZD50));
97 return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, toXYZD50);
98 }
99
make_wide_gamut()100 static sk_sp<SkColorSpace> make_wide_gamut() {
101 // ProPhoto
102 SkColorSpacePrimaries primaries;
103 primaries.fRX = 0.7347f;
104 primaries.fRY = 0.2653f;
105 primaries.fGX = 0.1596f;
106 primaries.fGY = 0.8404f;
107 primaries.fBX = 0.0366f;
108 primaries.fBY = 0.0001f;
109 primaries.fWX = 0.34567f;
110 primaries.fWY = 0.35850f;
111 return make_srgb_transfer_fn(primaries);
112 }
113
make_small_gamut()114 static sk_sp<SkColorSpace> make_small_gamut() {
115 SkColorSpacePrimaries primaries;
116 primaries.fRX = 0.50f;
117 primaries.fRY = 0.33f;
118 primaries.fGX = 0.30f;
119 primaries.fGY = 0.50f;
120 primaries.fBX = 0.25f;
121 primaries.fBY = 0.16f;
122 primaries.fWX = 0.3127f;
123 primaries.fWY = 0.3290f;
124 return make_srgb_transfer_fn(primaries);
125 }
126
draw_image(SkCanvas * canvas,SkImage * image,SkColorType dstColorType,SkAlphaType dstAlphaType,sk_sp<SkColorSpace> dstColorSpace,SkImage::CachingHint hint)127 static void draw_image(SkCanvas* canvas, SkImage* image, SkColorType dstColorType,
128 SkAlphaType dstAlphaType, sk_sp<SkColorSpace> dstColorSpace,
129 SkImage::CachingHint hint) {
130 size_t rowBytes = image->width() * SkColorTypeBytesPerPixel(dstColorType);
131 sk_sp<SkData> data = SkData::MakeUninitialized(rowBytes * image->height());
132 dstColorSpace = fix_for_colortype(dstColorSpace.get(), dstColorType);
133 SkImageInfo dstInfo = SkImageInfo::Make(image->width(), image->height(), dstColorType,
134 dstAlphaType, dstColorSpace);
135 if (!image->readPixels(dstInfo, data->writable_data(), rowBytes, 0, 0, hint)) {
136 memset(data->writable_data(), 0, rowBytes * image->height());
137 }
138
139 // SkImage must be premul, so manually premul the data if we unpremul'd during readPixels
140 if (kUnpremul_SkAlphaType == dstAlphaType) {
141 auto xform = SkColorSpaceXform::New(dstColorSpace.get(), dstColorSpace.get());
142 if (!xform->apply(select_xform_format(dstColorType), data->writable_data(),
143 select_xform_format(dstColorType), data->data(),
144 image->width() * image->height(), kPremul_SkAlphaType)) {
145 memset(data->writable_data(), 0, rowBytes * image->height());
146 }
147 dstInfo = dstInfo.makeAlphaType(kPremul_SkAlphaType);
148 }
149
150 // readPixels() does not always clamp F16. The drawing code expects pixels in the 0-1 range.
151 clamp_if_necessary(dstInfo, data->writable_data());
152
153 // Now that we have called readPixels(), dump the raw pixels into an srgb image.
154 sk_sp<SkColorSpace> srgb = fix_for_colortype(
155 SkColorSpace::MakeSRGB().get(), dstColorType);
156 sk_sp<SkImage> raw = SkImage::MakeRasterData(dstInfo.makeColorSpace(srgb), data, rowBytes);
157 canvas->drawImage(raw.get(), 0.0f, 0.0f, nullptr);
158 }
159
160 class ReadPixelsGM : public skiagm::GM {
161 public:
ReadPixelsGM()162 ReadPixelsGM() {}
163
164 protected:
onShortName()165 SkString onShortName() override {
166 return SkString("readpixels");
167 }
168
onISize()169 SkISize onISize() override {
170 return SkISize::Make(6 * kWidth, 9 * kHeight);
171 }
172
onDraw(SkCanvas * canvas)173 void onDraw(SkCanvas* canvas) override {
174 if (!canvas->imageInfo().colorSpace()) {
175 // This gm is only interesting in color correct modes.
176 return;
177 }
178
179 const SkAlphaType alphaTypes[] = {
180 kUnpremul_SkAlphaType,
181 kPremul_SkAlphaType,
182 };
183 const SkColorType colorTypes[] = {
184 kRGBA_8888_SkColorType,
185 kBGRA_8888_SkColorType,
186 kRGBA_F16_SkColorType,
187 };
188 const sk_sp<SkColorSpace> colorSpaces[] = {
189 make_wide_gamut(),
190 SkColorSpace::MakeSRGB(),
191 make_small_gamut(),
192 };
193
194 for (sk_sp<SkColorSpace> dstColorSpace : colorSpaces) {
195 for (SkColorType srcColorType : colorTypes) {
196 canvas->save();
197 sk_sp<SkImage> image = make_raster_image(srcColorType);
198 if (GrContext* context = canvas->getGrContext()) {
199 image = image->makeTextureImage(context, canvas->imageInfo().colorSpace());
200 }
201 if (image) {
202 for (SkColorType dstColorType : colorTypes) {
203 for (SkAlphaType dstAlphaType : alphaTypes) {
204 draw_image(canvas, image.get(), dstColorType, dstAlphaType,
205 dstColorSpace, SkImage::kAllow_CachingHint);
206 canvas->translate((float)kWidth, 0.0f);
207 }
208 }
209 }
210 canvas->restore();
211 canvas->translate(0.0f, (float) kHeight);
212 }
213 }
214 }
215
216 private:
217 typedef skiagm::GM INHERITED;
218 };
219 DEF_GM( return new ReadPixelsGM; )
220
221 class ReadPixelsCodecGM : public skiagm::GM {
222 public:
ReadPixelsCodecGM()223 ReadPixelsCodecGM() {}
224
225 protected:
onShortName()226 SkString onShortName() override {
227 return SkString("readpixelscodec");
228 }
229
onISize()230 SkISize onISize() override {
231 return SkISize::Make(3 * (kEncodedWidth + 1), 12 * (kEncodedHeight + 1));
232 }
233
onDraw(SkCanvas * canvas)234 void onDraw(SkCanvas* canvas) override {
235 if (!canvas->imageInfo().colorSpace()) {
236 // This gm is only interesting in color correct modes.
237 return;
238 }
239
240 const SkAlphaType alphaTypes[] = {
241 kUnpremul_SkAlphaType,
242 kPremul_SkAlphaType,
243 };
244 const SkColorType colorTypes[] = {
245 kRGBA_8888_SkColorType,
246 kBGRA_8888_SkColorType,
247 kRGBA_F16_SkColorType,
248 };
249 const sk_sp<SkColorSpace> colorSpaces[] = {
250 make_wide_gamut(),
251 SkColorSpace::MakeSRGB(),
252 make_small_gamut(),
253 };
254 const SkImage::CachingHint hints[] = {
255 SkImage::kAllow_CachingHint,
256 SkImage::kDisallow_CachingHint,
257 };
258
259 sk_sp<SkImage> image = make_codec_image();
260 for (sk_sp<SkColorSpace> dstColorSpace : colorSpaces) {
261 canvas->save();
262 for (SkColorType dstColorType : colorTypes) {
263 for (SkAlphaType dstAlphaType : alphaTypes) {
264 for (SkImage::CachingHint hint : hints) {
265 draw_image(canvas, image.get(), dstColorType, dstAlphaType, dstColorSpace,
266 hint);
267 canvas->translate(0.0f, (float) kEncodedHeight + 1);
268 }
269 }
270 }
271 canvas->restore();
272 canvas->translate((float) kEncodedWidth + 1, 0.0f);
273 }
274 }
275
276 private:
277 static const int kEncodedWidth = 8;
278 static const int kEncodedHeight = 8;
279
280 typedef skiagm::GM INHERITED;
281 };
282 DEF_GM( return new ReadPixelsCodecGM; )
283
284 class ReadPixelsPictureGM : public skiagm::GM {
285 public:
ReadPixelsPictureGM()286 ReadPixelsPictureGM() {}
287
288 protected:
onShortName()289 SkString onShortName() override {
290 return SkString("readpixelspicture");
291 }
292
onISize()293 SkISize onISize() override {
294 return SkISize::Make(3 * kWidth, 12 * kHeight);
295 }
296
onDraw(SkCanvas * canvas)297 void onDraw(SkCanvas* canvas) override {
298 if (!canvas->imageInfo().colorSpace()) {
299 // This gm is only interesting in color correct modes.
300 return;
301 }
302
303 const sk_sp<SkImage> images[] = {
304 make_picture_image(),
305 };
306 const SkAlphaType alphaTypes[] = {
307 kUnpremul_SkAlphaType,
308 kPremul_SkAlphaType,
309 };
310 const SkColorType colorTypes[] = {
311 kRGBA_8888_SkColorType,
312 kBGRA_8888_SkColorType,
313 kRGBA_F16_SkColorType,
314 };
315 const sk_sp<SkColorSpace> colorSpaces[] = {
316 make_wide_gamut(),
317 SkColorSpace::MakeSRGB(),
318 make_small_gamut(),
319 };
320 const SkImage::CachingHint hints[] = {
321 SkImage::kAllow_CachingHint,
322 SkImage::kDisallow_CachingHint,
323 };
324
325 for (sk_sp<SkImage> image : images) {
326 for (sk_sp<SkColorSpace> dstColorSpace : colorSpaces) {
327 canvas->save();
328 for (SkColorType dstColorType : colorTypes) {
329 for (SkAlphaType dstAlphaType : alphaTypes) {
330 for (SkImage::CachingHint hint : hints) {
331 draw_image(canvas, image.get(), dstColorType, dstAlphaType,
332 dstColorSpace, hint);
333 canvas->translate(0.0f, (float) kHeight);
334 }
335 }
336 }
337 canvas->restore();
338 canvas->translate((float) kWidth, 0.0f);
339 }
340 }
341 }
342
343 private:
344
345 typedef skiagm::GM INHERITED;
346 };
347 DEF_GM( return new ReadPixelsPictureGM; )
348