-
-
Notifications
You must be signed in to change notification settings - Fork 889
Jpeg downscaling decoding #2076
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
Merged
JimBobSquarePants
merged 34 commits into
SixLabors:main
from
br3aker:dp/jpeg-downscaling-decode
Jul 17, 2022
Merged
Changes from 10 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
36180c6
playground
11220a3
Replaced absurdly complicated math from JpegComponentPostProcessor
afbf44b
Infrastructure
017919a
First working prototype, not optimized
5cca314
Added playground benchmarks
a10be09
Resizing converter no longer depends on avx converter only
1ce994a
Unified spectral conversion for direct and downscaled routines
4e17b69
Added second stage to the resizing decoding
f4d5f1a
Chroma subsampling for downscaling decoder
282e593
Update playground
8192ff2
Initial processor implementation, code base for tests
52f507d
Separated scaled IDCT methods
6eceb6c
Moved quantization table initialization to component post processors
03407f1
4x4 implementation, tests
1050cf2
Fixed bug leading to gray images after decoding
9575a24
Fix compilation error
c57ca1b
Merge branch 'main' into dp/jpeg-downscaling-decode
12776f0
IDCT resizing modes
7057245
Code cleanup, removed invalid second pass logic, marked scaled decodi…
bfbfdfa
Added tests for out jpeg image size getter method
b943f80
Restored Program.cs
bb82e27
Merge branch 'main' into dp/jpeg-downscaling-decode
6747339
Docs & review fixes
1aff245
Merge branch 'dp/jpeg-downscaling-decode' of https://github.com/br3ak…
f011dcc
Unsafe.Add fix
3feb7f6
Merge branch 'main' of https://github.com/SixLabors/ImageSharp into d…
95c56b0
Small bug fixes, ready for merging
ed86426
Updated load-resize-save benchmark, deleted obsolete benchmarks
9f35b78
Merge branch 'main' into dp/jpeg-downscaling-decode
2896faf
Merge branch 'main' into dp/jpeg-downscaling-decode
brianpopow 3f16a68
Merge branch 'main' into dp/jpeg-downscaling-decode
9851315
Added resizing benchmark results
d0de191
Merge remote-tracking branch 'upstream/main' into dp/jpeg-downscaling…
JimBobSquarePants 7a9cf87
Fix headers
JimBobSquarePants File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
src/ImageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/ComponentProcessor.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using System; | ||
| using SixLabors.ImageSharp.Memory; | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
| { | ||
| internal abstract class ComponentProcessor : IDisposable | ||
| { | ||
| public ComponentProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, Size postProcessorBufferSize, IJpegComponent component, int blockSize) | ||
| { | ||
| this.Frame = frame; | ||
| this.Component = component; | ||
|
|
||
| this.BlockAreaSize = component.SubSamplingDivisors * blockSize; | ||
| this.ColorBuffer = memoryAllocator.Allocate2DOveraligned<float>( | ||
| postProcessorBufferSize.Width, | ||
| postProcessorBufferSize.Height, | ||
| this.BlockAreaSize.Height); | ||
| } | ||
|
|
||
| protected JpegFrame Frame { get; } | ||
|
|
||
| protected IJpegComponent Component { get; } | ||
|
|
||
| protected Buffer2D<float> ColorBuffer { get; } | ||
|
|
||
| protected Size BlockAreaSize { get; } | ||
|
|
||
| public abstract void CopyBlocksToColorBuffer(int spectralStep); | ||
|
|
||
| public void ClearSpectralBuffers() | ||
| { | ||
| Buffer2D<Block8x8> spectralBlocks = this.Component.SpectralBlocks; | ||
| for (int i = 0; i < spectralBlocks.Height; i++) | ||
| { | ||
| spectralBlocks.DangerousGetRowSpan(i).Clear(); | ||
| } | ||
| } | ||
|
|
||
| public Span<float> GetColorBufferRowSpan(int row) => | ||
| this.ColorBuffer.DangerousGetRowSpan(row); | ||
|
|
||
| public void Dispose() => this.ColorBuffer.Dispose(); | ||
| } | ||
| } |
68 changes: 68 additions & 0 deletions
68
...mageSharp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DirectComponentProcessor.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using System; | ||
| using SixLabors.ImageSharp.Memory; | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
| { | ||
| internal sealed class DirectComponentProcessor : ComponentProcessor | ||
| { | ||
| private readonly IRawJpegData rawJpeg; | ||
|
|
||
| public DirectComponentProcessor(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component) | ||
| : base(memoryAllocator, frame, postProcessorBufferSize, component, blockSize: 8) | ||
| => this.rawJpeg = rawJpeg; | ||
|
|
||
| public override void CopyBlocksToColorBuffer(int spectralStep) | ||
| { | ||
| Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks; | ||
|
|
||
| float maximumValue = this.Frame.MaxColorChannelValue; | ||
|
|
||
| int destAreaStride = this.ColorBuffer.Width; | ||
|
|
||
| int blocksRowsPerStep = this.Component.SamplingFactors.Height; | ||
|
|
||
| int yBlockStart = spectralStep * blocksRowsPerStep; | ||
|
|
||
| Size subSamplingDivisors = this.Component.SubSamplingDivisors; | ||
|
|
||
| Block8x8F dequantTable = this.rawJpeg.QuantizationTables[this.Component.QuantizationTableIndex]; | ||
| Block8x8F workspaceBlock = default; | ||
|
|
||
| for (int y = 0; y < blocksRowsPerStep; y++) | ||
| { | ||
| int yBuffer = y * this.BlockAreaSize.Height; | ||
|
|
||
| Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); | ||
| Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); | ||
|
|
||
| for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) | ||
| { | ||
| // Integer to float | ||
| workspaceBlock.LoadFrom(ref blockRow[xBlock]); | ||
|
|
||
| // Dequantize | ||
| workspaceBlock.MultiplyInPlace(ref dequantTable); | ||
|
|
||
| // Convert from spectral to color | ||
| FastFloatingPointDCT.TransformIDCT(ref workspaceBlock); | ||
|
|
||
| // To conform better to libjpeg we actually NEED TO loose precision here. | ||
| // This is because they store blocks as Int16 between all the operations. | ||
| // To be "more accurate", we need to emulate this by rounding! | ||
| workspaceBlock.NormalizeColorsAndRoundInPlace(maximumValue); | ||
|
|
||
| // Write to color buffer acording to sampling factors | ||
| int xColorBufferStart = xBlock * this.BlockAreaSize.Width; | ||
| workspaceBlock.ScaledCopyTo( | ||
| ref colorBufferRow[xColorBufferStart], | ||
| destAreaStride, | ||
| subSamplingDivisors.Width, | ||
| subSamplingDivisors.Height); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
92 changes: 92 additions & 0 deletions
92
...arp/Formats/Jpeg/Components/Decoder/ComponentProcessors/DownScalingComponentProcessor8.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using System; | ||
| using System.Runtime.CompilerServices; | ||
| using SixLabors.ImageSharp.Memory; | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder | ||
| { | ||
| internal sealed class DownScalingComponentProcessor8 : ComponentProcessor | ||
| { | ||
| private readonly float dcDequantizer; | ||
|
|
||
| public DownScalingComponentProcessor8(MemoryAllocator memoryAllocator, JpegFrame frame, IRawJpegData rawJpeg, Size postProcessorBufferSize, IJpegComponent component) | ||
| : base(memoryAllocator, frame, postProcessorBufferSize, component, 1) | ||
| => this.dcDequantizer = rawJpeg.QuantizationTables[component.QuantizationTableIndex][0]; | ||
|
|
||
| public override void CopyBlocksToColorBuffer(int spectralStep) | ||
| { | ||
| Buffer2D<Block8x8> spectralBuffer = this.Component.SpectralBlocks; | ||
|
|
||
| float maximumValue = this.Frame.MaxColorChannelValue; | ||
| float normalizationValue = MathF.Ceiling(maximumValue / 2); | ||
|
|
||
| int destAreaStride = this.ColorBuffer.Width; | ||
|
|
||
| int blocksRowsPerStep = this.Component.SamplingFactors.Height; | ||
| Size subSamplingDivisors = this.Component.SubSamplingDivisors; | ||
|
|
||
| int yBlockStart = spectralStep * blocksRowsPerStep; | ||
|
|
||
| for (int y = 0; y < blocksRowsPerStep; y++) | ||
| { | ||
| int yBuffer = y * this.BlockAreaSize.Height; | ||
|
|
||
| Span<float> colorBufferRow = this.ColorBuffer.DangerousGetRowSpan(yBuffer); | ||
| Span<Block8x8> blockRow = spectralBuffer.DangerousGetRowSpan(yBlockStart + y); | ||
|
|
||
| for (int xBlock = 0; xBlock < spectralBuffer.Width; xBlock++) | ||
| { | ||
| // get direct current term - averaged 8x8 pixel value | ||
| float dc = blockRow[xBlock][0]; | ||
|
|
||
| // dequantization | ||
| dc *= this.dcDequantizer; | ||
|
|
||
| // Normalize & round | ||
| dc = (float)Math.Round(Numerics.Clamp(dc + normalizationValue, 0, maximumValue)); | ||
|
|
||
| // Save to the intermediate buffer | ||
| int xColorBufferStart = xBlock * this.BlockAreaSize.Width; | ||
| ScaledCopyTo( | ||
| dc, | ||
| ref colorBufferRow[xColorBufferStart], | ||
| destAreaStride, | ||
| subSamplingDivisors.Width, | ||
| subSamplingDivisors.Height); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [MethodImpl(InliningOptions.ShortMethod)] | ||
| public static void ScaledCopyTo(float value, ref float destRef, int destStrideWidth, int horizontalScale, int verticalScale) | ||
| { | ||
| if (horizontalScale == 1 && verticalScale == 1) | ||
| { | ||
| Unsafe.Add(ref destRef, 0) = value; | ||
br3aker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return; | ||
| } | ||
|
|
||
| if (horizontalScale == 2 && verticalScale == 2) | ||
| { | ||
| Unsafe.Add(ref destRef, 0) = value; | ||
| Unsafe.Add(ref destRef, 1) = value; | ||
| Unsafe.Add(ref destRef, 0 + destStrideWidth) = value; | ||
| Unsafe.Add(ref destRef, 1 + destStrideWidth) = value; | ||
br3aker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return; | ||
| } | ||
|
|
||
| // TODO: Optimize: implement all cases with scale-specific, loopless code! | ||
| for (int y = 0; y < verticalScale; y++) | ||
| { | ||
| for (int x = 0; x < horizontalScale; x++) | ||
| { | ||
| Unsafe.Add(ref destRef, x) = value; | ||
br3aker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| destRef = ref Unsafe.Add(ref destRef, destStrideWidth); | ||
br3aker marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.