home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / libgimp / gimpexport.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-19  |  17.0 KB  |  595 lines

  1. /* LIBGIMP - The GIMP Library
  2.  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
  3.  *
  4.  * gimpexport.c
  5.  * Copyright (C) 1999-2000 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 2 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, write to the
  19.  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20.  * Boston, MA 02111-1307, USA.
  21.  */                             
  22.  
  23. #include "config.h"
  24.  
  25. #include <gtk/gtk.h>
  26.  
  27. #include "gimp.h"
  28. #include "gimpui.h"
  29.  
  30. #include "libgimp-intl.h"
  31.  
  32.  
  33. typedef void (* ExportFunc) (gint32  imageID,
  34.                  gint32 *drawable_ID);
  35.  
  36.  
  37. /* the export action structure */
  38. typedef struct 
  39. {
  40.   ExportFunc  default_action;
  41.   ExportFunc  alt_action;
  42.   gchar      *reason;
  43.   gchar      *possibilities[2];
  44.   gint        choice;
  45. } ExportAction;
  46.  
  47.  
  48. /* the functions that do the actual export */
  49.  
  50. static void
  51. export_merge (gint32  image_ID,
  52.           gint32 *drawable_ID)
  53. {
  54.   gint32  nlayers;
  55.   gint32  nvisible = 0;
  56.   gint32  i;
  57.   gint32 *layers;
  58.   gint32  visible = *drawable_ID;
  59.   gint32  merged;
  60.  
  61.   layers = gimp_image_get_layers (image_ID, &nlayers);
  62.   for (i = 0; i < nlayers; i++)
  63.     {
  64.       if (gimp_drawable_visible (layers[i]))
  65.     {
  66.       nvisible++;
  67.       visible = layers[i];
  68.     }
  69.     }
  70.  
  71.   if (nvisible == 1 && *drawable_ID != visible)
  72.     *drawable_ID = visible;    
  73.  
  74.   if (nvisible > 1)
  75.     {
  76.       g_free (layers);
  77.       merged = gimp_image_merge_visible_layers (image_ID, GIMP_CLIP_TO_IMAGE);
  78.  
  79.       if (merged != -1)
  80.     *drawable_ID = merged;
  81.       else
  82.     return;  /* shouldn't happen */
  83.       
  84.       layers = gimp_image_get_layers (image_ID, &nlayers);
  85.     }    
  86.   
  87.   /* remove any remaining (invisible) layers */ 
  88.   for (i = 0; i < nlayers; i++)
  89.     {
  90.       if (layers[i] != *drawable_ID)
  91.     gimp_image_remove_layer (image_ID, layers[i]);
  92.     }
  93.   g_free (layers);  
  94. }
  95.  
  96. static void
  97. export_flatten (gint32  image_ID,
  98.         gint32 *drawable_ID)
  99. {  
  100.   gint32 flattened;
  101.   
  102.   flattened = gimp_image_flatten (image_ID);
  103.  
  104.   if (flattened != -1)
  105.     *drawable_ID = flattened;
  106. }
  107.  
  108. static void
  109. export_convert_rgb (gint32  image_ID,
  110.             gint32 *drawable_ID)
  111. {  
  112.   gimp_image_convert_rgb (image_ID);
  113. }
  114.  
  115. static void
  116. export_convert_grayscale (gint32  image_ID,
  117.               gint32 *drawable_ID)
  118. {  
  119.   gimp_image_convert_grayscale (image_ID);
  120. }
  121.  
  122. static void
  123. export_convert_indexed (gint32  image_ID,
  124.             gint32 *drawable_ID)
  125. {  
  126.   gint32 nlayers;
  127.   
  128.   /* check alpha */
  129.   g_free (gimp_image_get_layers (image_ID, &nlayers));
  130.   if (nlayers > 1 || gimp_drawable_has_alpha (*drawable_ID))
  131.     gimp_image_convert_indexed (image_ID, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, 255, FALSE, FALSE, "");
  132.   else
  133.     gimp_image_convert_indexed (image_ID, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, 256, FALSE, FALSE, ""); 
  134. }
  135.  
  136. static void
  137. export_add_alpha (gint32  image_ID,
  138.           gint32 *drawable_ID)
  139. {  
  140.   gint32  nlayers;
  141.   gint32  i;
  142.   gint32 *layers;
  143.  
  144.   layers = gimp_image_get_layers (image_ID, &nlayers);
  145.   for (i = 0; i < nlayers; i++)
  146.     {
  147.       if (!gimp_drawable_has_alpha (layers[i]))
  148.     gimp_layer_add_alpha (layers[i]);
  149.     }
  150.   g_free (layers);  
  151. }
  152.  
  153.  
  154. /* a set of predefined actions */
  155.  
  156. static ExportAction export_action_merge =
  157. {
  158.   export_merge,
  159.   NULL,
  160.   N_("can't handle layers"),
  161.   { N_("Merge Visible Layers"), NULL },
  162.   0
  163. };
  164.  
  165. static ExportAction export_action_animate_or_merge =
  166. {
  167.   export_merge,
  168.   NULL,
  169.   N_("can only handle layers as animation frames"),
  170.   { N_("Merge Visible Layers"), N_("Save as Animation")},
  171.   0
  172. };
  173.  
  174. static ExportAction export_action_animate_or_flatten =
  175. {
  176.   export_flatten,
  177.   NULL,
  178.   N_("can only handle layers as animation frames"),
  179.   { N_("Flatten Image"), N_("Save as Animation") },
  180.   0
  181. };
  182.  
  183. static ExportAction export_action_merge_flat =
  184. {
  185.   export_flatten,
  186.   NULL,
  187.   N_("can't handle layers"),
  188.   { N_("Flatten Image"), NULL },
  189.   0
  190. };
  191.  
  192. static ExportAction export_action_flatten =
  193. {
  194.   export_flatten,
  195.   NULL,
  196.   N_("can't handle transparency"),
  197.   { N_("Flatten Image"), NULL },
  198.   0
  199. };
  200.  
  201. static ExportAction export_action_convert_rgb =
  202. {
  203.   export_convert_rgb,
  204.   NULL,
  205.   N_("can only handle RGB images"),
  206.   { N_("Convert to RGB"), NULL },
  207.   0
  208. };
  209.  
  210. static ExportAction export_action_convert_grayscale =
  211. {
  212.   export_convert_grayscale,
  213.   NULL,
  214.   N_("can only handle grayscale images"),
  215.   { N_("Convert to Grayscale"), NULL },
  216.   0
  217. };
  218.  
  219. static ExportAction export_action_convert_indexed =
  220. {
  221.   export_convert_indexed,
  222.   NULL,
  223.   N_("can only handle indexed images"),
  224.   { N_("Convert to Indexed using default settings\n"
  225.        "(Do it manually to tune the result)"), NULL },
  226.   0
  227. };
  228.  
  229. static ExportAction export_action_convert_rgb_or_grayscale =
  230. {
  231.   export_convert_rgb,
  232.   export_convert_grayscale,
  233.   N_("can only handle RGB or grayscale images"),
  234.   { N_("Convert to RGB"), N_("Convert to Grayscale")},
  235.   0
  236. };
  237.  
  238. static ExportAction export_action_convert_rgb_or_indexed =
  239. {
  240.   export_convert_rgb,
  241.   export_convert_indexed,
  242.   N_("can only handle RGB or indexed images"),
  243.   { N_("Convert to RGB"), N_("Convert to Indexed using default settings\n"
  244.                  "(Do it manually to tune the result)")},
  245.   0
  246. };
  247.  
  248. static ExportAction export_action_convert_indexed_or_grayscale =
  249. {
  250.   export_convert_indexed,
  251.   export_convert_grayscale,
  252.   N_("can only handle grayscale or indexed images"),
  253.   { N_("Convert to Indexed using default settings\n"
  254.        "(Do it manually to tune the result)"), 
  255.     N_("Convert to Grayscale") },
  256.   0
  257. };
  258.  
  259. static ExportAction export_action_add_alpha =
  260. {
  261.   export_add_alpha,
  262.   NULL,
  263.   N_("needs an alpha channel"),
  264.   { N_("Add Alpha Channel"), NULL},
  265.   0
  266. };
  267.  
  268.  
  269. /* dialog functions */
  270.  
  271. static GtkWidget            *dialog = NULL;
  272. static GimpExportReturnType  dialog_return = GIMP_EXPORT_CANCEL;
  273.  
  274. static void
  275. export_export_callback (GtkWidget *widget,
  276.             gpointer   data)
  277. {
  278.   gtk_widget_destroy (dialog);
  279.   dialog_return = GIMP_EXPORT_EXPORT;
  280. }
  281.  
  282. static void
  283. export_skip_callback (GtkWidget *widget,
  284.               gpointer   data)
  285. {
  286.   gtk_widget_destroy (dialog);
  287.   dialog_return = GIMP_EXPORT_IGNORE;
  288. }
  289.  
  290. static void
  291. export_cancel_callback (GtkWidget *widget,
  292.             gpointer   data)
  293. {
  294.   dialog_return = GIMP_EXPORT_CANCEL;
  295.   dialog = NULL;
  296.   gtk_main_quit ();
  297. }
  298.  
  299. static void
  300. export_toggle_callback (GtkWidget *widget,
  301.             gpointer   data)
  302. {
  303.   gint *choice = (gint*)data;
  304.   
  305.   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
  306.     *choice = 0;
  307.   else
  308.     *choice = 1;
  309. }
  310.  
  311. static gint
  312. export_dialog (GSList      *actions,
  313.            const gchar *format)
  314. {
  315.   GtkWidget    *frame;
  316.   GtkWidget    *vbox;
  317.   GtkWidget    *hbox;
  318.   GtkWidget    *button;
  319.   GtkWidget    *label;
  320.   GSList       *list;
  321.   gchar        *text;
  322.   ExportAction *action;
  323.  
  324.   dialog_return = GIMP_EXPORT_CANCEL;
  325.   g_return_val_if_fail (actions != NULL && format != NULL, dialog_return);
  326.  
  327.   /*
  328.    *  Plug-ins have called gtk_init () before calling gimp_export ().
  329.    *  Otherwise bad things will happen now!!
  330.    */
  331.  
  332.   /* the dialog */
  333.   dialog = gimp_dialog_new (_("Export File"), "export_file",
  334.                 gimp_standard_help_func, "dialogs/export_file.html",
  335.                 GTK_WIN_POS_MOUSE,
  336.                 FALSE, FALSE, FALSE,
  337.  
  338.                 _("Export"), export_export_callback,
  339.                 NULL, NULL, NULL, TRUE, FALSE,
  340.                 _("Ignore"), export_skip_callback,
  341.                 NULL, NULL, NULL, FALSE, FALSE,
  342.                 _("Cancel"), gtk_widget_destroy,
  343.                 NULL, 1, NULL, FALSE, TRUE,
  344.  
  345.                 NULL);
  346.  
  347.   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
  348.               GTK_SIGNAL_FUNC (export_cancel_callback),
  349.               NULL);
  350.  
  351.   /* the headline */
  352.   vbox = gtk_vbox_new (FALSE, 6);
  353.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), vbox);
  354.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
  355.   gtk_widget_show (vbox);
  356.  
  357.   label = gtk_label_new (_("Your image should be exported before it "
  358.                "can be saved for the following reasons:"));
  359.   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  360.   gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
  361.   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  362.   gtk_widget_show (label);
  363.  
  364.   for (list = actions; list; list = list->next)
  365.     {
  366.       action = (ExportAction *) (list->data);
  367.  
  368.       text = g_strdup_printf ("%s %s", format, gettext (action->reason));
  369.       frame = gtk_frame_new (text);
  370.       g_free (text);
  371.       gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
  372.  
  373.       hbox = gtk_hbox_new (FALSE, 4);
  374.       gtk_container_add (GTK_CONTAINER (frame), hbox);
  375.       gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
  376.  
  377.       if (action->possibilities[0] && action->possibilities[1])
  378.     {
  379.       GSList *radio_group = NULL;
  380.  
  381.       button = gtk_radio_button_new_with_label (radio_group, 
  382.                             gettext (action->possibilities[0]));
  383.       gtk_label_set_justify (GTK_LABEL (GTK_BIN (button)->child), GTK_JUSTIFY_LEFT);
  384.       radio_group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
  385.       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  386.       gtk_signal_connect (GTK_OBJECT (button), "toggled",
  387.                   GTK_SIGNAL_FUNC (export_toggle_callback),
  388.                   &action->choice);
  389.       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
  390.       gtk_widget_show (button);
  391.  
  392.       button = gtk_radio_button_new_with_label (radio_group, 
  393.                             gettext (action->possibilities[1]));
  394.       gtk_label_set_justify (GTK_LABEL (GTK_BIN (button)->child), GTK_JUSTIFY_LEFT);
  395.       radio_group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
  396.       gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  397.       gtk_widget_show (button);
  398.     } 
  399.       else if (action->possibilities[0])
  400.     {
  401.       label = gtk_label_new (gettext (action->possibilities[0]));
  402.       gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
  403.       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 2);
  404.       gtk_widget_show (label);
  405.       action->choice = 0;
  406.     }
  407.       else if (action->possibilities[1])
  408.     {
  409.       label = gtk_label_new (gettext (action->possibilities[1]));
  410.       gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
  411.       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 2);
  412.       gtk_widget_show (label);
  413.       action->choice = 1;
  414.     }      
  415.       gtk_widget_show (hbox);
  416.       gtk_widget_show (frame);
  417.     }
  418.  
  419.   /* the footline */
  420.   label = gtk_label_new (_("The export conversion won't modify your original image."));
  421.   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
  422.   gtk_widget_show (label);
  423.  
  424.   gtk_widget_show (dialog);
  425.   gtk_main ();
  426.  
  427.   return dialog_return;
  428. }
  429.  
  430. /**
  431.  * gimp_export_image:
  432.  * @image_ID: Pointer to the image_ID.
  433.  * @drawable_ID: Pointer to the drawable_ID.
  434.  * @format_name: The (short) name of the image_format (e.g. JPEG or GIF).
  435.  * @capabilities: What can the image_format do?
  436.  *
  437.  * Takes an image and a drawable to be saved together with a
  438.  * description of the capabilities of the image_format. If the
  439.  * type of image doesn't match the capabilities of the format
  440.  * a dialog is opened that informs the user that the image has
  441.  * to be exported and offers to do the necessary conversions.
  442.  *
  443.  * If the user chooses to export the image, a copy is created.
  444.  * This copy is then converted, the image_ID and drawable_ID
  445.  * are changed to point to the new image and the procedure returns
  446.  * GIMP_EXPORT_EXPORT. The save_plugin has to take care of deleting the
  447.  * created image using gimp_image_delete() when it has saved it.
  448.  *
  449.  * If the user chooses to Ignore the export problem, the image_ID
  450.  * and drawable_ID is not altered, GIMP_EXPORT_IGNORE is returned and 
  451.  * the save_plugin should try to save the original image. If the 
  452.  * user chooses Cancel, GIMP_EXPORT_CANCEL is returned and the 
  453.  * save_plugin should quit itself with status #STATUS_CANCEL.
  454.  *
  455.  * Returns: An enum of #GimpExportReturnType describing the user_action.
  456.  **/
  457. GimpExportReturnType
  458. gimp_export_image (gint32                 *image_ID,
  459.            gint32                 *drawable_ID,
  460.            const gchar            *format_name,
  461.            GimpExportCapabilities  capabilities)
  462. {
  463.   GSList *actions = NULL;
  464.   GSList *list;
  465.   GimpImageBaseType type;
  466.   gint32  i;
  467.   gint32  nlayers;
  468.   gint32* layers;
  469.   gboolean added_flatten = FALSE;
  470.   gboolean background_has_alpha = TRUE;
  471.   ExportAction *action;
  472.  
  473.   g_return_val_if_fail (*image_ID > -1 && *drawable_ID > -1, FALSE);
  474.  
  475.   /* do some sanity checks */
  476.   if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
  477.     capabilities |= GIMP_EXPORT_CAN_HANDLE_ALPHA ;
  478.   if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
  479.     capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
  480.  
  481.   /* check alpha */
  482.   layers = gimp_image_get_layers (*image_ID, &nlayers);
  483.   for (i = 0; i < nlayers; i++)
  484.     {
  485.       if (gimp_drawable_has_alpha (layers[i]))
  486.     {
  487.  
  488.       if ( !(capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA ) )
  489.         {
  490.           actions = g_slist_prepend (actions, &export_action_flatten);
  491.           added_flatten = TRUE;
  492.           break;
  493.         }
  494.     }
  495.       else 
  496.     {
  497.           /* If this is the last layer, it's visible and has no alpha
  498.              channel, then the image has a "flat" background */
  499.             if (i == nlayers - 1 && gimp_layer_get_visible(layers[i])) 
  500.         background_has_alpha = FALSE;
  501.  
  502.       if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
  503.         {
  504.           actions = g_slist_prepend (actions, &export_action_add_alpha);
  505.           break;
  506.         }
  507.     }
  508.     }
  509.   g_free (layers);
  510.  
  511.   /* check multiple layers */
  512.   if (!added_flatten && nlayers > 1)
  513.     {
  514.       if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
  515.     {
  516.       if (background_has_alpha || capabilities & GIMP_EXPORT_NEEDS_ALPHA)
  517.         actions = g_slist_prepend (actions, &export_action_animate_or_merge);
  518.       else
  519.         actions = g_slist_prepend (actions, &export_action_animate_or_flatten);
  520.     }
  521.       else if ( !(capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
  522.     {
  523.       if (background_has_alpha || capabilities & GIMP_EXPORT_NEEDS_ALPHA)
  524.         actions = g_slist_prepend (actions, &export_action_merge);
  525.       else
  526.         actions = g_slist_prepend (actions, &export_action_merge_flat);
  527.     }
  528.     }
  529.  
  530.   /* check the image type */      
  531.   type = gimp_image_base_type (*image_ID);
  532.   switch (type)
  533.     {
  534.     case GIMP_RGB:
  535.        if ( !(capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) )
  536.     {
  537.       if ((capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED) && (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
  538.         actions = g_slist_prepend (actions, &export_action_convert_indexed_or_grayscale);
  539.       else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
  540.         actions = g_slist_prepend (actions, &export_action_convert_indexed);
  541.       else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
  542.         actions = g_slist_prepend (actions, &export_action_convert_grayscale);
  543.     }
  544.       break;
  545.     case GIMP_GRAY:
  546.       if ( !(capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY) )
  547.     {
  548.       if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) && (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
  549.         actions = g_slist_prepend (actions, &export_action_convert_rgb_or_indexed);
  550.       else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
  551.         actions = g_slist_prepend (actions, &export_action_convert_rgb);
  552.       else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
  553.         actions = g_slist_prepend (actions, &export_action_convert_indexed);
  554.     }
  555.       break;
  556.     case GIMP_INDEXED:
  557.        if ( !(capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED) )
  558.     {
  559.       if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) && (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
  560.         actions = g_slist_prepend (actions, &export_action_convert_rgb_or_grayscale);
  561.       else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
  562.         actions = g_slist_prepend (actions, &export_action_convert_rgb);
  563.       else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
  564.         actions = g_slist_prepend (actions, &export_action_convert_grayscale);
  565.     }
  566.       break;
  567.     }
  568.   
  569.   if (actions)
  570.     {
  571.       actions = g_slist_reverse (actions);
  572.       dialog_return = export_dialog (actions, format_name);
  573.     }
  574.   else
  575.     dialog_return = GIMP_EXPORT_IGNORE;
  576.  
  577.   if (dialog_return == GIMP_EXPORT_EXPORT)
  578.     {
  579.       *image_ID = gimp_image_duplicate (*image_ID);
  580.       *drawable_ID = gimp_image_get_active_layer (*image_ID);
  581.       gimp_image_undo_disable (*image_ID);
  582.       for (list = actions; list; list = list->next)
  583.     {
  584.       action = (ExportAction*)(list->data);
  585.       if (action->choice == 0 && action->default_action)  
  586.         action->default_action (*image_ID, drawable_ID);
  587.       else if (action->choice == 1 && action->alt_action)
  588.         action->alt_action (*image_ID, drawable_ID);
  589.     }
  590.     }
  591.   g_slist_free (actions);
  592.  
  593.   return dialog_return;
  594. }
  595.