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