/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gm.h" #include "SkColorPriv.h" #include "SkColorSpaceXform.h" #include "SkColorSpaceXformPriv.h" #include "SkOpts.h" #include "SkUtils.h" static void clamp_to_alpha(uint32_t* pixels, int count) { for (int i = 0; i < count; i++) { uint8_t a = SkGetPackedA32(pixels[i]); uint8_t r = SkGetPackedR32(pixels[i]); uint8_t g = SkGetPackedG32(pixels[i]); uint8_t b = SkGetPackedB32(pixels[i]); pixels[i] = SkPackARGB32(a, SkTMin(a, r), SkTMin(a, g), SkTMin(a, b)); } } class GammaEncodedPremulGM : public skiagm::GM { public: GammaEncodedPremulGM(sk_sp dst, sk_sp src, const char* desc) : fDstSpace(dst) , fSrcSpace(src) , fXform(SkColorSpaceXform::New(src.get(), dst.get())) , fName(SkStringPrintf("gamma_encoded_premul_dst-v-src_%s", desc)) { int i = 0; for (int r = 0; r < kColorSteps; r++) { for (int g = 0; g < kColorSteps; g++) { for (int b = 0; b < kColorSteps; b++) { fColors[i++] = SkColorSetRGB(r * kColorScale, g * kColorScale, b * kColorScale); } } } } protected: virtual SkISize onISize() override { return SkISize::Make(kAlphaMax, kNumColors * 2 * kStripeHeight); } SkString onShortName() override { return fName; } void onDraw(SkCanvas* canvas) override { if (canvas->imageInfo().isOpaque()) { return; } SkBitmap bitmap; SkImageInfo bitmapInfo = SkImageInfo::MakeN32Premul(kAlphaMax, 1, canvas->imageInfo().refColorSpace()); bitmap.allocPixels(bitmapInfo); uint32_t* pixels = bitmap.getAddr32(0, 0); for (int i = 0; i < kNumColors; i++) { // Create an entire row of the same color, with the alpha from 0 to kAlphaMax. uint32_t row[kAlphaMax]; sk_memset32(row, fColors[i], kAlphaMax); for (int a = 0; a < kAlphaMax; a++) { row[a] = (row[a] & 0x00FFFFFF) | (a << 24); } // Tranform row to dst, then premultiply. fXform->apply(select_xform_format(kN32_SkColorType), pixels, SkColorSpaceXform::kBGRA_8888_ColorFormat, row, kAlphaMax, kUnpremul_SkAlphaType); SkOpts::RGBA_to_rgbA(pixels, pixels, kAlphaMax); bitmap.notifyPixelsChanged(); // Write the dst space premultiplied row to the canvas. for (int j = 0; j < kStripeHeight; j++) { canvas->drawBitmap(bitmap, 0, 2 * i * kStripeHeight + j); } // Premultiply, then transform the row to dst. SkOpts::RGBA_to_rgbA(pixels, row, kAlphaMax); fXform->apply(select_xform_format(kN32_SkColorType), pixels, SkColorSpaceXform::kBGRA_8888_ColorFormat, pixels, kAlphaMax, kUnpremul_SkAlphaType); clamp_to_alpha(pixels, kAlphaMax); bitmap.notifyPixelsChanged(); // Write the src space premultiplied row to the canvas. for (int j = 0; j < kStripeHeight; j++) { canvas->drawBitmap(bitmap, 0, (2 * i + 1) * kStripeHeight + j); } } } private: static constexpr int kColorSteps = 4; static constexpr int kNumColors = kColorSteps * kColorSteps * kColorSteps; static constexpr int kColorScale = 255 / (kColorSteps - 1); static constexpr int kStripeHeight = 10; static constexpr int kAlphaMax = 255; sk_sp fDstSpace; sk_sp fSrcSpace; std::unique_ptr fXform; SkString fName; SkColor fColors[kNumColors]; typedef GM INHERITED; }; DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeSRGB(), SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kRec2020_Gamut), "toWideGamut");) DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kRec2020_Gamut), SkColorSpace::MakeSRGB(), "fromWideGamut");) DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeSRGB(), SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkColorSpace::kSRGB_Gamut), "toLinear");) DEF_GM(return new GammaEncodedPremulGM( SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkColorSpace::kSRGB_Gamut), SkColorSpace::MakeSRGB(), "fromLinear");) DEF_GM(return new GammaEncodedPremulGM( SkColorSpace::MakeRGB({ 1.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, SkColorSpace::kSRGB_Gamut), SkColorSpace::MakeSRGB(), "from1.8");)