text: // Copyright 2019 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "third_party/blink/renderer/platform/graphics/dark_mode_color_filter.h"
  
  #include "base/check.h"
  #include "base/notreached.h"
  #include "third_party/blink/renderer/platform/graphics/dark_mode_lab_color_space.h"
  #include "third_party/skia/include/core/SkColorFilter.h"
  #include "third_party/skia/include/effects/SkHighContrastFilter.h"
  #include "third_party/skia/include/effects/SkTableColorFilter.h"
  
  namespace blink {
  namespace {
  
  // SkColorFilterWrapper implementation.
  class SkColorFilterWrapper : public DarkModeColorFilter {
   public:
    static std::unique_ptr<SkColorFilterWrapper> Create(
        sk_sp<SkColorFilter> color_filter) {
      return std::unique_ptr<SkColorFilterWrapper>(
          new SkColorFilterWrapper(color_filter));
    }
  
    static std::unique_ptr<SkColorFilterWrapper> Create(
        SkHighContrastConfig::InvertStyle invert_style,
        const DarkModeSettings& settings) {
      SkHighContrastConfig config;
      config.fInvertStyle = invert_style;
      config.fGrayscale = settings.grayscale;
      config.fContrast = settings.contrast;
  
      return std::unique_ptr<SkColorFilterWrapper>(
          new SkColorFilterWrapper(SkHighContrastFilter::Make(config)));
    }
  
    SkColor InvertColor(SkColor color) const override {
      return filter_->filterColor(color);
    }
  
    sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
  
   private:
    explicit SkColorFilterWrapper(sk_sp<SkColorFilter> filter)
        : filter_(filter) {}
  
    sk_sp<SkColorFilter> filter_;
  };
  
  // LABColorFilter implementation.
  class LABColorFilter : public DarkModeColorFilter {
   public:
    LABColorFilter() : transformer_(lab::DarkModeSRGBLABTransformer()) {
      SkHighContrastConfig config;
      config.fInvertStyle = SkHighContrastConfig::InvertStyle::kInvertLightness;
      config.fGrayscale = false;
      config.fContrast = 0.0;
      filter_ = SkHighContrastFilter::Make(config);
    }
  
    SkColor InvertColor(SkColor color) const override {
      SkV3 rgb = {SkColorGetR(color) / 255.0f, SkColorGetG(color) / 255.0f,
                  SkColorGetB(color) / 255.0f};
      SkV3 lab = transformer_.SRGBToLAB(rgb);
      lab.x = std::min(110.0f - lab.x, 100.0f);
      rgb = transformer_.LABToSRGB(lab);
  
      SkColor inverted_color = SkColorSetARGB(
          SkColorGetA(color), static_cast<unsigned int>(rgb.x * 255 + 0.5),
          static_cast<unsigned int>(rgb.y * 255 + 0.5),
          static_cast<unsigned int>(rgb.z * 255 + 0.5));
      return AdjustGray(inverted_color);
    }
  
    sk_sp<SkColorFilter> ToSkColorFilter() const override { return filter_; }
  
   private:
    // Further darken dark grays to match the primary surface color recommended by
    // the material design guidelines:
    //   https://material.io/design/color/dark-theme.html#properties
    //
    // TODO(gilmanmh): Consider adding a more general way to adjust colors after
    // applying the main filter.
    SkColor AdjustGray(SkColor color) const {
      static const uint8_t kBrightnessThreshold = 32;
      static const uint8_t kAdjustedBrightness = 18;
  
      uint8_t r = SkColorGetR(color);
      uint8_t g = SkColorGetG(color);
      uint8_t b = SkColorGetB(color);
  
      if (r == b && r == g && r < kBrightnessThreshold &&
          r > kAdjustedBrightness) {
        return SkColorSetRGB(kAdjustedBrightness, kAdjustedBrightness,
                             kAdjustedBrightness);
      }
  
      return color;
    }
  
    const lab::DarkModeSRGBLABTransformer transformer_;
    sk_sp<SkColorFilter> filter_;
  };
  
  }  // namespace
  
  std::unique_ptr<DarkModeColorFilter> DarkModeColorFilter::FromSettings(
      const DarkModeSettings& settings) {
    switch (settings.mode) {
      case DarkModeInversionAlgorithm::kSimpleInvertForTesting:
        uint8_t identity[256], invert[256];
        for (int i = 0; i < 256; ++i) {
          identity[i] = i;
          invert[i] = 255 - i;
        }
        return SkColorFilterWrapper::Create(
            SkTableColorFilter::MakeARGB(identity, invert, invert, invert));
  
      case DarkModeInversionAlgorithm::kInvertBrightness:
        return SkColorFilterWrapper::Create(
            SkHighContrastConfig::InvertStyle::kInvertBrightness, settings);
  
      case DarkModeInversionAlgorithm::kInvertLightness:
        return SkColorFilterWrapper::Create(
            SkHighContrastConfig::InvertStyle::kInvertLightness, settings);
  
      case DarkModeInversionAlgorithm::kInvertLightnessLAB:
        return std::make_unique<LABColorFilter>();
    }
    NOTREACHED();
  }
  
  DarkModeColorFilter::~DarkModeColorFilter() {}
  
  }  // namespace blink
  