1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <stdio.h>
21 #include <setjmp.h>
22 #include <string.h>
23 
24 #include <jpeglib.h>
25 #include <jerror.h>
26 
27 #include <libgimp/gimp.h>
28 #include <libgimp/gimpui.h>
29 
30 #include "libgimp/stdplugins-intl.h"
31 
32 #include "jpeg.h"
33 #include "jpeg-settings.h"
34 #include "jpeg-load.h"
35 #include "jpeg-save.h"
36 
37 /* Declare local functions.
38  */
39 
40 static void  query (void);
41 static void  run   (const gchar      *name,
42                     gint              nparams,
43                     const GimpParam  *param,
44                     gint             *nreturn_vals,
45                     GimpParam       **return_vals);
46 
47 gboolean         undo_touched;
48 gboolean         load_interactive;
49 gchar           *image_comment;
50 gint32           display_ID;
51 JpegSaveVals     jsvals;
52 gint32           orig_image_ID_global;
53 gint32           drawable_ID_global;
54 gint             orig_quality;
55 JpegSubsampling  orig_subsmp;
56 gint             num_quant_tables;
57 
58 
59 const GimpPlugInInfo PLUG_IN_INFO =
60 {
61   NULL,  /* init_proc  */
62   NULL,  /* quit_proc  */
63   query, /* query_proc */
64   run,   /* run_proc   */
65 };
66 
67 
MAIN()68 MAIN ()
69 
70 
71 static void
72 query (void)
73 {
74   static const GimpParamDef load_args[] =
75   {
76     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
77     { GIMP_PDB_STRING,   "filename",     "The name of the file to load" },
78     { GIMP_PDB_STRING,   "raw-filename", "The name of the file to load" }
79   };
80   static const GimpParamDef load_return_vals[] =
81   {
82     { GIMP_PDB_IMAGE,   "image",         "Output image" }
83   };
84 
85   static const GimpParamDef thumb_args[] =
86   {
87     { GIMP_PDB_STRING, "filename",     "The name of the file to load"  },
88     { GIMP_PDB_INT32,  "thumb-size",   "Preferred thumbnail size"      }
89   };
90   static const GimpParamDef thumb_return_vals[] =
91   {
92     { GIMP_PDB_IMAGE,  "image",        "Thumbnail image"               },
93     { GIMP_PDB_INT32,  "image-width",  "Width of full-sized image"     },
94     { GIMP_PDB_INT32,  "image-height", "Height of full-sized image"    }
95   };
96 
97   static const GimpParamDef save_args[] =
98   {
99     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
100     { GIMP_PDB_IMAGE,    "image",        "Input image" },
101     { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to save" },
102     { GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in" },
103     { GIMP_PDB_STRING,   "raw-filename", "The name of the file to save the image in" },
104     { GIMP_PDB_FLOAT,    "quality",      "Quality of saved image (0 <= quality <= 1)" },
105     { GIMP_PDB_FLOAT,    "smoothing",    "Smoothing factor for saved image (0 <= smoothing <= 1)" },
106     { GIMP_PDB_INT32,    "optimize",     "Use optimized tables during Huffman coding (0/1)" },
107     { GIMP_PDB_INT32,    "progressive",  "Create progressive JPEG images (0/1)" },
108     { GIMP_PDB_STRING,   "comment",      "Image comment" },
109     { GIMP_PDB_INT32,    "subsmp",       "Sub-sampling type { 0, 1, 2, 3 } 0 == 4:2:0 (chroma quartered), 1 == 4:2:2 Horizontal (chroma halved), 2 == 4:4:4 (best quality), 3 == 4:2:2 Vertical (chroma halved)" },
110     { GIMP_PDB_INT32,    "baseline",     "Force creation of a baseline JPEG (non-baseline JPEGs can't be read by all decoders) (0/1)" },
111     { GIMP_PDB_INT32,    "restart",      "Interval of restart markers (in MCU rows, 0 = no restart markers)" },
112     { GIMP_PDB_INT32,    "dct",          "DCT method to use { INTEGER (0), FIXED (1), FLOAT (2) }" }
113   };
114 
115   gimp_install_procedure (LOAD_PROC,
116                           "loads files in the JPEG file format",
117                           "loads files in the JPEG file format",
118                           "Spencer Kimball, Peter Mattis & others",
119                           "Spencer Kimball & Peter Mattis",
120                           "1995-2007",
121                           N_("JPEG image"),
122                           NULL,
123                           GIMP_PLUGIN,
124                           G_N_ELEMENTS (load_args),
125                           G_N_ELEMENTS (load_return_vals),
126                           load_args, load_return_vals);
127 
128   gimp_register_file_handler_mime (LOAD_PROC, "image/jpeg");
129   gimp_register_magic_load_handler (LOAD_PROC,
130                                     "jpg,jpeg,jpe",
131                                     "",
132                                     "0,string,\xff\xd8\xff");
133 
134   gimp_install_procedure (LOAD_THUMB_PROC,
135                           "Loads a thumbnail from a JPEG image",
136                           "Loads a thumbnail from a JPEG image (only if it exists)",
137                           "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
138                           "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
139                           "November 15, 2004",
140                           NULL,
141                           NULL,
142                           GIMP_PLUGIN,
143                           G_N_ELEMENTS (thumb_args),
144                           G_N_ELEMENTS (thumb_return_vals),
145                           thumb_args, thumb_return_vals);
146 
147   gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
148 
149   gimp_install_procedure (SAVE_PROC,
150                           "saves files in the JPEG file format",
151                           "saves files in the lossy, widely supported JPEG format",
152                           "Spencer Kimball, Peter Mattis & others",
153                           "Spencer Kimball & Peter Mattis",
154                           "1995-2007",
155                           N_("JPEG image"),
156                           "RGB*, GRAY*",
157                           GIMP_PLUGIN,
158                           G_N_ELEMENTS (save_args), 0,
159                           save_args, NULL);
160 
161   gimp_register_file_handler_mime (SAVE_PROC, "image/jpeg");
162   gimp_register_save_handler (SAVE_PROC, "jpg,jpeg,jpe", "");
163 }
164 
165 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)166 run (const gchar      *name,
167      gint              nparams,
168      const GimpParam  *param,
169      gint             *nreturn_vals,
170      GimpParam       **return_vals)
171 {
172   static GimpParam   values[6];
173   GimpRunMode        run_mode;
174   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
175   gint32             image_ID;
176   gint32             drawable_ID;
177   GimpParasite      *parasite;
178   GError            *error  = NULL;
179 
180   run_mode = param[0].data.d_int32;
181 
182   INIT_I18N ();
183   gegl_init (NULL, NULL);
184 
185   *nreturn_vals = 1;
186   *return_vals  = values;
187   values[0].type          = GIMP_PDB_STATUS;
188   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
189 
190   preview_image_ID = -1;
191   preview_layer_ID = -1;
192 
193   orig_quality = 0;
194   orig_subsmp = JPEG_SUBSAMPLING_2x2_1x1_1x1;
195   num_quant_tables = 0;
196 
197   if (strcmp (name, LOAD_PROC) == 0)
198     {
199       gboolean resolution_loaded = FALSE;
200 
201       switch (run_mode)
202         {
203         case GIMP_RUN_INTERACTIVE:
204         case GIMP_RUN_WITH_LAST_VALS:
205           gimp_ui_init (PLUG_IN_BINARY, FALSE);
206           load_interactive = TRUE;
207           break;
208         default:
209           load_interactive = FALSE;
210           break;
211         }
212 
213       image_ID = load_image (param[1].data.d_string, run_mode, FALSE,
214                              &resolution_loaded, &error);
215 
216       if (image_ID != -1)
217         {
218           GFile        *file = g_file_new_for_path (param[1].data.d_string);
219           GimpMetadata *metadata;
220 
221           metadata = gimp_image_metadata_load_prepare (image_ID, "image/jpeg",
222                                                        file, NULL);
223 
224           if (metadata)
225             {
226               GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
227 
228               if (resolution_loaded)
229                 flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
230 
231               gimp_image_metadata_load_finish (image_ID, "image/jpeg",
232                                                metadata, flags,
233                                                load_interactive);
234 
235               g_object_unref (metadata);
236             }
237 
238           g_object_unref (file);
239 
240           *nreturn_vals = 2;
241           values[1].type         = GIMP_PDB_IMAGE;
242           values[1].data.d_image = image_ID;
243         }
244       else
245         {
246           status = GIMP_PDB_EXECUTION_ERROR;
247         }
248     }
249   else if (strcmp (name, LOAD_THUMB_PROC) == 0)
250     {
251       if (nparams < 2)
252         {
253           status = GIMP_PDB_CALLING_ERROR;
254         }
255       else
256         {
257           GFile        *file   = g_file_new_for_path (param[0].data.d_string);
258           gint          width  = 0;
259           gint          height = 0;
260           GimpImageType type   = -1;
261 
262           image_ID = load_thumbnail_image (file, &width, &height, &type,
263                                            &error);
264 
265           g_object_unref (file);
266 
267           if (image_ID != -1)
268             {
269               *nreturn_vals = 6;
270               values[1].type         = GIMP_PDB_IMAGE;
271               values[1].data.d_image = image_ID;
272               values[2].type         = GIMP_PDB_INT32;
273               values[2].data.d_int32 = width;
274               values[3].type         = GIMP_PDB_INT32;
275               values[3].data.d_int32 = height;
276               values[4].type         = GIMP_PDB_INT32;
277               values[4].data.d_int32 = type;
278               values[5].type         = GIMP_PDB_INT32;
279               values[5].data.d_int32 = 1; /* num_layers */
280             }
281           else
282             {
283               status = GIMP_PDB_EXECUTION_ERROR;
284             }
285         }
286     }
287   else if (strcmp (name, SAVE_PROC) == 0)
288     {
289       GimpMetadata          *metadata;
290       GimpMetadataSaveFlags  metadata_flags;
291       gint32                 orig_image_ID;
292       GimpExportReturn       export = GIMP_EXPORT_CANCEL;
293 
294       image_ID    = param[1].data.d_int32;
295       drawable_ID = param[2].data.d_int32;
296 
297       orig_image_ID = image_ID;
298 
299       switch (run_mode)
300         {
301         case GIMP_RUN_INTERACTIVE:
302         case GIMP_RUN_WITH_LAST_VALS:
303           gimp_ui_init (PLUG_IN_BINARY, FALSE);
304 
305           export = gimp_export_image (&image_ID, &drawable_ID, "JPEG",
306                                       GIMP_EXPORT_CAN_HANDLE_RGB |
307                                       GIMP_EXPORT_CAN_HANDLE_GRAY);
308 
309           switch (export)
310             {
311             case GIMP_EXPORT_EXPORT:
312               {
313                 gchar *tmp = g_filename_from_utf8 (_("Export Preview"), -1,
314                                                    NULL, NULL, NULL);
315                 if (tmp)
316                   {
317                     gimp_image_set_filename (image_ID, tmp);
318                     g_free (tmp);
319                   }
320 
321                 display_ID = -1;
322               }
323               break;
324 
325             case GIMP_EXPORT_IGNORE:
326               break;
327 
328             case GIMP_EXPORT_CANCEL:
329               values[0].data.d_status = GIMP_PDB_CANCEL;
330               return;
331               break;
332             }
333           break;
334 
335         default:
336           break;
337         }
338 
339       /* Initialize with hardcoded defaults */
340       load_defaults ();
341 
342       /* Override the defaults with preferences. */
343       metadata = gimp_image_metadata_save_prepare (orig_image_ID,
344                                                    "image/jpeg",
345                                                    &metadata_flags);
346       jsvals.save_exif      = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
347       jsvals.save_xmp       = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
348       jsvals.save_iptc      = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
349       jsvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
350       jsvals.save_profile   = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
351 
352       parasite = gimp_image_get_parasite (orig_image_ID, "gimp-comment");
353       if (parasite)
354         {
355           image_comment = g_strndup (gimp_parasite_data (parasite),
356                                      gimp_parasite_data_size (parasite));
357           gimp_parasite_free (parasite);
358         }
359 
360       /* Override preferences from JPG export defaults (if saved). */
361       load_parasite ();
362 
363       switch (run_mode)
364         {
365         case GIMP_RUN_NONINTERACTIVE:
366           /*  Make sure all the arguments are there!  */
367           /*  pw - added two more progressive and comment */
368           /*  sg - added subsampling, preview, baseline, restarts and DCT */
369           if (nparams != 14)
370             {
371               status = GIMP_PDB_CALLING_ERROR;
372             }
373           else
374             {
375               /* Once the PDB gets default parameters, remove this hack */
376               if (param[5].data.d_float >= 0.01)
377                 {
378                   jsvals.quality     = 100.0 * param[5].data.d_float;
379                   jsvals.smoothing   = param[6].data.d_float;
380                   jsvals.optimize    = param[7].data.d_int32;
381                   jsvals.progressive = param[8].data.d_int32;
382                   jsvals.baseline    = param[11].data.d_int32;
383                   jsvals.subsmp      = param[10].data.d_int32;
384                   jsvals.restart     = param[12].data.d_int32;
385                   jsvals.dct         = param[13].data.d_int32;
386 
387                   /* free up the default -- wasted some effort earlier */
388                   g_free (image_comment);
389                   image_comment = g_strdup (param[9].data.d_string);
390                 }
391 
392               jsvals.preview = FALSE;
393 
394               if (jsvals.quality < 0.0 || jsvals.quality > 100.0)
395                 status = GIMP_PDB_CALLING_ERROR;
396               else if (jsvals.smoothing < 0.0 || jsvals.smoothing > 1.0)
397                 status = GIMP_PDB_CALLING_ERROR;
398               else if (jsvals.subsmp < 0 || jsvals.subsmp > 3)
399                 status = GIMP_PDB_CALLING_ERROR;
400               else if (jsvals.dct < 0 || jsvals.dct > 2)
401                 status = GIMP_PDB_CALLING_ERROR;
402             }
403           break;
404 
405         case GIMP_RUN_INTERACTIVE:
406         case GIMP_RUN_WITH_LAST_VALS:
407           /* restore the values found when loading the file (if available) */
408           jpeg_restore_original_settings (orig_image_ID,
409                                           &orig_quality,
410                                           &orig_subsmp,
411                                           &num_quant_tables);
412 
413           /* load up the previously used values (if file was saved once) */
414           parasite = gimp_image_get_parasite (orig_image_ID,
415                                               "jpeg-save-options");
416           if (parasite)
417             {
418               const JpegSaveVals *save_vals = gimp_parasite_data (parasite);
419 
420               jsvals.quality          = save_vals->quality;
421               jsvals.smoothing        = save_vals->smoothing;
422               jsvals.optimize         = save_vals->optimize;
423               jsvals.progressive      = save_vals->progressive;
424               jsvals.baseline         = save_vals->baseline;
425               jsvals.subsmp           = save_vals->subsmp;
426               jsvals.restart          = save_vals->restart;
427               jsvals.dct              = save_vals->dct;
428               jsvals.preview          = save_vals->preview;
429               jsvals.save_exif        = save_vals->save_exif;
430               jsvals.save_thumbnail   = save_vals->save_thumbnail;
431               jsvals.save_xmp         = save_vals->save_xmp;
432               jsvals.save_iptc        = save_vals->save_iptc;
433               jsvals.use_orig_quality = save_vals->use_orig_quality;
434 
435               gimp_parasite_free (parasite);
436             }
437           else
438             {
439               /* We are called with GIMP_RUN_WITH_LAST_VALS but this image
440                * doesn't have a "jpeg-save-options" parasite. It's better
441                * to prompt the user with a dialog now so that she has control
442                * over the JPEG encoding parameters.
443                */
444               run_mode = GIMP_RUN_INTERACTIVE;
445 
446               /* If this image was loaded from a JPEG file and has not been
447                * saved yet, try to use some of the settings from the
448                * original file if they are better than the default values.
449                */
450               if (orig_quality > jsvals.quality)
451                 {
452                   jsvals.quality = orig_quality;
453                 }
454 
455               /* Skip changing subsampling to original if we already have best
456                * setting or if original have worst setting */
457               if (!(jsvals.subsmp == JPEG_SUBSAMPLING_1x1_1x1_1x1 ||
458                     orig_subsmp == JPEG_SUBSAMPLING_2x2_1x1_1x1))
459                 {
460                   jsvals.subsmp = orig_subsmp;
461                 }
462 
463               if (orig_quality == jsvals.quality &&
464                   orig_subsmp == jsvals.subsmp)
465                 {
466                   jsvals.use_orig_quality = TRUE;
467                 }
468             }
469           break;
470         }
471 
472       if (run_mode == GIMP_RUN_INTERACTIVE)
473         {
474           if (jsvals.preview)
475             {
476               /* we freeze undo saving so that we can avoid sucking up
477                * tile cache with our unneeded preview steps. */
478               gimp_image_undo_freeze (image_ID);
479 
480               undo_touched = TRUE;
481             }
482 
483           /* prepare for the preview */
484           preview_image_ID = image_ID;
485           orig_image_ID_global = orig_image_ID;
486           drawable_ID_global = drawable_ID;
487 
488           /*  First acquire information with a dialog  */
489           status = (save_dialog () ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL);
490 
491           if (undo_touched)
492             {
493               /* thaw undo saving and flush the displays to have them
494                * reflect the current shortcuts */
495               gimp_image_undo_thaw (image_ID);
496               gimp_displays_flush ();
497             }
498         }
499 
500       if (status == GIMP_PDB_SUCCESS)
501         {
502           if (! save_image (param[3].data.d_string,
503                             image_ID, drawable_ID, orig_image_ID, FALSE,
504                             &error))
505             {
506               status = GIMP_PDB_EXECUTION_ERROR;
507             }
508         }
509 
510       if (export == GIMP_EXPORT_EXPORT)
511         {
512           /* If the image was exported, delete the new display. */
513           /* This also deletes the image.
514            */
515 
516           if (display_ID != -1)
517             gimp_display_delete (display_ID);
518           else
519             gimp_image_delete (image_ID);
520         }
521 
522       if (status == GIMP_PDB_SUCCESS)
523         {
524           /* pw - now we need to change the defaults to be whatever
525            * was used to save this image.  Dump the old parasites
526            * and add new ones.
527            */
528 
529           gimp_image_detach_parasite (orig_image_ID, "gimp-comment");
530           if (image_comment && strlen (image_comment))
531             {
532               parasite = gimp_parasite_new ("gimp-comment",
533                                             GIMP_PARASITE_PERSISTENT,
534                                             strlen (image_comment) + 1,
535                                             image_comment);
536               gimp_image_attach_parasite (orig_image_ID, parasite);
537               gimp_parasite_free (parasite);
538             }
539 
540           parasite = gimp_parasite_new ("jpeg-save-options",
541                                         0, sizeof (jsvals), &jsvals);
542           gimp_image_attach_parasite (orig_image_ID, parasite);
543           gimp_parasite_free (parasite);
544 
545           /* write metadata */
546 
547           if (metadata)
548             {
549               GFile *file;
550 
551               gimp_metadata_set_bits_per_sample (metadata, 8);
552 
553               if (jsvals.save_exif)
554                 metadata_flags |= GIMP_METADATA_SAVE_EXIF;
555               else
556                 metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
557 
558               if (jsvals.save_xmp)
559                 metadata_flags |= GIMP_METADATA_SAVE_XMP;
560               else
561                 metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
562 
563               if (jsvals.save_iptc)
564                 metadata_flags |= GIMP_METADATA_SAVE_IPTC;
565               else
566                 metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
567 
568               if (jsvals.save_thumbnail)
569                 metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
570               else
571                 metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
572 
573               if (jsvals.save_profile)
574                 metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
575               else
576                 metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
577 
578               file = g_file_new_for_path (param[3].data.d_string);
579 	          if (! gimp_image_metadata_save_finish (orig_image_ID,
580 	                                                 "image/jpeg",
581 	                                                 metadata, metadata_flags,
582 	                                                 file, &error))
583 	            {
584 	              if (error)
585 	                {
586 	                  /* Even though a failure to write metadata is not enough
587 	                     reason to say we failed to save the image, we should
588 	                     still notify the user about the problem. */
589 	                  g_message ("%s: saving metadata failed: %s",
590 	                             G_STRFUNC, error->message);
591 	                  g_error_free (error);
592 	                }
593 	            }
594               g_object_unref (file);
595             }
596         }
597 
598       if (metadata)
599         g_object_unref (metadata);
600     }
601   else
602     {
603       status = GIMP_PDB_CALLING_ERROR;
604     }
605 
606   if (status != GIMP_PDB_SUCCESS && error)
607     {
608       *nreturn_vals = 2;
609       values[1].type          = GIMP_PDB_STRING;
610       values[1].data.d_string = error->message;
611     }
612 
613   values[0].data.d_status = status;
614 }
615 
616 /*
617  * Here's the routine that will replace the standard error_exit method:
618  */
619 
620 void
my_error_exit(j_common_ptr cinfo)621 my_error_exit (j_common_ptr cinfo)
622 {
623   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
624   my_error_ptr myerr = (my_error_ptr) cinfo->err;
625 
626   /* Always display the message. */
627   /* We could postpone this until after returning, if we chose. */
628   (*cinfo->err->output_message) (cinfo);
629 
630   /* Return control to the setjmp point */
631   longjmp (myerr->setjmp_buffer, 1);
632 }
633 
634 
635 void
my_output_message(j_common_ptr cinfo)636 my_output_message (j_common_ptr cinfo)
637 {
638   gchar  buffer[JMSG_LENGTH_MAX + 1];
639 
640   (*cinfo->err->format_message)(cinfo, buffer);
641 
642   g_message ("%s", buffer);
643 }
644