pax_global_header00006660000000000000000000000064142402611500014505gustar00rootroot0000000000000052 comment=acc6c51027ded81e7b8533d73e75bb9246a9e138 webp-pixbuf-loader-0.0.5/000077500000000000000000000000001424026115000152035ustar00rootroot00000000000000webp-pixbuf-loader-0.0.5/.travis.yml000066400000000000000000000005001424026115000173070ustar00rootroot00000000000000os: linux dist: focal language: c before_install: - sudo apt update - sudo apt install ninja-build meson libwebp-dev libgdk-pixbuf2.0-dev script: - mkdir build - meson build -Dgdk_pixbuf_query_loaders_path=/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders - cd build - ninja - ninja test webp-pixbuf-loader-0.0.5/LICENSE.LGPL-2000066400000000000000000000012721424026115000171060ustar00rootroot00000000000000This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. webp-pixbuf-loader-0.0.5/README.md000066400000000000000000000013571424026115000164700ustar00rootroot00000000000000WebP GDK Pixbuf Loader library ============================== [![Packaging status](https://repology.org/badge/vertical-allrepos/webp-pixbuf-loader.svg?exclude_unsupported=1)](https://repology.org/project/webp-pixbuf-loader/versions) Building from source -------------------- Build: ``` meson builddir ninja -C builddir ``` Build on Debian/Ubuntu: Install dependencies: ``` sudo apt install libwebp-dev libgdk-pixbuf2.0-dev libgtk-3-dev meson build-essential ``` (libgtk-3-dev is optional, used in development) Build: ``` meson builddir -Dgdk_pixbuf_query_loaders_path=/usr/lib/x86_64-linux-gnu/gdk-pixbuf-2.0/gdk-pixbuf-query-loaders ninja -C builddir ``` Install: ``` sudo ninja -C builddir install ``` webp-pixbuf-loader-0.0.5/io-webp-anim.c000066400000000000000000000556251424026115000176500ustar00rootroot00000000000000/* GdkPixbuf library - WebP Image Loader * * SPDX-License-Identifier: LGPL-2.0-or-later * Copyright (C) 2021 Alan Hawrelak * * Authors: Alan Hawrelak * with some code copied from io-webp.c with its authors. */ #include "io-webp-anim.h" /* Private part of the GdkPixbufWebpAnim structure. */ struct _GdkPixbufWebpAnim { GdkPixbufAnimation parent_instance; WebPContext *context; WebPAnimInfo *animInfo; WebPAnimDecoderOptions *decOptions; WebPAnimDecoder *dec; /* dec and demuxer have identical lifetimes. dec owns demuxer. */ WebPDemuxer *demuxer; GdkPixbufWebpAnimIter *webp_iter; WebPData pdata; uint8_t *curr_frame_ptr; /* owned by dec. */ uint32_t loops_completed; }; /* Private part of the GdkPixbufWebpAnimIter structure. */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS struct _GdkPixbufWebpAnimIter { GdkPixbufAnimationIter parent_instance; GdkPixbufWebpAnim *webp_anim; WebPIterator *wpiter; int cur_frame_num; }; G_GNUC_END_IGNORE_DEPRECATIONS /* End of private structs */ G_DEFINE_TYPE (GdkPixbufWebpAnim, gdk_pixbuf_webp_anim, GDK_TYPE_PIXBUF_ANIMATION); G_DEFINE_TYPE (GdkPixbufWebpAnimIter, gdk_pixbuf_webp_anim_iter, GDK_TYPE_PIXBUF_ANIMATION_ITER); /* gdk_pixbuf_webp_anim_class code =============================== */ G_GNUC_BEGIN_IGNORE_DEPRECATIONS static GdkPixbufAnimationIter * gdk_pixbuf_webp_anim_get_iter (GdkPixbufAnimation *anim, const GTimeVal *start_time); G_GNUC_END_IGNORE_DEPRECATIONS static GdkPixbuf * gdk_pixbuf_webp_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter); static gboolean gdk_pixbuf_webp_image_is_static_image (GdkPixbufAnimation *animation) { GdkPixbufWebpAnim *webp_anim; gboolean is_static = TRUE; webp_anim = GDK_PIXBUF_WEBP_ANIM (animation); if (webp_anim != NULL && webp_anim->context != NULL) { /* WebPGetFeatures already called on webp_anim->context->features in creating webp_anim. */ if (webp_anim->context->features.has_animation != 0) is_static = FALSE; } return is_static; } static GdkPixbuf * gdk_pixbuf_webp_anim_get_static_image (GdkPixbufAnimation *anim) { GdkPixbufWebpAnim *webp_anim; GdkPixbufAnimationIter *iter; webp_anim = GDK_PIXBUF_WEBP_ANIM (anim); if (!webp_anim->webp_iter) { iter = gdk_pixbuf_webp_anim_get_iter (anim, NULL); webp_anim->webp_iter = GDK_PIXBUF_WEBP_ANIM_ITER (iter); if (webp_anim->webp_iter == NULL) { return NULL; } } else { iter = GDK_PIXBUF_ANIMATION_ITER (webp_anim->webp_iter); } return gdk_pixbuf_webp_anim_iter_get_pixbuf (iter); } static void iter_restart (GdkPixbufWebpAnimIter *iter) { iter->cur_frame_num = 1; } /* * This assumes that * WebPAnimDecoderGetNext(decoder, &webp_iter->webp_anim->curr_frame_ptr, &atime) * has just been called and there is data in curr_frame_ptr to create a pixbuf. */ static GdkPixbuf * data_to_pixbuf (GdkPixbufWebpAnimIter *iter, gboolean *has_error) { GdkPixbufWebpAnimIter *webp_iter; gint w, h; GdkPixbuf *pixbuf; guint8 *out; gboolean use_alpha; webp_iter = GDK_PIXBUF_WEBP_ANIM_ITER (iter); w = (gint) webp_iter->webp_anim->animInfo->canvas_width; h = (gint) webp_iter->webp_anim->animInfo->canvas_height; /* WebPAnimDecoderGetNext returns an RGBA type buffer, even if the incoming * WebP container is RGB. */ use_alpha = TRUE; /* = webp_iter->webp_anim->context->features.has_alpha; */ out = webp_iter->webp_anim->curr_frame_ptr; if (!out) { *has_error = TRUE; return NULL; } pixbuf = gdk_pixbuf_new_from_data ((const guchar *) out, GDK_COLORSPACE_RGB, use_alpha, 8, w, h, w * 4, /* w * (use_alpha ? 4 : 3) */ NULL, /* Do not use destroy_data, as dec handles cleanup. */ NULL); if (!pixbuf) { *has_error = TRUE; return NULL; } GdkPixbuf *pixb = webp_iter->webp_anim->context->pixbuf; if (pixb) g_object_unref (pixb); if (!GDK_IS_PIXBUF (pixbuf)) { *has_error = TRUE; return NULL; } webp_iter->webp_anim->context->pixbuf = pixbuf; return pixbuf; } G_GNUC_BEGIN_IGNORE_DEPRECATIONS static GdkPixbufAnimationIter * gdk_pixbuf_webp_anim_get_iter (GdkPixbufAnimation *anim, const GTimeVal *start_time) { GTimeVal atime; int amillitime; gboolean has_err = FALSE; if (!anim) return NULL; GdkPixbufWebpAnim *old_anim = GDK_PIXBUF_WEBP_ANIM (anim); if (old_anim && old_anim->webp_iter) return GDK_PIXBUF_ANIMATION_ITER (old_anim->webp_iter); if (start_time) atime = *start_time; else g_get_current_time (&atime); GdkPixbufWebpAnimIter *webp_iter = NULL; webp_iter = g_object_new (GDK_TYPE_PIXBUF_WEBP_ANIM_ITER, NULL); webp_iter->webp_anim = GDK_PIXBUF_WEBP_ANIM (anim); webp_iter->webp_anim->webp_iter = webp_iter; g_object_ref (webp_iter->webp_anim); webp_iter->wpiter = g_try_new0(WebPIterator, 1); /* Attempt to initialize the WebPIterator. */ if (!WebPDemuxGetFrame (webp_iter->webp_anim->demuxer, 1, webp_iter->wpiter)) { return NULL; } WebPAnimDecoder *decoder = webp_iter->webp_anim->dec; if (!WebPAnimDecoderGetNext (decoder, &webp_iter->webp_anim->curr_frame_ptr, &amillitime)) { return NULL; } (void) data_to_pixbuf (webp_iter, &has_err); if (has_err) return NULL; iter_restart (webp_iter); return GDK_PIXBUF_ANIMATION_ITER (webp_iter); } G_GNUC_END_IGNORE_DEPRECATIONS static void gdk_pixbuf_webp_anim_get_size (GdkPixbufAnimation *animation, int *width, int *height) { GdkPixbufWebpAnim *anim = GDK_PIXBUF_WEBP_ANIM (animation); if (anim && anim->context) { if (width) *width = anim->context->features.width; /* canvas width */ if (height) *height = anim->context->features.height; /* canvas height */ } } static void gdk_pixbuf_webp_anim_finalize (GObject *object) { GdkPixbufWebpAnim *anim = GDK_PIXBUF_WEBP_ANIM (object); if (anim->context->anim_incr.filedata) { g_free (anim->context->anim_incr.filedata); anim->context->anim_incr.filedata = NULL; } if (anim->context->anim_incr.accum_data) { g_free (anim->context->anim_incr.accum_data); anim->context->anim_incr.accum_data = NULL; anim->context->anim_incr.used_len = 0; } if (anim->webp_iter) { g_object_unref (anim->webp_iter); anim->webp_iter = NULL; } WebPAnimDecoderDelete (anim->dec); g_free (anim->animInfo); g_free (anim->decOptions); if (anim->context->pixbuf) { g_object_unref (anim->context->pixbuf); anim->context->pixbuf = NULL; } g_free (anim->context); /* not handled in io-webp.c */ anim->context = NULL; G_OBJECT_CLASS (gdk_pixbuf_webp_anim_parent_class)->finalize (object); } static GdkPixbuf * gdk_pixbuf_webp_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter) { /* WebPBitstreamFeatures features; */ GdkPixbufWebpAnimIter *webp_iter; webp_iter = GDK_PIXBUF_WEBP_ANIM_ITER (iter); if (webp_iter && webp_iter->webp_anim && webp_iter->webp_anim->context) { /* features = webp_iter->webp_anim->context->features; */ } else return NULL; WebPIterator *curr = webp_iter->wpiter; if (!curr) return NULL; gboolean ispix = GDK_IS_PIXBUF (webp_iter->webp_anim->context->pixbuf); if (ispix) return webp_iter->webp_anim->context->pixbuf; else return NULL; } static void gdk_pixbuf_webp_anim_class_init (GdkPixbufWebpAnimClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass); object_class->finalize = gdk_pixbuf_webp_anim_finalize; anim_class->is_static_image = gdk_pixbuf_webp_image_is_static_image; anim_class->get_static_image = gdk_pixbuf_webp_anim_get_static_image; anim_class->get_size = gdk_pixbuf_webp_anim_get_size; anim_class->get_iter = gdk_pixbuf_webp_anim_get_iter; } static void gdk_pixbuf_webp_anim_init (GdkPixbufWebpAnim *anim) { } /* end gdk_pixbuf_webp_anim_class code =============================== */ /* gdk_pixbuf_webp_anim_iter class code ============================== */ static void gdk_pixbuf_webp_anim_iter_init (GdkPixbufWebpAnimIter *iter) { } static void gdk_pixbuf_webp_anim_iter_finalize (GObject *object) { GdkPixbufWebpAnimIter *iter = GDK_PIXBUF_WEBP_ANIM_ITER (object); /* See the note on WebPDemuxGetFrame in webp/demux.h. * Also apparently WebPDemuxDelete does not need to be called * because we are not using the demux chunk calls. */ WebPDemuxReleaseIterator (iter->wpiter); g_free (iter->wpiter); iter->wpiter = NULL; /* webp_anim and iter have reciprocal references, hence this little dance. */ iter->webp_anim->webp_iter = NULL; g_object_unref (iter->webp_anim); iter->webp_anim = NULL; G_OBJECT_CLASS (gdk_pixbuf_webp_anim_iter_parent_class)->finalize (object); } static int gdk_pixbuf_webp_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter) { int dur = 0; GdkPixbufWebpAnimIter *webp_iter; webp_iter = GDK_PIXBUF_WEBP_ANIM_ITER (iter); if (webp_iter && webp_iter->wpiter) { dur = webp_iter->wpiter->duration; /* do what gif's do for short duration frames. * See gdk-pixbuf/gdk-pixbuf/io-gif.c in the function gif_get_lzw. * And also see https://developers.google.com/speed/webp/docs/riff_container under "Frame Duration". */ if (dur == 0) dur = 100; /* slow */ if (dur < 20) dur = 20; /* fast */ /* If the loops are completed, * give the delay time of -1, to indicate * the current image stays forever. */ if ((webp_iter->webp_anim->loops_completed >= 1) && (webp_iter->webp_anim->animInfo->loop_count > 0) && /* loop_count == 0 means loop forever. */ (webp_iter->webp_anim->loops_completed >= webp_iter->webp_anim->animInfo->loop_count)) { dur = -1; } } return dur; } static gboolean gdk_pixbuf_webp_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter) { gboolean is_loading = FALSE; GdkPixbufWebpAnimIter *webp_iter; webp_iter = GDK_PIXBUF_WEBP_ANIM_ITER (iter); if (webp_iter && webp_iter->wpiter) is_loading = !webp_iter->wpiter->complete; return is_loading; } G_GNUC_BEGIN_IGNORE_DEPRECATIONS static gboolean gdk_pixbuf_webp_anim_iter_advance (GdkPixbufAnimationIter *iter, const GTimeVal *current_time) { int timestamp; GTimeVal atime; gboolean hasFrameChanged = FALSE; GdkPixbufWebpAnimIter *webp_iter; webp_iter = GDK_PIXBUF_WEBP_ANIM_ITER (iter); if (!webp_iter || !webp_iter->wpiter) return FALSE; if (current_time) atime = *current_time; else g_get_current_time (&atime); /* Now move to the proper frame */ WebPAnimDecoder *decoder = webp_iter->webp_anim->dec; uint32_t num_frames = webp_iter->webp_anim->animInfo->frame_count; int cur_frame = webp_iter->cur_frame_num; if (webp_iter->wpiter->complete) { /* then change frames. */ gboolean at_end = FALSE; if (webp_iter->cur_frame_num >= num_frames) { webp_iter->webp_anim->loops_completed += 1; at_end = ((webp_iter->webp_anim->animInfo->loop_count > 0) && (webp_iter->webp_anim->loops_completed >= webp_iter->webp_anim->animInfo->loop_count)); if (at_end) { /* This allows breaking, the otherwise endless, loop in eog at * eog-image.c:line 2434 in private_timeout(). */ return TRUE; } /* Clean up for the next loop. */ WebPAnimDecoderReset ((WebPAnimDecoder *) decoder); /* typecast discards const */ cur_frame = 0; /* so it will loop at the end of the animation. */ } if (!webp_iter->wpiter->complete) { /* This does not handle partial loads. All data must be loaded. */ return FALSE; } cur_frame += 1; if (!WebPAnimDecoderGetNext (decoder, &webp_iter->webp_anim->curr_frame_ptr, ×tamp)) { return FALSE; } /* * Frame duration can vary with each frame. WebPAnimDecoderGetNext does not * give access to the new duration. WebPDemuxGetFrame is used to update * the WebPIterator which contains duration. */ if (!WebPDemuxGetFrame (webp_iter->webp_anim->demuxer, cur_frame, webp_iter->wpiter)) { return FALSE; } gboolean has_err = FALSE; (void) data_to_pixbuf (webp_iter, &has_err); if (has_err) return FALSE; /* set the status variables. */ webp_iter->cur_frame_num = cur_frame; hasFrameChanged = TRUE; } return hasFrameChanged; } G_GNUC_END_IGNORE_DEPRECATIONS static void gdk_pixbuf_webp_anim_iter_class_init (GdkPixbufWebpAnimIterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkPixbufAnimationIterClass *anim_iter_class = GDK_PIXBUF_ANIMATION_ITER_CLASS (klass); object_class->finalize = gdk_pixbuf_webp_anim_iter_finalize; anim_iter_class->get_delay_time = gdk_pixbuf_webp_anim_iter_get_delay_time; anim_iter_class->get_pixbuf = gdk_pixbuf_webp_anim_iter_get_pixbuf; anim_iter_class->on_currently_loading_frame = gdk_pixbuf_webp_anim_iter_on_currently_loading_frame; anim_iter_class->advance = gdk_pixbuf_webp_anim_iter_advance; } /* end gdk_pixbuf_webp_anim_iter class code ============================== */ /* returns raw data in pdata. * If error, pdata-> size and bytes are set to 0 or NULL. * Updates context->features if successful. */ void get_data_from_file (FILE *f, WebPContext *context, GError **error, WebPData *pdata) { guint32 data_size; gpointer data; WebPBitstreamFeatures features; pdata->size = 0; pdata->bytes = NULL; /* Get data size */ fseek (f, 0, SEEK_END); data_size = ftell (f); fseek (f, 0, SEEK_SET); /* Get data */ data = g_malloc (data_size); gboolean ok = (fread (data, data_size, 1, f) == 1); if (!ok) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Failed to read file"); return; } /* Check for RIFF and WEBP tags in WebP container header. */ char tag[5]; tag[4] = 0; for (int i = 0; i < 4; i++) { tag[i] = *(gchar *) (data + i); } int rc2 = strcmp (tag, "RIFF"); if (rc2 != 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header..."); return; } for (int i = 0; i < 4; i++) { tag[i] = *(gchar *) (data + 8 + i); } rc2 = strcmp (tag, "WEBP"); if (rc2 != 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header..."); return; } pdata->bytes = data; pdata->size = data_size; if (context->anim_incr.filedata) g_free (context->anim_incr.filedata); context->anim_incr.filedata = data; if (WebPGetFeatures (data, data_size, &features) == VP8_STATUS_OK) { context->features = features; } } /* in_context parameter can be NULL. */ extern GdkPixbufAnimation * gdk_pixbuf__webp_image_load_animation_data (const guchar *buf, guint size, WebPContext *in_context, GError **error) { g_return_val_if_fail (buf != NULL, NULL); g_return_val_if_fail (size != 0, NULL); WebPContext *context = in_context; GdkPixbufWebpAnim *animation = NULL; WebPAnimDecoderOptions *dec_options = NULL; WebPAnimDecoder *dec = NULL; WebPAnimInfo *ptr_anim_info = NULL; animation = g_object_new (GDK_TYPE_PIXBUF_WEBP_ANIM, NULL); if (animation == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Not enough memory to load WebP file"); goto error_end; } dec_options = g_try_new0(WebPAnimDecoderOptions, 1); if (!dec_options || !WebPAnimDecoderOptionsInit (dec_options)) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "WebPAnimDecoderOptionsInit() failed."); goto error_end; } dec_options->color_mode = MODE_RGBA; if (context == NULL) { context = g_try_new0(WebPContext, 1); if (context == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Not enough memory to load WebP file"); goto error_end; } } animation->decOptions = dec_options; animation->pdata.bytes = buf; animation->pdata.size = size; if (!WebPInitDecoderConfig (&context->config)) goto error_end; context->config.options.dithering_strength = 50; context->config.options.alpha_dithering_strength = 100; /* pdata has the same lifetime as dec, below. */ dec = WebPAnimDecoderNew (&animation->pdata, dec_options); ptr_anim_info = g_try_new0 (WebPAnimInfo, 1); if (ptr_anim_info == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Not enough memory to load WebP file"); goto error_end; } if (!WebPAnimDecoderGetInfo (dec, ptr_anim_info)) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "WebPAnimDecoderGetInfo could not get animation info."); goto error_end; } animation->dec = dec; animation->decOptions = dec_options; animation->demuxer = (WebPDemuxer *) WebPAnimDecoderGetDemuxer (dec); /* cast to discard const. */ animation->animInfo = ptr_anim_info; context->error = error; animation->context = context; return GDK_PIXBUF_ANIMATION (animation); error_end: if (context && context->error && *(context->error)) g_print ("%s\n", (*(context->error))->message); if (ptr_anim_info != NULL) g_free (ptr_anim_info); if (dec != NULL) WebPAnimDecoderDelete (dec); if (animation != NULL) (void) g_object_unref (animation); if (dec_options != NULL) g_free (dec_options); return NULL; } extern GdkPixbufAnimation * gdk_pixbuf__webp_image_load_animation (FILE *file, GError **error) { g_return_val_if_fail (file != NULL, NULL); WebPContext *context = NULL; context = g_try_new0(WebPContext, 1); if (context == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Not enough memory to load WebP file"); return NULL; } /* read in the file to get data. This updates context->features. */ WebPData pdata; get_data_from_file (file, context, error, &pdata); return gdk_pixbuf__webp_image_load_animation_data (pdata.bytes, pdata.size, context, error); } webp-pixbuf-loader-0.0.5/io-webp-anim.h000066400000000000000000000030541424026115000176420ustar00rootroot00000000000000/* GdkPixbuf library - WebP Image Loader * * SPDX-License-Identifier: LGPL-2.0-or-later * Copyright (C) 2021 Alan Hawrelak * * Authors: Alan Hawrelak */ #ifndef IO_WEBP_ANIM_H #define IO_WEBP_ANIM_H #include #include #include #include #include #include #define GDK_PIXBUF_ENABLE_BACKEND #include #include #undef GDK_PIXBUF_ENABLE_BACKEND #include "io-webp.h" G_BEGIN_DECLS /* GdkPixbufWebpAnim */ #define GDK_TYPE_PIXBUF_WEBP_ANIM (gdk_pixbuf_webp_anim_get_type ()) G_DECLARE_FINAL_TYPE(GdkPixbufWebpAnim, GDK_TYPE_PIXBUF_WEBP_ANIM, GdkPixbufWebp, ANIM, GdkPixbufAnimation) #define GDK_PIXBUF_WEBP_ANIM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_WEBP_ANIM, GdkPixbufWebpAnim)) #define GDK_IS_PIXBUF_WEBP_ANIM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_WEBP_ANIM)) /* GdkPixbufWebpAnimIter */ #define GDK_TYPE_PIXBUF_WEBP_ANIM_ITER (gdk_pixbuf_webp_anim_iter_get_type ()) G_DECLARE_FINAL_TYPE(GdkPixbufWebpAnimIter, GDK_PIXBUF_WEBP_ANIM_ITER, GdkPixbufWebp, ANIM_ITER, GdkPixbufAnimationIter) #define GDK_PIXBUF_WEBP_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_WEBP_ANIM_ITER, GdkPixbufWebpAnimIter)) #define GDK_IS_PIXBUF_WEBP_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_WEBP_ANIM_ITER)) G_END_DECLS #endif /* IO_WEBP_ANIM_H */ webp-pixbuf-loader-0.0.5/io-webp.c000066400000000000000000000667171424026115000167320ustar00rootroot00000000000000/* GdkPixbuf library - WebP Image Loader * * SPDX-License-Identifier: LGPL-2.0-or-later * Copyright (C) 2011 Alberto Ruiz * Copyright (C) 2011 David Mazary * Copyright (C) 2014 Přemysl Janouch * * Authors: Alberto Ruiz * David Mazary * Přemysl Janouch */ #include "io-webp.h" #define IMAGE_READ_BUFFER_SIZE 65535 typedef enum { ACCUMstate_need_initialize = 0, ACCUMstate_need_data, ACCUMstate_have_all_data, ACCUMstate_sending_frames } ACCUMstate; /* from io-webp-anim.c */ extern GdkPixbufAnimation * gdk_pixbuf__webp_image_load_animation (FILE *file, GError **error); extern GdkPixbufAnimation * gdk_pixbuf__webp_image_load_animation_data (const guchar *buf, guint size, WebPContext *in_context, GError **error); static void destroy_data (guchar *pixels, gpointer data) { g_free (pixels); } /* Shared library entry point */ static GdkPixbuf * gdk_pixbuf__webp_image_load (FILE *f, GError **error) { GdkPixbuf * volatile pixbuf = NULL; guint32 data_size; guint8 *out; gint w, h, ok; gpointer data; WebPBitstreamFeatures features; gboolean use_alpha = TRUE; /* Get data size */ fseek (f, 0, SEEK_END); data_size = ftell(f); fseek (f, 0, SEEK_SET); /* Get data */ data = g_malloc (data_size); ok = (fread (data, data_size, 1, f) == 1); if (!ok) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Failed to read file"); return FALSE; } /* Take the safe route and only disable the alpha channel when we're sure that there is not any. */ if (WebPGetFeatures (data, data_size, &features) == VP8_STATUS_OK && features.has_alpha == FALSE) { use_alpha = FALSE; } if (use_alpha) { out = WebPDecodeRGBA (data, data_size, &w, &h); } else { out = WebPDecodeRGB (data, data_size, &w, &h); } g_free (data); if (!out) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Cannot create WebP decoder."); return FALSE; } pixbuf = gdk_pixbuf_new_from_data ((const guchar *)out, GDK_COLORSPACE_RGB, use_alpha, 8, w, h, w * (use_alpha ? 4 : 3), destroy_data, NULL); if (!pixbuf) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Failed to decode image"); return FALSE; } return pixbuf; } static gpointer gdk_pixbuf__webp_image_begin_load (GdkPixbufModuleSizeFunc size_func, GdkPixbufModulePreparedFunc prepare_func, GdkPixbufModuleUpdatedFunc update_func, gpointer user_data, GError **error) { WebPContext *context = g_new0 (WebPContext, 1); context->size_func = size_func; context->prepare_func = prepare_func; context->update_func = update_func; context->user_data = user_data; return context; } static gboolean gdk_pixbuf__webp_image_stop_load (gpointer context, GError **error) { WebPContext *data = (WebPContext *) context; g_return_val_if_fail(data != NULL, TRUE); gboolean has_animation = data->config.input.has_animation || data->features.has_animation; if ((data->pixbuf) && !has_animation) { g_object_unref (data->pixbuf); } if (data->idec) { WebPIDelete (data->idec); } return TRUE; } /* * anim_context will contain the new ptr and size if one has to be made. * newbuf incoming data to add * size of the incoming data * returns the new ptr */ static gpointer realloc_copy_mem (AnimIncrDecode *anim_context, const guchar *newbuf, guint size) { guchar *core_data = anim_context->accum_data; size_t core_used_len = anim_context->used_len; size_t core_cur_max_len = anim_context->cur_max_len; size_t size_to_add = IMAGE_READ_BUFFER_SIZE; if (core_data == NULL) { guchar *ptr = g_try_malloc0 (core_cur_max_len + size + size_to_add); if (ptr == NULL) { core_used_len = 0; core_cur_max_len = 0; } else { memcpy (ptr, newbuf, size); core_data = ptr; core_used_len = size; core_cur_max_len = size + size_to_add; } } else { /* (core_data != NULL) */ if (core_cur_max_len >= core_used_len + size) { memcpy (core_data + core_used_len, newbuf, size); core_used_len += size; } else { size_t extended_size = 0; guchar *newptr = g_try_realloc (core_data, core_cur_max_len + size + size_to_add); extended_size = core_cur_max_len + size + size_to_add; if (!newptr) { newptr = g_try_realloc (core_data, core_cur_max_len + size); extended_size = core_cur_max_len + size; } if (newptr) { core_data = newptr; core_cur_max_len = extended_size; memcpy (core_data + core_used_len, newbuf, size); core_used_len += size; } } } anim_context->accum_data = core_data; anim_context->cur_max_len = core_cur_max_len; anim_context->used_len = core_used_len; return core_data; } /* * Only called after all data is present. * At that point the size is data->anim_incr.total_data_len . */ static void create_anim (WebPContext *data, guchar *ptr, guint size, GError **error) { data->anim_incr.accum_data = ptr; if (data->anim_incr.used_len == data->anim_incr.total_data_len) { data->anim_incr.state = ACCUMstate_have_all_data; GdkPixbufAnimation *anim = gdk_pixbuf__webp_image_load_animation_data ( data->anim_incr.accum_data, data->anim_incr.used_len, data, error); GdkPixbufAnimationIter *anim_iter = gdk_pixbuf_animation_get_iter (anim, NULL); GdkPixbuf *pixbuf = gdk_pixbuf_animation_iter_get_pixbuf (anim_iter); data->pixbuf = pixbuf; data->anim_incr.state = ACCUMstate_sending_frames; if (data->prepare_func) { (* data->prepare_func) (data->pixbuf, anim, data->user_data); } } } /* handle animation. * The WebP animation API was not designed for incremental load. * Defeat the purpose of incremental load, by loading all increments * into one data buffer. Do not create a gdk_pixbuf until all data * is present. Then issue data->prepare_func call. */ static gboolean gdk_pixbuf__webp_anim_load_increment (gpointer context, const guchar *buf, guint size, GError **error) { WebPContext *data = (WebPContext *) context; if (data->anim_incr.state == ACCUMstate_need_initialize) { if (size < 12) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header..."); return FALSE; } /* check for "RIFF" tag, and then the "WEBP" tag. */ char tag[5]; tag[4] = 0; for (int i = 0; i < 4; i++) { tag[i] = *(gchar *) (buf + i); } int rc2 = strcmp (tag, "RIFF"); if (rc2 != 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header..."); return FALSE; } for (int i = 0; i < 4; i++) { tag[i] = *(gchar *) (buf + 8 + i); } rc2 = strcmp (tag, "WEBP"); if (rc2 != 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header..."); return FALSE; } /* The next 4 bytes give the size of the webp container less the 8 byte header. */ uint32_t anim_size = *(uint32_t *) (buf + 4); /* gives file size not counting the first 8 bytes. */ uint32_t file_size = anim_size + 8; if (file_size < size) { /* user asking to insert data larger than the image size set in the file data. */ g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header.."); return FALSE; } data->anim_incr.total_data_len = file_size; /* allow for 8 byte header. "RIFF" + SIZE . */ guchar *ptr = calloc (sizeof (guchar), file_size); if (ptr == NULL) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Failed to allocate memory for the WebP image."); return FALSE; } gint w, h; gint rc = WebPGetInfo (buf, size, &w, &h); if (rc == 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header."); return FALSE; } data->got_header = TRUE; if (data->size_func) { gint scaled_w = w; gint scaled_h = h; (*data->size_func) (&scaled_w, &scaled_h, data->user_data); if (scaled_w != 0 && scaled_h != 0 && (scaled_w != w || scaled_h != h)) { data->config.options.use_scaling = TRUE; data->config.options.scaled_width = scaled_w; data->config.options.scaled_height = scaled_h; w = scaled_w; h = scaled_h; } } memcpy (ptr, buf, size); data->anim_incr.accum_data = ptr; data->anim_incr.used_len = size; data->anim_incr.cur_max_len = anim_size + 8; data->anim_incr.total_data_len = anim_size + 8; data->anim_incr.state = ACCUMstate_need_data; data->config.options.dithering_strength = 50; data->config.options.alpha_dithering_strength = 100; if (size == data->anim_incr.total_data_len) { data->anim_incr.state = ACCUMstate_have_all_data; create_anim (data, ptr, size, error); } else if (size > data->anim_incr.total_data_len) { return FALSE; } return TRUE; } else if (data->anim_incr.state == ACCUMstate_need_data) { gpointer ptr = realloc_copy_mem (&data->anim_incr, buf, size); if (data->anim_incr.used_len == data->anim_incr.total_data_len) { data->anim_incr.state = ACCUMstate_have_all_data; create_anim (data, ptr, data->anim_incr.used_len, error); } else if (data->anim_incr.used_len > data->anim_incr.total_data_len) { return FALSE; } return TRUE; } else if (data->anim_incr.state == ACCUMstate_have_all_data) { return TRUE; } else if (data->anim_incr.state == ACCUMstate_sending_frames) { return TRUE; } return FALSE; } static gboolean gdk_pixbuf__webp_image_load_increment (gpointer context, const guchar *buf, guint size, GError **error) { gint w, h, stride; WebPContext *data = (WebPContext *) context; g_return_val_if_fail(data != NULL, FALSE); gboolean use_animation = FALSE; if (data->got_header) { if (data->config.input.has_animation) { return gdk_pixbuf__webp_anim_load_increment (context, buf, size, error); } } else { gint rc = WebPGetInfo (buf, size, &w, &h); if (rc == 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "Cannot read WebP image header."); return FALSE; } data->got_header = TRUE; if (data->size_func) { gint scaled_w = w; gint scaled_h = h; (* data->size_func) (&scaled_w, &scaled_h, data->user_data); if (scaled_w != 0 && scaled_h != 0 && (scaled_w != w || scaled_h != h)) { data->config.options.use_scaling = TRUE; data->config.options.scaled_width = scaled_w; data->config.options.scaled_height = scaled_h; w = scaled_w; h = scaled_h; } } WebPBitstreamFeatures features; gboolean use_alpha = TRUE; /* Take the safe route and only disable the alpha channel when we're sure that there is not any. */ if (WebPGetFeatures (buf, size, &features) == VP8_STATUS_OK) { if (features.has_alpha == FALSE) use_alpha = FALSE; if (features.has_animation) { use_animation = TRUE; data->config.input.has_animation = TRUE; data->features = features; } } data->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, use_alpha, 8, w, h); guint pixbuf_length; stride = gdk_pixbuf_get_rowstride (data->pixbuf); guchar *pixbuf_data = gdk_pixbuf_get_pixels_with_length (data->pixbuf, &pixbuf_length); /* Initialise the picture to transparent black. */ memset (pixbuf_data, 0x00, pixbuf_length); data->config.output.colorspace = use_alpha ? MODE_RGBA : MODE_RGB; data->config.output.is_external_memory = TRUE; data->config.output.u.RGBA.rgba = pixbuf_data; data->config.output.u.RGBA.stride = stride; /* XXX: this may not be true: the last row doesn't strictly have to include stride padding, even though it does in case of gdk_pixbuf_new(), looking at its source. There is an explicit check in libwebp that requires that the following value equals stride times height, however, even though it is fairly unlikely that anyone would actually want to write into the padding. */ data->config.output.u.RGBA.size = h * stride; if (use_animation) return gdk_pixbuf__webp_anim_load_increment (context, buf, size, error); data->idec = WebPIDecode (NULL, 0, &data->config); if (!data->idec) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Cannot create WebP decoder."); return FALSE; } if (data->prepare_func) { (* data->prepare_func) (data->pixbuf, NULL, data->user_data); } } /* Append size bytes to decoder's buffer */ const VP8StatusCode status = WebPIAppend (data->idec, buf, size); if (status != VP8_STATUS_SUSPENDED && status != VP8_STATUS_OK) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, "WebP decoder failed with status code %d.", status); return FALSE; } /* Decode decoder's updated buffer */ guint8 *dec_output; dec_output = WebPIDecGetRGB (data->idec, &data->last_y, &w, &h, &stride); if (dec_output == NULL && status != VP8_STATUS_SUSPENDED) { g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Bad inputs to WebP decoder."); return FALSE; } if (data->update_func) { (* data->update_func) (data->pixbuf, 0, 0, w, data->last_y, data->user_data); } return TRUE; } static int write_file (const uint8_t* data, size_t data_size, const WebPPicture* const pic) { FILE* const out = (FILE *) pic->custom_ptr; return data_size ? (fwrite (data, data_size, 1, out) == 1) : 1; } typedef struct { GdkPixbufSaveFunc func; gpointer data; } save_context; static int save_callback (const uint8_t* data, size_t data_size, const WebPPicture* const pic) { save_context *env = (save_context *) pic->custom_ptr; return (* env->func) ((const char*)data, data_size, NULL, env->data); } static gboolean real_save_webp (GdkPixbuf *pixbuf, gchar **keys, gchar **values, GError **error, gboolean to_callback, FILE *f, save_context *context) { WebPPicture picture; WebPConfig config; gint w, h, rowstride, has_alpha, rc; guchar *pixels; if (!WebPPictureInit(&picture) || !WebPConfigInit(&config)) { g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "WebP encoder version mismatch."); return FALSE; } if (keys && *keys) { gchar **kiter = keys; gchar **viter = values; while (*kiter) { if (strncmp (*kiter, "quality", 7) == 0) { float quality = (float) g_ascii_strtod (*viter, NULL); if (quality < 0 || quality > 100) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "WebP quality must be a value between 0 and 100."); return FALSE; } config.quality = quality; } else if (strncmp (*kiter, "preset", 6) == 0) { WebPPreset preset; if (strncmp (*viter, "default", 7) == 0) { preset = WEBP_PRESET_DEFAULT; } else if (strncmp (*viter, "photo", 5) == 0) { preset = WEBP_PRESET_PHOTO; } else if (strncmp (*viter, "picture", 7) == 0) { preset = WEBP_PRESET_PICTURE; } else if (strncmp (*viter, "drawing", 7) == 0) { preset = WEBP_PRESET_DRAWING; } else if (strncmp (*viter, "icon", 4) == 0) { preset = WEBP_PRESET_ICON; } else if (strncmp (*viter, "text", 4) == 0) { preset = WEBP_PRESET_TEXT; } else { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "WebP encoder invalid preset."); return FALSE; } if (WebPConfigPreset (&config, preset, config.quality) == 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Could not initialize decoder with preset."); return FALSE; } } ++kiter; ++viter; } } if (WebPValidateConfig (&config) != 1) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, "Invalid encoding configuration"); return FALSE; } w = gdk_pixbuf_get_width (pixbuf); h = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); pixels = gdk_pixbuf_get_pixels (pixbuf); picture.width = w; picture.height = h; if (has_alpha) { rc = WebPPictureImportRGBA (&picture, pixels, rowstride); } else { rc = WebPPictureImportRGB (&picture, pixels, rowstride); } if (rc == 0) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, "Failed to allocate picture"); return FALSE; } if (to_callback) { picture.writer = save_callback; picture.custom_ptr = (void*) context; } else { picture.writer = write_file; picture.custom_ptr = (void*) f; } if (WebPEncode(&config, &picture) == 0) { fprintf(stderr, "Error! Cannot encode picture as WebP\n"); } return TRUE; } static gboolean gdk_pixbuf__webp_image_save (FILE *f, GdkPixbuf *pixbuf, gchar **keys, gchar **values, GError **error) { return real_save_webp (pixbuf, keys, values, error, FALSE, f, NULL); } static gboolean gdk_pixbuf__webp_image_save_to_callback (GdkPixbufSaveFunc save_func, gpointer user_data, GdkPixbuf *pixbuf, gchar **keys, gchar **values, GError **error) { save_context *context = g_new0 (save_context, 1); context->func = save_func; context->data = user_data; return real_save_webp (pixbuf, keys, values, error, TRUE, NULL, context); } void fill_vtable (GdkPixbufModule *module) { module->load = gdk_pixbuf__webp_image_load; module->begin_load = gdk_pixbuf__webp_image_begin_load; module->stop_load = gdk_pixbuf__webp_image_stop_load; module->load_increment = gdk_pixbuf__webp_image_load_increment; module->save = gdk_pixbuf__webp_image_save; module->save_to_callback = gdk_pixbuf__webp_image_save_to_callback; module->load_animation = gdk_pixbuf__webp_image_load_animation; } void fill_info (GdkPixbufFormat *info) { static GdkPixbufModulePattern signature[] = { { "RIFFsizeWEBP", " xxxx ", 100 }, { NULL, NULL, 0 } }; static gchar *mime_types[] = { "image/webp", "audio/x-riff", /* FIXME hack around systems missing mime type */ NULL }; static gchar *extensions[] = { "webp", NULL }; info->name = "webp"; info->signature = signature; info->description = "The WebP image format"; info->mime_types = mime_types; info->extensions = extensions; info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE; info->license = "LGPL"; } webp-pixbuf-loader-0.0.5/io-webp.h000066400000000000000000000024321424026115000167170ustar00rootroot00000000000000/* GdkPixbuf library - WebP Image Loader * * SPDX-License-Identifier: LGPL-2.0-or-later * Copyright (C) 2011 Alberto Ruiz * Copyright (C) 2011 David Mazary * Copyright (C) 2014 Přemysl Janouch * * Authors: Alberto Ruiz * David Mazary * Přemysl Janouch */ #ifndef IO_WEBP_H #define IO_WEBP_H #include #include #include #define GDK_PIXBUF_ENABLE_BACKEND #include #undef GDK_PIXBUF_ENABLE_BACKEND /* Animation progressive loader state */ typedef struct { int state; /* ACCUMstate */ guchar *filedata; guchar *accum_data; size_t used_len; size_t cur_max_len; size_t total_data_len; } AnimIncrDecode; /* Progressive loader context */ typedef struct { GdkPixbufModuleSizeFunc size_func; GdkPixbufModuleUpdatedFunc update_func; GdkPixbufModulePreparedFunc prepare_func; WebPDecoderConfig config; gpointer user_data; GdkPixbuf *pixbuf; gboolean got_header; WebPIDecoder *idec; WebPBitstreamFeatures features; /* used by animation. */ AnimIncrDecode anim_incr; /* used by animation. */ gint last_y; GError **error; } WebPContext; #endif /* IO_WEBP_H */ webp-pixbuf-loader-0.0.5/meson.build000066400000000000000000000034351424026115000173520ustar00rootroot00000000000000project('webp-pixbuf-loader', 'c', meson_version: '>=0.50') gdkpb = dependency('gdk-pixbuf-2.0', version: '>2.22.0', method: 'pkg-config') gdk_pb_moddir = get_option('gdk_pixbuf_moduledir') if gdk_pb_moddir == '' gdk_pb_moddir = gdkpb.get_pkgconfig_variable('gdk_pixbuf_moduledir') endif gdk_pb_query_loaders = gdkpb.get_pkgconfig_variable('gdk_pixbuf_query_loaders') webp = dependency('libwebp', version: '>0.4.3') webpdemux = dependency('libwebpdemux', version: '>0.4.3') gio2 = dependency('gio-2.0', version: '>2.56.0', method: 'pkg-config') gtk3 = dependency('gtk+-3.0', version: '>3.22.0', method: 'pkg-config', required: false) # -34/-64 is to overcome a Fedora bug in the .pc file # debian has a similar problem with its .pc file but it requires knowing the platform triad gdk_pb_query_loaders = find_program(get_option('gdk_pixbuf_query_loaders_path'), gdk_pb_query_loaders, gdk_pb_query_loaders+'-32', gdk_pb_query_loaders+'-64') pbl_webp = shared_library('pixbufloader-webp', sources: ['io-webp.c', 'io-webp-anim.c'], dependencies: [gdkpb, webp, webpdemux], # Workaround for https://gitlab.gnome.org/GNOME/glib/issues/1413 name_suffix: host_machine.system() == 'darwin' ? 'so' : [], install: true, install_dir: gdk_pb_moddir) cdata = configuration_data() cdata.set('bindir', get_option('prefix') / get_option('bindir')) configure_file(input: 'webp-pixbuf.thumbnailer.in', output: 'webp-pixbuf.thumbnailer', configuration: cdata, install: true, install_dir: get_option('datadir') / 'thumbnailers') meson.add_install_script(gdk_pb_query_loaders.path(), '--update-cache') subdir('tests') webp-pixbuf-loader-0.0.5/meson_options.txt000066400000000000000000000003561424026115000206440ustar00rootroot00000000000000option('gdk_pixbuf_query_loaders_path', type: 'string', description: 'A non default path for the gdk-pixbuf-query-loaders binary') option('gdk_pixbuf_moduledir', type: 'string', description: 'The path to install gdk-pixbuf modules into') webp-pixbuf-loader-0.0.5/tests/000077500000000000000000000000001424026115000163455ustar00rootroot00000000000000webp-pixbuf-loader-0.0.5/tests/loaders.cache.in000066400000000000000000000005341424026115000213720ustar00rootroot00000000000000# GdkPixbuf Image Loader Modules file # Automatically generated file, do not edit # Created by gdk-pixbuf-query-loaders from gdk-pixbuf-2.40.0 # # LoaderDir = /usr/lib64/gdk-pixbuf-2.0/2.10.0/loaders # "@MODULE_PATH@" "webp" 5 "gdk-pixbuf" "The WebP image format" "LGPL" "image/webp" "audio/x-riff" "" "webp" "" "RIFFsizeWEBP" " xxxx " 100 webp-pixbuf-loader-0.0.5/tests/meson.build000066400000000000000000000047711424026115000205200ustar00rootroot00000000000000 t1 = executable('t1', 't1.c', link_with : pbl_webp, dependencies : [gdkpb, webp, webpdemux]) t2 = executable('t2', 't2.c', link_with : pbl_webp, dependencies : [gdkpb, webp, webpdemux]) t3 = executable('t3', 't3.c', link_with : pbl_webp, dependencies : [gdkpb, webp, webpdemux]) t4 = executable('t4', 't4.c', link_with : pbl_webp, dependencies : [gdkpb, webp, webpdemux, gio2]) # t4-gtk3 see below. t5 = executable('t5', 't5.c', link_with : pbl_webp, dependencies : [gdkpb, webp, webpdemux, gio2]) t6 = executable('t6', 't6.c', link_with : pbl_webp, dependencies : [gdkpb, webp, webpdemux, gio2]) loaders_data = configuration_data() loaders_data.set('MODULE_PATH', pbl_webp.full_path()) loaders = configure_file(input: 'loaders.cache.in', output : 'loaders.cache', configuration : loaders_data) test('load 1x1 image', t1, env : ['GDK_PIXBUF_MODULE_FILE=' + meson.current_build_dir() + '/loaders.cache', 'TEST_FILE=' + meson.current_source_dir() + '/t1.webp']) test('load 200x200 image', t2, env : ['GDK_PIXBUF_MODULE_FILE=' + meson.current_build_dir() + '/loaders.cache', 'TEST_FILE=' + meson.current_source_dir() + '/t2.webp']) test('load animation and parse', t3, env : ['GDK_PIXBUF_MODULE_FILE=' + meson.current_build_dir() + '/loaders.cache', 'TEST_FILE=' + meson.current_source_dir() + '/t3.webp']) test('load animation and display', t4, env : ['GDK_PIXBUF_MODULE_FILE=' + meson.current_build_dir() + '/loaders.cache', 'TEST_FILE=' + meson.current_source_dir() + '/t3.webp']) if gtk3.found() t4_gtk3_exe = executable('t4_gtk3_exe', sources: ['t4-gtk3.c'], link_with : pbl_webp, dependencies: [gdkpb, webp, webpdemux, gtk3], install: false) #test('t4_gtk3_exe', t4_gtk3_exe) endif test('load animation test duration', t5, env : ['GDK_PIXBUF_MODULE_FILE=' + meson.current_build_dir() + '/loaders.cache', 'TEST_FILE=' + meson.current_source_dir() + '/t5.webp']) test('get file info', t6, env : ['GDK_PIXBUF_MODULE_FILE=' + meson.current_build_dir() + '/loaders.cache', 'TEST_FILE=' + meson.current_source_dir() + '/t1.webp']) webp-pixbuf-loader-0.0.5/tests/t1.c000066400000000000000000000010071424026115000170330ustar00rootroot00000000000000#include gint main (gint argc, gchar **argv) { GError *error = NULL; gchar **env = g_get_environ(); g_warning("%s", g_environ_getenv(env, "TEST_FILE")); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (g_environ_getenv(env, "TEST_FILE"), &error); if (error) { g_error("%s", error->message); }; g_assert(error == NULL); g_assert(gdk_pixbuf_get_width(pixbuf) == 1); g_assert(gdk_pixbuf_get_height(pixbuf) == 1); g_strfreev(env); g_object_unref(pixbuf); return 0; } webp-pixbuf-loader-0.0.5/tests/t1.webp000066400000000000000000000014121424026115000175460ustar00rootroot00000000000000RIFFWEBPVP8X 0ICCPlcms0mntrRGB XYZ  +acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBALPHpVP8 2*%t9O)??g"webp-pixbuf-loader-0.0.5/tests/t2.c000066400000000000000000000010131424026115000170310ustar00rootroot00000000000000#include gint main (gint argc, gchar **argv) { GError *error = NULL; gchar **env = g_get_environ(); g_warning("%s", g_environ_getenv(env, "TEST_FILE")); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (g_environ_getenv(env, "TEST_FILE"), &error); if (error) { g_error("%s", error->message); }; g_assert(error == NULL); g_assert(gdk_pixbuf_get_width(pixbuf) == 200); g_assert(gdk_pixbuf_get_height(pixbuf) == 200); g_strfreev(env); g_object_unref(pixbuf); return 0; } webp-pixbuf-loader-0.0.5/tests/t2.webp000066400000000000000000000033741424026115000175600ustar00rootroot00000000000000RIFFWEBPVP8 '*>FJOq,m.}5?>c_t"gA3?/X0>=Nf?ˏwڻ/َ #Vɢy*4XMAU؃=pTmg snWtBhLu͞19v'V G[+8DgQ[2 If'¿#}bЂ=7#uKG􊝳=?}72>Fq^ⰹ0aOn'߁|ʪB""!ޞvMQQQ?#-[kCkId~ߡ-tW7(byaig+# n_ʁs.U1so1HeD f]Cj7=MBt/ic1E<΂tj> &pi0oW]VJL~@{X}|ZF^9da稈Fx%aVbȌv ~"n5:nw_R" 4;R=So sppQhK>Ix|l!Hy?@[+ɱpWM1"]` kVli D{"R <8dnNM?g*?oi7du?6kgq!YA36g]ae_#|r1IN^.== 虒|%'5ȯaފ /okẀ_ _y w ud:YHR0Eh:*d*͎-x$Scv8qieжMXV{OS@4K蘔gʚyȌY +6m wú-Zu20.ƴ2?^[. "B>~cã= !'H^Y{qB`zFqO|1I ř&K](tNĪr[t>)692^m#m^{/͋ğ Ln gint main(gint argc, gchar **argv) { GError *error = NULL; gchar **env = g_get_environ(); g_warning("%s", g_environ_getenv(env, "TEST_FILE")); GdkPixbufAnimation *anim = NULL; GdkPixbufAnimationIter *anim_iter = NULL; anim = gdk_pixbuf_animation_new_from_file(g_environ_getenv(env, "TEST_FILE"), &error); gboolean isStatic = gdk_pixbuf_animation_is_static_image(anim); if (!isStatic) { gboolean hasAdv = TRUE; int delay = 0; int cntFrames = 0; GdkPixbuf *pixbuf = NULL; GTimeVal curTime; g_get_current_time(&curTime); anim_iter = gdk_pixbuf_animation_get_iter(anim, &curTime); while (hasAdv) { gboolean has_new_frame = FALSE; g_get_current_time(&curTime); has_new_frame = gdk_pixbuf_animation_iter_advance(anim_iter, &curTime); if (has_new_frame) { cntFrames += 1; pixbuf = gdk_pixbuf_animation_iter_get_pixbuf(anim_iter); int w = gdk_pixbuf_get_width(pixbuf); int h = gdk_pixbuf_get_height(pixbuf); if (cntFrames == 1) { g_print("Width is %d and Height is %d .\n", w, h); g_assert(w == 300); g_assert(h == 300); } } else { break; } delay = gdk_pixbuf_animation_iter_get_delay_time(anim_iter); if ((delay < 0) || (cntFrames > 100)) { break; } } g_print("Total frames parsed: %d\n", cntFrames); g_assert(cntFrames == 10); /* note there should be 2 loops in the test t3.webp file. */ g_object_unref(anim_iter); } g_object_unref(anim); g_strfreev(env); return 0; } webp-pixbuf-loader-0.0.5/tests/t3.webp000066400000000000000000000055721424026115000175630ustar00rootroot00000000000000RIFFr WEBPVP8X ++ANIMANMF;2BKdVP8L/B {Hj /"V۶,ϯݒC0%:8Tv` C Nn@t{A,L&"BQT,T!YF5Ԗat}( | %uPC֙EQkB]wTcI-_kӹMZbANMF81GMALPHlE۶i;Zm۶m۶m|ٶm۶m;y;3TE0Qe7h߫sjϚiN Чǖ/ * \{7~]j 2,9<{Gj }\wlIzwfueJ"VyJV=9a޽}Sslהk$e2^Z' ƞw+GJG>K:ʑS]FU3(ufP^d c櫑Uu=j"HF0\g2yW-s]ˌT6ɠf â ؒZx1[Ei 'R Hw`љ&ع3+:j]v}316ј>֢ؐ?웲1n H3 ׳lWs#B`憎T@O, m} {@.}k̍, ch,آ0 z1B=l0yg3B[{?c\0Aзzs˜+@"+.(<L4zRQ6p]f\FQh6t[<aQ17h>ƭRɻ8VP8 4*HN>HL#"!H i F4hѣF~HANMFT91HOALPH !Im{mVm۶m۶m69ͤ K[\ڊ]Q:7_Ltg&h~rly*I:Bu%|OvE_+2Y>ߧFiωZx?fԺBYcb.|c N֋v~ŀ\A1[Ed‡}#A}zgb:I:ڷh; Rp-f5 tp>3|xps!Oϑ( 8Lc __!{7Y;q鳷\"ҀQR,m@},W6 )U;2_̩׆(DYw~ OUZ9߉@<*M}6;kFBcv8En\r='bd @S4JF)sS3cK9(QrF)=8_$|,WK%nHaQR}Olkn#yɵ܄5:eOs"9=}nX:1 nt=>~A;+ҶSgośAMf&&2z6<+@`t?d2#7ĕ4csL7/Rze.۴ h3m].x}cc}|\ARVP8 4*IP>HL#"!H i F4hѣF~HANMF62RKALPHٶi[m۶m۶ٶm۶mۚټѯD{G91<{üf@ =#gtN/UD? P7J "&w?+@c |I l؁&!MBW`zIH jS ~ ={MA;`XW^f b^{3)< abSqԦ wYLA>r`1,o |V],))) zIzy7.wf Q7c>Hl <)(7r/`4 gt.hjG4WĢ?#ۣ 4OL&S+VP8 6P*SL>HL#"!( ijիVZjզANMF~92GMALPH)Ii[}m<۶m۶m۶m۶m۶q8{^^ 8CI6: ^ 55a]vm[rnʦ nWlxkdu-[WǷ"ADGZѮVA^^}{S'.y C}>2b)`3z6=UD/)kDP RƘ<>0?J VЀ_pY&ާ>z E,WZ!RvJ* /8dPl1)#g ƜrJr,grrFi &^L/b~-g).#ny){GHC7)Dzt/Ux{ `I }]SO_#olVx6Rp)LZu '~9cuk״iӦ]{;*uh2> y?!Dض 3i!itipT&ɠe<ȍ wfA&&!B84VP8 4*HN>HL#"!H i F4hѣF~Hwebp-pixbuf-loader-0.0.5/tests/t4-gtk3.c000066400000000000000000000061731424026115000177150ustar00rootroot00000000000000#include #include /* This test is not an automated test. * It is intended to allow one to eye-ball webp animated images * in a debug environment, to see if there are problems with * image reproduction, timing, etc. */ typedef struct _AnimationStructure { GtkWindow *window; GdkPixbufAnimation *anim; GdkPixbufAnimationIter *anim_iter; GtkWidget *image; int delay; } AnimationStructure; gboolean delete_objects(GtkWidget *widget, GdkEvent *event, gpointer data) { AnimationStructure *ani = (AnimationStructure *) data; if (ani->anim) g_object_unref(ani->anim); g_free(ani); return FALSE; } static void activate(GtkApplication *app, gpointer user_data) { GtkWidget *window; GtkWidget *label; AnimationStructure *ani = (AnimationStructure *) user_data; window = gtk_application_window_new(app); GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); label = gtk_label_new("Test WebP Animation"); gtk_container_add(GTK_CONTAINER (box), label); GtkWidget *image = NULL; /*GdkPixbuf *staticPixbuf = NULL; staticPixbuf = gdk_pixbuf_animation_get_static_image (ani->anim); image = gtk_image_new_from_pixbuf (staticPixbuf); */ image = gtk_image_new_from_animation(ani->anim); gtk_container_add(GTK_CONTAINER (box), image); gtk_container_add(GTK_CONTAINER (window), box); gtk_window_set_title(GTK_WINDOW (window), "Test"); gtk_window_set_default_size(GTK_WINDOW (window), 500, 500); g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(delete_objects), ani); gtk_widget_show_all(window); } /* end of function activate */ gint main(gint argc, gchar **argv) { GError *error = NULL; gchar **env = g_get_environ(); g_warning("%s", g_environ_getenv(env, "TEST_FILE")); gtk_init(&argc, &argv); /* setup animation. */ GdkPixbufAnimation *anim = NULL; GdkPixbufAnimationIter *anim_iter = NULL; anim = gdk_pixbuf_animation_new_from_file(g_environ_getenv(env, "TEST_FILE"), &error); gboolean isStatic = gdk_pixbuf_animation_is_static_image(anim); if (!isStatic) { GtkApplication *app; GTimeVal curTime; AnimationStructure *ani = g_new0(AnimationStructure, 1); ani->anim = anim; g_get_current_time(&curTime); anim_iter = gdk_pixbuf_animation_get_iter(anim, &curTime); int delay = gdk_pixbuf_animation_iter_get_delay_time(anim_iter); ani->anim_iter = anim_iter; ani->delay = delay; app = gtk_application_new(NULL, G_APPLICATION_FLAGS_NONE); g_signal_connect(app, "activate", G_CALLBACK(activate), ani); (void) g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); } g_strfreev(env); return 0; } webp-pixbuf-loader-0.0.5/tests/t4.c000066400000000000000000000106751424026115000170510ustar00rootroot00000000000000#include #define IMAGE_READ_BUFFER_SIZE 500 gint main(gint argc, gchar **argv) { GError *err = NULL; GError **error = &err; gchar **env = g_get_environ(); g_warning("%s", g_environ_getenv(env, "TEST_FILE")); const gchar *file_path = g_environ_getenv(env, "TEST_FILE"); /* setup animation. */ GFileInputStream *input_stream; goffset bytes_read = 0; goffset bytes_read_total = 0; guchar *buffer = NULL; gchar *mime_type = "image/webp"; GdkPixbufLoader *loader = NULL; GdkPixbufAnimation *anim = NULL; GdkPixbufAnimationIter *anim_iter = NULL; gboolean failed = FALSE; GFile *file1 = g_file_new_for_path(file_path); input_stream = g_file_read (file1, NULL, error); buffer = g_new0 (guchar, IMAGE_READ_BUFFER_SIZE); loader = gdk_pixbuf_loader_new_with_mime_type(mime_type, error); if (! GDK_IS_PIXBUF_LOADER(loader)) { g_assert(FALSE); } bytes_read = g_input_stream_read (G_INPUT_STREAM (input_stream), buffer, IMAGE_READ_BUFFER_SIZE, NULL, error); bytes_read_total += bytes_read; while (bytes_read > 0) { if (!gdk_pixbuf_loader_write(loader, buffer, bytes_read, error)) { g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Failed to parse WebP input stream"); failed = TRUE; break; } bytes_read = g_input_stream_read (G_INPUT_STREAM (input_stream), buffer, IMAGE_READ_BUFFER_SIZE, NULL, error); bytes_read_total += bytes_read; } g_assert((bytes_read == 0) && (bytes_read_total > 0)); /* Finished reading all data. */ g_assert(!failed); /* Data accumulation has not failed. */ anim = gdk_pixbuf_loader_get_animation(loader); g_assert((anim != NULL)); /* animation has been created. */ if (gdk_pixbuf_animation_is_static_image(anim)) { GdkPixbuf *staticPixbuf; staticPixbuf = gdk_pixbuf_animation_get_static_image(anim); g_assert((staticPixbuf != NULL)); g_object_unref(staticPixbuf); g_object_unref(anim); goto end_of_test; } gboolean hasAdv = TRUE; int cntFrames = 0; int delay = 0; GTimeVal curTime; GdkPixbuf *pixbuf = NULL; anim_iter = gdk_pixbuf_animation_get_iter(anim, NULL); g_assert((anim_iter != NULL)); /* animation iterator has been created. */ while (hasAdv) { gboolean has_new_frame = FALSE; g_get_current_time(&curTime); has_new_frame = gdk_pixbuf_animation_iter_advance(anim_iter, &curTime); if (has_new_frame) { cntFrames += 1; pixbuf = gdk_pixbuf_animation_iter_get_pixbuf(anim_iter); int w = gdk_pixbuf_get_width(pixbuf); int h = gdk_pixbuf_get_height(pixbuf); if (cntFrames == 1) { g_print("Width is %d and Height is %d .\n", w, h); g_assert(w == 300); g_assert(h == 300); } } else { break; } delay = gdk_pixbuf_animation_iter_get_delay_time(anim_iter); if ((delay < 0) || (cntFrames > 100)) { break; } } g_print("Total frames parsed: %d\n", cntFrames); g_assert(cntFrames == 10); /* note there should be 2 loops in the test t3.webp file. */ end_of_test: g_object_unref(G_OBJECT (input_stream)); g_object_unref(G_OBJECT (file1)); gdk_pixbuf_loader_close(loader, error); g_object_unref(loader); g_object_unref(anim_iter); g_object_unref(anim); g_free(buffer); g_strfreev(env); return 0; } webp-pixbuf-loader-0.0.5/tests/t5.c000066400000000000000000000123041424026115000170410ustar00rootroot00000000000000/* * This tests duration for various frames in t5.webp . * The first frame and the fifth (last) frame have duration 1000, * and the other frames have duration 300. * The webp container has 3 loops. */ #include #define IMAGE_READ_BUFFER_SIZE 500 gint main(gint argc, gchar **argv) { GError *err = NULL; GError **error = &err; gchar **env = g_get_environ(); g_warning("%s", g_environ_getenv(env, "TEST_FILE")); const gchar *file_path = g_environ_getenv(env, "TEST_FILE"); /* setup animation. */ GFileInputStream *input_stream; goffset bytes_read = 0; goffset bytes_read_total = 0; guchar *buffer = NULL; gchar *mime_type = "image/webp"; GdkPixbufLoader *loader = NULL; GdkPixbufAnimation *anim = NULL; GdkPixbufAnimationIter *anim_iter = NULL; gboolean failed = FALSE; GFile *file1 = g_file_new_for_path(file_path); input_stream = g_file_read (file1, NULL, error); buffer = g_new0 (guchar, IMAGE_READ_BUFFER_SIZE); loader = gdk_pixbuf_loader_new_with_mime_type(mime_type, error); if (! GDK_IS_PIXBUF_LOADER(loader)) g_assert(FALSE); bytes_read = g_input_stream_read (G_INPUT_STREAM (input_stream), buffer, IMAGE_READ_BUFFER_SIZE, NULL, error); bytes_read_total += bytes_read; while (bytes_read > 0) { if (!gdk_pixbuf_loader_write(loader, buffer, bytes_read, error)) { g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, "Failed to parse WebP input stream"); failed = TRUE; break; } bytes_read = g_input_stream_read (G_INPUT_STREAM (input_stream), buffer, IMAGE_READ_BUFFER_SIZE, NULL, error); bytes_read_total += bytes_read; } g_assert((bytes_read == 0) && (bytes_read_total > 0)); /* Finished reading all data. */ g_assert(!failed); /* Data accumulation has not failed. */ anim = gdk_pixbuf_loader_get_animation(loader); g_assert((anim != NULL)); /* animation has been created. */ if (gdk_pixbuf_animation_is_static_image(anim)) { GdkPixbuf *staticPixbuf; staticPixbuf = gdk_pixbuf_animation_get_static_image(anim); g_assert((staticPixbuf != NULL)); g_object_unref(staticPixbuf); g_object_unref(anim); goto end_of_test; } gboolean hasAdv = TRUE; int cntFrames = 0; int delay = 0; GTimeVal curTime; GdkPixbuf *pixbuf = NULL; anim_iter = gdk_pixbuf_animation_get_iter(anim, NULL); g_assert((anim_iter != NULL)); /* animation iterator has been created. */ while (hasAdv) { if (cntFrames == 0) { delay = gdk_pixbuf_animation_iter_get_delay_time(anim_iter); g_assert(delay == 1000); } gboolean has_new_frame = FALSE; g_get_current_time(&curTime); has_new_frame = gdk_pixbuf_animation_iter_advance(anim_iter, &curTime); if (has_new_frame) { cntFrames += 1; pixbuf = gdk_pixbuf_animation_iter_get_pixbuf(anim_iter); int w = gdk_pixbuf_get_width(pixbuf); int h = gdk_pixbuf_get_height(pixbuf); if (cntFrames == 1) { g_print("Width is %d and Height is %d .\n", w, h); g_assert(w == 300); g_assert(h == 300); } } else { break; } delay = gdk_pixbuf_animation_iter_get_delay_time(anim_iter); if ((delay < 0) || (cntFrames > 100)) break; /* check duration for various frames. */ if (cntFrames == 5 || cntFrames == 10) g_assert(delay == 1000); if (cntFrames == 1 || cntFrames == 6 || cntFrames == 11) g_assert(delay == 300); if (cntFrames == 4 || cntFrames == 9 || cntFrames == 14) g_assert(delay == 1000); } g_print("Total frames parsed: %d\n", cntFrames); g_assert(cntFrames == 15); /* note there should be 3 loops in the test t5.webp file. */ end_of_test: g_object_unref(G_OBJECT (input_stream)); g_object_unref(G_OBJECT (file1)); gdk_pixbuf_loader_close(loader, error); g_object_unref(loader); g_object_unref(anim_iter); g_object_unref(anim); g_free(buffer); g_strfreev(env); return 0; } webp-pixbuf-loader-0.0.5/tests/t5.webp000066400000000000000000000104001424026115000175470ustar00rootroot00000000000000RIFFWEBPVP8X ++ANIMANMF52ZALPH5E۶)I:m۶m۶vٶڶ]mQ>f 5V\?1nq gVx Zrpx0]iŴ^՛<`.v.D| =(.ƶG&w)\\}|gp?WB3]_c䥬\AUXFj}rRXT =a';&'+>;8˟?hî`{B\l \"[~X,8^hec&Of1{/Oml*UO8lPZ9yy?/L,l;5SD$t/.A`^T~W}@ G]_ "`87~ u 7e <܈9(sl/̷IX,o hgr޷i tԄr0J9،hd{~f*^{F"lE+ 4W&W}^s60hj{e٪@;CΚV@Ci4qO`C nR|j b 0VL>q觀݀)Ee&Ö~rHEĘٱŰ`Nn8$"Ծtqo g#o;Ϻx-ll$ޘ^eY5B!{Փ'𫘍Hx1 Ch}n}t`HL#"  insck^XEk^X5iANMF81GM,ALPHlE۶i;Zm۶m۶m|ٶm۶m;y;3TE0Qe7h߫sjϚiN Чǖ/ * \{7~]j 2,9<{Gj }\wlIzwfueJ"VyJV=9a޽}Sslהk$e2^Z' ƞw+GJG>K:ʑS]FU3(ufP^d c櫑Uu=j"HF0\g2yW-s]ˌT6ɠf â ؒZx1[Ei 'R Hw`љ&ع3+:j]v}316ј>֢ؐ?웲1n H3 ׳lWs#B`憎T@O, m} {@.}k̍, ch,آ0 z1B=l0yg3B[{?c\0Aзzs˜+@"+.(<L4zRQ6p]f\FQh6t[<aQ17h>ƭRɻ8VP8 4*HN>HL#"!H i F4hѣF~HANMFT91HO,ALPH !Im{mVm۶m۶m69ͤ K[\ڊ]Q:7_Ltg&h~rly*I:Bu%|OvE_+2Y>ߧFiωZx?fԺBYcb.|c N֋v~ŀ\A1[Ed‡}#A}zgb:I:ڷh; Rp-f5 tp>3|xps!Oϑ( 8Lc __!{7Y;q鳷\"ҀQR,m@},W6 )U;2_̩׆(DYw~ OUZ9߉@<*M}6;kFBcv8En\r='bd @S4JF)sS3cK9(QrF)=8_$|,WK%nHaQR}Olkn#yɵ܄5:eOs"9=}nX:1 nt=>~A;+ҶSgośAMf&&2z6<+@`t?d2#7ĕ4csL7/Rze.۴ h3m].x}cc}|\ARVP8 4*IP>HL#"!H i F4hѣF~HANMF62RK,ALPHٶi[m۶m۶ٶm۶mۚټѯD{G91<{üf@ =#gtN/UD? P7J "&w?+@c |I l؁&!MBW`zIH jS ~ ={MA;`XW^f b^{3)< abSqԦ wYLA>r`1,o |V],))) zIzy7.wf Q7c>Hl <)(7r/`4 gt.hjG4WĢ?#ۣ 4OL&S+VP8 6P*SL>HL#"!( ijիVZjզANMF(32YALPHmFzj<ضm۶m۶ms9c۶n\?w#"&@ ;Bp 8Qx?tu1{w_7|VLضF9G)_}%"J[RL[ o߆E/^?q?Zѻ+F%:j4vxRo8Suonb}^L %AŮW*kB$CԆcȝC!qoqgЇ`Fs#u4"T|#tT1A'EIy#뤁e:cdN1Z##P G #D1ZK0z3@m<:&c<hN]gUo1>B.M/dDcjǟTgUpmᘹ=@,zgVE`a. [xtkWgسC jϕpL akDS&D=?K%GWSۣDΖņ kvX>D$śl/*y5cUH]H*$$X'ދOu\gEO4дXiL*eV<6'JŋɳtmVIo\/jZ=v9҅wWV%Pɬ -@B',qx%T$ȏNϰZĬxxG~C$*-\K"^ڨ!^~xsƊ ~e 3+𕷲w{]x(.ė[ER ((MP_̻o*i} CUq">i9\pDTBG1@Hـ JM v+=xZe F%]6jEفqJMOp^)Z(Qiy'C%?"W3oPWe,êπ< "̫|>ZU آ0Wf*+@ \ !D፽sY&=YZDOùDd5+Wx`X'È|7n T0+`5b_ %w})/̫_F:_:K3;HPi[NQ .W\ʕ T HU0s,#z>EEq_kd>_FWC:iRͨ^,:XX[4ip, VP8 B*Z>HL#"  insck^XEk^X5iwebp-pixbuf-loader-0.0.5/tests/t6.c000066400000000000000000000010521424026115000170400ustar00rootroot00000000000000#include gint main (gint argc, gchar **argv) { GError *error = NULL; gchar **env = g_get_environ(); g_warning("%s", g_environ_getenv(env, "TEST_FILE")); GdkPixbufFormat *format = gdk_pixbuf_get_file_info (g_environ_getenv(env, "TEST_FILE"), NULL, NULL); if (!format) { g_error("%s", error->message); }; g_assert(format != NULL); gchar *name = gdk_pixbuf_format_get_name (format); g_assert_cmpstr(name, ==, "webp"); g_free (name); g_strfreev (env); gdk_pixbuf_format_free (format); return 0; } webp-pixbuf-loader-0.0.5/webp-pixbuf.thumbnailer.in000066400000000000000000000002021424026115000222660ustar00rootroot00000000000000[Thumbnailer Entry] TryExec=@bindir@/gdk-pixbuf-thumbnailer Exec=@bindir@/gdk-pixbuf-thumbnailer -s %s %u %o MimeType=image/webp;