/* * Copyright (C)2011, 2015 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the libjpeg-turbo Project nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <string.h> #include <setjmp.h> #include <errno.h> #include "cdjpeg.h" #include <jpeglib.h> #include <jpegint.h> #include "tjutil.h" #include "bmp.h" /* This duplicates the functionality of the VirtualGL bitmap library using the components from cjpeg and djpeg */ /* Error handling (based on example in example.c) */ static char errStr[JMSG_LENGTH_MAX]="No error"; struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr *my_error_ptr; static void my_error_exit(j_common_ptr cinfo) { my_error_ptr myerr=(my_error_ptr)cinfo->err; (*cinfo->err->output_message)(cinfo); longjmp(myerr->setjmp_buffer, 1); } /* Based on output_message() in jerror.c */ static void my_output_message(j_common_ptr cinfo) { (*cinfo->err->format_message)(cinfo, errStr); } #define _throw(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \ retval=-1; goto bailout;} #define _throwunix(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s\n%s", m, \ strerror(errno)); retval=-1; goto bailout;} static void pixelconvert(unsigned char *srcbuf, int srcpf, int srcbottomup, unsigned char *dstbuf, int dstpf, int dstbottomup, int w, int h) { unsigned char *srcrowptr=srcbuf, *srccolptr; int srcps=tjPixelSize[srcpf]; int srcstride=srcbottomup? -w*srcps:w*srcps; unsigned char *dstrowptr=dstbuf, *dstcolptr; int dstps=tjPixelSize[dstpf]; int dststride=dstbottomup? -w*dstps:w*dstps; int row, col; if(srcbottomup) srcrowptr=&srcbuf[w*srcps*(h-1)]; if(dstbottomup) dstrowptr=&dstbuf[w*dstps*(h-1)]; /* NOTE: These quick & dirty CMYK<->RGB conversion routines are for testing purposes only. Properly converting between CMYK and RGB requires a color management system. */ if(dstpf==TJPF_CMYK) { for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride) { for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr; col<w; col++, srccolptr+=srcps) { double c=1.0-((double)(srccolptr[tjRedOffset[srcpf]])/255.); double m=1.0-((double)(srccolptr[tjGreenOffset[srcpf]])/255.); double y=1.0-((double)(srccolptr[tjBlueOffset[srcpf]])/255.); double k=min(min(c,m),min(y,1.0)); if(k==1.0) c=m=y=0.0; else { c=(c-k)/(1.0-k); m=(m-k)/(1.0-k); y=(y-k)/(1.0-k); } if(c>1.0) c=1.0; if(c<0.) c=0.; if(m>1.0) m=1.0; if(m<0.) m=0.; if(y>1.0) y=1.0; if(y<0.) y=0.; if(k>1.0) k=1.0; if(k<0.) k=0.; *dstcolptr++=(unsigned char)(255.0-c*255.0+0.5); *dstcolptr++=(unsigned char)(255.0-m*255.0+0.5); *dstcolptr++=(unsigned char)(255.0-y*255.0+0.5); *dstcolptr++=(unsigned char)(255.0-k*255.0+0.5); } } } else if(srcpf==TJPF_CMYK) { for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride) { for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr; col<w; col++, dstcolptr+=dstps) { double c=(double)(*srccolptr++); double m=(double)(*srccolptr++); double y=(double)(*srccolptr++); double k=(double)(*srccolptr++); double r=c*k/255.; double g=m*k/255.; double b=y*k/255.; if(r>255.0) r=255.0; if(r<0.) r=0.; if(g>255.0) g=255.0; if(g<0.) g=0.; if(b>255.0) b=255.0; if(b<0.) b=0.; dstcolptr[tjRedOffset[dstpf]]=(unsigned char)(r+0.5); dstcolptr[tjGreenOffset[dstpf]]=(unsigned char)(g+0.5); dstcolptr[tjBlueOffset[dstpf]]=(unsigned char)(b+0.5); } } } else { for(row=0; row<h; row++, srcrowptr+=srcstride, dstrowptr+=dststride) { for(col=0, srccolptr=srcrowptr, dstcolptr=dstrowptr; col<w; col++, srccolptr+=srcps, dstcolptr+=dstps) { dstcolptr[tjRedOffset[dstpf]]=srccolptr[tjRedOffset[srcpf]]; dstcolptr[tjGreenOffset[dstpf]]=srccolptr[tjGreenOffset[srcpf]]; dstcolptr[tjBlueOffset[dstpf]]=srccolptr[tjBlueOffset[srcpf]]; } } } } int loadbmp(char *filename, unsigned char **buf, int *w, int *h, int dstpf, int bottomup) { int retval=0, dstps, srcpf, tempc; struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; cjpeg_source_ptr src; FILE *file=NULL; memset(&cinfo, 0, sizeof(struct jpeg_compress_struct)); if(!filename || !buf || !w || !h || dstpf<0 || dstpf>=TJ_NUMPF) _throw("loadbmp(): Invalid argument"); if((file=fopen(filename, "rb"))==NULL) _throwunix("loadbmp(): Cannot open input file"); cinfo.err=jpeg_std_error(&jerr.pub); jerr.pub.error_exit=my_error_exit; jerr.pub.output_message=my_output_message; if(setjmp(jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ retval=-1; goto bailout; } jpeg_create_compress(&cinfo); if((tempc=getc(file))<0 || ungetc(tempc, file)==EOF) _throwunix("loadbmp(): Could not read input file") else if(tempc==EOF) _throw("loadbmp(): Input file contains no data"); if(tempc=='B') { if((src=jinit_read_bmp(&cinfo))==NULL) _throw("loadbmp(): Could not initialize bitmap loader"); } else if(tempc=='P') { if((src=jinit_read_ppm(&cinfo))==NULL) _throw("loadbmp(): Could not initialize bitmap loader"); } else _throw("loadbmp(): Unsupported file type"); src->input_file=file; (*src->start_input)(&cinfo, src); (*cinfo.mem->realize_virt_arrays)((j_common_ptr)&cinfo); *w=cinfo.image_width; *h=cinfo.image_height; if(cinfo.input_components==1 && cinfo.in_color_space==JCS_RGB) srcpf=TJPF_GRAY; else srcpf=TJPF_RGB; dstps=tjPixelSize[dstpf]; if((*buf=(unsigned char *)malloc((*w)*(*h)*dstps))==NULL) _throw("loadbmp(): Memory allocation failure"); while(cinfo.next_scanline<cinfo.image_height) { int i, nlines=(*src->get_pixel_rows)(&cinfo, src); for(i=0; i<nlines; i++) { unsigned char *outbuf; int row; row=cinfo.next_scanline+i; if(bottomup) outbuf=&(*buf)[((*h)-row-1)*(*w)*dstps]; else outbuf=&(*buf)[row*(*w)*dstps]; pixelconvert(src->buffer[i], srcpf, 0, outbuf, dstpf, bottomup, *w, nlines); } cinfo.next_scanline+=nlines; } (*src->finish_input)(&cinfo, src); bailout: jpeg_destroy_compress(&cinfo); if(file) fclose(file); if(retval<0 && buf && *buf) {free(*buf); *buf=NULL;} return retval; } int savebmp(char *filename, unsigned char *buf, int w, int h, int srcpf, int bottomup) { int retval=0, srcps, dstpf; struct jpeg_decompress_struct dinfo; struct my_error_mgr jerr; djpeg_dest_ptr dst; FILE *file=NULL; char *ptr=NULL; memset(&dinfo, 0, sizeof(struct jpeg_decompress_struct)); if(!filename || !buf || w<1 || h<1 || srcpf<0 || srcpf>=TJ_NUMPF) _throw("savebmp(): Invalid argument"); if((file=fopen(filename, "wb"))==NULL) _throwunix("savebmp(): Cannot open output file"); dinfo.err=jpeg_std_error(&jerr.pub); jerr.pub.error_exit=my_error_exit; jerr.pub.output_message=my_output_message; if(setjmp(jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ retval=-1; goto bailout; } jpeg_create_decompress(&dinfo); if(srcpf==TJPF_GRAY) { dinfo.out_color_components=dinfo.output_components=1; dinfo.out_color_space=JCS_GRAYSCALE; } else { dinfo.out_color_components=dinfo.output_components=3; dinfo.out_color_space=JCS_RGB; } dinfo.image_width=w; dinfo.image_height=h; dinfo.global_state=DSTATE_READY; dinfo.scale_num=dinfo.scale_denom=1; ptr=strrchr(filename, '.'); if(ptr && !strcasecmp(ptr, ".bmp")) { if((dst=jinit_write_bmp(&dinfo, 0))==NULL) _throw("savebmp(): Could not initialize bitmap writer"); } else { if((dst=jinit_write_ppm(&dinfo))==NULL) _throw("savebmp(): Could not initialize PPM writer"); } dst->output_file=file; (*dst->start_output)(&dinfo, dst); (*dinfo.mem->realize_virt_arrays)((j_common_ptr)&dinfo); if(srcpf==TJPF_GRAY) dstpf=srcpf; else dstpf=TJPF_RGB; srcps=tjPixelSize[srcpf]; while(dinfo.output_scanline<dinfo.output_height) { int i, nlines=dst->buffer_height; for(i=0; i<nlines; i++) { unsigned char *inbuf; int row; row=dinfo.output_scanline+i; if(bottomup) inbuf=&buf[(h-row-1)*w*srcps]; else inbuf=&buf[row*w*srcps]; pixelconvert(inbuf, srcpf, bottomup, dst->buffer[i], dstpf, 0, w, nlines); } (*dst->put_pixel_rows)(&dinfo, dst, nlines); dinfo.output_scanline+=nlines; } (*dst->finish_output)(&dinfo, dst); bailout: jpeg_destroy_decompress(&dinfo); if(file) fclose(file); return retval; } const char *bmpgeterr(void) { return errStr; }