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 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 *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; 112 if(c<0.) c=0.; 113 if(m>1.0) m=1.0; 114 if(m<0.) m=0.; 115 if(y>1.0) y=1.0; 116 if(y<0.) y=0.; 117 if(k>1.0) k=1.0; 118 if(k<0.) k=0.; 119 *dstcolptr++=(unsigned char)(255.0-c*255.0+0.5); 120 *dstcolptr++=(unsigned char)(255.0-m*255.0+0.5); 121 *dstcolptr++=(unsigned char)(255.0-y*255.0+0.5); 122 *dstcolptr++=(unsigned char)(255.0-k*255.0+0.5); 123 } 124 } 125 } 126 else if(srcpf==TJPF_CMYK) 127 { 128 for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride) 129 { 130 for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr; 131 col<w; col++, dstcolptr+=dstps) 132 { 133 double c=(double)(*srccolptr++); 134 double m=(double)(*srccolptr++); 135 double y=(double)(*srccolptr++); 136 double k=(double)(*srccolptr++); 137 double r=c*k/255.; 138 double g=m*k/255.; 139 double b=y*k/255.; 140 if(r>255.0) r=255.0; 141 if(r<0.) r=0.; 142 if(g>255.0) g=255.0; 143 if(g<0.) g=0.; 144 if(b>255.0) b=255.0; 145 if(b<0.) b=0.; 146 dstcolptr[tjRedOffset[dstpf]]=(unsigned char)(r+0.5); 147 dstcolptr[tjGreenOffset[dstpf]]=(unsigned char)(g+0.5); 148 dstcolptr[tjBlueOffset[dstpf]]=(unsigned char)(b+0.5); 149 } 150 } 151 } 152 else 153 { 154 for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride) 155 { 156 for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr; 157 col<w; col++, srccolptr+=srcps, dstcolptr+=dstps) 158 { 159 dstcolptr[tjRedOffset[dstpf]]=srccolptr[tjRedOffset[srcpf]]; 160 dstcolptr[tjGreenOffset[dstpf]]=srccolptr[tjGreenOffset[srcpf]]; 161 dstcolptr[tjBlueOffset[dstpf]]=srccolptr[tjBlueOffset[srcpf]]; 162 } 163 } 164 } 165} 166 167 168int loadbmp(char *filename, unsigned char **buf, int *w, int *h, 169 int dstpf, int bottomup) 170{ 171 int retval=0, dstps, srcpf, tempc; 172 struct jpeg_compress_struct cinfo; 173 struct my_error_mgr jerr; 174 cjpeg_source_ptr src; 175 FILE *file=NULL; 176 177 memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); 178 179 if(!filename || !buf || !w || !h || dstpf<0 || dstpf>=TJ_NUMPF) 180 _throw("loadbmp(): Invalid argument"); 181 182 if((file=fopen(filename, "rb"))==NULL) 183 _throwunix("loadbmp(): Cannot open input file"); 184 185 cinfo.err=jpeg_std_error(&jerr.pub); 186 jerr.pub.error_exit=my_error_exit; 187 jerr.pub.output_message=my_output_message; 188 189 if(setjmp(jerr.setjmp_buffer)) 190 { 191 /* If we get here, the JPEG code has signaled an error. */ 192 retval=-1; goto bailout; 193 } 194 195 jpeg_create_compress(&cinfo); 196 if((tempc=getc(file))<0 || ungetc(tempc, file)==EOF) 197 _throwunix("loadbmp(): Could not read input file") 198 else if(tempc==EOF) _throw("loadbmp(): Input file contains no data"); 199 200 if(tempc=='B') 201 { 202 if((src=jinit_read_bmp(&cinfo))==NULL) 203 _throw("loadbmp(): Could not initialize bitmap loader"); 204 } 205 else if(tempc=='P') 206 { 207 if((src=jinit_read_ppm(&cinfo))==NULL) 208 _throw("loadbmp(): Could not initialize bitmap loader"); 209 } 210 else _throw("loadbmp(): Unsupported file type"); 211 212 src->input_file=file; 213 (*src->start_input)(&cinfo, src); 214 (*cinfo.mem->realize_virt_arrays)((j_common_ptr)&cinfo); 215 216 *w=cinfo.image_width; *h=cinfo.image_height; 217 218 if(cinfo.input_components==1 && cinfo.in_color_space==JCS_RGB) 219 srcpf=TJPF_GRAY; 220 else srcpf=TJPF_RGB; 221 222 dstps=tjPixelSize[dstpf]; 223 if((*buf=(unsigned char *)malloc((*w)*(*h)*dstps))==NULL) 224 _throw("loadbmp(): Memory allocation failure"); 225 226 while(cinfo.next_scanline<cinfo.image_height) 227 { 228 int i, nlines=(*src->get_pixel_rows)(&cinfo, src); 229 for(i=0; i<nlines; i++) 230 { 231 unsigned char *outbuf; int row; 232 row=cinfo.next_scanline+i; 233 if(bottomup) outbuf=&(*buf)[((*h)-row-1)*(*w)*dstps]; 234 else outbuf=&(*buf)[row*(*w)*dstps]; 235 pixelconvert(src->buffer[i], srcpf, 0, outbuf, dstpf, bottomup, *w, 236 nlines); 237 } 238 cinfo.next_scanline+=nlines; 239 } 240 241 (*src->finish_input)(&cinfo, src); 242 243 bailout: 244 jpeg_destroy_compress(&cinfo); 245 if(file) fclose(file); 246 if(retval<0 && buf && *buf) {free(*buf); *buf=NULL;} 247 return retval; 248} 249 250 251int savebmp(char *filename, unsigned char *buf, int w, int h, int srcpf, 252 int bottomup) 253{ 254 int retval=0, srcps, dstpf; 255 struct jpeg_decompress_struct dinfo; 256 struct my_error_mgr jerr; 257 djpeg_dest_ptr dst; 258 FILE *file=NULL; 259 char *ptr=NULL; 260 261 memset(&dinfo, 0, sizeof(struct jpeg_decompress_struct)); 262 263 if(!filename || !buf || w<1 || h<1 || srcpf<0 || srcpf>=TJ_NUMPF) 264 _throw("savebmp(): Invalid argument"); 265 266 if((file=fopen(filename, "wb"))==NULL) 267 _throwunix("savebmp(): Cannot open output file"); 268 269 dinfo.err=jpeg_std_error(&jerr.pub); 270 jerr.pub.error_exit=my_error_exit; 271 jerr.pub.output_message=my_output_message; 272 273 if(setjmp(jerr.setjmp_buffer)) 274 { 275 /* If we get here, the JPEG code has signaled an error. */ 276 retval=-1; goto bailout; 277 } 278 279 jpeg_create_decompress(&dinfo); 280 if(srcpf==TJPF_GRAY) 281 { 282 dinfo.out_color_components=dinfo.output_components=1; 283 dinfo.out_color_space=JCS_GRAYSCALE; 284 } 285 else 286 { 287 dinfo.out_color_components=dinfo.output_components=3; 288 dinfo.out_color_space=JCS_RGB; 289 } 290 dinfo.image_width=w; dinfo.image_height=h; 291 dinfo.global_state=DSTATE_READY; 292 dinfo.scale_num=dinfo.scale_denom=1; 293 294 ptr=strrchr(filename, '.'); 295 if(ptr && !strcasecmp(ptr, ".bmp")) 296 { 297 if((dst=jinit_write_bmp(&dinfo, 0))==NULL) 298 _throw("savebmp(): Could not initialize bitmap writer"); 299 } 300 else 301 { 302 if((dst=jinit_write_ppm(&dinfo))==NULL) 303 _throw("savebmp(): Could not initialize PPM writer"); 304 } 305 306 dst->output_file=file; 307 (*dst->start_output)(&dinfo, dst); 308 (*dinfo.mem->realize_virt_arrays)((j_common_ptr)&dinfo); 309 310 if(srcpf==TJPF_GRAY) dstpf=srcpf; 311 else dstpf=TJPF_RGB; 312 srcps=tjPixelSize[srcpf]; 313 314 while(dinfo.output_scanline<dinfo.output_height) 315 { 316 int i, nlines=dst->buffer_height; 317 for(i=0; i<nlines; i++) 318 { 319 unsigned char *inbuf; int row; 320 row=dinfo.output_scanline+i; 321 if(bottomup) inbuf=&buf[(h-row-1)*w*srcps]; 322 else inbuf=&buf[row*w*srcps]; 323 pixelconvert(inbuf, srcpf, bottomup, dst->buffer[i], dstpf, 0, w, 324 nlines); 325 } 326 (*dst->put_pixel_rows)(&dinfo, dst, nlines); 327 dinfo.output_scanline+=nlines; 328 } 329 330 (*dst->finish_output)(&dinfo, dst); 331 332 bailout: 333 jpeg_destroy_decompress(&dinfo); 334 if(file) fclose(file); 335 return retval; 336} 337 338const char *bmpgeterr(void) 339{ 340 return errStr; 341} 342