1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-06-01 06:28:13 +00:00

LibGfx/JPEGXL: Include the alpha channel in the output bitmap

Now that we are able to read extra channels, it's time to include them
in the final bitmap. They are usually at a smaller resolution than the
final bitmap and the first step to render them is upscaling. Luckily,
this is not necessary for rendering the `alpha_nonpremultiplied` case of
the conformance test suite, so, as usual, I implemented this rendering
function as a check + no-op.

Then, we simply test if an alpha channel is present and emit the
corresponding data when creating the bitmap.

Finally, it means that we are now capable of rendering images with a
full size alpha channel, like `alpha_nonpremultiplied`. In other words,
we now successfully decode one of the image of the official test suite!
This commit is contained in:
Lucas CHOLLET 2023-07-28 23:24:29 -04:00 committed by Andreas Kling
parent b2a559ddb0
commit fa43c70951

View file

@ -410,6 +410,16 @@ struct ImageMetadata {
{
return number_of_color_channels() + num_extra_channels;
}
Optional<u16> alpha_channel() const
{
for (u16 i = 0; i < ec_info.size(); ++i) {
if (ec_info[i].type == ExtraChannelInfo::ExtraChannelType::kAlpha)
return i + number_of_color_channels();
}
return OptionalNone {};
}
};
static ErrorOr<ImageMetadata> read_metadata_header(LittleEndianInputBitStream& stream)
@ -1197,15 +1207,17 @@ public:
return image;
}
ErrorOr<NonnullRefPtr<Bitmap>> to_bitmap(u8 bits_per_sample) const
ErrorOr<NonnullRefPtr<Bitmap>> to_bitmap(ImageMetadata& metadata) const
{
// FIXME: which channel size should we use?
auto const width = m_channels[0].width();
auto const height = m_channels[0].height();
auto bitmap = TRY(Bitmap::create(BitmapFormat::BGRx8888, { width, height }));
auto bitmap = TRY(Bitmap::create(BitmapFormat::BGRA8888, { width, height }));
// FIXME: This assumes a raw image with RGB channels, other cases are possible
auto const alpha_channel = metadata.alpha_channel();
auto const bits_per_sample = metadata.bit_depth.bits_per_sample;
VERIFY(bits_per_sample >= 8);
for (u32 y {}; y < height; ++y) {
for (u32 x {}; x < width; ++x) {
@ -1218,11 +1230,20 @@ public:
return clamp(sample + .5, 0, (1 << maximum_supported_bit_depth) - 1);
};
Color const color {
to_u8(m_channels[0].get(x, y)),
to_u8(m_channels[1].get(x, y)),
to_u8(m_channels[2].get(x, y)),
};
auto const color = [&]() -> Color {
if (!alpha_channel.has_value()) {
return { to_u8(m_channels[0].get(x, y)),
to_u8(m_channels[1].get(x, y)),
to_u8(m_channels[2].get(x, y)) };
}
return {
to_u8(m_channels[0].get(x, y)),
to_u8(m_channels[1].get(x, y)),
to_u8(m_channels[2].get(x, y)),
to_u8(m_channels[*alpha_channel].get(x, y)),
};
}();
bitmap->set_pixel(x, y, color);
}
}
@ -1767,6 +1788,19 @@ static ErrorOr<void> apply_image_features(Image& image, ImageMetadata const& met
}
///
/// L.4 - Extra channel rendering
static ErrorOr<void> render_extra_channels(Image&, ImageMetadata const& metadata)
{
for (u16 i = metadata.number_of_color_channels(); i < metadata.number_of_channels(); ++i) {
auto const ec_index = i - metadata.number_of_color_channels();
if (metadata.ec_info[ec_index].dim_shift != 0)
TODO();
}
return {};
}
///
class JPEGXLLoadingContext {
public:
JPEGXLLoadingContext(NonnullOwnPtr<Stream> stream)
@ -1805,7 +1839,9 @@ public:
if (m_metadata.xyb_encoded || frame.frame_header.do_YCbCr)
TODO();
m_bitmap = TRY(image.to_bitmap(m_metadata.bit_depth.bits_per_sample));
TRY(render_extra_channels(image, m_metadata));
m_bitmap = TRY(image.to_bitmap(m_metadata));
return {};
}