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