From 4fd5d450be4a8caa20ea8c321696dd203b995329 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Tue, 9 Jan 2024 19:17:02 -0500 Subject: [PATCH] LibPDF: Add support for image masks An image mask is a 1-bit-per-pixel bitmap that's black where the current color should be painted, and white where it should be transparent (think: like ink). load_image() already converts images like this into 8-bit-per-pixel images that have 0xff, 0xff, 0xff in rgb for opaque (originally 0 bit) pixels and 0, 0, 0 in rgb for transparent pixels. So we just move copy the image mask's image data into the alpha channel and replace rgb with the current color, and then draw it like a regular bitmap. --- Userland/Libraries/LibPDF/Renderer.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp index 87d166b52d..0c22f9171b 100644 --- a/Userland/Libraries/LibPDF/Renderer.cpp +++ b/Userland/Libraries/LibPDF/Renderer.cpp @@ -1207,10 +1207,20 @@ PDFErrorOr Renderer::show_image(NonnullRefPtr image) return {}; } auto image_bitmap = TRY(load_image(image)); - if (image_bitmap.is_image_mask) - return Error(Error::Type::RenderingUnsupported, "Image masks"); + if (image_bitmap.is_image_mask) { + // PDF 1.7 spec, 4.8.5 Masked Images, Stencil Masking: + // "An image mask (an image XObject whose ImageMask entry is true) [...] is treated as a stencil mask [...]. + // Sample values [...] designate places on the page that should either be marked with the current color or masked out (not marked at all)." + if (!state().paint_style.has()) + return Error(Error::Type::RenderingUnsupported, "Image masks with pattern fill not yet implemented"); - if (image_dict->contains(CommonNames::SMask)) { + // Move mask to alpha channel, and put current color in RGB. + auto current_color = state().paint_style.get(); + for (auto& pixel : *image_bitmap.bitmap) { + u8 mask_alpha = Color::from_argb(pixel).luminosity(); + pixel = current_color.with_alpha(mask_alpha).value(); + } + } else 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.bitmap, smask_bitmap.bitmap)); } else if (image_dict->contains(CommonNames::Mask)) {