1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28/* this small program is used to measure the performance of libjpeg decompression 29 * algorithm... 30 */ 31 32#include <time.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <errno.h> 37#include <unistd.h> 38#include <sys/time.h> 39#include "jpeglib.h" 40#include <setjmp.h> 41#ifdef HAVE_ANDROID_OS 42#include <hardware_legacy/qemu_tracing.h> 43#endif 44 45#define USE_STDIO 46 47#define CHUNK 32768 48 49typedef struct { 50 struct jpeg_source_mgr jpeg_mgr; 51 char* base; 52 char* cursor; 53 char* end; 54} SourceMgrRec, *SourceMgr; 55 56static void 57_source_init_source(j_decompress_ptr cinfo) 58{ 59 SourceMgr src = (SourceMgr) cinfo->src; 60 61 src->jpeg_mgr.next_input_byte = (unsigned char*)src->base, 62 src->jpeg_mgr.bytes_in_buffer = src->end - src->base; 63} 64 65static int 66_source_fill_input_buffer(j_decompress_ptr cinfo) 67{ 68 SourceMgr src = (SourceMgr) cinfo->src; 69 70 cinfo->err->error_exit((j_common_ptr)cinfo); 71 return FALSE; 72} 73 74static void 75_source_skip_input_data(j_decompress_ptr cinfo, long num_bytes) 76{ 77 SourceMgr src = (SourceMgr) cinfo->src; 78 79 if (src->jpeg_mgr.next_input_byte + num_bytes > (unsigned char*)src->end ) { 80 cinfo->err->error_exit((j_common_ptr)cinfo); 81 } 82 83 src->jpeg_mgr.next_input_byte += num_bytes; 84 src->jpeg_mgr.bytes_in_buffer -= num_bytes; 85} 86 87static int 88_source_resync_to_restart( j_decompress_ptr cinfo, int desired) 89{ 90 SourceMgr src = (SourceMgr) cinfo->src; 91 92 src->jpeg_mgr.next_input_byte = (unsigned char*)src->base; 93 src->jpeg_mgr.bytes_in_buffer = src->end - src->base; 94 return TRUE; 95} 96 97static void 98_source_term_source(j_decompress_ptr cinfo) 99{ 100 // nothing to do 101} 102 103static void 104_source_init( SourceMgr src, char* base, long size ) 105{ 106 src->base = base; 107 src->cursor = base; 108 src->end = base + size; 109 110 src->jpeg_mgr.init_source = _source_init_source; 111 src->jpeg_mgr.fill_input_buffer = _source_fill_input_buffer; 112 src->jpeg_mgr.skip_input_data = _source_skip_input_data; 113 src->jpeg_mgr.resync_to_restart = _source_resync_to_restart; 114 src->jpeg_mgr.term_source = _source_term_source; 115} 116 117 118typedef struct { 119 struct jpeg_error_mgr jpeg_mgr; 120 jmp_buf jumper; 121 int volatile error; 122 123} ErrorMgrRec, *ErrorMgr; 124 125static void _error_exit(j_common_ptr cinfo) 126{ 127 ErrorMgr error = (ErrorMgr) cinfo->err; 128 129 (*error->jpeg_mgr.output_message) (cinfo); 130 131 /* Let the memory manager delete any temp files before we die */ 132 longjmp(error->jumper, -1); 133} 134 135#ifdef USE_STDIO 136int decompress(FILE* input_file, int dct_method, int disable_rgb) 137#else 138int decompress(char* data, long fsize) 139#endif 140{ 141 ErrorMgrRec errmgr; 142 SourceMgrRec sourcemgr; 143 struct jpeg_decompress_struct cinfo; 144 int volatile error = 0; 145 jmp_buf jumper; 146 int isRGB; 147 char* pixels; 148 JSAMPLE* temprow; 149 150 memset( &cinfo, 0, sizeof(cinfo) ); 151 memset( &errmgr, 0, sizeof(errmgr) ); 152 jpeg_create_decompress(&cinfo); 153 cinfo.err = jpeg_std_error(&errmgr.jpeg_mgr); 154#if 0 155 errmgr.jpeg_mgr.error_exit = _error_exit; 156 errmgr.error = 0; 157#endif 158 159 if (setjmp(errmgr.jumper) != 0) { 160 fprintf(stderr, "returning error from jpeglib ---\n" ); 161 goto Exit; 162 } 163 164#ifdef USE_STDIO 165 /* Specify data source for decompression */ 166 jpeg_stdio_src(&cinfo, input_file); 167#else 168 _source_init( &sourcemgr, data, fsize ); 169 cinfo.src = &sourcemgr.jpeg_mgr; 170#endif 171 172 jpeg_read_header(&cinfo, 1); 173 174 if (3 == cinfo.num_components && JCS_RGB == cinfo.out_color_space) 175 isRGB = 1; 176 else if (1 == cinfo.num_components && JCS_GRAYSCALE == cinfo.out_color_space) 177 isRGB = 0; // could use Index8 config if we want... 178 else { 179 fprintf( stderr, "unsupported jpeg colorspace %d with %d components\n", 180 cinfo.jpeg_color_space, cinfo.num_components ); 181 goto Exit; 182 } 183 184 cinfo.dct_method = dct_method; 185 if (disable_rgb) 186 cinfo.out_color_space = JCS_YCbCr; 187 188 jpeg_start_decompress(&cinfo); 189 190 temprow = calloc( cinfo.num_components * cinfo.output_width, sizeof(JSAMPLE) ); 191 192 { 193 unsigned y; 194 for (y = 0; y < cinfo.output_height; y++) { 195 JSAMPLE* rowptr = temprow; 196 (void)jpeg_read_scanlines(&cinfo, &rowptr, 1); 197 } 198 } 199 jpeg_finish_decompress(&cinfo); 200 201 free( temprow ); 202Exit: 203 jpeg_destroy_decompress(&cinfo); 204 return error; 205} 206 207 208#define DEFAULT_REPEAT 10 209 210static void usage(void) 211{ 212 fprintf(stderr, "usage: test_jpeg [options] filename.jpg [filename2.jpg ...]\n" ); 213 fprintf(stderr, "options: -r NN repeat count (default %d)\n", DEFAULT_REPEAT ); 214 fprintf(stderr, " -d N idct method (0=default, 1=fastest, 2=slow, 3=float)\n" ); 215 fprintf(stderr, " -C no RGB color conversion (YCbCr instead)\n" ); 216 exit(1); 217} 218 219static double 220get_time_usec( void ) 221{ 222#ifdef HAVE_ANDROID_OS 223 struct timespec ts; 224 225 if ( clock_gettime( CLOCK_MONOTONIC, &ts ) < 0 ) 226 fprintf(stderr, "clock_gettime: %s\n", strerror(errno) ); 227 228 return ts.tv_sec*1e6 + ts.tv_nsec*1e-3; 229#else 230 struct timeval tv; 231 if (gettimeofday( &tv, NULL ) < 0) 232 fprintf(stderr, "gettimeofday: %s\n", strerror(errno) ); 233 234 return tv.tv_sec*1000000. + tv.tv_usec*1.0; 235#endif 236} 237 238 239int main( int argc, char** argv ) 240{ 241 FILE* f; 242 int repeat_count = DEFAULT_REPEAT; 243 int dct_method = JDCT_DEFAULT; 244 int disable_rgb = 0; 245 double usec0, usec1; 246 247 if (argc < 2) 248 usage(); 249 250 for ( ; argc > 1 && argv[1][0] == '-'; argc--, argv++) { 251 const char* arg = &argv[1][1]; 252 switch (arg[0]) { 253 case 'r': 254 if (arg[1] == 0) { 255 if (argc < 3) 256 usage(); 257 arg = argv[2]; 258 argc--; 259 argv++; 260 } else 261 arg += 1; 262 263 repeat_count = strtol(arg, NULL, 10); 264 265 if (repeat_count <= 0) 266 repeat_count = 1; 267 break; 268 269 case 'C': 270 disable_rgb = 1; 271 break; 272 273 case 'd': 274 if (arg[1] == 0) { 275 if (argc < 3) 276 usage(); 277 arg = argv[2]; 278 argc--; 279 argv++; 280 } else 281 arg += 1; 282 283 dct_method = strtol(arg, NULL, 10); 284 switch (dct_method) { 285 case 0: 286 dct_method = JDCT_DEFAULT; 287 break; 288 case 1: 289 dct_method = JDCT_IFAST; 290 break; 291 case 2: 292 dct_method = JDCT_ISLOW; 293 break; 294 case 3: 295 dct_method = JDCT_FLOAT; 296 break; 297 default: 298 usage(); 299 } 300 break; 301 302 default: 303 usage(); 304 } 305 } 306 307 for ( ; argc > 1; argc--, argv++ ) 308 { 309 long fsize; 310 char* data; 311 FILE* f = fopen( argv[1], "rb" ); 312 int rr; 313 314 if (f == NULL) { 315 fprintf(stderr, "could not open '%s': %s\n", argv[1], strerror(errno) ); 316 continue; 317 } 318 319 fseek( f, 0, SEEK_END ); 320 fsize = ftell(f); 321 fseek( f, 0, SEEK_SET ); 322 323 usec0 = get_time_usec(); 324#ifdef HAVE_ANDROID_OS 325 qemu_start_tracing(); 326#endif 327#ifdef USE_STDIO 328 for ( rr = repeat_count; rr > 0; rr-- ) { 329 fseek( f, 0, SEEK_SET ); 330 decompress(f, dct_method, disable_rgb); 331 } 332 fclose( f ); 333#else 334 335 data = malloc( fsize ); 336 if (data == NULL) { 337 if (fsize > 0) 338 fprintf(stderr, "could not allocate %ld bytes to load '%s'\n", fsize, argv[1] ); 339 fclose(f); 340 continue; 341 } 342 fread( data, 1, fsize, f ); 343 fclose(f); 344 345 usec1 = get_time_usec() - usec0; 346 printf( "compressed load: %10.2f ms (%ld bytes)\n", usec1*1e-3, fsize ); 347 348 usec0 = get_time_usec(); 349 for ( rr = repeat_count; rr > 0; rr -- ) 350 { 351 decompress( data, fsize ); 352 } 353 free( data ); 354#endif 355#ifdef HAVE_ANDROID_OS 356 qemu_stop_tracing(); 357#endif 358 usec1 = get_time_usec() - usec0; 359 printf( "decompression took: %10.3f ms (%.2f KB/s, %d passes)\n", usec1/1e3, fsize*(1e6/1024)*repeat_count/usec1, repeat_count ); 360 } 361 return 0; 362} 363