Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default JPEG encoding options are not publicly accessible #133

Closed
loudenvier opened this issue Nov 2, 2023 · 4 comments
Closed

Default JPEG encoding options are not publicly accessible #133

loudenvier opened this issue Nov 2, 2023 · 4 comments

Comments

@loudenvier
Copy link

loudenvier commented Nov 2, 2023

I know I can pass a JpegEncoderOptions object when calling MagicProcessor.ProcessImages, but I'm at a loss how to set default jpeg encoder options. Since the encoder is selected automatically depending on the source file extension or signature, I only want to control jpeg options and not force all files to be converted to jpeg. My current code is, sadly, forcing jpeg encoding for all files:

MagicImageProcessor.ProcessImage(filename, stm, new ProcessImageSettings {
    Width = o.Width ?? 0,
    Height = o.Height ?? 0,
    ResizeMode = CropScaleMode.Max,
    HybridMode = o.ScaleMode,
    EncoderOptions = new JpegEncoderOptions {
        Quality = 70,
}

I'm aware of CodecManager but I didn't find any example on how to simply setup some encoder specific default options, more precisely jpeg options. I know it's probably a silly question :-) but I'm not being able to solve it by myself.

@saucecontrol
Copy link
Owner

saucecontrol commented Nov 2, 2023

Howdy!

In general, you can change the default options for a codec just after registration, like this:

CodecManager.Configure(codecs => {
    // locate the desired codec
    var png = codecs.OfType<EncoderInfo>().First(c => c.MimeTypes.FirstOrDefault() == ImageMimeTypes.Png);

    // remove the default definition
    codecs.Remove(png);

    // replace it with a new one with different options
    codecs.Add(png with { DefaultOptions = new PngEncoderOptions(PngFilter.None, Interlace: false) });
});

However, it seems that I inadvertently left the encoder definition for JPEG defined as internal, so you won't be able to change JPEG options through the normal API. It is, however, possible to do in the current release through reflection, and this will be safe when done at app startup in CodecManager.Configure.

CodecManager.Configure(codecs => {
    // locate the JPEG codec
    var jpeg = codecs.OfType<IImageEncoderInfo>().First(c => c.MimeTypes.FirstOrDefault() == ImageMimeTypes.Jpeg);

    // change the default options on the existing definition
    var prop = jpeg.GetType().GetProperty(nameof(IImageEncoderInfo.DefaultOptions));
    prop!.SetValue(jpeg, new JpegEncoderOptions(Quality: 70, Subsample: ChromaSubsampleMode.Subsample420));
});

This is also possible with the libjpeg native plugin, which supports progressive encoding for smaller file sizes.

CodecManager.Configure(codecs => {
    // register the libjpeg plugin, replacing the Windows codec if present
    codecs.UseLibjpeg();

    // locate the JPEG codec
    var jpeg = codecs.OfType<IImageEncoderInfo>().First(c => c.MimeTypes.FirstOrDefault() == ImageMimeTypes.Jpeg);

    // change the default options on the existing definition
    var prop = jpeg.GetType().GetProperty(nameof(IImageEncoderInfo.DefaultOptions));
    prop!.SetValue(jpeg, new JpegOptimizedEncoderOptions(Quality: 70, Subsample: ChromaSubsampleMode.Subsample420, Progressive: JpegProgressiveMode.Semi));
});

I will plan on making the JPEG options public in a future release so that the reflection call isn't necessary.

@saucecontrol saucecontrol changed the title How to set default JPEG encoding options? Default JPEG encoding options are not publicly accessible Nov 2, 2023
@loudenvier
Copy link
Author

Oh Internal classes, been there, done that :-) ... They have a tendency to come back and bite me in the butt... When I started "doing" Kotlin programming I found it so strange that everything defaults to public, but now I'm leaning towards this and only making private the really, really, really implementation details. If something needs to be visible inside other files in the assembly (internal) chances are they'll be needed somewhere else down the road :-)

But, anyways, reflection to the rescue. Everything now works like a charm! I'm using it on a simple Image Resizer app: https://github.com/loudenvier/imgsizer

(still need to upload the changes based on your help though)

@saucecontrol
Copy link
Owner

saucecontrol commented Mar 6, 2024

I've just published v0.14.1 with the fix, so the reflection workaround is no longer necessary. Something like this should work:

CodecManager.Configure(codecs => {
    // locate the JPEG codec
    var jpeg = codecs.OfType<EncoderInfo>().First(c => c.MimeTypes.FirstOrDefault() == ImageMimeTypes.Jpeg);

    // remove the default definition
    codecs.Remove(jpeg);

    // replace it with a new one with different options
    codecs.Add(jpeg with { DefaultOptions = new JpegEncoderOptions(Quality: 70, Subsample: ChromaSubsampleMode.Subsample420) });
});

@loudenvier
Copy link
Author

loudenvier commented Mar 6, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants