1+ using System . Buffers ;
12using System . Numerics ;
23using System . Runtime . CompilerServices ;
34using BitConverter = Lagrange . Core . Utility . Binary . BitConverter ;
@@ -6,59 +7,192 @@ namespace Lagrange.Core.Utility;
67
78internal static class ImageResolver
89{
9- public static ImageFormat Resolve ( byte [ ] image , out Vector2 size )
10+ // GIF
11+ private static readonly byte [ ] GIF87A_0_6 = new byte [ ] { 0x47 , 0x49 , 0x46 , 0x38 , 0x37 , 0x61 } ;
12+ private static readonly byte [ ] GIF89A_0_6 = new byte [ ] { 0x47 , 0x49 , 0x46 , 0x38 , 0x39 , 0x61 } ;
13+ // JPEG
14+ private static readonly byte [ ] JPEG_0_2 = new byte [ ] { 0xFF , 0xD8 } ;
15+ // PNG
16+ private static readonly byte [ ] PNG_0_8 = new byte [ ] { 0x89 , 0x50 , 0x4E , 0x47 , 0x0D , 0x0A , 0x1A , 0x0A } ;
17+ // WEBP
18+ private static readonly byte [ ] RIFF_0_4 = new byte [ ] { 0x52 , 0x49 , 0x46 , 0x46 } ;
19+ private static readonly byte [ ] WEBP_8_12 = new byte [ ] { 0x57 , 0x45 , 0x42 , 0x50 } ;
20+ private static readonly byte [ ] VP8_12_16 = new byte [ ] { 0x56 , 0x50 , 0x38 , 0x20 } ;
21+ private static readonly byte [ ] VP8L_12_16 = new byte [ ] { 0x56 , 0x50 , 0x38 , 0x4C } ;
22+ private static readonly byte [ ] VP8X_12_16 = new byte [ ] { 0x56 , 0x50 , 0x38 , 0x58 } ;
23+ // BMP
24+ private static readonly byte [ ] BMP_0_2 = new byte [ ] { 0x42 , 0x4D } ;
25+ // TIFF
26+ private static readonly byte [ ] LLTIFF_0_2 = new byte [ ] { 0x49 , 0x49 } ;
27+ private static readonly byte [ ] MMTIFF_0_2 = new byte [ ] { 0x4D , 0x4D } ;
28+
29+ public static ImageFormat Resolve ( Stream image , out Vector2 size )
1030 {
11- ReadOnlySpan < byte > readOnlySpan = image . AsSpan ( ) ;
12- if ( readOnlySpan [ ..6 ] . SequenceEqual ( new byte [ ] { 0x47 , 0x49 , 0x46 , 0x38 , 0x39 , 0x61 } ) ||
13- readOnlySpan [ ..6 ] . SequenceEqual ( new byte [ ] { 0x47 , 0x49 , 0x46 , 0x38 , 0x37 , 0x61 } ) ) // GIF89a / GIF87a
14- {
15- size = new Vector2 ( BitConverter . ToUInt16 ( readOnlySpan [ 6 ..8 ] ) , BitConverter . ToUInt16 ( readOnlySpan [ 8 ..10 ] ) ) ;
16- return ImageFormat . Gif ;
17- }
31+ Span < byte > buffer = stackalloc byte [ 1024 ] ;
32+ int length = image . Read ( buffer ) ;
1833
19- if ( readOnlySpan [ ..2 ] . SequenceEqual ( new byte [ ] { 0xFF , 0xD8 } ) ) // JPEG
20- {
21- size = Vector2 . Zero ;
22- for ( int i = 2 ; i < readOnlySpan . Length - 10 ; i ++ )
34+ { // JPEG
35+ if ( buffer [ ..2 ] . SequenceEqual ( JPEG_0_2 ) )
2336 {
24- if ( ( Unsafe . ReadUnaligned < ushort > ( ref image [ i ] ) & 0xFCFF ) == 0xC0FF ) // SOF0 ~ SOF3
37+ int read = 2 ;
38+ while ( true )
2539 {
26- size = new Vector2 ( BitConverter . ToUInt16 ( readOnlySpan [ ( i + 7 ) ..( i + 9 ) ] , false ) , BitConverter . ToUInt16 ( readOnlySpan [ ( i + 5 ) ..( i + 7 ) ] , false ) ) ;
27- break ;
40+ int remaining = length - read ;
41+ if ( remaining < 9 )
42+ {
43+ buffer [ read ..length ] . CopyTo ( buffer [ ..remaining ] ) ;
44+ if ( ( length = image . Read ( buffer [ remaining ..] ) ) == 0 )
45+ {
46+ size = Vector2 . Zero ;
47+ return ImageFormat . Unknown ;
48+ }
49+ read = 0 ;
50+
51+ continue ;
52+ }
53+
54+ if ( ( buffer [ read + 1 ] & 0xFC ) == 0xC0 ) // SOF0 - SOF3
55+ {
56+ size = new Vector2 ( BitConverter . ToUInt16 ( buffer [ ( read + 7 ) ..( read + 9 ) ] , false ) , BitConverter . ToUInt16 ( buffer [ ( read + 5 ) ..( read + 7 ) ] , false ) ) ;
57+ return ImageFormat . Jpeg ;
58+ }
59+
60+ int len = 2 + BitConverter . ToUInt16 ( buffer [ ( read + 2 ) ..( read + 4 ) ] , false ) ;
61+ if ( len > remaining )
62+ {
63+ if ( image . CanSeek )
64+ {
65+ image . Seek ( len - remaining , SeekOrigin . Current ) ;
66+ if ( ( length = image . Read ( buffer ) ) == 0 )
67+ {
68+ size = Vector2 . Zero ;
69+ return ImageFormat . Unknown ;
70+ }
71+ }
72+ else
73+ {
74+ int skipped = remaining ;
75+ while ( skipped + 1024 > len )
76+ {
77+ if ( ( length = image . Read ( buffer ) ) == 0 )
78+ {
79+ size = Vector2 . Zero ;
80+ return ImageFormat . Unknown ;
81+ }
82+ skipped += length ;
83+ }
84+
85+ if ( image . Read ( buffer [ ..( len - skipped ) ] ) == 0 )
86+ {
87+ size = Vector2 . Zero ;
88+ return ImageFormat . Unknown ;
89+ }
90+
91+ if ( ( length = image . Read ( buffer ) ) == 0 )
92+ {
93+ size = Vector2 . Zero ;
94+ return ImageFormat . Unknown ;
95+ }
96+ }
97+ read = 0 ;
98+
99+ continue ;
100+ }
101+ read += len ;
28102 }
29103 }
30- return ImageFormat . Jpeg ;
31104 }
32105
33- if ( readOnlySpan [ ..8 ] . SequenceEqual ( new byte [ ] { 0x89 , 0x50 , 0x4E , 0x47 , 0x0D , 0x0A , 0x1A , 0x0A } ) ) // PNG
34- {
35- size = new Vector2 ( BitConverter . ToUInt32 ( readOnlySpan [ 16 ..20 ] , false ) , BitConverter . ToUInt32 ( readOnlySpan [ 20 ..24 ] , false ) ) ;
36- return ImageFormat . Png ;
106+ { // GIF
107+ Span < byte > header = buffer [ ..6 ] ;
108+ if ( header . SequenceEqual ( GIF87A_0_6 ) || header . SequenceEqual ( GIF89A_0_6 ) )
109+ {
110+ size = new Vector2 ( BitConverter . ToUInt16 ( buffer [ 6 ..8 ] ) , BitConverter . ToUInt16 ( buffer [ 8 ..10 ] ) ) ;
111+ return ImageFormat . Gif ;
112+ }
37113 }
38114
39- if ( readOnlySpan [ ..4 ] . SequenceEqual ( new byte [ ] { 0x52 , 0x49 , 0x46 , 0x46 } ) && readOnlySpan [ 8 ..12 ] . SequenceEqual ( new byte [ ] { 0x57 , 0x45 , 0x42 , 0x50 } ) ) // RIFF WEBP
40- {
41- if ( readOnlySpan [ 12 ..16 ] . SequenceEqual ( new byte [ ] { 0x56 , 0x50 , 0x38 , 0x58 } ) ) // VP8X
42- size = new Vector2 ( BitConverter . ToUInt16 ( readOnlySpan [ 24 ..27 ] ) + 1 , BitConverter . ToUInt16 ( readOnlySpan [ 27 ..30 ] ) + 1 ) ;
43- else if ( readOnlySpan [ 12 ..16 ] . SequenceEqual ( new byte [ ] { 0x56 , 0x50 , 0x38 , 0x4C } ) ) // VP8L
44- size = new Vector2 ( ( BitConverter . ToInt32 ( readOnlySpan [ 21 ..25 ] ) & 0x3FFF ) + 1 , ( ( BitConverter . ToInt32 ( readOnlySpan [ 21 ..25 ] ) & 0xFFFC000 ) >> 0x0E ) + 1 ) ;
45- else // VP8
46- size = new Vector2 ( BitConverter . ToUInt16 ( readOnlySpan [ 26 ..28 ] ) , BitConverter . ToUInt16 ( readOnlySpan [ 28 ..30 ] ) ) ;
47- return ImageFormat . Webp ;
115+ { // PNG
116+ if ( buffer [ ..8 ] . SequenceEqual ( PNG_0_8 ) )
117+ {
118+ size = new Vector2 ( BitConverter . ToUInt32 ( buffer [ 16 ..20 ] , false ) , BitConverter . ToUInt32 ( buffer [ 20 ..24 ] , false ) ) ;
119+ return ImageFormat . Png ;
120+ }
48121 }
49122
50- if ( readOnlySpan [ ..2 ] . SequenceEqual ( new byte [ ] { 0x42 , 0x4D } ) ) // BMP
51- {
52- size = new Vector2 ( BitConverter . ToUInt16 ( readOnlySpan [ 18 ..20 ] ) , BitConverter . ToUInt16 ( readOnlySpan [ 22 ..24 ] ) ) ;
53- return ImageFormat . Bmp ;
123+ { // BMP
124+ if ( buffer [ ..2 ] . SequenceEqual ( BMP_0_2 ) )
125+ {
126+ size = new Vector2 ( BitConverter . ToUInt16 ( buffer [ 18 ..20 ] ) , BitConverter . ToUInt16 ( buffer [ 22 ..24 ] ) ) ;
127+ return ImageFormat . Bmp ;
128+ }
54129 }
55130
56- if ( readOnlySpan [ ..2 ] . SequenceEqual ( new byte [ ] { 0x49 , 0x49 } ) || readOnlySpan [ ..2 ] . SequenceEqual ( new byte [ ] { 0x4D , 0x4D } ) ) // TIFF
57- {
58- size = new Vector2 ( BitConverter . ToUInt16 ( readOnlySpan [ 18 ..20 ] ) , BitConverter . ToUInt16 ( readOnlySpan [ 30 ..32 ] ) ) ;
59- return ImageFormat . Tiff ;
131+ { // WEBP
132+ if ( length < 30 )
133+ {
134+ size = Vector2 . Zero ;
135+ return ImageFormat . Unknown ;
136+ }
137+
138+ if ( buffer [ ..4 ] . SequenceEqual ( RIFF_0_4 ) && buffer [ 8 ..12 ] . SequenceEqual ( WEBP_8_12 ) ) // RIFF WEBP
139+ {
140+ Span < byte > fourCC = buffer [ 12 ..16 ] ;
141+
142+ // VP8
143+ if ( fourCC . SequenceEqual ( VP8_12_16 ) )
144+ {
145+ size = new Vector2 ( BitConverter . ToUInt16 ( buffer [ 26 ..28 ] ) , BitConverter . ToUInt16 ( buffer [ 28 ..30 ] ) ) ;
146+ return ImageFormat . Webp ;
147+ }
148+
149+ // VP8L
150+ if ( fourCC . SequenceEqual ( VP8L_12_16 ) )
151+ {
152+ size = new Vector2 ( ( BitConverter . ToInt32 ( buffer [ 21 ..25 ] ) & 0x3FFF ) + 1 , ( ( BitConverter . ToInt32 ( buffer [ 21 ..25 ] ) & 0xFFFC000 ) >> 0x0E ) + 1 ) ;
153+ return ImageFormat . Webp ;
154+ }
155+
156+ // VP8X
157+ if ( fourCC . SequenceEqual ( VP8X_12_16 ) )
158+ {
159+ size = new Vector2 ( BitConverter . ToUInt16 ( buffer [ 24 ..27 ] ) , BitConverter . ToUInt16 ( buffer [ 27 ..30 ] ) ) ;
160+ return ImageFormat . Webp ;
161+ }
162+ }
60163 }
61164
165+ // { // TODO TIFF
166+ // bool ll = false;
167+ // if (ll = buffer[..2].SequenceEqual(LLTIFF_0_2) || buffer[..2].SequenceEqual(MMTIFF_0_2))
168+ // {
169+ // uint offset = BitConverter.ToUInt32(buffer[4..8], ll);
170+ // int read = (int)offset;
171+ // while (true)
172+ // {
173+ // int remaining = length - read;
174+ // if (remaining < 2)
175+ // {
176+ // buffer[read..length].CopyTo(buffer[..remaining]);
177+ // if ((length = image.Read(buffer[remaining..])) == 0)
178+ // {
179+ // size = Vector2.Zero;
180+ // return ImageFormat.Unknown;
181+ // }
182+ // read = 0;
183+
184+ // continue;
185+ // }
186+
187+ // int count = BitConverter.ToUInt16(buffer[read..2]);
188+ // for (int i = 0; i < count; i++)
189+ // {
190+ // // TODO: read fd
191+ // }
192+ // }
193+ // }
194+ // }
195+
62196 size = Vector2 . Zero ;
63197 return ImageFormat . Unknown ;
64198 }
@@ -71,6 +205,6 @@ internal enum ImageFormat : uint
71205 Jpeg = 1000 ,
72206 Gif = 2000 ,
73207 Webp = 1002 ,
74- Bmp = 1005 ,
75- Tiff
208+ Bmp = 1005
209+ // Tiff
76210}
0 commit comments