11use std:: cmp:: Ordering ;
22use std:: fs:: { self , File } ;
3+ use std:: io:: Cursor ;
34use std:: path:: { Path , PathBuf } ;
45use std:: time:: { Duration , Instant } ;
56
67use anyhow:: { bail, ensure, Context , Result } ;
78use bytemuck:: cast_slice;
89use c_vec:: CVec ;
10+ use qoi:: { Decoder , Encoder } ;
911use structopt:: StructOpt ;
1012use walkdir:: { DirEntry , WalkDir } ;
1113
@@ -124,26 +126,45 @@ impl Image {
124126trait Codec {
125127 type Output : AsRef < [ u8 ] > ;
126128
127- fn name ( ) -> & ' static str ;
128- fn encode ( img : & Image ) -> Result < Self :: Output > ;
129- fn decode ( data : & [ u8 ] , img : & Image ) -> Result < Self :: Output > ;
129+ fn name ( & self ) -> & ' static str ;
130+ fn encode ( & self , img : & Image ) -> Result < Self :: Output > ;
131+ fn decode ( & self , data : & [ u8 ] , img : & Image ) -> Result < Self :: Output > ;
130132}
131133
132- struct CodecQoiRust ;
134+ struct CodecQoiRust {
135+ pub stream : bool ,
136+ }
133137
134138impl Codec for CodecQoiRust {
135139 type Output = Vec < u8 > ;
136140
137- fn name ( ) -> & ' static str {
138- "qoi-rust"
141+ fn name ( & self ) -> & ' static str {
142+ if self . stream {
143+ "qoi-rust[stream]"
144+ } else {
145+ "qoi-rust"
146+ }
139147 }
140148
141- fn encode ( img : & Image ) -> Result < Vec < u8 > > {
142- Ok ( qoi:: encode_to_vec ( & img. data , img. width , img. height ) ?)
149+ fn encode ( & self , img : & Image ) -> Result < Vec < u8 > > {
150+ if self . stream {
151+ let mut stream = Vec :: new ( ) ;
152+ let encoder = Encoder :: new ( & img. data , img. width , img. height ) ?;
153+ encoder. encode_to_stream ( & mut stream) ?;
154+ Ok ( stream)
155+ } else {
156+ Ok ( qoi:: encode_to_vec ( & img. data , img. width , img. height ) ?)
157+ }
143158 }
144159
145- fn decode ( data : & [ u8 ] , _img : & Image ) -> Result < Vec < u8 > > {
146- Ok ( qoi:: decode_to_vec ( data) ?. 1 )
160+ fn decode ( & self , data : & [ u8 ] , _img : & Image ) -> Result < Vec < u8 > > {
161+ if self . stream {
162+ let stream = Cursor :: new ( data) ;
163+ let mut decoder = Decoder :: from_stream ( stream) ?;
164+ Ok ( decoder. decode_to_vec ( ) ?)
165+ } else {
166+ Ok ( qoi:: decode_to_vec ( data) ?. 1 )
167+ }
147168 }
148169}
149170
@@ -152,15 +173,15 @@ struct CodecQoiC;
152173impl Codec for CodecQoiC {
153174 type Output = CVec < u8 > ;
154175
155- fn name ( ) -> & ' static str {
176+ fn name ( & self ) -> & ' static str {
156177 "qoi.h"
157178 }
158179
159- fn encode ( img : & Image ) -> Result < CVec < u8 > > {
180+ fn encode ( & self , img : & Image ) -> Result < CVec < u8 > > {
160181 libqoi:: qoi_encode ( & img. data , img. width , img. height , img. channels )
161182 }
162183
163- fn decode ( data : & [ u8 ] , img : & Image ) -> Result < CVec < u8 > > {
184+ fn decode ( & self , data : & [ u8 ] , img : & Image ) -> Result < CVec < u8 > > {
164185 Ok ( libqoi:: qoi_decode ( data, img. channels ) ?. 1 )
165186 }
166187}
@@ -209,33 +230,33 @@ impl ImageBench {
209230 Self { results : vec ! [ ] , n_pixels : img. n_pixels ( ) , n_bytes : img. n_bytes ( ) }
210231 }
211232
212- pub fn run < C : Codec > ( & mut self , img : & Image , sec_allowed : f64 ) -> Result < ( ) > {
213- let ( encoded, t_encode) = timeit ( || C :: encode ( img) ) ;
233+ pub fn run < C : Codec > ( & mut self , codec : & C , img : & Image , sec_allowed : f64 ) -> Result < ( ) > {
234+ let ( encoded, t_encode) = timeit ( || codec . encode ( img) ) ;
214235 let encoded = encoded?;
215- let ( decoded, t_decode) = timeit ( || C :: decode ( encoded. as_ref ( ) , img) ) ;
236+ let ( decoded, t_decode) = timeit ( || codec . decode ( encoded. as_ref ( ) , img) ) ;
216237 let decoded = decoded?;
217238 let roundtrip = decoded. as_ref ( ) == img. data . as_slice ( ) ;
218- if C :: name ( ) == "qoi-rust" {
219- assert ! ( roundtrip, "{}: decoded data doesn't roundtrip" , C :: name( ) ) ;
239+ if codec . name ( ) == "qoi-rust" {
240+ assert ! ( roundtrip, "{}: decoded data doesn't roundtrip" , codec . name( ) ) ;
220241 } else {
221- ensure ! ( roundtrip, "{}: decoded data doesn't roundtrip" , C :: name( ) ) ;
242+ ensure ! ( roundtrip, "{}: decoded data doesn't roundtrip" , codec . name( ) ) ;
222243 }
223244
224245 let n_encode = ( sec_allowed / 2. / t_encode. as_secs_f64 ( ) ) . max ( 2. ) . ceil ( ) as usize ;
225246 let mut encode_tm = Vec :: with_capacity ( n_encode) ;
226247 for _ in 0 ..n_encode {
227- encode_tm. push ( timeit ( || C :: encode ( img) ) . 1 ) ;
248+ encode_tm. push ( timeit ( || codec . encode ( img) ) . 1 ) ;
228249 }
229250 let encode_sec = encode_tm. iter ( ) . map ( Duration :: as_secs_f64) . collect ( ) ;
230251
231252 let n_decode = ( sec_allowed / 2. / t_decode. as_secs_f64 ( ) ) . max ( 2. ) . ceil ( ) as usize ;
232253 let mut decode_tm = Vec :: with_capacity ( n_decode) ;
233254 for _ in 0 ..n_decode {
234- decode_tm. push ( timeit ( || C :: decode ( encoded. as_ref ( ) , img) ) . 1 ) ;
255+ decode_tm. push ( timeit ( || codec . decode ( encoded. as_ref ( ) , img) ) . 1 ) ;
235256 }
236257 let decode_sec = decode_tm. iter ( ) . map ( Duration :: as_secs_f64) . collect ( ) ;
237258
238- self . results . push ( BenchResult :: new ( C :: name ( ) , decode_sec, encode_sec) ) ;
259+ self . results . push ( BenchResult :: new ( codec . name ( ) , decode_sec, encode_sec) ) ;
239260 Ok ( ( ) )
240261 }
241262
@@ -362,7 +383,7 @@ impl BenchTotals {
362383 }
363384}
364385
365- fn bench_png ( filename : & Path , seconds : f64 , use_median : bool ) -> Result < ImageBench > {
386+ fn bench_png ( filename : & Path , seconds : f64 , use_median : bool , stream : bool ) -> Result < ImageBench > {
366387 let f = filename. to_string_lossy ( ) ;
367388 let img = Image :: read_png ( filename) . context ( format ! ( "error reading PNG file: {}" , f) ) ?;
368389 let size_png_kb = fs:: metadata ( filename) ?. len ( ) / 1024 ;
@@ -373,16 +394,18 @@ fn bench_png(filename: &Path, seconds: f64, use_median: bool) -> Result<ImageBen
373394 f, img. width, img. height, img. channels, size_png_kb, size_mb_raw, mpixels
374395 ) ;
375396 let mut bench = ImageBench :: new ( & img) ;
376- bench. run :: < CodecQoiC > ( & img, seconds) ?;
377- bench. run :: < CodecQoiRust > ( & img, seconds) ?;
397+ bench. run ( & CodecQoiC , & img, seconds) ?;
398+ bench. run ( & CodecQoiRust { stream } , & img, seconds) ?;
378399 bench. report ( use_median) ;
379400 Ok ( bench)
380401}
381402
382- fn bench_suite ( files : & [ PathBuf ] , seconds : f64 , use_median : bool , fancy : bool ) -> Result < ( ) > {
403+ fn bench_suite (
404+ files : & [ PathBuf ] , seconds : f64 , use_median : bool , fancy : bool , stream : bool ,
405+ ) -> Result < ( ) > {
383406 let mut totals = BenchTotals :: new ( ) ;
384407 for file in files {
385- match bench_png ( file, seconds, use_median) {
408+ match bench_png ( file, seconds, use_median, stream ) {
386409 Ok ( res) => totals. update ( & res) ,
387410 Err ( err) => eprintln ! ( "{:?}" , err) ,
388411 }
@@ -405,15 +428,18 @@ struct Args {
405428 #[ structopt( short, long) ]
406429 average : bool ,
407430 /// Simple totals, no fancy tables.
408- #[ structopt( short , long) ]
431+ #[ structopt( long) ]
409432 simple : bool ,
433+ /// Use stream API for qoi-rust.
434+ #[ structopt( long) ]
435+ stream : bool ,
410436}
411437
412438fn main ( ) -> Result < ( ) > {
413439 let args = <Args as StructOpt >:: from_args ( ) ;
414440 ensure ! ( !args. paths. is_empty( ) , "no input paths given" ) ;
415441 let files = find_pngs ( & args. paths ) ?;
416442 ensure ! ( !files. is_empty( ) , "no PNG files found in given paths" ) ;
417- bench_suite ( & files, args. seconds , !args. average , !args. simple ) ?;
443+ bench_suite ( & files, args. seconds , !args. average , !args. simple , args . stream ) ?;
418444 Ok ( ( ) )
419445}
0 commit comments