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 "SkImageInfoPriv.h" 17 #include "SkPictureRecorder.h" 18 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 42 sk_sp<SkColorSpace> fix_for_colortype(SkColorSpace* colorSpace, SkColorType colorType) { 43 if (kRGBA_F16_SkColorType == colorType) { 44 return 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 53 static sk_sp<SkImage> make_raster_image(SkColorType colorType) { 54 std::unique_ptr<SkStream> stream(GetResourceAsStream("images/google_chrome.ico")); 55 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromStream(std::move(stream)); 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 68 static sk_sp<SkImage> make_codec_image() { 69 sk_sp<SkData> encoded = GetResourceAsData("images/randPixels.png"); 70 return SkImage::MakeFromEncoded(encoded); 71 } 72 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 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 94 static sk_sp<SkColorSpace> make_parametric_transfer_fn(const SkColorSpacePrimaries& primaries) { 95 SkMatrix44 toXYZD50(SkMatrix44::kUninitialized_Constructor); 96 SkAssertResult(primaries.toXYZD50(&toXYZD50)); 97 SkColorSpaceTransferFn fn; 98 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; 99 return SkColorSpace::MakeRGB(fn, toXYZD50); 100 } 101 102 static sk_sp<SkColorSpace> make_wide_gamut() { 103 // ProPhoto 104 SkColorSpacePrimaries primaries; 105 primaries.fRX = 0.7347f; 106 primaries.fRY = 0.2653f; 107 primaries.fGX = 0.1596f; 108 primaries.fGY = 0.8404f; 109 primaries.fBX = 0.0366f; 110 primaries.fBY = 0.0001f; 111 primaries.fWX = 0.34567f; 112 primaries.fWY = 0.35850f; 113 return make_parametric_transfer_fn(primaries); 114 } 115 116 static sk_sp<SkColorSpace> make_small_gamut() { 117 SkColorSpacePrimaries primaries; 118 primaries.fRX = 0.50f; 119 primaries.fRY = 0.33f; 120 primaries.fGX = 0.30f; 121 primaries.fGY = 0.50f; 122 primaries.fBX = 0.25f; 123 primaries.fBY = 0.16f; 124 primaries.fWX = 0.3127f; 125 primaries.fWY = 0.3290f; 126 return make_parametric_transfer_fn(primaries); 127 } 128 129 static void draw_image(SkCanvas* canvas, SkImage* image, SkColorType dstColorType, 130 SkAlphaType dstAlphaType, sk_sp<SkColorSpace> dstColorSpace, 131 SkImage::CachingHint hint) { 132 size_t rowBytes = image->width() * SkColorTypeBytesPerPixel(dstColorType); 133 sk_sp<SkData> data = SkData::MakeUninitialized(rowBytes * image->height()); 134 dstColorSpace = fix_for_colortype(dstColorSpace.get(), dstColorType); 135 SkImageInfo dstInfo = SkImageInfo::Make(image->width(), image->height(), dstColorType, 136 dstAlphaType, dstColorSpace); 137 if (!image->readPixels(dstInfo, data->writable_data(), rowBytes, 0, 0, hint)) { 138 memset(data->writable_data(), 0, rowBytes * image->height()); 139 } 140 141 // SkImage must be premul, so manually premul the data if we unpremul'd during readPixels 142 if (kUnpremul_SkAlphaType == dstAlphaType) { 143 auto xform = SkColorSpaceXform::New(dstColorSpace.get(), dstColorSpace.get()); 144 if (!xform->apply(select_xform_format(dstColorType), data->writable_data(), 145 select_xform_format(dstColorType), data->data(), 146 image->width() * image->height(), kPremul_SkAlphaType)) { 147 memset(data->writable_data(), 0, rowBytes * image->height()); 148 } 149 dstInfo = dstInfo.makeAlphaType(kPremul_SkAlphaType); 150 } 151 152 // readPixels() does not always clamp F16. The drawing code expects pixels in the 0-1 range. 153 clamp_if_necessary(dstInfo, data->writable_data()); 154 155 // Now that we have called readPixels(), dump the raw pixels into an srgb image. 156 sk_sp<SkColorSpace> srgb = fix_for_colortype( 157 SkColorSpace::MakeSRGB().get(), dstColorType); 158 sk_sp<SkImage> raw = SkImage::MakeRasterData(dstInfo.makeColorSpace(srgb), data, rowBytes); 159 canvas->drawImage(raw.get(), 0.0f, 0.0f, nullptr); 160 } 161 162 class ReadPixelsGM : public skiagm::GM { 163 public: 164 ReadPixelsGM() {} 165 166 protected: 167 SkString onShortName() override { 168 return SkString("readpixels"); 169 } 170 171 SkISize onISize() override { 172 return SkISize::Make(6 * kWidth, 9 * kHeight); 173 } 174 175 void onDraw(SkCanvas* canvas) override { 176 if (!canvas->imageInfo().colorSpace()) { 177 // This gm is only interesting in color correct modes. 178 return; 179 } 180 181 const SkAlphaType alphaTypes[] = { 182 kUnpremul_SkAlphaType, 183 kPremul_SkAlphaType, 184 }; 185 const SkColorType colorTypes[] = { 186 kRGBA_8888_SkColorType, 187 kBGRA_8888_SkColorType, 188 kRGBA_F16_SkColorType, 189 }; 190 const sk_sp<SkColorSpace> colorSpaces[] = { 191 make_wide_gamut(), 192 SkColorSpace::MakeSRGB(), 193 make_small_gamut(), 194 }; 195 196 for (sk_sp<SkColorSpace> dstColorSpace : colorSpaces) { 197 for (SkColorType srcColorType : colorTypes) { 198 canvas->save(); 199 sk_sp<SkImage> image = make_raster_image(srcColorType); 200 if (GrContext* context = canvas->getGrContext()) { 201 image = image->makeTextureImage(context, canvas->imageInfo().colorSpace()); 202 } 203 if (image) { 204 for (SkColorType dstColorType : colorTypes) { 205 for (SkAlphaType dstAlphaType : alphaTypes) { 206 draw_image(canvas, image.get(), dstColorType, dstAlphaType, 207 dstColorSpace, SkImage::kAllow_CachingHint); 208 canvas->translate((float)kWidth, 0.0f); 209 } 210 } 211 } 212 canvas->restore(); 213 canvas->translate(0.0f, (float) kHeight); 214 } 215 } 216 } 217 218 private: 219 typedef skiagm::GM INHERITED; 220 }; 221 DEF_GM( return new ReadPixelsGM; ) 222 223 class ReadPixelsCodecGM : public skiagm::GM { 224 public: 225 ReadPixelsCodecGM() {} 226 227 protected: 228 SkString onShortName() override { 229 return SkString("readpixelscodec"); 230 } 231 232 SkISize onISize() override { 233 return SkISize::Make(3 * (kEncodedWidth + 1), 12 * (kEncodedHeight + 1)); 234 } 235 236 void onDraw(SkCanvas* canvas) override { 237 if (!canvas->imageInfo().colorSpace()) { 238 // This gm is only interesting in color correct modes. 239 return; 240 } 241 242 const SkAlphaType alphaTypes[] = { 243 kUnpremul_SkAlphaType, 244 kPremul_SkAlphaType, 245 }; 246 const SkColorType colorTypes[] = { 247 kRGBA_8888_SkColorType, 248 kBGRA_8888_SkColorType, 249 kRGBA_F16_SkColorType, 250 }; 251 const sk_sp<SkColorSpace> colorSpaces[] = { 252 make_wide_gamut(), 253 SkColorSpace::MakeSRGB(), 254 make_small_gamut(), 255 }; 256 const SkImage::CachingHint hints[] = { 257 SkImage::kAllow_CachingHint, 258 SkImage::kDisallow_CachingHint, 259 }; 260 261 sk_sp<SkImage> image = make_codec_image(); 262 for (sk_sp<SkColorSpace> dstColorSpace : colorSpaces) { 263 canvas->save(); 264 for (SkColorType dstColorType : colorTypes) { 265 for (SkAlphaType dstAlphaType : alphaTypes) { 266 for (SkImage::CachingHint hint : hints) { 267 draw_image(canvas, image.get(), dstColorType, dstAlphaType, dstColorSpace, 268 hint); 269 canvas->translate(0.0f, (float) kEncodedHeight + 1); 270 } 271 } 272 } 273 canvas->restore(); 274 canvas->translate((float) kEncodedWidth + 1, 0.0f); 275 } 276 } 277 278 private: 279 static const int kEncodedWidth = 8; 280 static const int kEncodedHeight = 8; 281 282 typedef skiagm::GM INHERITED; 283 }; 284 DEF_GM( return new ReadPixelsCodecGM; ) 285 286 class ReadPixelsPictureGM : public skiagm::GM { 287 public: 288 ReadPixelsPictureGM() {} 289 290 protected: 291 SkString onShortName() override { 292 return SkString("readpixelspicture"); 293 } 294 295 SkISize onISize() override { 296 return SkISize::Make(3 * kWidth, 12 * kHeight); 297 } 298 299 void onDraw(SkCanvas* canvas) override { 300 if (!canvas->imageInfo().colorSpace()) { 301 // This gm is only interesting in color correct modes. 302 return; 303 } 304 305 const sk_sp<SkImage> images[] = { 306 make_picture_image(), 307 }; 308 const SkAlphaType alphaTypes[] = { 309 kUnpremul_SkAlphaType, 310 kPremul_SkAlphaType, 311 }; 312 const SkColorType colorTypes[] = { 313 kRGBA_8888_SkColorType, 314 kBGRA_8888_SkColorType, 315 kRGBA_F16_SkColorType, 316 }; 317 const sk_sp<SkColorSpace> colorSpaces[] = { 318 make_wide_gamut(), 319 SkColorSpace::MakeSRGB(), 320 make_small_gamut(), 321 }; 322 const SkImage::CachingHint hints[] = { 323 SkImage::kAllow_CachingHint, 324 SkImage::kDisallow_CachingHint, 325 }; 326 327 for (sk_sp<SkImage> image : images) { 328 for (sk_sp<SkColorSpace> dstColorSpace : colorSpaces) { 329 canvas->save(); 330 for (SkColorType dstColorType : colorTypes) { 331 for (SkAlphaType dstAlphaType : alphaTypes) { 332 for (SkImage::CachingHint hint : hints) { 333 draw_image(canvas, image.get(), dstColorType, dstAlphaType, 334 dstColorSpace, hint); 335 canvas->translate(0.0f, (float) kHeight); 336 } 337 } 338 } 339 canvas->restore(); 340 canvas->translate((float) kWidth, 0.0f); 341 } 342 } 343 } 344 345 private: 346 347 typedef skiagm::GM INHERITED; 348 }; 349 DEF_GM( return new ReadPixelsPictureGM; ) 350