1 /*
2 * jpegsave.cpp - class for saving an ImageSource to disk as a JPEG file.
3 *
4 * Copyright (c) 2007 by Alastair M. Robinson
5 * Distributed under the terms of the GNU General Public License -
6 * see the file named "COPYING" for more details.
7 *
8 *
9 */
10
11
12 #include <iostream>
13
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <setjmp.h>
17 #include <sys/stat.h>
18
19 #include "debug.h"
20 #include "lcmswrapper.h"
21 #include "imagesource_flatten.h"
22
23 #include "iccjpeg.h"
24 #include "jpegsave.h"
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "gettext.h"
31 #define _(x) gettext(x)
32
33 using namespace std;
34
35
36 struct JPEGSaver_ErrManager
37 {
38 struct jpeg_error_mgr std;
39 FILE *file;
40 };
41
42
isjpeg_error_exit(j_common_ptr cinfo)43 static void isjpeg_error_exit (j_common_ptr cinfo)
44 {
45 char buffer[JMSG_LENGTH_MAX];
46 JPEGSaver_ErrManager *myerr = (JPEGSaver_ErrManager *) cinfo->err;
47 cinfo->err->output_message(cinfo);
48 cinfo->err->format_message(cinfo,buffer);
49 cerr << buffer << endl;
50 fclose(myerr->file);
51 exit(1);
52 }
53
54
~JPEGSaver()55 JPEGSaver::~JPEGSaver()
56 {
57 if(err)
58 {
59 if(err->file)
60 fclose(err->file);
61 delete err;
62 }
63 if(cinfo)
64 {
65 jpeg_destroy_compress((jpeg_compress_struct *)cinfo);
66 delete cinfo;
67 }
68
69 if(tmpbuffer)
70 free(tmpbuffer);
71 }
72
73
Save()74 void JPEGSaver::Save()
75 {
76 if(err->file)
77 {
78 int i;
79 unsigned char *dst;
80 ISDataType *src;
81
82 for(int row=0;row<height;++row)
83 {
84 if(progress && !(row&31))
85 {
86 if(!progress->DoProgress(row,height))
87 return;
88 }
89
90 dst=tmpbuffer;
91
92 src=imagesource->GetRow(row);
93
94 switch(imagesource->type)
95 {
96 case IS_TYPE_RGB:
97 for(i=0;i<bytesperrow;++i)
98 {
99 unsigned int t;
100 t=int(src[i]);
101 t=ISTOEIGHT(t);
102 if(t>255) t=255;
103 if(t<0) t=0;
104 dst[i]=t;
105 }
106 break;
107 default:
108 for(i=0;i<bytesperrow;++i)
109 {
110 unsigned int t;
111 t=IS_SAMPLEMAX-src[i];
112 t=ISTOEIGHT(t);
113 if(t>255) t=255;
114 if(t<0) t=0;
115 dst[i]=t;
116 }
117 break;
118 }
119 JSAMPROW rowptr[1]={tmpbuffer};
120 jpeg_write_scanlines(cinfo, rowptr, 1);
121 }
122 if(progress)
123 progress->DoProgress(height,height);
124 jpeg_finish_compress(cinfo);
125 jpeg_destroy_compress(cinfo);
126 delete cinfo;
127 cinfo=NULL;
128 }
129 }
130
131
EmbedProfile(CMSProfile * profile)132 void JPEGSaver::EmbedProfile(CMSProfile *profile)
133 {
134 if(!profile)
135 return;
136 if(!cinfo)
137 throw "JPEGSaver: cinfo already freed!";
138 FILE* f;
139 size_t size, EmbedLen;
140 JOCTET *EmbedBuffer;
141
142 const char *fn=profile->GetFilename();
143 if(!fn)
144 return;
145
146 if(!(f = fopen(fn, "rb")))
147 return;
148
149 fseek(f,0,SEEK_END);
150 size=ftell(f);
151 fseek(f,0,SEEK_SET);
152
153 cerr << "Profile " << fn << " is " << size << "bytes." << endl;
154
155 EmbedBuffer = (JOCTET *) malloc(size + 1);
156 EmbedLen = fread(EmbedBuffer, 1, size, f);
157 fclose(f);
158 EmbedBuffer[EmbedLen] = 0;
159
160 write_icc_profile(cinfo,EmbedBuffer,EmbedLen);
161 free(EmbedBuffer);
162 }
163
164
JPEGSaver(const char * filename,struct ImageSource * is,int compression)165 JPEGSaver::JPEGSaver(const char *filename,struct ImageSource *is,int compression)
166 : ImageSaver(), imagesource(is), cinfo(NULL), tmpbuffer(NULL)
167 {
168 if(STRIP_ALPHA(is->type)==IS_TYPE_BW)
169 throw _("JPEG Saver only supports greyscale and colour images!");
170
171 // if(STRIP_ALPHA(is->type)==IS_TYPE_CMYK)
172 // throw _("Saving CMYK JPEGs not (yet) supported");
173
174 if(HAS_ALPHA(is->type))
175 is=new ImageSource_Flatten(is);
176
177 this->width=is->width;
178 this->height=is->height;
179
180 cinfo=new jpeg_compress_struct;
181 err=new JPEGSaver_ErrManager;
182 memset(cinfo,0,sizeof(jpeg_compress_struct));
183 memset(err,0,sizeof(JPEGSaver_ErrManager));
184 cinfo->err = jpeg_std_error(&err->std);
185 err->std.error_exit = isjpeg_error_exit;
186
187 if((err->file = fopen(filename,"wb")) == NULL)
188 throw _("Can't open file for saving");
189
190 cerr << "File " << filename << " opened" << endl;
191
192 jpeg_create_compress(cinfo);
193 jpeg_stdio_dest(cinfo, err->file);
194
195 cinfo->image_width=is->width;
196 cinfo->image_height=is->height;
197
198 switch(is->type)
199 {
200 case IS_TYPE_RGB:
201 cinfo->input_components=3;
202 cinfo->in_color_space = JCS_RGB;
203 jpeg_set_defaults(cinfo);
204 break;
205 case IS_TYPE_GREY:
206 cinfo->input_components=1;
207 cinfo->in_color_space = JCS_GRAYSCALE;
208 jpeg_set_defaults(cinfo);
209 break;
210 case IS_TYPE_CMYK:
211 cinfo->input_components=4;
212 cinfo->in_color_space = JCS_CMYK;
213 cinfo->jpeg_color_space = JCS_YCCK;
214 jpeg_set_defaults(cinfo);
215 jpeg_set_colorspace(cinfo, JCS_YCCK);
216 cinfo->write_JFIF_header=TRUE; // HACK to force resolution to be saved.
217 // Not strictly correct, since JFIF files can't be CMYK.
218 // LCMS's jpegicc does this too (though probably not deliberately!)
219 break;
220 default:
221 throw _("JPEG Saver can currently only save RGB or Greyscale images.");
222 break;
223 }
224
225 jpeg_set_quality(cinfo,compression,TRUE);
226
227 cinfo->density_unit=1;
228 cinfo->X_density=is->xres;
229 cinfo->Y_density=is->yres;
230
231 Debug[TRACE] << "JPEGSaver: Set xres to " << cinfo->X_density << endl;
232 Debug[TRACE] << "JPEGSaver: Set yres to " << cinfo->Y_density << endl;
233
234 jpeg_start_compress(cinfo,TRUE);
235
236 if(is->GetEmbeddedProfile())
237 EmbedProfile(is->GetEmbeddedProfile());
238
239 bytesperrow = width*is->samplesperpixel;
240
241 if(!(tmpbuffer=(unsigned char *)malloc(bytesperrow)))
242 throw "No memory for tmpbuffer";
243 }
244