Loading libs/hwui/CanvasTransform.cpp +21 −15 Original line number Diff line number Diff line Loading @@ -15,32 +15,38 @@ */ #include "CanvasTransform.h" #include "utils/Color.h" #include "Properties.h" #include <ui/ColorSpace.h> #include <SkColorFilter.h> #include <SkPaint.h> #include <log/log.h> #include <algorithm> #include <cmath> namespace android::uirenderer { static SkColor makeLight(SkColor color) { SkScalar hsv[3]; SkColorToHSV(color, hsv); if (hsv[1] > .2f) return color; // hsv[1] *= .85f; // hsv[2] = std::min(1.0f, std::max(hsv[2], 1 - hsv[2]) * 1.3f); hsv[2] = std::max(hsv[2], 1.1f - hsv[2]); return SkHSVToColor(SkColorGetA(color), hsv); Lab lab = sRGBToLab(color); float invertedL = std::min(110 - lab.L, 100.0f); if (invertedL > lab.L) { lab.L = invertedL; return LabToSRGB(lab, SkColorGetA(color)); } else { return color; } } static SkColor makeDark(SkColor color) { SkScalar hsv[3]; SkColorToHSV(color, hsv); if (hsv[1] > .2f) return color; // hsv[1] *= .85f; // hsv[2] = std::max(0.0f, std::min(hsv[2], 1 - hsv[2]) * .7f); hsv[2] = std::min(hsv[2], 1.1f - hsv[2]); return SkHSVToColor(SkColorGetA(color), hsv); Lab lab = sRGBToLab(color); float invertedL = std::min(110 - lab.L, 100.0f); if (invertedL < lab.L) { lab.L = invertedL; return LabToSRGB(lab, SkColorGetA(color)); } else { return color; } } static SkColor transformColor(ColorTransform transform, SkColor color) { Loading libs/hwui/HardwareBitmapUploader.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -286,7 +286,7 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou eglDestroySyncKHR(display, fence); } return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info())); return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap))); } }; // namespace android::uirenderer libs/hwui/hwui/Bitmap.cpp +105 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" Loading @@ -34,6 +35,7 @@ #include <SkToSRGBColorFilter.h> #include <limits> #include <SkHighContrastFilter.h> namespace android { Loading Loading @@ -195,11 +197,13 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info mPixelStorage.ashmem.size = mappedSize; } Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info) Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette) : SkPixelRef(info.width(), info.height(), nullptr, bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride()) , mInfo(validateAlpha(info)) , mPixelStorageType(PixelStorageType::Hardware) { , mPixelStorageType(PixelStorageType::Hardware) , mPalette(palette) , mPaletteGenerationId(getGenerationID()) { mPixelStorage.hardware.buffer = buffer; buffer->incStrong(buffer); setImmutable(); // HW bitmaps are always immutable Loading Loading @@ -326,7 +330,106 @@ sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) { if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) { *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace()); } // TODO: Move this to the canvas (or other?) layer where we have the target lightness // mode and can selectively do the right thing. if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) { SkHighContrastConfig config; config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; *outputColorFilter = SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter); } return image; } class MinMaxAverage { public: void add(float sample) { if (mCount == 0) { mMin = sample; mMax = sample; } else { mMin = std::min(mMin, sample); mMax = std::max(mMax, sample); } mTotal += sample; mCount++; } float average() { return mTotal / mCount; } float min() { return mMin; } float max() { return mMax; } float delta() { return mMax - mMin; } private: float mMin = 0.0f; float mMax = 0.0f; float mTotal = 0.0f; int mCount = 0; }; BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) { ATRACE_CALL(); SkPixmap pixmap{info, addr, rowBytes}; // TODO: This calculation of converting to HSV & tracking min/max is probably overkill // Experiment with something simpler since we just want to figure out if it's "color-ful" // and then the average perceptual lightness. MinMaxAverage hue, saturation, value; int sampledCount = 0; // Sample a grid of 100 pixels to get an overall estimation of the colors in play const int x_step = std::max(1, pixmap.width() / 10); const int y_step = std::max(1, pixmap.height() / 10); for (int x = 0; x < pixmap.width(); x += x_step) { for (int y = 0; y < pixmap.height(); y += y_step) { SkColor color = pixmap.getColor(x, y); if (!info.isOpaque() && SkColorGetA(color) < 75) { continue; } sampledCount++; float hsv[3]; SkColorToHSV(color, hsv); hue.add(hsv[0]); saturation.add(hsv[1]); value.add(hsv[2]); } } // TODO: Tune the coverage threshold if (sampledCount < 5) { ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d", sampledCount, info.width(), info.height(), (int) info.colorType(), (int) info.alphaType()); return BitmapPalette::Unknown; } ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = %f]", sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(), saturation.average()); if (hue.delta() <= 20 && saturation.delta() <= .1f) { if (value.average() >= .5f) { return BitmapPalette::Light; } else { return BitmapPalette::Dark; } } return BitmapPalette::Unknown; } } // namespace android libs/hwui/hwui/Bitmap.h +24 −1 Original line number Diff line number Diff line Loading @@ -34,6 +34,12 @@ enum class PixelStorageType { Hardware, }; enum class BitmapPalette { Unknown, Light, Dark, }; namespace uirenderer { namespace renderthread { class RenderThread; Loading Loading @@ -63,7 +69,7 @@ public: Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, size_t rowBytes); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette = BitmapPalette::Unknown); int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); } Loading Loading @@ -103,6 +109,20 @@ public: */ sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter); static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes); static BitmapPalette computePalette(const SkBitmap& bitmap) { return computePalette(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes()); } BitmapPalette palette() { if (!isHardware() && mPaletteGenerationId != getGenerationID()) { mPalette = computePalette(info(), pixels(), rowBytes()); mPaletteGenerationId = getGenerationID(); } return mPalette; } private: virtual ~Bitmap(); void* getStorage() const; Loading @@ -111,6 +131,9 @@ private: const PixelStorageType mPixelStorageType; BitmapPalette mPalette = BitmapPalette::Unknown; uint32_t mPaletteGenerationId = -1; bool mHasHardwareMipMap = false; union { Loading libs/hwui/utils/Color.cpp +95 −1 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ #include "Color.h" #include <utils/Log.h> #include <ui/ColorSpace.h> #include <algorithm> #include <cmath> namespace android { Loading Loading @@ -107,5 +109,97 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { } } template<typename T> static constexpr T clamp(T x, T min, T max) { return x < min ? min : x > max ? max : x; } //static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; static const mat3 BRADFORD = mat3{ float3{ 0.8951f, -0.7502f, 0.0389f}, float3{ 0.2664f, 1.7135f, -0.0685f}, float3{-0.1614f, 0.0367f, 1.0296f} }; static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) { float3 srcLMS = matrix * srcWhitePoint; float3 dstLMS = matrix * dstWhitePoint; return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; } namespace LabColorSpace { static constexpr float A = 216.0f / 24389.0f; static constexpr float B = 841.0f / 108.0f; static constexpr float C = 4.0f / 29.0f; static constexpr float D = 6.0f / 29.0f; float3 toXyz(const Lab& lab) { float3 v { lab.L, lab.a, lab.b }; v[0] = clamp(v[0], 0.0f, 100.0f); v[1] = clamp(v[1], -128.0f, 128.0f); v[2] = clamp(v[2], -128.0f, 128.0f); float fy = (v[0] + 16.0f) / 116.0f; float fx = fy + (v[1] * 0.002f); float fz = fy - (v[2] * 0.005f); float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C); float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C); float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C); v[0] = X * ILLUMINANT_D50_XYZ[0]; v[1] = Y * ILLUMINANT_D50_XYZ[1]; v[2] = Z * ILLUMINANT_D50_XYZ[2]; return v; } Lab fromXyz(const float3& v) { float X = v[0] / ILLUMINANT_D50_XYZ[0]; float Y = v[1] / ILLUMINANT_D50_XYZ[1]; float Z = v[2] / ILLUMINANT_D50_XYZ[2]; float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C; float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C; float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C; float L = 116.0f * fy - 16.0f; float a = 500.0f * (fx - fy); float b = 200.0f * (fy - fz); return Lab { clamp(L, 0.0f, 100.0f), clamp(a, -128.0f, 128.0f), clamp(b, -128.0f, 128.0f) }; } }; Lab sRGBToLab(SkColor color) { auto colorSpace = ColorSpace::sRGB(); float3 rgb; rgb.r = SkColorGetR(color) / 255.0f; rgb.g = SkColorGetG(color) / 255.0f; rgb.b = SkColorGetB(color) / 255.0f; float3 xyz = colorSpace.rgbToXYZ(rgb); float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1}); xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz; return LabColorSpace::fromXyz(xyz); } SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) { auto colorSpace = ColorSpace::sRGB(); float3 xyz = LabColorSpace::toXyz(lab); float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1}); xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz; float3 rgb = colorSpace.xyzToRGB(xyz); return SkColorSetARGB(alpha, static_cast<uint8_t>(rgb.r * 255), static_cast<uint8_t>(rgb.g * 255), static_cast<uint8_t>(rgb.b * 255)); } }; // namespace uirenderer }; // namespace android Loading
libs/hwui/CanvasTransform.cpp +21 −15 Original line number Diff line number Diff line Loading @@ -15,32 +15,38 @@ */ #include "CanvasTransform.h" #include "utils/Color.h" #include "Properties.h" #include <ui/ColorSpace.h> #include <SkColorFilter.h> #include <SkPaint.h> #include <log/log.h> #include <algorithm> #include <cmath> namespace android::uirenderer { static SkColor makeLight(SkColor color) { SkScalar hsv[3]; SkColorToHSV(color, hsv); if (hsv[1] > .2f) return color; // hsv[1] *= .85f; // hsv[2] = std::min(1.0f, std::max(hsv[2], 1 - hsv[2]) * 1.3f); hsv[2] = std::max(hsv[2], 1.1f - hsv[2]); return SkHSVToColor(SkColorGetA(color), hsv); Lab lab = sRGBToLab(color); float invertedL = std::min(110 - lab.L, 100.0f); if (invertedL > lab.L) { lab.L = invertedL; return LabToSRGB(lab, SkColorGetA(color)); } else { return color; } } static SkColor makeDark(SkColor color) { SkScalar hsv[3]; SkColorToHSV(color, hsv); if (hsv[1] > .2f) return color; // hsv[1] *= .85f; // hsv[2] = std::max(0.0f, std::min(hsv[2], 1 - hsv[2]) * .7f); hsv[2] = std::min(hsv[2], 1.1f - hsv[2]); return SkHSVToColor(SkColorGetA(color), hsv); Lab lab = sRGBToLab(color); float invertedL = std::min(110 - lab.L, 100.0f); if (invertedL < lab.L) { lab.L = invertedL; return LabToSRGB(lab, SkColorGetA(color)); } else { return color; } } static SkColor transformColor(ColorTransform transform, SkColor color) { Loading
libs/hwui/HardwareBitmapUploader.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -286,7 +286,7 @@ sk_sp<Bitmap> HardwareBitmapUploader::allocateHardwareBitmap(const SkBitmap& sou eglDestroySyncKHR(display, fence); } return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info())); return sk_sp<Bitmap>(new Bitmap(buffer.get(), bitmap.info(), Bitmap::computePalette(bitmap))); } }; // namespace android::uirenderer
libs/hwui/hwui/Bitmap.cpp +105 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "Caches.h" #include "HardwareBitmapUploader.h" #include "Properties.h" #include "renderthread/RenderProxy.h" #include "utils/Color.h" Loading @@ -34,6 +35,7 @@ #include <SkToSRGBColorFilter.h> #include <limits> #include <SkHighContrastFilter.h> namespace android { Loading Loading @@ -195,11 +197,13 @@ Bitmap::Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info mPixelStorage.ashmem.size = mappedSize; } Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info) Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette) : SkPixelRef(info.width(), info.height(), nullptr, bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride()) , mInfo(validateAlpha(info)) , mPixelStorageType(PixelStorageType::Hardware) { , mPixelStorageType(PixelStorageType::Hardware) , mPalette(palette) , mPaletteGenerationId(getGenerationID()) { mPixelStorage.hardware.buffer = buffer; buffer->incStrong(buffer); setImmutable(); // HW bitmaps are always immutable Loading Loading @@ -326,7 +330,106 @@ sk_sp<SkImage> Bitmap::makeImage(sk_sp<SkColorFilter>* outputColorFilter) { if (image->colorSpace() != nullptr && !image->colorSpace()->isSRGB()) { *outputColorFilter = SkToSRGBColorFilter::Make(image->refColorSpace()); } // TODO: Move this to the canvas (or other?) layer where we have the target lightness // mode and can selectively do the right thing. if (palette() != BitmapPalette::Unknown && uirenderer::Properties::forceDarkMode) { SkHighContrastConfig config; config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; *outputColorFilter = SkHighContrastFilter::Make(config)->makeComposed(*outputColorFilter); } return image; } class MinMaxAverage { public: void add(float sample) { if (mCount == 0) { mMin = sample; mMax = sample; } else { mMin = std::min(mMin, sample); mMax = std::max(mMax, sample); } mTotal += sample; mCount++; } float average() { return mTotal / mCount; } float min() { return mMin; } float max() { return mMax; } float delta() { return mMax - mMin; } private: float mMin = 0.0f; float mMax = 0.0f; float mTotal = 0.0f; int mCount = 0; }; BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes) { ATRACE_CALL(); SkPixmap pixmap{info, addr, rowBytes}; // TODO: This calculation of converting to HSV & tracking min/max is probably overkill // Experiment with something simpler since we just want to figure out if it's "color-ful" // and then the average perceptual lightness. MinMaxAverage hue, saturation, value; int sampledCount = 0; // Sample a grid of 100 pixels to get an overall estimation of the colors in play const int x_step = std::max(1, pixmap.width() / 10); const int y_step = std::max(1, pixmap.height() / 10); for (int x = 0; x < pixmap.width(); x += x_step) { for (int y = 0; y < pixmap.height(); y += y_step) { SkColor color = pixmap.getColor(x, y); if (!info.isOpaque() && SkColorGetA(color) < 75) { continue; } sampledCount++; float hsv[3]; SkColorToHSV(color, hsv); hue.add(hsv[0]); saturation.add(hsv[1]); value.add(hsv[2]); } } // TODO: Tune the coverage threshold if (sampledCount < 5) { ALOGV("Not enough samples, only found %d for image sized %dx%d, format = %d, alpha = %d", sampledCount, info.width(), info.height(), (int) info.colorType(), (int) info.alphaType()); return BitmapPalette::Unknown; } ALOGV("samples = %d, hue [min = %f, max = %f, avg = %f]; saturation [min = %f, max = %f, avg = %f]", sampledCount, hue.min(), hue.max(), hue.average(), saturation.min(), saturation.max(), saturation.average()); if (hue.delta() <= 20 && saturation.delta() <= .1f) { if (value.average() >= .5f) { return BitmapPalette::Light; } else { return BitmapPalette::Dark; } } return BitmapPalette::Unknown; } } // namespace android
libs/hwui/hwui/Bitmap.h +24 −1 Original line number Diff line number Diff line Loading @@ -34,6 +34,12 @@ enum class PixelStorageType { Hardware, }; enum class BitmapPalette { Unknown, Light, Dark, }; namespace uirenderer { namespace renderthread { class RenderThread; Loading Loading @@ -63,7 +69,7 @@ public: Bitmap(void* address, void* context, FreeFunc freeFunc, const SkImageInfo& info, size_t rowBytes); Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info, size_t rowBytes); Bitmap(GraphicBuffer* buffer, const SkImageInfo& info); Bitmap(GraphicBuffer* buffer, const SkImageInfo& info, BitmapPalette palette = BitmapPalette::Unknown); int rowBytesAsPixels() const { return rowBytes() >> mInfo.shiftPerPixel(); } Loading Loading @@ -103,6 +109,20 @@ public: */ sk_sp<SkImage> makeImage(sk_sp<SkColorFilter>* outputColorFilter); static BitmapPalette computePalette(const SkImageInfo& info, const void* addr, size_t rowBytes); static BitmapPalette computePalette(const SkBitmap& bitmap) { return computePalette(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes()); } BitmapPalette palette() { if (!isHardware() && mPaletteGenerationId != getGenerationID()) { mPalette = computePalette(info(), pixels(), rowBytes()); mPaletteGenerationId = getGenerationID(); } return mPalette; } private: virtual ~Bitmap(); void* getStorage() const; Loading @@ -111,6 +131,9 @@ private: const PixelStorageType mPixelStorageType; BitmapPalette mPalette = BitmapPalette::Unknown; uint32_t mPaletteGenerationId = -1; bool mHasHardwareMipMap = false; union { Loading
libs/hwui/utils/Color.cpp +95 −1 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ #include "Color.h" #include <utils/Log.h> #include <ui/ColorSpace.h> #include <algorithm> #include <cmath> namespace android { Loading Loading @@ -107,5 +109,97 @@ sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { } } template<typename T> static constexpr T clamp(T x, T min, T max) { return x < min ? min : x > max ? max : x; } //static const float2 ILLUMINANT_D50_XY = {0.34567f, 0.35850f}; static const float3 ILLUMINANT_D50_XYZ = {0.964212f, 1.0f, 0.825188f}; static const mat3 BRADFORD = mat3{ float3{ 0.8951f, -0.7502f, 0.0389f}, float3{ 0.2664f, 1.7135f, -0.0685f}, float3{-0.1614f, 0.0367f, 1.0296f} }; static mat3 adaptation(const mat3& matrix, const float3& srcWhitePoint, const float3& dstWhitePoint) { float3 srcLMS = matrix * srcWhitePoint; float3 dstLMS = matrix * dstWhitePoint; return inverse(matrix) * mat3{dstLMS / srcLMS} * matrix; } namespace LabColorSpace { static constexpr float A = 216.0f / 24389.0f; static constexpr float B = 841.0f / 108.0f; static constexpr float C = 4.0f / 29.0f; static constexpr float D = 6.0f / 29.0f; float3 toXyz(const Lab& lab) { float3 v { lab.L, lab.a, lab.b }; v[0] = clamp(v[0], 0.0f, 100.0f); v[1] = clamp(v[1], -128.0f, 128.0f); v[2] = clamp(v[2], -128.0f, 128.0f); float fy = (v[0] + 16.0f) / 116.0f; float fx = fy + (v[1] * 0.002f); float fz = fy - (v[2] * 0.005f); float X = fx > D ? fx * fx * fx : (1.0f / B) * (fx - C); float Y = fy > D ? fy * fy * fy : (1.0f / B) * (fy - C); float Z = fz > D ? fz * fz * fz : (1.0f / B) * (fz - C); v[0] = X * ILLUMINANT_D50_XYZ[0]; v[1] = Y * ILLUMINANT_D50_XYZ[1]; v[2] = Z * ILLUMINANT_D50_XYZ[2]; return v; } Lab fromXyz(const float3& v) { float X = v[0] / ILLUMINANT_D50_XYZ[0]; float Y = v[1] / ILLUMINANT_D50_XYZ[1]; float Z = v[2] / ILLUMINANT_D50_XYZ[2]; float fx = X > A ? pow(X, 1.0f / 3.0f) : B * X + C; float fy = Y > A ? pow(Y, 1.0f / 3.0f) : B * Y + C; float fz = Z > A ? pow(Z, 1.0f / 3.0f) : B * Z + C; float L = 116.0f * fy - 16.0f; float a = 500.0f * (fx - fy); float b = 200.0f * (fy - fz); return Lab { clamp(L, 0.0f, 100.0f), clamp(a, -128.0f, 128.0f), clamp(b, -128.0f, 128.0f) }; } }; Lab sRGBToLab(SkColor color) { auto colorSpace = ColorSpace::sRGB(); float3 rgb; rgb.r = SkColorGetR(color) / 255.0f; rgb.g = SkColorGetG(color) / 255.0f; rgb.b = SkColorGetB(color) / 255.0f; float3 xyz = colorSpace.rgbToXYZ(rgb); float3 srcXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1}); xyz = adaptation(BRADFORD, srcXYZ, ILLUMINANT_D50_XYZ) * xyz; return LabColorSpace::fromXyz(xyz); } SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) { auto colorSpace = ColorSpace::sRGB(); float3 xyz = LabColorSpace::toXyz(lab); float3 dstXYZ = ColorSpace::XYZ(float3{colorSpace.getWhitePoint(), 1}); xyz = adaptation(BRADFORD, ILLUMINANT_D50_XYZ, dstXYZ) * xyz; float3 rgb = colorSpace.xyzToRGB(xyz); return SkColorSetARGB(alpha, static_cast<uint8_t>(rgb.r * 255), static_cast<uint8_t>(rgb.g * 255), static_cast<uint8_t>(rgb.b * 255)); } }; // namespace uirenderer }; // namespace android