mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-20 09:49:15 +03:00
LibPDF: Move error for /ImageMask out of load_image()
...and tweak load_image() to support loading mask images (which don't have a color space and are always 1 bit per pixel).
This commit is contained in:
parent
3ad9782e25
commit
a3507ef65b
Notes:
sideshowbarker
2024-07-17 03:05:16 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/a3507ef65b Pull-request: https://github.com/SerenityOS/serenity/pull/22398
@ -1030,7 +1030,7 @@ static Vector<u8> upsample_to_8_bit(ReadonlyBytes content, int samples_per_line,
|
||||
return upsampled_storage;
|
||||
}
|
||||
|
||||
PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> Renderer::load_image(NonnullRefPtr<StreamObject> image)
|
||||
PDFErrorOr<Renderer::LoadedImage> Renderer::load_image(NonnullRefPtr<StreamObject> image)
|
||||
{
|
||||
auto image_dict = image->dict();
|
||||
auto width = TRY(m_document->resolve_to<int>(image_dict->get_value(CommonNames::Width)));
|
||||
@ -1051,17 +1051,19 @@ PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> Renderer::load_image(NonnullRefPtr<Stream
|
||||
if (TRY(is_filter(CommonNames::JPXDecode))) {
|
||||
return Error(Error::Type::RenderingUnsupported, "JPXDecode filter");
|
||||
}
|
||||
|
||||
bool is_image_mask = false;
|
||||
if (image_dict->contains(CommonNames::ImageMask)) {
|
||||
auto is_mask = TRY(m_document->resolve_to<bool>(image_dict->get_value(CommonNames::ImageMask)));
|
||||
if (is_mask) {
|
||||
return Error(Error::Type::RenderingUnsupported, "Image masks");
|
||||
}
|
||||
is_image_mask = TRY(m_document->resolve_to<bool>(image_dict->get_value(CommonNames::ImageMask)));
|
||||
}
|
||||
|
||||
// "(Required for images, except those that use the JPXDecode filter; not allowed for image masks) [...]
|
||||
// it can be any type of color space except Pattern."
|
||||
NonnullRefPtr<ColorSpace> color_space = DeviceGrayColorSpace::the();
|
||||
if (!is_image_mask) {
|
||||
auto color_space_object = MUST(image_dict->get_object(m_document, CommonNames::ColorSpace));
|
||||
auto color_space = TRY(get_color_space_from_document(color_space_object));
|
||||
color_space = TRY(get_color_space_from_document(color_space_object));
|
||||
}
|
||||
|
||||
auto color_rendering_intent = state().color_rendering_intent;
|
||||
if (image_dict->contains(CommonNames::Intent))
|
||||
@ -1069,7 +1071,11 @@ PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> Renderer::load_image(NonnullRefPtr<Stream
|
||||
// FIXME: Do something with color_rendering_intent.
|
||||
|
||||
// "Valid values are 1, 2, 4, 8, and (in PDF 1.5) 16."
|
||||
auto bits_per_component = TRY(m_document->resolve_to<int>(image_dict->get_value(CommonNames::BitsPerComponent)));
|
||||
// Per spec, this is required even for /Mask images, but it's required to be 1 there.
|
||||
// In practice, it's sometimes missing for /Mask images.
|
||||
auto bits_per_component = 1;
|
||||
if (!is_image_mask)
|
||||
bits_per_component = TRY(m_document->resolve_to<int>(image_dict->get_value(CommonNames::BitsPerComponent)));
|
||||
switch (bits_per_component) {
|
||||
case 1:
|
||||
case 2:
|
||||
@ -1091,6 +1097,13 @@ PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> Renderer::load_image(NonnullRefPtr<Stream
|
||||
upsampled_storage = upsample_to_8_bit(content, width * n_components, bits_per_component, mode);
|
||||
content = upsampled_storage;
|
||||
bits_per_component = 8;
|
||||
|
||||
if (is_image_mask) {
|
||||
// "a sample value of 0 marks the page with the current color, and a 1 leaves the previous contents unchanged."
|
||||
// That's opposite of the normal alpha convention, and we're upsampling masks to 8 bit and use that as normal alpha.
|
||||
for (u8& byte : upsampled_storage)
|
||||
byte = ~byte;
|
||||
}
|
||||
}
|
||||
|
||||
if (bits_per_component == 16) {
|
||||
@ -1113,7 +1126,7 @@ PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> Renderer::load_image(NonnullRefPtr<Stream
|
||||
|
||||
if (TRY(is_filter(CommonNames::DCTDecode))) {
|
||||
// TODO: stream objects could store Variant<bytes/Bitmap> to avoid serialisation/deserialisation here
|
||||
return TRY(Gfx::Bitmap::create_from_serialized_bytes(image->bytes()));
|
||||
return LoadedImage { TRY(Gfx::Bitmap::create_from_serialized_bytes(image->bytes())), is_image_mask };
|
||||
}
|
||||
|
||||
auto bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, { width, height }));
|
||||
@ -1146,7 +1159,7 @@ PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> Renderer::load_image(NonnullRefPtr<Stream
|
||||
++y;
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
return LoadedImage { bitmap, is_image_mask };
|
||||
}
|
||||
|
||||
Gfx::AffineTransform Renderer::calculate_image_space_transformation(int width, int height)
|
||||
@ -1199,9 +1212,12 @@ PDFErrorOr<void> Renderer::show_image(NonnullRefPtr<StreamObject> image)
|
||||
return {};
|
||||
}
|
||||
auto image_bitmap = TRY(load_image(image));
|
||||
if (image_bitmap.is_image_mask)
|
||||
return Error(Error::Type::RenderingUnsupported, "Image masks");
|
||||
|
||||
if (image_dict->contains(CommonNames::SMask)) {
|
||||
auto smask_bitmap = TRY(load_image(TRY(image_dict->get_stream(m_document, CommonNames::SMask))));
|
||||
TRY(apply_alpha_channel(image_bitmap, smask_bitmap));
|
||||
TRY(apply_alpha_channel(image_bitmap.bitmap, smask_bitmap.bitmap));
|
||||
} else if (image_dict->contains(CommonNames::Mask)) {
|
||||
auto mask_object = TRY(image_dict->get_object(m_document, CommonNames::Mask));
|
||||
if (mask_object->is<StreamObject>()) {
|
||||
@ -1213,7 +1229,7 @@ PDFErrorOr<void> Renderer::show_image(NonnullRefPtr<StreamObject> image)
|
||||
|
||||
auto image_space = calculate_image_space_transformation(width, height);
|
||||
auto image_rect = Gfx::FloatRect { 0, 0, width, height };
|
||||
m_painter.draw_scaled_bitmap_with_transform(image_bitmap->rect(), image_bitmap, image_rect, image_space);
|
||||
m_painter.draw_scaled_bitmap_with_transform(image_bitmap.bitmap->rect(), image_bitmap.bitmap, image_rect, image_space);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,13 @@ private:
|
||||
void end_path_paint();
|
||||
PDFErrorOr<void> set_graphics_state_from_dict(NonnullRefPtr<DictObject>);
|
||||
PDFErrorOr<void> show_text(ByteString const&);
|
||||
PDFErrorOr<NonnullRefPtr<Gfx::Bitmap>> load_image(NonnullRefPtr<StreamObject>);
|
||||
|
||||
struct LoadedImage {
|
||||
NonnullRefPtr<Gfx::Bitmap> bitmap;
|
||||
bool is_image_mask = false;
|
||||
};
|
||||
PDFErrorOr<LoadedImage> load_image(NonnullRefPtr<StreamObject>);
|
||||
|
||||
PDFErrorOr<void> show_image(NonnullRefPtr<StreamObject>);
|
||||
void show_empty_image(int width, int height);
|
||||
PDFErrorOr<NonnullRefPtr<ColorSpace>> get_color_space_from_resources(Value const&, NonnullRefPtr<DictObject>);
|
||||
|
Loading…
Reference in New Issue
Block a user