* Copyright (c) 2025 Huawei Device Co., Ltd.
* 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 "ge_tone_mapping_helper.h"
#include <algorithm>
#include <mutex>
#include "ge_edge_light_shader_filter.h"
#include "ge_log.h"
namespace OHOS {
namespace Rosen {
namespace {
const std::vector<Vector2f> g_hdrCurveAnchors = {
{0.0f, 0.0f}, {0.75f, 0.75f}, {1.0f, 0.95f}, {1.25f, 0.97f},
{1.5f, 0.98f}, {1.75f, 0.9875f}, {1.90f, 0.995f}, {2.0f, 1.0f},
};
const std::vector<Vector2f> g_hdrCurveControlPoint = {
{0.375f, 0.375f}, {0.875f, 0.875f}, {1.125f, 0.9625f}, {1.375f, 0.9725f},
{1.625f, 0.98f}, {1.825f, 0.99f}, {1.95f, 0.998f},
};
constexpr float SDR_LUMINANCE = 1.0f;
}
Vector4f GEToneMappingHelper::GetBrightnessMapping(float headroom, Vector4f& input)
{
float highValue = std::max({input.x_, input.y_, input.z_, SDR_LUMINANCE});
float compressRatio = GetBrightnessMapping(headroom, highValue) / highValue;
Vector4f output;
output.x_ = input.x_ * compressRatio;
output.y_ = input.y_ * compressRatio;
output.z_ = input.z_ * compressRatio;
output.w_ = input.w_;
return output;
}
float GEToneMappingHelper::GetBrightnessMapping(float headroom, float input)
{
if (ROSEN_GE(headroom, EFFECT_MAX_LUMINANCE)) {
return input;
}
int rangeIndex = -1;
for (auto point : g_hdrCurveAnchors) {
if (input <= point.x_) {
break;
}
rangeIndex++;
}
if (rangeIndex == -1) {
return 0.0f;
}
if ((rangeIndex >= static_cast<int>(g_hdrCurveAnchors.size()) - 1) ||
(rangeIndex >= static_cast<int>(g_hdrCurveControlPoint.size()))) {
return headroom;
}
Vector2f start = g_hdrCurveAnchors[rangeIndex];
Vector2f end = g_hdrCurveAnchors[rangeIndex + 1];
Vector2f control = g_hdrCurveControlPoint[rangeIndex];
float y = 0.0f;
if (CalcBezierResultY(start, end, control, input, y)) {
y = ((EFFECT_MAX_LUMINANCE - headroom) * y + (headroom - SDR_LUMINANCE) * input) /
(EFFECT_MAX_LUMINANCE - SDR_LUMINANCE);
LOGD("GEToneMappingHelper::GetBrightnessMapping y is %{public}f", y);
return std::clamp(y, 0.0f, headroom);
}
return std::clamp(input, 0.0f, headroom);
}
Drawing::Color4f GEToneMappingHelper::GetBrightnessMapping(float brightness, Drawing::Color4f& input)
{
float highValue = std::max({input.redF_, input.greenF_, input.blueF_, SDR_LUMINANCE});
float compressRatio = GetBrightnessMapping(brightness, highValue) / highValue;
Drawing::Color4f output;
output.redF_ = input.redF_ * compressRatio;
output.greenF_ = input.greenF_ * compressRatio;
output.blueF_ = input.blueF_ * compressRatio;
output.alphaF_ = input.alphaF_;
return output;
}
bool GEToneMappingHelper::CalcBezierResultY(
const Vector2f& start, const Vector2f& end, const Vector2f& control, float x, float& y)
{
const float a = start[0] - 2 * control[0] + end[0];
const float b = 2 * (control[0] - start[0]);
const float c = start[0] - x;
const float discriminant = b * b - 4 * a * c;
float t = 0.0f;
if (ROSEN_LNE(discriminant, 0.0f)) {
return false;
} else if (ROSEN_EQ(a, 0.0f)) {
t = -c / b;
} else {
const float sqrtD = std::sqrt(discriminant);
const float t1 = (-b + sqrtD) / (2.0 * a);
const float t2 = (-b - sqrtD) / (2.0 * a);
if (ROSEN_GE(t1, 0.0f) && ROSEN_LE(t1, 1.0f)) {
t = t1;
} else if (ROSEN_GE(t2, 0.0f) && ROSEN_LE(t2, 1.0f)) {
t = t2;
}
}
y = start[1] + t * (2.0f * (control[1] - start[1]) + t * (start[1] - 2.0f * control[1] + end[1]));
return true;
}
}
}