Uh oh!
There was an error while loading. Please reload this page.
Add support for decoding jpeg's with arithmetic coding - #2073
Conversation
gfoidl
left a comment
There was a problem hiding this comment.
I always enjoy reading such fine code.
At the same time I'm sorry for so much comments.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| return; | ||
| } | ||
| int blockCol = (mcuCol * h) + x; |
There was a problem hiding this comment.
You could hoist mcuCol * h before the loop for (int x ..., as it's invariant for this loop and I don't expect JIT to hoist it for us (especially on older runtimes).
| this.DecodeBlockBaseline( | ||
| component, | ||
| ref Unsafe.Add(ref blockRef, blockCol), |
There was a problem hiding this comment.
| refUnsafe.Add(refblockRef,blockCol), | |
| refUnsafe.Add(refblockRef,(uint)blockCol), |
to avoid the sign-extending move.
There was a problem hiding this comment.
@gfoidl I dont understand the suggestion: How can I use uint here, when Unsafe.Add expects int?
There was a problem hiding this comment.
Here it's for the overload Unsafe.Add<T>(ref T source, nuint elementOffset) and uses the implicit conversion from uint -> nuint.
So the long form of the suggestion is
| refUnsafe.Add(refblockRef,blockCol), | |
| refUnsafe.Add(refblockRef,(nuint)(uint)blockCol), |
but as the compiler does the second conversion for us, I've shortened it to
| refUnsafe.Add(refblockRef,blockCol), | |
| refUnsafe.Add(refblockRef,(uint)blockCol), |
In essence, and to avoid the movsxd (the sign extending move), we need to convert the int to any of IntPtr / nint or UIntPtr / nuint. As first step here is to prove to the JIT that there's only positive numbers by the uint-cast, which elides the movsxd emitting.
The actual target type (nint or nuint respectively there IntPtr base variants) doesn't really matter then.
There was a problem hiding this comment.
Thank you for the explanation. Now I see why it does not work: The uint overload is only available on .net6.0, so cant use that here.
There was a problem hiding this comment.
Ah, sorry, didn't check that. The use (nint)(uint) for the cast.
Uh oh!
There was an error while loading. Please reload this page.
| v = -v; | ||
| } | ||
| Unsafe.Add(ref destinationRef, ZigZag.TransposingOrder[k]) = (short)v; |
There was a problem hiding this comment.
| Unsafe.Add(refdestinationRef,ZigZag.TransposingOrder[k])=(short)v; | |
| Unsafe.Add(refdestinationRef,(uint)ZigZag.TransposingOrder[k])=(short)v; |
| namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
| { | ||
| internal class ArithmeticStatistics |
There was a problem hiding this comment.
Can this be written as
usingSystem.Diagnostics;usingSystem.Runtime.CompilerServices;internalunsafestructArithmeticStatistics{privatefixedbytestatistics[256];publicArithmeticStatistics(booldc,intidentifier){this.IsDcStatistics=dc;this.Identifier=identifier;}publicboolIsDcStatistics{get;privateset;}publicintIdentifier{get;privateset;}publicrefbyteGetReference()=>refthis.statistics[0];publicrefbyteGetReference(intoffset){Debug.Assert(offset<256);returnrefthis.statistics[(uint)offset];}publicvoidReset(){Unsafe.InitBlockUnaligned(refthis.GetReference(),0x00,(uint)(this.IsDcStatistics?64:256));}}?
- avoids the additional allocation for each stats-object
- downside: size is fixed to 256, so potentially the list will be larger
- codegen is better than with the class especially for
GetReferenceandReset(which are the hottest methods here?)
| /// <summary> | ||
| /// Gets the component id. | ||
| /// </summary> | ||
| public byte Id { get; } |
There was a problem hiding this comment.
The existing interface members don't have the access modifier specified.
It's not strictly needed, but C# allows this (since C# 8 I think).
At least it should be consistent withing this file.
Uh oh!
There was an error while loading. Please reload this page.
| // Validate: width/height > 0 (they are upper-bounded by 2 byte max value so no need to check that) | ||
| // Validate: width/height > 0 (they are upper-bounded by 2 byte max value so no need to check that). | ||
| if (frameHeight == 0 || frameWidth == 0) |
There was a problem hiding this comment.
| if(frameHeight==0||frameWidth==0) | |
| if((frameHeight|frameWidth)==0) |
produces less code and avoids a branch.
Note: current JIT will do this optimizaiton for use, but older runtimes miss that optimization.
I know that I'm asking for too much but can we postpone this PR from merging for a bit? I'll push some fixes on your branch if you don't mind. EDIT: |
brianpopow
commented
Mar 25, 2022
@gfoidl thanks for your review, its always appreciated!
Its not a problem, there is no need to rush this PR.
I would not mind, if you push directly to this branch, your help is very welcome, but I dont think you have permission to do so. |
br3aker
commented
Mar 25, 2022
Code itself is very good, it's just those tiny little spots I'd want to fix but they do not block merging so it's okay. I will create a PR with general refactoring of the decoder part. |
Co-authored-by: Günther Foidl <gue@korporal.at>
# Conflicts: # src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
br3aker
left a comment
There was a problem hiding this comment.
Looks good overall but please revert component type to JpegComponent in JpegDecoderCore.
| /// <summary> | ||
| /// The arithmetic decoding tables. | ||
| /// </summary> | ||
| private List<ArithmeticDecodingTable> arithmeticDecodingTables; | ||
| /// <summary> | ||
| /// The restart interval. | ||
| /// </summary> | ||
| private int? resetInterval; |
There was a problem hiding this comment.
I don't like that this arithmetic coding specific property is leaked in the 'master' class, same goes for reset interval which should only be used inside scanDecoder but it's a broad question for some further refactoring - I'll work on it right after scaled decoding PR.
| /// Gets or sets the frame component collection. | ||
| /// </summary> | ||
| public JpegComponent[] Components { get; set; } | ||
| public IJpegComponent[] Components { get; set; } |
There was a problem hiding this comment.
As far as I remember, IJpegComponent interface is used in tests for libjpeg output comparison but I think we shouldn't use this interface everywhere - this leads to virtual calls for every component field getter .
br3aker
commented
May 1, 2022
@brianpopow hi! Anything blocking this PR from being merged? |
brianpopow
commented
May 1, 2022
@br3aker: I think this can be merged now. |
JimBobSquarePants
commented
May 1, 2022
Nope. Rock on |
Prerequisites
Description
This PR adds support for decoding jpeg's with arithmetic coding.