mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 05:17:35 +00:00
LibPDF+LibGfx: Don't invert CMYK channels in JPEG data in PDFs
This is a hack: Ideally we'd have a CMYK Bitmap pixel format, and we'd convert to rgb at blit time. Then we could also apply color profiles (which for CMYK images are CMYK-based). Also, the colors for our CMYK->RGB conversion are off for PDFs, and we have distinct codepaths for this in Gfx::Color (for paths) and JPEGs. So when we fix that, we'll have to fix it in two places. But this doesn't require a lot of code and it's a huge visual progression, so let's go with it for now.
This commit is contained in:
parent
bd7ae7f91e
commit
bfe27228a3
3 changed files with 26 additions and 6 deletions
|
@ -416,15 +416,16 @@ enum class ColorTransform {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct JPEGLoadingContext {
|
struct JPEGLoadingContext {
|
||||||
JPEGLoadingContext(JPEGStream jpeg_stream)
|
JPEGLoadingContext(JPEGStream jpeg_stream, JPEGDecoderOptions options)
|
||||||
: stream(move(jpeg_stream))
|
: stream(move(jpeg_stream))
|
||||||
|
, options(options)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static ErrorOr<NonnullOwnPtr<JPEGLoadingContext>> create(NonnullOwnPtr<Stream> stream)
|
static ErrorOr<NonnullOwnPtr<JPEGLoadingContext>> create(NonnullOwnPtr<Stream> stream, JPEGDecoderOptions options)
|
||||||
{
|
{
|
||||||
auto jpeg_stream = TRY(JPEGStream::create(move(stream)));
|
auto jpeg_stream = TRY(JPEGStream::create(move(stream)));
|
||||||
return make<JPEGLoadingContext>(move(jpeg_stream));
|
return make<JPEGLoadingContext>(move(jpeg_stream), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
|
@ -453,6 +454,7 @@ struct JPEGLoadingContext {
|
||||||
Array<i16, 4> previous_dc_values {};
|
Array<i16, 4> previous_dc_values {};
|
||||||
MacroblockMeta mblock_meta;
|
MacroblockMeta mblock_meta;
|
||||||
JPEGStream stream;
|
JPEGStream stream;
|
||||||
|
JPEGDecoderOptions options;
|
||||||
|
|
||||||
Optional<ColorTransform> color_transform {};
|
Optional<ColorTransform> color_transform {};
|
||||||
|
|
||||||
|
@ -1640,7 +1642,8 @@ static void invert_colors_for_adobe_images(JPEGLoadingContext const& context, Ve
|
||||||
|
|
||||||
static void cmyk_to_rgb(JPEGLoadingContext const& context, Vector<Macroblock>& macroblocks)
|
static void cmyk_to_rgb(JPEGLoadingContext const& context, Vector<Macroblock>& macroblocks)
|
||||||
{
|
{
|
||||||
invert_colors_for_adobe_images(context, macroblocks);
|
if (context.options.cmyk == JPEGDecoderOptions::CMYK::Normal)
|
||||||
|
invert_colors_for_adobe_images(context, macroblocks);
|
||||||
|
|
||||||
for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) {
|
for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) {
|
||||||
for (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) {
|
for (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) {
|
||||||
|
@ -1944,9 +1947,14 @@ bool JPEGImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JPEGImageDecoderPlugin::create(ReadonlyBytes data)
|
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JPEGImageDecoderPlugin::create(ReadonlyBytes data)
|
||||||
|
{
|
||||||
|
return create_with_options(data, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JPEGImageDecoderPlugin::create_with_options(ReadonlyBytes data, JPEGDecoderOptions options)
|
||||||
{
|
{
|
||||||
auto stream = TRY(try_make<FixedMemoryStream>(data));
|
auto stream = TRY(try_make<FixedMemoryStream>(data));
|
||||||
auto context = TRY(JPEGLoadingContext::create(move(stream)));
|
auto context = TRY(JPEGLoadingContext::create(move(stream), options));
|
||||||
auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) JPEGImageDecoderPlugin(move(context))));
|
auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) JPEGImageDecoderPlugin(move(context))));
|
||||||
TRY(decode_header(*plugin->m_context));
|
TRY(decode_header(*plugin->m_context));
|
||||||
return plugin;
|
return plugin;
|
||||||
|
|
|
@ -16,10 +16,22 @@ struct JPEGLoadingContext;
|
||||||
|
|
||||||
// For the specification, see: https://www.w3.org/Graphics/JPEG/itu-t81.pdf
|
// For the specification, see: https://www.w3.org/Graphics/JPEG/itu-t81.pdf
|
||||||
|
|
||||||
|
struct JPEGDecoderOptions {
|
||||||
|
enum class CMYK {
|
||||||
|
// For standalone jpeg files.
|
||||||
|
Normal,
|
||||||
|
|
||||||
|
// For jpeg data embedded in PDF files.
|
||||||
|
PDF,
|
||||||
|
};
|
||||||
|
CMYK cmyk { CMYK::Normal };
|
||||||
|
};
|
||||||
|
|
||||||
class JPEGImageDecoderPlugin : public ImageDecoderPlugin {
|
class JPEGImageDecoderPlugin : public ImageDecoderPlugin {
|
||||||
public:
|
public:
|
||||||
static bool sniff(ReadonlyBytes);
|
static bool sniff(ReadonlyBytes);
|
||||||
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create(ReadonlyBytes);
|
||||||
|
static ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> create_with_options(ReadonlyBytes, JPEGDecoderOptions = {});
|
||||||
|
|
||||||
virtual ~JPEGImageDecoderPlugin() override;
|
virtual ~JPEGImageDecoderPlugin() override;
|
||||||
virtual IntSize size() override;
|
virtual IntSize size() override;
|
||||||
|
|
|
@ -271,7 +271,7 @@ PDFErrorOr<ByteBuffer> Filter::decode_jbig2(ReadonlyBytes)
|
||||||
PDFErrorOr<ByteBuffer> Filter::decode_dct(ReadonlyBytes bytes)
|
PDFErrorOr<ByteBuffer> Filter::decode_dct(ReadonlyBytes bytes)
|
||||||
{
|
{
|
||||||
if (Gfx::JPEGImageDecoderPlugin::sniff({ bytes.data(), bytes.size() })) {
|
if (Gfx::JPEGImageDecoderPlugin::sniff({ bytes.data(), bytes.size() })) {
|
||||||
auto decoder = TRY(Gfx::JPEGImageDecoderPlugin::create({ bytes.data(), bytes.size() }));
|
auto decoder = TRY(Gfx::JPEGImageDecoderPlugin::create_with_options({ bytes.data(), bytes.size() }, { .cmyk = Gfx::JPEGDecoderOptions::CMYK::PDF }));
|
||||||
auto frame = TRY(decoder->frame(0));
|
auto frame = TRY(decoder->frame(0));
|
||||||
return TRY(frame.image->serialize_to_byte_buffer());
|
return TRY(frame.image->serialize_to_byte_buffer());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue