Support for comments and progressive save for JPEGs.
--Sven
This commit is contained in:
11
ChangeLog
11
ChangeLog
@ -1,3 +1,14 @@
|
|||||||
|
Thu Jan 14 00:02:56 MET 1999 Sven Neumann <sven@gimp.org>
|
||||||
|
|
||||||
|
* acconfig.h
|
||||||
|
* config.h
|
||||||
|
* configure.in: check for progressive mode in libjpeg
|
||||||
|
|
||||||
|
* plug-ins/jpeg/jpeg.c: applied a patch from
|
||||||
|
Pete <pwhiting@fury.ittc.ukans.edu> that introduces comments
|
||||||
|
and progressive saving for JPEGs
|
||||||
|
|
||||||
|
|
||||||
Tue Jan 12 23:25:46 PST 1999 Manish Singh <yosh@gimp.org>
|
Tue Jan 12 23:25:46 PST 1999 Manish Singh <yosh@gimp.org>
|
||||||
|
|
||||||
* app/menus.c: doh, we build tool menus on the fly
|
* app/menus.c: doh, we build tool menus on the fly
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#undef HAVE_IPC_H
|
#undef HAVE_IPC_H
|
||||||
#undef HAVE_LC_MESSAGES
|
#undef HAVE_LC_MESSAGES
|
||||||
#undef HAVE_NDIR_H
|
#undef HAVE_NDIR_H
|
||||||
|
#undef HAVE_PROGRESSIVE_JPEG
|
||||||
#undef HAVE_PUTENV
|
#undef HAVE_PUTENV
|
||||||
#undef HAVE_SHM_H
|
#undef HAVE_SHM_H
|
||||||
#undef HAVE_STPCPY
|
#undef HAVE_STPCPY
|
||||||
@ -44,6 +45,8 @@
|
|||||||
|
|
||||||
#undef USE_PTHREADS
|
#undef USE_PTHREADS
|
||||||
|
|
||||||
|
#undef WITH_SYMBOL_UNDERSCORE
|
||||||
|
|
||||||
|
|
||||||
/* Leave that blank line there!! Autoheader needs it.
|
/* Leave that blank line there!! Autoheader needs it.
|
||||||
If you're adding to this file, keep in mind:
|
If you're adding to this file, keep in mind:
|
||||||
|
@ -232,6 +232,9 @@ dnl Test for libjpeg
|
|||||||
AC_MSG_RESULT($jpeg_ok)
|
AC_MSG_RESULT($jpeg_ok)
|
||||||
if test "$jpeg_ok" = yes; then
|
if test "$jpeg_ok" = yes; then
|
||||||
JPEG='jpeg'; LIBJPEG_LIB='-ljpeg'
|
JPEG='jpeg'; LIBJPEG_LIB='-ljpeg'
|
||||||
|
AC_CHECK_LIB(jpeg, jpeg_simple_progression,
|
||||||
|
AC_DEFINE(HAVE_PROGRESSIVE_JPEG),
|
||||||
|
AC_MSG_WARN(JPEG library does not support progressive saving.))
|
||||||
else
|
else
|
||||||
AC_MSG_WARN(*** JPEG plug-in will not be built (JPEG header file not found) ***)
|
AC_MSG_WARN(*** JPEG plug-in will not be built (JPEG header file not found) ***)
|
||||||
fi
|
fi
|
||||||
|
@ -24,21 +24,58 @@
|
|||||||
* from that file. The filter, therefore, also uses libjpeg.
|
* from that file. The filter, therefore, also uses libjpeg.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 11-JAN-99 - Added support for JPEG comments and Progressive saves.
|
||||||
|
* -pete whiting <pwhiting@sprint.net>
|
||||||
|
*
|
||||||
|
* Comments of size up to 32k can be stored in the header of jpeg
|
||||||
|
* files. (They are not compressed.) The JPEG specs and libraries
|
||||||
|
* support the storing of multiple comments. The behavior of this
|
||||||
|
* code is to merge all comments in a loading image into a single
|
||||||
|
* comment (putting \n between each) and attach that string as a
|
||||||
|
* parasite - gimp-comment - to the image. When saving, the image is
|
||||||
|
* checked to see if it has the gimp-comment parasite - if so, that is
|
||||||
|
* used as the default comment in the save dialog. Further, the other
|
||||||
|
* jpeg parameters (quaility, smoothing, compression and progressive)
|
||||||
|
* are attached to the image as a parasite. This allows the
|
||||||
|
* parameters to remain consistent between saves. I was not able to
|
||||||
|
* figure out how to determine the quaility, smoothing or compression
|
||||||
|
* values of an image as it is being loaded, but the code is there to
|
||||||
|
* support it if anyone knows how. Progressive mode is a method of
|
||||||
|
* saving the image such that as a browser (or other app supporting
|
||||||
|
* progressive loads - gimp doesn't) loads the image it first gets a
|
||||||
|
* low res version displayed and then the image is progressively
|
||||||
|
* enhanced until you get the final version. It doesn't add any size
|
||||||
|
* to the image - the only draw back is some might find it annoying.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "libgimp/gimp.h"
|
#include "libgimp/gimp.h"
|
||||||
|
|
||||||
#define SCALE_WIDTH 125
|
#define SCALE_WIDTH 125
|
||||||
|
|
||||||
|
/* if you are not compiling this from inside the gimp tree, you have to */
|
||||||
|
/* take care yourself if your JPEG library supports progressive mode */
|
||||||
|
/* #undef HAVE_PROGRESSIVE_JPEG if your library don't support it */
|
||||||
|
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
|
||||||
|
|
||||||
|
#define DEFAULT_QUALITY 0.75
|
||||||
|
#define DEFAULT_SMOOTHING 0.0
|
||||||
|
#define DEFAULT_OPTIMIZE 1
|
||||||
|
#define DEFAULT_PROGRESSIVE 0
|
||||||
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
gdouble quality;
|
gdouble quality;
|
||||||
gdouble smoothing;
|
gdouble smoothing;
|
||||||
gint optimize;
|
gint optimize;
|
||||||
|
gint progressive;
|
||||||
} JpegSaveVals;
|
} JpegSaveVals;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -69,7 +106,8 @@ static void save_scale_update (GtkAdjustment *adjustment,
|
|||||||
double *scale_val);
|
double *scale_val);
|
||||||
static void save_optimize_update (GtkWidget *widget,
|
static void save_optimize_update (GtkWidget *widget,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static void save_progressive_toggle (GtkWidget *widget,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
GPlugInInfo PLUG_IN_INFO =
|
GPlugInInfo PLUG_IN_INFO =
|
||||||
{
|
{
|
||||||
@ -81,9 +119,10 @@ GPlugInInfo PLUG_IN_INFO =
|
|||||||
|
|
||||||
static JpegSaveVals jsvals =
|
static JpegSaveVals jsvals =
|
||||||
{
|
{
|
||||||
0.75, /* quality */
|
DEFAULT_QUALITY,
|
||||||
0.0, /* smoothing */
|
DEFAULT_SMOOTHING,
|
||||||
1 /* optimize */
|
DEFAULT_OPTIMIZE,
|
||||||
|
DEFAULT_PROGRESSIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
static JpegSaveInterface jsint =
|
static JpegSaveInterface jsint =
|
||||||
@ -91,6 +130,7 @@ static JpegSaveInterface jsint =
|
|||||||
FALSE /* run */
|
FALSE /* run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *image_comment=NULL;
|
||||||
|
|
||||||
MAIN ()
|
MAIN ()
|
||||||
|
|
||||||
@ -120,6 +160,8 @@ query ()
|
|||||||
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
||||||
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
||||||
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
||||||
|
{ PARAM_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG" },
|
||||||
|
{ PARAM_STRING, "comment", "Image comment" }
|
||||||
};
|
};
|
||||||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||||||
|
|
||||||
@ -162,6 +204,9 @@ run (char *name,
|
|||||||
GRunModeType run_mode;
|
GRunModeType run_mode;
|
||||||
GStatusType status = STATUS_SUCCESS;
|
GStatusType status = STATUS_SUCCESS;
|
||||||
gint32 image_ID;
|
gint32 image_ID;
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
Parasite *parasite;
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
run_mode = param[0].data.d_int32;
|
run_mode = param[0].data.d_int32;
|
||||||
|
|
||||||
@ -188,12 +233,43 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else if (strcmp (name, "file_jpeg_save") == 0)
|
else if (strcmp (name, "file_jpeg_save") == 0)
|
||||||
{
|
{
|
||||||
|
image_ID = param[1].data.d_int32;
|
||||||
|
if(image_comment) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "gimp-comment");
|
||||||
|
if (parasite) {
|
||||||
|
image_comment = g_strdup(parasite->data);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
if (!image_comment) image_comment = g_strdup(DEFAULT_COMMENT);
|
||||||
|
jsvals.quality = DEFAULT_QUALITY;
|
||||||
|
jsvals.smoothing = DEFAULT_SMOOTHING;
|
||||||
|
jsvals.optimize = DEFAULT_OPTIMIZE;
|
||||||
|
jsvals.progressive = DEFAULT_PROGRESSIVE;
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case RUN_INTERACTIVE:
|
case RUN_INTERACTIVE:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
/* load up the previously used values */
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
/* First acquire information with a dialog */
|
/* First acquire information with a dialog */
|
||||||
if (! save_dialog ())
|
if (! save_dialog ())
|
||||||
return;
|
return;
|
||||||
@ -201,13 +277,20 @@ run (char *name,
|
|||||||
|
|
||||||
case RUN_NONINTERACTIVE:
|
case RUN_NONINTERACTIVE:
|
||||||
/* Make sure all the arguments are there! */
|
/* Make sure all the arguments are there! */
|
||||||
if (nparams != 8)
|
/* pw - added two more progressive and comment */
|
||||||
|
if (nparams != 10)
|
||||||
status = STATUS_CALLING_ERROR;
|
status = STATUS_CALLING_ERROR;
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
jsvals.quality = param[5].data.d_float;
|
jsvals.quality = param[5].data.d_float;
|
||||||
jsvals.smoothing = param[6].data.d_float;
|
jsvals.smoothing = param[6].data.d_float;
|
||||||
jsvals.optimize = param[7].data.d_int32;
|
jsvals.optimize = param[7].data.d_int32;
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
jsvals.progressive = param[8].data.d_int32;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
/* free up the default -- wasted some effort earlier */
|
||||||
|
if(image_comment) g_free(image_comment);
|
||||||
|
image_comment=g_strdup(param[9].data.d_string);
|
||||||
}
|
}
|
||||||
if (status == STATUS_SUCCESS &&
|
if (status == STATUS_SUCCESS &&
|
||||||
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
||||||
@ -220,6 +303,17 @@ run (char *name,
|
|||||||
case RUN_WITH_LAST_VALS:
|
case RUN_WITH_LAST_VALS:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -227,7 +321,9 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*nreturn_vals = 1;
|
*nreturn_vals = 1;
|
||||||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
if (save_image (param[3].data.d_string,
|
||||||
|
param[1].data.d_int32,
|
||||||
|
param[2].data.d_int32))
|
||||||
{
|
{
|
||||||
/* Store mvals data */
|
/* Store mvals data */
|
||||||
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
||||||
@ -236,6 +332,32 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - now we need to change the defaults to be whatever
|
||||||
|
* was used to save this image. Dump the old parasites
|
||||||
|
* and add new ones. */
|
||||||
|
|
||||||
|
gimp_image_detach_parasite(image_ID,"gimp-comment");
|
||||||
|
if(strlen(image_comment)) {
|
||||||
|
parasite=parasite_new("gimp-comment",
|
||||||
|
PARASITE_PERSISTENT,
|
||||||
|
strlen(image_comment)+1,
|
||||||
|
image_comment);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
gimp_image_detach_parasite(image_ID, "jpeg-save-options");
|
||||||
|
|
||||||
|
parasite=parasite_new("jpeg-save-options",0,sizeof(jsvals),&jsvals);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
|
||||||
|
#endif /* Have Parasites */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +406,17 @@ load_image (char *filename)
|
|||||||
int scanlines;
|
int scanlines;
|
||||||
int i, start, end;
|
int i, start, end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
JpegSaveVals local_save_vals;
|
||||||
|
jpeg_saved_marker_ptr curr_marker;
|
||||||
|
Parasite *comment_parasite;
|
||||||
|
Parasite *vals_parasite;
|
||||||
|
GString *local_image_comments=NULL;
|
||||||
|
#endif GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
|
||||||
/* We set up the normal JPEG error routines. */
|
/* We set up the normal JPEG error routines. */
|
||||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||||
jerr.pub.error_exit = my_error_exit;
|
jerr.pub.error_exit = my_error_exit;
|
||||||
@ -320,6 +453,10 @@ load_image (char *filename)
|
|||||||
|
|
||||||
jpeg_stdio_src (&cinfo, infile);
|
jpeg_stdio_src (&cinfo, infile);
|
||||||
|
|
||||||
|
/* pw - step 2.1 let the lib know we want the comments. */
|
||||||
|
|
||||||
|
jpeg_save_markers(&cinfo,JPEG_COM,0xFFFF);
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
(void) jpeg_read_header (&cinfo, TRUE);
|
(void) jpeg_read_header (&cinfo, TRUE);
|
||||||
@ -329,6 +466,62 @@ load_image (char *filename)
|
|||||||
* See libjpeg.doc for more info.
|
* See libjpeg.doc for more info.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - Walk the marker list looking for comments (that is the only
|
||||||
|
* thing that should be in there - but be explicit.) If there are
|
||||||
|
* multiple comments, merge them into one with a /n between each. */
|
||||||
|
|
||||||
|
for(curr_marker=cinfo.marker_list;
|
||||||
|
curr_marker!=NULL;
|
||||||
|
curr_marker=curr_marker->next) {
|
||||||
|
|
||||||
|
if(curr_marker->marker==JPEG_COM) {
|
||||||
|
int len=curr_marker->data_length;
|
||||||
|
char *string=g_strndup(curr_marker->data,len);
|
||||||
|
if(len != curr_marker->original_length) {
|
||||||
|
g_warning("*** Warning *** comment truncated\n");
|
||||||
|
}
|
||||||
|
if(image_comment) {
|
||||||
|
g_string_append_c(local_image_comments,'\n');
|
||||||
|
g_string_append(local_image_comments,string);
|
||||||
|
} else {
|
||||||
|
local_image_comments=g_string_new(string);
|
||||||
|
}
|
||||||
|
g_free(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(local_image_comments) {
|
||||||
|
char *string=local_image_comments->str;
|
||||||
|
g_string_free(local_image_comments,FALSE);
|
||||||
|
comment_parasite = parasite_new("gimp-comment", PARASITE_PERSISTENT,
|
||||||
|
strlen(string)+1,string);
|
||||||
|
} else {
|
||||||
|
comment_parasite=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - figuring out what the saved values were is non-trivial.
|
||||||
|
* They don't seem to be in the cinfo structure. For now, I will
|
||||||
|
* just use the defaults, but if someone figures out how to derive
|
||||||
|
* them this is the place to store them. */
|
||||||
|
|
||||||
|
local_save_vals.quality=DEFAULT_QUALITY;
|
||||||
|
local_save_vals.smoothing=DEFAULT_SMOOTHING;
|
||||||
|
local_save_vals.optimize=DEFAULT_OPTIMIZE;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
local_save_vals.progressive=cinfo.progressive_mode;
|
||||||
|
#else
|
||||||
|
local_save_vals.progressive=0;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
|
||||||
|
vals_parasite = parasite_new("jpeg-save-options", 0,
|
||||||
|
sizeof(local_save_vals), &local_save_vals);
|
||||||
|
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
|
|
||||||
/* Step 4: set parameters for decompression */
|
/* Step 4: set parameters for decompression */
|
||||||
|
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
@ -438,6 +631,18 @@ load_image (char *filename)
|
|||||||
*/
|
*/
|
||||||
gimp_drawable_flush (drawable);
|
gimp_drawable_flush (drawable);
|
||||||
|
|
||||||
|
/* pw - Last of all, attach the parasites (couldn't do it earlier -
|
||||||
|
there was no image. */
|
||||||
|
|
||||||
|
if(comment_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, comment_parasite);
|
||||||
|
parasite_free(comment_parasite);
|
||||||
|
}
|
||||||
|
if(vals_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, vals_parasite);
|
||||||
|
parasite_free(vals_parasite);
|
||||||
|
}
|
||||||
|
|
||||||
return image_ID;
|
return image_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +770,12 @@ save_image (char *filename,
|
|||||||
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
||||||
cinfo.optimize_coding = jsvals.optimize;
|
cinfo.optimize_coding = jsvals.optimize;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
if(jsvals.progressive) {
|
||||||
|
jpeg_simple_progression(&cinfo);
|
||||||
|
}
|
||||||
|
#endif HAVE_PROGRESSIVE_JPEG
|
||||||
|
|
||||||
/* Step 4: Start compressor */
|
/* Step 4: Start compressor */
|
||||||
|
|
||||||
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
||||||
@ -572,6 +783,14 @@ save_image (char *filename,
|
|||||||
*/
|
*/
|
||||||
jpeg_start_compress (&cinfo, TRUE);
|
jpeg_start_compress (&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Step 4.1: Write the comment out - pw */
|
||||||
|
if(image_comment) {
|
||||||
|
jpeg_write_marker(&cinfo,
|
||||||
|
JPEG_COM,
|
||||||
|
image_comment,
|
||||||
|
strlen(image_comment));
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 5: while (scan lines remain to be written) */
|
/* Step 5: while (scan lines remain to be written) */
|
||||||
/* jpeg_write_scanlines(...); */
|
/* jpeg_write_scanlines(...); */
|
||||||
|
|
||||||
@ -651,6 +870,14 @@ save_dialog ()
|
|||||||
GtkWidget *table;
|
GtkWidget *table;
|
||||||
GtkWidget *toggle;
|
GtkWidget *toggle;
|
||||||
GtkObject *scale_data;
|
GtkObject *scale_data;
|
||||||
|
|
||||||
|
GtkWidget *progressive;
|
||||||
|
|
||||||
|
GtkWidget *text;
|
||||||
|
GtkWidget *com_frame;
|
||||||
|
GtkWidget *com_table;
|
||||||
|
GtkWidget *vscrollbar;
|
||||||
|
|
||||||
gchar **argv;
|
gchar **argv;
|
||||||
gint argc;
|
gint argc;
|
||||||
|
|
||||||
@ -691,7 +918,7 @@ save_dialog ()
|
|||||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||||||
table = gtk_table_new (3, 2, FALSE);
|
table = gtk_table_new (4, 2, FALSE);
|
||||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||||
|
|
||||||
@ -734,6 +961,56 @@ save_dialog ()
|
|||||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
||||||
gtk_widget_show(toggle);
|
gtk_widget_show(toggle);
|
||||||
|
|
||||||
|
|
||||||
|
progressive = gtk_check_button_new_with_label("Progressive");
|
||||||
|
gtk_table_attach(GTK_TABLE(table), progressive, 0, 2, 3, 4,
|
||||||
|
GTK_FILL, 0, 0, 0);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(progressive), "toggled",
|
||||||
|
(GtkSignalFunc)save_progressive_toggle, NULL);
|
||||||
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(progressive),
|
||||||
|
jsvals.progressive);
|
||||||
|
gtk_widget_show(progressive);
|
||||||
|
|
||||||
|
#ifndef HAVE_PROGRESSIVE_JPEG
|
||||||
|
gtk_widget_set_sensitive(progressive,FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
com_frame = gtk_frame_new ("Image Comments");
|
||||||
|
gtk_frame_set_shadow_type (GTK_FRAME (com_frame), GTK_SHADOW_ETCHED_IN);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_frame), 10);
|
||||||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), com_frame, TRUE, TRUE, 0
|
||||||
|
);
|
||||||
|
com_table = gtk_table_new (1, 1, FALSE);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_table), 10);
|
||||||
|
gtk_container_add (GTK_CONTAINER (com_frame), com_table);
|
||||||
|
|
||||||
|
text = gtk_text_new (NULL, NULL);
|
||||||
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
||||||
|
if(image_comment)
|
||||||
|
gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,image_comment,-1);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), text, 0, 1, 0, 1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
|
||||||
|
/* Add a vertical scrollbar to the GtkText widget */
|
||||||
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), vscrollbar, 1, 2, 0, 1,
|
||||||
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
gtk_widget_show (vscrollbar);
|
||||||
|
|
||||||
|
/* pw - mild hack here. I didn't like redoing the comment string
|
||||||
|
* each time a character was typed, so I associated the text area
|
||||||
|
* with the dialog. That way, just before the dialog destroys
|
||||||
|
* itself (once the ok button is hit) it can save whatever was in
|
||||||
|
* the comment text area to the comment string. See the
|
||||||
|
* save-ok-callback for more details. */
|
||||||
|
|
||||||
|
gtk_object_set_data((GtkObject *)dlg,"text",text);
|
||||||
|
|
||||||
|
gtk_widget_show (text);
|
||||||
|
gtk_widget_show (com_frame);
|
||||||
|
gtk_widget_show (com_table);
|
||||||
|
|
||||||
gtk_widget_show (frame);
|
gtk_widget_show (frame);
|
||||||
gtk_widget_show (table);
|
gtk_widget_show (table);
|
||||||
gtk_widget_show (dlg);
|
gtk_widget_show (dlg);
|
||||||
@ -758,7 +1035,21 @@ static void
|
|||||||
save_ok_callback (GtkWidget *widget,
|
save_ok_callback (GtkWidget *widget,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *text;
|
||||||
jsint.run = TRUE;
|
jsint.run = TRUE;
|
||||||
|
|
||||||
|
/* pw - get the comment text object and grab it's data */
|
||||||
|
text=gtk_object_get_data(GTK_OBJECT(data),"text");
|
||||||
|
if(image_comment!=NULL) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - gtk_editable_get_chars allocates a copy of the string, so
|
||||||
|
* don't worry about the gtk_widget_destroy killing it. */
|
||||||
|
|
||||||
|
image_comment=gtk_editable_get_chars(GTK_EDITABLE (text),0,-1);
|
||||||
|
|
||||||
gtk_widget_destroy (GTK_WIDGET (data));
|
gtk_widget_destroy (GTK_WIDGET (data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,3 +1066,12 @@ save_optimize_update(GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_progressive_toggle(GtkWidget *widget,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
jsvals.progressive = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,21 +24,58 @@
|
|||||||
* from that file. The filter, therefore, also uses libjpeg.
|
* from that file. The filter, therefore, also uses libjpeg.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 11-JAN-99 - Added support for JPEG comments and Progressive saves.
|
||||||
|
* -pete whiting <pwhiting@sprint.net>
|
||||||
|
*
|
||||||
|
* Comments of size up to 32k can be stored in the header of jpeg
|
||||||
|
* files. (They are not compressed.) The JPEG specs and libraries
|
||||||
|
* support the storing of multiple comments. The behavior of this
|
||||||
|
* code is to merge all comments in a loading image into a single
|
||||||
|
* comment (putting \n between each) and attach that string as a
|
||||||
|
* parasite - gimp-comment - to the image. When saving, the image is
|
||||||
|
* checked to see if it has the gimp-comment parasite - if so, that is
|
||||||
|
* used as the default comment in the save dialog. Further, the other
|
||||||
|
* jpeg parameters (quaility, smoothing, compression and progressive)
|
||||||
|
* are attached to the image as a parasite. This allows the
|
||||||
|
* parameters to remain consistent between saves. I was not able to
|
||||||
|
* figure out how to determine the quaility, smoothing or compression
|
||||||
|
* values of an image as it is being loaded, but the code is there to
|
||||||
|
* support it if anyone knows how. Progressive mode is a method of
|
||||||
|
* saving the image such that as a browser (or other app supporting
|
||||||
|
* progressive loads - gimp doesn't) loads the image it first gets a
|
||||||
|
* low res version displayed and then the image is progressively
|
||||||
|
* enhanced until you get the final version. It doesn't add any size
|
||||||
|
* to the image - the only draw back is some might find it annoying.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "libgimp/gimp.h"
|
#include "libgimp/gimp.h"
|
||||||
|
|
||||||
#define SCALE_WIDTH 125
|
#define SCALE_WIDTH 125
|
||||||
|
|
||||||
|
/* if you are not compiling this from inside the gimp tree, you have to */
|
||||||
|
/* take care yourself if your JPEG library supports progressive mode */
|
||||||
|
/* #undef HAVE_PROGRESSIVE_JPEG if your library don't support it */
|
||||||
|
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
|
||||||
|
|
||||||
|
#define DEFAULT_QUALITY 0.75
|
||||||
|
#define DEFAULT_SMOOTHING 0.0
|
||||||
|
#define DEFAULT_OPTIMIZE 1
|
||||||
|
#define DEFAULT_PROGRESSIVE 0
|
||||||
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
gdouble quality;
|
gdouble quality;
|
||||||
gdouble smoothing;
|
gdouble smoothing;
|
||||||
gint optimize;
|
gint optimize;
|
||||||
|
gint progressive;
|
||||||
} JpegSaveVals;
|
} JpegSaveVals;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -69,7 +106,8 @@ static void save_scale_update (GtkAdjustment *adjustment,
|
|||||||
double *scale_val);
|
double *scale_val);
|
||||||
static void save_optimize_update (GtkWidget *widget,
|
static void save_optimize_update (GtkWidget *widget,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static void save_progressive_toggle (GtkWidget *widget,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
GPlugInInfo PLUG_IN_INFO =
|
GPlugInInfo PLUG_IN_INFO =
|
||||||
{
|
{
|
||||||
@ -81,9 +119,10 @@ GPlugInInfo PLUG_IN_INFO =
|
|||||||
|
|
||||||
static JpegSaveVals jsvals =
|
static JpegSaveVals jsvals =
|
||||||
{
|
{
|
||||||
0.75, /* quality */
|
DEFAULT_QUALITY,
|
||||||
0.0, /* smoothing */
|
DEFAULT_SMOOTHING,
|
||||||
1 /* optimize */
|
DEFAULT_OPTIMIZE,
|
||||||
|
DEFAULT_PROGRESSIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
static JpegSaveInterface jsint =
|
static JpegSaveInterface jsint =
|
||||||
@ -91,6 +130,7 @@ static JpegSaveInterface jsint =
|
|||||||
FALSE /* run */
|
FALSE /* run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *image_comment=NULL;
|
||||||
|
|
||||||
MAIN ()
|
MAIN ()
|
||||||
|
|
||||||
@ -120,6 +160,8 @@ query ()
|
|||||||
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
||||||
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
||||||
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
||||||
|
{ PARAM_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG" },
|
||||||
|
{ PARAM_STRING, "comment", "Image comment" }
|
||||||
};
|
};
|
||||||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||||||
|
|
||||||
@ -162,6 +204,9 @@ run (char *name,
|
|||||||
GRunModeType run_mode;
|
GRunModeType run_mode;
|
||||||
GStatusType status = STATUS_SUCCESS;
|
GStatusType status = STATUS_SUCCESS;
|
||||||
gint32 image_ID;
|
gint32 image_ID;
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
Parasite *parasite;
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
run_mode = param[0].data.d_int32;
|
run_mode = param[0].data.d_int32;
|
||||||
|
|
||||||
@ -188,12 +233,43 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else if (strcmp (name, "file_jpeg_save") == 0)
|
else if (strcmp (name, "file_jpeg_save") == 0)
|
||||||
{
|
{
|
||||||
|
image_ID = param[1].data.d_int32;
|
||||||
|
if(image_comment) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "gimp-comment");
|
||||||
|
if (parasite) {
|
||||||
|
image_comment = g_strdup(parasite->data);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
if (!image_comment) image_comment = g_strdup(DEFAULT_COMMENT);
|
||||||
|
jsvals.quality = DEFAULT_QUALITY;
|
||||||
|
jsvals.smoothing = DEFAULT_SMOOTHING;
|
||||||
|
jsvals.optimize = DEFAULT_OPTIMIZE;
|
||||||
|
jsvals.progressive = DEFAULT_PROGRESSIVE;
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case RUN_INTERACTIVE:
|
case RUN_INTERACTIVE:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
/* load up the previously used values */
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
/* First acquire information with a dialog */
|
/* First acquire information with a dialog */
|
||||||
if (! save_dialog ())
|
if (! save_dialog ())
|
||||||
return;
|
return;
|
||||||
@ -201,13 +277,20 @@ run (char *name,
|
|||||||
|
|
||||||
case RUN_NONINTERACTIVE:
|
case RUN_NONINTERACTIVE:
|
||||||
/* Make sure all the arguments are there! */
|
/* Make sure all the arguments are there! */
|
||||||
if (nparams != 8)
|
/* pw - added two more progressive and comment */
|
||||||
|
if (nparams != 10)
|
||||||
status = STATUS_CALLING_ERROR;
|
status = STATUS_CALLING_ERROR;
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
jsvals.quality = param[5].data.d_float;
|
jsvals.quality = param[5].data.d_float;
|
||||||
jsvals.smoothing = param[6].data.d_float;
|
jsvals.smoothing = param[6].data.d_float;
|
||||||
jsvals.optimize = param[7].data.d_int32;
|
jsvals.optimize = param[7].data.d_int32;
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
jsvals.progressive = param[8].data.d_int32;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
/* free up the default -- wasted some effort earlier */
|
||||||
|
if(image_comment) g_free(image_comment);
|
||||||
|
image_comment=g_strdup(param[9].data.d_string);
|
||||||
}
|
}
|
||||||
if (status == STATUS_SUCCESS &&
|
if (status == STATUS_SUCCESS &&
|
||||||
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
||||||
@ -220,6 +303,17 @@ run (char *name,
|
|||||||
case RUN_WITH_LAST_VALS:
|
case RUN_WITH_LAST_VALS:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -227,7 +321,9 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*nreturn_vals = 1;
|
*nreturn_vals = 1;
|
||||||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
if (save_image (param[3].data.d_string,
|
||||||
|
param[1].data.d_int32,
|
||||||
|
param[2].data.d_int32))
|
||||||
{
|
{
|
||||||
/* Store mvals data */
|
/* Store mvals data */
|
||||||
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
||||||
@ -236,6 +332,32 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - now we need to change the defaults to be whatever
|
||||||
|
* was used to save this image. Dump the old parasites
|
||||||
|
* and add new ones. */
|
||||||
|
|
||||||
|
gimp_image_detach_parasite(image_ID,"gimp-comment");
|
||||||
|
if(strlen(image_comment)) {
|
||||||
|
parasite=parasite_new("gimp-comment",
|
||||||
|
PARASITE_PERSISTENT,
|
||||||
|
strlen(image_comment)+1,
|
||||||
|
image_comment);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
gimp_image_detach_parasite(image_ID, "jpeg-save-options");
|
||||||
|
|
||||||
|
parasite=parasite_new("jpeg-save-options",0,sizeof(jsvals),&jsvals);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
|
||||||
|
#endif /* Have Parasites */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +406,17 @@ load_image (char *filename)
|
|||||||
int scanlines;
|
int scanlines;
|
||||||
int i, start, end;
|
int i, start, end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
JpegSaveVals local_save_vals;
|
||||||
|
jpeg_saved_marker_ptr curr_marker;
|
||||||
|
Parasite *comment_parasite;
|
||||||
|
Parasite *vals_parasite;
|
||||||
|
GString *local_image_comments=NULL;
|
||||||
|
#endif GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
|
||||||
/* We set up the normal JPEG error routines. */
|
/* We set up the normal JPEG error routines. */
|
||||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||||
jerr.pub.error_exit = my_error_exit;
|
jerr.pub.error_exit = my_error_exit;
|
||||||
@ -320,6 +453,10 @@ load_image (char *filename)
|
|||||||
|
|
||||||
jpeg_stdio_src (&cinfo, infile);
|
jpeg_stdio_src (&cinfo, infile);
|
||||||
|
|
||||||
|
/* pw - step 2.1 let the lib know we want the comments. */
|
||||||
|
|
||||||
|
jpeg_save_markers(&cinfo,JPEG_COM,0xFFFF);
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
(void) jpeg_read_header (&cinfo, TRUE);
|
(void) jpeg_read_header (&cinfo, TRUE);
|
||||||
@ -329,6 +466,62 @@ load_image (char *filename)
|
|||||||
* See libjpeg.doc for more info.
|
* See libjpeg.doc for more info.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - Walk the marker list looking for comments (that is the only
|
||||||
|
* thing that should be in there - but be explicit.) If there are
|
||||||
|
* multiple comments, merge them into one with a /n between each. */
|
||||||
|
|
||||||
|
for(curr_marker=cinfo.marker_list;
|
||||||
|
curr_marker!=NULL;
|
||||||
|
curr_marker=curr_marker->next) {
|
||||||
|
|
||||||
|
if(curr_marker->marker==JPEG_COM) {
|
||||||
|
int len=curr_marker->data_length;
|
||||||
|
char *string=g_strndup(curr_marker->data,len);
|
||||||
|
if(len != curr_marker->original_length) {
|
||||||
|
g_warning("*** Warning *** comment truncated\n");
|
||||||
|
}
|
||||||
|
if(image_comment) {
|
||||||
|
g_string_append_c(local_image_comments,'\n');
|
||||||
|
g_string_append(local_image_comments,string);
|
||||||
|
} else {
|
||||||
|
local_image_comments=g_string_new(string);
|
||||||
|
}
|
||||||
|
g_free(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(local_image_comments) {
|
||||||
|
char *string=local_image_comments->str;
|
||||||
|
g_string_free(local_image_comments,FALSE);
|
||||||
|
comment_parasite = parasite_new("gimp-comment", PARASITE_PERSISTENT,
|
||||||
|
strlen(string)+1,string);
|
||||||
|
} else {
|
||||||
|
comment_parasite=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - figuring out what the saved values were is non-trivial.
|
||||||
|
* They don't seem to be in the cinfo structure. For now, I will
|
||||||
|
* just use the defaults, but if someone figures out how to derive
|
||||||
|
* them this is the place to store them. */
|
||||||
|
|
||||||
|
local_save_vals.quality=DEFAULT_QUALITY;
|
||||||
|
local_save_vals.smoothing=DEFAULT_SMOOTHING;
|
||||||
|
local_save_vals.optimize=DEFAULT_OPTIMIZE;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
local_save_vals.progressive=cinfo.progressive_mode;
|
||||||
|
#else
|
||||||
|
local_save_vals.progressive=0;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
|
||||||
|
vals_parasite = parasite_new("jpeg-save-options", 0,
|
||||||
|
sizeof(local_save_vals), &local_save_vals);
|
||||||
|
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
|
|
||||||
/* Step 4: set parameters for decompression */
|
/* Step 4: set parameters for decompression */
|
||||||
|
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
@ -438,6 +631,18 @@ load_image (char *filename)
|
|||||||
*/
|
*/
|
||||||
gimp_drawable_flush (drawable);
|
gimp_drawable_flush (drawable);
|
||||||
|
|
||||||
|
/* pw - Last of all, attach the parasites (couldn't do it earlier -
|
||||||
|
there was no image. */
|
||||||
|
|
||||||
|
if(comment_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, comment_parasite);
|
||||||
|
parasite_free(comment_parasite);
|
||||||
|
}
|
||||||
|
if(vals_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, vals_parasite);
|
||||||
|
parasite_free(vals_parasite);
|
||||||
|
}
|
||||||
|
|
||||||
return image_ID;
|
return image_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +770,12 @@ save_image (char *filename,
|
|||||||
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
||||||
cinfo.optimize_coding = jsvals.optimize;
|
cinfo.optimize_coding = jsvals.optimize;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
if(jsvals.progressive) {
|
||||||
|
jpeg_simple_progression(&cinfo);
|
||||||
|
}
|
||||||
|
#endif HAVE_PROGRESSIVE_JPEG
|
||||||
|
|
||||||
/* Step 4: Start compressor */
|
/* Step 4: Start compressor */
|
||||||
|
|
||||||
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
||||||
@ -572,6 +783,14 @@ save_image (char *filename,
|
|||||||
*/
|
*/
|
||||||
jpeg_start_compress (&cinfo, TRUE);
|
jpeg_start_compress (&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Step 4.1: Write the comment out - pw */
|
||||||
|
if(image_comment) {
|
||||||
|
jpeg_write_marker(&cinfo,
|
||||||
|
JPEG_COM,
|
||||||
|
image_comment,
|
||||||
|
strlen(image_comment));
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 5: while (scan lines remain to be written) */
|
/* Step 5: while (scan lines remain to be written) */
|
||||||
/* jpeg_write_scanlines(...); */
|
/* jpeg_write_scanlines(...); */
|
||||||
|
|
||||||
@ -651,6 +870,14 @@ save_dialog ()
|
|||||||
GtkWidget *table;
|
GtkWidget *table;
|
||||||
GtkWidget *toggle;
|
GtkWidget *toggle;
|
||||||
GtkObject *scale_data;
|
GtkObject *scale_data;
|
||||||
|
|
||||||
|
GtkWidget *progressive;
|
||||||
|
|
||||||
|
GtkWidget *text;
|
||||||
|
GtkWidget *com_frame;
|
||||||
|
GtkWidget *com_table;
|
||||||
|
GtkWidget *vscrollbar;
|
||||||
|
|
||||||
gchar **argv;
|
gchar **argv;
|
||||||
gint argc;
|
gint argc;
|
||||||
|
|
||||||
@ -691,7 +918,7 @@ save_dialog ()
|
|||||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||||||
table = gtk_table_new (3, 2, FALSE);
|
table = gtk_table_new (4, 2, FALSE);
|
||||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||||
|
|
||||||
@ -734,6 +961,56 @@ save_dialog ()
|
|||||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
||||||
gtk_widget_show(toggle);
|
gtk_widget_show(toggle);
|
||||||
|
|
||||||
|
|
||||||
|
progressive = gtk_check_button_new_with_label("Progressive");
|
||||||
|
gtk_table_attach(GTK_TABLE(table), progressive, 0, 2, 3, 4,
|
||||||
|
GTK_FILL, 0, 0, 0);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(progressive), "toggled",
|
||||||
|
(GtkSignalFunc)save_progressive_toggle, NULL);
|
||||||
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(progressive),
|
||||||
|
jsvals.progressive);
|
||||||
|
gtk_widget_show(progressive);
|
||||||
|
|
||||||
|
#ifndef HAVE_PROGRESSIVE_JPEG
|
||||||
|
gtk_widget_set_sensitive(progressive,FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
com_frame = gtk_frame_new ("Image Comments");
|
||||||
|
gtk_frame_set_shadow_type (GTK_FRAME (com_frame), GTK_SHADOW_ETCHED_IN);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_frame), 10);
|
||||||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), com_frame, TRUE, TRUE, 0
|
||||||
|
);
|
||||||
|
com_table = gtk_table_new (1, 1, FALSE);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_table), 10);
|
||||||
|
gtk_container_add (GTK_CONTAINER (com_frame), com_table);
|
||||||
|
|
||||||
|
text = gtk_text_new (NULL, NULL);
|
||||||
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
||||||
|
if(image_comment)
|
||||||
|
gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,image_comment,-1);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), text, 0, 1, 0, 1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
|
||||||
|
/* Add a vertical scrollbar to the GtkText widget */
|
||||||
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), vscrollbar, 1, 2, 0, 1,
|
||||||
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
gtk_widget_show (vscrollbar);
|
||||||
|
|
||||||
|
/* pw - mild hack here. I didn't like redoing the comment string
|
||||||
|
* each time a character was typed, so I associated the text area
|
||||||
|
* with the dialog. That way, just before the dialog destroys
|
||||||
|
* itself (once the ok button is hit) it can save whatever was in
|
||||||
|
* the comment text area to the comment string. See the
|
||||||
|
* save-ok-callback for more details. */
|
||||||
|
|
||||||
|
gtk_object_set_data((GtkObject *)dlg,"text",text);
|
||||||
|
|
||||||
|
gtk_widget_show (text);
|
||||||
|
gtk_widget_show (com_frame);
|
||||||
|
gtk_widget_show (com_table);
|
||||||
|
|
||||||
gtk_widget_show (frame);
|
gtk_widget_show (frame);
|
||||||
gtk_widget_show (table);
|
gtk_widget_show (table);
|
||||||
gtk_widget_show (dlg);
|
gtk_widget_show (dlg);
|
||||||
@ -758,7 +1035,21 @@ static void
|
|||||||
save_ok_callback (GtkWidget *widget,
|
save_ok_callback (GtkWidget *widget,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *text;
|
||||||
jsint.run = TRUE;
|
jsint.run = TRUE;
|
||||||
|
|
||||||
|
/* pw - get the comment text object and grab it's data */
|
||||||
|
text=gtk_object_get_data(GTK_OBJECT(data),"text");
|
||||||
|
if(image_comment!=NULL) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - gtk_editable_get_chars allocates a copy of the string, so
|
||||||
|
* don't worry about the gtk_widget_destroy killing it. */
|
||||||
|
|
||||||
|
image_comment=gtk_editable_get_chars(GTK_EDITABLE (text),0,-1);
|
||||||
|
|
||||||
gtk_widget_destroy (GTK_WIDGET (data));
|
gtk_widget_destroy (GTK_WIDGET (data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,3 +1066,12 @@ save_optimize_update(GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_progressive_toggle(GtkWidget *widget,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
jsvals.progressive = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,21 +24,58 @@
|
|||||||
* from that file. The filter, therefore, also uses libjpeg.
|
* from that file. The filter, therefore, also uses libjpeg.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 11-JAN-99 - Added support for JPEG comments and Progressive saves.
|
||||||
|
* -pete whiting <pwhiting@sprint.net>
|
||||||
|
*
|
||||||
|
* Comments of size up to 32k can be stored in the header of jpeg
|
||||||
|
* files. (They are not compressed.) The JPEG specs and libraries
|
||||||
|
* support the storing of multiple comments. The behavior of this
|
||||||
|
* code is to merge all comments in a loading image into a single
|
||||||
|
* comment (putting \n between each) and attach that string as a
|
||||||
|
* parasite - gimp-comment - to the image. When saving, the image is
|
||||||
|
* checked to see if it has the gimp-comment parasite - if so, that is
|
||||||
|
* used as the default comment in the save dialog. Further, the other
|
||||||
|
* jpeg parameters (quaility, smoothing, compression and progressive)
|
||||||
|
* are attached to the image as a parasite. This allows the
|
||||||
|
* parameters to remain consistent between saves. I was not able to
|
||||||
|
* figure out how to determine the quaility, smoothing or compression
|
||||||
|
* values of an image as it is being loaded, but the code is there to
|
||||||
|
* support it if anyone knows how. Progressive mode is a method of
|
||||||
|
* saving the image such that as a browser (or other app supporting
|
||||||
|
* progressive loads - gimp doesn't) loads the image it first gets a
|
||||||
|
* low res version displayed and then the image is progressively
|
||||||
|
* enhanced until you get the final version. It doesn't add any size
|
||||||
|
* to the image - the only draw back is some might find it annoying.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "libgimp/gimp.h"
|
#include "libgimp/gimp.h"
|
||||||
|
|
||||||
#define SCALE_WIDTH 125
|
#define SCALE_WIDTH 125
|
||||||
|
|
||||||
|
/* if you are not compiling this from inside the gimp tree, you have to */
|
||||||
|
/* take care yourself if your JPEG library supports progressive mode */
|
||||||
|
/* #undef HAVE_PROGRESSIVE_JPEG if your library don't support it */
|
||||||
|
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
|
||||||
|
|
||||||
|
#define DEFAULT_QUALITY 0.75
|
||||||
|
#define DEFAULT_SMOOTHING 0.0
|
||||||
|
#define DEFAULT_OPTIMIZE 1
|
||||||
|
#define DEFAULT_PROGRESSIVE 0
|
||||||
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
gdouble quality;
|
gdouble quality;
|
||||||
gdouble smoothing;
|
gdouble smoothing;
|
||||||
gint optimize;
|
gint optimize;
|
||||||
|
gint progressive;
|
||||||
} JpegSaveVals;
|
} JpegSaveVals;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -69,7 +106,8 @@ static void save_scale_update (GtkAdjustment *adjustment,
|
|||||||
double *scale_val);
|
double *scale_val);
|
||||||
static void save_optimize_update (GtkWidget *widget,
|
static void save_optimize_update (GtkWidget *widget,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static void save_progressive_toggle (GtkWidget *widget,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
GPlugInInfo PLUG_IN_INFO =
|
GPlugInInfo PLUG_IN_INFO =
|
||||||
{
|
{
|
||||||
@ -81,9 +119,10 @@ GPlugInInfo PLUG_IN_INFO =
|
|||||||
|
|
||||||
static JpegSaveVals jsvals =
|
static JpegSaveVals jsvals =
|
||||||
{
|
{
|
||||||
0.75, /* quality */
|
DEFAULT_QUALITY,
|
||||||
0.0, /* smoothing */
|
DEFAULT_SMOOTHING,
|
||||||
1 /* optimize */
|
DEFAULT_OPTIMIZE,
|
||||||
|
DEFAULT_PROGRESSIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
static JpegSaveInterface jsint =
|
static JpegSaveInterface jsint =
|
||||||
@ -91,6 +130,7 @@ static JpegSaveInterface jsint =
|
|||||||
FALSE /* run */
|
FALSE /* run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *image_comment=NULL;
|
||||||
|
|
||||||
MAIN ()
|
MAIN ()
|
||||||
|
|
||||||
@ -120,6 +160,8 @@ query ()
|
|||||||
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
||||||
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
||||||
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
||||||
|
{ PARAM_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG" },
|
||||||
|
{ PARAM_STRING, "comment", "Image comment" }
|
||||||
};
|
};
|
||||||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||||||
|
|
||||||
@ -162,6 +204,9 @@ run (char *name,
|
|||||||
GRunModeType run_mode;
|
GRunModeType run_mode;
|
||||||
GStatusType status = STATUS_SUCCESS;
|
GStatusType status = STATUS_SUCCESS;
|
||||||
gint32 image_ID;
|
gint32 image_ID;
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
Parasite *parasite;
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
run_mode = param[0].data.d_int32;
|
run_mode = param[0].data.d_int32;
|
||||||
|
|
||||||
@ -188,12 +233,43 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else if (strcmp (name, "file_jpeg_save") == 0)
|
else if (strcmp (name, "file_jpeg_save") == 0)
|
||||||
{
|
{
|
||||||
|
image_ID = param[1].data.d_int32;
|
||||||
|
if(image_comment) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "gimp-comment");
|
||||||
|
if (parasite) {
|
||||||
|
image_comment = g_strdup(parasite->data);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
if (!image_comment) image_comment = g_strdup(DEFAULT_COMMENT);
|
||||||
|
jsvals.quality = DEFAULT_QUALITY;
|
||||||
|
jsvals.smoothing = DEFAULT_SMOOTHING;
|
||||||
|
jsvals.optimize = DEFAULT_OPTIMIZE;
|
||||||
|
jsvals.progressive = DEFAULT_PROGRESSIVE;
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case RUN_INTERACTIVE:
|
case RUN_INTERACTIVE:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
/* load up the previously used values */
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
/* First acquire information with a dialog */
|
/* First acquire information with a dialog */
|
||||||
if (! save_dialog ())
|
if (! save_dialog ())
|
||||||
return;
|
return;
|
||||||
@ -201,13 +277,20 @@ run (char *name,
|
|||||||
|
|
||||||
case RUN_NONINTERACTIVE:
|
case RUN_NONINTERACTIVE:
|
||||||
/* Make sure all the arguments are there! */
|
/* Make sure all the arguments are there! */
|
||||||
if (nparams != 8)
|
/* pw - added two more progressive and comment */
|
||||||
|
if (nparams != 10)
|
||||||
status = STATUS_CALLING_ERROR;
|
status = STATUS_CALLING_ERROR;
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
jsvals.quality = param[5].data.d_float;
|
jsvals.quality = param[5].data.d_float;
|
||||||
jsvals.smoothing = param[6].data.d_float;
|
jsvals.smoothing = param[6].data.d_float;
|
||||||
jsvals.optimize = param[7].data.d_int32;
|
jsvals.optimize = param[7].data.d_int32;
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
jsvals.progressive = param[8].data.d_int32;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
/* free up the default -- wasted some effort earlier */
|
||||||
|
if(image_comment) g_free(image_comment);
|
||||||
|
image_comment=g_strdup(param[9].data.d_string);
|
||||||
}
|
}
|
||||||
if (status == STATUS_SUCCESS &&
|
if (status == STATUS_SUCCESS &&
|
||||||
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
||||||
@ -220,6 +303,17 @@ run (char *name,
|
|||||||
case RUN_WITH_LAST_VALS:
|
case RUN_WITH_LAST_VALS:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -227,7 +321,9 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*nreturn_vals = 1;
|
*nreturn_vals = 1;
|
||||||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
if (save_image (param[3].data.d_string,
|
||||||
|
param[1].data.d_int32,
|
||||||
|
param[2].data.d_int32))
|
||||||
{
|
{
|
||||||
/* Store mvals data */
|
/* Store mvals data */
|
||||||
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
||||||
@ -236,6 +332,32 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - now we need to change the defaults to be whatever
|
||||||
|
* was used to save this image. Dump the old parasites
|
||||||
|
* and add new ones. */
|
||||||
|
|
||||||
|
gimp_image_detach_parasite(image_ID,"gimp-comment");
|
||||||
|
if(strlen(image_comment)) {
|
||||||
|
parasite=parasite_new("gimp-comment",
|
||||||
|
PARASITE_PERSISTENT,
|
||||||
|
strlen(image_comment)+1,
|
||||||
|
image_comment);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
gimp_image_detach_parasite(image_ID, "jpeg-save-options");
|
||||||
|
|
||||||
|
parasite=parasite_new("jpeg-save-options",0,sizeof(jsvals),&jsvals);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
|
||||||
|
#endif /* Have Parasites */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +406,17 @@ load_image (char *filename)
|
|||||||
int scanlines;
|
int scanlines;
|
||||||
int i, start, end;
|
int i, start, end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
JpegSaveVals local_save_vals;
|
||||||
|
jpeg_saved_marker_ptr curr_marker;
|
||||||
|
Parasite *comment_parasite;
|
||||||
|
Parasite *vals_parasite;
|
||||||
|
GString *local_image_comments=NULL;
|
||||||
|
#endif GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
|
||||||
/* We set up the normal JPEG error routines. */
|
/* We set up the normal JPEG error routines. */
|
||||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||||
jerr.pub.error_exit = my_error_exit;
|
jerr.pub.error_exit = my_error_exit;
|
||||||
@ -320,6 +453,10 @@ load_image (char *filename)
|
|||||||
|
|
||||||
jpeg_stdio_src (&cinfo, infile);
|
jpeg_stdio_src (&cinfo, infile);
|
||||||
|
|
||||||
|
/* pw - step 2.1 let the lib know we want the comments. */
|
||||||
|
|
||||||
|
jpeg_save_markers(&cinfo,JPEG_COM,0xFFFF);
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
(void) jpeg_read_header (&cinfo, TRUE);
|
(void) jpeg_read_header (&cinfo, TRUE);
|
||||||
@ -329,6 +466,62 @@ load_image (char *filename)
|
|||||||
* See libjpeg.doc for more info.
|
* See libjpeg.doc for more info.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - Walk the marker list looking for comments (that is the only
|
||||||
|
* thing that should be in there - but be explicit.) If there are
|
||||||
|
* multiple comments, merge them into one with a /n between each. */
|
||||||
|
|
||||||
|
for(curr_marker=cinfo.marker_list;
|
||||||
|
curr_marker!=NULL;
|
||||||
|
curr_marker=curr_marker->next) {
|
||||||
|
|
||||||
|
if(curr_marker->marker==JPEG_COM) {
|
||||||
|
int len=curr_marker->data_length;
|
||||||
|
char *string=g_strndup(curr_marker->data,len);
|
||||||
|
if(len != curr_marker->original_length) {
|
||||||
|
g_warning("*** Warning *** comment truncated\n");
|
||||||
|
}
|
||||||
|
if(image_comment) {
|
||||||
|
g_string_append_c(local_image_comments,'\n');
|
||||||
|
g_string_append(local_image_comments,string);
|
||||||
|
} else {
|
||||||
|
local_image_comments=g_string_new(string);
|
||||||
|
}
|
||||||
|
g_free(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(local_image_comments) {
|
||||||
|
char *string=local_image_comments->str;
|
||||||
|
g_string_free(local_image_comments,FALSE);
|
||||||
|
comment_parasite = parasite_new("gimp-comment", PARASITE_PERSISTENT,
|
||||||
|
strlen(string)+1,string);
|
||||||
|
} else {
|
||||||
|
comment_parasite=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - figuring out what the saved values were is non-trivial.
|
||||||
|
* They don't seem to be in the cinfo structure. For now, I will
|
||||||
|
* just use the defaults, but if someone figures out how to derive
|
||||||
|
* them this is the place to store them. */
|
||||||
|
|
||||||
|
local_save_vals.quality=DEFAULT_QUALITY;
|
||||||
|
local_save_vals.smoothing=DEFAULT_SMOOTHING;
|
||||||
|
local_save_vals.optimize=DEFAULT_OPTIMIZE;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
local_save_vals.progressive=cinfo.progressive_mode;
|
||||||
|
#else
|
||||||
|
local_save_vals.progressive=0;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
|
||||||
|
vals_parasite = parasite_new("jpeg-save-options", 0,
|
||||||
|
sizeof(local_save_vals), &local_save_vals);
|
||||||
|
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
|
|
||||||
/* Step 4: set parameters for decompression */
|
/* Step 4: set parameters for decompression */
|
||||||
|
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
@ -438,6 +631,18 @@ load_image (char *filename)
|
|||||||
*/
|
*/
|
||||||
gimp_drawable_flush (drawable);
|
gimp_drawable_flush (drawable);
|
||||||
|
|
||||||
|
/* pw - Last of all, attach the parasites (couldn't do it earlier -
|
||||||
|
there was no image. */
|
||||||
|
|
||||||
|
if(comment_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, comment_parasite);
|
||||||
|
parasite_free(comment_parasite);
|
||||||
|
}
|
||||||
|
if(vals_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, vals_parasite);
|
||||||
|
parasite_free(vals_parasite);
|
||||||
|
}
|
||||||
|
|
||||||
return image_ID;
|
return image_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +770,12 @@ save_image (char *filename,
|
|||||||
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
||||||
cinfo.optimize_coding = jsvals.optimize;
|
cinfo.optimize_coding = jsvals.optimize;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
if(jsvals.progressive) {
|
||||||
|
jpeg_simple_progression(&cinfo);
|
||||||
|
}
|
||||||
|
#endif HAVE_PROGRESSIVE_JPEG
|
||||||
|
|
||||||
/* Step 4: Start compressor */
|
/* Step 4: Start compressor */
|
||||||
|
|
||||||
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
||||||
@ -572,6 +783,14 @@ save_image (char *filename,
|
|||||||
*/
|
*/
|
||||||
jpeg_start_compress (&cinfo, TRUE);
|
jpeg_start_compress (&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Step 4.1: Write the comment out - pw */
|
||||||
|
if(image_comment) {
|
||||||
|
jpeg_write_marker(&cinfo,
|
||||||
|
JPEG_COM,
|
||||||
|
image_comment,
|
||||||
|
strlen(image_comment));
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 5: while (scan lines remain to be written) */
|
/* Step 5: while (scan lines remain to be written) */
|
||||||
/* jpeg_write_scanlines(...); */
|
/* jpeg_write_scanlines(...); */
|
||||||
|
|
||||||
@ -651,6 +870,14 @@ save_dialog ()
|
|||||||
GtkWidget *table;
|
GtkWidget *table;
|
||||||
GtkWidget *toggle;
|
GtkWidget *toggle;
|
||||||
GtkObject *scale_data;
|
GtkObject *scale_data;
|
||||||
|
|
||||||
|
GtkWidget *progressive;
|
||||||
|
|
||||||
|
GtkWidget *text;
|
||||||
|
GtkWidget *com_frame;
|
||||||
|
GtkWidget *com_table;
|
||||||
|
GtkWidget *vscrollbar;
|
||||||
|
|
||||||
gchar **argv;
|
gchar **argv;
|
||||||
gint argc;
|
gint argc;
|
||||||
|
|
||||||
@ -691,7 +918,7 @@ save_dialog ()
|
|||||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||||||
table = gtk_table_new (3, 2, FALSE);
|
table = gtk_table_new (4, 2, FALSE);
|
||||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||||
|
|
||||||
@ -734,6 +961,56 @@ save_dialog ()
|
|||||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
||||||
gtk_widget_show(toggle);
|
gtk_widget_show(toggle);
|
||||||
|
|
||||||
|
|
||||||
|
progressive = gtk_check_button_new_with_label("Progressive");
|
||||||
|
gtk_table_attach(GTK_TABLE(table), progressive, 0, 2, 3, 4,
|
||||||
|
GTK_FILL, 0, 0, 0);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(progressive), "toggled",
|
||||||
|
(GtkSignalFunc)save_progressive_toggle, NULL);
|
||||||
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(progressive),
|
||||||
|
jsvals.progressive);
|
||||||
|
gtk_widget_show(progressive);
|
||||||
|
|
||||||
|
#ifndef HAVE_PROGRESSIVE_JPEG
|
||||||
|
gtk_widget_set_sensitive(progressive,FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
com_frame = gtk_frame_new ("Image Comments");
|
||||||
|
gtk_frame_set_shadow_type (GTK_FRAME (com_frame), GTK_SHADOW_ETCHED_IN);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_frame), 10);
|
||||||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), com_frame, TRUE, TRUE, 0
|
||||||
|
);
|
||||||
|
com_table = gtk_table_new (1, 1, FALSE);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_table), 10);
|
||||||
|
gtk_container_add (GTK_CONTAINER (com_frame), com_table);
|
||||||
|
|
||||||
|
text = gtk_text_new (NULL, NULL);
|
||||||
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
||||||
|
if(image_comment)
|
||||||
|
gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,image_comment,-1);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), text, 0, 1, 0, 1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
|
||||||
|
/* Add a vertical scrollbar to the GtkText widget */
|
||||||
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), vscrollbar, 1, 2, 0, 1,
|
||||||
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
gtk_widget_show (vscrollbar);
|
||||||
|
|
||||||
|
/* pw - mild hack here. I didn't like redoing the comment string
|
||||||
|
* each time a character was typed, so I associated the text area
|
||||||
|
* with the dialog. That way, just before the dialog destroys
|
||||||
|
* itself (once the ok button is hit) it can save whatever was in
|
||||||
|
* the comment text area to the comment string. See the
|
||||||
|
* save-ok-callback for more details. */
|
||||||
|
|
||||||
|
gtk_object_set_data((GtkObject *)dlg,"text",text);
|
||||||
|
|
||||||
|
gtk_widget_show (text);
|
||||||
|
gtk_widget_show (com_frame);
|
||||||
|
gtk_widget_show (com_table);
|
||||||
|
|
||||||
gtk_widget_show (frame);
|
gtk_widget_show (frame);
|
||||||
gtk_widget_show (table);
|
gtk_widget_show (table);
|
||||||
gtk_widget_show (dlg);
|
gtk_widget_show (dlg);
|
||||||
@ -758,7 +1035,21 @@ static void
|
|||||||
save_ok_callback (GtkWidget *widget,
|
save_ok_callback (GtkWidget *widget,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *text;
|
||||||
jsint.run = TRUE;
|
jsint.run = TRUE;
|
||||||
|
|
||||||
|
/* pw - get the comment text object and grab it's data */
|
||||||
|
text=gtk_object_get_data(GTK_OBJECT(data),"text");
|
||||||
|
if(image_comment!=NULL) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - gtk_editable_get_chars allocates a copy of the string, so
|
||||||
|
* don't worry about the gtk_widget_destroy killing it. */
|
||||||
|
|
||||||
|
image_comment=gtk_editable_get_chars(GTK_EDITABLE (text),0,-1);
|
||||||
|
|
||||||
gtk_widget_destroy (GTK_WIDGET (data));
|
gtk_widget_destroy (GTK_WIDGET (data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,3 +1066,12 @@ save_optimize_update(GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_progressive_toggle(GtkWidget *widget,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
jsvals.progressive = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,21 +24,58 @@
|
|||||||
* from that file. The filter, therefore, also uses libjpeg.
|
* from that file. The filter, therefore, also uses libjpeg.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 11-JAN-99 - Added support for JPEG comments and Progressive saves.
|
||||||
|
* -pete whiting <pwhiting@sprint.net>
|
||||||
|
*
|
||||||
|
* Comments of size up to 32k can be stored in the header of jpeg
|
||||||
|
* files. (They are not compressed.) The JPEG specs and libraries
|
||||||
|
* support the storing of multiple comments. The behavior of this
|
||||||
|
* code is to merge all comments in a loading image into a single
|
||||||
|
* comment (putting \n between each) and attach that string as a
|
||||||
|
* parasite - gimp-comment - to the image. When saving, the image is
|
||||||
|
* checked to see if it has the gimp-comment parasite - if so, that is
|
||||||
|
* used as the default comment in the save dialog. Further, the other
|
||||||
|
* jpeg parameters (quaility, smoothing, compression and progressive)
|
||||||
|
* are attached to the image as a parasite. This allows the
|
||||||
|
* parameters to remain consistent between saves. I was not able to
|
||||||
|
* figure out how to determine the quaility, smoothing or compression
|
||||||
|
* values of an image as it is being loaded, but the code is there to
|
||||||
|
* support it if anyone knows how. Progressive mode is a method of
|
||||||
|
* saving the image such that as a browser (or other app supporting
|
||||||
|
* progressive loads - gimp doesn't) loads the image it first gets a
|
||||||
|
* low res version displayed and then the image is progressively
|
||||||
|
* enhanced until you get the final version. It doesn't add any size
|
||||||
|
* to the image - the only draw back is some might find it annoying.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "libgimp/gimp.h"
|
#include "libgimp/gimp.h"
|
||||||
|
|
||||||
#define SCALE_WIDTH 125
|
#define SCALE_WIDTH 125
|
||||||
|
|
||||||
|
/* if you are not compiling this from inside the gimp tree, you have to */
|
||||||
|
/* take care yourself if your JPEG library supports progressive mode */
|
||||||
|
/* #undef HAVE_PROGRESSIVE_JPEG if your library don't support it */
|
||||||
|
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
|
||||||
|
|
||||||
|
#define DEFAULT_QUALITY 0.75
|
||||||
|
#define DEFAULT_SMOOTHING 0.0
|
||||||
|
#define DEFAULT_OPTIMIZE 1
|
||||||
|
#define DEFAULT_PROGRESSIVE 0
|
||||||
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
gdouble quality;
|
gdouble quality;
|
||||||
gdouble smoothing;
|
gdouble smoothing;
|
||||||
gint optimize;
|
gint optimize;
|
||||||
|
gint progressive;
|
||||||
} JpegSaveVals;
|
} JpegSaveVals;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -69,7 +106,8 @@ static void save_scale_update (GtkAdjustment *adjustment,
|
|||||||
double *scale_val);
|
double *scale_val);
|
||||||
static void save_optimize_update (GtkWidget *widget,
|
static void save_optimize_update (GtkWidget *widget,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static void save_progressive_toggle (GtkWidget *widget,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
GPlugInInfo PLUG_IN_INFO =
|
GPlugInInfo PLUG_IN_INFO =
|
||||||
{
|
{
|
||||||
@ -81,9 +119,10 @@ GPlugInInfo PLUG_IN_INFO =
|
|||||||
|
|
||||||
static JpegSaveVals jsvals =
|
static JpegSaveVals jsvals =
|
||||||
{
|
{
|
||||||
0.75, /* quality */
|
DEFAULT_QUALITY,
|
||||||
0.0, /* smoothing */
|
DEFAULT_SMOOTHING,
|
||||||
1 /* optimize */
|
DEFAULT_OPTIMIZE,
|
||||||
|
DEFAULT_PROGRESSIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
static JpegSaveInterface jsint =
|
static JpegSaveInterface jsint =
|
||||||
@ -91,6 +130,7 @@ static JpegSaveInterface jsint =
|
|||||||
FALSE /* run */
|
FALSE /* run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *image_comment=NULL;
|
||||||
|
|
||||||
MAIN ()
|
MAIN ()
|
||||||
|
|
||||||
@ -120,6 +160,8 @@ query ()
|
|||||||
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
||||||
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
||||||
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
||||||
|
{ PARAM_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG" },
|
||||||
|
{ PARAM_STRING, "comment", "Image comment" }
|
||||||
};
|
};
|
||||||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||||||
|
|
||||||
@ -162,6 +204,9 @@ run (char *name,
|
|||||||
GRunModeType run_mode;
|
GRunModeType run_mode;
|
||||||
GStatusType status = STATUS_SUCCESS;
|
GStatusType status = STATUS_SUCCESS;
|
||||||
gint32 image_ID;
|
gint32 image_ID;
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
Parasite *parasite;
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
run_mode = param[0].data.d_int32;
|
run_mode = param[0].data.d_int32;
|
||||||
|
|
||||||
@ -188,12 +233,43 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else if (strcmp (name, "file_jpeg_save") == 0)
|
else if (strcmp (name, "file_jpeg_save") == 0)
|
||||||
{
|
{
|
||||||
|
image_ID = param[1].data.d_int32;
|
||||||
|
if(image_comment) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "gimp-comment");
|
||||||
|
if (parasite) {
|
||||||
|
image_comment = g_strdup(parasite->data);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
if (!image_comment) image_comment = g_strdup(DEFAULT_COMMENT);
|
||||||
|
jsvals.quality = DEFAULT_QUALITY;
|
||||||
|
jsvals.smoothing = DEFAULT_SMOOTHING;
|
||||||
|
jsvals.optimize = DEFAULT_OPTIMIZE;
|
||||||
|
jsvals.progressive = DEFAULT_PROGRESSIVE;
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case RUN_INTERACTIVE:
|
case RUN_INTERACTIVE:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
/* load up the previously used values */
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
/* First acquire information with a dialog */
|
/* First acquire information with a dialog */
|
||||||
if (! save_dialog ())
|
if (! save_dialog ())
|
||||||
return;
|
return;
|
||||||
@ -201,13 +277,20 @@ run (char *name,
|
|||||||
|
|
||||||
case RUN_NONINTERACTIVE:
|
case RUN_NONINTERACTIVE:
|
||||||
/* Make sure all the arguments are there! */
|
/* Make sure all the arguments are there! */
|
||||||
if (nparams != 8)
|
/* pw - added two more progressive and comment */
|
||||||
|
if (nparams != 10)
|
||||||
status = STATUS_CALLING_ERROR;
|
status = STATUS_CALLING_ERROR;
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
jsvals.quality = param[5].data.d_float;
|
jsvals.quality = param[5].data.d_float;
|
||||||
jsvals.smoothing = param[6].data.d_float;
|
jsvals.smoothing = param[6].data.d_float;
|
||||||
jsvals.optimize = param[7].data.d_int32;
|
jsvals.optimize = param[7].data.d_int32;
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
jsvals.progressive = param[8].data.d_int32;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
/* free up the default -- wasted some effort earlier */
|
||||||
|
if(image_comment) g_free(image_comment);
|
||||||
|
image_comment=g_strdup(param[9].data.d_string);
|
||||||
}
|
}
|
||||||
if (status == STATUS_SUCCESS &&
|
if (status == STATUS_SUCCESS &&
|
||||||
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
||||||
@ -220,6 +303,17 @@ run (char *name,
|
|||||||
case RUN_WITH_LAST_VALS:
|
case RUN_WITH_LAST_VALS:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -227,7 +321,9 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*nreturn_vals = 1;
|
*nreturn_vals = 1;
|
||||||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
if (save_image (param[3].data.d_string,
|
||||||
|
param[1].data.d_int32,
|
||||||
|
param[2].data.d_int32))
|
||||||
{
|
{
|
||||||
/* Store mvals data */
|
/* Store mvals data */
|
||||||
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
||||||
@ -236,6 +332,32 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - now we need to change the defaults to be whatever
|
||||||
|
* was used to save this image. Dump the old parasites
|
||||||
|
* and add new ones. */
|
||||||
|
|
||||||
|
gimp_image_detach_parasite(image_ID,"gimp-comment");
|
||||||
|
if(strlen(image_comment)) {
|
||||||
|
parasite=parasite_new("gimp-comment",
|
||||||
|
PARASITE_PERSISTENT,
|
||||||
|
strlen(image_comment)+1,
|
||||||
|
image_comment);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
gimp_image_detach_parasite(image_ID, "jpeg-save-options");
|
||||||
|
|
||||||
|
parasite=parasite_new("jpeg-save-options",0,sizeof(jsvals),&jsvals);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
|
||||||
|
#endif /* Have Parasites */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +406,17 @@ load_image (char *filename)
|
|||||||
int scanlines;
|
int scanlines;
|
||||||
int i, start, end;
|
int i, start, end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
JpegSaveVals local_save_vals;
|
||||||
|
jpeg_saved_marker_ptr curr_marker;
|
||||||
|
Parasite *comment_parasite;
|
||||||
|
Parasite *vals_parasite;
|
||||||
|
GString *local_image_comments=NULL;
|
||||||
|
#endif GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
|
||||||
/* We set up the normal JPEG error routines. */
|
/* We set up the normal JPEG error routines. */
|
||||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||||
jerr.pub.error_exit = my_error_exit;
|
jerr.pub.error_exit = my_error_exit;
|
||||||
@ -320,6 +453,10 @@ load_image (char *filename)
|
|||||||
|
|
||||||
jpeg_stdio_src (&cinfo, infile);
|
jpeg_stdio_src (&cinfo, infile);
|
||||||
|
|
||||||
|
/* pw - step 2.1 let the lib know we want the comments. */
|
||||||
|
|
||||||
|
jpeg_save_markers(&cinfo,JPEG_COM,0xFFFF);
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
(void) jpeg_read_header (&cinfo, TRUE);
|
(void) jpeg_read_header (&cinfo, TRUE);
|
||||||
@ -329,6 +466,62 @@ load_image (char *filename)
|
|||||||
* See libjpeg.doc for more info.
|
* See libjpeg.doc for more info.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - Walk the marker list looking for comments (that is the only
|
||||||
|
* thing that should be in there - but be explicit.) If there are
|
||||||
|
* multiple comments, merge them into one with a /n between each. */
|
||||||
|
|
||||||
|
for(curr_marker=cinfo.marker_list;
|
||||||
|
curr_marker!=NULL;
|
||||||
|
curr_marker=curr_marker->next) {
|
||||||
|
|
||||||
|
if(curr_marker->marker==JPEG_COM) {
|
||||||
|
int len=curr_marker->data_length;
|
||||||
|
char *string=g_strndup(curr_marker->data,len);
|
||||||
|
if(len != curr_marker->original_length) {
|
||||||
|
g_warning("*** Warning *** comment truncated\n");
|
||||||
|
}
|
||||||
|
if(image_comment) {
|
||||||
|
g_string_append_c(local_image_comments,'\n');
|
||||||
|
g_string_append(local_image_comments,string);
|
||||||
|
} else {
|
||||||
|
local_image_comments=g_string_new(string);
|
||||||
|
}
|
||||||
|
g_free(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(local_image_comments) {
|
||||||
|
char *string=local_image_comments->str;
|
||||||
|
g_string_free(local_image_comments,FALSE);
|
||||||
|
comment_parasite = parasite_new("gimp-comment", PARASITE_PERSISTENT,
|
||||||
|
strlen(string)+1,string);
|
||||||
|
} else {
|
||||||
|
comment_parasite=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - figuring out what the saved values were is non-trivial.
|
||||||
|
* They don't seem to be in the cinfo structure. For now, I will
|
||||||
|
* just use the defaults, but if someone figures out how to derive
|
||||||
|
* them this is the place to store them. */
|
||||||
|
|
||||||
|
local_save_vals.quality=DEFAULT_QUALITY;
|
||||||
|
local_save_vals.smoothing=DEFAULT_SMOOTHING;
|
||||||
|
local_save_vals.optimize=DEFAULT_OPTIMIZE;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
local_save_vals.progressive=cinfo.progressive_mode;
|
||||||
|
#else
|
||||||
|
local_save_vals.progressive=0;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
|
||||||
|
vals_parasite = parasite_new("jpeg-save-options", 0,
|
||||||
|
sizeof(local_save_vals), &local_save_vals);
|
||||||
|
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
|
|
||||||
/* Step 4: set parameters for decompression */
|
/* Step 4: set parameters for decompression */
|
||||||
|
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
@ -438,6 +631,18 @@ load_image (char *filename)
|
|||||||
*/
|
*/
|
||||||
gimp_drawable_flush (drawable);
|
gimp_drawable_flush (drawable);
|
||||||
|
|
||||||
|
/* pw - Last of all, attach the parasites (couldn't do it earlier -
|
||||||
|
there was no image. */
|
||||||
|
|
||||||
|
if(comment_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, comment_parasite);
|
||||||
|
parasite_free(comment_parasite);
|
||||||
|
}
|
||||||
|
if(vals_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, vals_parasite);
|
||||||
|
parasite_free(vals_parasite);
|
||||||
|
}
|
||||||
|
|
||||||
return image_ID;
|
return image_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +770,12 @@ save_image (char *filename,
|
|||||||
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
||||||
cinfo.optimize_coding = jsvals.optimize;
|
cinfo.optimize_coding = jsvals.optimize;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
if(jsvals.progressive) {
|
||||||
|
jpeg_simple_progression(&cinfo);
|
||||||
|
}
|
||||||
|
#endif HAVE_PROGRESSIVE_JPEG
|
||||||
|
|
||||||
/* Step 4: Start compressor */
|
/* Step 4: Start compressor */
|
||||||
|
|
||||||
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
||||||
@ -572,6 +783,14 @@ save_image (char *filename,
|
|||||||
*/
|
*/
|
||||||
jpeg_start_compress (&cinfo, TRUE);
|
jpeg_start_compress (&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Step 4.1: Write the comment out - pw */
|
||||||
|
if(image_comment) {
|
||||||
|
jpeg_write_marker(&cinfo,
|
||||||
|
JPEG_COM,
|
||||||
|
image_comment,
|
||||||
|
strlen(image_comment));
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 5: while (scan lines remain to be written) */
|
/* Step 5: while (scan lines remain to be written) */
|
||||||
/* jpeg_write_scanlines(...); */
|
/* jpeg_write_scanlines(...); */
|
||||||
|
|
||||||
@ -651,6 +870,14 @@ save_dialog ()
|
|||||||
GtkWidget *table;
|
GtkWidget *table;
|
||||||
GtkWidget *toggle;
|
GtkWidget *toggle;
|
||||||
GtkObject *scale_data;
|
GtkObject *scale_data;
|
||||||
|
|
||||||
|
GtkWidget *progressive;
|
||||||
|
|
||||||
|
GtkWidget *text;
|
||||||
|
GtkWidget *com_frame;
|
||||||
|
GtkWidget *com_table;
|
||||||
|
GtkWidget *vscrollbar;
|
||||||
|
|
||||||
gchar **argv;
|
gchar **argv;
|
||||||
gint argc;
|
gint argc;
|
||||||
|
|
||||||
@ -691,7 +918,7 @@ save_dialog ()
|
|||||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||||||
table = gtk_table_new (3, 2, FALSE);
|
table = gtk_table_new (4, 2, FALSE);
|
||||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||||
|
|
||||||
@ -734,6 +961,56 @@ save_dialog ()
|
|||||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
||||||
gtk_widget_show(toggle);
|
gtk_widget_show(toggle);
|
||||||
|
|
||||||
|
|
||||||
|
progressive = gtk_check_button_new_with_label("Progressive");
|
||||||
|
gtk_table_attach(GTK_TABLE(table), progressive, 0, 2, 3, 4,
|
||||||
|
GTK_FILL, 0, 0, 0);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(progressive), "toggled",
|
||||||
|
(GtkSignalFunc)save_progressive_toggle, NULL);
|
||||||
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(progressive),
|
||||||
|
jsvals.progressive);
|
||||||
|
gtk_widget_show(progressive);
|
||||||
|
|
||||||
|
#ifndef HAVE_PROGRESSIVE_JPEG
|
||||||
|
gtk_widget_set_sensitive(progressive,FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
com_frame = gtk_frame_new ("Image Comments");
|
||||||
|
gtk_frame_set_shadow_type (GTK_FRAME (com_frame), GTK_SHADOW_ETCHED_IN);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_frame), 10);
|
||||||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), com_frame, TRUE, TRUE, 0
|
||||||
|
);
|
||||||
|
com_table = gtk_table_new (1, 1, FALSE);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_table), 10);
|
||||||
|
gtk_container_add (GTK_CONTAINER (com_frame), com_table);
|
||||||
|
|
||||||
|
text = gtk_text_new (NULL, NULL);
|
||||||
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
||||||
|
if(image_comment)
|
||||||
|
gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,image_comment,-1);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), text, 0, 1, 0, 1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
|
||||||
|
/* Add a vertical scrollbar to the GtkText widget */
|
||||||
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), vscrollbar, 1, 2, 0, 1,
|
||||||
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
gtk_widget_show (vscrollbar);
|
||||||
|
|
||||||
|
/* pw - mild hack here. I didn't like redoing the comment string
|
||||||
|
* each time a character was typed, so I associated the text area
|
||||||
|
* with the dialog. That way, just before the dialog destroys
|
||||||
|
* itself (once the ok button is hit) it can save whatever was in
|
||||||
|
* the comment text area to the comment string. See the
|
||||||
|
* save-ok-callback for more details. */
|
||||||
|
|
||||||
|
gtk_object_set_data((GtkObject *)dlg,"text",text);
|
||||||
|
|
||||||
|
gtk_widget_show (text);
|
||||||
|
gtk_widget_show (com_frame);
|
||||||
|
gtk_widget_show (com_table);
|
||||||
|
|
||||||
gtk_widget_show (frame);
|
gtk_widget_show (frame);
|
||||||
gtk_widget_show (table);
|
gtk_widget_show (table);
|
||||||
gtk_widget_show (dlg);
|
gtk_widget_show (dlg);
|
||||||
@ -758,7 +1035,21 @@ static void
|
|||||||
save_ok_callback (GtkWidget *widget,
|
save_ok_callback (GtkWidget *widget,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *text;
|
||||||
jsint.run = TRUE;
|
jsint.run = TRUE;
|
||||||
|
|
||||||
|
/* pw - get the comment text object and grab it's data */
|
||||||
|
text=gtk_object_get_data(GTK_OBJECT(data),"text");
|
||||||
|
if(image_comment!=NULL) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - gtk_editable_get_chars allocates a copy of the string, so
|
||||||
|
* don't worry about the gtk_widget_destroy killing it. */
|
||||||
|
|
||||||
|
image_comment=gtk_editable_get_chars(GTK_EDITABLE (text),0,-1);
|
||||||
|
|
||||||
gtk_widget_destroy (GTK_WIDGET (data));
|
gtk_widget_destroy (GTK_WIDGET (data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,3 +1066,12 @@ save_optimize_update(GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_progressive_toggle(GtkWidget *widget,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
jsvals.progressive = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,21 +24,58 @@
|
|||||||
* from that file. The filter, therefore, also uses libjpeg.
|
* from that file. The filter, therefore, also uses libjpeg.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 11-JAN-99 - Added support for JPEG comments and Progressive saves.
|
||||||
|
* -pete whiting <pwhiting@sprint.net>
|
||||||
|
*
|
||||||
|
* Comments of size up to 32k can be stored in the header of jpeg
|
||||||
|
* files. (They are not compressed.) The JPEG specs and libraries
|
||||||
|
* support the storing of multiple comments. The behavior of this
|
||||||
|
* code is to merge all comments in a loading image into a single
|
||||||
|
* comment (putting \n between each) and attach that string as a
|
||||||
|
* parasite - gimp-comment - to the image. When saving, the image is
|
||||||
|
* checked to see if it has the gimp-comment parasite - if so, that is
|
||||||
|
* used as the default comment in the save dialog. Further, the other
|
||||||
|
* jpeg parameters (quaility, smoothing, compression and progressive)
|
||||||
|
* are attached to the image as a parasite. This allows the
|
||||||
|
* parameters to remain consistent between saves. I was not able to
|
||||||
|
* figure out how to determine the quaility, smoothing or compression
|
||||||
|
* values of an image as it is being loaded, but the code is there to
|
||||||
|
* support it if anyone knows how. Progressive mode is a method of
|
||||||
|
* saving the image such that as a browser (or other app supporting
|
||||||
|
* progressive loads - gimp doesn't) loads the image it first gets a
|
||||||
|
* low res version displayed and then the image is progressively
|
||||||
|
* enhanced until you get the final version. It doesn't add any size
|
||||||
|
* to the image - the only draw back is some might find it annoying.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "libgimp/gimp.h"
|
#include "libgimp/gimp.h"
|
||||||
|
|
||||||
#define SCALE_WIDTH 125
|
#define SCALE_WIDTH 125
|
||||||
|
|
||||||
|
/* if you are not compiling this from inside the gimp tree, you have to */
|
||||||
|
/* take care yourself if your JPEG library supports progressive mode */
|
||||||
|
/* #undef HAVE_PROGRESSIVE_JPEG if your library don't support it */
|
||||||
|
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
|
||||||
|
|
||||||
|
#define DEFAULT_QUALITY 0.75
|
||||||
|
#define DEFAULT_SMOOTHING 0.0
|
||||||
|
#define DEFAULT_OPTIMIZE 1
|
||||||
|
#define DEFAULT_PROGRESSIVE 0
|
||||||
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
gdouble quality;
|
gdouble quality;
|
||||||
gdouble smoothing;
|
gdouble smoothing;
|
||||||
gint optimize;
|
gint optimize;
|
||||||
|
gint progressive;
|
||||||
} JpegSaveVals;
|
} JpegSaveVals;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -69,7 +106,8 @@ static void save_scale_update (GtkAdjustment *adjustment,
|
|||||||
double *scale_val);
|
double *scale_val);
|
||||||
static void save_optimize_update (GtkWidget *widget,
|
static void save_optimize_update (GtkWidget *widget,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static void save_progressive_toggle (GtkWidget *widget,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
GPlugInInfo PLUG_IN_INFO =
|
GPlugInInfo PLUG_IN_INFO =
|
||||||
{
|
{
|
||||||
@ -81,9 +119,10 @@ GPlugInInfo PLUG_IN_INFO =
|
|||||||
|
|
||||||
static JpegSaveVals jsvals =
|
static JpegSaveVals jsvals =
|
||||||
{
|
{
|
||||||
0.75, /* quality */
|
DEFAULT_QUALITY,
|
||||||
0.0, /* smoothing */
|
DEFAULT_SMOOTHING,
|
||||||
1 /* optimize */
|
DEFAULT_OPTIMIZE,
|
||||||
|
DEFAULT_PROGRESSIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
static JpegSaveInterface jsint =
|
static JpegSaveInterface jsint =
|
||||||
@ -91,6 +130,7 @@ static JpegSaveInterface jsint =
|
|||||||
FALSE /* run */
|
FALSE /* run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *image_comment=NULL;
|
||||||
|
|
||||||
MAIN ()
|
MAIN ()
|
||||||
|
|
||||||
@ -120,6 +160,8 @@ query ()
|
|||||||
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
||||||
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
||||||
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
||||||
|
{ PARAM_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG" },
|
||||||
|
{ PARAM_STRING, "comment", "Image comment" }
|
||||||
};
|
};
|
||||||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||||||
|
|
||||||
@ -162,6 +204,9 @@ run (char *name,
|
|||||||
GRunModeType run_mode;
|
GRunModeType run_mode;
|
||||||
GStatusType status = STATUS_SUCCESS;
|
GStatusType status = STATUS_SUCCESS;
|
||||||
gint32 image_ID;
|
gint32 image_ID;
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
Parasite *parasite;
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
run_mode = param[0].data.d_int32;
|
run_mode = param[0].data.d_int32;
|
||||||
|
|
||||||
@ -188,12 +233,43 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else if (strcmp (name, "file_jpeg_save") == 0)
|
else if (strcmp (name, "file_jpeg_save") == 0)
|
||||||
{
|
{
|
||||||
|
image_ID = param[1].data.d_int32;
|
||||||
|
if(image_comment) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "gimp-comment");
|
||||||
|
if (parasite) {
|
||||||
|
image_comment = g_strdup(parasite->data);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
if (!image_comment) image_comment = g_strdup(DEFAULT_COMMENT);
|
||||||
|
jsvals.quality = DEFAULT_QUALITY;
|
||||||
|
jsvals.smoothing = DEFAULT_SMOOTHING;
|
||||||
|
jsvals.optimize = DEFAULT_OPTIMIZE;
|
||||||
|
jsvals.progressive = DEFAULT_PROGRESSIVE;
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case RUN_INTERACTIVE:
|
case RUN_INTERACTIVE:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
/* load up the previously used values */
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
/* First acquire information with a dialog */
|
/* First acquire information with a dialog */
|
||||||
if (! save_dialog ())
|
if (! save_dialog ())
|
||||||
return;
|
return;
|
||||||
@ -201,13 +277,20 @@ run (char *name,
|
|||||||
|
|
||||||
case RUN_NONINTERACTIVE:
|
case RUN_NONINTERACTIVE:
|
||||||
/* Make sure all the arguments are there! */
|
/* Make sure all the arguments are there! */
|
||||||
if (nparams != 8)
|
/* pw - added two more progressive and comment */
|
||||||
|
if (nparams != 10)
|
||||||
status = STATUS_CALLING_ERROR;
|
status = STATUS_CALLING_ERROR;
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
jsvals.quality = param[5].data.d_float;
|
jsvals.quality = param[5].data.d_float;
|
||||||
jsvals.smoothing = param[6].data.d_float;
|
jsvals.smoothing = param[6].data.d_float;
|
||||||
jsvals.optimize = param[7].data.d_int32;
|
jsvals.optimize = param[7].data.d_int32;
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
jsvals.progressive = param[8].data.d_int32;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
/* free up the default -- wasted some effort earlier */
|
||||||
|
if(image_comment) g_free(image_comment);
|
||||||
|
image_comment=g_strdup(param[9].data.d_string);
|
||||||
}
|
}
|
||||||
if (status == STATUS_SUCCESS &&
|
if (status == STATUS_SUCCESS &&
|
||||||
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
||||||
@ -220,6 +303,17 @@ run (char *name,
|
|||||||
case RUN_WITH_LAST_VALS:
|
case RUN_WITH_LAST_VALS:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -227,7 +321,9 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*nreturn_vals = 1;
|
*nreturn_vals = 1;
|
||||||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
if (save_image (param[3].data.d_string,
|
||||||
|
param[1].data.d_int32,
|
||||||
|
param[2].data.d_int32))
|
||||||
{
|
{
|
||||||
/* Store mvals data */
|
/* Store mvals data */
|
||||||
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
||||||
@ -236,6 +332,32 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - now we need to change the defaults to be whatever
|
||||||
|
* was used to save this image. Dump the old parasites
|
||||||
|
* and add new ones. */
|
||||||
|
|
||||||
|
gimp_image_detach_parasite(image_ID,"gimp-comment");
|
||||||
|
if(strlen(image_comment)) {
|
||||||
|
parasite=parasite_new("gimp-comment",
|
||||||
|
PARASITE_PERSISTENT,
|
||||||
|
strlen(image_comment)+1,
|
||||||
|
image_comment);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
gimp_image_detach_parasite(image_ID, "jpeg-save-options");
|
||||||
|
|
||||||
|
parasite=parasite_new("jpeg-save-options",0,sizeof(jsvals),&jsvals);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
|
||||||
|
#endif /* Have Parasites */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +406,17 @@ load_image (char *filename)
|
|||||||
int scanlines;
|
int scanlines;
|
||||||
int i, start, end;
|
int i, start, end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
JpegSaveVals local_save_vals;
|
||||||
|
jpeg_saved_marker_ptr curr_marker;
|
||||||
|
Parasite *comment_parasite;
|
||||||
|
Parasite *vals_parasite;
|
||||||
|
GString *local_image_comments=NULL;
|
||||||
|
#endif GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
|
||||||
/* We set up the normal JPEG error routines. */
|
/* We set up the normal JPEG error routines. */
|
||||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||||
jerr.pub.error_exit = my_error_exit;
|
jerr.pub.error_exit = my_error_exit;
|
||||||
@ -320,6 +453,10 @@ load_image (char *filename)
|
|||||||
|
|
||||||
jpeg_stdio_src (&cinfo, infile);
|
jpeg_stdio_src (&cinfo, infile);
|
||||||
|
|
||||||
|
/* pw - step 2.1 let the lib know we want the comments. */
|
||||||
|
|
||||||
|
jpeg_save_markers(&cinfo,JPEG_COM,0xFFFF);
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
(void) jpeg_read_header (&cinfo, TRUE);
|
(void) jpeg_read_header (&cinfo, TRUE);
|
||||||
@ -329,6 +466,62 @@ load_image (char *filename)
|
|||||||
* See libjpeg.doc for more info.
|
* See libjpeg.doc for more info.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - Walk the marker list looking for comments (that is the only
|
||||||
|
* thing that should be in there - but be explicit.) If there are
|
||||||
|
* multiple comments, merge them into one with a /n between each. */
|
||||||
|
|
||||||
|
for(curr_marker=cinfo.marker_list;
|
||||||
|
curr_marker!=NULL;
|
||||||
|
curr_marker=curr_marker->next) {
|
||||||
|
|
||||||
|
if(curr_marker->marker==JPEG_COM) {
|
||||||
|
int len=curr_marker->data_length;
|
||||||
|
char *string=g_strndup(curr_marker->data,len);
|
||||||
|
if(len != curr_marker->original_length) {
|
||||||
|
g_warning("*** Warning *** comment truncated\n");
|
||||||
|
}
|
||||||
|
if(image_comment) {
|
||||||
|
g_string_append_c(local_image_comments,'\n');
|
||||||
|
g_string_append(local_image_comments,string);
|
||||||
|
} else {
|
||||||
|
local_image_comments=g_string_new(string);
|
||||||
|
}
|
||||||
|
g_free(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(local_image_comments) {
|
||||||
|
char *string=local_image_comments->str;
|
||||||
|
g_string_free(local_image_comments,FALSE);
|
||||||
|
comment_parasite = parasite_new("gimp-comment", PARASITE_PERSISTENT,
|
||||||
|
strlen(string)+1,string);
|
||||||
|
} else {
|
||||||
|
comment_parasite=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - figuring out what the saved values were is non-trivial.
|
||||||
|
* They don't seem to be in the cinfo structure. For now, I will
|
||||||
|
* just use the defaults, but if someone figures out how to derive
|
||||||
|
* them this is the place to store them. */
|
||||||
|
|
||||||
|
local_save_vals.quality=DEFAULT_QUALITY;
|
||||||
|
local_save_vals.smoothing=DEFAULT_SMOOTHING;
|
||||||
|
local_save_vals.optimize=DEFAULT_OPTIMIZE;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
local_save_vals.progressive=cinfo.progressive_mode;
|
||||||
|
#else
|
||||||
|
local_save_vals.progressive=0;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
|
||||||
|
vals_parasite = parasite_new("jpeg-save-options", 0,
|
||||||
|
sizeof(local_save_vals), &local_save_vals);
|
||||||
|
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
|
|
||||||
/* Step 4: set parameters for decompression */
|
/* Step 4: set parameters for decompression */
|
||||||
|
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
@ -438,6 +631,18 @@ load_image (char *filename)
|
|||||||
*/
|
*/
|
||||||
gimp_drawable_flush (drawable);
|
gimp_drawable_flush (drawable);
|
||||||
|
|
||||||
|
/* pw - Last of all, attach the parasites (couldn't do it earlier -
|
||||||
|
there was no image. */
|
||||||
|
|
||||||
|
if(comment_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, comment_parasite);
|
||||||
|
parasite_free(comment_parasite);
|
||||||
|
}
|
||||||
|
if(vals_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, vals_parasite);
|
||||||
|
parasite_free(vals_parasite);
|
||||||
|
}
|
||||||
|
|
||||||
return image_ID;
|
return image_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +770,12 @@ save_image (char *filename,
|
|||||||
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
||||||
cinfo.optimize_coding = jsvals.optimize;
|
cinfo.optimize_coding = jsvals.optimize;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
if(jsvals.progressive) {
|
||||||
|
jpeg_simple_progression(&cinfo);
|
||||||
|
}
|
||||||
|
#endif HAVE_PROGRESSIVE_JPEG
|
||||||
|
|
||||||
/* Step 4: Start compressor */
|
/* Step 4: Start compressor */
|
||||||
|
|
||||||
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
||||||
@ -572,6 +783,14 @@ save_image (char *filename,
|
|||||||
*/
|
*/
|
||||||
jpeg_start_compress (&cinfo, TRUE);
|
jpeg_start_compress (&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Step 4.1: Write the comment out - pw */
|
||||||
|
if(image_comment) {
|
||||||
|
jpeg_write_marker(&cinfo,
|
||||||
|
JPEG_COM,
|
||||||
|
image_comment,
|
||||||
|
strlen(image_comment));
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 5: while (scan lines remain to be written) */
|
/* Step 5: while (scan lines remain to be written) */
|
||||||
/* jpeg_write_scanlines(...); */
|
/* jpeg_write_scanlines(...); */
|
||||||
|
|
||||||
@ -651,6 +870,14 @@ save_dialog ()
|
|||||||
GtkWidget *table;
|
GtkWidget *table;
|
||||||
GtkWidget *toggle;
|
GtkWidget *toggle;
|
||||||
GtkObject *scale_data;
|
GtkObject *scale_data;
|
||||||
|
|
||||||
|
GtkWidget *progressive;
|
||||||
|
|
||||||
|
GtkWidget *text;
|
||||||
|
GtkWidget *com_frame;
|
||||||
|
GtkWidget *com_table;
|
||||||
|
GtkWidget *vscrollbar;
|
||||||
|
|
||||||
gchar **argv;
|
gchar **argv;
|
||||||
gint argc;
|
gint argc;
|
||||||
|
|
||||||
@ -691,7 +918,7 @@ save_dialog ()
|
|||||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||||||
table = gtk_table_new (3, 2, FALSE);
|
table = gtk_table_new (4, 2, FALSE);
|
||||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||||
|
|
||||||
@ -734,6 +961,56 @@ save_dialog ()
|
|||||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
||||||
gtk_widget_show(toggle);
|
gtk_widget_show(toggle);
|
||||||
|
|
||||||
|
|
||||||
|
progressive = gtk_check_button_new_with_label("Progressive");
|
||||||
|
gtk_table_attach(GTK_TABLE(table), progressive, 0, 2, 3, 4,
|
||||||
|
GTK_FILL, 0, 0, 0);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(progressive), "toggled",
|
||||||
|
(GtkSignalFunc)save_progressive_toggle, NULL);
|
||||||
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(progressive),
|
||||||
|
jsvals.progressive);
|
||||||
|
gtk_widget_show(progressive);
|
||||||
|
|
||||||
|
#ifndef HAVE_PROGRESSIVE_JPEG
|
||||||
|
gtk_widget_set_sensitive(progressive,FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
com_frame = gtk_frame_new ("Image Comments");
|
||||||
|
gtk_frame_set_shadow_type (GTK_FRAME (com_frame), GTK_SHADOW_ETCHED_IN);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_frame), 10);
|
||||||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), com_frame, TRUE, TRUE, 0
|
||||||
|
);
|
||||||
|
com_table = gtk_table_new (1, 1, FALSE);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_table), 10);
|
||||||
|
gtk_container_add (GTK_CONTAINER (com_frame), com_table);
|
||||||
|
|
||||||
|
text = gtk_text_new (NULL, NULL);
|
||||||
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
||||||
|
if(image_comment)
|
||||||
|
gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,image_comment,-1);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), text, 0, 1, 0, 1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
|
||||||
|
/* Add a vertical scrollbar to the GtkText widget */
|
||||||
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), vscrollbar, 1, 2, 0, 1,
|
||||||
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
gtk_widget_show (vscrollbar);
|
||||||
|
|
||||||
|
/* pw - mild hack here. I didn't like redoing the comment string
|
||||||
|
* each time a character was typed, so I associated the text area
|
||||||
|
* with the dialog. That way, just before the dialog destroys
|
||||||
|
* itself (once the ok button is hit) it can save whatever was in
|
||||||
|
* the comment text area to the comment string. See the
|
||||||
|
* save-ok-callback for more details. */
|
||||||
|
|
||||||
|
gtk_object_set_data((GtkObject *)dlg,"text",text);
|
||||||
|
|
||||||
|
gtk_widget_show (text);
|
||||||
|
gtk_widget_show (com_frame);
|
||||||
|
gtk_widget_show (com_table);
|
||||||
|
|
||||||
gtk_widget_show (frame);
|
gtk_widget_show (frame);
|
||||||
gtk_widget_show (table);
|
gtk_widget_show (table);
|
||||||
gtk_widget_show (dlg);
|
gtk_widget_show (dlg);
|
||||||
@ -758,7 +1035,21 @@ static void
|
|||||||
save_ok_callback (GtkWidget *widget,
|
save_ok_callback (GtkWidget *widget,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *text;
|
||||||
jsint.run = TRUE;
|
jsint.run = TRUE;
|
||||||
|
|
||||||
|
/* pw - get the comment text object and grab it's data */
|
||||||
|
text=gtk_object_get_data(GTK_OBJECT(data),"text");
|
||||||
|
if(image_comment!=NULL) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - gtk_editable_get_chars allocates a copy of the string, so
|
||||||
|
* don't worry about the gtk_widget_destroy killing it. */
|
||||||
|
|
||||||
|
image_comment=gtk_editable_get_chars(GTK_EDITABLE (text),0,-1);
|
||||||
|
|
||||||
gtk_widget_destroy (GTK_WIDGET (data));
|
gtk_widget_destroy (GTK_WIDGET (data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,3 +1066,12 @@ save_optimize_update(GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_progressive_toggle(GtkWidget *widget,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
jsvals.progressive = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,21 +24,58 @@
|
|||||||
* from that file. The filter, therefore, also uses libjpeg.
|
* from that file. The filter, therefore, also uses libjpeg.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 11-JAN-99 - Added support for JPEG comments and Progressive saves.
|
||||||
|
* -pete whiting <pwhiting@sprint.net>
|
||||||
|
*
|
||||||
|
* Comments of size up to 32k can be stored in the header of jpeg
|
||||||
|
* files. (They are not compressed.) The JPEG specs and libraries
|
||||||
|
* support the storing of multiple comments. The behavior of this
|
||||||
|
* code is to merge all comments in a loading image into a single
|
||||||
|
* comment (putting \n between each) and attach that string as a
|
||||||
|
* parasite - gimp-comment - to the image. When saving, the image is
|
||||||
|
* checked to see if it has the gimp-comment parasite - if so, that is
|
||||||
|
* used as the default comment in the save dialog. Further, the other
|
||||||
|
* jpeg parameters (quaility, smoothing, compression and progressive)
|
||||||
|
* are attached to the image as a parasite. This allows the
|
||||||
|
* parameters to remain consistent between saves. I was not able to
|
||||||
|
* figure out how to determine the quaility, smoothing or compression
|
||||||
|
* values of an image as it is being loaded, but the code is there to
|
||||||
|
* support it if anyone knows how. Progressive mode is a method of
|
||||||
|
* saving the image such that as a browser (or other app supporting
|
||||||
|
* progressive loads - gimp doesn't) loads the image it first gets a
|
||||||
|
* low res version displayed and then the image is progressively
|
||||||
|
* enhanced until you get the final version. It doesn't add any size
|
||||||
|
* to the image - the only draw back is some might find it annoying.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "libgimp/gimp.h"
|
#include "libgimp/gimp.h"
|
||||||
|
|
||||||
#define SCALE_WIDTH 125
|
#define SCALE_WIDTH 125
|
||||||
|
|
||||||
|
/* if you are not compiling this from inside the gimp tree, you have to */
|
||||||
|
/* take care yourself if your JPEG library supports progressive mode */
|
||||||
|
/* #undef HAVE_PROGRESSIVE_JPEG if your library don't support it */
|
||||||
|
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
|
||||||
|
|
||||||
|
#define DEFAULT_QUALITY 0.75
|
||||||
|
#define DEFAULT_SMOOTHING 0.0
|
||||||
|
#define DEFAULT_OPTIMIZE 1
|
||||||
|
#define DEFAULT_PROGRESSIVE 0
|
||||||
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
gdouble quality;
|
gdouble quality;
|
||||||
gdouble smoothing;
|
gdouble smoothing;
|
||||||
gint optimize;
|
gint optimize;
|
||||||
|
gint progressive;
|
||||||
} JpegSaveVals;
|
} JpegSaveVals;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -69,7 +106,8 @@ static void save_scale_update (GtkAdjustment *adjustment,
|
|||||||
double *scale_val);
|
double *scale_val);
|
||||||
static void save_optimize_update (GtkWidget *widget,
|
static void save_optimize_update (GtkWidget *widget,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static void save_progressive_toggle (GtkWidget *widget,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
GPlugInInfo PLUG_IN_INFO =
|
GPlugInInfo PLUG_IN_INFO =
|
||||||
{
|
{
|
||||||
@ -81,9 +119,10 @@ GPlugInInfo PLUG_IN_INFO =
|
|||||||
|
|
||||||
static JpegSaveVals jsvals =
|
static JpegSaveVals jsvals =
|
||||||
{
|
{
|
||||||
0.75, /* quality */
|
DEFAULT_QUALITY,
|
||||||
0.0, /* smoothing */
|
DEFAULT_SMOOTHING,
|
||||||
1 /* optimize */
|
DEFAULT_OPTIMIZE,
|
||||||
|
DEFAULT_PROGRESSIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
static JpegSaveInterface jsint =
|
static JpegSaveInterface jsint =
|
||||||
@ -91,6 +130,7 @@ static JpegSaveInterface jsint =
|
|||||||
FALSE /* run */
|
FALSE /* run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *image_comment=NULL;
|
||||||
|
|
||||||
MAIN ()
|
MAIN ()
|
||||||
|
|
||||||
@ -120,6 +160,8 @@ query ()
|
|||||||
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
||||||
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
||||||
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
||||||
|
{ PARAM_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG" },
|
||||||
|
{ PARAM_STRING, "comment", "Image comment" }
|
||||||
};
|
};
|
||||||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||||||
|
|
||||||
@ -162,6 +204,9 @@ run (char *name,
|
|||||||
GRunModeType run_mode;
|
GRunModeType run_mode;
|
||||||
GStatusType status = STATUS_SUCCESS;
|
GStatusType status = STATUS_SUCCESS;
|
||||||
gint32 image_ID;
|
gint32 image_ID;
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
Parasite *parasite;
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
run_mode = param[0].data.d_int32;
|
run_mode = param[0].data.d_int32;
|
||||||
|
|
||||||
@ -188,12 +233,43 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else if (strcmp (name, "file_jpeg_save") == 0)
|
else if (strcmp (name, "file_jpeg_save") == 0)
|
||||||
{
|
{
|
||||||
|
image_ID = param[1].data.d_int32;
|
||||||
|
if(image_comment) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "gimp-comment");
|
||||||
|
if (parasite) {
|
||||||
|
image_comment = g_strdup(parasite->data);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
if (!image_comment) image_comment = g_strdup(DEFAULT_COMMENT);
|
||||||
|
jsvals.quality = DEFAULT_QUALITY;
|
||||||
|
jsvals.smoothing = DEFAULT_SMOOTHING;
|
||||||
|
jsvals.optimize = DEFAULT_OPTIMIZE;
|
||||||
|
jsvals.progressive = DEFAULT_PROGRESSIVE;
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case RUN_INTERACTIVE:
|
case RUN_INTERACTIVE:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
/* load up the previously used values */
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
/* First acquire information with a dialog */
|
/* First acquire information with a dialog */
|
||||||
if (! save_dialog ())
|
if (! save_dialog ())
|
||||||
return;
|
return;
|
||||||
@ -201,13 +277,20 @@ run (char *name,
|
|||||||
|
|
||||||
case RUN_NONINTERACTIVE:
|
case RUN_NONINTERACTIVE:
|
||||||
/* Make sure all the arguments are there! */
|
/* Make sure all the arguments are there! */
|
||||||
if (nparams != 8)
|
/* pw - added two more progressive and comment */
|
||||||
|
if (nparams != 10)
|
||||||
status = STATUS_CALLING_ERROR;
|
status = STATUS_CALLING_ERROR;
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
jsvals.quality = param[5].data.d_float;
|
jsvals.quality = param[5].data.d_float;
|
||||||
jsvals.smoothing = param[6].data.d_float;
|
jsvals.smoothing = param[6].data.d_float;
|
||||||
jsvals.optimize = param[7].data.d_int32;
|
jsvals.optimize = param[7].data.d_int32;
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
jsvals.progressive = param[8].data.d_int32;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
/* free up the default -- wasted some effort earlier */
|
||||||
|
if(image_comment) g_free(image_comment);
|
||||||
|
image_comment=g_strdup(param[9].data.d_string);
|
||||||
}
|
}
|
||||||
if (status == STATUS_SUCCESS &&
|
if (status == STATUS_SUCCESS &&
|
||||||
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
||||||
@ -220,6 +303,17 @@ run (char *name,
|
|||||||
case RUN_WITH_LAST_VALS:
|
case RUN_WITH_LAST_VALS:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -227,7 +321,9 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*nreturn_vals = 1;
|
*nreturn_vals = 1;
|
||||||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
if (save_image (param[3].data.d_string,
|
||||||
|
param[1].data.d_int32,
|
||||||
|
param[2].data.d_int32))
|
||||||
{
|
{
|
||||||
/* Store mvals data */
|
/* Store mvals data */
|
||||||
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
||||||
@ -236,6 +332,32 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - now we need to change the defaults to be whatever
|
||||||
|
* was used to save this image. Dump the old parasites
|
||||||
|
* and add new ones. */
|
||||||
|
|
||||||
|
gimp_image_detach_parasite(image_ID,"gimp-comment");
|
||||||
|
if(strlen(image_comment)) {
|
||||||
|
parasite=parasite_new("gimp-comment",
|
||||||
|
PARASITE_PERSISTENT,
|
||||||
|
strlen(image_comment)+1,
|
||||||
|
image_comment);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
gimp_image_detach_parasite(image_ID, "jpeg-save-options");
|
||||||
|
|
||||||
|
parasite=parasite_new("jpeg-save-options",0,sizeof(jsvals),&jsvals);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
|
||||||
|
#endif /* Have Parasites */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +406,17 @@ load_image (char *filename)
|
|||||||
int scanlines;
|
int scanlines;
|
||||||
int i, start, end;
|
int i, start, end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
JpegSaveVals local_save_vals;
|
||||||
|
jpeg_saved_marker_ptr curr_marker;
|
||||||
|
Parasite *comment_parasite;
|
||||||
|
Parasite *vals_parasite;
|
||||||
|
GString *local_image_comments=NULL;
|
||||||
|
#endif GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
|
||||||
/* We set up the normal JPEG error routines. */
|
/* We set up the normal JPEG error routines. */
|
||||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||||
jerr.pub.error_exit = my_error_exit;
|
jerr.pub.error_exit = my_error_exit;
|
||||||
@ -320,6 +453,10 @@ load_image (char *filename)
|
|||||||
|
|
||||||
jpeg_stdio_src (&cinfo, infile);
|
jpeg_stdio_src (&cinfo, infile);
|
||||||
|
|
||||||
|
/* pw - step 2.1 let the lib know we want the comments. */
|
||||||
|
|
||||||
|
jpeg_save_markers(&cinfo,JPEG_COM,0xFFFF);
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
(void) jpeg_read_header (&cinfo, TRUE);
|
(void) jpeg_read_header (&cinfo, TRUE);
|
||||||
@ -329,6 +466,62 @@ load_image (char *filename)
|
|||||||
* See libjpeg.doc for more info.
|
* See libjpeg.doc for more info.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - Walk the marker list looking for comments (that is the only
|
||||||
|
* thing that should be in there - but be explicit.) If there are
|
||||||
|
* multiple comments, merge them into one with a /n between each. */
|
||||||
|
|
||||||
|
for(curr_marker=cinfo.marker_list;
|
||||||
|
curr_marker!=NULL;
|
||||||
|
curr_marker=curr_marker->next) {
|
||||||
|
|
||||||
|
if(curr_marker->marker==JPEG_COM) {
|
||||||
|
int len=curr_marker->data_length;
|
||||||
|
char *string=g_strndup(curr_marker->data,len);
|
||||||
|
if(len != curr_marker->original_length) {
|
||||||
|
g_warning("*** Warning *** comment truncated\n");
|
||||||
|
}
|
||||||
|
if(image_comment) {
|
||||||
|
g_string_append_c(local_image_comments,'\n');
|
||||||
|
g_string_append(local_image_comments,string);
|
||||||
|
} else {
|
||||||
|
local_image_comments=g_string_new(string);
|
||||||
|
}
|
||||||
|
g_free(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(local_image_comments) {
|
||||||
|
char *string=local_image_comments->str;
|
||||||
|
g_string_free(local_image_comments,FALSE);
|
||||||
|
comment_parasite = parasite_new("gimp-comment", PARASITE_PERSISTENT,
|
||||||
|
strlen(string)+1,string);
|
||||||
|
} else {
|
||||||
|
comment_parasite=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - figuring out what the saved values were is non-trivial.
|
||||||
|
* They don't seem to be in the cinfo structure. For now, I will
|
||||||
|
* just use the defaults, but if someone figures out how to derive
|
||||||
|
* them this is the place to store them. */
|
||||||
|
|
||||||
|
local_save_vals.quality=DEFAULT_QUALITY;
|
||||||
|
local_save_vals.smoothing=DEFAULT_SMOOTHING;
|
||||||
|
local_save_vals.optimize=DEFAULT_OPTIMIZE;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
local_save_vals.progressive=cinfo.progressive_mode;
|
||||||
|
#else
|
||||||
|
local_save_vals.progressive=0;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
|
||||||
|
vals_parasite = parasite_new("jpeg-save-options", 0,
|
||||||
|
sizeof(local_save_vals), &local_save_vals);
|
||||||
|
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
|
|
||||||
/* Step 4: set parameters for decompression */
|
/* Step 4: set parameters for decompression */
|
||||||
|
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
@ -438,6 +631,18 @@ load_image (char *filename)
|
|||||||
*/
|
*/
|
||||||
gimp_drawable_flush (drawable);
|
gimp_drawable_flush (drawable);
|
||||||
|
|
||||||
|
/* pw - Last of all, attach the parasites (couldn't do it earlier -
|
||||||
|
there was no image. */
|
||||||
|
|
||||||
|
if(comment_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, comment_parasite);
|
||||||
|
parasite_free(comment_parasite);
|
||||||
|
}
|
||||||
|
if(vals_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, vals_parasite);
|
||||||
|
parasite_free(vals_parasite);
|
||||||
|
}
|
||||||
|
|
||||||
return image_ID;
|
return image_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +770,12 @@ save_image (char *filename,
|
|||||||
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
||||||
cinfo.optimize_coding = jsvals.optimize;
|
cinfo.optimize_coding = jsvals.optimize;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
if(jsvals.progressive) {
|
||||||
|
jpeg_simple_progression(&cinfo);
|
||||||
|
}
|
||||||
|
#endif HAVE_PROGRESSIVE_JPEG
|
||||||
|
|
||||||
/* Step 4: Start compressor */
|
/* Step 4: Start compressor */
|
||||||
|
|
||||||
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
||||||
@ -572,6 +783,14 @@ save_image (char *filename,
|
|||||||
*/
|
*/
|
||||||
jpeg_start_compress (&cinfo, TRUE);
|
jpeg_start_compress (&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Step 4.1: Write the comment out - pw */
|
||||||
|
if(image_comment) {
|
||||||
|
jpeg_write_marker(&cinfo,
|
||||||
|
JPEG_COM,
|
||||||
|
image_comment,
|
||||||
|
strlen(image_comment));
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 5: while (scan lines remain to be written) */
|
/* Step 5: while (scan lines remain to be written) */
|
||||||
/* jpeg_write_scanlines(...); */
|
/* jpeg_write_scanlines(...); */
|
||||||
|
|
||||||
@ -651,6 +870,14 @@ save_dialog ()
|
|||||||
GtkWidget *table;
|
GtkWidget *table;
|
||||||
GtkWidget *toggle;
|
GtkWidget *toggle;
|
||||||
GtkObject *scale_data;
|
GtkObject *scale_data;
|
||||||
|
|
||||||
|
GtkWidget *progressive;
|
||||||
|
|
||||||
|
GtkWidget *text;
|
||||||
|
GtkWidget *com_frame;
|
||||||
|
GtkWidget *com_table;
|
||||||
|
GtkWidget *vscrollbar;
|
||||||
|
|
||||||
gchar **argv;
|
gchar **argv;
|
||||||
gint argc;
|
gint argc;
|
||||||
|
|
||||||
@ -691,7 +918,7 @@ save_dialog ()
|
|||||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||||||
table = gtk_table_new (3, 2, FALSE);
|
table = gtk_table_new (4, 2, FALSE);
|
||||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||||
|
|
||||||
@ -734,6 +961,56 @@ save_dialog ()
|
|||||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
||||||
gtk_widget_show(toggle);
|
gtk_widget_show(toggle);
|
||||||
|
|
||||||
|
|
||||||
|
progressive = gtk_check_button_new_with_label("Progressive");
|
||||||
|
gtk_table_attach(GTK_TABLE(table), progressive, 0, 2, 3, 4,
|
||||||
|
GTK_FILL, 0, 0, 0);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(progressive), "toggled",
|
||||||
|
(GtkSignalFunc)save_progressive_toggle, NULL);
|
||||||
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(progressive),
|
||||||
|
jsvals.progressive);
|
||||||
|
gtk_widget_show(progressive);
|
||||||
|
|
||||||
|
#ifndef HAVE_PROGRESSIVE_JPEG
|
||||||
|
gtk_widget_set_sensitive(progressive,FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
com_frame = gtk_frame_new ("Image Comments");
|
||||||
|
gtk_frame_set_shadow_type (GTK_FRAME (com_frame), GTK_SHADOW_ETCHED_IN);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_frame), 10);
|
||||||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), com_frame, TRUE, TRUE, 0
|
||||||
|
);
|
||||||
|
com_table = gtk_table_new (1, 1, FALSE);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_table), 10);
|
||||||
|
gtk_container_add (GTK_CONTAINER (com_frame), com_table);
|
||||||
|
|
||||||
|
text = gtk_text_new (NULL, NULL);
|
||||||
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
||||||
|
if(image_comment)
|
||||||
|
gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,image_comment,-1);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), text, 0, 1, 0, 1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
|
||||||
|
/* Add a vertical scrollbar to the GtkText widget */
|
||||||
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), vscrollbar, 1, 2, 0, 1,
|
||||||
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
gtk_widget_show (vscrollbar);
|
||||||
|
|
||||||
|
/* pw - mild hack here. I didn't like redoing the comment string
|
||||||
|
* each time a character was typed, so I associated the text area
|
||||||
|
* with the dialog. That way, just before the dialog destroys
|
||||||
|
* itself (once the ok button is hit) it can save whatever was in
|
||||||
|
* the comment text area to the comment string. See the
|
||||||
|
* save-ok-callback for more details. */
|
||||||
|
|
||||||
|
gtk_object_set_data((GtkObject *)dlg,"text",text);
|
||||||
|
|
||||||
|
gtk_widget_show (text);
|
||||||
|
gtk_widget_show (com_frame);
|
||||||
|
gtk_widget_show (com_table);
|
||||||
|
|
||||||
gtk_widget_show (frame);
|
gtk_widget_show (frame);
|
||||||
gtk_widget_show (table);
|
gtk_widget_show (table);
|
||||||
gtk_widget_show (dlg);
|
gtk_widget_show (dlg);
|
||||||
@ -758,7 +1035,21 @@ static void
|
|||||||
save_ok_callback (GtkWidget *widget,
|
save_ok_callback (GtkWidget *widget,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *text;
|
||||||
jsint.run = TRUE;
|
jsint.run = TRUE;
|
||||||
|
|
||||||
|
/* pw - get the comment text object and grab it's data */
|
||||||
|
text=gtk_object_get_data(GTK_OBJECT(data),"text");
|
||||||
|
if(image_comment!=NULL) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - gtk_editable_get_chars allocates a copy of the string, so
|
||||||
|
* don't worry about the gtk_widget_destroy killing it. */
|
||||||
|
|
||||||
|
image_comment=gtk_editable_get_chars(GTK_EDITABLE (text),0,-1);
|
||||||
|
|
||||||
gtk_widget_destroy (GTK_WIDGET (data));
|
gtk_widget_destroy (GTK_WIDGET (data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,3 +1066,12 @@ save_optimize_update(GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_progressive_toggle(GtkWidget *widget,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
jsvals.progressive = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,21 +24,58 @@
|
|||||||
* from that file. The filter, therefore, also uses libjpeg.
|
* from that file. The filter, therefore, also uses libjpeg.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 11-JAN-99 - Added support for JPEG comments and Progressive saves.
|
||||||
|
* -pete whiting <pwhiting@sprint.net>
|
||||||
|
*
|
||||||
|
* Comments of size up to 32k can be stored in the header of jpeg
|
||||||
|
* files. (They are not compressed.) The JPEG specs and libraries
|
||||||
|
* support the storing of multiple comments. The behavior of this
|
||||||
|
* code is to merge all comments in a loading image into a single
|
||||||
|
* comment (putting \n between each) and attach that string as a
|
||||||
|
* parasite - gimp-comment - to the image. When saving, the image is
|
||||||
|
* checked to see if it has the gimp-comment parasite - if so, that is
|
||||||
|
* used as the default comment in the save dialog. Further, the other
|
||||||
|
* jpeg parameters (quaility, smoothing, compression and progressive)
|
||||||
|
* are attached to the image as a parasite. This allows the
|
||||||
|
* parameters to remain consistent between saves. I was not able to
|
||||||
|
* figure out how to determine the quaility, smoothing or compression
|
||||||
|
* values of an image as it is being loaded, but the code is there to
|
||||||
|
* support it if anyone knows how. Progressive mode is a method of
|
||||||
|
* saving the image such that as a browser (or other app supporting
|
||||||
|
* progressive loads - gimp doesn't) loads the image it first gets a
|
||||||
|
* low res version displayed and then the image is progressively
|
||||||
|
* enhanced until you get the final version. It doesn't add any size
|
||||||
|
* to the image - the only draw back is some might find it annoying.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
|
#include "config.h" /* configure cares about HAVE_PROGRESSIVE_JPEG */
|
||||||
#include "gtk/gtk.h"
|
#include "gtk/gtk.h"
|
||||||
#include "libgimp/gimp.h"
|
#include "libgimp/gimp.h"
|
||||||
|
|
||||||
#define SCALE_WIDTH 125
|
#define SCALE_WIDTH 125
|
||||||
|
|
||||||
|
/* if you are not compiling this from inside the gimp tree, you have to */
|
||||||
|
/* take care yourself if your JPEG library supports progressive mode */
|
||||||
|
/* #undef HAVE_PROGRESSIVE_JPEG if your library don't support it */
|
||||||
|
/* #define HAVE_PROGRESSIVE_JPEG if your library knows how to handle it */
|
||||||
|
|
||||||
|
#define DEFAULT_QUALITY 0.75
|
||||||
|
#define DEFAULT_SMOOTHING 0.0
|
||||||
|
#define DEFAULT_OPTIMIZE 1
|
||||||
|
#define DEFAULT_PROGRESSIVE 0
|
||||||
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
gdouble quality;
|
gdouble quality;
|
||||||
gdouble smoothing;
|
gdouble smoothing;
|
||||||
gint optimize;
|
gint optimize;
|
||||||
|
gint progressive;
|
||||||
} JpegSaveVals;
|
} JpegSaveVals;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@ -69,7 +106,8 @@ static void save_scale_update (GtkAdjustment *adjustment,
|
|||||||
double *scale_val);
|
double *scale_val);
|
||||||
static void save_optimize_update (GtkWidget *widget,
|
static void save_optimize_update (GtkWidget *widget,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static void save_progressive_toggle (GtkWidget *widget,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
GPlugInInfo PLUG_IN_INFO =
|
GPlugInInfo PLUG_IN_INFO =
|
||||||
{
|
{
|
||||||
@ -81,9 +119,10 @@ GPlugInInfo PLUG_IN_INFO =
|
|||||||
|
|
||||||
static JpegSaveVals jsvals =
|
static JpegSaveVals jsvals =
|
||||||
{
|
{
|
||||||
0.75, /* quality */
|
DEFAULT_QUALITY,
|
||||||
0.0, /* smoothing */
|
DEFAULT_SMOOTHING,
|
||||||
1 /* optimize */
|
DEFAULT_OPTIMIZE,
|
||||||
|
DEFAULT_PROGRESSIVE
|
||||||
};
|
};
|
||||||
|
|
||||||
static JpegSaveInterface jsint =
|
static JpegSaveInterface jsint =
|
||||||
@ -91,6 +130,7 @@ static JpegSaveInterface jsint =
|
|||||||
FALSE /* run */
|
FALSE /* run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char *image_comment=NULL;
|
||||||
|
|
||||||
MAIN ()
|
MAIN ()
|
||||||
|
|
||||||
@ -120,6 +160,8 @@ query ()
|
|||||||
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
{ PARAM_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
|
||||||
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
{ PARAM_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
|
||||||
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
{ PARAM_INT32, "optimize", "Optimization of entropy encoding parameters" },
|
||||||
|
{ PARAM_INT32, "progressive", "Enable progressive jpeg image loading - ignored if not compiled with HAVE_PROGRESSIVE_JPEG" },
|
||||||
|
{ PARAM_STRING, "comment", "Image comment" }
|
||||||
};
|
};
|
||||||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||||||
|
|
||||||
@ -162,6 +204,9 @@ run (char *name,
|
|||||||
GRunModeType run_mode;
|
GRunModeType run_mode;
|
||||||
GStatusType status = STATUS_SUCCESS;
|
GStatusType status = STATUS_SUCCESS;
|
||||||
gint32 image_ID;
|
gint32 image_ID;
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
Parasite *parasite;
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
run_mode = param[0].data.d_int32;
|
run_mode = param[0].data.d_int32;
|
||||||
|
|
||||||
@ -188,12 +233,43 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else if (strcmp (name, "file_jpeg_save") == 0)
|
else if (strcmp (name, "file_jpeg_save") == 0)
|
||||||
{
|
{
|
||||||
|
image_ID = param[1].data.d_int32;
|
||||||
|
if(image_comment) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "gimp-comment");
|
||||||
|
if (parasite) {
|
||||||
|
image_comment = g_strdup(parasite->data);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
if (!image_comment) image_comment = g_strdup(DEFAULT_COMMENT);
|
||||||
|
jsvals.quality = DEFAULT_QUALITY;
|
||||||
|
jsvals.smoothing = DEFAULT_SMOOTHING;
|
||||||
|
jsvals.optimize = DEFAULT_OPTIMIZE;
|
||||||
|
jsvals.progressive = DEFAULT_PROGRESSIVE;
|
||||||
|
|
||||||
switch (run_mode)
|
switch (run_mode)
|
||||||
{
|
{
|
||||||
case RUN_INTERACTIVE:
|
case RUN_INTERACTIVE:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
/* load up the previously used values */
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
/* First acquire information with a dialog */
|
/* First acquire information with a dialog */
|
||||||
if (! save_dialog ())
|
if (! save_dialog ())
|
||||||
return;
|
return;
|
||||||
@ -201,13 +277,20 @@ run (char *name,
|
|||||||
|
|
||||||
case RUN_NONINTERACTIVE:
|
case RUN_NONINTERACTIVE:
|
||||||
/* Make sure all the arguments are there! */
|
/* Make sure all the arguments are there! */
|
||||||
if (nparams != 8)
|
/* pw - added two more progressive and comment */
|
||||||
|
if (nparams != 10)
|
||||||
status = STATUS_CALLING_ERROR;
|
status = STATUS_CALLING_ERROR;
|
||||||
if (status == STATUS_SUCCESS)
|
if (status == STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
jsvals.quality = param[5].data.d_float;
|
jsvals.quality = param[5].data.d_float;
|
||||||
jsvals.smoothing = param[6].data.d_float;
|
jsvals.smoothing = param[6].data.d_float;
|
||||||
jsvals.optimize = param[7].data.d_int32;
|
jsvals.optimize = param[7].data.d_int32;
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
jsvals.progressive = param[8].data.d_int32;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
/* free up the default -- wasted some effort earlier */
|
||||||
|
if(image_comment) g_free(image_comment);
|
||||||
|
image_comment=g_strdup(param[9].data.d_string);
|
||||||
}
|
}
|
||||||
if (status == STATUS_SUCCESS &&
|
if (status == STATUS_SUCCESS &&
|
||||||
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
(jsvals.quality < 0.0 || jsvals.quality > 1.0))
|
||||||
@ -220,6 +303,17 @@ run (char *name,
|
|||||||
case RUN_WITH_LAST_VALS:
|
case RUN_WITH_LAST_VALS:
|
||||||
/* Possibly retrieve data */
|
/* Possibly retrieve data */
|
||||||
gimp_get_data ("file_jpeg_save", &jsvals);
|
gimp_get_data ("file_jpeg_save", &jsvals);
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
parasite = gimp_image_find_parasite(image_ID, "jpeg-save-options");
|
||||||
|
if (parasite)
|
||||||
|
{
|
||||||
|
jsvals.quality = ((JpegSaveVals *)parasite->data)->quality;
|
||||||
|
jsvals.smoothing = ((JpegSaveVals *)parasite->data)->smoothing;
|
||||||
|
jsvals.optimize = ((JpegSaveVals *)parasite->data)->optimize;
|
||||||
|
jsvals.progressive = ((JpegSaveVals *)parasite->data)->progressive;
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -227,7 +321,9 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
*nreturn_vals = 1;
|
*nreturn_vals = 1;
|
||||||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
if (save_image (param[3].data.d_string,
|
||||||
|
param[1].data.d_int32,
|
||||||
|
param[2].data.d_int32))
|
||||||
{
|
{
|
||||||
/* Store mvals data */
|
/* Store mvals data */
|
||||||
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
gimp_set_data ("file_jpeg_save", &jsvals, sizeof (JpegSaveVals));
|
||||||
@ -236,6 +332,32 @@ run (char *name,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - now we need to change the defaults to be whatever
|
||||||
|
* was used to save this image. Dump the old parasites
|
||||||
|
* and add new ones. */
|
||||||
|
|
||||||
|
gimp_image_detach_parasite(image_ID,"gimp-comment");
|
||||||
|
if(strlen(image_comment)) {
|
||||||
|
parasite=parasite_new("gimp-comment",
|
||||||
|
PARASITE_PERSISTENT,
|
||||||
|
strlen(image_comment)+1,
|
||||||
|
image_comment);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
}
|
||||||
|
gimp_image_detach_parasite(image_ID, "jpeg-save-options");
|
||||||
|
|
||||||
|
parasite=parasite_new("jpeg-save-options",0,sizeof(jsvals),&jsvals);
|
||||||
|
gimp_image_attach_parasite(image_ID,parasite);
|
||||||
|
parasite_free(parasite);
|
||||||
|
|
||||||
|
#endif /* Have Parasites */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +406,17 @@ load_image (char *filename)
|
|||||||
int scanlines;
|
int scanlines;
|
||||||
int i, start, end;
|
int i, start, end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
JpegSaveVals local_save_vals;
|
||||||
|
jpeg_saved_marker_ptr curr_marker;
|
||||||
|
Parasite *comment_parasite;
|
||||||
|
Parasite *vals_parasite;
|
||||||
|
GString *local_image_comments=NULL;
|
||||||
|
#endif GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
|
||||||
/* We set up the normal JPEG error routines. */
|
/* We set up the normal JPEG error routines. */
|
||||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||||
jerr.pub.error_exit = my_error_exit;
|
jerr.pub.error_exit = my_error_exit;
|
||||||
@ -320,6 +453,10 @@ load_image (char *filename)
|
|||||||
|
|
||||||
jpeg_stdio_src (&cinfo, infile);
|
jpeg_stdio_src (&cinfo, infile);
|
||||||
|
|
||||||
|
/* pw - step 2.1 let the lib know we want the comments. */
|
||||||
|
|
||||||
|
jpeg_save_markers(&cinfo,JPEG_COM,0xFFFF);
|
||||||
|
|
||||||
/* Step 3: read file parameters with jpeg_read_header() */
|
/* Step 3: read file parameters with jpeg_read_header() */
|
||||||
|
|
||||||
(void) jpeg_read_header (&cinfo, TRUE);
|
(void) jpeg_read_header (&cinfo, TRUE);
|
||||||
@ -329,6 +466,62 @@ load_image (char *filename)
|
|||||||
* See libjpeg.doc for more info.
|
* See libjpeg.doc for more info.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef GIMP_HAVE_PARASITES
|
||||||
|
|
||||||
|
/* pw - Walk the marker list looking for comments (that is the only
|
||||||
|
* thing that should be in there - but be explicit.) If there are
|
||||||
|
* multiple comments, merge them into one with a /n between each. */
|
||||||
|
|
||||||
|
for(curr_marker=cinfo.marker_list;
|
||||||
|
curr_marker!=NULL;
|
||||||
|
curr_marker=curr_marker->next) {
|
||||||
|
|
||||||
|
if(curr_marker->marker==JPEG_COM) {
|
||||||
|
int len=curr_marker->data_length;
|
||||||
|
char *string=g_strndup(curr_marker->data,len);
|
||||||
|
if(len != curr_marker->original_length) {
|
||||||
|
g_warning("*** Warning *** comment truncated\n");
|
||||||
|
}
|
||||||
|
if(image_comment) {
|
||||||
|
g_string_append_c(local_image_comments,'\n');
|
||||||
|
g_string_append(local_image_comments,string);
|
||||||
|
} else {
|
||||||
|
local_image_comments=g_string_new(string);
|
||||||
|
}
|
||||||
|
g_free(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(local_image_comments) {
|
||||||
|
char *string=local_image_comments->str;
|
||||||
|
g_string_free(local_image_comments,FALSE);
|
||||||
|
comment_parasite = parasite_new("gimp-comment", PARASITE_PERSISTENT,
|
||||||
|
strlen(string)+1,string);
|
||||||
|
} else {
|
||||||
|
comment_parasite=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - figuring out what the saved values were is non-trivial.
|
||||||
|
* They don't seem to be in the cinfo structure. For now, I will
|
||||||
|
* just use the defaults, but if someone figures out how to derive
|
||||||
|
* them this is the place to store them. */
|
||||||
|
|
||||||
|
local_save_vals.quality=DEFAULT_QUALITY;
|
||||||
|
local_save_vals.smoothing=DEFAULT_SMOOTHING;
|
||||||
|
local_save_vals.optimize=DEFAULT_OPTIMIZE;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
local_save_vals.progressive=cinfo.progressive_mode;
|
||||||
|
#else
|
||||||
|
local_save_vals.progressive=0;
|
||||||
|
#endif /* HAVE_PROGRESSIVE_JPEG */
|
||||||
|
|
||||||
|
vals_parasite = parasite_new("jpeg-save-options", 0,
|
||||||
|
sizeof(local_save_vals), &local_save_vals);
|
||||||
|
|
||||||
|
#endif /* GIMP_HAVE_PARASITES */
|
||||||
|
|
||||||
|
|
||||||
/* Step 4: set parameters for decompression */
|
/* Step 4: set parameters for decompression */
|
||||||
|
|
||||||
/* In this example, we don't need to change any of the defaults set by
|
/* In this example, we don't need to change any of the defaults set by
|
||||||
@ -438,6 +631,18 @@ load_image (char *filename)
|
|||||||
*/
|
*/
|
||||||
gimp_drawable_flush (drawable);
|
gimp_drawable_flush (drawable);
|
||||||
|
|
||||||
|
/* pw - Last of all, attach the parasites (couldn't do it earlier -
|
||||||
|
there was no image. */
|
||||||
|
|
||||||
|
if(comment_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, comment_parasite);
|
||||||
|
parasite_free(comment_parasite);
|
||||||
|
}
|
||||||
|
if(vals_parasite){
|
||||||
|
gimp_image_attach_parasite(image_ID, vals_parasite);
|
||||||
|
parasite_free(vals_parasite);
|
||||||
|
}
|
||||||
|
|
||||||
return image_ID;
|
return image_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,6 +770,12 @@ save_image (char *filename,
|
|||||||
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
cinfo.smoothing_factor = (int) (jsvals.smoothing * 100);
|
||||||
cinfo.optimize_coding = jsvals.optimize;
|
cinfo.optimize_coding = jsvals.optimize;
|
||||||
|
|
||||||
|
#ifdef HAVE_PROGRESSIVE_JPEG
|
||||||
|
if(jsvals.progressive) {
|
||||||
|
jpeg_simple_progression(&cinfo);
|
||||||
|
}
|
||||||
|
#endif HAVE_PROGRESSIVE_JPEG
|
||||||
|
|
||||||
/* Step 4: Start compressor */
|
/* Step 4: Start compressor */
|
||||||
|
|
||||||
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
/* TRUE ensures that we will write a complete interchange-JPEG file.
|
||||||
@ -572,6 +783,14 @@ save_image (char *filename,
|
|||||||
*/
|
*/
|
||||||
jpeg_start_compress (&cinfo, TRUE);
|
jpeg_start_compress (&cinfo, TRUE);
|
||||||
|
|
||||||
|
/* Step 4.1: Write the comment out - pw */
|
||||||
|
if(image_comment) {
|
||||||
|
jpeg_write_marker(&cinfo,
|
||||||
|
JPEG_COM,
|
||||||
|
image_comment,
|
||||||
|
strlen(image_comment));
|
||||||
|
}
|
||||||
|
|
||||||
/* Step 5: while (scan lines remain to be written) */
|
/* Step 5: while (scan lines remain to be written) */
|
||||||
/* jpeg_write_scanlines(...); */
|
/* jpeg_write_scanlines(...); */
|
||||||
|
|
||||||
@ -651,6 +870,14 @@ save_dialog ()
|
|||||||
GtkWidget *table;
|
GtkWidget *table;
|
||||||
GtkWidget *toggle;
|
GtkWidget *toggle;
|
||||||
GtkObject *scale_data;
|
GtkObject *scale_data;
|
||||||
|
|
||||||
|
GtkWidget *progressive;
|
||||||
|
|
||||||
|
GtkWidget *text;
|
||||||
|
GtkWidget *com_frame;
|
||||||
|
GtkWidget *com_table;
|
||||||
|
GtkWidget *vscrollbar;
|
||||||
|
|
||||||
gchar **argv;
|
gchar **argv;
|
||||||
gint argc;
|
gint argc;
|
||||||
|
|
||||||
@ -691,7 +918,7 @@ save_dialog ()
|
|||||||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||||||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||||||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||||||
table = gtk_table_new (3, 2, FALSE);
|
table = gtk_table_new (4, 2, FALSE);
|
||||||
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
gtk_container_border_width (GTK_CONTAINER (table), 10);
|
||||||
gtk_container_add (GTK_CONTAINER (frame), table);
|
gtk_container_add (GTK_CONTAINER (frame), table);
|
||||||
|
|
||||||
@ -734,6 +961,56 @@ save_dialog ()
|
|||||||
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(toggle), jsvals.optimize);
|
||||||
gtk_widget_show(toggle);
|
gtk_widget_show(toggle);
|
||||||
|
|
||||||
|
|
||||||
|
progressive = gtk_check_button_new_with_label("Progressive");
|
||||||
|
gtk_table_attach(GTK_TABLE(table), progressive, 0, 2, 3, 4,
|
||||||
|
GTK_FILL, 0, 0, 0);
|
||||||
|
gtk_signal_connect(GTK_OBJECT(progressive), "toggled",
|
||||||
|
(GtkSignalFunc)save_progressive_toggle, NULL);
|
||||||
|
gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(progressive),
|
||||||
|
jsvals.progressive);
|
||||||
|
gtk_widget_show(progressive);
|
||||||
|
|
||||||
|
#ifndef HAVE_PROGRESSIVE_JPEG
|
||||||
|
gtk_widget_set_sensitive(progressive,FALSE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
com_frame = gtk_frame_new ("Image Comments");
|
||||||
|
gtk_frame_set_shadow_type (GTK_FRAME (com_frame), GTK_SHADOW_ETCHED_IN);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_frame), 10);
|
||||||
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), com_frame, TRUE, TRUE, 0
|
||||||
|
);
|
||||||
|
com_table = gtk_table_new (1, 1, FALSE);
|
||||||
|
gtk_container_border_width (GTK_CONTAINER (com_table), 10);
|
||||||
|
gtk_container_add (GTK_CONTAINER (com_frame), com_table);
|
||||||
|
|
||||||
|
text = gtk_text_new (NULL, NULL);
|
||||||
|
gtk_text_set_editable (GTK_TEXT (text), TRUE);
|
||||||
|
if(image_comment)
|
||||||
|
gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,image_comment,-1);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), text, 0, 1, 0, 1,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
|
||||||
|
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
|
||||||
|
/* Add a vertical scrollbar to the GtkText widget */
|
||||||
|
vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
|
||||||
|
gtk_table_attach (GTK_TABLE (com_table), vscrollbar, 1, 2, 0, 1,
|
||||||
|
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
|
||||||
|
gtk_widget_show (vscrollbar);
|
||||||
|
|
||||||
|
/* pw - mild hack here. I didn't like redoing the comment string
|
||||||
|
* each time a character was typed, so I associated the text area
|
||||||
|
* with the dialog. That way, just before the dialog destroys
|
||||||
|
* itself (once the ok button is hit) it can save whatever was in
|
||||||
|
* the comment text area to the comment string. See the
|
||||||
|
* save-ok-callback for more details. */
|
||||||
|
|
||||||
|
gtk_object_set_data((GtkObject *)dlg,"text",text);
|
||||||
|
|
||||||
|
gtk_widget_show (text);
|
||||||
|
gtk_widget_show (com_frame);
|
||||||
|
gtk_widget_show (com_table);
|
||||||
|
|
||||||
gtk_widget_show (frame);
|
gtk_widget_show (frame);
|
||||||
gtk_widget_show (table);
|
gtk_widget_show (table);
|
||||||
gtk_widget_show (dlg);
|
gtk_widget_show (dlg);
|
||||||
@ -758,7 +1035,21 @@ static void
|
|||||||
save_ok_callback (GtkWidget *widget,
|
save_ok_callback (GtkWidget *widget,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
GtkWidget *text;
|
||||||
jsint.run = TRUE;
|
jsint.run = TRUE;
|
||||||
|
|
||||||
|
/* pw - get the comment text object and grab it's data */
|
||||||
|
text=gtk_object_get_data(GTK_OBJECT(data),"text");
|
||||||
|
if(image_comment!=NULL) {
|
||||||
|
g_free(image_comment);
|
||||||
|
image_comment=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pw - gtk_editable_get_chars allocates a copy of the string, so
|
||||||
|
* don't worry about the gtk_widget_destroy killing it. */
|
||||||
|
|
||||||
|
image_comment=gtk_editable_get_chars(GTK_EDITABLE (text),0,-1);
|
||||||
|
|
||||||
gtk_widget_destroy (GTK_WIDGET (data));
|
gtk_widget_destroy (GTK_WIDGET (data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,3 +1066,12 @@ save_optimize_update(GtkWidget *widget,
|
|||||||
{
|
{
|
||||||
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
jsvals.optimize = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
save_progressive_toggle(GtkWidget *widget,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
jsvals.progressive = GTK_TOGGLE_BUTTON(widget)->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user