blob: 9fcf7bbd5d6257801997ba85013e14efae60f50b [file] [log] [blame]
DRC0fc884a2011-05-24 09:13:17 +00001/*
DRC2ab5ea62015-01-14 19:39:01 +00002 * Copyright (C)2011, 2015 D. R. Commander. All Rights Reserved.
DRC2e7b76b2009-04-03 12:04:24 +00003 *
DRC0fc884a2011-05-24 09:13:17 +00004 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
DRC2e7b76b2009-04-03 12:04:24 +00006 *
DRC0fc884a2011-05-24 09:13:17 +00007 * - 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 */
DRC2e7b76b2009-04-03 12:04:24 +000028
DRC2e7b76b2009-04-03 12:04:24 +000029#include <stdio.h>
30#include <string.h>
DRC0fc884a2011-05-24 09:13:17 +000031#include <setjmp.h>
32#include <errno.h>
DRC296c71b2011-05-25 04:12:52 +000033#include "cdjpeg.h"
DRC0fc884a2011-05-24 09:13:17 +000034#include <jpeglib.h>
35#include <jpegint.h>
DRC43484642011-05-24 17:03:51 +000036#include "tjutil.h"
DRC0fc884a2011-05-24 09:13:17 +000037#include "bmp.h"
DRC2e7b76b2009-04-03 12:04:24 +000038
DRC2e7b76b2009-04-03 12:04:24 +000039
DRC0fc884a2011-05-24 09:13:17 +000040/* 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
DRC2e7b76b2009-04-03 12:04:24 +000049{
DRC0fc884a2011-05-24 09:13:17 +000050 struct jpeg_error_mgr pub;
51 jmp_buf setjmp_buffer;
52};
53typedef struct my_error_mgr *my_error_ptr;
DRC2e7b76b2009-04-03 12:04:24 +000054
DRC0fc884a2011-05-24 09:13:17 +000055static void my_error_exit(j_common_ptr cinfo)
DRC2e7b76b2009-04-03 12:04:24 +000056{
DRC0fc884a2011-05-24 09:13:17 +000057 my_error_ptr myerr=(my_error_ptr)cinfo->err;
58 (*cinfo->err->output_message)(cinfo);
59 longjmp(myerr->setjmp_buffer, 1);
DRC2e7b76b2009-04-03 12:04:24 +000060}
61
DRC0fc884a2011-05-24 09:13:17 +000062/* Based on output_message() in jerror.c */
63
64static void my_output_message(j_common_ptr cinfo)
DRC2e7b76b2009-04-03 12:04:24 +000065{
DRC0fc884a2011-05-24 09:13:17 +000066 (*cinfo->err->format_message)(cinfo, errStr);
67}
DRC2e7b76b2009-04-03 12:04:24 +000068
DRC0fc884a2011-05-24 09:13:17 +000069#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;}
DRC2e7b76b2009-04-03 12:04:24 +000073
DRC0fc884a2011-05-24 09:13:17 +000074
75static void pixelconvert(unsigned char *srcbuf, int srcpf, int srcbottomup,
76 unsigned char *dstbuf, int dstpf, int dstbottomup, int w, int h)
77{
DRC2ab5ea62015-01-14 19:39:01 +000078 unsigned char *srcrowptr=srcbuf, *srccolptr;
DRC0fc884a2011-05-24 09:13:17 +000079 int srcps=tjPixelSize[srcpf];
80 int srcstride=srcbottomup? -w*srcps:w*srcps;
DRC2ab5ea62015-01-14 19:39:01 +000081 unsigned char *dstrowptr=dstbuf, *dstcolptr;
DRC0fc884a2011-05-24 09:13:17 +000082 int dstps=tjPixelSize[dstpf];
83 int dststride=dstbottomup? -w*dstps:w*dstps;
84 int row, col;
85
DRC2ab5ea62015-01-14 19:39:01 +000086 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)
DRC2e7b76b2009-04-03 12:04:24 +000094 {
DRC2ab5ea62015-01-14 19:39:01 +000095 for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride)
DRC2e7b76b2009-04-03 12:04:24 +000096 {
DRC2ab5ea62015-01-14 19:39:01 +000097 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 }
DRC2e7b76b2009-04-03 12:04:24 +0000156 }
157 }
DRC2e7b76b2009-04-03 12:04:24 +0000158}
159
160
DRC1a45b812014-05-09 18:06:58 +0000161int loadbmp(char *filename, unsigned char **buf, int *w, int *h,
DRC0fc884a2011-05-24 09:13:17 +0000162 int dstpf, int bottomup)
DRC2e7b76b2009-04-03 12:04:24 +0000163{
DRC0fc884a2011-05-24 09:13:17 +0000164 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;
DRC2e7b76b2009-04-03 12:04:24 +0000169
DRC8e11a052011-10-18 22:07:13 +0000170 memset(&cinfo, 0, sizeof(struct jpeg_compress_struct));
171
DRC0fc884a2011-05-24 09:13:17 +0000172 if(!filename || !buf || !w || !h || dstpf<0 || dstpf>=TJ_NUMPF)
173 _throw("loadbmp(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +0000174
DRC0fc884a2011-05-24 09:13:17 +0000175 if((file=fopen(filename, "rb"))==NULL)
176 _throwunix("loadbmp(): Cannot open input file");
DRC2e7b76b2009-04-03 12:04:24 +0000177
DRC0fc884a2011-05-24 09:13:17 +0000178 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))
DRC2e7b76b2009-04-03 12:04:24 +0000183 {
DRC0fc884a2011-05-24 09:13:17 +0000184 /* If we get here, the JPEG code has signaled an error. */
185 retval=-1; goto bailout;
DRC2e7b76b2009-04-03 12:04:24 +0000186 }
187
DRC0fc884a2011-05-24 09:13:17 +0000188 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");
DRC2e7b76b2009-04-03 12:04:24 +0000192
DRC0fc884a2011-05-24 09:13:17 +0000193 if(tempc=='B')
DRC2e7b76b2009-04-03 12:04:24 +0000194 {
DRC0fc884a2011-05-24 09:13:17 +0000195 if((src=jinit_read_bmp(&cinfo))==NULL)
196 _throw("loadbmp(): Could not initialize bitmap loader");
DRC2e7b76b2009-04-03 12:04:24 +0000197 }
DRC0fc884a2011-05-24 09:13:17 +0000198 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");
DRC2e7b76b2009-04-03 12:04:24 +0000204
DRC0fc884a2011-05-24 09:13:17 +0000205 src->input_file=file;
206 (*src->start_input)(&cinfo, src);
207 (*cinfo.mem->realize_virt_arrays)((j_common_ptr)&cinfo);
DRC2e7b76b2009-04-03 12:04:24 +0000208
DRC0fc884a2011-05-24 09:13:17 +0000209 *w=cinfo.image_width; *h=cinfo.image_height;
DRC2e7b76b2009-04-03 12:04:24 +0000210
DRC0fc884a2011-05-24 09:13:17 +0000211 if(cinfo.input_components==1 && cinfo.in_color_space==JCS_RGB)
212 srcpf=TJPF_GRAY;
213 else srcpf=TJPF_RGB;
DRC2e7b76b2009-04-03 12:04:24 +0000214
DRC0fc884a2011-05-24 09:13:17 +0000215 dstps=tjPixelSize[dstpf];
216 if((*buf=(unsigned char *)malloc((*w)*(*h)*dstps))==NULL)
217 _throw("loadbmp(): Memory allocation failure");
DRC2e7b76b2009-04-03 12:04:24 +0000218
DRC0fc884a2011-05-24 09:13:17 +0000219 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;
DRCdf42b3c2014-03-13 08:32:11 +0000232 }
DRC0fc884a2011-05-24 09:13:17 +0000233
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;
DRC2e7b76b2009-04-03 12:04:24 +0000241}
242
DRC2e7b76b2009-04-03 12:04:24 +0000243
DRC0fc884a2011-05-24 09:13:17 +0000244int savebmp(char *filename, unsigned char *buf, int w, int h, int srcpf,
245 int bottomup)
DRC2e7b76b2009-04-03 12:04:24 +0000246{
DRC0fc884a2011-05-24 09:13:17 +0000247 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;
DRC2e7b76b2009-04-03 12:04:24 +0000253
DRC8e11a052011-10-18 22:07:13 +0000254 memset(&dinfo, 0, sizeof(struct jpeg_decompress_struct));
255
DRC0fc884a2011-05-24 09:13:17 +0000256 if(!filename || !buf || w<1 || h<1 || srcpf<0 || srcpf>=TJ_NUMPF)
257 _throw("savebmp(): Invalid argument");
DRC2e7b76b2009-04-03 12:04:24 +0000258
DRC0fc884a2011-05-24 09:13:17 +0000259 if((file=fopen(filename, "wb"))==NULL)
260 _throwunix("savebmp(): Cannot open output file");
DRC2e7b76b2009-04-03 12:04:24 +0000261
DRC0fc884a2011-05-24 09:13:17 +0000262 dinfo.err=jpeg_std_error(&jerr.pub);
263 jerr.pub.error_exit=my_error_exit;
264 jerr.pub.output_message=my_output_message;
DRC2e7b76b2009-04-03 12:04:24 +0000265
DRC0fc884a2011-05-24 09:13:17 +0000266 if(setjmp(jerr.setjmp_buffer))
DRC2e7b76b2009-04-03 12:04:24 +0000267 {
DRC0fc884a2011-05-24 09:13:17 +0000268 /* If we get here, the JPEG code has signaled an error. */
269 retval=-1; goto bailout;
DRC2e7b76b2009-04-03 12:04:24 +0000270 }
271
DRC0fc884a2011-05-24 09:13:17 +0000272 jpeg_create_decompress(&dinfo);
273 if(srcpf==TJPF_GRAY)
DRC2e7b76b2009-04-03 12:04:24 +0000274 {
DRC0fc884a2011-05-24 09:13:17 +0000275 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");
DRC2e7b76b2009-04-03 12:04:24 +0000297 }
298
DRCdf42b3c2014-03-13 08:32:11 +0000299 dst->output_file=file;
DRC0fc884a2011-05-24 09:13:17 +0000300 (*dst->start_output)(&dinfo, dst);
301 (*dinfo.mem->realize_virt_arrays)((j_common_ptr)&dinfo);
DRC2e7b76b2009-04-03 12:04:24 +0000302
DRC0fc884a2011-05-24 09:13:17 +0000303 if(srcpf==TJPF_GRAY) dstpf=srcpf;
304 else dstpf=TJPF_RGB;
305 srcps=tjPixelSize[srcpf];
DRC2e7b76b2009-04-03 12:04:24 +0000306
DRC0fc884a2011-05-24 09:13:17 +0000307 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;
DRCdf42b3c2014-03-13 08:32:11 +0000321 }
DRC2e7b76b2009-04-03 12:04:24 +0000322
DRC0fc884a2011-05-24 09:13:17 +0000323 (*dst->finish_output)(&dinfo, dst);
DRC2e7b76b2009-04-03 12:04:24 +0000324
DRC0fc884a2011-05-24 09:13:17 +0000325 bailout:
326 jpeg_destroy_decompress(&dinfo);
327 if(file) fclose(file);
328 return retval;
DRC2e7b76b2009-04-03 12:04:24 +0000329}
330
331const char *bmpgeterr(void)
332{
DRC0fc884a2011-05-24 09:13:17 +0000333 return errStr;
DRC2e7b76b2009-04-03 12:04:24 +0000334}