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 #ifdef HAVE_LIBTIFF
24 #include <tiffio.h>
25 #endif /* HAVE_LIBTIFF */
26 #include <glib/gi18n.h>
27 #include <gthumb.h>
28 #include "cairo-io-enum-types.h"
29 #include "gth-image-saver-tiff.h"
30 #include "preferences.h"
31
32
33 struct _GthImageSaverTiffPrivate {
34 GSettings *settings;
35 GtkBuilder *builder;
36 char *default_ext;
37 };
38
39
G_DEFINE_TYPE_WITH_CODE(GthImageSaverTiff,gth_image_saver_tiff,GTH_TYPE_IMAGE_SAVER,G_ADD_PRIVATE (GthImageSaverTiff))40 G_DEFINE_TYPE_WITH_CODE (GthImageSaverTiff,
41 gth_image_saver_tiff,
42 GTH_TYPE_IMAGE_SAVER,
43 G_ADD_PRIVATE (GthImageSaverTiff))
44
45
46 static void
47 gth_image_saver_tiff_finalize (GObject *object)
48 {
49 GthImageSaverTiff *self = GTH_IMAGE_SAVER_TIFF (object);
50
51 _g_object_unref (self->priv->settings);
52 _g_object_unref (self->priv->builder);
53 g_free (self->priv->default_ext);
54
55 G_OBJECT_CLASS (gth_image_saver_tiff_parent_class)->finalize (object);
56 }
57
58
59 static const char *
gth_image_saver_tiff_get_default_ext(GthImageSaver * base)60 gth_image_saver_tiff_get_default_ext (GthImageSaver *base)
61 {
62 GthImageSaverTiff *self = GTH_IMAGE_SAVER_TIFF (base);
63
64 if (self->priv->default_ext == NULL)
65 self->priv->default_ext = g_settings_get_string (self->priv->settings, PREF_TIFF_DEFAULT_EXT);
66
67 return self->priv->default_ext;
68 }
69
70
71 static GtkWidget *
gth_image_saver_tiff_get_control(GthImageSaver * base)72 gth_image_saver_tiff_get_control (GthImageSaver *base)
73 {
74 #ifdef HAVE_LIBTIFF
75
76 GthImageSaverTiff *self = GTH_IMAGE_SAVER_TIFF (base);
77 char **extensions;
78 int i;
79 int active_idx;
80 GthTiffCompression compression_type;
81
82 _g_object_unref (self->priv->builder);
83 self->priv->builder = _gtk_builder_new_from_file ("tiff-options.ui", "cairo_io");
84
85 active_idx = 0;
86 extensions = g_strsplit (gth_image_saver_get_extensions (base), " ", -1);
87 for (i = 0; extensions[i] != NULL; i++) {
88 GtkTreeIter iter;
89
90 gtk_list_store_append (GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder, "tiff_default_ext_liststore")), &iter);
91 gtk_list_store_set (GTK_LIST_STORE (gtk_builder_get_object (self->priv->builder, "tiff_default_ext_liststore")),
92 &iter,
93 0, extensions[i],
94 -1);
95 if (g_str_equal (extensions[i], gth_image_saver_get_default_ext (base)))
96 active_idx = i;
97 }
98 gtk_combo_box_set_active (GTK_COMBO_BOX (_gtk_builder_get_widget (self->priv->builder, "tiff_default_extension_combobox")), active_idx);
99 g_strfreev (extensions);
100
101 compression_type = g_settings_get_enum (self->priv->settings, PREF_TIFF_COMPRESSION);
102 switch (compression_type) {
103 case GTH_TIFF_COMPRESSION_NONE:
104 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_comp_none_radiobutton")), TRUE);
105 break;
106 case GTH_TIFF_COMPRESSION_DEFLATE:
107 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_comp_deflate_radiobutton")), TRUE);
108 break;
109 case GTH_TIFF_COMPRESSION_JPEG:
110 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_comp_jpeg_radiobutton")), TRUE);
111 break;
112 }
113
114 gtk_spin_button_set_value (GTK_SPIN_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_hdpi_spinbutton")),
115 g_settings_get_int (self->priv->settings, PREF_TIFF_HORIZONTAL_RES));
116 gtk_spin_button_set_value (GTK_SPIN_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_vdpi_spinbutton")),
117 g_settings_get_int (self->priv->settings, PREF_TIFF_VERTICAL_RES));
118
119 return _gtk_builder_get_widget (self->priv->builder, "tiff_options");
120
121 #else /* ! HAVE_LIBTIFF */
122
123 return GTH_IMAGE_SAVER_CLASS (gth_image_saver_tiff_parent_class)->get_control (base);
124
125 #endif /* HAVE_LIBTIFF */
126 }
127
128
129 static void
gth_image_saver_tiff_save_options(GthImageSaver * base)130 gth_image_saver_tiff_save_options (GthImageSaver *base)
131 {
132 #ifdef HAVE_LIBTIFF
133
134 GthImageSaverTiff *self = GTH_IMAGE_SAVER_TIFF (base);
135 GtkTreeIter iter;
136 GthTiffCompression compression_type;
137
138 if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (_gtk_builder_get_widget (self->priv->builder, "tiff_default_extension_combobox")), &iter)) {
139 g_free (self->priv->default_ext);
140 gtk_tree_model_get (GTK_TREE_MODEL (gtk_builder_get_object (self->priv->builder, "tiff_default_ext_liststore")),
141 &iter,
142 0, &self->priv->default_ext,
143 -1);
144 g_settings_set_string (self->priv->settings, PREF_TIFF_DEFAULT_EXT, self->priv->default_ext);
145 }
146
147 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_comp_none_radiobutton"))))
148 compression_type = GTH_TIFF_COMPRESSION_NONE;
149 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_comp_deflate_radiobutton"))))
150 compression_type = GTH_TIFF_COMPRESSION_DEFLATE;
151 else
152 compression_type = GTH_TIFF_COMPRESSION_JPEG;
153 g_settings_set_enum (self->priv->settings, PREF_TIFF_COMPRESSION, compression_type);
154
155 g_settings_set_int (self->priv->settings, PREF_TIFF_HORIZONTAL_RES, (int) gtk_spin_button_get_value (GTK_SPIN_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_hdpi_spinbutton"))));
156 g_settings_set_int (self->priv->settings, PREF_TIFF_VERTICAL_RES, (int) gtk_spin_button_get_value (GTK_SPIN_BUTTON (_gtk_builder_get_widget (self->priv->builder, "tiff_vdpi_spinbutton"))));
157
158 #endif /* HAVE_LIBTIFF */
159 }
160
161
162 static gboolean
gth_image_saver_tiff_can_save(GthImageSaver * self,const char * mime_type)163 gth_image_saver_tiff_can_save (GthImageSaver *self,
164 const char *mime_type)
165 {
166 #ifdef HAVE_LIBTIFF
167
168 return g_content_type_equals (mime_type, "image/tiff");
169
170 #else /* ! HAVE_LIBTIFF */
171
172 GSList *formats;
173 GSList *scan;
174 GdkPixbufFormat *tiff_format;
175
176 if (! g_content_type_equals (mime_type, "image/tiff"))
177 return FALSE;
178
179 formats = gdk_pixbuf_get_formats ();
180 tiff_format = NULL;
181 for (scan = formats; (tiff_format == NULL) && (scan != NULL); scan = g_slist_next (scan)) {
182 GdkPixbufFormat *format = scan->data;
183 char **mime_types;
184 int i;
185
186 mime_types = gdk_pixbuf_format_get_mime_types (format);
187 for (i = 0; mime_types[i] != NULL; i++)
188 if (g_content_type_equals (mime_types[i], "image/tiff"))
189 break;
190
191 if (mime_types[i] == NULL)
192 continue;
193
194 if (! gdk_pixbuf_format_is_writable (format))
195 continue;
196
197 tiff_format = format;
198 }
199
200 return tiff_format != NULL;
201
202 #endif /* HAVE_LIBTIFF */
203 }
204
205
206 #ifdef HAVE_LIBTIFF
207
208
209 /* -- gth_image_saver_tiff_save_pixbuf -- */
210
211
212 #define TILE_HEIGHT 40 /* FIXME */
213
214
215 static tsize_t
tiff_read(thandle_t handle,tdata_t buf,tsize_t size)216 tiff_read (thandle_t handle, tdata_t buf, tsize_t size)
217 {
218 return -1;
219 }
220
221
222 static tsize_t
tiff_write(thandle_t handle,tdata_t buf,tsize_t size)223 tiff_write (thandle_t handle, tdata_t buf, tsize_t size)
224 {
225 GthBufferData *buffer_data = (GthBufferData *)handle;
226
227 gth_buffer_data_write (buffer_data, buf, size, NULL);
228 return size;
229 }
230
231
232 static toff_t
tiff_seek(thandle_t handle,toff_t offset,int whence)233 tiff_seek (thandle_t handle, toff_t offset, int whence)
234 {
235 GthBufferData *buffer_data = (GthBufferData *)handle;
236
237 return gth_buffer_data_seek (buffer_data, offset, whence);
238 }
239
240
241 static int
tiff_close(thandle_t context)242 tiff_close (thandle_t context)
243 {
244 return 0;
245 }
246
247
248 static toff_t
tiff_size(thandle_t handle)249 tiff_size (thandle_t handle)
250 {
251 return -1;
252 }
253
254
255 static gboolean
_cairo_surface_write_as_tiff(cairo_surface_t * image,char ** buffer,gsize * buffer_size,char ** keys,char ** values,GError ** error)256 _cairo_surface_write_as_tiff (cairo_surface_t *image,
257 char **buffer,
258 gsize *buffer_size,
259 char **keys,
260 char **values,
261 GError **error)
262 {
263 GthBufferData *buffer_data;
264 TIFF *tif;
265 int cols, rows, row;
266 glong rowsperstrip;
267 gushort compression;
268 int alpha;
269 gshort predictor;
270 gshort photometric;
271 gshort samplesperpixel;
272 gshort bitspersample;
273 gushort extra_samples[1];
274 int rowstride;
275 guchar *pixels, *ptr, *buf;
276 int horizontal_dpi = 72, vertical_dpi = 72;
277 gboolean save_resolution = FALSE;
278
279 compression = COMPRESSION_DEFLATE;
280
281 if (keys && *keys) {
282 char **kiter = keys;
283 char **viter = values;
284
285 while (*kiter) {
286 if (strcmp (*kiter, "compression") == 0) {
287 if (*viter == NULL) {
288 g_set_error (error,
289 GDK_PIXBUF_ERROR,
290 GDK_PIXBUF_ERROR_BAD_OPTION,
291 "Must specify a compression type");
292 return FALSE;
293 }
294
295 if (strcmp (*viter, "none") == 0)
296 compression = COMPRESSION_NONE;
297 else if (strcmp (*viter, "pack bits") == 0)
298 compression = COMPRESSION_PACKBITS;
299 else if (strcmp (*viter, "lzw") == 0)
300 compression = COMPRESSION_LZW;
301 else if (strcmp (*viter, "deflate") == 0)
302 compression = COMPRESSION_DEFLATE;
303 else if (strcmp (*viter, "jpeg") == 0)
304 compression = COMPRESSION_JPEG;
305 else {
306 g_set_error (error,
307 GDK_PIXBUF_ERROR,
308 GDK_PIXBUF_ERROR_BAD_OPTION,
309 "Unsupported compression type passed to the TIFF saver");
310 return FALSE;
311 }
312 }
313 else if (strcmp (*kiter, "vertical dpi") == 0) {
314 char *endptr = NULL;
315 vertical_dpi = strtol (*viter, &endptr, 10);
316 save_resolution = TRUE;
317
318 if (endptr == *viter) {
319 g_set_error (error,
320 GDK_PIXBUF_ERROR,
321 GDK_PIXBUF_ERROR_BAD_OPTION,
322 "TIFF vertical dpi must be a value greater than 0; value '%s' could not be parsed.",
323 *viter);
324 return FALSE;
325 }
326
327 if (vertical_dpi < 0) {
328 g_set_error (error,
329 GDK_PIXBUF_ERROR,
330 GDK_PIXBUF_ERROR_BAD_OPTION,
331 "TIFF vertical dpi must be a value greater than 0; value '%d' is not allowed.",
332 vertical_dpi);
333 return FALSE;
334 }
335 }
336 else if (strcmp (*kiter, "horizontal dpi") == 0) {
337 char *endptr = NULL;
338 horizontal_dpi = strtol (*viter, &endptr, 10);
339 save_resolution = TRUE;
340
341 if (endptr == *viter) {
342 g_set_error (error,
343 GDK_PIXBUF_ERROR,
344 GDK_PIXBUF_ERROR_BAD_OPTION,
345 "TIFF horizontal dpi must be a value greater than 0; value '%s' could not be parsed.",
346 *viter);
347 return FALSE;
348 }
349
350 if (horizontal_dpi < 0) {
351 g_set_error (error,
352 GDK_PIXBUF_ERROR,
353 GDK_PIXBUF_ERROR_BAD_OPTION,
354 "TIFF horizontal dpi must be a value greater than 0; value '%d' is not allowed.",
355 horizontal_dpi);
356 return FALSE;
357 }
358 }
359 else {
360 g_warning ("Bad option name '%s' passed to the TIFF saver", *kiter);
361 return FALSE;
362 }
363
364 ++kiter;
365 ++viter;
366 }
367 }
368
369 predictor = 0;
370 rowsperstrip = TILE_HEIGHT;
371
372 buffer_data = gth_buffer_data_new ();
373 tif = TIFFClientOpen ("gth-tiff-writer", "w",
374 buffer_data,
375 tiff_read,
376 tiff_write,
377 tiff_seek,
378 tiff_close,
379 tiff_size,
380 NULL,
381 NULL);
382 if (tif == NULL) {
383 g_set_error_literal (error,
384 GDK_PIXBUF_ERROR,
385 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
386 "Couldn't allocate memory for writing TIFF file");
387 return FALSE;
388 }
389
390 cols = cairo_image_surface_get_width (image);
391 rows = cairo_image_surface_get_height (image);
392 alpha = _cairo_image_surface_get_has_alpha (image);
393 pixels = _cairo_image_surface_flush_and_get_data (image);
394 rowstride = cairo_image_surface_get_stride (image);
395
396 predictor = 2;
397 bitspersample = 8;
398 photometric = PHOTOMETRIC_RGB;
399
400 if (alpha)
401 samplesperpixel = 4;
402 else
403 samplesperpixel = 3;
404
405 /* Set TIFF parameters. */
406
407 TIFFSetField (tif, TIFFTAG_SUBFILETYPE, 0);
408 TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, cols);
409 TIFFSetField (tif, TIFFTAG_IMAGELENGTH, rows);
410 TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
411 TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
412 TIFFSetField (tif, TIFFTAG_COMPRESSION, compression);
413
414 if ((compression == COMPRESSION_LZW || compression == COMPRESSION_DEFLATE)
415 && (predictor != 0))
416 {
417 TIFFSetField (tif, TIFFTAG_PREDICTOR, predictor);
418 }
419
420 if (alpha) {
421 extra_samples [0] = EXTRASAMPLE_ASSOCALPHA;
422 TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
423 }
424
425 TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
426 /*TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, filename);*/
427 TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
428 TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
429 TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
430
431 if (save_resolution) {
432 TIFFSetField (tif, TIFFTAG_XRESOLUTION, (double) horizontal_dpi);
433 TIFFSetField (tif, TIFFTAG_YRESOLUTION, (double) vertical_dpi);
434 TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
435 }
436
437 buf = g_try_malloc (cols * samplesperpixel * sizeof (guchar));
438 if (! buf) {
439 g_set_error_literal (error,
440 GDK_PIXBUF_ERROR,
441 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
442 _("Insufficient memory"));
443 return FALSE;
444 }
445
446 /* Now write the TIFF data. */
447 ptr = pixels;
448 for (row = 0; row < rows; row++) {
449 _cairo_copy_line_as_rgba_big_endian (buf, ptr, cols, alpha);
450 if (TIFFWriteScanline (tif, buf, row, 0) < 0) {
451 g_set_error (error,
452 GDK_PIXBUF_ERROR,
453 GDK_PIXBUF_ERROR_FAILED,
454 "TIFF Failed a scanline write on row %d",
455 row);
456 return FALSE;
457 }
458
459 ptr += rowstride;
460 }
461
462 g_free (buf);
463
464 TIFFFlushData (tif);
465 TIFFClose (tif);
466
467 gth_buffer_data_get (buffer_data, buffer, buffer_size);
468 gth_buffer_data_free (buffer_data, FALSE);
469
470 return TRUE;
471 }
472
473
474 #endif /* HAVE_LIBTIFF */
475
476
477 static gboolean
gth_image_saver_tiff_save_image(GthImageSaver * base,GthImage * image,char ** buffer,gsize * buffer_size,const char * mime_type,GCancellable * cancellable,GError ** error)478 gth_image_saver_tiff_save_image (GthImageSaver *base,
479 GthImage *image,
480 char **buffer,
481 gsize *buffer_size,
482 const char *mime_type,
483 GCancellable *cancellable,
484 GError **error)
485 {
486 #ifdef HAVE_LIBTIFF
487 GthImageSaverTiff *self = GTH_IMAGE_SAVER_TIFF (base);
488 char **option_keys;
489 char **option_values;
490 int i = -1;
491 int i_value;
492 cairo_surface_t *surface;
493 gboolean result;
494
495 option_keys = g_malloc (sizeof (char *) * 4);
496 option_values = g_malloc (sizeof (char *) * 4);
497
498 i++;
499 option_keys[i] = g_strdup ("compression");;
500 option_values[i] = g_settings_get_string (self->priv->settings, PREF_TIFF_COMPRESSION);
501
502 i++;
503 i_value = g_settings_get_int (self->priv->settings, PREF_TIFF_VERTICAL_RES);
504 option_keys[i] = g_strdup ("vertical dpi");;
505 option_values[i] = g_strdup_printf ("%d", i_value);
506
507 i++;
508 i_value = g_settings_get_int (self->priv->settings, PREF_TIFF_HORIZONTAL_RES);
509 option_keys[i] = g_strdup ("horizontal dpi");;
510 option_values[i] = g_strdup_printf ("%d", i_value);
511
512 i++;
513 option_keys[i] = NULL;
514 option_values[i] = NULL;
515
516 surface = gth_image_get_cairo_surface (image);
517 result = _cairo_surface_write_as_tiff (surface,
518 buffer,
519 buffer_size,
520 option_keys,
521 option_values,
522 error);
523
524 cairo_surface_destroy (surface);
525 g_strfreev (option_keys);
526 g_strfreev (option_values);
527
528 #else /* ! HAVE_LIBTIFF */
529
530 GdkPixbuf *pixbuf;
531 char *pixbuf_type;
532 gboolean result;
533
534 pixbuf = gth_image_get_pixbuf (image);
535 pixbuf_type = _gdk_pixbuf_get_type_from_mime_type (mime_type);
536 result = gdk_pixbuf_save_to_bufferv (pixbuf,
537 buffer,
538 buffer_size,
539 pixbuf_type,
540 NULL,
541 NULL,
542 error);
543
544 g_free (pixbuf_type);
545 g_object_unref (pixbuf);
546
547 #endif /* HAVE_LIBTIFF */
548
549 return result;
550 }
551
552
553 static void
gth_image_saver_tiff_class_init(GthImageSaverTiffClass * klass)554 gth_image_saver_tiff_class_init (GthImageSaverTiffClass *klass)
555 {
556 GObjectClass *object_class;
557 GthImageSaverClass *image_saver_class;
558
559 object_class = G_OBJECT_CLASS (klass);
560 object_class->finalize = gth_image_saver_tiff_finalize;
561
562 image_saver_class = GTH_IMAGE_SAVER_CLASS (klass);
563 image_saver_class->id = "tiff";
564 image_saver_class->display_name = _("TIFF");
565 image_saver_class->mime_type = "image/tiff";
566 image_saver_class->extensions = "tiff tif";
567 image_saver_class->get_default_ext = gth_image_saver_tiff_get_default_ext;
568 image_saver_class->get_control = gth_image_saver_tiff_get_control;
569 image_saver_class->save_options = gth_image_saver_tiff_save_options;
570 image_saver_class->can_save = gth_image_saver_tiff_can_save;
571 image_saver_class->save_image = gth_image_saver_tiff_save_image;
572 }
573
574
575 static void
gth_image_saver_tiff_init(GthImageSaverTiff * self)576 gth_image_saver_tiff_init (GthImageSaverTiff *self)
577 {
578 self->priv = gth_image_saver_tiff_get_instance_private (self);
579 self->priv->settings = g_settings_new (GTHUMB_IMAGE_SAVERS_TIFF_SCHEMA);
580 self->priv->builder = NULL;
581 self->priv->default_ext = NULL;
582 }
583