1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <webp/encode.h>
24 #include <glib/gi18n.h>
25 #include <gthumb.h>
26 #include "gth-image-saver-webp.h"
27 #include "preferences.h"
28
29
30 #define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
31
32
33 struct _GthImageSaverWebpPrivate {
34 GtkBuilder *builder;
35 GSettings *settings;
36 };
37
38
G_DEFINE_TYPE_WITH_CODE(GthImageSaverWebp,gth_image_saver_webp,GTH_TYPE_IMAGE_SAVER,G_ADD_PRIVATE (GthImageSaverWebp))39 G_DEFINE_TYPE_WITH_CODE (GthImageSaverWebp,
40 gth_image_saver_webp,
41 GTH_TYPE_IMAGE_SAVER,
42 G_ADD_PRIVATE (GthImageSaverWebp))
43
44
45 static void
46 gth_image_saver_webp_finalize (GObject *object)
47 {
48 GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (object);
49
50 _g_object_unref (self->priv->builder);
51 _g_object_unref (self->priv->settings);
52 G_OBJECT_CLASS (gth_image_saver_webp_parent_class)->finalize (object);
53 }
54
55
56 static GtkWidget *
gth_image_saver_webp_get_control(GthImageSaver * base)57 gth_image_saver_webp_get_control (GthImageSaver *base)
58 {
59 GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (base);
60
61 _g_object_unref (self->priv->builder);
62 self->priv->builder = _gtk_builder_new_from_file ("webp-options.ui", "cairo_io");
63
64 gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("quality_adjustment")),
65 g_settings_get_int (self->priv->settings, PREF_WEBP_QUALITY));
66 gtk_adjustment_set_value (GTK_ADJUSTMENT (GET_WIDGET ("method_adjustment")),
67 g_settings_get_int (self->priv->settings, PREF_WEBP_METHOD));
68 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("lossless_checkbutton")),
69 g_settings_get_boolean (self->priv->settings, PREF_WEBP_LOSSLESS));
70
71 return GET_WIDGET ("webp_options");
72 }
73
74
75 static void
gth_image_saver_webp_save_options(GthImageSaver * base)76 gth_image_saver_webp_save_options (GthImageSaver *base)
77 {
78 GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (base);
79
80 g_settings_set_int (self->priv->settings, PREF_WEBP_QUALITY, (int) gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET ("quality_adjustment"))));
81 g_settings_set_int (self->priv->settings, PREF_WEBP_METHOD, (int) gtk_adjustment_get_value (GTK_ADJUSTMENT (GET_WIDGET ("method_adjustment"))));
82 g_settings_set_boolean (self->priv->settings, PREF_WEBP_LOSSLESS, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("lossless_checkbutton"))));
83 }
84
85
86 static gboolean
gth_image_saver_webp_can_save(GthImageSaver * self,const char * mime_type)87 gth_image_saver_webp_can_save (GthImageSaver *self,
88 const char *mime_type)
89 {
90 return g_content_type_equals (mime_type, "image/webp");
91 }
92
93
94 typedef struct {
95 GError **error;
96 GthBufferData *buffer_data;
97 int success;
98 } CairoWebpData;
99
100
101 static void
_cairo_webp_data_destroy(CairoWebpData * cairo_webp_data)102 _cairo_webp_data_destroy (CairoWebpData *cairo_webp_data)
103 {
104 gth_buffer_data_free (cairo_webp_data->buffer_data, ! cairo_webp_data->success);
105 g_free (cairo_webp_data);
106 }
107
108
109 static int
cairo_webp_writer_func(const uint8_t * data,size_t data_size,const WebPPicture * picture)110 cairo_webp_writer_func (const uint8_t *data,
111 size_t data_size,
112 const WebPPicture *picture)
113 {
114 CairoWebpData *cairo_webp_data = picture->custom_ptr;
115
116 cairo_webp_data->success = gth_buffer_data_write (cairo_webp_data->buffer_data,
117 (void *) data,
118 data_size,
119 cairo_webp_data->error);
120
121 return cairo_webp_data->success;
122 }
123
124
125 static int
_WebPPictureImportCairoSurface(WebPPicture * const picture,cairo_surface_t * image)126 _WebPPictureImportCairoSurface (WebPPicture *const picture,
127 cairo_surface_t *image)
128 {
129 int stride;
130 guchar *src_row;
131 uint32_t *dest_row;
132 int y, x, temp;
133 guchar r, g, b, a;
134
135 if (_cairo_image_surface_get_has_alpha (image))
136 picture->colorspace |= WEBP_CSP_ALPHA_BIT;
137 else
138 picture->colorspace &= ~WEBP_CSP_ALPHA_BIT;
139
140 if (! WebPPictureAlloc (picture))
141 return 0;
142
143 stride = cairo_image_surface_get_stride (image);
144 src_row = _cairo_image_surface_flush_and_get_data (image);
145 dest_row = picture->argb;
146
147 for (y= 0; y < cairo_image_surface_get_height (image); y++) {
148 guchar *pixel = src_row;
149
150 for (x = 0; x < cairo_image_surface_get_width (image); x++) {
151 CAIRO_GET_RGBA (pixel, r, g, b, a);
152 dest_row[x] = ((a << 24) | (r << 16) | (g << 8) | b);
153
154 pixel += 4;
155 }
156
157 src_row += stride;
158 dest_row += picture->argb_stride;
159 }
160
161 return 1;
162 }
163
164
165 static gboolean
_cairo_surface_write_as_webp(cairo_surface_t * image,char ** buffer,gsize * buffer_size,char ** keys,char ** values,GError ** error)166 _cairo_surface_write_as_webp (cairo_surface_t *image,
167 char **buffer,
168 gsize *buffer_size,
169 char **keys,
170 char **values,
171 GError **error)
172 {
173 gboolean lossless;
174 int quality;
175 int method;
176 WebPConfig config;
177 CairoWebpData *cairo_webp_data;
178 WebPPicture pic;
179
180 lossless = TRUE;
181 quality = 75;
182 method = 4;
183
184 if (keys && *keys) {
185 char **kiter = keys;
186 char **viter = values;
187
188 while (*kiter) {
189 if (strcmp (*kiter, "lossless") == 0) {
190 if (*viter == NULL) {
191 g_set_error (error,
192 G_IO_ERROR,
193 G_IO_ERROR_INVALID_DATA,
194 "Must specify a value for the 'lossless' option");
195 return FALSE;
196 }
197
198 lossless = atoi (*viter);
199
200 if (lossless < 0 || lossless > 1) {
201 g_set_error (error,
202 G_IO_ERROR,
203 G_IO_ERROR_INVALID_DATA,
204 "Invalid value set for the 'lossless' option of the WebP saver");
205 return FALSE;
206 }
207 }
208 else if (strcmp (*kiter, "quality") == 0) {
209 if (*viter == NULL) {
210 g_set_error (error,
211 G_IO_ERROR,
212 G_IO_ERROR_INVALID_DATA,
213 "Must specify a quality value to the WebP saver");
214 return FALSE;
215 }
216
217 quality = atoi (*viter);
218
219 if (quality < 0 || quality > 100) {
220 g_set_error (error,
221 G_IO_ERROR,
222 G_IO_ERROR_INVALID_DATA,
223 "Unsupported quality value passed to the WebP saver");
224 return FALSE;
225 }
226 }
227 else if (strcmp (*kiter, "method") == 0) {
228 if (*viter == NULL) {
229 g_set_error (error,
230 G_IO_ERROR,
231 G_IO_ERROR_INVALID_DATA,
232 "Must specify a method value to the WebP saver");
233 return FALSE;
234 }
235
236 method = atoi (*viter);
237
238 if (method < 0 || method > 6) {
239 g_set_error (error,
240 G_IO_ERROR,
241 G_IO_ERROR_INVALID_DATA,
242 "Unsupported method value passed to the WebP saver");
243 return FALSE;
244 }
245 }
246 else {
247 g_warning ("Bad option name '%s' passed to the WebP saver", *kiter);
248 return FALSE;
249 }
250
251 ++kiter;
252 ++viter;
253 }
254 }
255
256 if (! WebPConfigInit (&config)) {
257 g_set_error (error,
258 G_IO_ERROR,
259 G_IO_ERROR_INVALID_DATA,
260 "Version error");
261 return FALSE;
262 }
263
264 config.lossless = lossless;
265 config.quality = quality;
266 config.method = method;
267
268 if (! WebPValidateConfig (&config)) {
269 g_set_error (error,
270 G_IO_ERROR,
271 G_IO_ERROR_INVALID_DATA,
272 "Config error");
273 return FALSE;
274 }
275
276 cairo_webp_data = g_new0 (CairoWebpData, 1);
277 cairo_webp_data->error = error;
278 cairo_webp_data->buffer_data = gth_buffer_data_new ();
279 cairo_webp_data->success = FALSE;
280
281 WebPPictureInit (&pic);
282 pic.width = cairo_image_surface_get_width (image);
283 pic.height = cairo_image_surface_get_height (image);
284 pic.writer = cairo_webp_writer_func;
285 pic.custom_ptr = cairo_webp_data;
286 pic.use_argb = TRUE;
287
288 if (_WebPPictureImportCairoSurface (&pic, image)) {
289 int ok = WebPEncode (&config, &pic);
290 WebPPictureFree (&pic);
291
292 if (cairo_webp_data->success && ! ok) {
293 g_set_error (cairo_webp_data->error,
294 G_IO_ERROR,
295 G_IO_ERROR_INVALID_DATA,
296 "Encoding error: %d", pic.error_code);
297 cairo_webp_data->success = FALSE;
298 }
299 }
300 else {
301 g_set_error (cairo_webp_data->error,
302 G_IO_ERROR,
303 G_IO_ERROR_INVALID_DATA,
304 "Memory error");
305 cairo_webp_data->success = FALSE;
306 }
307
308 if (cairo_webp_data->success)
309 gth_buffer_data_get (cairo_webp_data->buffer_data, buffer, buffer_size);
310
311 _cairo_webp_data_destroy (cairo_webp_data);
312
313 return TRUE;
314 }
315
316
317 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)318 gth_image_saver_webp_save_image (GthImageSaver *base,
319 GthImage *image,
320 char **buffer,
321 gsize *buffer_size,
322 const char *mime_type,
323 GCancellable *cancellable,
324 GError **error)
325 {
326 GthImageSaverWebp *self = GTH_IMAGE_SAVER_WEBP (base);
327 cairo_surface_t *surface;
328 char **option_keys;
329 char **option_values;
330 int i = -1;
331 int i_value;
332 gboolean result;
333
334 option_keys = g_new (char *, 4);
335 option_values = g_new (char *, 4);
336
337 i++;
338 i_value = g_settings_get_boolean (self->priv->settings, PREF_WEBP_LOSSLESS);
339 option_keys[i] = g_strdup ("lossless");;
340 option_values[i] = g_strdup_printf ("%d", i_value);
341
342 i++;
343 i_value = g_settings_get_int (self->priv->settings, PREF_WEBP_QUALITY);
344 option_keys[i] = g_strdup ("quality");;
345 option_values[i] = g_strdup_printf ("%d", i_value);
346
347 i++;
348 i_value = g_settings_get_int (self->priv->settings, PREF_WEBP_METHOD);
349 option_keys[i] = g_strdup ("method");;
350 option_values[i] = g_strdup_printf ("%d", i_value);
351
352 i++;
353 option_keys[i] = NULL;
354 option_values[i] = NULL;
355
356 surface = gth_image_get_cairo_surface (image);
357 result = _cairo_surface_write_as_webp (surface,
358 buffer,
359 buffer_size,
360 option_keys,
361 option_values,
362 error);
363
364 cairo_surface_destroy (surface);
365 g_strfreev (option_keys);
366 g_strfreev (option_values);
367
368 return result;
369 }
370
371
372 static void
gth_image_saver_webp_class_init(GthImageSaverWebpClass * klass)373 gth_image_saver_webp_class_init (GthImageSaverWebpClass *klass)
374 {
375 GObjectClass *object_class;
376 GthImageSaverClass *image_saver_class;
377
378 object_class = G_OBJECT_CLASS (klass);
379 object_class->finalize = gth_image_saver_webp_finalize;
380
381 image_saver_class = GTH_IMAGE_SAVER_CLASS (klass);
382 image_saver_class->id = "webp";
383 image_saver_class->display_name = _("WebP");
384 image_saver_class->mime_type = "image/webp";
385 image_saver_class->extensions = "webp";
386 image_saver_class->get_default_ext = NULL;
387 image_saver_class->get_control = gth_image_saver_webp_get_control;
388 image_saver_class->save_options = gth_image_saver_webp_save_options;
389 image_saver_class->can_save = gth_image_saver_webp_can_save;
390 image_saver_class->save_image = gth_image_saver_webp_save_image;
391 }
392
393
394 static void
gth_image_saver_webp_init(GthImageSaverWebp * self)395 gth_image_saver_webp_init (GthImageSaverWebp *self)
396 {
397 self->priv = gth_image_saver_webp_get_instance_private (self);
398 self->priv->settings = g_settings_new (GTHUMB_IMAGE_SAVERS_WEBP_SCHEMA);
399 self->priv->builder = NULL;
400 }
401