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

Add support for HEIF based images #2633

Open
wants to merge 190 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 81 commits
Commits
Show all changes
190 commits
Select commit Hold shift + click to select a range
0829e86
Scaffolding for HEIC file format
ynse01 Dec 25, 2023
b5ce29d
Fix build
ynse01 Dec 25, 2023
bc97554
Initial Metadata parsing
ynse01 Dec 26, 2023
235aa38
HEIC test images
ynse01 Dec 26, 2023
c0297a3
Adapt code to .NET 8
ynse01 Dec 26, 2023
96b92e8
4CC generated by template
ynse01 Dec 27, 2023
9ad76ce
Rename Heic4CharCode files
ynse01 Dec 27, 2023
c3ce980
Use single buffer per decoder instance
ynse01 Dec 27, 2023
ff9629a
Add another sample image
ynse01 Dec 27, 2023
028e8a5
Initial tests
ynse01 Dec 27, 2023
f892c04
Byte alignment fixes
ynse01 Dec 27, 2023
193fd94
Boundary checking before box allocation
ynse01 Dec 27, 2023
8366d72
Add HIF test image
ynse01 Dec 27, 2023
5f9db61
Add handler parsing
ynse01 Dec 27, 2023
d6dc2b1
Parse HIF metadata
ynse01 Dec 27, 2023
f90d4eb
More HIF support
ynse01 Dec 27, 2023
f234c8b
Decoding of thumbnail with JPEG
ynse01 Dec 27, 2023
e972551
Exposed used compression method in metadata
ynse01 Dec 28, 2023
183b873
Reference image for Heic Decoder Test
ynse01 Dec 28, 2023
0bfa0fe
Initial encoder
ynse01 Dec 28, 2023
b60ce90
Move to Memory namespace
ynse01 Dec 28, 2023
23388b2
Box size predictions
ynse01 Dec 28, 2023
3f4e74b
Fix Heic encoder test
ynse01 Dec 29, 2023
54db9c7
Box size improvements
ynse01 Dec 29, 2023
6372a9a
Add AVIF test image
ynse01 Dec 29, 2023
bb3dc4e
Merge branch 'heic-support' of https://github.com/ynse01/ImageSharp i…
ynse01 Dec 29, 2023
3fae594
Identify AVIF files
ynse01 Dec 29, 2023
046b28f
Tests for AutoExpandingMemory class
ynse01 Dec 29, 2023
9d99b1a
Move to Heif directory
ynse01 Dec 29, 2023
4645e40
Rename namespace and classes to Heif
ynse01 Dec 29, 2023
5ad3d67
Fix configuration test
ynse01 Dec 29, 2023
42ff8f9
Remove reference to x265
ynse01 Jan 5, 2024
934dd61
Include SVT reference
ynse01 Jan 5, 2024
ccbae05
Consistent use of name HEIF
ynse01 Jan 5, 2024
0301aed
Decode ICC profile into metadata
ynse01 Jan 19, 2024
5dedc94
Initial av1 bitstream parsing
ynse01 Jan 7, 2024
f934927
Read parameters
ynse01 Jan 8, 2024
e45a41b
Av1 bitstream tests
ynse01 Jan 12, 2024
7b961ff
Optimized WriteBoolean()
ynse01 Jan 12, 2024
961d55e
Skeleton
ynse01 Jan 19, 2024
790207f
Prepare quantization
ynse01 Jan 20, 2024
03eda75
Merge branch 'main' into heic-support
JimBobSquarePants Feb 7, 2024
43569db
Merge branch 'main' into heic-support
JimBobSquarePants Mar 14, 2024
db44498
Casing in Heif4CharCode
ynse01 Mar 14, 2024
73bdbd8
Make some classes internal
ynse01 Mar 14, 2024
de392d8
Read header from stack allocated buffer
ynse01 Mar 15, 2024
d146069
Check Stream.Read return value
ynse01 Mar 17, 2024
c847f5d
Fix build
ynse01 Mar 17, 2024
a39182d
Merge remote-tracking branch 'upstream/main' into avif-in-heif
ynse01 Apr 5, 2024
b27765f
Merge branch 'heic-support' into avif-in-heif
ynse01 Apr 5, 2024
80140c4
Inverse quantization
ynse01 Apr 6, 2024
2c8dd67
Merge branch 'main' into heic-support
ynse01 Apr 17, 2024
40e9d68
Fix reading of larger literals
ynse01 Apr 17, 2024
66803d0
Merge remote-tracking branch 'upstream/main' into avif-in-heif
ynse01 Apr 17, 2024
894bbf2
Update infra
ynse01 Apr 17, 2024
47dc774
Obu header decoding
ynse01 Apr 21, 2024
6aa74f2
Rename to Av1Decoder
ynse01 Apr 21, 2024
f2bfd5a
Tile decoding interface
ynse01 Apr 23, 2024
73f4dce
Merge branch 'avif-in-heif' into heic-support
ynse01 Apr 23, 2024
a1a1a5a
Fix build
ynse01 Apr 23, 2024
07138c4
Use Tile Decoder interface
ynse01 Apr 23, 2024
6863542
Merge branch 'main' into heic-support
JimBobSquarePants Apr 26, 2024
a1cb2dd
Introduce ObuWriter
ynse01 Apr 28, 2024
74677ed
Fix bitstream writer
ynse01 May 1, 2024
ef572ac
ObuHeader round trips
ynse01 May 3, 2024
95d9f09
Initial symbol parsing
ynse01 May 12, 2024
06b0c04
Fix literal reading and writing
ynse01 May 17, 2024
e34b831
First symbol coding roundtrip
ynse01 May 17, 2024
fec0413
Initial symbol decoding
ynse01 May 28, 2024
b26c0fa
PartitionType symbol round trips
ynse01 Jun 1, 2024
b4f9e31
Merge branch 'main' into heic-support
ynse01 Jun 1, 2024
1184968
Introduce Tile Decoder
ynse01 Jun 1, 2024
881199e
Introduce Av1PartitionInfo and fill it during decoding
ynse01 Jun 2, 2024
98bad4d
Superblock decoding
ynse01 Jun 3, 2024
c0fcb42
Namespace rename
ynse01 Jun 3, 2024
bfed4ee
Add spec reference
ynse01 Jun 3, 2024
a8539f2
Rename Av1Constants class
ynse01 Jun 3, 2024
671c89d
Extend readme
ynse01 Jun 3, 2024
19ca2ea
Introduce Frame buffer class
ynse01 Jun 5, 2024
e64d74d
Simplify IAv1TileDecoder
ynse01 Jun 8, 2024
60869e1
Bug fixes in OBU parsing
ynse01 Jun 9, 2024
d8e7078
Implement reading AV1 Codec Configuration Record
brianpopow Jun 23, 2024
84127fa
Fix mistake in switch case for parsing Heif4CharCode.Av1C
brianpopow Jun 23, 2024
cb4f9ee
Add comments where the methods can be found in the spec
brianpopow Jun 24, 2024
aa44d0c
Add av1 spec file
brianpopow Jun 24, 2024
9ee8d92
Merge branch 'main' into heic-support
ynse01 Jun 24, 2024
c918eca
Merge branch 'heic-support' of https://github.com/ynse01/ImageSharp i…
ynse01 Jun 24, 2024
0ffdf8d
Fix merge errors
ynse01 Jun 24, 2024
a674546
Fix code style violations
ynse01 Jun 24, 2024
215ca66
Fix bug in ObuFrameHeader parsing
ynse01 Jun 25, 2024
5d793a2
Implement ReadSegmentationParameters
brianpopow Jun 25, 2024
dd9f383
Fix escape < sign in XML comment
brianpopow Jun 25, 2024
1d288d3
Implement ReadFilmGrainFilterParameters()
brianpopow Jun 26, 2024
4d79d59
Fix stylecop warnings
brianpopow Jun 26, 2024
6aa609f
Strength test criteria
ynse01 Jun 26, 2024
136110f
Adjustments in ReadSequenceHeader() for case when image is not Reduce…
brianpopow Jun 28, 2024
2accf95
Implement 5.5.4. read Decoder model info
brianpopow Jun 29, 2024
41b980c
Implement missing parts in ReadSequenceHeader for case when ReducedSt…
brianpopow Jun 29, 2024
c4d34c5
Fix issue in ReadSequenceHeader() not reading OrderHintBits
brianpopow Jun 29, 2024
a0fdf02
Implement partition context logic
ynse01 Jun 29, 2024
3556921
Implement ReadTimingInfo, it is needed in ReadUncompressedFrameHeader…
brianpopow Jun 30, 2024
0cf2726
Tiling initialization fixes
ynse01 Jun 30, 2024
5792d05
Annotate Av1TileDecoder methods with spec sections
brianpopow Jul 2, 2024
3382518
Fix build
ynse01 Jul 2, 2024
aaac78a
When TemporalDelimiter header is encountered, seenFrameHeader flag wi…
brianpopow Jul 3, 2024
e60b39f
Implement Transform parsing
ynse01 Jul 3, 2024
9fb5826
Smoke test for tiling
ynse01 Jul 3, 2024
13173a3
Implement GetPlaneResidualSize()
brianpopow Jul 4, 2024
3d84a51
Suppress warning about SA1500: Braces for multi-line statements
brianpopow Jul 4, 2024
6c4e213
Implement Residual
ynse01 Jul 4, 2024
1ff0150
Implement TransformBlock
ynse01 Jul 4, 2024
4e53f7c
Add some code comments
ynse01 Jul 5, 2024
d01cb8e
Merge branch 'main' into heic-support
ynse01 Jul 5, 2024
38c23a5
Disable failing test for in-progress code
ynse01 Jul 5, 2024
30f8884
Implement Coefficient syntax
ynse01 Jul 5, 2024
c8a6203
Implement ScanOrder scan constants
ynse01 Jul 6, 2024
1d83ac3
Implement remaining distributions
ynse01 Jul 6, 2024
23d533a
Some bug fixes
ynse01 Jul 6, 2024
a48e271
Fully implement Heif Item Location
ynse01 Jul 8, 2024
c771350
Prune ITileDecoder interface
ynse01 Jul 9, 2024
be134e8
Various tiling bug fixes
ynse01 Jul 9, 2024
30d7c09
More bug fixes
ynse01 Jul 11, 2024
a1f0f9e
Merge remote-tracking branch 'upstream/main' into heic-support
ynse01 Jul 11, 2024
a4ba77b
Implement mode info indexing in frame buffer
ynse01 Jul 12, 2024
471e3d3
Fix issue in ReadLoopRestorationParameters(): LoopRestorationParamete…
brianpopow Jul 12, 2024
c445729
Fix issue in coefficient parsing
ynse01 Jul 12, 2024
d527d0e
Rename ChannelCount property to PlaneCount
ynse01 Jul 13, 2024
68be923
PlaneCount calculation inside ObuColorConfig
ynse01 Jul 13, 2024
a6a32d4
TransformInfo retrieval changes
ynse01 Jul 13, 2024
d762207
Transform Unit Count variable rename
ynse01 Jul 13, 2024
cb736d7
Continue implement case !IsReducedStillPictureHeader in ReadUncompres…
brianpopow Jul 14, 2024
541ab35
Fix issue in Transform Unit Count
ynse01 Jul 15, 2024
441949f
Simplify code by using constants
ynse01 Jul 15, 2024
5d41465
Renaming Superblock variables
ynse01 Jul 16, 2024
4e1aca9
More Superblock renaming
ynse01 Jul 19, 2024
3976aa2
Merge branch 'main' into heic-support
ynse01 Jul 19, 2024
1812983
Improvements to pretty print routines for OBU headers
ynse01 Jul 19, 2024
62728b6
Add test bitreader test ReadLiteral32BitsWithMsbSet()
brianpopow Jul 20, 2024
38a6f43
Add suggestion howto change ReadLiteral()
brianpopow Jul 20, 2024
72c4263
Add tests for ReadSignedFromUnsigned and ReadLittleEndian
brianpopow Jul 20, 2024
0313bdf
Replace Av1BitStreamReader implementation
ynse01 Jul 20, 2024
cb3f51b
Add tests for ReadUnsignedVariableLength()
brianpopow Jul 20, 2024
64f4241
Additional tests for ReadLittleEndianBytes128()
brianpopow Jul 20, 2024
e8b7682
Also assert bit position
brianpopow Jul 20, 2024
c524346
OBU reader bug fixes
ynse01 Jul 20, 2024
e45bf57
Combine Delta parameters implementation
ynse01 Jul 21, 2024
6e10cc9
Few Obu unit tests taken over from libgav1
ynse01 Jul 21, 2024
4c019f5
Bug fixes in TileDecoder, inspired by libgav1
ynse01 Jul 21, 2024
22b7c72
Implement Read Chroma from Luma syntax
ynse01 Jul 21, 2024
4323205
Misc tile parsing fixes
ynse01 Jul 21, 2024
984d0ac
Additional unit tests for OBU bitstream
ynse01 Jul 23, 2024
e4743ce
Fix build
ynse01 Jul 23, 2024
26d8c22
Scan order unit test and fixes
ynse01 Jul 25, 2024
73718f1
Unit tests for block and transform sizes
ynse01 Jul 25, 2024
08cbc5a
Unit test for Partition Type
ynse01 Jul 26, 2024
5d27f30
Add extremely small AVIF test image
ynse01 Jul 26, 2024
fa2e1b0
Add tests for Orange4x4 image
ynse01 Jul 26, 2024
9409a12
Merge branch 'main' into heic-support
ynse01 Jul 26, 2024
96329f4
Rename TileDecoder into TileReader
ynse01 Jul 27, 2024
056814f
Namespace update
ynse01 Jul 27, 2024
ab2ae29
ObuWriter improvements
ynse01 Aug 4, 2024
8b28187
Merge remote-tracking branch 'upstream/main' into heic-support
ynse01 Aug 4, 2024
b8322fc
Merge in required changes from upstream
ynse01 Aug 4, 2024
23bf5de
Obu reading and writing improvements
ynse01 Aug 4, 2024
729f351
Obu bitstream writer writes to Span iso Stream
ynse01 Aug 4, 2024
2bac577
Fix AutoExpandingMemoryTest
ynse01 Aug 4, 2024
0b1639b
Sync method arguments
ynse01 Aug 5, 2024
1385266
Binary identical OBU read write round trip
ynse01 Aug 5, 2024
1fe733a
Merge remote-tracking branch 'upstream/main' into heic-support
ynse01 Aug 6, 2024
0ff8911
Introduce Av1FrameBuffer class and renaming others
ynse01 Aug 9, 2024
4f4ace4
Skeleton code for pixel pipeline
ynse01 Aug 19, 2024
d93b89c
Merge remote-tracking branch 'upstream/main' into heic-support
ynse01 Aug 19, 2024
2b60337
Rename according to naming scheme
ynse01 Aug 19, 2024
dfff87e
Implement interface in HeifMetadata
ynse01 Aug 19, 2024
d748a0d
Introduce DC predictors
ynse01 Aug 20, 2024
c9baaf0
Prediction decoding
ynse01 Aug 24, 2024
aa1b299
Implement Block Decoder
ynse01 Aug 24, 2024
ad57a99
Skeleton code for Transform
ynse01 Sep 5, 2024
fd76bd8
Disable test which has no implementation yet
ynse01 Sep 5, 2024
97052eb
Implement 4x4 forward DCT transform
ynse01 Sep 30, 2024
242dded
Refactor forward transformers
ynse01 Oct 5, 2024
639186b
Test accuracy of 1d forward transformer
ynse01 Oct 5, 2024
f1908c8
Implement all 1 dimensional forward transforms
ynse01 Oct 6, 2024
c19d687
Bounds check for forward transforms
ynse01 Oct 6, 2024
47c2416
Some more forward transform tests
ynse01 Oct 11, 2024
101e841
Partial accuracy test for forward transform
ynse01 Oct 15, 2024
796ea84
Implementation of some inverse transformers
ynse01 Oct 22, 2024
34e8fc2
2-dimensional inverse transform implementation
ynse01 Oct 23, 2024
2fa5ca1
Tests for inverse transform
ynse01 Nov 2, 2024
8f227bf
Merge branch 'main' into heic-support
ynse01 Nov 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,6 @@
*.ico filter=lfs diff=lfs merge=lfs -text
*.cur filter=lfs diff=lfs merge=lfs -text
*.ani filter=lfs diff=lfs merge=lfs -text
*.heic filter=lfs diff=lfs merge=lfs -text
*.hif filter=lfs diff=lfs merge=lfs -text
*.avif filter=lfs diff=lfs merge=lfs -text
17 changes: 15 additions & 2 deletions ImageSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{815C0625-CD3
ProjectSection(SolutionItems) = preProject
src\Directory.Build.props = src\Directory.Build.props
src\Directory.Build.targets = src\Directory.Build.targets
src\README.md = src\README.md
src\ImageSharp.ruleset = src\ImageSharp.ruleset
src\README.md = src\README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp", "src\ImageSharp\ImageSharp.csproj", "{2AA31A1F-142C-43F4-8687-09ABCA4B3A26}"
Expand Down Expand Up @@ -215,6 +215,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B68
ProjectSection(SolutionItems) = preProject
tests\Images\Input\Jpg\issues\issue-1076-invalid-subsampling.jpg = tests\Images\Input\Jpg\issues\issue-1076-invalid-subsampling.jpg
tests\Images\Input\Jpg\issues\issue-1221-identify-multi-frame.jpg = tests\Images\Input\Jpg\issues\issue-1221-identify-multi-frame.jpg
tests\Images\Input\Jpg\issues\issue-2067-comment.jpg = tests\Images\Input\Jpg\issues\issue-2067-comment.jpg
tests\Images\Input\Jpg\issues\issue1006-incorrect-resize.jpg = tests\Images\Input\Jpg\issues\issue1006-incorrect-resize.jpg
tests\Images\Input\Jpg\issues\issue1049-exif-resize.jpg = tests\Images\Input\Jpg\issues\issue1049-exif-resize.jpg
tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg = tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg
Expand All @@ -238,7 +239,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B68
tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg = tests\Images\Input\Jpg\issues\issue750-exif-tranform.jpg
tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg = tests\Images\Input\Jpg\issues\Issue845-Incorrect-Quality99.jpg
tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg = tests\Images\Input\Jpg\issues\issue855-incorrect-colorspace.jpg
tests\Images\Input\Jpg\issues\issue-2067-comment.jpg = tests\Images\Input\Jpg\issues\issue-2067-comment.jpg
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fuzz", "fuzz", "{516A3532-6AC2-417B-AD79-9BD5D0D378A0}"
Expand Down Expand Up @@ -661,6 +661,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Qoi", "Qoi", "{E801B508-493
tests\Images\Input\Qoi\wikipedia_008.qoi = tests\Images\Input\Qoi\wikipedia_008.qoi
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Heif", "Heif", "{BA5D603A-C84C-43E5-B300-8BB886B02936}"
ProjectSection(SolutionItems) = preProject
tests\Images\Input\Heif\dwsample-heic-640.heic = tests\Images\Input\Heif\dwsample-heic-640.heic
tests\Images\Input\Heif\image1.heic = tests\Images\Input\Heif\image1.heic
tests\Images\Input\Heif\image2.heic = tests\Images\Input\Heif\image2.heic
tests\Images\Input\Heif\image3.heic = tests\Images\Input\Heif\image3.heic
tests\Images\Input\Heif\image4.heic = tests\Images\Input\Heif\image4.heic
tests\Images\Input\Heif\IMG-20230508-0053.hif = tests\Images\Input\Heif\IMG-20230508-0053.hif
tests\Images\Input\Heif\Irvine_CA.avif = tests\Images\Input\Heif\Irvine_CA.avif
tests\Images\Input\Heif\jpeg444_xnconvert.avif = tests\Images\Input\Heif\jpeg444_xnconvert.avif
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -714,6 +726,7 @@ Global
{670DD46C-82E9-499A-B2D2-00A802ED0141} = {E1C42A6F-913B-4A7B-B1A8-2BB62843B254}
{5DFC394F-136F-4B76-9BCA-3BA786515EFC} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
{E801B508-4935-41CD-BA85-CF11BFF55A45} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
{BA5D603A-C84C-43E5-B300-8BB886B02936} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795}
Expand Down
2 changes: 1 addition & 1 deletion shared-infrastructure
5 changes: 4 additions & 1 deletion src/ImageSharp/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Pbm;
using SixLabors.ImageSharp.Formats.Png;
Expand Down Expand Up @@ -211,6 +212,7 @@ public void Configure(IImageFormatConfigurationModule configuration)
/// <see cref="TiffConfigurationModule"/>.
/// <see cref="WebpConfigurationModule"/>.
/// <see cref="QoiConfigurationModule"/>.
/// <see cref="HeifConfigurationModule"/>.
/// </summary>
/// <returns>The default configuration of <see cref="Configuration"/>.</returns>
internal static Configuration CreateDefaultInstance() => new(
Expand All @@ -222,5 +224,6 @@ public void Configure(IImageFormatConfigurationModule configuration)
new TgaConfigurationModule(),
new TiffConfigurationModule(),
new WebpConfigurationModule(),
new QoiConfigurationModule());
new QoiConfigurationModule(),
new HeifConfigurationModule());
}
11 changes: 11 additions & 0 deletions src/ImageSharp/Formats/Heif/Av1/Av1BitDepth.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Heif.Av1;

internal enum Av1BitDepth : int
{
EightBit = 0,
TenBit = 1,
TwelveBit = 2,
}
185 changes: 185 additions & 0 deletions src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace SixLabors.ImageSharp.Formats.Heif.Av1;

internal ref struct Av1BitStreamReader
{
private const int WordSize = 32;

private readonly Span<uint> data;
private uint currentWord;
private uint nextWord;
private int wordPosition = 0;
private int bitOffset = 0;

public Av1BitStreamReader(Span<byte> data)
{
this.data = MemoryMarshal.Cast<byte, uint>(data);
this.wordPosition = -1;
this.AdvanceToNextWord();
this.AdvanceToNextWord();
}

public readonly int BitPosition => ((this.wordPosition - 1) * WordSize) + this.bitOffset;

/// <summary>
/// Gets the number of bytes in the readers buffer.
/// </summary>
public readonly int Length => this.data.Length;

public void Reset()
{
this.wordPosition = 0;
this.bitOffset = 0;
}

public void Skip(int bitCount)
{
this.bitOffset += bitCount;
while (this.bitOffset >= WordSize)
{
this.bitOffset -= WordSize;
this.wordPosition++;
}
}

public uint ReadLiteral(int bitCount)
{
DebugGuard.MustBeBetweenOrEqualTo(bitCount, 0, 32, nameof(bitCount));

uint bits = (this.currentWord << this.bitOffset) >> (WordSize - bitCount);
this.bitOffset += bitCount;
if (this.bitOffset > WordSize)
{
int overshoot = WordSize + WordSize - this.bitOffset;
if (overshoot < 32)
{
bits |= this.nextWord >> overshoot;
}
}

if (this.bitOffset >= WordSize)
{
this.AdvanceToNextWord();
this.bitOffset -= WordSize;
}

return bits;
}

internal bool ReadBoolean() => this.ReadLiteral(1) > 0;

public ulong ReadLittleEndianBytes128(out int length)
{
// See section 4.10.5 of the AV1-Specification
DebugGuard.IsTrue((this.bitOffset & 0x07) == 0, $"Reading of Little Endian 128 value only allowed on byte alignment (offset {this.BitPosition}).");

ulong value = 0;
length = 0;
for (int i = 0; i < 56; i += 7)
{
uint leb128Byte = this.ReadLiteral(8);
value |= (leb128Byte & 0x7FUL) << i;
length++;
if ((leb128Byte & 0x80U) == 0)
{
break;
}
}

return value;
}

public uint ReadUnsignedVariableLength()
{
// See section 4.10.3 of the AV1-Specification
int leadingZerosCount = 0;
while (leadingZerosCount < 32 && this.ReadLiteral(1) == 0U)
{
leadingZerosCount++;
}

if (leadingZerosCount == 32)
{
return uint.MaxValue;
}

uint basis = (1U << leadingZerosCount) - 1U;
uint value = this.ReadLiteral(leadingZerosCount);
return basis + value;
}

public uint ReadNonSymmetric(uint n)
{
// See section 4.10.7 of the AV1-Specification
if (n <= 1)
{
return 0;
}

int w = (int)(Av1Math.FloorLog2(n) + 1);
uint m = (uint)((1 << w) - n);
uint v = this.ReadLiteral(w - 1);
if (v < m)
{
return v;
}

return (v << 1) - m + this.ReadLiteral(1);
}

public int ReadSignedFromUnsigned(int n)
{
// See section 4.10.6 of the AV1-Specification
int signedValue;
uint value = this.ReadLiteral(n);
uint signMask = 1U << (n - 1);
if ((value & signMask) == signMask)
{
// Prevent overflow by casting to long;
signedValue = (int)((long)value - (signMask << 1));
}
else
{
signedValue = (int)value;
}

return signedValue;
}

public uint ReadLittleEndian(int n)
{
// See section 4.10.4 of the AV1-Specification
DebugGuard.IsTrue((this.bitOffset & (WordSize - 1)) == 0, "Reading of Little Endian value only allowed on byte alignment");

uint t = 0;
for (int i = 0; i < 8 * n; i += 8)
{
t += this.ReadLiteral(8) << i;
}

return t;
}

public void AdvanceToNextWord()
{
this.currentWord = this.nextWord;
this.wordPosition++;
uint temp = this.data[this.wordPosition];
this.nextWord = (temp << 24) | ((temp & 0x0000ff00U) << 8) | ((temp & 0x00ff0000U) >> 8) | (temp >> 24);
}

public Span<byte> GetSymbolReader(int tileDataSize)
{
DebugGuard.IsTrue((this.bitOffset & (WordSize - 1)) == 0, "Symbol reading needs to start on byte boundary.");

// TODO: Pass exact byte iso Word start.
Span<uint> span = this.data.Slice(this.bitOffset >> WordSize, tileDataSize);
this.bitOffset += tileDataSize << 8;
return MemoryMarshal.Cast<uint, byte>(span);
}
}
111 changes: 111 additions & 0 deletions src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Heif.Av1;

internal ref struct Av1BitStreamWriter(Stream stream)
{
private const int WordSize = 8;
private readonly Stream stream = stream;
private byte buffer = 0;
private int bitOffset = 0;

public readonly int BitPosition => (int)(this.stream.Position * WordSize) + this.bitOffset;

public readonly int Length => (int)this.stream.Length;

public void Skip(int bitCount)
{
this.bitOffset += bitCount;
while (this.bitOffset >= WordSize)
{
this.bitOffset -= WordSize;
this.stream.WriteByte(this.buffer);
this.buffer = 0;
}
}

public void Flush()
{
this.stream.WriteByte(this.buffer);
this.bitOffset = 0;
}

public void WriteLiteral(uint value, int bitCount)
{
for (int bit = bitCount - 1; bit >= 0; bit--)
{
this.WriteBit((byte)((value >> bit) & 0x1));
}
}

internal void WriteBoolean(bool value)
{
byte boolByte = value ? (byte)1 : (byte)0;
this.WriteBit(boolByte);
}

public void WriteSignedFromUnsigned(int signedValue, int n)
{
// See section 4.10.6 of the AV1-Specification
ulong value = (ulong)signedValue;
if (signedValue < 0)
{
value += 1UL << n;
}

this.WriteLiteral((uint)value, n);
}

public void WriteLittleEndianBytes128(uint value)
{
if (value < 128)
{
this.WriteLiteral(value, 8);
}
else if (value < 0x8000U)
{
this.WriteLiteral((value & 0x7FU) | 0x80U, 8);
this.WriteLiteral(value >> 7, 8);
}
else
{
throw new NotImplementedException("No such large values yet.");
}
}

internal void WriteNonSymmetric(uint value, uint numberOfSymbols)
{
// See section 4.10.7 of the AV1-Specification
if (numberOfSymbols <= 1)
{
return;
}

int w = (int)(Av1Math.FloorLog2(numberOfSymbols) + 1);
uint m = (uint)((1 << w) - numberOfSymbols);
if (value < m)
{
this.WriteLiteral(value, w - 1);
}
else
{
uint extraBit = ((value + m) >> 1) - value;
uint k = (value + m - extraBit) >> 1;
this.WriteLiteral(k, w - 1);
this.WriteLiteral(extraBit, 1);
}
}

private void WriteBit(byte value)
{
this.buffer = (byte)(((value << (7 - this.bitOffset)) & 0xff) | this.buffer);
this.bitOffset++;
if (this.bitOffset == WordSize)
{
this.stream.WriteByte(this.buffer);
this.buffer = 0;
this.bitOffset = 0;
}
}
}
Loading