| /* |
| * 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; |
| } |