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