1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3 *
4 * gimpexport.c
5 * Copyright (C) 1999-2004 Sven Neumann <sven@gimp.org>
6 *
7 * This library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <https://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23
24 #include <string.h>
25
26 #include <gtk/gtk.h>
27
28 #include "gimp.h"
29 #include "gimpui.h"
30
31 #include "libgimp-intl.h"
32
33
34 /**
35 * SECTION: gimpexport
36 * @title: gimpexport
37 * @short_description: Export an image before it is saved.
38 *
39 * This function should be called by all save_plugins unless they are
40 * able to save all image formats GIMP knows about. It takes care of
41 * asking the user if she wishes to export the image to a format the
42 * save_plugin can handle. It then performs the necessary conversions
43 * (e.g. Flatten) on a copy of the image so that the image can be
44 * saved without changing the original image.
45 *
46 * The capabilities of the save_plugin are specified by combining
47 * #GimpExportCapabilities using a bitwise OR.
48 *
49 * Make sure you have initialized GTK+ before you call this function
50 * as it will most probably have to open a dialog.
51 **/
52
53
54 typedef void (* ExportFunc) (gint32 imageID,
55 gint32 *drawable_ID);
56
57
58 /* the export action structure */
59 typedef struct
60 {
61 ExportFunc default_action;
62 ExportFunc alt_action;
63 const gchar *reason;
64 const gchar *possibilities[2];
65 gint choice;
66 } ExportAction;
67
68
69 /* the functions that do the actual export */
70
71 static void
export_merge(gint32 image_ID,gint32 * drawable_ID)72 export_merge (gint32 image_ID,
73 gint32 *drawable_ID)
74 {
75 gint32 nlayers;
76 gint32 nvisible = 0;
77 gint32 i;
78 gint32 *layers;
79 gint32 merged;
80 gint32 transp;
81
82 layers = gimp_image_get_layers (image_ID, &nlayers);
83 for (i = 0; i < nlayers; i++)
84 {
85 if (gimp_item_get_visible (layers[i]))
86 nvisible++;
87 }
88
89 if (nvisible <= 1)
90 {
91 /* if there is only one (or zero) visible layer, add a new
92 * transparent layer that has the same size as the canvas. The
93 * merge that follows will ensure that the offset, opacity and
94 * size are correct
95 */
96 transp = gimp_layer_new (image_ID, "-",
97 gimp_image_width (image_ID),
98 gimp_image_height (image_ID),
99 gimp_drawable_type (*drawable_ID) | 1,
100 100.0, GIMP_LAYER_MODE_NORMAL);
101 gimp_image_insert_layer (image_ID, transp, -1, 1);
102 gimp_selection_none (image_ID);
103 gimp_drawable_edit_clear (transp);
104 nvisible++;
105 }
106
107 if (nvisible > 1)
108 {
109 g_free (layers);
110 merged = gimp_image_merge_visible_layers (image_ID, GIMP_CLIP_TO_IMAGE);
111
112 if (merged != -1)
113 *drawable_ID = merged;
114 else
115 return; /* shouldn't happen */
116
117 layers = gimp_image_get_layers (image_ID, &nlayers);
118
119 /* make sure that the merged drawable matches the image size */
120 if (gimp_drawable_width (merged) != gimp_image_width (image_ID) ||
121 gimp_drawable_height (merged) != gimp_image_height (image_ID))
122 {
123 gint off_x, off_y;
124
125 gimp_drawable_offsets (merged, &off_x, &off_y);
126 gimp_layer_resize (merged,
127 gimp_image_width (image_ID),
128 gimp_image_height (image_ID),
129 off_x, off_y);
130 }
131 }
132
133 /* remove any remaining (invisible) layers */
134 for (i = 0; i < nlayers; i++)
135 {
136 if (layers[i] != *drawable_ID)
137 gimp_image_remove_layer (image_ID, layers[i]);
138 }
139 g_free (layers);
140 }
141
142 static void
export_flatten(gint32 image_ID,gint32 * drawable_ID)143 export_flatten (gint32 image_ID,
144 gint32 *drawable_ID)
145 {
146 gint32 flattened;
147
148 flattened = gimp_image_flatten (image_ID);
149
150 if (flattened != -1)
151 *drawable_ID = flattened;
152 }
153
154 static void
export_remove_alpha(gint32 image_ID,gint32 * drawable_ID)155 export_remove_alpha (gint32 image_ID,
156 gint32 *drawable_ID)
157 {
158 gint32 n_layers;
159 gint32 *layers;
160 gint i;
161
162 layers = gimp_image_get_layers (image_ID, &n_layers);
163
164 for (i = 0; i < n_layers; i++)
165 {
166 if (gimp_drawable_has_alpha (layers[i]))
167 gimp_layer_flatten (layers[i]);
168 }
169
170 g_free (layers);
171 }
172
173 static void
export_apply_masks(gint32 image_ID,gint * drawable_ID)174 export_apply_masks (gint32 image_ID,
175 gint *drawable_ID)
176 {
177 gint32 n_layers;
178 gint32 *layers;
179 gint i;
180
181 layers = gimp_image_get_layers (image_ID, &n_layers);
182
183 for (i = 0; i < n_layers; i++)
184 {
185 if (gimp_layer_get_mask (layers[i]) != -1)
186 {
187 /* we can't apply the mask directly to a layer group, so merge it
188 * first
189 */
190 if (gimp_item_is_group (layers[i]))
191 layers[i] = gimp_image_merge_layer_group (image_ID, layers[i]);
192
193 gimp_layer_remove_mask (layers[i], GIMP_MASK_APPLY);
194 }
195 }
196
197 g_free (layers);
198 }
199
200 static void
export_convert_rgb(gint32 image_ID,gint32 * drawable_ID)201 export_convert_rgb (gint32 image_ID,
202 gint32 *drawable_ID)
203 {
204 gimp_image_convert_rgb (image_ID);
205 }
206
207 static void
export_convert_grayscale(gint32 image_ID,gint32 * drawable_ID)208 export_convert_grayscale (gint32 image_ID,
209 gint32 *drawable_ID)
210 {
211 gimp_image_convert_grayscale (image_ID);
212 }
213
214 static void
export_convert_indexed(gint32 image_ID,gint32 * drawable_ID)215 export_convert_indexed (gint32 image_ID,
216 gint32 *drawable_ID)
217 {
218 gint32 nlayers;
219
220 /* check alpha */
221 g_free (gimp_image_get_layers (image_ID, &nlayers));
222 if (nlayers > 1 || gimp_drawable_has_alpha (*drawable_ID))
223 gimp_image_convert_indexed (image_ID,
224 GIMP_CONVERT_DITHER_NONE,
225 GIMP_CONVERT_PALETTE_GENERATE,
226 255, FALSE, FALSE, "");
227 else
228 gimp_image_convert_indexed (image_ID,
229 GIMP_CONVERT_DITHER_NONE,
230 GIMP_CONVERT_PALETTE_GENERATE,
231 256, FALSE, FALSE, "");
232 }
233
234 static void
export_convert_bitmap(gint32 image_ID,gint32 * drawable_ID)235 export_convert_bitmap (gint32 image_ID,
236 gint32 *drawable_ID)
237 {
238 if (gimp_image_base_type (image_ID) == GIMP_INDEXED)
239 gimp_image_convert_rgb (image_ID);
240
241 gimp_image_convert_indexed (image_ID,
242 GIMP_CONVERT_DITHER_FS,
243 GIMP_CONVERT_PALETTE_GENERATE,
244 2, FALSE, FALSE, "");
245 }
246
247 static void
export_add_alpha(gint32 image_ID,gint32 * drawable_ID)248 export_add_alpha (gint32 image_ID,
249 gint32 *drawable_ID)
250 {
251 gint32 nlayers;
252 gint32 i;
253 gint32 *layers;
254
255 layers = gimp_image_get_layers (image_ID, &nlayers);
256 for (i = 0; i < nlayers; i++)
257 {
258 if (!gimp_drawable_has_alpha (layers[i]))
259 gimp_layer_add_alpha (layers[i]);
260 }
261 g_free (layers);
262 }
263
264 static void
export_crop_image(gint32 image_ID,gint32 * drawable_ID)265 export_crop_image (gint32 image_ID,
266 gint32 *drawable_ID)
267 {
268 gimp_image_crop (image_ID,
269 gimp_image_width (image_ID),
270 gimp_image_height (image_ID),
271 0, 0);
272 }
273
274 static void
export_resize_image(gint32 image_ID,gint32 * drawable_ID)275 export_resize_image (gint32 image_ID,
276 gint32 *drawable_ID)
277 {
278 gimp_image_resize_to_layers (image_ID);
279 }
280
281 static void
export_void(gint32 image_ID,gint32 * drawable_ID)282 export_void (gint32 image_ID,
283 gint32 *drawable_ID)
284 {
285 /* do nothing */
286 }
287
288
289 /* a set of predefined actions */
290
291 static ExportAction export_action_merge =
292 {
293 export_merge,
294 NULL,
295 N_("%s plug-in can't handle layers"),
296 { N_("Merge Visible Layers"), NULL },
297 0
298 };
299
300 static ExportAction export_action_merge_single =
301 {
302 export_merge,
303 NULL,
304 N_("%s plug-in can't handle layer offsets, size or opacity"),
305 { N_("Merge Visible Layers"), NULL },
306 0
307 };
308
309 static ExportAction export_action_animate_or_merge =
310 {
311 NULL,
312 export_merge,
313 N_("%s plug-in can only handle layers as animation frames"),
314 { N_("Save as Animation"), N_("Merge Visible Layers") },
315 0
316 };
317
318 static ExportAction export_action_animate_or_flatten =
319 {
320 NULL,
321 export_flatten,
322 N_("%s plug-in can only handle layers as animation frames"),
323 { N_("Save as Animation"), N_("Flatten Image") },
324 0
325 };
326
327 static ExportAction export_action_merge_or_flatten =
328 {
329 export_flatten,
330 export_merge,
331 N_("%s plug-in can't handle layers"),
332 { N_("Flatten Image"), N_("Merge Visible Layers") },
333 1
334 };
335
336 static ExportAction export_action_flatten =
337 {
338 export_flatten,
339 NULL,
340 N_("%s plug-in can't handle transparency"),
341 { N_("Flatten Image"), NULL },
342 0
343 };
344
345 static ExportAction export_action_remove_alpha =
346 {
347 export_remove_alpha,
348 NULL,
349 N_("%s plug-in can't handle transparent layers"),
350 { N_("Flatten Image"), NULL },
351 0
352 };
353
354 static ExportAction export_action_apply_masks =
355 {
356 export_apply_masks,
357 NULL,
358 N_("%s plug-in can't handle layer masks"),
359 { N_("Apply Layer Masks"), NULL },
360 0
361 };
362
363 static ExportAction export_action_convert_rgb =
364 {
365 export_convert_rgb,
366 NULL,
367 N_("%s plug-in can only handle RGB images"),
368 { N_("Convert to RGB"), NULL },
369 0
370 };
371
372 static ExportAction export_action_convert_grayscale =
373 {
374 export_convert_grayscale,
375 NULL,
376 N_("%s plug-in can only handle grayscale images"),
377 { N_("Convert to Grayscale"), NULL },
378 0
379 };
380
381 static ExportAction export_action_convert_indexed =
382 {
383 export_convert_indexed,
384 NULL,
385 N_("%s plug-in can only handle indexed images"),
386 { N_("Convert to Indexed using default settings\n"
387 "(Do it manually to tune the result)"), NULL },
388 0
389 };
390
391 static ExportAction export_action_convert_bitmap =
392 {
393 export_convert_bitmap,
394 NULL,
395 N_("%s plug-in can only handle bitmap (two color) indexed images"),
396 { N_("Convert to Indexed using bitmap default settings\n"
397 "(Do it manually to tune the result)"), NULL },
398 0
399 };
400
401 static ExportAction export_action_convert_rgb_or_grayscale =
402 {
403 export_convert_rgb,
404 export_convert_grayscale,
405 N_("%s plug-in can only handle RGB or grayscale images"),
406 { N_("Convert to RGB"), N_("Convert to Grayscale")},
407 0
408 };
409
410 static ExportAction export_action_convert_rgb_or_indexed =
411 {
412 export_convert_rgb,
413 export_convert_indexed,
414 N_("%s plug-in can only handle RGB or indexed images"),
415 { N_("Convert to RGB"), N_("Convert to Indexed using default settings\n"
416 "(Do it manually to tune the result)")},
417 0
418 };
419
420 static ExportAction export_action_convert_indexed_or_grayscale =
421 {
422 export_convert_indexed,
423 export_convert_grayscale,
424 N_("%s plug-in can only handle grayscale or indexed images"),
425 { N_("Convert to Indexed using default settings\n"
426 "(Do it manually to tune the result)"),
427 N_("Convert to Grayscale") },
428 0
429 };
430
431 static ExportAction export_action_add_alpha =
432 {
433 export_add_alpha,
434 NULL,
435 N_("%s plug-in needs an alpha channel"),
436 { N_("Add Alpha Channel"), NULL},
437 0
438 };
439
440 static ExportAction export_action_crop_or_resize =
441 {
442 export_crop_image,
443 export_resize_image,
444 N_("%s plug-in needs to crop the layers to the image bounds"),
445 { N_("Crop Layers"), N_("Resize Image to Layers")},
446 0
447 };
448
449
450 static ExportFunc
export_action_get_func(const ExportAction * action)451 export_action_get_func (const ExportAction *action)
452 {
453 if (action->choice == 0 && action->default_action)
454 {
455 return action->default_action;
456 }
457
458 if (action->choice == 1 && action->alt_action)
459 {
460 return action->alt_action;
461 }
462
463 return export_void;
464 }
465
466 static void
export_action_perform(const ExportAction * action,gint32 image_ID,gint32 * drawable_ID)467 export_action_perform (const ExportAction *action,
468 gint32 image_ID,
469 gint32 *drawable_ID)
470 {
471 export_action_get_func (action) (image_ID, drawable_ID);
472 }
473
474
475 /* dialog functions */
476
477 static void
export_toggle_callback(GtkWidget * widget,gpointer data)478 export_toggle_callback (GtkWidget *widget,
479 gpointer data)
480 {
481 gint *choice = (gint *) data;
482
483 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
484 *choice = FALSE;
485 else
486 *choice = TRUE;
487 }
488
489 static GimpExportReturn
confirm_save_dialog(const gchar * message,const gchar * format_name)490 confirm_save_dialog (const gchar *message,
491 const gchar *format_name)
492 {
493 GtkWidget *dialog;
494 GtkWidget *hbox;
495 GtkWidget *image;
496 GtkWidget *main_vbox;
497 GtkWidget *label;
498 gchar *text;
499 GimpExportReturn retval;
500
501 g_return_val_if_fail (message != NULL, GIMP_EXPORT_CANCEL);
502 g_return_val_if_fail (format_name != NULL, GIMP_EXPORT_CANCEL);
503
504 dialog = gimp_dialog_new (_("Confirm Save"), "gimp-export-image-confirm",
505 NULL, 0,
506 gimp_standard_help_func,
507 "gimp-export-confirm-dialog",
508
509 _("_Cancel"), GTK_RESPONSE_CANCEL,
510 _("C_onfirm"), GTK_RESPONSE_OK,
511
512 NULL);
513
514 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
515 GTK_RESPONSE_OK,
516 GTK_RESPONSE_CANCEL,
517 -1);
518
519 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
520 gimp_window_set_transient (GTK_WINDOW (dialog));
521
522 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
523 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
524 hbox, TRUE, TRUE, 0);
525 gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
526 gtk_widget_show (hbox);
527
528 image = gtk_image_new_from_icon_name ("dialog-warning",
529 GTK_ICON_SIZE_DIALOG);
530 gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
531 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
532 gtk_widget_show (image);
533
534 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
535 gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
536 gtk_widget_show (main_vbox);
537
538 text = g_strdup_printf (message, format_name);
539 label = gtk_label_new (text);
540 g_free (text);
541
542 gimp_label_set_attributes (GTK_LABEL (label),
543 PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
544 PANGO_ATTR_WEIGHT, PANGO_WEIGHT_BOLD,
545 -1);
546 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
547 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
548 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
549 gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
550 gtk_widget_show (label);
551
552 gtk_widget_show (dialog);
553
554 switch (gimp_dialog_run (GIMP_DIALOG (dialog)))
555 {
556 case GTK_RESPONSE_OK:
557 retval = GIMP_EXPORT_EXPORT;
558 break;
559
560 default:
561 retval = GIMP_EXPORT_CANCEL;
562 break;
563 }
564
565 gtk_widget_destroy (dialog);
566
567 return retval;
568 }
569
570 static GimpExportReturn
export_dialog(GSList * actions,const gchar * format_name)571 export_dialog (GSList *actions,
572 const gchar *format_name)
573 {
574 GtkWidget *dialog;
575 GtkWidget *hbox;
576 GtkWidget *image;
577 GtkWidget *main_vbox;
578 GtkWidget *label;
579 GSList *list;
580 gchar *text;
581 GimpExportReturn retval;
582
583 g_return_val_if_fail (actions != NULL, GIMP_EXPORT_CANCEL);
584 g_return_val_if_fail (format_name != NULL, GIMP_EXPORT_CANCEL);
585
586 dialog = gimp_dialog_new (_("Export File"), "gimp-export-image",
587 NULL, 0,
588 gimp_standard_help_func, "gimp-export-dialog",
589
590 _("_Ignore"), GTK_RESPONSE_NO,
591 _("_Cancel"), GTK_RESPONSE_CANCEL,
592 _("_Export"), GTK_RESPONSE_OK,
593
594 NULL);
595
596 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
597 GTK_RESPONSE_NO,
598 GTK_RESPONSE_OK,
599 GTK_RESPONSE_CANCEL,
600 -1);
601
602 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
603 gimp_window_set_transient (GTK_WINDOW (dialog));
604
605 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
606 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
607 hbox, TRUE, TRUE, 0);
608 gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
609 gtk_widget_show (hbox);
610
611 image = gtk_image_new_from_icon_name ("dialog-information",
612 GTK_ICON_SIZE_DIALOG);
613 gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
614 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
615 gtk_widget_show (image);
616
617 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
618 gtk_box_pack_start (GTK_BOX (hbox), main_vbox, FALSE, FALSE, 0);
619 gtk_widget_show (main_vbox);
620
621 /* the headline */
622 text = g_strdup_printf (_("Your image should be exported before it "
623 "can be saved as %s for the following reasons:"),
624 format_name);
625 label = gtk_label_new (text);
626 g_free (text);
627
628 gimp_label_set_attributes (GTK_LABEL (label),
629 PANGO_ATTR_SCALE, PANGO_SCALE_LARGE,
630 -1);
631 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
632 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
633 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
634 gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
635 gtk_widget_show (label);
636
637 for (list = actions; list; list = g_slist_next (list))
638 {
639 ExportAction *action = list->data;
640 GtkWidget *frame;
641 GtkWidget *vbox;
642
643 text = g_strdup_printf (gettext (action->reason), format_name);
644 frame = gimp_frame_new (text);
645 g_free (text);
646
647 gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, FALSE, 0);
648 gtk_widget_show (frame);
649
650 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
651 gtk_container_add (GTK_CONTAINER (frame), vbox);
652
653 if (action->possibilities[0] && action->possibilities[1])
654 {
655 GtkWidget *button;
656 GSList *radio_group = NULL;
657
658 button = gtk_radio_button_new_with_label (radio_group,
659 gettext (action->possibilities[0]));
660 gtk_label_set_justify (GTK_LABEL (gtk_bin_get_child (GTK_BIN (button))),
661 GTK_JUSTIFY_LEFT);
662 radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
663 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
664 g_signal_connect (button, "toggled",
665 G_CALLBACK (export_toggle_callback),
666 &action->choice);
667 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
668 (action->choice == 0));
669 gtk_widget_show (button);
670
671 button = gtk_radio_button_new_with_label (radio_group,
672 gettext (action->possibilities[1]));
673 gtk_label_set_justify (GTK_LABEL (gtk_bin_get_child (GTK_BIN (button))),
674 GTK_JUSTIFY_LEFT);
675 radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
676 gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
677 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
678 (action->choice == 1));
679 gtk_widget_show (button);
680 }
681 else if (action->possibilities[0])
682 {
683 label = gtk_label_new (gettext (action->possibilities[0]));
684 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
685 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
686 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
687 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
688 gtk_widget_show (label);
689 action->choice = 0;
690 }
691
692 gtk_widget_show (vbox);
693 }
694
695 /* the footline */
696 label = gtk_label_new (_("The export conversion won't modify your "
697 "original image."));
698 gimp_label_set_attributes (GTK_LABEL (label),
699 PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
700 -1);
701 gtk_label_set_xalign (GTK_LABEL (label), 0.0);
702 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
703 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
704 gtk_box_pack_start (GTK_BOX (main_vbox), label, FALSE, FALSE, 0);
705 gtk_widget_show (label);
706
707 gtk_widget_show (dialog);
708
709 switch (gimp_dialog_run (GIMP_DIALOG (dialog)))
710 {
711 case GTK_RESPONSE_OK:
712 retval = GIMP_EXPORT_EXPORT;
713 break;
714
715 case GTK_RESPONSE_NO:
716 retval = GIMP_EXPORT_IGNORE;
717 break;
718
719 default:
720 retval = GIMP_EXPORT_CANCEL;
721 break;
722 }
723
724 gtk_widget_destroy (dialog);
725
726 return retval;
727 }
728
729 /**
730 * gimp_export_image:
731 * @image_ID: Pointer to the image_ID.
732 * @drawable_ID: Pointer to the drawable_ID.
733 * @format_name: The (short) name of the image_format (e.g. JPEG or GIF).
734 * @capabilities: What can the image_format do?
735 *
736 * Takes an image and a drawable to be saved together with a
737 * description of the capabilities of the image_format. If the
738 * type of image doesn't match the capabilities of the format
739 * a dialog is opened that informs the user that the image has
740 * to be exported and offers to do the necessary conversions.
741 *
742 * If the user chooses to export the image, a copy is created.
743 * This copy is then converted, the image_ID and drawable_ID
744 * are changed to point to the new image and the procedure returns
745 * GIMP_EXPORT_EXPORT. The save_plugin has to take care of deleting the
746 * created image using gimp_image_delete() when it has saved it.
747 *
748 * If the user chooses to Ignore the export problem, the image_ID
749 * and drawable_ID is not altered, GIMP_EXPORT_IGNORE is returned and
750 * the save_plugin should try to save the original image. If the
751 * user chooses Cancel, GIMP_EXPORT_CANCEL is returned and the
752 * save_plugin should quit itself with status %GIMP_PDB_CANCEL.
753 *
754 * If @format_name is NULL, no dialogs will be shown and this function
755 * will behave as if the user clicked on the 'Export' button, if a
756 * dialog would have been shown.
757 *
758 * Returns: An enum of #GimpExportReturn describing the user_action.
759 **/
760 GimpExportReturn
gimp_export_image(gint32 * image_ID,gint32 * drawable_ID,const gchar * format_name,GimpExportCapabilities capabilities)761 gimp_export_image (gint32 *image_ID,
762 gint32 *drawable_ID,
763 const gchar *format_name,
764 GimpExportCapabilities capabilities)
765 {
766 GSList *actions = NULL;
767 GimpImageBaseType type;
768 gint32 i;
769 gint32 n_layers;
770 gint32 *layers;
771 gboolean interactive = FALSE;
772 gboolean added_flatten = FALSE;
773 gboolean has_layer_masks = FALSE;
774 gboolean background_has_alpha = TRUE;
775 GimpExportReturn retval = GIMP_EXPORT_CANCEL;
776
777 g_return_val_if_fail (*image_ID > -1 && *drawable_ID > -1, FALSE);
778
779 /* do some sanity checks */
780 if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
781 capabilities |= GIMP_EXPORT_CAN_HANDLE_ALPHA;
782
783 if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
784 capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
785
786 if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS)
787 capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
788
789 if (format_name && g_getenv ("GIMP_INTERACTIVE_EXPORT"))
790 interactive = TRUE;
791
792 /* ask for confirmation if the user is not saving a layer (see bug #51114) */
793 if (interactive &&
794 ! gimp_item_is_layer (*drawable_ID) &&
795 ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
796 {
797 if (gimp_item_is_layer_mask (*drawable_ID))
798 {
799 retval = confirm_save_dialog
800 (_("You are about to save a layer mask as %s.\n"
801 "This will not save the visible layers."), format_name);
802 }
803 else if (gimp_item_is_channel (*drawable_ID))
804 {
805 retval = confirm_save_dialog
806 (_("You are about to save a channel (saved selection) as %s.\n"
807 "This will not save the visible layers."), format_name);
808 }
809 else
810 {
811 /* this should not happen */
812 g_warning ("%s: unknown drawable type!", G_STRFUNC);
813 }
814
815 /* cancel - the user can then select an appropriate layer to save */
816 if (retval == GIMP_EXPORT_CANCEL)
817 return GIMP_EXPORT_CANCEL;
818 }
819
820
821 /* check alpha and layer masks */
822 layers = gimp_image_get_layers (*image_ID, &n_layers);
823
824 for (i = 0; i < n_layers; i++)
825 {
826 if (gimp_drawable_has_alpha (layers[i]))
827 {
828 if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA))
829 {
830 if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
831 {
832 actions = g_slist_prepend (actions, &export_action_flatten);
833 added_flatten = TRUE;
834 break;
835 }
836 else
837 {
838 actions = g_slist_prepend (actions, &export_action_remove_alpha);
839 break;
840 }
841 }
842 }
843 else
844 {
845 /* If this is the last layer, it's visible and has no alpha
846 * channel, then the image has a "flat" background
847 */
848 if (i == n_layers - 1 && gimp_item_get_visible (layers[i]))
849 background_has_alpha = FALSE;
850
851 if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
852 {
853 actions = g_slist_prepend (actions, &export_action_add_alpha);
854 break;
855 }
856 }
857 }
858
859 if (! added_flatten)
860 {
861 for (i = 0; i < n_layers; i++)
862 {
863 if (gimp_layer_get_mask (layers[i]) != -1)
864 has_layer_masks = TRUE;
865 }
866 }
867
868 if (! added_flatten)
869 {
870 gint32 n_children;
871 gint32 *children;
872
873 children = gimp_item_get_children (layers[0], &n_children);
874
875 if ((capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS) &&
876 (capabilities & GIMP_EXPORT_NEEDS_CROP))
877 {
878 GeglRectangle image_bounds;
879 gboolean needs_crop = FALSE;
880
881 image_bounds.x = 0;
882 image_bounds.y = 0;
883 image_bounds.width = gimp_image_width (*image_ID);
884 image_bounds.height = gimp_image_height (*image_ID);
885
886 for (i = 0; i < n_layers; i++)
887 {
888 GeglRectangle layer_bounds;
889
890 gimp_drawable_offsets (layers[i],
891 &layer_bounds.x, &layer_bounds.y);
892
893 layer_bounds.width = gimp_drawable_width (layers[i]);
894 layer_bounds.height = gimp_drawable_height (layers[i]);
895
896 if (! gegl_rectangle_contains (&image_bounds, &layer_bounds))
897 {
898 needs_crop = TRUE;
899
900 break;
901 }
902 }
903
904 if (needs_crop)
905 {
906 actions = g_slist_prepend (actions,
907 &export_action_crop_or_resize);
908 }
909 }
910
911 /* check if layer size != canvas size, opacity != 100%, or offsets != 0 */
912 if (n_layers == 1 &&
913 ! children &&
914 gimp_item_is_layer (*drawable_ID) &&
915 ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
916 {
917 gint offset_x;
918 gint offset_y;
919
920 gimp_drawable_offsets (*drawable_ID, &offset_x, &offset_y);
921
922 if ((gimp_layer_get_opacity (*drawable_ID) < 100.0) ||
923 (gimp_image_width (*image_ID) !=
924 gimp_drawable_width (*drawable_ID)) ||
925 (gimp_image_height (*image_ID) !=
926 gimp_drawable_height (*drawable_ID)) ||
927 offset_x || offset_y)
928 {
929 if (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA)
930 {
931 actions = g_slist_prepend (actions,
932 &export_action_merge_single);
933 }
934 else
935 {
936 actions = g_slist_prepend (actions,
937 &export_action_flatten);
938 }
939 }
940 }
941 /* check multiple layers */
942 else if (n_layers > 1)
943 {
944 if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
945 {
946 if (background_has_alpha ||
947 capabilities & GIMP_EXPORT_NEEDS_ALPHA)
948 actions = g_slist_prepend (actions,
949 &export_action_animate_or_merge);
950 else
951 actions = g_slist_prepend (actions,
952 &export_action_animate_or_flatten);
953 }
954 else if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
955 {
956 if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
957 actions = g_slist_prepend (actions,
958 &export_action_merge);
959 else
960 actions = g_slist_prepend (actions,
961 &export_action_merge_or_flatten);
962 }
963 }
964 /* check for a single toplevel layer group */
965 else if (children)
966 {
967 if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
968 {
969 if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
970 actions = g_slist_prepend (actions,
971 &export_action_merge);
972 else
973 actions = g_slist_prepend (actions,
974 &export_action_merge_or_flatten);
975 }
976 }
977
978 g_free (children);
979
980 /* check layer masks */
981 if (has_layer_masks &&
982 ! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS))
983 actions = g_slist_prepend (actions, &export_action_apply_masks);
984 }
985
986 g_free (layers);
987
988 /* check the image type */
989 type = gimp_image_base_type (*image_ID);
990 switch (type)
991 {
992 case GIMP_RGB:
993 if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB))
994 {
995 if ((capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED) &&
996 (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
997 actions = g_slist_prepend (actions,
998 &export_action_convert_indexed_or_grayscale);
999 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
1000 actions = g_slist_prepend (actions,
1001 &export_action_convert_indexed);
1002 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
1003 actions = g_slist_prepend (actions,
1004 &export_action_convert_grayscale);
1005 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
1006 actions = g_slist_prepend (actions,
1007 &export_action_convert_bitmap);
1008 }
1009 break;
1010
1011 case GIMP_GRAY:
1012 if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
1013 {
1014 if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
1015 (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
1016 actions = g_slist_prepend (actions,
1017 &export_action_convert_rgb_or_indexed);
1018 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
1019 actions = g_slist_prepend (actions,
1020 &export_action_convert_rgb);
1021 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
1022 actions = g_slist_prepend (actions,
1023 &export_action_convert_indexed);
1024 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
1025 actions = g_slist_prepend (actions,
1026 &export_action_convert_bitmap);
1027 }
1028 break;
1029
1030 case GIMP_INDEXED:
1031 if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
1032 {
1033 if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
1034 (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
1035 actions = g_slist_prepend (actions,
1036 &export_action_convert_rgb_or_grayscale);
1037 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
1038 actions = g_slist_prepend (actions,
1039 &export_action_convert_rgb);
1040 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
1041 actions = g_slist_prepend (actions,
1042 &export_action_convert_grayscale);
1043 else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
1044 {
1045 gint n_colors;
1046
1047 g_free (gimp_image_get_colormap (*image_ID, &n_colors));
1048
1049 if (n_colors > 2)
1050 actions = g_slist_prepend (actions,
1051 &export_action_convert_bitmap);
1052 }
1053 }
1054 break;
1055 }
1056
1057 if (actions)
1058 {
1059 actions = g_slist_reverse (actions);
1060
1061 if (interactive)
1062 retval = export_dialog (actions, format_name);
1063 else
1064 retval = GIMP_EXPORT_EXPORT;
1065 }
1066 else
1067 {
1068 retval = GIMP_EXPORT_IGNORE;
1069 }
1070
1071 if (retval == GIMP_EXPORT_EXPORT)
1072 {
1073 GSList *list;
1074
1075 *image_ID = gimp_image_duplicate (*image_ID);
1076 *drawable_ID = gimp_image_get_active_layer (*image_ID);
1077
1078 gimp_image_undo_disable (*image_ID);
1079
1080 for (list = actions; list; list = list->next)
1081 {
1082 export_action_perform (list->data, *image_ID, drawable_ID);
1083 }
1084 }
1085
1086 g_slist_free (actions);
1087
1088 return retval;
1089 }
1090
1091 /**
1092 * gimp_export_dialog_new:
1093 * @format_name: The short name of the image_format (e.g. JPEG or PNG).
1094 * @role: The dialog's @role which will be set with
1095 * gtk_window_set_role().
1096 * @help_id: The GIMP help id.
1097 *
1098 * Creates a new export dialog. All file plug-ins should use this
1099 * dialog to get a consistent look on the export dialogs. Use
1100 * gimp_export_dialog_get_content_area() to get a #GtkVBox to be
1101 * filled with export options. The export dialog is a wrapped
1102 * #GimpDialog.
1103 *
1104 * The dialog response when the user clicks on the Export button is
1105 * %GTK_RESPONSE_OK, and when the Cancel button is clicked it is
1106 * %GTK_RESPONSE_CANCEL.
1107 *
1108 * Returns: The new export dialog.
1109 *
1110 * Since: 2.8
1111 **/
1112 GtkWidget *
gimp_export_dialog_new(const gchar * format_name,const gchar * role,const gchar * help_id)1113 gimp_export_dialog_new (const gchar *format_name,
1114 const gchar *role,
1115 const gchar *help_id)
1116 {
1117 GtkWidget *dialog;
1118 /* TRANSLATORS: the %s parameter is an image format name (ex: PNG). */
1119 gchar *title = g_strdup_printf (_("Export Image as %s"), format_name);
1120
1121 dialog = gimp_dialog_new (title, role,
1122 NULL, 0,
1123 gimp_standard_help_func, help_id,
1124
1125 _("_Cancel"), GTK_RESPONSE_CANCEL,
1126 _("_Export"), GTK_RESPONSE_OK,
1127
1128 NULL);
1129
1130 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1131 GTK_RESPONSE_OK,
1132 GTK_RESPONSE_CANCEL,
1133 -1);
1134
1135 gimp_window_set_transient (GTK_WINDOW (dialog));
1136
1137 g_free (title);
1138
1139 return dialog;
1140 }
1141
1142 /**
1143 * gimp_export_dialog_get_content_area:
1144 * @dialog: A dialog created with gimp_export_dialog_new()
1145 *
1146 * Returns the #GtkVBox of the passed export dialog to be filled with
1147 * export options.
1148 *
1149 * Returns: The #GtkVBox to fill with export options.
1150 *
1151 * Since: 2.8
1152 **/
1153 GtkWidget *
gimp_export_dialog_get_content_area(GtkWidget * dialog)1154 gimp_export_dialog_get_content_area (GtkWidget *dialog)
1155 {
1156 return gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1157 }
1158