@@ -138,8 +138,7 @@ public static int DecompressDXT1<TOutputColor, TOutputChannel>(ReadOnlySpan<byte
138138 /// <returns>Number of bytes read from <paramref name="input"/></returns>
139139 public static int DecompressDXT3 ( ReadOnlySpan < byte > input , int width , int height , out byte [ ] output )
140140 {
141- output = new byte [ width * height * sizeof ( uint ) ] ;
142- return DecompressDXT3 ( input , width , height , output ) ;
141+ return DecompressDXT3 < ColorBGRA32 , byte > ( input , width , height , out output ) ;
143142 }
144143
145144 /// <summary>
@@ -152,55 +151,107 @@ public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height
152151 /// <returns>Number of bytes read from <paramref name="input"/></returns>
153152 public static int DecompressDXT3 ( ReadOnlySpan < byte > input , int width , int height , Span < byte > output )
154153 {
155- ThrowHelper . ThrowIfNotEnoughSpace ( output , width , height ) ;
154+ return DecompressDXT3 < ColorBGRA32 , byte > ( input , width , height , output ) ;
155+ }
156+
157+ /// <summary>
158+ /// Decompress a DXT3 image
159+ /// </summary>
160+ /// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
161+ /// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
162+ /// <param name="input">Input buffer containing the compressed image.</param>
163+ /// <param name="width">Pixel width of the image.</param>
164+ /// <param name="height">Pixel height of the image.</param>
165+ /// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
166+ /// <returns>Number of bytes read from <paramref name="input"/></returns>
167+ public static int DecompressDXT3 < TOutputColor , TOutputChannel > ( ReadOnlySpan < byte > input , int width , int height , out byte [ ] output )
168+ where TOutputChannel : unmanaged
169+ where TOutputColor : unmanaged, IColor < TOutputChannel >
170+ {
171+ output = new byte [ width * height * Unsafe . SizeOf < TOutputColor > ( ) ] ;
172+ return DecompressDXT3 < TOutputColor , TOutputChannel > ( input , width , height , output ) ;
173+ }
174+
175+ /// <summary>
176+ /// Decompress a DXT1 image
177+ /// </summary>
178+ /// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
179+ /// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
180+ /// <param name="input">Input buffer containing the compressed image.</param>
181+ /// <param name="width">Pixel width of the image.</param>
182+ /// <param name="height">Pixel height of the image.</param>
183+ /// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
184+ /// <returns>Number of bytes read from <paramref name="input"/></returns>
185+ public static int DecompressDXT3 < TOutputColor , TOutputChannel > ( ReadOnlySpan < byte > input , int width , int height , Span < byte > output )
186+ where TOutputChannel : unmanaged
187+ where TOutputColor : unmanaged, IColor < TOutputChannel >
188+ {
189+ return DecompressDXT3 < TOutputColor , TOutputChannel > ( input , width , height , MemoryMarshal . Cast < byte , TOutputColor > ( output ) ) ;
190+ }
191+
192+ /// <summary>
193+ /// Decompress a DXT3 image
194+ /// </summary>
195+ /// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
196+ /// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
197+ /// <param name="input">Input buffer containing the compressed image.</param>
198+ /// <param name="width">Pixel width of the image.</param>
199+ /// <param name="height">Pixel height of the image.</param>
200+ /// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
201+ /// <returns>Number of bytes read from <paramref name="input"/></returns>
202+ public static int DecompressDXT3 < TOutputColor , TOutputChannel > ( ReadOnlySpan < byte > input , int width , int height , Span < TOutputColor > output )
203+ where TOutputChannel : unmanaged
204+ where TOutputColor : unmanaged, IColor < TOutputChannel >
205+ {
206+ ThrowHelper . ThrowIfNotEnoughSpace ( output . Length , width * height ) ;
156207
157208 int offset = 0 ;
158209 int bcw = ( width + 3 ) / 4 ;
159210 int bch = ( height + 3 ) / 4 ;
160211 int clen_last = ( width + 3 ) % 4 + 1 ;
161- uint [ ] buffer = new uint [ 16 ] ;
162- int [ ] colors = new int [ 4 ] ;
163- int [ ] alphas = new int [ 16 ] ;
212+ Span < TOutputColor > buffer = stackalloc TOutputColor [ 16 ] ;
213+ Span < ColorRGB < byte > > colors = stackalloc ColorRGB < byte > [ 4 ] ;
214+ Span < byte > alphas = stackalloc byte [ 16 ] ;
164215 for ( int t = 0 ; t < bch ; t ++ )
165216 {
166217 for ( int s = 0 ; s < bcw ; s ++ , offset += 16 )
167218 {
168219 for ( int i = 0 ; i < 4 ; i ++ )
169220 {
170221 int alpha = input [ offset + i * 2 ] | input [ offset + i * 2 + 1 ] << 8 ;
171- alphas [ i * 4 + 0 ] = ( ( ( alpha >> 0 ) & 0xF ) * 0x11 ) << 24 ;
172- alphas [ i * 4 + 1 ] = ( ( ( alpha >> 4 ) & 0xF ) * 0x11 ) << 24 ;
173- alphas [ i * 4 + 2 ] = ( ( ( alpha >> 8 ) & 0xF ) * 0x11 ) << 24 ;
174- alphas [ i * 4 + 3 ] = ( ( ( alpha >> 12 ) & 0xF ) * 0x11 ) << 24 ;
222+ alphas [ i * 4 + 0 ] = ( byte ) ( ( ( alpha >> 0 ) & 0xF ) * 0x11 ) ;
223+ alphas [ i * 4 + 1 ] = ( byte ) ( ( ( alpha >> 4 ) & 0xF ) * 0x11 ) ;
224+ alphas [ i * 4 + 2 ] = ( byte ) ( ( ( alpha >> 8 ) & 0xF ) * 0x11 ) ;
225+ alphas [ i * 4 + 3 ] = ( byte ) ( ( ( alpha >> 12 ) & 0xF ) * 0x11 ) ;
175226 }
176227
177228 int q0 = input [ offset + 8 ] | input [ offset + 9 ] << 8 ;
178229 int q1 = input [ offset + 10 ] | input [ offset + 11 ] << 8 ;
179230 Rgb565 ( q0 , out byte r0 , out byte g0 , out byte b0 ) ;
180231 Rgb565 ( q1 , out byte r1 , out byte g1 , out byte b1 ) ;
181- colors [ 0 ] = Color ( r0 , g0 , b0 , 0 ) ;
182- colors [ 1 ] = Color ( r1 , g1 , b1 , 0 ) ;
232+ colors [ 0 ] = new ColorRGB < byte > ( r0 , g0 , b0 ) ;
233+ colors [ 1 ] = new ColorRGB < byte > ( r1 , g1 , b1 ) ;
183234 if ( q0 > q1 )
184235 {
185- colors [ 2 ] = Color ( ( r0 * 2 + r1 ) / 3 , ( g0 * 2 + g1 ) / 3 , ( b0 * 2 + b1 ) / 3 , 0 ) ;
186- colors [ 3 ] = Color ( ( r0 + r1 * 2 ) / 3 , ( g0 + g1 * 2 ) / 3 , ( b0 + b1 * 2 ) / 3 , 0 ) ;
236+ colors [ 2 ] = new ColorRGB < byte > ( ( byte ) ( ( r0 * 2 + r1 ) / 3 ) , ( byte ) ( ( g0 * 2 + g1 ) / 3 ) , ( byte ) ( ( b0 * 2 + b1 ) / 3 ) ) ;
237+ colors [ 3 ] = new ColorRGB < byte > ( ( byte ) ( ( r0 + r1 * 2 ) / 3 ) , ( byte ) ( ( g0 + g1 * 2 ) / 3 ) , ( byte ) ( ( b0 + b1 * 2 ) / 3 ) ) ;
187238 }
188239 else
189240 {
190- colors [ 2 ] = Color ( ( r0 + r1 ) / 2 , ( g0 + g1 ) / 2 , ( b0 + b1 ) / 2 , 0 ) ;
241+ colors [ 2 ] = new ColorRGB < byte > ( ( byte ) ( ( r0 + r1 ) / 2 ) , ( byte ) ( ( g0 + g1 ) / 2 ) , ( byte ) ( ( b0 + b1 ) / 2 ) ) ;
242+ colors [ 3 ] . SetBlack < ColorRGB < byte > , byte > ( ) ;
191243 }
192244
193245 uint d = ToUInt32 ( input , offset + 12 ) ;
194246 for ( int i = 0 ; i < 16 ; i ++ , d >>= 2 )
195247 {
196- buffer [ i ] = unchecked ( ( uint ) ( colors [ d & 3 ] | alphas [ i ] ) ) ;
248+ buffer [ i ] . SetConvertedChannels < TOutputColor , TOutputChannel , ColorRGB < byte > , byte > ( colors [ unchecked ( ( int ) ( d & 3 ) ) ] , alphas [ i ] ) ;
197249 }
198250
199- int clen = ( s < bcw - 1 ? 4 : clen_last ) * 4 ;
251+ int clen = s < bcw - 1 ? 4 : clen_last ;
200252 for ( int i = 0 , y = t * 4 ; i < 4 && y < height ; i ++ , y ++ )
201253 {
202- ReadOnlySpan < byte > bufferSpan = MemoryMarshal . Cast < uint , byte > ( new ReadOnlySpan < uint > ( buffer ) ) ;
203- BlockCopy ( bufferSpan , i * 4 * 4 , output , ( y * width + s * 4 ) * 4 , clen ) ;
254+ BlockCopy ( buffer , i * 4 , output , y * width + s * 4 , clen ) ;
204255 }
205256 }
206257 }
@@ -218,8 +269,7 @@ public static int DecompressDXT3(ReadOnlySpan<byte> input, int width, int height
218269 /// <returns>Number of bytes read from <paramref name="input"/></returns>
219270 public static int DecompressDXT5 ( ReadOnlySpan < byte > input , int width , int height , out byte [ ] output )
220271 {
221- output = new byte [ width * height * sizeof ( uint ) ] ;
222- return DecompressDXT5 ( input , width , height , output ) ;
272+ return DecompressDXT5 < ColorBGRA32 , byte > ( input , width , height , out output ) ;
223273 }
224274
225275 /// <summary>
@@ -232,75 +282,126 @@ public static int DecompressDXT5(ReadOnlySpan<byte> input, int width, int height
232282 /// <returns>Number of bytes read from <paramref name="input"/></returns>
233283 public static int DecompressDXT5 ( ReadOnlySpan < byte > input , int width , int height , Span < byte > output )
234284 {
235- ThrowHelper . ThrowIfNotEnoughSpace ( output , width , height ) ;
285+ return DecompressDXT5 < ColorBGRA32 , byte > ( input , width , height , output ) ;
286+ }
287+
288+ /// <summary>
289+ /// Decompress a DXT5 image
290+ /// </summary>
291+ /// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
292+ /// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
293+ /// <param name="input">Input buffer containing the compressed image.</param>
294+ /// <param name="width">Pixel width of the image.</param>
295+ /// <param name="height">Pixel height of the image.</param>
296+ /// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
297+ /// <returns>Number of bytes read from <paramref name="input"/></returns>
298+ public static int DecompressDXT5 < TOutputColor , TOutputChannel > ( ReadOnlySpan < byte > input , int width , int height , out byte [ ] output )
299+ where TOutputChannel : unmanaged
300+ where TOutputColor : unmanaged, IColor < TOutputChannel >
301+ {
302+ output = new byte [ width * height * Unsafe . SizeOf < TOutputColor > ( ) ] ;
303+ return DecompressDXT5 < TOutputColor , TOutputChannel > ( input , width , height , output ) ;
304+ }
305+
306+ /// <summary>
307+ /// Decompress a DXT1 image
308+ /// </summary>
309+ /// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
310+ /// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
311+ /// <param name="input">Input buffer containing the compressed image.</param>
312+ /// <param name="width">Pixel width of the image.</param>
313+ /// <param name="height">Pixel height of the image.</param>
314+ /// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
315+ /// <returns>Number of bytes read from <paramref name="input"/></returns>
316+ public static int DecompressDXT5 < TOutputColor , TOutputChannel > ( ReadOnlySpan < byte > input , int width , int height , Span < byte > output )
317+ where TOutputChannel : unmanaged
318+ where TOutputColor : unmanaged, IColor < TOutputChannel >
319+ {
320+ return DecompressDXT5 < TOutputColor , TOutputChannel > ( input , width , height , MemoryMarshal . Cast < byte , TOutputColor > ( output ) ) ;
321+ }
322+
323+ /// <summary>
324+ /// Decompress a DXT5 image
325+ /// </summary>
326+ /// <typeparam name="TOutputColor">The <see cref="IColor{T}"/> type used for each pixel.</typeparam>
327+ /// <typeparam name="TOutputChannel">The channel type used in <typeparamref name="TOutputColor"/>.</typeparam>
328+ /// <param name="input">Input buffer containing the compressed image.</param>
329+ /// <param name="width">Pixel width of the image.</param>
330+ /// <param name="height">Pixel height of the image.</param>
331+ /// <param name="output">An output buffer. Must be at least width * height * pixelSize.</param>
332+ /// <returns>Number of bytes read from <paramref name="input"/></returns>
333+ public static int DecompressDXT5 < TOutputColor , TOutputChannel > ( ReadOnlySpan < byte > input , int width , int height , Span < TOutputColor > output )
334+ where TOutputChannel : unmanaged
335+ where TOutputColor : unmanaged, IColor < TOutputChannel >
336+ {
337+ ThrowHelper . ThrowIfNotEnoughSpace ( output . Length , width * height ) ;
236338
237339 int offset = 0 ;
238340 int bcw = ( width + 3 ) / 4 ;
239341 int bch = ( height + 3 ) / 4 ;
240342 int clen_last = ( width + 3 ) % 4 + 1 ;
241- uint [ ] buffer = new uint [ 16 ] ;
242- int [ ] colors = new int [ 4 ] ;
243- int [ ] alphas = new int [ 8 ] ;
343+ Span < TOutputColor > buffer = stackalloc TOutputColor [ 16 ] ;
344+ Span < ColorRGB < byte > > colors = stackalloc ColorRGB < byte > [ 4 ] ;
345+ Span < byte > alphas = stackalloc byte [ 8 ] ;
244346 for ( int t = 0 ; t < bch ; t ++ )
245347 {
246348 for ( int s = 0 ; s < bcw ; s ++ , offset += 16 )
247349 {
248- alphas [ 0 ] = input [ offset + 0 ] ;
249- alphas [ 1 ] = input [ offset + 1 ] ;
250- if ( alphas [ 0 ] > alphas [ 1 ] )
350+ byte a0 = input [ offset + 0 ] ;
351+ byte a1 = input [ offset + 1 ] ;
352+ alphas [ 0 ] = a0 ;
353+ alphas [ 1 ] = a1 ;
354+ if ( a0 > a1 )
251355 {
252- alphas [ 2 ] = ( alphas [ 0 ] * 6 + alphas [ 1 ] ) / 7 ;
253- alphas [ 3 ] = ( alphas [ 0 ] * 5 + alphas [ 1 ] * 2 ) / 7 ;
254- alphas [ 4 ] = ( alphas [ 0 ] * 4 + alphas [ 1 ] * 3 ) / 7 ;
255- alphas [ 5 ] = ( alphas [ 0 ] * 3 + alphas [ 1 ] * 4 ) / 7 ;
256- alphas [ 6 ] = ( alphas [ 0 ] * 2 + alphas [ 1 ] * 5 ) / 7 ;
257- alphas [ 7 ] = ( alphas [ 0 ] + alphas [ 1 ] * 6 ) / 7 ;
356+ alphas [ 2 ] = ( byte ) ( ( a0 * 6 + a1 ) / 7 ) ;
357+ alphas [ 3 ] = ( byte ) ( ( a0 * 5 + a1 * 2 ) / 7 ) ;
358+ alphas [ 4 ] = ( byte ) ( ( a0 * 4 + a1 * 3 ) / 7 ) ;
359+ alphas [ 5 ] = ( byte ) ( ( a0 * 3 + a1 * 4 ) / 7 ) ;
360+ alphas [ 6 ] = ( byte ) ( ( a0 * 2 + a1 * 5 ) / 7 ) ;
361+ alphas [ 7 ] = ( byte ) ( ( a0 + a1 * 6 ) / 7 ) ;
258362 }
259363 else
260364 {
261- alphas [ 2 ] = ( alphas [ 0 ] * 4 + alphas [ 1 ] ) / 5 ;
262- alphas [ 3 ] = ( alphas [ 0 ] * 3 + alphas [ 1 ] * 2 ) / 5 ;
263- alphas [ 4 ] = ( alphas [ 0 ] * 2 + alphas [ 1 ] * 3 ) / 5 ;
264- alphas [ 5 ] = ( alphas [ 0 ] + alphas [ 1 ] * 4 ) / 5 ;
265- alphas [ 7 ] = 255 ;
266- }
267- for ( int i = 0 ; i < 8 ; i ++ )
268- {
269- alphas [ i ] <<= 24 ;
365+ alphas [ 2 ] = ( byte ) ( ( a0 * 4 + a1 ) / 5 ) ;
366+ alphas [ 3 ] = ( byte ) ( ( a0 * 3 + a1 * 2 ) / 5 ) ;
367+ alphas [ 4 ] = ( byte ) ( ( a0 * 2 + a1 * 3 ) / 5 ) ;
368+ alphas [ 5 ] = ( byte ) ( ( a0 + a1 * 4 ) / 5 ) ;
369+ alphas [ 6 ] = byte . MinValue ;
370+ alphas [ 7 ] = byte . MaxValue ;
270371 }
271372
272373 int q0 = input [ offset + 8 ] | input [ offset + 9 ] << 8 ;
273374 int q1 = input [ offset + 10 ] | input [ offset + 11 ] << 8 ;
274375 Rgb565 ( q0 , out byte r0 , out byte g0 , out byte b0 ) ;
275376 Rgb565 ( q1 , out byte r1 , out byte g1 , out byte b1 ) ;
276- colors [ 0 ] = Color ( r0 , g0 , b0 , 0 ) ;
277- colors [ 1 ] = Color ( r1 , g1 , b1 , 0 ) ;
377+ colors [ 0 ] = new ColorRGB < byte > ( r0 , g0 , b0 ) ;
378+ colors [ 1 ] = new ColorRGB < byte > ( r1 , g1 , b1 ) ;
278379 if ( q0 > q1 )
279380 {
280- colors [ 2 ] = Color ( ( r0 * 2 + r1 ) / 3 , ( g0 * 2 + g1 ) / 3 , ( b0 * 2 + b1 ) / 3 , 0 ) ;
281- colors [ 3 ] = Color ( ( r0 + r1 * 2 ) / 3 , ( g0 + g1 * 2 ) / 3 , ( b0 + b1 * 2 ) / 3 , 0 ) ;
381+ colors [ 2 ] = new ColorRGB < byte > ( ( byte ) ( ( r0 * 2 + r1 ) / 3 ) , ( byte ) ( ( g0 * 2 + g1 ) / 3 ) , ( byte ) ( ( b0 * 2 + b1 ) / 3 ) ) ;
382+ colors [ 3 ] = new ColorRGB < byte > ( ( byte ) ( ( r0 + r1 * 2 ) / 3 ) , ( byte ) ( ( g0 + g1 * 2 ) / 3 ) , ( byte ) ( ( b0 + b1 * 2 ) / 3 ) ) ;
282383 }
283384 else
284385 {
285- colors [ 2 ] = Color ( ( r0 + r1 ) / 2 , ( g0 + g1 ) / 2 , ( b0 + b1 ) / 2 , 0 ) ;
386+ colors [ 2 ] = new ColorRGB < byte > ( ( byte ) ( ( r0 + r1 ) / 2 ) , ( byte ) ( ( g0 + g1 ) / 2 ) , ( byte ) ( ( b0 + b1 ) / 2 ) ) ;
387+ colors [ 3 ] . SetBlack < ColorRGB < byte > , byte > ( ) ;
286388 }
287389
288390 ulong da = ToUInt64 ( input , offset ) >> 16 ;
289391 uint dc = ToUInt32 ( input , offset + 12 ) ;
290392 for ( int i = 0 ; i < 16 ; i ++ , da >>= 3 , dc >>= 2 )
291393 {
292- buffer [ i ] = unchecked ( ( uint ) ( alphas [ da & 7 ] | colors [ dc & 3 ] ) ) ;
394+ buffer [ i ] . SetConvertedChannels < TOutputColor , TOutputChannel , ColorRGB < byte > , byte > ( colors [ unchecked ( ( int ) ( dc & 3 ) ) ] , alphas [ unchecked ( ( int ) ( da & 7 ) ) ] ) ;
293395 }
294396
295- int clen = ( s < bcw - 1 ? 4 : clen_last ) * 4 ;
397+ int clen = s < bcw - 1 ? 4 : clen_last ;
296398 for ( int i = 0 , y = t * 4 ; i < 4 && y < height ; i ++ , y ++ )
297399 {
298- ReadOnlySpan < byte > bufferSpan = MemoryMarshal . Cast < uint , byte > ( new ReadOnlySpan < uint > ( buffer ) ) ;
299- BlockCopy ( bufferSpan , i * 4 * 4 , output , ( y * width + s * 4 ) * 4 , clen ) ;
400+ BlockCopy ( buffer , i * 4 , output , y * width + s * 4 , clen ) ;
300401 }
301402 }
302403 }
303-
404+
304405 return offset ;
305406 }
306407
@@ -315,12 +416,6 @@ private static void Rgb565(int c, out byte r, out byte g, out byte b)
315416 b |= ( byte ) ( b >> 5 ) ;
316417 }
317418
318- [ MethodImpl ( MethodImplOptions . AggressiveInlining | MethodImplOptions . AggressiveOptimization ) ]
319- private static int Color ( int r , int g , int b , int a )
320- {
321- return ( byte ) r << 16 | ( byte ) g << 8 | ( byte ) b | ( byte ) a << 24 ;
322- }
323-
324419 /// <summary>
325420 /// Based on <see cref="Buffer.BlockCopy(Array, int, Array, int, int)"/>
326421 /// </summary>
0 commit comments