1 /*
2  * Copyright (C)2011, 2015 D. R. Commander.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of the libjpeg-turbo Project nor the names of its
13  *   contributors may be used to endorse or promote products derived from this
14  *   software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <stdio.h>
30 #include <string.h>
31 #include <setjmp.h>
32 #include <errno.h>
33 #include "cdjpeg.h"
34 #include <jpeglib.h>
35 #include <jpegint.h>
36 #include "tjutil.h"
37 #include "bmp.h"
38 
39 
40 /* This duplicates the functionality of the VirtualGL bitmap library using
41    the components from cjpeg and djpeg */
42 
43 
44 /* Error handling (based on example in example.c) */
45 
46 static char errStr[JMSG_LENGTH_MAX]="No error";
47 
48 struct my_error_mgr
49 {
50 	struct jpeg_error_mgr pub;
51 	jmp_buf setjmp_buffer;
52 };
53 typedef struct my_error_mgr *my_error_ptr;
54 
my_error_exit(j_common_ptr cinfo)55 static void my_error_exit(j_common_ptr cinfo)
56 {
57 	my_error_ptr myerr=(my_error_ptr)cinfo->err;
58 	(*cinfo->err->output_message)(cinfo);
59 	longjmp(myerr->setjmp_buffer, 1);
60 }
61 
62 /* Based on output_message() in jerror.c */
63 
my_output_message(j_common_ptr cinfo)64 static void my_output_message(j_common_ptr cinfo)
65 {
66 	(*cinfo->err->format_message)(cinfo, errStr);
67 }
68 
69 #define _throw(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m);  \
70 	retval=-1;  goto bailout;}
71 #define _throwunix(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m,  \
72 	strerror(errno));  retval=-1;  goto bailout;}
73 
74 
pixelconvert(unsigned char * srcbuf,int srcpf,int srcbottomup,unsigned char * dstbuf,int dstpf,int dstbottomup,int w,int h)75 static void pixelconvert(unsigned char *srcbuf, int srcpf, int srcbottomup,
76 	unsigned char *dstbuf, int dstpf, int dstbottomup, int w, int h)
77 {
78 	unsigned char *srcrowptr=srcbuf, *srccolptr;
79 	int srcps=tjPixelSize[srcpf];
80 	int srcstride=srcbottomup? -w*srcps:w*srcps;
81 	unsigned char *dstrowptr=dstbuf, *dstcolptr;
82 	int dstps=tjPixelSize[dstpf];
83 	int dststride=dstbottomup? -w*dstps:w*dstps;
84 	int row, col;
85 
86 	if(srcbottomup) srcrowptr=&srcbuf[w*srcps*(h-1)];
87 	if(dstbottomup) dstrowptr=&dstbuf[w*dstps*(h-1)];
88 
89 	/* NOTE: These quick & dirty CMYK<->RGB conversion routines are for testing
90 	   purposes only.  Properly converting between CMYK and RGB requires a color
91 	   management system. */
92 
93 	if(dstpf==TJPF_CMYK)
94 	{
95 		for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride)
96 		{
97 			for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr;
98 				col<w; col++, srccolptr+=srcps)
99 			{
100 				double c=1.0-((double)(srccolptr[tjRedOffset[srcpf]])/255.);
101 				double m=1.0-((double)(srccolptr[tjGreenOffset[srcpf]])/255.);
102 				double y=1.0-((double)(srccolptr[tjBlueOffset[srcpf]])/255.);
103 				double k=min(min(c,m),min(y,1.0));
104 				if(k==1.0) c=m=y=0.0;
105 				else
106 				{
107 					c=(c-k)/(1.0-k);
108 					m=(m-k)/(1.0-k);
109 					y=(y-k)/(1.0-k);
110 				}
111 				if(c>1.0) c=1.0;  if(c<0.) c=0.;
112 				if(m>1.0) m=1.0;  if(m<0.) m=0.;
113 				if(y>1.0) y=1.0;  if(y<0.) y=0.;
114 				if(k>1.0) k=1.0;  if(k<0.) k=0.;
115 				*dstcolptr++=(unsigned char)(255.0-c*255.0+0.5);
116 				*dstcolptr++=(unsigned char)(255.0-m*255.0+0.5);
117 				*dstcolptr++=(unsigned char)(255.0-y*255.0+0.5);
118 				*dstcolptr++=(unsigned char)(255.0-k*255.0+0.5);
119 			}
120 		}
121 	}
122 	else if(srcpf==TJPF_CMYK)
123 	{
124 		for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride)
125 		{
126 			for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr;
127 				col<w; col++, dstcolptr+=dstps)
128 			{
129 				double c=(double)(*srccolptr++);
130 				double m=(double)(*srccolptr++);
131 				double y=(double)(*srccolptr++);
132 				double k=(double)(*srccolptr++);
133 				double r=c*k/255.;
134 				double g=m*k/255.;
135 				double b=y*k/255.;
136 				if(r>255.0) r=255.0;  if(r<0.) r=0.;
137 				if(g>255.0) g=255.0;  if(g<0.) g=0.;
138 				if(b>255.0) b=255.0;  if(b<0.) b=0.;
139 				dstcolptr[tjRedOffset[dstpf]]=(unsigned char)(r+0.5);
140 				dstcolptr[tjGreenOffset[dstpf]]=(unsigned char)(g+0.5);
141 				dstcolptr[tjBlueOffset[dstpf]]=(unsigned char)(b+0.5);
142 			}
143 		}
144 	}
145 	else
146 	{
147 		for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride)
148 		{
149 			for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr;
150 				col<w; col++, srccolptr+=srcps, dstcolptr+=dstps)
151 			{
152 				dstcolptr[tjRedOffset[dstpf]]=srccolptr[tjRedOffset[srcpf]];
153 				dstcolptr[tjGreenOffset[dstpf]]=srccolptr[tjGreenOffset[srcpf]];
154 				dstcolptr[tjBlueOffset[dstpf]]=srccolptr[tjBlueOffset[srcpf]];
155 			}
156 		}
157 	}
158 }
159 
160 
loadbmp(char * filename,unsigned char ** buf,int * w,int * h,int dstpf,int bottomup)161 int loadbmp(char *filename, unsigned char **buf, int *w, int *h,
162 	int dstpf, int bottomup)
163 {
164 	int retval=0, dstps, srcpf, tempc;
165 	struct jpeg_compress_struct cinfo;
166 	struct my_error_mgr jerr;
167 	cjpeg_source_ptr src;
168 	FILE *file=NULL;
169 
170 	memset(&cinfo, 0, sizeof(struct jpeg_compress_struct));
171 
172 	if(!filename || !buf || !w || !h || dstpf<0 || dstpf>=TJ_NUMPF)
173 		_throw("loadbmp(): Invalid argument");
174 
175 	if((file=fopen(filename, "rb"))==NULL)
176 		_throwunix("loadbmp(): Cannot open input file");
177 
178 	cinfo.err=jpeg_std_error(&jerr.pub);
179 	jerr.pub.error_exit=my_error_exit;
180 	jerr.pub.output_message=my_output_message;
181 
182 	if(setjmp(jerr.setjmp_buffer))
183 	{
184 		/* If we get here, the JPEG code has signaled an error. */
185 		retval=-1;  goto bailout;
186 	}
187 
188 	jpeg_create_compress(&cinfo);
189 	if((tempc=getc(file))<0 || ungetc(tempc, file)==EOF)
190 		_throwunix("loadbmp(): Could not read input file")
191 	else if(tempc==EOF) _throw("loadbmp(): Input file contains no data");
192 
193 	if(tempc=='B')
194 	{
195 		if((src=jinit_read_bmp(&cinfo))==NULL)
196 			_throw("loadbmp(): Could not initialize bitmap loader");
197 	}
198 	else if(tempc=='P')
199 	{
200 		if((src=jinit_read_ppm(&cinfo))==NULL)
201 			_throw("loadbmp(): Could not initialize bitmap loader");
202 	}
203 	else _throw("loadbmp(): Unsupported file type");
204 
205 	src->input_file=file;
206 	(*src->start_input)(&cinfo, src);
207 	(*cinfo.mem->realize_virt_arrays)((j_common_ptr)&cinfo);
208 
209 	*w=cinfo.image_width;  *h=cinfo.image_height;
210 
211 	if(cinfo.input_components==1 && cinfo.in_color_space==JCS_RGB)
212 		srcpf=TJPF_GRAY;
213 	else srcpf=TJPF_RGB;
214 
215 	dstps=tjPixelSize[dstpf];
216 	if((*buf=(unsigned char *)malloc((*w)*(*h)*dstps))==NULL)
217 		_throw("loadbmp(): Memory allocation failure");
218 
219 	while(cinfo.next_scanline<cinfo.image_height)
220 	{
221 		int i, nlines=(*src->get_pixel_rows)(&cinfo, src);
222 		for(i=0; i<nlines; i++)
223 		{
224 			unsigned char *outbuf;  int row;
225 			row=cinfo.next_scanline+i;
226 			if(bottomup) outbuf=&(*buf)[((*h)-row-1)*(*w)*dstps];
227 			else outbuf=&(*buf)[row*(*w)*dstps];
228 			pixelconvert(src->buffer[i], srcpf, 0, outbuf, dstpf, bottomup, *w,
229 				nlines);
230 		}
231 		cinfo.next_scanline+=nlines;
232 	}
233 
234 	(*src->finish_input)(&cinfo, src);
235 
236 	bailout:
237 	jpeg_destroy_compress(&cinfo);
238 	if(file) fclose(file);
239 	if(retval<0 && buf && *buf) {free(*buf);  *buf=NULL;}
240 	return retval;
241 }
242 
243 
savebmp(char * filename,unsigned char * buf,int w,int h,int srcpf,int bottomup)244 int savebmp(char *filename, unsigned char *buf, int w, int h, int srcpf,
245 	int bottomup)
246 {
247 	int retval=0, srcps, dstpf;
248 	struct jpeg_decompress_struct dinfo;
249 	struct my_error_mgr jerr;
250 	djpeg_dest_ptr dst;
251 	FILE *file=NULL;
252 	char *ptr=NULL;
253 
254 	memset(&dinfo, 0, sizeof(struct jpeg_decompress_struct));
255 
256 	if(!filename || !buf || w<1 || h<1 || srcpf<0 || srcpf>=TJ_NUMPF)
257 		_throw("savebmp(): Invalid argument");
258 
259 	if((file=fopen(filename, "wb"))==NULL)
260 		_throwunix("savebmp(): Cannot open output file");
261 
262 	dinfo.err=jpeg_std_error(&jerr.pub);
263 	jerr.pub.error_exit=my_error_exit;
264 	jerr.pub.output_message=my_output_message;
265 
266 	if(setjmp(jerr.setjmp_buffer))
267 	{
268 		/* If we get here, the JPEG code has signaled an error. */
269 		retval=-1;  goto bailout;
270 	}
271 
272 	jpeg_create_decompress(&dinfo);
273 	if(srcpf==TJPF_GRAY)
274 	{
275 		dinfo.out_color_components=dinfo.output_components=1;
276 		dinfo.out_color_space=JCS_GRAYSCALE;
277 	}
278 	else
279 	{
280 		dinfo.out_color_components=dinfo.output_components=3;
281 		dinfo.out_color_space=JCS_RGB;
282 	}
283 	dinfo.image_width=w;  dinfo.image_height=h;
284 	dinfo.global_state=DSTATE_READY;
285 	dinfo.scale_num=dinfo.scale_denom=1;
286 
287 	ptr=strrchr(filename, '.');
288 	if(ptr && !strcasecmp(ptr, ".bmp"))
289 	{
290 		if((dst=jinit_write_bmp(&dinfo, 0))==NULL)
291 			_throw("savebmp(): Could not initialize bitmap writer");
292 	}
293 	else
294 	{
295 		if((dst=jinit_write_ppm(&dinfo))==NULL)
296 			_throw("savebmp(): Could not initialize PPM writer");
297 	}
298 
299 	dst->output_file=file;
300 	(*dst->start_output)(&dinfo, dst);
301 	(*dinfo.mem->realize_virt_arrays)((j_common_ptr)&dinfo);
302 
303 	if(srcpf==TJPF_GRAY) dstpf=srcpf;
304 	else dstpf=TJPF_RGB;
305 	srcps=tjPixelSize[srcpf];
306 
307 	while(dinfo.output_scanline<dinfo.output_height)
308 	{
309 		int i, nlines=dst->buffer_height;
310 		for(i=0; i<nlines; i++)
311 		{
312 			unsigned char *inbuf;  int row;
313 			row=dinfo.output_scanline+i;
314 			if(bottomup) inbuf=&buf[(h-row-1)*w*srcps];
315 			else inbuf=&buf[row*w*srcps];
316 			pixelconvert(inbuf, srcpf, bottomup, dst->buffer[i], dstpf, 0, w,
317 				nlines);
318 		}
319 		(*dst->put_pixel_rows)(&dinfo, dst, nlines);
320 		dinfo.output_scanline+=nlines;
321 	}
322 
323 	(*dst->finish_output)(&dinfo, dst);
324 
325 	bailout:
326 	jpeg_destroy_decompress(&dinfo);
327 	if(file) fclose(file);
328 	return retval;
329 }
330 
bmpgeterr(void)331 const char *bmpgeterr(void)
332 {
333 	return errStr;
334 }
335