mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
LibGfx/ICC: Refactor matrix/matrix conversion code a bit
No behavior change; the goal is to make it usable by LibPDF too.
This commit is contained in:
parent
2fc6ec0f46
commit
4704e6aa5f
Notes:
sideshowbarker
2024-07-17 04:32:07 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/4704e6aa5f Pull-request: https://github.com/SerenityOS/serenity/pull/22700
2 changed files with 104 additions and 53 deletions
|
@ -1571,33 +1571,25 @@ ErrorOr<CIELAB> Profile::to_lab(ReadonlyBytes color) const
|
|||
return CIELAB { lab[0], lab[1], lab[2] };
|
||||
}
|
||||
|
||||
bool Profile::is_matrix_matrix_conversion(Profile const& source_profile) const
|
||||
MatrixMatrixConversion::MatrixMatrixConversion(LutCurveType source_red_TRC,
|
||||
LutCurveType source_green_TRC,
|
||||
LutCurveType source_blue_TRC,
|
||||
FloatMatrix3x3 matrix,
|
||||
LutCurveType destination_red_TRC,
|
||||
LutCurveType destination_green_TRC,
|
||||
LutCurveType destination_blue_TRC)
|
||||
: m_source_red_TRC(move(source_red_TRC))
|
||||
, m_source_green_TRC(move(source_green_TRC))
|
||||
, m_source_blue_TRC(move(source_blue_TRC))
|
||||
, m_matrix(matrix)
|
||||
, m_destination_red_TRC(move(destination_red_TRC))
|
||||
, m_destination_green_TRC(move(destination_green_TRC))
|
||||
, m_destination_blue_TRC(move(destination_blue_TRC))
|
||||
{
|
||||
auto has_normal_device_class = [](DeviceClass device) {
|
||||
return device == DeviceClass::InputDevice
|
||||
|| device == DeviceClass::DisplayDevice
|
||||
|| device == DeviceClass::OutputDevice
|
||||
|| device == DeviceClass::ColorSpace;
|
||||
};
|
||||
|
||||
return has_normal_device_class(device_class())
|
||||
&& has_normal_device_class(source_profile.device_class())
|
||||
&& connection_space() == ColorSpace::PCSXYZ
|
||||
&& source_profile.connection_space() == ColorSpace::PCSXYZ
|
||||
&& data_color_space() == ColorSpace::RGB
|
||||
&& source_profile.data_color_space() == ColorSpace::RGB
|
||||
&& !m_cached_has_any_a_to_b_tag
|
||||
&& !source_profile.m_cached_has_any_a_to_b_tag
|
||||
&& m_cached_has_all_rgb_matrix_tags
|
||||
&& source_profile.m_cached_has_all_rgb_matrix_tags;
|
||||
}
|
||||
|
||||
ErrorOr<void> Profile::convert_image_matrix_matrix(Gfx::Bitmap& bitmap, Profile const& source_profile) const
|
||||
Color MatrixMatrixConversion::map(FloatVector3 in_rgb) const
|
||||
{
|
||||
auto const& sourceRedTRC = *source_profile.m_tag_table.get(redTRCTag).value();
|
||||
auto const& sourceGreenTRC = *source_profile.m_tag_table.get(greenTRCTag).value();
|
||||
auto const& sourceBlueTRC = *source_profile.m_tag_table.get(blueTRCTag).value();
|
||||
|
||||
auto evaluate_curve = [](TagData const& trc, float f) {
|
||||
VERIFY(trc.type() == CurveTagData::Type || trc.type() == ParametricCurveTagData::Type);
|
||||
if (trc.type() == CurveTagData::Type)
|
||||
|
@ -1605,12 +1597,6 @@ ErrorOr<void> Profile::convert_image_matrix_matrix(Gfx::Bitmap& bitmap, Profile
|
|||
return static_cast<ParametricCurveTagData const&>(trc).evaluate(f);
|
||||
};
|
||||
|
||||
FloatMatrix3x3 matrix = source_profile.rgb_to_xyz_matrix() * TRY(xyz_to_rgb_matrix());
|
||||
|
||||
auto const& destinationRedTRC = *m_tag_table.get(redTRCTag).value();
|
||||
auto const& destinationGreenTRC = *m_tag_table.get(greenTRCTag).value();
|
||||
auto const& destinationBlueTRC = *m_tag_table.get(blueTRCTag).value();
|
||||
|
||||
auto evaluate_curve_inverse = [](TagData const& trc, float f) {
|
||||
VERIFY(trc.type() == CurveTagData::Type || trc.type() == ParametricCurveTagData::Type);
|
||||
if (trc.type() == CurveTagData::Type)
|
||||
|
@ -1618,36 +1604,77 @@ ErrorOr<void> Profile::convert_image_matrix_matrix(Gfx::Bitmap& bitmap, Profile
|
|||
return static_cast<ParametricCurveTagData const&>(trc).evaluate_inverse(f);
|
||||
};
|
||||
|
||||
FloatVector3 linear_rgb = {
|
||||
evaluate_curve(m_source_red_TRC, in_rgb[0]),
|
||||
evaluate_curve(m_source_green_TRC, in_rgb[1]),
|
||||
evaluate_curve(m_source_blue_TRC, in_rgb[2]),
|
||||
};
|
||||
linear_rgb = m_matrix * linear_rgb;
|
||||
|
||||
linear_rgb.clamp(0.f, 1.f);
|
||||
float device_r = evaluate_curve_inverse(m_destination_red_TRC, linear_rgb[0]);
|
||||
float device_g = evaluate_curve_inverse(m_destination_green_TRC, linear_rgb[1]);
|
||||
float device_b = evaluate_curve_inverse(m_destination_blue_TRC, linear_rgb[2]);
|
||||
|
||||
u8 out_r = round(255 * device_r);
|
||||
u8 out_g = round(255 * device_g);
|
||||
u8 out_b = round(255 * device_b);
|
||||
|
||||
return Color(out_r, out_g, out_b);
|
||||
}
|
||||
|
||||
Optional<MatrixMatrixConversion> Profile::matrix_matrix_conversion(Profile const& source_profile) const
|
||||
{
|
||||
auto has_normal_device_class = [](DeviceClass device) {
|
||||
return device == DeviceClass::InputDevice
|
||||
|| device == DeviceClass::DisplayDevice
|
||||
|| device == DeviceClass::OutputDevice
|
||||
|| device == DeviceClass::ColorSpace;
|
||||
};
|
||||
|
||||
bool is_matrix_matrix_conversion = has_normal_device_class(device_class())
|
||||
&& has_normal_device_class(source_profile.device_class())
|
||||
&& connection_space() == ColorSpace::PCSXYZ
|
||||
&& source_profile.connection_space() == ColorSpace::PCSXYZ
|
||||
&& data_color_space() == ColorSpace::RGB
|
||||
&& source_profile.data_color_space() == ColorSpace::RGB
|
||||
&& !m_cached_has_any_a_to_b_tag
|
||||
&& !source_profile.m_cached_has_any_a_to_b_tag
|
||||
&& m_cached_has_all_rgb_matrix_tags
|
||||
&& source_profile.m_cached_has_all_rgb_matrix_tags
|
||||
&& rgb_to_xyz_matrix().is_invertible();
|
||||
|
||||
if (!is_matrix_matrix_conversion)
|
||||
return OptionalNone {};
|
||||
|
||||
LutCurveType sourceRedTRC = *source_profile.m_tag_table.get(redTRCTag).value();
|
||||
LutCurveType sourceGreenTRC = *source_profile.m_tag_table.get(greenTRCTag).value();
|
||||
LutCurveType sourceBlueTRC = *source_profile.m_tag_table.get(blueTRCTag).value();
|
||||
|
||||
FloatMatrix3x3 matrix = source_profile.rgb_to_xyz_matrix() * MUST(xyz_to_rgb_matrix());
|
||||
|
||||
LutCurveType destinationRedTRC = *m_tag_table.get(redTRCTag).value();
|
||||
LutCurveType destinationGreenTRC = *m_tag_table.get(greenTRCTag).value();
|
||||
LutCurveType destinationBlueTRC = *m_tag_table.get(blueTRCTag).value();
|
||||
|
||||
return MatrixMatrixConversion(sourceRedTRC, sourceGreenTRC, sourceBlueTRC, matrix, destinationRedTRC, destinationGreenTRC, destinationBlueTRC);
|
||||
}
|
||||
|
||||
ErrorOr<void> Profile::convert_image_matrix_matrix(Gfx::Bitmap& bitmap, MatrixMatrixConversion const& map) const
|
||||
{
|
||||
for (auto& pixel : bitmap) {
|
||||
FloatVector3 rgb { (float)Color::from_argb(pixel).red(), (float)Color::from_argb(pixel).green(), (float)Color::from_argb(pixel).blue() };
|
||||
rgb = rgb / 255.0f;
|
||||
|
||||
FloatVector3 linear_rgb = {
|
||||
evaluate_curve(sourceRedTRC, rgb[0]),
|
||||
evaluate_curve(sourceGreenTRC, rgb[1]),
|
||||
evaluate_curve(sourceBlueTRC, rgb[2]),
|
||||
};
|
||||
linear_rgb = matrix * linear_rgb;
|
||||
|
||||
linear_rgb.clamp(0.f, 1.f);
|
||||
float device_r = evaluate_curve_inverse(destinationRedTRC, linear_rgb[0]);
|
||||
float device_g = evaluate_curve_inverse(destinationGreenTRC, linear_rgb[1]);
|
||||
float device_b = evaluate_curve_inverse(destinationBlueTRC, linear_rgb[2]);
|
||||
|
||||
u8 out_r = round(255 * device_r);
|
||||
u8 out_g = round(255 * device_g);
|
||||
u8 out_b = round(255 * device_b);
|
||||
|
||||
pixel = Color(out_r, out_g, out_b, Color::from_argb(pixel).alpha()).value();
|
||||
auto out = map.map(rgb / 255.0f);
|
||||
out.set_alpha(Color::from_argb(pixel).alpha());
|
||||
pixel = out.value();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> Profile::convert_image(Gfx::Bitmap& bitmap, Profile const& source_profile) const
|
||||
{
|
||||
if (is_matrix_matrix_conversion(source_profile))
|
||||
return convert_image_matrix_matrix(bitmap, source_profile);
|
||||
if (auto map = matrix_matrix_conversion(source_profile); map.has_value())
|
||||
return convert_image_matrix_matrix(bitmap, map.value());
|
||||
|
||||
for (auto& pixel : bitmap) {
|
||||
u8 rgb[] = { Color::from_argb(pixel).red(), Color::from_argb(pixel).green(), Color::from_argb(pixel).blue() };
|
||||
|
|
|
@ -149,6 +149,29 @@ struct ProfileHeader {
|
|||
Optional<Crypto::Hash::MD5::DigestType> id;
|
||||
};
|
||||
|
||||
// FIXME: This doesn't belong here.
|
||||
class MatrixMatrixConversion {
|
||||
public:
|
||||
MatrixMatrixConversion(LutCurveType source_red_TRC,
|
||||
LutCurveType source_green_TRC,
|
||||
LutCurveType source_blue_TRC,
|
||||
FloatMatrix3x3 matrix,
|
||||
LutCurveType destination_red_TRC,
|
||||
LutCurveType destination_green_TRC,
|
||||
LutCurveType destination_blue_TRC);
|
||||
|
||||
Color map(FloatVector3) const;
|
||||
|
||||
private:
|
||||
LutCurveType m_source_red_TRC;
|
||||
LutCurveType m_source_green_TRC;
|
||||
LutCurveType m_source_blue_TRC;
|
||||
FloatMatrix3x3 m_matrix;
|
||||
LutCurveType m_destination_red_TRC;
|
||||
LutCurveType m_destination_green_TRC;
|
||||
LutCurveType m_destination_blue_TRC;
|
||||
};
|
||||
|
||||
class Profile : public RefCounted<Profile> {
|
||||
public:
|
||||
static ErrorOr<NonnullRefPtr<Profile>> try_load_from_externally_owned_memory(ReadonlyBytes);
|
||||
|
@ -225,6 +248,8 @@ public:
|
|||
XYZ const& green_matrix_column() const;
|
||||
XYZ const& blue_matrix_column() const;
|
||||
|
||||
Optional<MatrixMatrixConversion> matrix_matrix_conversion(Profile const& source_profile) const;
|
||||
|
||||
private:
|
||||
Profile(ProfileHeader const& header, OrderedHashMap<TagSignature, NonnullRefPtr<TagData>> tag_table)
|
||||
: m_header(header)
|
||||
|
@ -248,8 +273,7 @@ private:
|
|||
// FIXME: The color conversion stuff should be in some other class.
|
||||
ErrorOr<FloatVector3> to_pcs_a_to_b(TagData const& tag_data, ReadonlyBytes) const;
|
||||
ErrorOr<void> from_pcs_b_to_a(TagData const& tag_data, FloatVector3 const&, Bytes) const;
|
||||
bool is_matrix_matrix_conversion(Profile const& source_profile) const;
|
||||
ErrorOr<void> convert_image_matrix_matrix(Gfx::Bitmap& bitmap, Profile const& source_profile) const;
|
||||
ErrorOr<void> convert_image_matrix_matrix(Gfx::Bitmap&, MatrixMatrixConversion const&) const;
|
||||
|
||||
// Cached values.
|
||||
bool m_cached_has_any_a_to_b_tag { false };
|
||||
|
|
Loading…
Reference in a new issue