| @@ -36,6 +36,12 @@ |
| 36 | 36 | | enum { DEF_GIF_DELAY = 75 }; |
| 37 | 37 | | #endif |
| 38 | 38 | | |
| 39 | + | #if HAVE_LIBWEBP |
| 40 | + | #include <webp/decode.h> |
| 41 | + | #include <webp/demux.h> |
| 42 | + | enum { DEF_WEBP_DELAY = 75 }; |
| 43 | + | #endif |
| 44 | + | |
| 39 | 45 | | float zoom_min; |
| 40 | 46 | | float zoom_max; |
| 41 | 47 | | |
| @@ -297,6 +303,135 @@ bool img_load_gif(img_t *img, const fileinfo_t *file) |
| 297 | 303 | | } |
| 298 | 304 | | #endif /* HAVE_LIBGIF */ |
| 299 | 305 | | |
| 306 | + | |
| 307 | + | #if HAVE_LIBWEBP |
| 308 | + | bool is_webp(const char *path) |
| 309 | + | { |
| 310 | + | /* The size (in bytes) of the largest amount of data required to verify a WebP image. */ |
| 311 | + | enum { max = 30 }; |
| 312 | + | const unsigned char fmt[max]; |
| 313 | + | bool ret = false; |
| 314 | + | FILE *f; |
| 315 | + | |
| 316 | + | if ((f = fopen(path, "rb")) != NULL) { |
| 317 | + | if (fread((unsigned char *) fmt, 1, max, f) == max) |
| 318 | + | ret = WebPGetInfo(fmt, max, NULL, NULL); |
| 319 | + | fclose(f); |
| 320 | + | } |
| 321 | + | return ret; |
| 322 | + | } |
| 323 | + | |
| 324 | + | /* fframe img |
| 325 | + | * NULL NULL = do nothing |
| 326 | + | * x NULL = load the first frame as an Imlib_Image |
| 327 | + | * NULL x = load all frames into img->multi. |
| 328 | + | */ |
| 329 | + | bool img_load_webp(const fileinfo_t *file, Imlib_Image *fframe, img_t *img) |
| 330 | + | { |
| 331 | + | FILE *webp_file; |
| 332 | + | WebPData data; |
| 333 | + | data.bytes = NULL; |
| 334 | + | |
| 335 | + | Imlib_Image im = NULL; |
| 336 | + | struct WebPAnimDecoderOptions opts; |
| 337 | + | WebPAnimDecoder *dec = NULL; |
| 338 | + | struct WebPAnimInfo info; |
| 339 | + | unsigned char *buf = NULL; |
| 340 | + | int ts; |
| 341 | + | const WebPDemuxer *demux; |
| 342 | + | WebPIterator iter; |
| 343 | + | unsigned long flags; |
| 344 | + | unsigned int delay; |
| 345 | + | bool err = false; |
| 346 | + | |
| 347 | + | if ((err = fframe == NULL && img == NULL)) |
| 348 | + | goto fail; |
| 349 | + | |
| 350 | + | if ((err = (webp_file = fopen(file->path, "rb")) == NULL)) { |
| 351 | + | error(0, 0, "%s: Error opening webp image", file->name); |
| 352 | + | goto fail; |
| 353 | + | } |
| 354 | + | fseek(webp_file, 0L, SEEK_END); |
| 355 | + | data.size = ftell(webp_file); |
| 356 | + | rewind(webp_file); |
| 357 | + | data.bytes = emalloc(data.size); |
| 358 | + | if ((err = fread((unsigned char *)data.bytes, 1, data.size, webp_file) != data.size)) { |
| 359 | + | error(0, 0, "%s: Error reading webp image", file->name); |
| 360 | + | goto fail; |
| 361 | + | } |
| 362 | + | |
| 363 | + | /* Setup the WebP Animation Decoder */ |
| 364 | + | if ((err = !WebPAnimDecoderOptionsInit(&opts))) { |
| 365 | + | error(0, 0, "%s: WebP library version mismatch", file->name); |
| 366 | + | goto fail; |
| 367 | + | } |
| 368 | + | opts.color_mode = MODE_BGRA; |
| 369 | + | /* NOTE: Multi-threaded decoding may cause problems on some system */ |
| 370 | + | opts.use_threads = true; |
| 371 | + | dec = WebPAnimDecoderNew(&data, &opts); |
| 372 | + | if ((err = (dec == NULL) || !WebPAnimDecoderGetInfo(dec, &info))) { |
| 373 | + | error(0, 0, "%s: WebP parsing or memory error (file is corrupt?)", file->name); |
| 374 | + | goto fail; |
| 375 | + | } |
| 376 | + | demux = WebPAnimDecoderGetDemuxer(dec); |
| 377 | + | |
| 378 | + | if (img == NULL) { /* Only get the first frame and put it into fframe. */ |
| 379 | + | if ((err = !WebPAnimDecoderGetNext(dec, &buf, &ts))) { |
| 380 | + | error(0, 0, "%s: Error loading first frame", file->name); |
| 381 | + | goto fail; |
| 382 | + | } |
| 383 | + | *fframe = imlib_create_image_using_copied_data( |
| 384 | + | info.canvas_width, info.canvas_height, (DATA32*)buf); |
| 385 | + | imlib_context_set_image(*fframe); |
| 386 | + | imlib_image_set_has_alpha(WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS) & ALPHA_FLAG); |
| 387 | + | } else { |
| 388 | + | /* Get global information for the image */ |
| 389 | + | flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); |
| 390 | + | img->w = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); |
| 391 | + | img->h = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); |
| 392 | + | img->multi.cap = info.frame_count; |
| 393 | + | img->multi.sel = 0; |
| 394 | + | img->multi.frames = emalloc(info.frame_count * sizeof(img_frame_t)); |
| 395 | + | |
| 396 | + | /* Load and decode frames (also works on images with only 1 frame) */ |
| 397 | + | img->multi.cnt = 0; |
| 398 | + | while (WebPAnimDecoderGetNext(dec, &buf, &ts)) { |
| 399 | + | im = imlib_create_image_using_copied_data( |
| 400 | + | info.canvas_width, info.canvas_height, (DATA32*)buf); |
| 401 | + | imlib_context_set_image(im); |
| 402 | + | imlib_image_set_format("webp"); |
| 403 | + | /* Get an iterator of this frame - used for frame info (duration, etc.) */ |
| 404 | + | WebPDemuxGetFrame(demux, img->multi.cnt+1, &iter); |
| 405 | + | imlib_image_set_has_alpha((flags & ALPHA_FLAG) == ALPHA_FLAG); |
| 406 | + | /* Store info for this frame */ |
| 407 | + | img->multi.frames[img->multi.cnt].im = im; |
| 408 | + | delay = iter.duration > 0 ? iter.duration : DEF_WEBP_DELAY; |
| 409 | + | img->multi.frames[img->multi.cnt].delay = delay; |
| 410 | + | img->multi.length += img->multi.frames[img->multi.cnt].delay; |
| 411 | + | img->multi.cnt++; |
| 412 | + | } |
| 413 | + | WebPDemuxReleaseIterator(&iter); |
| 414 | + | |
| 415 | + | if (img->multi.cnt > 1) { |
| 416 | + | imlib_context_set_image(img->im); |
| 417 | + | imlib_free_image(); |
| 418 | + | img->im = img->multi.frames[0].im; |
| 419 | + | } else if (img->multi.cnt == 1) { |
| 420 | + | imlib_context_set_image(img->multi.frames[0].im); |
| 421 | + | imlib_free_image(); |
| 422 | + | img->multi.cnt = 0; |
| 423 | + | } |
| 424 | + | imlib_context_set_image(img->im); |
| 425 | + | } |
| 426 | + | imlib_image_set_format("webp"); |
| 427 | + | fail: |
| 428 | + | if (dec != NULL) |
| 429 | + | WebPAnimDecoderDelete(dec); |
| 430 | + | free((unsigned char *)data.bytes); |
| 431 | + | return !err; |
| 432 | + | } |
| 433 | + | #endif /* HAVE_LIBWEBP */ |
| 434 | + | |
| 300 | 435 | | Imlib_Image img_open(const fileinfo_t *file) |
| 301 | 436 | | { |
| 302 | 437 | | struct stat st; |
| @@ -305,10 +440,21 @@ Imlib_Image img_open(const fileinfo_t *file) |
| 305 | 440 | | if (access(file->path, R_OK) == 0 && |
| 306 | 441 | | stat(file->path, &st) == 0 && S_ISREG(st.st_mode)) |
| 307 | 442 | | { |
| 308 | | − | im = imlib_load_image(file->path); |
| 443 | + | #if HAVE_LIBWEBP |
| 444 | + | if (is_webp(file->path)) |
| 445 | + | img_load_webp(file, &im, NULL); |
| 446 | + | else |
| 447 | + | #endif |
| 448 | + | im = imlib_load_image(file->path); |
| 309 | 449 | | if (im != NULL) { |
| 310 | 450 | | imlib_context_set_image(im); |
| 451 | + | #if HAVE_LIBWEBP |
| 452 | + | const char *fmt; |
| 453 | + | if ((fmt = imlib_image_format()) != NULL && !STREQ(fmt, "webp") && |
| 454 | + | imlib_image_get_data_for_reading_only() == NULL) { |
| 455 | + | #else |
| 311 | 456 | | if (imlib_image_get_data_for_reading_only() == NULL) { |
| 457 | + | #endif |
| 312 | 458 | | imlib_free_image(); |
| 313 | 459 | | im = NULL; |
| 314 | 460 | | } |
| @@ -336,6 +482,10 @@ bool img_load(img_t *img, const fileinfo_t *file) |
| 336 | 482 | | #if HAVE_LIBGIF |
| 337 | 483 | | if (STREQ(fmt, "gif")) |
| 338 | 484 | | img_load_gif(img, file); |
| 485 | + | #endif |
| 486 | + | #if HAVE_LIBWEBP |
| 487 | + | if (STREQ(fmt, "webp")) |
| 488 | + | img_load_webp(file, NULL, img); |
| 339 | 489 | | #endif |
| 340 | 490 | | } |
| 341 | 491 | | img->w = imlib_image_get_width(); |