2222
2323// ----
2424
25+ bool g_multithreaded = false;
26+ bool g_print_decode_time = false;
27+
28+ // ----
29+
30+ typedef struct worker_data_struct {
31+ // Request.
32+ const uint8_t * src_ptr ;
33+ size_t src_len ;
34+ qoir_decode_options * options ;
35+ int32_t y0 ;
36+ int32_t y1 ;
37+
38+ // Response.
39+ const char * status_message ;
40+ } worker_data ;
41+
42+ int //
43+ work (void * data ) {
44+ worker_data * wd = (worker_data * )data ;
45+ qoir_decode_options opts = {0 };
46+ memcpy (& opts , wd -> options , sizeof (* wd -> options ));
47+ if (!opts .pixbuf .data ) {
48+ wd -> status_message =
49+ "main: inconsistent qoir_decode_options.pixbuf.data field\n" ;
50+ return 0 ;
51+ }
52+ if (opts .src_clip_rectangle .y0 < wd -> y0 ) {
53+ opts .src_clip_rectangle .y0 = wd -> y0 ;
54+ }
55+ if (opts .src_clip_rectangle .y1 > wd -> y1 ) {
56+ opts .src_clip_rectangle .y1 = wd -> y1 ;
57+ }
58+
59+ qoir_decode_result res = qoir_decode (wd -> src_ptr , wd -> src_len , & opts );
60+ if (res .owned_memory ) {
61+ free (res .owned_memory );
62+ wd -> status_message =
63+ "main: inconsistent qoir_decode_result.owned_memory field\n" ;
64+ } else {
65+ wd -> status_message = res .status_message ;
66+ }
67+ return 0 ;
68+ }
69+
70+ qoir_decode_result //
71+ multithreaded_decode ( //
72+ const uint8_t * src_ptr , //
73+ const size_t src_len , //
74+ const qoir_decode_options * options ) {
75+ qoir_decode_pixel_configuration_result config =
76+ qoir_decode_pixel_configuration (src_ptr , src_len );
77+ if (config .status_message ) {
78+ qoir_decode_result res = {0 };
79+ res .status_message = config .status_message ;
80+ return res ;
81+ }
82+
83+ uint32_t height_in_tiles =
84+ qoir_calculate_number_of_tiles_1d (config .dst_pixcfg .height_in_pixels );
85+ if (height_in_tiles <= 1 ) {
86+ return qoir_decode (src_ptr , src_len , options );
87+ }
88+
89+ uint64_t pixbuf_len = 4 * (uint64_t )config .dst_pixcfg .width_in_pixels *
90+ (uint64_t )config .dst_pixcfg .height_in_pixels ;
91+ if (pixbuf_len > SIZE_MAX ) {
92+ qoir_decode_result res = {0 };
93+ res .status_message =
94+ qoir_status_message__error_unsupported_pixbuf_dimensions ;
95+ return res ;
96+ }
97+
98+ qoir_decode_options opts = {0 };
99+ if (options ) {
100+ memcpy (& opts , options , sizeof (* options ));
101+ }
102+
103+ opts .pixbuf .pixcfg .pixfmt = QOIR_PIXEL_FORMAT__BGRA_PREMUL ;
104+ opts .pixbuf .pixcfg .width_in_pixels = config .dst_pixcfg .width_in_pixels ;
105+ opts .pixbuf .pixcfg .height_in_pixels = config .dst_pixcfg .height_in_pixels ;
106+ opts .pixbuf .data = malloc (pixbuf_len );
107+ opts .pixbuf .stride_in_bytes = 4 * opts .pixbuf .pixcfg .width_in_pixels ;
108+ if (!opts .pixbuf .data ) {
109+ qoir_decode_result res = {0 };
110+ res .status_message = qoir_status_message__error_out_of_memory ;
111+ return res ;
112+ }
113+
114+ if (!opts .use_src_clip_rectangle ) {
115+ opts .use_src_clip_rectangle = true;
116+ opts .src_clip_rectangle =
117+ qoir_make_rectangle (0 , 0 , (int32_t )config .dst_pixcfg .width_in_pixels ,
118+ (int32_t )config .dst_pixcfg .height_in_pixels );
119+ }
120+
121+ const char * status_message = NULL ;
122+ uint32_t num_threads = height_in_tiles ;
123+ if (num_threads > 16 ) {
124+ num_threads = 16 ;
125+ }
126+ worker_data data [16 ] = {0 };
127+ SDL_Thread * threads [16 ] = {0 };
128+
129+ for (uint32_t i = 0 ; i < num_threads ; i ++ ) {
130+ data [i ].src_ptr = src_ptr ;
131+ data [i ].src_len = src_len ;
132+ data [i ].options = & opts ;
133+ data [i ].y0 = (((i + 0 ) * height_in_tiles ) / num_threads ) * QOIR_TILE_SIZE ;
134+ data [i ].y1 = (((i + 1 ) * height_in_tiles ) / num_threads ) * QOIR_TILE_SIZE ;
135+ threads [i ] = SDL_CreateThread (& work , "worker" , & data [i ]);
136+ if (!threads [i ]) {
137+ status_message = "main: could not create thread" ;
138+ }
139+ }
140+
141+ for (uint32_t i = 0 ; i < num_threads ; i ++ ) {
142+ if (threads [i ]) {
143+ SDL_WaitThread (threads [i ], NULL );
144+ if (!status_message ) {
145+ status_message = data [i ].status_message ;
146+ }
147+ }
148+ }
149+ if (status_message ) {
150+ free (opts .pixbuf .data );
151+ qoir_decode_result res = {0 };
152+ res .status_message = status_message ;
153+ return res ;
154+ }
155+ qoir_decode_result res = {0 };
156+ res .owned_memory = opts .pixbuf .data ;
157+ memcpy (& res .dst_pixbuf , & opts .pixbuf , sizeof (opts .pixbuf ));
158+ return res ;
159+ }
160+
161+ // ----
162+
25163SDL_Surface * //
26164load (const char * filename , void * * owned_memory ) {
27165 SDL_RWops * rw = SDL_RWFromFile (filename , "rb" );
@@ -60,9 +198,12 @@ load(const char* filename, void** owned_memory) {
60198 return NULL ;
61199 }
62200
201+ uint64_t now = SDL_GetPerformanceCounter ();
63202 qoir_decode_options opts = {0 };
64203 opts .pixfmt = QOIR_PIXEL_FORMAT__BGRA_PREMUL ;
65- qoir_decode_result decode = qoir_decode (ptr , len , & opts );
204+ qoir_decode_result decode = g_multithreaded
205+ ? multithreaded_decode (ptr , len , & opts )
206+ : qoir_decode (ptr , len , & opts );
66207 free (ptr );
67208 ptr = NULL ;
68209 len = 0 ;
@@ -72,6 +213,11 @@ load(const char* filename, void** owned_memory) {
72213 decode .status_message );
73214 return NULL ;
74215 }
216+ if (g_print_decode_time ) {
217+ uint64_t elapsed = SDL_GetPerformanceCounter () - now ;
218+ printf ("%" PRIu64 " microseconds to decode.\n" ,
219+ (elapsed * 1000000 ) / SDL_GetPerformanceFrequency ());
220+ }
75221
76222 * owned_memory = decode .owned_memory ;
77223 return SDL_CreateRGBSurfaceFrom (
@@ -116,8 +262,29 @@ draw(SDL_Window* window, SDL_Surface* surface) {
116262
117263int //
118264main (int argc , char * * argv ) {
119- if (argc != 2 ) {
120- fprintf (stderr , "usage: %s filename\n" , argv [0 ]);
265+ const char * filename = NULL ;
266+ bool too_many_args = false;
267+ for (int i = 1 ; i < argc ; i ++ ) {
268+ const char * arg = argv [i ];
269+ if ((arg [0 ] == '-' ) && (arg [1 ] == '-' )) {
270+ arg ++ ;
271+ }
272+ if (strcmp (arg , "-multithreaded" ) == 0 ) {
273+ g_multithreaded = true;
274+ continue ;
275+ } else if (strcmp (arg , "-print-decode-time" ) == 0 ) {
276+ g_print_decode_time = true;
277+ continue ;
278+ } else if (filename == NULL ) {
279+ filename = argv [i ];
280+ continue ;
281+ }
282+ too_many_args = true;
283+ }
284+
285+ if (too_many_args || (filename == NULL )) {
286+ fprintf (stderr , "usage: %s -print-decode-time -multithreaded filename\n" ,
287+ argv [0 ]);
121288 return 1 ;
122289 }
123290 if (SDL_Init (SDL_INIT_VIDEO ) < 0 ) {
@@ -132,7 +299,7 @@ main(int argc, char** argv) {
132299 return 1 ;
133300 }
134301 void * surface_owned_memory = NULL ;
135- SDL_Surface * surface = load (argv [ 1 ] , & surface_owned_memory );
302+ SDL_Surface * surface = load (filename , & surface_owned_memory );
136303 if (!surface ) {
137304 return 1 ;
138305 }
0 commit comments