blob: b4c83629cfa2da5f55a9a1ace1dbf2cdb6b22f7c [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "color_transform_manager.h"
#include <lib/fidl/cpp/clone.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/status.h>
#include "src/ui/a11y/lib/util/util.h"
namespace a11y {
// clang-format off
const std::array<float, 9> kIdentityMatrix = {
1, 0, 0,
0, 1, 0,
0, 0, 1};
const std::array<float, 3> kZero3x1Vector = {0, 0, 0};
// To invert a color vector in RGB space, we first convert it to
// YIQ color space, then rotate it along Y axis for 180 degrees,
// convert it back to RGB space, and subtract it by 1.
//
// Formula of inverted color:
// [R' G' B']' = [1, 1, 1] - inv(T) . diag(1, -1, -1) . T . [R G B]'
// = [1, 1, 1] + kColorInversionMatrix . [R G B]'
//
// where R, G, B \in [0, 1], and T is the RGB to YIQ conversion
// matrix:
// T = [[0.299 0.587 0.114]
// [0.596 -0.274 -0.321]
// [0.211 -0.523 0.311]]
//
// Thus the color inveresion matrix is
// kColorInversionMatrix
// = [[ 0.402 -1.174 -0.228]
// [-0.598 -0.174 -0.228]
// [-0.599 -1.177 0.771]]
//
const std::array<float, 9> kColorInversionMatrix = {
0.402f, -1.174f, -0.228f,
-0.598f, -0.174f, -0.228f,
-0.599f, -1.177f, 0.771f};
// Post offsets should be strictly less than 1.
const std::array<float, 3> kColorInversionPostOffset = {.999f, .999f, .999f};
const std::array<float, 9> kCorrectProtanomaly = {
0.622774, 0.377226, 0.000000,
0.264275, 0.735725, -0.000000,
0.216821, -0.216821, 1.000000};
const std::array<float, 9> kCorrectDeuteranomaly = {
0.288299f, 0.711701f, 0.000000f,
0.052709f, 0.947291f, -0.000000f,
-0.257912f, 0.257912f, 1.000000f};
const std::array<float, 9> kCorrectTritanomaly = {
1.000000f, -0.805712f, 0.805712f,
0.000000f, 0.378838f, 0.621162f,
-0.000000f, 0.104823f, 0.895177f};
// clang-format on
namespace {
struct ColorAdjustmentArgs {
std::array<float, 9> color_adjustment_matrix;
std::array<float, 3> color_adjustment_pre_offset;
std::array<float, 3> color_adjustment_post_offset;
};
ColorAdjustmentArgs GetColorAdjustmentArgs(
bool color_inversion_enabled,
fuchsia::accessibility::ColorCorrectionMode color_correction_mode) {
std::array<float, 9> color_inversion_matrix = kIdentityMatrix;
std::array<float, 9> color_correction_matrix = kIdentityMatrix;
std::array<float, 3> color_adjustment_pre_offset = kZero3x1Vector;
std::array<float, 3> color_adjustment_post_offset = kZero3x1Vector;
if (color_inversion_enabled) {
color_inversion_matrix = kColorInversionMatrix;
color_adjustment_post_offset = kColorInversionPostOffset;
}
switch (color_correction_mode) {
case fuchsia::accessibility::ColorCorrectionMode::CORRECT_PROTANOMALY:
color_correction_matrix = kCorrectProtanomaly;
break;
case fuchsia::accessibility::ColorCorrectionMode::CORRECT_DEUTERANOMALY:
color_correction_matrix = kCorrectDeuteranomaly;
break;
case fuchsia::accessibility::ColorCorrectionMode::CORRECT_TRITANOMALY:
color_correction_matrix = kCorrectTritanomaly;
break;
case fuchsia::accessibility::ColorCorrectionMode::DISABLED:
// fall through
default:
color_correction_matrix = kIdentityMatrix;
break;
}
return ColorAdjustmentArgs{.color_adjustment_matrix = Multiply3x3MatrixRowMajor(
color_inversion_matrix, color_correction_matrix),
.color_adjustment_pre_offset = color_adjustment_pre_offset,
.color_adjustment_post_offset = color_adjustment_post_offset};
}
} // namespace
ColorTransformManager::ColorTransformManager(sys::ComponentContext* startup_context) {
FX_CHECK(startup_context);
startup_context->outgoing()->AddPublicService(bindings_.GetHandler(this));
}
void ColorTransformManager::RegisterColorTransformHandler(
fidl::InterfaceHandle<fuchsia::accessibility::ColorTransformHandler> handle) {
color_transform_handler_ptr_ = handle.Bind();
color_transform_handler_ptr_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "ColorTransformHandler disconnected with status: "
<< zx_status_get_string(status);
});
MaybeSetColorTransformConfiguration();
}
void ColorTransformManager::ChangeColorTransform(
bool color_inversion_enabled,
fuchsia::accessibility::ColorCorrectionMode color_correction_mode) {
fuchsia::accessibility::ColorTransformConfiguration color_transform_configuration;
ColorAdjustmentArgs color_adjustment_args =
GetColorAdjustmentArgs(color_inversion_enabled, color_correction_mode);
color_transform_configuration.set_color_inversion_enabled(color_inversion_enabled);
color_transform_configuration.set_color_correction(color_correction_mode);
color_transform_configuration.set_color_adjustment_matrix(
color_adjustment_args.color_adjustment_matrix);
color_transform_configuration.set_color_adjustment_post_offset(
color_adjustment_args.color_adjustment_post_offset);
color_transform_configuration.set_color_adjustment_pre_offset(
color_adjustment_args.color_adjustment_pre_offset);
cached_color_transform_configuration_ = std::move(color_transform_configuration);
MaybeSetColorTransformConfiguration();
}
void ColorTransformManager::MaybeSetColorTransformConfiguration() {
if (!color_transform_handler_ptr_ || !cached_color_transform_configuration_) {
return;
}
// The use of fidl::Clone here is preferred over std::move to handle the
// unlikely case in which ColorTransformHandler is re-bound. Color transform
// changes should be infrequent, so the performance penalty is worth the
// additional robustness.
color_transform_handler_ptr_->SetColorTransformConfiguration(
fidl::Clone(*cached_color_transform_configuration_),
[] { FX_LOGS(INFO) << "Color transform configuration changed."; });
}
} // namespace a11y