@@ -151,3 +151,79 @@ ycbcr_image_avx2!(RgbImageAVX2, 3, 0, 1, 2);
151151ycbcr_image_avx2 ! ( RgbaImageAVX2 , 4 , 0 , 1 , 2 ) ;
152152ycbcr_image_avx2 ! ( BgrImageAVX2 , 3 , 2 , 1 , 0 ) ;
153153ycbcr_image_avx2 ! ( BgraImageAVX2 , 4 , 2 , 1 , 0 ) ;
154+
155+ #[ cfg( test) ]
156+ mod tests {
157+ use super :: * ;
158+ use std:: vec:: Vec ;
159+
160+ // A very basic linear congruential generator (LCG) to avoid external dependencies.
161+ pub struct SimpleRng {
162+ state : u64 ,
163+ }
164+
165+ impl SimpleRng {
166+ /// Create a new RNG with a given seed.
167+ pub fn new ( seed : u64 ) -> Self {
168+ Self { state : seed }
169+ }
170+
171+ /// Generate the next random u64 value.
172+ pub fn next_u64 ( & mut self ) -> u64 {
173+ // Constants from Numerical Recipes
174+ self . state = self . state . wrapping_mul ( 6364136223846793005 ) . wrapping_add ( 1 ) ;
175+ self . state
176+ }
177+
178+ /// Generate a random byte in 0..=255
179+ pub fn next_byte ( & mut self ) -> u8 {
180+ ( self . next_u64 ( ) & 0xFF ) as u8
181+ }
182+
183+ /// Fill a Vec<u8> with random bytes of the given length.
184+ pub fn random_bytes ( & mut self , len : usize ) -> Vec < u8 > {
185+ ( 0 ..len) . map ( |_| self . next_byte ( ) ) . collect ( )
186+ }
187+ }
188+
189+ #[ test]
190+ #[ cfg( feature = "simd" ) ]
191+ fn avx_matches_scalar_rgb ( ) {
192+ let mut rng = SimpleRng :: new ( 42 ) ;
193+ let width = 512 + 3 ; // power of two plus a bit to stress remainder handling
194+ let height = 1 ;
195+ let bpp = 3 ;
196+
197+ let input = rng. random_bytes ( width * height * bpp) ; // power of two plus a bit to exercise remainder handling
198+
199+ let scalar_result: Vec < [ u8 ; 3 ] > = input
200+ . chunks_exact ( bpp)
201+ . map ( |chunk| {
202+ let [ r, g, b, ..] = chunk else { unreachable ! ( ) } ;
203+ let ( y, cb, cr) = rgb_to_ycbcr ( * r, * g, * b) ;
204+ [ y, cb, cr]
205+ } )
206+ . collect ( ) ;
207+
208+ let mut buffers = [ Vec :: new ( ) , Vec :: new ( ) , Vec :: new ( ) , Vec :: new ( ) ] ;
209+ let avx_input = RgbImageAVX2 (
210+ & input,
211+ width. try_into ( ) . unwrap ( ) ,
212+ height. try_into ( ) . unwrap ( ) ,
213+ ) ;
214+ unsafe {
215+ let avx_result = avx_input. fill_buffers_avx2 ( 0 , & mut buffers) ;
216+ }
217+
218+ for i in 0 ..3 {
219+ assert_eq ! ( buffers[ i] . len( ) , input. len( ) / 3 ) ;
220+ }
221+
222+ for ( i, pixel) in scalar_result. iter ( ) . copied ( ) . enumerate ( ) {
223+ let avx_pixel: [ u8 ; 3 ] = [ buffers[ 0 ] [ i] , buffers[ 1 ] [ i] , buffers[ 2 ] [ i] ] ;
224+ if pixel != avx_pixel {
225+ panic ! ( "Mismatch at index {i}: scalar result is {pixel:?}, avx result is {avx_pixel:?}" ) ;
226+ }
227+ }
228+ }
229+ }
0 commit comments