/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Pix * * Copyright (C) 2009 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "gth-image-saver-webp.h" #include "preferences.h" #define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x))) struct _GthImageSaverWebpPrivate { GtkBuilder *builder; GSettings *settings; }; G_DEFINE_TYPE_WITH_CODE (GthImageSaverWebp, gth_image_saver_webp, GTH_TYPE_IMAGE_SAVER, G_ADD_PRIVATE (GthImageSaverWebp)) static void gth_image_saver_webp_finalize (GObject *object) { GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (object); _g_object_unref (self->priv->builder); _g_object_unref (self->priv->settings); G_OBJECT_CLASS (gth_image_saver_webp_parent_class)->finalize (object); } static GtkWidget * gth_image_saver_webp_get_control (GthImageSaver *base) { GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (base); _g_object_unref (self->priv->builder); self->priv->builder = _gtk_builder_new_from_file ("webp-options.ui", "cairo_io"); gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("quality_adjustment")), g_settings_get_int (self->priv->settings, PREF_WEBP_QUALITY)); gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("method_adjustment")), g_settings_get_int (self->priv->settings, PREF_WEBP_METHOD)); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("lossless_checkbutton")), g_settings_get_boolean (self->priv->settings, PREF_WEBP_LOSSLESS)); return GET_WIDGET ("webp_options"); } static void gth_image_saver_webp_save_options (GthImageSaver *base) { GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (base); g_settings_set_int (self->priv->settings, PREF_WEBP_QUALITY, (int) gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET ("quality_adjustment")))); g_settings_set_int (self->priv->settings, PREF_WEBP_METHOD, (int) gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET ("method_adjustment")))); g_settings_set_boolean (self->priv->settings, PREF_WEBP_LOSSLESS, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("lossless_checkbutton")))); } static gboolean gth_image_saver_webp_can_save (GthImageSaver *self, const char *mime_type) { return g_content_type_equals (mime_type, "image/webp"); } typedef struct { GError **error; GthBufferData *buffer_data; int success; } CairoWebpData; static void _cairo_webp_data_destroy (CairoWebpData *cairo_webp_data) { gth_buffer_data_free (cairo_webp_data->buffer_data, ! cairo_webp_data->success); g_free (cairo_webp_data); } static int cairo_webp_writer_func (const uint8_t *data, size_t data_size, const WebPPicture *picture) { CairoWebpData *cairo_webp_data = picture->custom_ptr; cairo_webp_data->success = gth_buffer_data_write (cairo_webp_data->buffer_data, (void *) data, data_size, cairo_webp_data->error); return cairo_webp_data->success; } static int _WebPPictureImportCairoSurface (WebPPicture *const picture, cairo_surface_t *image) { int stride; guchar *src_row; uint32_t *dest_row; int y, x, temp; guchar r, g, b, a; if (_cairo_image_surface_get_has_alpha (image)) picture->colorspace |= WEBP_CSP_ALPHA_BIT; else picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; if (! WebPPictureAlloc (picture)) return 0; stride = cairo_image_surface_get_stride (image); src_row = _cairo_image_surface_flush_and_get_data (image); dest_row = picture->argb; for (y= 0; y < cairo_image_surface_get_height (image); y++) { guchar *pixel = src_row; for (x = 0; x < cairo_image_surface_get_width (image); x++) { CAIRO_GET_RGBA (pixel, r, g, b, a); dest_row[x] = ((a << 24) | (r << 16) | (g << 8) | b); pixel += 4; } src_row += stride; dest_row += picture->argb_stride; } return 1; } static gboolean _cairo_surface_write_as_webp (cairo_surface_t *image, char **buffer, gsize *buffer_size, char **keys, char **values, GError **error) { gboolean lossless; int quality; int method; WebPConfig config; CairoWebpData *cairo_webp_data; WebPPicture pic; lossless = TRUE; quality = 75; method = 4; if (keys && *keys) { char **kiter = keys; char **viter = values; while (*kiter) { if (strcmp (*kiter, "lossless") == 0) { if (*viter == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Must specify a value for the 'lossless' option"); return FALSE; } lossless = atoi (*viter); if (lossless < 0 || lossless > 1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid value set for the 'lossless' option of the WebP saver"); return FALSE; } } else if (strcmp (*kiter, "quality") == 0) { if (*viter == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Must specify a quality value to the WebP saver"); return FALSE; } quality = atoi (*viter); if (quality < 0 || quality > 100) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unsupported quality value passed to the WebP saver"); return FALSE; } } else if (strcmp (*kiter, "method") == 0) { if (*viter == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Must specify a method value to the WebP saver"); return FALSE; } method = atoi (*viter); if (method < 0 || method > 6) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unsupported method value passed to the WebP saver"); return FALSE; } } else { g_warning ("Bad option name '%s' passed to the WebP saver", *kiter); return FALSE; } ++kiter; ++viter; } } if (! WebPConfigInit (&config)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Version error"); return FALSE; } config.lossless = lossless; config.quality = quality; config.method = method; if (! WebPValidateConfig (&config)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Config error"); return FALSE; } cairo_webp_data = g_new0 (CairoWebpData, 1); cairo_webp_data->error = error; cairo_webp_data->buffer_data = gth_buffer_data_new (); cairo_webp_data->success = FALSE; WebPPictureInit (&pic); pic.width = cairo_image_surface_get_width (image); pic.height = cairo_image_surface_get_height (image); pic.writer = cairo_webp_writer_func; pic.custom_ptr = cairo_webp_data; pic.use_argb = TRUE; if (_WebPPictureImportCairoSurface (&pic, image)) { int ok = WebPEncode (&config, &pic); WebPPictureFree (&pic); if (cairo_webp_data->success && ! ok) { g_set_error (cairo_webp_data->error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Encoding error: %d", pic.error_code); cairo_webp_data->success = FALSE; } } else { g_set_error (cairo_webp_data->error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Memory error"); cairo_webp_data->success = FALSE; } if (cairo_webp_data->success) gth_buffer_data_get (cairo_webp_data->buffer_data, buffer, buffer_size); _cairo_webp_data_destroy (cairo_webp_data); return TRUE; } static gboolean gth_image_saver_webp_save_image (GthImageSaver *base, GthImage *image, char **buffer, gsize *buffer_size, const char *mime_type, GCancellable *cancellable, GError **error) { GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (base); cairo_surface_t *surface; char **option_keys; char **option_values; int i = -1; int i_value; gboolean result; option_keys = g_new (char *, 4); option_values = g_new (char *, 4); i++; i_value = g_settings_get_boolean (self->priv->settings, PREF_WEBP_LOSSLESS); option_keys[i] = g_strdup ("lossless");; option_values[i] = g_strdup_printf ("%d", i_value); i++; i_value = g_settings_get_int (self->priv->settings, PREF_WEBP_QUALITY); option_keys[i] = g_strdup ("quality");; option_values[i] = g_strdup_printf ("%d", i_value); i++; i_value = g_settings_get_int (self->priv->settings, PREF_WEBP_METHOD); option_keys[i] = g_strdup ("method");; option_values[i] = g_strdup_printf ("%d", i_value); i++; option_keys[i] = NULL; option_values[i] = NULL; surface = gth_image_get_cairo_surface (image); result = _cairo_surface_write_as_webp (surface, buffer, buffer_size, option_keys, option_values, error); cairo_surface_destroy (surface); g_strfreev (option_keys); g_strfreev (option_values); return result; } static void gth_image_saver_webp_class_init (GthImageSaverWebpClass *klass) { GObjectClass *object_class; GthImageSaverClass *image_saver_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = gth_image_saver_webp_finalize; image_saver_class = GTH_IMAGE_SAVER_CLASS (klass); image_saver_class->id = "webp"; image_saver_class->display_name = _("WebP"); image_saver_class->mime_type = "image/webp"; image_saver_class->extensions = "webp"; image_saver_class->get_default_ext = NULL; image_saver_class->get_control = gth_image_saver_webp_get_control; image_saver_class->save_options = gth_image_saver_webp_save_options; image_saver_class->can_save = gth_image_saver_webp_can_save; image_saver_class->save_image = gth_image_saver_webp_save_image; } static void gth_image_saver_webp_init (GthImageSaverWebp *self) { self->priv = gth_image_saver_webp_get_instance_private (self); self->priv->settings = g_settings_new (PIX_IMAGE_SAVERS_WEBP_SCHEMA); self->priv->builder = NULL; }