Add progressive JPEG encoder#2740
Conversation
|
Alexandr Ivanov seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
|
Hi @JimBobSquarePants, I've added a test. The test suite seems quite complicated to me, and I need more time to understand how it works before I can write more complex, byte-level tests. I've also split the I'd like to add a restart interval as well, but it seems to require more changes. |
Thanks for the updates! I've no issue with duplicate I'll pull down your code ASAP and have a good read through. Maybe I can help write tests. |
|
Hi @ardabada apologies for the slow response. The code all looks great so far! I think you can either
I'm happy with whatever approach you take. For tests I would keep it high level and simply encode/verify the output against expected results. |
| [WithFile(TestImages.Png.BikeGrayscale, nameof(LuminanceEncodingSetups), PixelTypes.L8)] | ||
| [WithFile(TestImages.Jpeg.Baseline.Cmyk, nameof(CmykEncodingSetups), PixelTypes.Rgb24)] | ||
| [WithFile(TestImages.Jpeg.Baseline.Ycck, nameof(YcckEncodingSetups), PixelTypes.Rgb24)] | ||
| public void EncodeProgressive_DefaultNumberOfScans<TPixel>(TestImageProvider<TPixel> provider, JpegEncodingColor colorType, int quality, float tolerance) |
There was a problem hiding this comment.
If you can add an additional test setting the restart interval this would be good to merge! 👍
|
@ardabada I pulled down your code and added the following test. It appears that writing anything other than the default value causes our decoder to fail. [Theory]
[WithFile(TestImages.Png.CalliphoraPartial, nameof(NonSubsampledEncodingSetups), PixelTypes.Rgb24)]
[WithFile(TestImages.Png.CalliphoraPartial, nameof(SubsampledEncodingSetups), PixelTypes.Rgb24)]
[WithFile(TestImages.Png.BikeGrayscale, nameof(LuminanceEncodingSetups), PixelTypes.L8)]
[WithFile(TestImages.Jpeg.Baseline.Cmyk, nameof(CmykEncodingSetups), PixelTypes.Rgb24)]
[WithFile(TestImages.Jpeg.Baseline.Ycck, nameof(YcckEncodingSetups), PixelTypes.Rgb24)]
public void EncodeProgressive_CustomNumberOfScans<TPixel>(TestImageProvider<TPixel> provider, JpegEncodingColor colorType, int quality, float tolerance)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage();
JpegEncoder encoder = new()
{
Quality = quality,
ColorType = colorType,
Progressive = true,
RestartInterval = 7
};
string info = $"{colorType}-Q{quality}";
using MemoryStream ms = new();
image.SaveAsJpeg(ms, encoder);
ms.Position = 0;
// TEMP: Save decoded output as PNG so we can do a pixel compare.
using Image<TPixel> image2 = Image.Load<TPixel>(ms);
image2.DebugSave(provider, testOutputDetails: info, extension: "png");
ImageComparer comparer = new TolerantImageComparer(tolerance);
image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "jpg");
}Here's an encoded jpeg which seems to be decodable by browsers, Windows, and System.Drawing. And here's how our decoder sees it. I did find an issue with DRI marker writing where we were writing too many bytes to the stream (see fixed version below) /// <summary>
/// Writes the DRI marker
/// </summary>
/// <param name="restartInterval">Numbers of MCUs between restart markers.</param>
/// <param name="buffer">Temporary buffer.</param>
private void WriteDri(int restartInterval, Span<byte> buffer)
{
if (restartInterval <= 0)
{
return;
}
this.WriteMarkerHeader(JpegConstants.Markers.DRI, 4, buffer);
buffer[1] = (byte)(restartInterval & 0xff);
buffer[0] = (byte)((restartInterval >> 8) & 0xff);
this.outputStream.Write(buffer, 0, 2); // See explicit offset and length.
}However, I think the issue is with the @br3aker If you have any time to help out here it would be greatly appreciated. |
|
Hi, @JimBobSquarePants, sorry for such long silence. I am currently looking into the |
No worries at all and thanks for replying. I'd like to get the bug in the reader fixed if possible before merging so that we don't forget. Would you be happy to investigate? |
|
Hi @JimBobSquarePants. PR updated. Looks like decoder is fixed now. |
|
Legend! Thanks for fixing it. I’ll pull down and review ASAP |
JimBobSquarePants
left a comment
There was a problem hiding this comment.
This is fantastic thank you. I feature I've wanted for many years!
| this.FlushToStream(); | ||
| } | ||
|
|
||
| if (this.restartInterval > 0) |
There was a problem hiding this comment.
Multiple if is fine here. Libjpeg turbo does the same.
Updated [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) from 3.1.12 to 4.0.0. <details> <summary>Release notes</summary> _Sourced from [SixLabors.ImageSharp's releases](https://github.com/SixLabors/ImageSharp/releases)._ ## 4.0.0 ## What's Changed * Update to net8 by @stefannikolei in SixLabors/ImageSharp#2583 * Handle dedup of local palette of 256 length - Main by @JimBobSquarePants in SixLabors/ImageSharp#2607 * Replace custom Crc32 by @JimBobSquarePants in SixLabors/ImageSharp#2611 * Sync 3.1 DrawImage fixes by @tocsoft in SixLabors/ImageSharp#2612 * Fix handling gif encoding for global palettes - Main by @JimBobSquarePants in SixLabors/ImageSharp#2615 * Bump actions/setup-dotnet from 3 to 4 by @dependabot[bot] in SixLabors/ImageSharp#2613 * Adjusted the casing of the Webp format name by @jscarle in SixLabors/ImageSharp#2623 * Fix Paeth Filter decode on platforms that do not support Ssse3 - Main by @JimBobSquarePants in SixLabors/ImageSharp#2620 * Fix WebP animation speed bug by @marklagendijk in SixLabors/ImageSharp#2624 * Promote PixelTypeInfo to Pixel by @stefannikolei in SixLabors/ImageSharp#2601 * TGA: Treat 32 bit True Color images always as transparent by @brianpopow in SixLabors/ImageSharp#2643 * Modernize and optimize pixel format operations across platforms. by @JimBobSquarePants in SixLabors/ImageSharp#2645 * Cleanup SimdUtils by @JimBobSquarePants in SixLabors/ImageSharp#2654 * Bump actions/cache from 3 to 4 by @dependabot[bot] in SixLabors/ImageSharp#2648 * Bump codecov/codecov-action from 3 to 4 by @dependabot[bot] in SixLabors/ImageSharp#2657 * Bump NuGet/setup-nuget from 1 to 2 by @dependabot[bot] in SixLabors/ImageSharp#2658 * Add v3.1.x fixes #2673 and #2674 into main. by @JimBobSquarePants in SixLabors/ImageSharp#2675 * Add fixes 2668, 2676, and 2677 to main by @JimBobSquarePants in SixLabors/ImageSharp#2678 * Merge 2681 to v4 Main by @JimBobSquarePants in SixLabors/ImageSharp#2690 * Add JPEG COM marker support by @RobertMut in SixLabors/ImageSharp#2641 * Bump actions/upload-artifact from 3 to 4 by @dependabot[bot] in SixLabors/ImageSharp#2625 * Only exit JPEG scan decoding after multiple EOF hits by @JimBobSquarePants in SixLabors/ImageSharp#2701 * V4 Ensure VP8X alpha flag is updated correctly. by @JimBobSquarePants in SixLabors/ImageSharp#2703 * Fix animated png handling (issue #2708) by @SpaceCheetah in SixLabors/ImageSharp#2710 * Merge latest release from v3 by @JimBobSquarePants in SixLabors/ImageSharp#2720 * Fix MacOS jobs by @antonfirsov in SixLabors/ImageSharp#2728 * Fix async-over-sync issue in Image.DecodeAsync() by @kroymann in SixLabors/ImageSharp#2725 * Fix overflow in MemoryAllocator.Create(options) by @antonfirsov in SixLabors/ImageSharp#2730 * GifDecoder: Limit lzw bits to a maximum of 12 bits by @brianpopow in SixLabors/ImageSharp#2744 * GifDecoder : Allow skipping bad metadata using identify by @JimBobSquarePants in SixLabors/ImageSharp#2749 * Add ICO and CUR file decoder. by @frg2089 in SixLabors/ImageSharp#2579 * v4 - Fix off-by-one error when centering a transform. by @JimBobSquarePants in SixLabors/ImageSharp#2761 * v4 Fix 2758 by @JimBobSquarePants in SixLabors/ImageSharp#2764 * Simplify Color Space Conversion APIs by @JimBobSquarePants in SixLabors/ImageSharp#2739 * Webp: Fix Issue 2763 by @brianpopow in SixLabors/ImageSharp#2767 * V4 Correctly break during Png decoding by @JimBobSquarePants in SixLabors/ImageSharp#2773 * V4 : Fix filtering on PNG encode. by @JimBobSquarePants in SixLabors/ImageSharp#2778 * Fix #2779 buffer overrun by @KirillAldashkin in SixLabors/ImageSharp#2780 * Fix ImageMetadata docs typo by @lofcz in SixLabors/ImageSharp#2781 * Add API for metadata conversion between formats. by @JimBobSquarePants in SixLabors/ImageSharp#2751 * Tiff decoder: Fix issue 2679 by @brianpopow in SixLabors/ImageSharp#2789 * Replace PngCrcChunkHandling by @JimBobSquarePants in SixLabors/ImageSharp#2786 * Add tagname to debugger visualization for Exif- and Iptc-values, to facilitate easier debugging and discovery by @lassevk in SixLabors/ImageSharp#2787 * V4 - Correctly handle transform spaces when building transform matrices. by @JimBobSquarePants in SixLabors/ImageSharp#2795 * Allow decoding Tiff of different frame size. by @JimBobSquarePants in SixLabors/ImageSharp#2788 * Add progressive JPEG encoder by @ardabada in SixLabors/ImageSharp#2740 * Fix using dither in BmpEncoder when bit per pixel is <= 4 by @mistoll in SixLabors/ImageSharp#2819 * Add QuadDistortion to ProjectiveTransformBuilder by @Socolin in SixLabors/ImageSharp#2748 * WEBP : Use Correct Width With AlphaDecoder by @JimBobSquarePants in SixLabors/ImageSharp#2823 ... (truncated) Commits viewable in [compare view](SixLabors/ImageSharp@v3.1.12...v4.0.0). </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: WarperSan <leumas.ecole@gmail.com>


Prerequisites
Description
This PR adds progressive JPEG encoder (see #10 and #449).
Implementation adapted from https://github.com/vstroebel/jpeg-encoder
No tests added yet. Restart interval also should be added.
Please take a look if it makes sense to you.