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 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 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 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 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 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 331 const char *bmpgeterr(void) 332 { 333 return errStr; 334 } 335