-
-
Notifications
You must be signed in to change notification settings - Fork 862
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
Add support for decoding jpeg's with arithmetic coding #2073
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I always enjoy reading such fine code.
At the same time I'm sorry for so much comments.
return; | ||
} | ||
|
||
int blockCol = (mcuCol * h) + x; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ref Unsafe.Add(ref blockRef, blockCol), | |
ref Unsafe.Add(ref blockRef, (uint)blockCol), |
to avoid the sign-extending move.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gfoidl I dont understand the suggestion: How can I use uint
here, when Unsafe.Add
expects int
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
ref Unsafe.Add(ref blockRef, blockCol), | |
ref Unsafe.Add(ref blockRef, (nuint)(uint)blockCol), |
but as the compiler does the second conversion for us, I've shortened it to
ref Unsafe.Add(ref blockRef, blockCol), | |
ref Unsafe.Add(ref blockRef, (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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry, didn't check that. The use (nint)(uint)
for the cast.
v = -v; | ||
} | ||
|
||
Unsafe.Add(ref destinationRef, ZigZag.TransposingOrder[k]) = (short)v; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unsafe.Add(ref destinationRef, ZigZag.TransposingOrder[k]) = (short)v; | |
Unsafe.Add(ref destinationRef, (uint)ZigZag.TransposingOrder[k]) = (short)v; |
|
||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
{ | ||
internal class ArithmeticStatistics |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be written as
using System.Diagnostics;
using System.Runtime.CompilerServices;
internal unsafe struct ArithmeticStatistics
{
private fixed byte statistics[256];
public ArithmeticStatistics(bool dc, int identifier)
{
this.IsDcStatistics = dc;
this.Identifier = identifier;
}
public bool IsDcStatistics { get; private set; }
public int Identifier { get; private set; }
public ref byte GetReference() => ref this.statistics[0];
public ref byte GetReference(int offset)
{
Debug.Assert(offset < 256);
return ref this.statistics[(uint)offset];
}
public void Reset()
{
Unsafe.InitBlockUnaligned(ref this.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
GetReference
andReset
(which are the hottest methods here?)
/// <summary> | ||
/// Gets the component id. | ||
/// </summary> | ||
public byte Id { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
@@ -1055,18 +1127,18 @@ private void ProcessStartOfFrameMarker(BufferedReadStream stream, int remaining, | |||
// 2 byte: Width | |||
int frameWidth = (this.temp[3] << 8) | this.temp[4]; | |||
|
|||
// 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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: |
@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. |
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>
…ated in LoadTables
# Conflicts: # src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
public JpegComponent[] Components { get; set; } | ||
public IJpegComponent[] Components { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 .
@brianpopow hi! Anything blocking this PR from being merged? |
@br3aker: I think this can be merged now. |
Nope. Rock on |
Prerequisites
Description
This PR adds support for decoding jpeg's with arithmetic coding.