dwebp.c (15621B)
1 // Copyright 2010 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // Command-line tool for decoding a WebP image. 11 // 12 // Author: Skal (pascal.massimino@gmail.com) 13 14 #include <assert.h> 15 #include <stdio.h> 16 #include <stdlib.h> 17 #include <string.h> 18 19 #ifdef HAVE_CONFIG_H 20 #include "webp/config.h" 21 #endif 22 23 #include "../examples/example_util.h" 24 #include "../imageio/image_enc.h" 25 #include "../imageio/webpdec.h" 26 #include "./stopwatch.h" 27 #include "./unicode.h" 28 #include "webp/decode.h" 29 #include "webp/types.h" 30 31 static int verbose = 0; 32 static int quiet = 0; 33 #ifndef WEBP_DLL 34 #ifdef __cplusplus 35 extern "C" { 36 #endif 37 38 extern void* VP8GetCPUInfo; // opaque forward declaration. 39 40 #ifdef __cplusplus 41 } // extern "C" 42 #endif 43 #endif // WEBP_DLL 44 45 46 static int SaveOutput(const WebPDecBuffer* const buffer, 47 WebPOutputFileFormat format, const char* const out_file) { 48 const int use_stdout = (out_file != NULL) && !WSTRCMP(out_file, "-"); 49 int ok = 1; 50 Stopwatch stop_watch; 51 52 if (verbose) { 53 StopwatchReset(&stop_watch); 54 } 55 ok = WebPSaveImage(buffer, format, out_file); 56 57 if (ok) { 58 if (!quiet) { 59 if (use_stdout) { 60 fprintf(stderr, "Saved to stdout\n"); 61 } else { 62 WFPRINTF(stderr, "Saved file %s\n", (const W_CHAR*)out_file); 63 } 64 } 65 if (verbose) { 66 const double write_time = StopwatchReadAndReset(&stop_watch); 67 fprintf(stderr, "Time to write output: %.3fs\n", write_time); 68 } 69 } else { 70 if (use_stdout) { 71 fprintf(stderr, "Error writing to stdout !!\n"); 72 } else { 73 WFPRINTF(stderr, "Error writing file %s !!\n", (const W_CHAR*)out_file); 74 } 75 } 76 return ok; 77 } 78 79 static void Help(void) { 80 printf("Usage: dwebp in_file [options] [-o out_file]\n\n" 81 "Decodes the WebP image file to PNG format [Default].\n" 82 "Note: Animated WebP files are not supported.\n\n" 83 "Use following options to convert into alternate image formats:\n" 84 " -pam ......... save the raw RGBA samples as a color PAM\n" 85 " -ppm ......... save the raw RGB samples as a color PPM\n" 86 " -bmp ......... save as uncompressed BMP format\n" 87 " -tiff ........ save as uncompressed TIFF format\n" 88 " -pgm ......... save the raw YUV samples as a grayscale PGM\n" 89 " file with IMC4 layout\n" 90 " -yuv ......... save the raw YUV samples in flat layout\n" 91 "\n" 92 " Other options are:\n" 93 " -version ..... print version number and exit\n" 94 " -nofancy ..... don't use the fancy YUV420 upscaler\n" 95 " -nofilter .... disable in-loop filtering\n" 96 " -nodither .... disable dithering\n" 97 " -dither <d> .. dithering strength (in 0..100)\n" 98 " -alpha_dither use alpha-plane dithering if needed\n" 99 " -mt .......... use multi-threading\n" 100 " -crop <x> <y> <w> <h> ... crop output with the given rectangle\n" 101 " -resize <w> <h> ......... resize output (*after* any cropping)\n" 102 " -flip ........ flip the output vertically\n" 103 " -alpha ....... only save the alpha plane\n" 104 " -incremental . use incremental decoding (useful for tests)\n" 105 " -h ........... this help message\n" 106 " -v ........... verbose (e.g. print encoding/decoding times)\n" 107 " -quiet ....... quiet mode, don't print anything\n" 108 #ifndef WEBP_DLL 109 " -noasm ....... disable all assembly optimizations\n" 110 #endif 111 ); 112 } 113 114 static const char* const kFormatType[] = { 115 "unspecified", "lossy", "lossless" 116 }; 117 118 static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config, 119 WebPOutputFileFormat format, 120 int use_external_memory) { 121 uint8_t* external_buffer = NULL; 122 WebPDecBuffer* const output_buffer = &config->output; 123 int w = config->input.width; 124 int h = config->input.height; 125 if (config->options.use_scaling) { 126 w = config->options.scaled_width; 127 h = config->options.scaled_height; 128 } else if (config->options.use_cropping) { 129 w = config->options.crop_width; 130 h = config->options.crop_height; 131 } 132 if (format >= RGB && format <= rgbA_4444) { 133 const int bpp = (format == RGB || format == BGR) ? 3 134 : (format == RGBA_4444 || format == rgbA_4444 || 135 format == RGB_565) ? 2 136 : 4; 137 uint32_t stride = bpp * w + 7; // <- just for exercising 138 external_buffer = (uint8_t*)WebPMalloc(stride * h); 139 if (external_buffer == NULL) return NULL; 140 output_buffer->u.RGBA.stride = stride; 141 output_buffer->u.RGBA.size = stride * h; 142 output_buffer->u.RGBA.rgba = external_buffer; 143 } else { // YUV and YUVA 144 const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace); 145 uint8_t* tmp; 146 uint32_t stride = w + 3; 147 uint32_t uv_stride = (w + 1) / 2 + 13; 148 uint32_t total_size = stride * h * (has_alpha ? 2 : 1) 149 + 2 * uv_stride * (h + 1) / 2; 150 assert(format >= YUV && format <= YUVA); 151 external_buffer = (uint8_t*)WebPMalloc(total_size); 152 if (external_buffer == NULL) return NULL; 153 tmp = external_buffer; 154 output_buffer->u.YUVA.y = tmp; 155 output_buffer->u.YUVA.y_stride = stride; 156 output_buffer->u.YUVA.y_size = stride * h; 157 tmp += output_buffer->u.YUVA.y_size; 158 if (has_alpha) { 159 output_buffer->u.YUVA.a = tmp; 160 output_buffer->u.YUVA.a_stride = stride; 161 output_buffer->u.YUVA.a_size = stride * h; 162 tmp += output_buffer->u.YUVA.a_size; 163 } else { 164 output_buffer->u.YUVA.a = NULL; 165 output_buffer->u.YUVA.a_stride = 0; 166 } 167 output_buffer->u.YUVA.u = tmp; 168 output_buffer->u.YUVA.u_stride = uv_stride; 169 output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2; 170 tmp += output_buffer->u.YUVA.u_size; 171 172 output_buffer->u.YUVA.v = tmp; 173 output_buffer->u.YUVA.v_stride = uv_stride; 174 output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2; 175 tmp += output_buffer->u.YUVA.v_size; 176 assert(tmp <= external_buffer + total_size); 177 } 178 output_buffer->is_external_memory = use_external_memory; 179 return external_buffer; 180 } 181 182 // Returns EXIT_SUCCESS on success, EXIT_FAILURE on failure. 183 int main(int argc, const char* argv[]) { 184 int ok = 0; 185 const char* in_file = NULL; 186 const char* out_file = NULL; 187 188 WebPDecoderConfig config; 189 WebPDecBuffer* const output_buffer = &config.output; 190 WebPBitstreamFeatures* const bitstream = &config.input; 191 WebPOutputFileFormat format = PNG; 192 uint8_t* external_buffer = NULL; 193 int use_external_memory = 0; 194 const uint8_t* data = NULL; 195 196 int incremental = 0; 197 int c; 198 199 INIT_WARGV(argc, argv); 200 201 if (!WebPInitDecoderConfig(&config)) { 202 fprintf(stderr, "Library version mismatch!\n"); 203 FREE_WARGV_AND_RETURN(EXIT_FAILURE); 204 } 205 206 for (c = 1; c < argc; ++c) { 207 int parse_error = 0; 208 if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { 209 Help(); 210 FREE_WARGV_AND_RETURN(EXIT_SUCCESS); 211 } else if (!strcmp(argv[c], "-o") && c < argc - 1) { 212 out_file = (const char*)GET_WARGV(argv, ++c); 213 } else if (!strcmp(argv[c], "-alpha")) { 214 format = ALPHA_PLANE_ONLY; 215 } else if (!strcmp(argv[c], "-nofancy")) { 216 config.options.no_fancy_upsampling = 1; 217 } else if (!strcmp(argv[c], "-nofilter")) { 218 config.options.bypass_filtering = 1; 219 } else if (!strcmp(argv[c], "-pam")) { 220 format = PAM; 221 } else if (!strcmp(argv[c], "-ppm")) { 222 format = PPM; 223 } else if (!strcmp(argv[c], "-bmp")) { 224 format = BMP; 225 } else if (!strcmp(argv[c], "-tiff")) { 226 format = TIFF; 227 } else if (!strcmp(argv[c], "-quiet")) { 228 quiet = 1; 229 } else if (!strcmp(argv[c], "-version")) { 230 const int version = WebPGetDecoderVersion(); 231 printf("%d.%d.%d\n", 232 (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff); 233 FREE_WARGV_AND_RETURN(EXIT_SUCCESS); 234 } else if (!strcmp(argv[c], "-pgm")) { 235 format = PGM; 236 } else if (!strcmp(argv[c], "-yuv")) { 237 format = RAW_YUV; 238 } else if (!strcmp(argv[c], "-pixel_format") && c < argc - 1) { 239 const char* const fmt = argv[++c]; 240 if (!strcmp(fmt, "RGB")) format = RGB; 241 else if (!strcmp(fmt, "RGBA")) format = RGBA; 242 else if (!strcmp(fmt, "BGR")) format = BGR; 243 else if (!strcmp(fmt, "BGRA")) format = BGRA; 244 else if (!strcmp(fmt, "ARGB")) format = ARGB; 245 else if (!strcmp(fmt, "RGBA_4444")) format = RGBA_4444; 246 else if (!strcmp(fmt, "RGB_565")) format = RGB_565; 247 else if (!strcmp(fmt, "rgbA")) format = rgbA; 248 else if (!strcmp(fmt, "bgrA")) format = bgrA; 249 else if (!strcmp(fmt, "Argb")) format = Argb; 250 else if (!strcmp(fmt, "rgbA_4444")) format = rgbA_4444; 251 else if (!strcmp(fmt, "YUV")) format = YUV; 252 else if (!strcmp(fmt, "YUVA")) format = YUVA; 253 else { 254 fprintf(stderr, "Can't parse pixel_format %s\n", fmt); 255 parse_error = 1; 256 } 257 } else if (!strcmp(argv[c], "-external_memory") && c < argc - 1) { 258 use_external_memory = ExUtilGetInt(argv[++c], 0, &parse_error); 259 parse_error |= (use_external_memory > 2 || use_external_memory < 0); 260 if (parse_error) { 261 fprintf(stderr, "Can't parse 'external_memory' value %s\n", argv[c]); 262 } 263 } else if (!strcmp(argv[c], "-mt")) { 264 config.options.use_threads = 1; 265 } else if (!strcmp(argv[c], "-alpha_dither")) { 266 config.options.alpha_dithering_strength = 100; 267 } else if (!strcmp(argv[c], "-nodither")) { 268 config.options.dithering_strength = 0; 269 } else if (!strcmp(argv[c], "-dither") && c < argc - 1) { 270 config.options.dithering_strength = 271 ExUtilGetInt(argv[++c], 0, &parse_error); 272 } else if (!strcmp(argv[c], "-crop") && c < argc - 4) { 273 config.options.use_cropping = 1; 274 config.options.crop_left = ExUtilGetInt(argv[++c], 0, &parse_error); 275 config.options.crop_top = ExUtilGetInt(argv[++c], 0, &parse_error); 276 config.options.crop_width = ExUtilGetInt(argv[++c], 0, &parse_error); 277 config.options.crop_height = ExUtilGetInt(argv[++c], 0, &parse_error); 278 } else if ((!strcmp(argv[c], "-scale") || !strcmp(argv[c], "-resize")) && 279 c < argc - 2) { // '-scale' is left for compatibility 280 config.options.use_scaling = 1; 281 config.options.scaled_width = ExUtilGetInt(argv[++c], 0, &parse_error); 282 config.options.scaled_height = ExUtilGetInt(argv[++c], 0, &parse_error); 283 } else if (!strcmp(argv[c], "-flip")) { 284 config.options.flip = 1; 285 } else if (!strcmp(argv[c], "-v")) { 286 verbose = 1; 287 #ifndef WEBP_DLL 288 } else if (!strcmp(argv[c], "-noasm")) { 289 VP8GetCPUInfo = NULL; 290 #endif 291 } else if (!strcmp(argv[c], "-incremental")) { 292 incremental = 1; 293 } else if (!strcmp(argv[c], "--")) { 294 if (c < argc - 1) in_file = (const char*)GET_WARGV(argv, ++c); 295 break; 296 } else if (argv[c][0] == '-') { 297 fprintf(stderr, "Unknown option '%s'\n", argv[c]); 298 Help(); 299 FREE_WARGV_AND_RETURN(EXIT_FAILURE); 300 } else { 301 in_file = (const char*)GET_WARGV(argv, c); 302 } 303 304 if (parse_error) { 305 Help(); 306 FREE_WARGV_AND_RETURN(EXIT_FAILURE); 307 } 308 } 309 310 if (in_file == NULL) { 311 fprintf(stderr, "missing input file!!\n"); 312 Help(); 313 FREE_WARGV_AND_RETURN(EXIT_FAILURE); 314 } 315 316 if (quiet) verbose = 0; 317 318 { 319 VP8StatusCode status = VP8_STATUS_OK; 320 size_t data_size = 0; 321 if (!LoadWebP(in_file, &data, &data_size, bitstream)) { 322 FREE_WARGV_AND_RETURN(EXIT_FAILURE); 323 } 324 325 switch (format) { 326 case PNG: 327 #ifdef HAVE_WINCODEC_H 328 output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; 329 #else 330 output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; 331 #endif 332 break; 333 case PAM: 334 output_buffer->colorspace = MODE_RGBA; 335 break; 336 case PPM: 337 output_buffer->colorspace = MODE_RGB; // drops alpha for PPM 338 break; 339 case BMP: 340 output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR; 341 break; 342 case TIFF: 343 output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB; 344 break; 345 case PGM: 346 case RAW_YUV: 347 output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV; 348 break; 349 case ALPHA_PLANE_ONLY: 350 output_buffer->colorspace = MODE_YUVA; 351 break; 352 // forced modes: 353 case RGB: output_buffer->colorspace = MODE_RGB; break; 354 case RGBA: output_buffer->colorspace = MODE_RGBA; break; 355 case BGR: output_buffer->colorspace = MODE_BGR; break; 356 case BGRA: output_buffer->colorspace = MODE_BGRA; break; 357 case ARGB: output_buffer->colorspace = MODE_ARGB; break; 358 case RGBA_4444: output_buffer->colorspace = MODE_RGBA_4444; break; 359 case RGB_565: output_buffer->colorspace = MODE_RGB_565; break; 360 case rgbA: output_buffer->colorspace = MODE_rgbA; break; 361 case bgrA: output_buffer->colorspace = MODE_bgrA; break; 362 case Argb: output_buffer->colorspace = MODE_Argb; break; 363 case rgbA_4444: output_buffer->colorspace = MODE_rgbA_4444; break; 364 case YUV: output_buffer->colorspace = MODE_YUV; break; 365 case YUVA: output_buffer->colorspace = MODE_YUVA; break; 366 default: goto Exit; 367 } 368 369 if (use_external_memory > 0 && format >= RGB) { 370 external_buffer = AllocateExternalBuffer(&config, format, 371 use_external_memory); 372 if (external_buffer == NULL) goto Exit; 373 } 374 375 { 376 Stopwatch stop_watch; 377 if (verbose) StopwatchReset(&stop_watch); 378 379 if (incremental) { 380 status = DecodeWebPIncremental(data, data_size, &config); 381 } else { 382 status = DecodeWebP(data, data_size, &config); 383 } 384 if (verbose) { 385 const double decode_time = StopwatchReadAndReset(&stop_watch); 386 fprintf(stderr, "Time to decode picture: %.3fs\n", decode_time); 387 } 388 } 389 390 ok = (status == VP8_STATUS_OK); 391 if (!ok) { 392 PrintWebPError(in_file, status); 393 goto Exit; 394 } 395 } 396 397 if (out_file != NULL) { 398 if (!quiet) { 399 WFPRINTF(stderr, "Decoded %s.", (const W_CHAR*)in_file); 400 fprintf(stderr, " Dimensions: %d x %d %s. Format: %s. Now saving...\n", 401 output_buffer->width, output_buffer->height, 402 bitstream->has_alpha ? " (with alpha)" : "", 403 kFormatType[bitstream->format]); 404 } 405 ok = SaveOutput(output_buffer, format, out_file); 406 } else { 407 if (!quiet) { 408 WFPRINTF(stderr, "File %s can be decoded ", (const W_CHAR*)in_file); 409 fprintf(stderr, "(dimensions: %d x %d %s. Format: %s).\n", 410 output_buffer->width, output_buffer->height, 411 bitstream->has_alpha ? " (with alpha)" : "", 412 kFormatType[bitstream->format]); 413 fprintf(stderr, "Nothing written; " 414 "use -o flag to save the result as e.g. PNG.\n"); 415 } 416 } 417 Exit: 418 WebPFreeDecBuffer(output_buffer); 419 WebPFree((void*)external_buffer); 420 WebPFree((void*)data); 421 FREE_WARGV_AND_RETURN(ok ? EXIT_SUCCESS : EXIT_FAILURE); 422 } 423 424 //------------------------------------------------------------------------------