mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:37:35 +00: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
2 changed files with 35 additions and 13 deletions
|
@ -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."
|
||||
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));
|
||||
NonnullRefPtr<ColorSpace> color_space = DeviceGrayColorSpace::the();
|
||||
if (!is_image_mask) {
|
||||
auto color_space_object = MUST(image_dict->get_object(m_document, CommonNames::ColorSpace));
|
||||
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 {};
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue