1
+ // Copyright (c) Six Labors and contributors.
2
+ // Licensed under the Apache License, Version 2.0.
3
+
4
+ using System . Runtime . CompilerServices ;
5
+ using SixLabors . ImageSharp . IO ;
6
+
7
+ namespace SixLabors . ImageSharp . Formats . Jpeg . Components . Decoder
8
+ {
9
+ /// <summary>
10
+ /// Used to buffer and track the bits read from the Huffman entropy encoded data.
11
+ /// </summary>
12
+ internal struct HuffmanScanBuffer
13
+ {
14
+ private readonly DoubleBufferedStreamReader stream ;
15
+
16
+ // The entropy encoded code buffer.
17
+ private ulong data ;
18
+
19
+ // The number of valid bits left to read in the buffer.
20
+ private int remain ;
21
+
22
+ // Whether there is more data to pull from the stream for the current mcu.
23
+ private bool noMore ;
24
+
25
+ public HuffmanScanBuffer ( DoubleBufferedStreamReader stream )
26
+ {
27
+ this . stream = stream ;
28
+ this . data = 0ul ;
29
+ this . remain = 0 ;
30
+ this . Marker = JpegConstants . Markers . XFF ;
31
+ this . MarkerPosition = 0 ;
32
+ this . BadMarker = false ;
33
+ this . noMore = false ;
34
+ this . Eof = false ;
35
+ }
36
+
37
+ /// <summary>
38
+ /// Gets or sets the current, if any, marker in the input stream.
39
+ /// </summary>
40
+ public byte Marker { get ; set ; }
41
+
42
+ /// <summary>
43
+ /// Gets or sets the opening position of an identified marker.
44
+ /// </summary>
45
+ public long MarkerPosition { get ; set ; }
46
+
47
+ /// <summary>
48
+ /// Gets or sets a value indicating whether we have a bad marker, I.E. One that is not between RST0 and RST7
49
+ /// </summary>
50
+ public bool BadMarker { get ; set ; }
51
+
52
+ /// <summary>
53
+ /// Gets or sets a value indicating whether we have prematurely reached the end of the file.
54
+ /// </summary>
55
+ public bool Eof { get ; set ; }
56
+
57
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
58
+ public void CheckBits ( )
59
+ {
60
+ if ( this . remain < 16 )
61
+ {
62
+ this . FillBuffer ( ) ;
63
+ }
64
+ }
65
+
66
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
67
+ public void Reset ( )
68
+ {
69
+ this . data = 0ul ;
70
+ this . remain = 0 ;
71
+ this . Marker = JpegConstants . Markers . XFF ;
72
+ this . MarkerPosition = 0 ;
73
+ this . BadMarker = false ;
74
+ this . noMore = false ;
75
+ this . Eof = false ;
76
+ }
77
+
78
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
79
+ public bool HasRestart ( )
80
+ {
81
+ byte m = this . Marker ;
82
+ return m >= JpegConstants . Markers . RST0 && m <= JpegConstants . Markers . RST7 ;
83
+ }
84
+
85
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
86
+ public void FillBuffer ( )
87
+ {
88
+ // Attempt to load at least the minimum number of required bits into the buffer.
89
+ // We fail to do so only if we hit a marker or reach the end of the input stream.
90
+ this . remain += 48 ;
91
+ this . data = ( this . data << 48 ) | this . GetBytes ( ) ;
92
+ }
93
+
94
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
95
+ public unsafe int DecodeHuffman ( ref HuffmanTable h )
96
+ {
97
+ this . CheckBits ( ) ;
98
+ int v = this . PeekBits ( JpegConstants . Huffman . LookupBits ) ;
99
+ int symbol = h . LookaheadValue [ v ] ;
100
+ int size = h . LookaheadSize [ v ] ;
101
+
102
+ if ( size == JpegConstants . Huffman . SlowBits )
103
+ {
104
+ ulong x = this . data << ( JpegConstants . Huffman . RegisterSize - this . remain ) ;
105
+ while ( x > h . MaxCode [ size ] )
106
+ {
107
+ size ++ ;
108
+ }
109
+
110
+ v = ( int ) ( x >> ( JpegConstants . Huffman . RegisterSize - size ) ) ;
111
+ symbol = h . Values [ h . ValOffset [ size ] + v ] ;
112
+ }
113
+
114
+ this . remain -= size ;
115
+
116
+ return symbol ;
117
+ }
118
+
119
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
120
+ public int Receive ( int nbits )
121
+ {
122
+ this . CheckBits ( ) ;
123
+ return Extend ( this . GetBits ( nbits ) , nbits ) ;
124
+ }
125
+
126
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
127
+ public int GetBits ( int nbits ) => ( int ) ExtractBits ( this . data , this . remain -= nbits , nbits ) ;
128
+
129
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
130
+ public int PeekBits ( int nbits ) => ( int ) ExtractBits ( this . data , this . remain - nbits , nbits ) ;
131
+
132
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
133
+ private static ulong ExtractBits ( ulong value , int offset , int size ) => ( value >> offset ) & ( ulong ) ( ( 1 << size ) - 1 ) ;
134
+
135
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
136
+ private static int Extend ( int v , int nbits ) => v - ( ( ( ( v + v ) >> nbits ) - 1 ) & ( ( 1 << nbits ) - 1 ) ) ;
137
+
138
+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
139
+ private ulong GetBytes ( )
140
+ {
141
+ ulong temp = 0 ;
142
+ for ( int i = 0 ; i < 6 ; i ++ )
143
+ {
144
+ int b = this . noMore ? 0 : this . stream . ReadByte ( ) ;
145
+
146
+ if ( b == - 1 )
147
+ {
148
+ // We've encountered the end of the file stream which means there's no EOI marker in the image
149
+ // or the SOS marker has the wrong dimensions set.
150
+ this . Eof = true ;
151
+ b = 0 ;
152
+ }
153
+
154
+ // Found a marker.
155
+ if ( b == JpegConstants . Markers . XFF )
156
+ {
157
+ this . MarkerPosition = this . stream . Position - 1 ;
158
+ int c = this . stream . ReadByte ( ) ;
159
+ while ( c == JpegConstants . Markers . XFF )
160
+ {
161
+ c = this . stream . ReadByte ( ) ;
162
+
163
+ if ( c == - 1 )
164
+ {
165
+ this . Eof = true ;
166
+ c = 0 ;
167
+ break ;
168
+ }
169
+ }
170
+
171
+ if ( c != 0 )
172
+ {
173
+ this . Marker = ( byte ) c ;
174
+ this . noMore = true ;
175
+ if ( ! this . HasRestart ( ) )
176
+ {
177
+ this . BadMarker = true ;
178
+ }
179
+ }
180
+ }
181
+
182
+ temp = ( temp << 8 ) | ( ulong ) ( long ) b ;
183
+ }
184
+
185
+ return temp ;
186
+ }
187
+ }
188
+ }
0 commit comments