1/*
2 * Copyright (C)2011 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
46static char errStr[JMSG_LENGTH_MAX]="No error";
47
48struct my_error_mgr
49{
50	struct jpeg_error_mgr pub;
51	jmp_buf setjmp_buffer;
52};
53typedef struct my_error_mgr *my_error_ptr;
54
55static 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
64static 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
75static 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 *srcptr=srcbuf, *srcptr2;
79	int srcps=tjPixelSize[srcpf];
80	int srcstride=srcbottomup? -w*srcps:w*srcps;
81	unsigned char *dstptr=dstbuf, *dstptr2;
82	int dstps=tjPixelSize[dstpf];
83	int dststride=dstbottomup? -w*dstps:w*dstps;
84	int row, col;
85
86	if(srcbottomup) srcptr=&srcbuf[w*srcps*(h-1)];
87	if(dstbottomup) dstptr=&dstbuf[w*dstps*(h-1)];
88	for(row=0; row<h; row++, srcptr+=srcstride, dstptr+=dststride)
89	{
90		for(col=0, srcptr2=srcptr, dstptr2=dstptr; col<w; col++, srcptr2+=srcps,
91			dstptr2+=dstps)
92		{
93			dstptr2[tjRedOffset[dstpf]]=srcptr2[tjRedOffset[srcpf]];
94			dstptr2[tjGreenOffset[dstpf]]=srcptr2[tjGreenOffset[srcpf]];
95			dstptr2[tjBlueOffset[dstpf]]=srcptr2[tjBlueOffset[srcpf]];
96		}
97	}
98}
99
100
101int loadbmp(char *filename, unsigned char **buf, int *w, int *h,
102	int dstpf, int bottomup)
103{
104	int retval=0, dstps, srcpf, tempc;
105	struct jpeg_compress_struct cinfo;
106	struct my_error_mgr jerr;
107	cjpeg_source_ptr src;
108	FILE *file=NULL;
109
110	memset(&cinfo, 0, sizeof(struct jpeg_compress_struct));
111
112	if(!filename || !buf || !w || !h || dstpf<0 || dstpf>=TJ_NUMPF)
113		_throw("loadbmp(): Invalid argument");
114
115	if((file=fopen(filename, "rb"))==NULL)
116		_throwunix("loadbmp(): Cannot open input file");
117
118	cinfo.err=jpeg_std_error(&jerr.pub);
119	jerr.pub.error_exit=my_error_exit;
120	jerr.pub.output_message=my_output_message;
121
122	if(setjmp(jerr.setjmp_buffer))
123	{
124		/* If we get here, the JPEG code has signaled an error. */
125		retval=-1;  goto bailout;
126	}
127
128	jpeg_create_compress(&cinfo);
129	if((tempc=getc(file))<0 || ungetc(tempc, file)==EOF)
130		_throwunix("loadbmp(): Could not read input file")
131	else if(tempc==EOF) _throw("loadbmp(): Input file contains no data");
132
133	if(tempc=='B')
134	{
135		if((src=jinit_read_bmp(&cinfo))==NULL)
136			_throw("loadbmp(): Could not initialize bitmap loader");
137	}
138	else if(tempc=='P')
139	{
140		if((src=jinit_read_ppm(&cinfo))==NULL)
141			_throw("loadbmp(): Could not initialize bitmap loader");
142	}
143	else _throw("loadbmp(): Unsupported file type");
144
145	src->input_file=file;
146	(*src->start_input)(&cinfo, src);
147	(*cinfo.mem->realize_virt_arrays)((j_common_ptr)&cinfo);
148
149	*w=cinfo.image_width;  *h=cinfo.image_height;
150
151	if(cinfo.input_components==1 && cinfo.in_color_space==JCS_RGB)
152		srcpf=TJPF_GRAY;
153	else srcpf=TJPF_RGB;
154
155	dstps=tjPixelSize[dstpf];
156	if((*buf=(unsigned char *)malloc((*w)*(*h)*dstps))==NULL)
157		_throw("loadbmp(): Memory allocation failure");
158
159	while(cinfo.next_scanline<cinfo.image_height)
160	{
161		int i, nlines=(*src->get_pixel_rows)(&cinfo, src);
162		for(i=0; i<nlines; i++)
163		{
164			unsigned char *outbuf;  int row;
165			row=cinfo.next_scanline+i;
166			if(bottomup) outbuf=&(*buf)[((*h)-row-1)*(*w)*dstps];
167			else outbuf=&(*buf)[row*(*w)*dstps];
168			pixelconvert(src->buffer[i], srcpf, 0, outbuf, dstpf, bottomup, *w,
169				nlines);
170		}
171		cinfo.next_scanline+=nlines;
172	}
173
174	(*src->finish_input)(&cinfo, src);
175
176	bailout:
177	jpeg_destroy_compress(&cinfo);
178	if(file) fclose(file);
179	if(retval<0 && buf && *buf) {free(*buf);  *buf=NULL;}
180	return retval;
181}
182
183
184int savebmp(char *filename, unsigned char *buf, int w, int h, int srcpf,
185	int bottomup)
186{
187	int retval=0, srcps, dstpf;
188	struct jpeg_decompress_struct dinfo;
189	struct my_error_mgr jerr;
190	djpeg_dest_ptr dst;
191	FILE *file=NULL;
192	char *ptr=NULL;
193
194	memset(&dinfo, 0, sizeof(struct jpeg_decompress_struct));
195
196	if(!filename || !buf || w<1 || h<1 || srcpf<0 || srcpf>=TJ_NUMPF)
197		_throw("savebmp(): Invalid argument");
198
199	if((file=fopen(filename, "wb"))==NULL)
200		_throwunix("savebmp(): Cannot open output file");
201
202	dinfo.err=jpeg_std_error(&jerr.pub);
203	jerr.pub.error_exit=my_error_exit;
204	jerr.pub.output_message=my_output_message;
205
206	if(setjmp(jerr.setjmp_buffer))
207	{
208		/* If we get here, the JPEG code has signaled an error. */
209		retval=-1;  goto bailout;
210	}
211
212	jpeg_create_decompress(&dinfo);
213	if(srcpf==TJPF_GRAY)
214	{
215		dinfo.out_color_components=dinfo.output_components=1;
216		dinfo.out_color_space=JCS_GRAYSCALE;
217	}
218	else
219	{
220		dinfo.out_color_components=dinfo.output_components=3;
221		dinfo.out_color_space=JCS_RGB;
222	}
223	dinfo.image_width=w;  dinfo.image_height=h;
224	dinfo.global_state=DSTATE_READY;
225	dinfo.scale_num=dinfo.scale_denom=1;
226
227	ptr=strrchr(filename, '.');
228	if(ptr && !strcasecmp(ptr, ".bmp"))
229	{
230		if((dst=jinit_write_bmp(&dinfo, 0))==NULL)
231			_throw("savebmp(): Could not initialize bitmap writer");
232	}
233	else
234	{
235		if((dst=jinit_write_ppm(&dinfo))==NULL)
236			_throw("savebmp(): Could not initialize PPM writer");
237	}
238
239	dst->output_file=file;
240	(*dst->start_output)(&dinfo, dst);
241	(*dinfo.mem->realize_virt_arrays)((j_common_ptr)&dinfo);
242
243	if(srcpf==TJPF_GRAY) dstpf=srcpf;
244	else dstpf=TJPF_RGB;
245	srcps=tjPixelSize[srcpf];
246
247	while(dinfo.output_scanline<dinfo.output_height)
248	{
249		int i, nlines=dst->buffer_height;
250		for(i=0; i<nlines; i++)
251		{
252			unsigned char *inbuf;  int row;
253			row=dinfo.output_scanline+i;
254			if(bottomup) inbuf=&buf[(h-row-1)*w*srcps];
255			else inbuf=&buf[row*w*srcps];
256			pixelconvert(inbuf, srcpf, bottomup, dst->buffer[i], dstpf, 0, w,
257				nlines);
258		}
259		(*dst->put_pixel_rows)(&dinfo, dst, nlines);
260		dinfo.output_scanline+=nlines;
261	}
262
263	(*dst->finish_output)(&dinfo, dst);
264
265	bailout:
266	jpeg_destroy_decompress(&dinfo);
267	if(file) fclose(file);
268	return retval;
269}
270
271const char *bmpgeterr(void)
272{
273	return errStr;
274}
275