/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "CanvasTransform.h" #include "Properties.h" #include "utils/Color.h" #include <SkColorFilter.h> #include <SkGradientShader.h> #include <SkPaint.h> #include <SkShader.h> #include <ui/ColorSpace.h> #include <algorithm> #include <cmath> #include <log/log.h> #include <SkHighContrastFilter.h> namespace android::uirenderer { static SkColor makeLight(SkColor color) { 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) { 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) { switch (transform) { case ColorTransform::Light: return makeLight(color); case ColorTransform::Dark: return makeDark(color); default: return color; } } static void applyColorTransform(ColorTransform transform, SkPaint& paint) { if (transform == ColorTransform::None) return; SkColor newColor = transformColor(transform, paint.getColor()); paint.setColor(newColor); if (paint.getShader()) { SkShader::GradientInfo info; std::array<SkColor, 10> _colorStorage; std::array<SkScalar, _colorStorage.size()> _offsetStorage; info.fColorCount = _colorStorage.size(); info.fColors = _colorStorage.data(); info.fColorOffsets = _offsetStorage.data(); SkShader::GradientType type = paint.getShader()->asAGradient(&info); if (info.fColorCount <= 10) { switch (type) { case SkShader::kLinear_GradientType: for (int i = 0; i < info.fColorCount; i++) { info.fColors[i] = transformColor(transform, info.fColors[i]); } paint.setShader(SkGradientShader::MakeLinear(info.fPoint, info.fColors, info.fColorOffsets, info.fColorCount, info.fTileMode, info.fGradientFlags, nullptr)); break; default:break; } } } if (paint.getColorFilter()) { SkBlendMode mode; SkColor color; // TODO: LRU this or something to avoid spamming new color mode filters if (paint.getColorFilter()->asColorMode(&color, &mode)) { color = transformColor(transform, color); paint.setColorFilter(SkColorFilter::MakeModeFilter(color, mode)); } } } static BitmapPalette paletteForColorHSV(SkColor color) { float hsv[3]; SkColorToHSV(color, hsv); return hsv[2] >= .5f ? BitmapPalette::Light : BitmapPalette::Dark; } static BitmapPalette filterPalette(const SkPaint* paint, BitmapPalette palette) { if (palette == BitmapPalette::Unknown || !paint || !paint->getColorFilter()) { return palette; } SkColor color = palette == BitmapPalette::Light ? SK_ColorWHITE : SK_ColorBLACK; color = paint->getColorFilter()->filterColor(color); return paletteForColorHSV(color); } bool transformPaint(ColorTransform transform, SkPaint* paint) { // TODO applyColorTransform(transform, *paint); return true; } bool transformPaint(ColorTransform transform, SkPaint* paint, BitmapPalette palette) { palette = filterPalette(paint, palette); bool shouldInvert = false; if (palette == BitmapPalette::Light && transform == ColorTransform::Dark) { shouldInvert = true; } if (palette == BitmapPalette::Dark && transform == ColorTransform::Light) { shouldInvert = true; } if (shouldInvert) { SkHighContrastConfig config; config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness; paint->setColorFilter(SkHighContrastFilter::Make(config)->makeComposed(paint->refColorFilter())); } return shouldInvert; } } // namespace android::uirenderer