blob: 66fe44f1b57027b02a03487b683adfed94bc21c9 [file] [log] [blame]
// Copyright 2017 The Cobalt Authors. All Rights Reserved.
//
// 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 <cmath>
#include <cstdlib>
#include <limits>
#include <string>
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "starboard/accessibility.h"
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/jni_utils.h"
#include "starboard/common/log.h"
#include "starboard/configuration.h"
#include "starboard/memory.h"
using starboard::android::shared::JniEnvExt;
using starboard::android::shared::ScopedLocalJavaRef;
namespace {
const int kRgbWhite = 0xFFFFFF;
const int kRgbBlack = 0x000000;
const int kRgbRed = 0xFF0000;
const int kRgbYellow = 0xFFFF00;
const int kRgbGreen = 0x00FF00;
const int kRgbCyan = 0x00FFFF;
const int kRgbBlue = 0x0000FF;
const int kRgbMagenta = 0xFF00FF;
const int kRgbColors[] = {
kRgbWhite,
kRgbBlack,
kRgbRed,
kRgbYellow,
kRgbGreen,
kRgbCyan,
kRgbBlue,
kRgbMagenta,
};
SbAccessibilityCaptionColor GetClosestCaptionColor(int color) {
int ref_color = kRgbWhite;
int min_distance = std::numeric_limits<int>::max();
int r = 0xFF & (color >> 16);
int g = 0xFF & (color >> 8);
int b = 0xFF & (color);
// Find the reference color with the least distance (squared).
for (int i = 0; i < SB_ARRAY_SIZE(kRgbColors); i++) {
int r_ref = 0xFF & (kRgbColors[i] >> 16);
int g_ref = 0xFF & (kRgbColors[i] >> 8);
int b_ref = 0xFF & (kRgbColors[i]);
int distance_squared = pow(r - r_ref, 2) +
pow(g - g_ref, 2) +
pow(b - b_ref, 2);
if (distance_squared < min_distance) {
ref_color = kRgbColors[i];
min_distance = distance_squared;
}
}
switch (ref_color) {
case kRgbWhite:
return kSbAccessibilityCaptionColorWhite;
case kRgbBlack:
return kSbAccessibilityCaptionColorBlack;
case kRgbRed:
return kSbAccessibilityCaptionColorRed;
case kRgbYellow:
return kSbAccessibilityCaptionColorYellow;
case kRgbGreen:
return kSbAccessibilityCaptionColorGreen;
case kRgbCyan:
return kSbAccessibilityCaptionColorCyan;
case kRgbBlue:
return kSbAccessibilityCaptionColorBlue;
case kRgbMagenta:
return kSbAccessibilityCaptionColorMagenta;
default:
NOTREACHED() << "Invalid RGB color conversion";
return kSbAccessibilityCaptionColorWhite;
}
}
SbAccessibilityCaptionCharacterEdgeStyle
AndroidEdgeTypeToSbEdgeStyle(int edge_type) {
switch (edge_type) {
case 0:
return kSbAccessibilityCaptionCharacterEdgeStyleNone;
case 1:
return kSbAccessibilityCaptionCharacterEdgeStyleUniform;
case 2:
return kSbAccessibilityCaptionCharacterEdgeStyleDropShadow;
case 3:
return kSbAccessibilityCaptionCharacterEdgeStyleRaised;
case 4:
return kSbAccessibilityCaptionCharacterEdgeStyleDepressed;
default:
NOTREACHED() << "Invalid edge type conversion";
return kSbAccessibilityCaptionCharacterEdgeStyleNone;
}
}
SbAccessibilityCaptionFontFamily AndroidFontFamilyToSbFontFamily(int family) {
switch (family) {
case 0:
return kSbAccessibilityCaptionFontFamilyCasual;
case 1:
return kSbAccessibilityCaptionFontFamilyCursive;
case 2:
return kSbAccessibilityCaptionFontFamilyMonospaceSansSerif;
case 3:
return kSbAccessibilityCaptionFontFamilyMonospaceSerif;
case 4:
return kSbAccessibilityCaptionFontFamilyProportionalSansSerif;
case 5:
return kSbAccessibilityCaptionFontFamilyProportionalSerif;
case 6:
return kSbAccessibilityCaptionFontFamilySmallCapitals;
default:
NOTREACHED() << "Invalid font family conversion";
return kSbAccessibilityCaptionFontFamilyCasual;
}
}
int FindClosestReferenceValue(int value, const int reference[],
size_t reference_size) {
int result = reference[0];
int min_difference = std::numeric_limits<int>::max();
for (int i = 0; i < reference_size; i++) {
int difference = abs(reference[i] - value);
if (difference < min_difference) {
result = reference[i];
min_difference = difference;
}
}
return result;
}
const int kFontSizes[] = {
25,
50,
75,
100,
125,
150,
175,
200,
225,
250,
275,
300
};
SbAccessibilityCaptionFontSizePercentage GetClosestFontSizePercentage(
int font_size_percent) {
int reference_size = FindClosestReferenceValue(
font_size_percent, kFontSizes, SB_ARRAY_SIZE(kFontSizes));
switch (reference_size) {
case 25:
return kSbAccessibilityCaptionFontSizePercentage25;
case 50:
return kSbAccessibilityCaptionFontSizePercentage50;
case 75:
return kSbAccessibilityCaptionFontSizePercentage75;
case 100:
return kSbAccessibilityCaptionFontSizePercentage100;
case 125:
return kSbAccessibilityCaptionFontSizePercentage125;
case 150:
return kSbAccessibilityCaptionFontSizePercentage150;
case 175:
return kSbAccessibilityCaptionFontSizePercentage175;
case 200:
return kSbAccessibilityCaptionFontSizePercentage200;
case 225:
return kSbAccessibilityCaptionFontSizePercentage225;
case 250:
return kSbAccessibilityCaptionFontSizePercentage250;
case 275:
return kSbAccessibilityCaptionFontSizePercentage275;
case 300:
return kSbAccessibilityCaptionFontSizePercentage300;
default:
NOTREACHED() << "Invalid font size";
return kSbAccessibilityCaptionFontSizePercentage100;
}
}
const int kOpacities[] = {
0,
25,
50,
75,
100,
};
SbAccessibilityCaptionOpacityPercentage GetClosestOpacity(int opacity_percent) {
int reference_opacity_percent = FindClosestReferenceValue(
opacity_percent, kOpacities, SB_ARRAY_SIZE(kOpacities));
switch (reference_opacity_percent) {
case 0:
return kSbAccessibilityCaptionOpacityPercentage0;
case 25:
return kSbAccessibilityCaptionOpacityPercentage25;
case 50:
return kSbAccessibilityCaptionOpacityPercentage50;
case 75:
return kSbAccessibilityCaptionOpacityPercentage75;
case 100:
return kSbAccessibilityCaptionOpacityPercentage100;
default:
NOTREACHED() << "Invalid opacity percentage";
return kSbAccessibilityCaptionOpacityPercentage100;
}
}
SbAccessibilityCaptionState BooleanToCaptionState(bool is_set) {
if (is_set) {
return kSbAccessibilityCaptionStateSet;
} else {
return kSbAccessibilityCaptionStateUnset;
}
}
void SetColorProperties(jobject j_caption_settings,
const char* color_field,
const char* has_color_field,
SbAccessibilityCaptionColor* color,
SbAccessibilityCaptionState* color_state,
SbAccessibilityCaptionOpacityPercentage* opacity,
SbAccessibilityCaptionState* opacity_state) {
JniEnvExt* env = JniEnvExt::Get();
jint j_color = env->GetIntFieldOrAbort(j_caption_settings, color_field, "I");
*color = GetClosestCaptionColor(j_color);
*opacity = GetClosestOpacity((0xFF & (j_color >> 24)) * 100 / 255);
*color_state = BooleanToCaptionState(
env->GetBooleanFieldOrAbort(j_caption_settings, has_color_field, "Z"));
// Color and opacity are combined into a single ARGB value.
// Therefore, if the color is set, so is the opacity.
*opacity_state = *color_state;
}
} // namespace
bool SbAccessibilityGetCaptionSettings(
SbAccessibilityCaptionSettings* caption_settings) {
if (!caption_settings ||
!SbMemoryIsZero(caption_settings,
sizeof(SbAccessibilityCaptionSettings))) {
return false;
}
JniEnvExt* env = JniEnvExt::Get();
ScopedLocalJavaRef<jobject> j_caption_settings(
env->CallStarboardObjectMethodOrAbort(
"getCaptionSettings", "()Ldev/cobalt/media/CaptionSettings;"));
jfloat font_scale =
env->GetFloatFieldOrAbort(j_caption_settings.Get(), "fontScale", "F");
caption_settings->font_size =
GetClosestFontSizePercentage(100.0 * font_scale);
// Android's captioning API always returns a font scale of 1 (100%) if
// the font size has not been set. This means we have no way to check if
// font size is "set" vs. "unset", so we'll just return "set" every time.
caption_settings->font_size_state = kSbAccessibilityCaptionStateSet;
// TODO: Convert Android typeface to font family.
caption_settings->font_family = kSbAccessibilityCaptionFontFamilyCasual;
caption_settings->font_family_state = kSbAccessibilityCaptionStateUnsupported;
caption_settings->character_edge_style = AndroidEdgeTypeToSbEdgeStyle(
env->GetIntFieldOrAbort(j_caption_settings.Get(), "edgeType", "I"));
caption_settings->character_edge_style_state = BooleanToCaptionState(
env->GetBooleanFieldOrAbort(j_caption_settings.Get(),
"hasEdgeType", "Z"));
SetColorProperties(
j_caption_settings.Get(), "foregroundColor", "hasForegroundColor",
&caption_settings->font_color,
&caption_settings->font_color_state,
&caption_settings->font_opacity,
&caption_settings->font_opacity_state);
SetColorProperties(
j_caption_settings.Get(), "backgroundColor", "hasBackgroundColor",
&caption_settings->background_color,
&caption_settings->background_color_state,
&caption_settings->background_opacity,
&caption_settings->background_opacity_state);
SetColorProperties(
j_caption_settings.Get(), "windowColor", "hasWindowColor",
&caption_settings->window_color,
&caption_settings->window_color_state,
&caption_settings->window_opacity,
&caption_settings->window_opacity_state);
caption_settings->is_enabled =
env->GetBooleanFieldOrAbort(j_caption_settings.Get(), "isEnabled", "Z");
caption_settings->supports_is_enabled = true;
caption_settings->supports_set_enabled = false;
return true;
}