]> granicus.if.org Git - libjpeg-turbo/commitdiff
TurboJPEG C API: Add BMP/PPM load/save functions
authorDRC <information@libjpeg-turbo.org>
Fri, 17 Nov 2017 00:09:07 +0000 (18:09 -0600)
committerDRC <information@libjpeg-turbo.org>
Sat, 18 Nov 2017 01:32:52 +0000 (19:32 -0600)
The main justification for this is to provide new libjpeg-turbo users
with a quick & easy way of developing a complete JPEG
compression/decompression program without requiring them to build
libjpeg-turbo from source (which was necessary in order to use the
project-private bmp API) or to use external libraries.  These new
functions build upon significant enhancements to rdbmp.c, wrbmp.c,
rdppm.c, and wrppm.c which allow those engines to convert directly
between the native pixel format of the file and a pixel format
("colorspace" in libjpeg parlance) specified by the calling program.
rdbmp.c and wrbmp.c have also been modified such that the calling
program can choose to read or write image rows in the native (bottom-up)
order of the file format, thus eliminating the need to use an inversion
array.  tjLoadImage() and tjSaveImage() leverage these new underlying
features in order to significantly improve upon the performance of the
old bmp API.

Because these new functions cannot work without the libjpeg-turbo
colorspace extensions, the libjpeg-compatible code in turbojpeg.c has
been removed.  That code was only there to serve as an example of how
to use the TurboJPEG API on top of libjpeg, but more specific, buildable
examples now exist in the https://github.com/libjpeg-turbo/ijg
repository.

24 files changed:
CMakeLists.txt
ChangeLog.md
bmp.c [deleted file]
bmp.h [deleted file]
cdjpeg.h
cjpeg.c
cmyk.h [new file with mode: 0644]
djpeg.c
doc/html/group___turbo_j_p_e_g.html
doc/html/search/all_74.js
doc/html/search/enumvalues_74.js
doc/html/search/functions_74.js
md5/md5.h
md5/md5cmp.c
rdbmp.c
rdppm.c
tjbench.c
tjunittest.c
turbojpeg-mapfile
turbojpeg-mapfile.jni
turbojpeg.c
turbojpeg.h
wrbmp.c
wrppm.c

index 943c155585aea4ae8efdf9076fa56f2b649a66e0..fed6a4cca28530e3f74066ccc9520e6b1e8c12b2 100644 (file)
@@ -541,7 +541,8 @@ endif()
 if(WITH_TURBOJPEG)
   if(ENABLE_SHARED)
     set(TURBOJPEG_SOURCES ${JPEG_SOURCES} $<TARGET_OBJECTS:simd> ${SIMD_OBJS}
-      turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c)
+      turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c rdbmp.c rdppm.c
+      wrbmp.c wrppm.c)
     set(TJMAPFILE ${CMAKE_CURRENT_SOURCE_DIR}/turbojpeg-mapfile)
     if(WITH_JAVA)
       set(TURBOJPEG_SOURCES ${TURBOJPEG_SOURCES} turbojpeg-jni.c)
@@ -549,6 +550,8 @@ if(WITH_TURBOJPEG)
       set(TJMAPFILE ${CMAKE_CURRENT_SOURCE_DIR}/turbojpeg-mapfile.jni)
     endif()
     add_library(turbojpeg SHARED ${TURBOJPEG_SOURCES})
+    set_property(TARGET turbojpeg PROPERTY COMPILE_FLAGS
+      "-DBMP_SUPPORTED -DPPM_SUPPORTED")
     if(WIN32)
       set_target_properties(turbojpeg PROPERTIES DEFINE_SYMBOL DLLDEFINE)
     endif()
@@ -565,37 +568,35 @@ if(WITH_TURBOJPEG)
         LINK_FLAGS "${TJMAPFLAG}${TJMAPFILE}")
     endif()
 
-    add_executable(tjunittest tjunittest.c tjutil.c)
+    add_executable(tjunittest tjunittest.c tjutil.c md5/md5.c md5/md5hl.c)
     target_link_libraries(tjunittest turbojpeg)
 
-    add_executable(tjbench tjbench.c bmp.c tjutil.c rdbmp.c rdppm.c wrbmp.c
-      wrppm.c)
-    target_link_libraries(tjbench turbojpeg jpeg)
+    add_executable(tjbench tjbench.c tjutil.c)
+    target_link_libraries(tjbench turbojpeg)
     if(UNIX)
       target_link_libraries(tjbench m)
     endif()
-    set_property(TARGET tjbench PROPERTY COMPILE_FLAGS
-      "-DBMP_SUPPORTED -DPPM_SUPPORTED")
   endif()
 
   if(ENABLE_STATIC)
     add_library(turbojpeg-static STATIC ${JPEG_SOURCES} $<TARGET_OBJECTS:simd>
-      ${SIMD_OBJS} turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c)
+      ${SIMD_OBJS} turbojpeg.c transupp.c jdatadst-tj.c jdatasrc-tj.c rdbmp.c
+      rdppm.c wrbmp.c wrppm.c)
+    set_property(TARGET turbojpeg-static PROPERTY COMPILE_FLAGS
+      "-DBMP_SUPPORTED -DPPM_SUPPORTED")
     if(NOT MSVC)
       set_target_properties(turbojpeg-static PROPERTIES OUTPUT_NAME turbojpeg)
     endif()
 
-    add_executable(tjunittest-static tjunittest.c tjutil.c)
+    add_executable(tjunittest-static tjunittest.c tjutil.c md5/md5.c
+      md5/md5hl.c)
     target_link_libraries(tjunittest-static turbojpeg-static)
 
-    add_executable(tjbench-static tjbench.c bmp.c tjutil.c rdbmp.c rdppm.c
-      wrbmp.c wrppm.c)
-    target_link_libraries(tjbench-static turbojpeg-static jpeg-static)
+    add_executable(tjbench-static tjbench.c tjutil.c)
+    target_link_libraries(tjbench-static turbojpeg-static)
     if(UNIX)
       target_link_libraries(tjbench-static m)
     endif()
-    set_property(TARGET tjbench-static PROPERTY COMPILE_FLAGS
-      "-DBMP_SUPPORTED -DPPM_SUPPORTED")
   endif()
 endif()
 
@@ -864,6 +865,7 @@ foreach(libtype ${TEST_LIBTYPES})
     add_test(tjunittest-${libtype}-yuv tjunittest${suffix} -yuv)
     add_test(tjunittest-${libtype}-yuv-alloc tjunittest${suffix} -yuv -alloc)
     add_test(tjunittest-${libtype}-yuv-nopad tjunittest${suffix} -yuv -noyuvpad)
+    add_test(tjunittest-${libtype}-bmp tjunittest${suffix} -bmp)
 
     set(MD5_PPM_GRAY_TILE 89d3ca21213d9d864b50b4e4e7de4ca6)
     set(MD5_PPM_420_8x8_TILE 847fceab15c5b7b911cb986cf0f71de3)
index 2ac91e24454e7b10709ff020b8a08395904c8862..11dcd1634cfd94b4fd5ad94f7032772b519aa641 100644 (file)
@@ -102,6 +102,14 @@ program was used to decompress an existing JPEG image.
 occurred when attempting to decompress a JPEG image that had been compressed
 with 4:1:1 chrominance subsampling.
 
+14. Added two functions to the TurboJPEG C API (`tjLoadImage()` and
+`tjSaveImage()`) that can be used to load/save a BMP or PPM/PGM image to/from a
+memory buffer with a specified pixel format and layout.  These functions
+replace the project-private (and slow) bmp API, which was previously used by
+TJBench, and they also provide a convenient way for first-time users of
+libjpeg-turbo to quickly develop a complete JPEG compression/decompression
+program.
+
 
 1.5.2
 =====
diff --git a/bmp.c b/bmp.c
deleted file mode 100644 (file)
index 2b8e80c..0000000
--- a/bmp.c
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * 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;
-}
diff --git a/bmp.h b/bmp.h
deleted file mode 100644 (file)
index c50c260..0000000
--- a/bmp.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C)2011 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.
- */
-
-#ifndef __BMP_H__
-#define __BMP_H__
-
-#include "./turbojpeg.h"
-
-int loadbmp(char *filename, unsigned char **buf, int *w, int *h, int pf,
-       int bottomup);
-
-int savebmp(char *filename, unsigned char *buf, int w, int h, int pf,
-       int bottomup);
-
-const char *bmpgeterr(void);
-
-#endif
index bb49fbfab2816730e40c20ce2e09a84bbe92f070..8c3b3b5d532022b08f597821aba3f07586c41660 100644 (file)
--- a/cdjpeg.h
+++ b/cdjpeg.h
@@ -96,9 +96,11 @@ typedef struct cdjpeg_progress_mgr *cd_progress_ptr;
 
 /* Module selection routines for I/O modules. */
 
-EXTERN(cjpeg_source_ptr) jinit_read_bmp (j_compress_ptr cinfo);
+EXTERN(cjpeg_source_ptr) jinit_read_bmp (j_compress_ptr cinfo,
+                                         boolean use_inversion_array);
 EXTERN(djpeg_dest_ptr) jinit_write_bmp (j_decompress_ptr cinfo,
-                                        boolean is_os2);
+                                        boolean is_os2,
+                                        boolean use_inversion_array);
 EXTERN(cjpeg_source_ptr) jinit_read_gif (j_compress_ptr cinfo);
 EXTERN(djpeg_dest_ptr) jinit_write_gif (j_decompress_ptr cinfo);
 EXTERN(cjpeg_source_ptr) jinit_read_ppm (j_compress_ptr cinfo);
@@ -151,3 +153,10 @@ EXTERN(FILE *) write_stdout (void);
 #ifndef EXIT_WARNING
 #define EXIT_WARNING  2
 #endif
+
+#define RGB2GRAY(r, g, b)  \
+  (JSAMPLE)((double)(r) * 0.299 + (double)(g) * 0.587 +  \
+            (double)(b) * 0.114 + 0.5)
+
+#define IsExtRGB(cs)  \
+  (cs == JCS_RGB || (cs >= JCS_EXT_RGB && cs <= JCS_EXT_ARGB))
diff --git a/cjpeg.c b/cjpeg.c
index da9af7319233e207b669a941451f0c67f285a564..90f8b867e4dadcf4dae45e8527a631a9b53bcde6 100644 (file)
--- a/cjpeg.c
+++ b/cjpeg.c
@@ -107,7 +107,7 @@ select_file_type (j_compress_ptr cinfo, FILE *infile)
   switch (c) {
 #ifdef BMP_SUPPORTED
   case 'B':
-    return jinit_read_bmp(cinfo);
+    return jinit_read_bmp(cinfo, TRUE);
 #endif
 #ifdef GIF_SUPPORTED
   case 'G':
diff --git a/cmyk.h b/cmyk.h
new file mode 100644 (file)
index 0000000..3e8fcb8
--- /dev/null
+++ b/cmyk.h
@@ -0,0 +1,64 @@
+/*
+ * cmyk.h
+ *
+ * Copyright (C) 2017, D. R. Commander.
+ * For conditions of distribution and use, see the accompanying README.ijg
+ * file.
+ *
+ * This file contains convenience functions for performing quick & dirty
+ * CMYK<->RGB conversion.  This algorithm is suitable for testing purposes
+ * only.  Properly converting between CMYK and RGB requires a color management
+ * system.
+ */
+
+#include <jpeglib.h>
+#include "jconfigint.h"
+
+#ifndef CMYK_H
+#define CMYK_H
+
+
+#ifndef min
+ #define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+
+/* Fully reversible */
+
+INLINE
+LOCAL(void)
+rgb_to_cmyk(JSAMPLE r, JSAMPLE g, JSAMPLE b, JSAMPLE *c, JSAMPLE *m,
+            JSAMPLE *y, JSAMPLE *k)
+{
+  double ctmp = 1.0 - ((double)r / 255.0);
+  double mtmp = 1.0 - ((double)g / 255.0);
+  double ytmp = 1.0 - ((double)b / 255.0);
+  double ktmp = min(min(ctmp, mtmp), ytmp);
+
+  if (ktmp == 1.0) ctmp = mtmp = ytmp = 0.0;
+  else {
+    ctmp = (ctmp - ktmp) / (1.0 - ktmp);
+    mtmp = (mtmp - ktmp) / (1.0 - ktmp);
+    ytmp = (ytmp - ktmp) / (1.0 - ktmp);
+  }
+  *c = (JSAMPLE)(255.0 - ctmp * 255.0 + 0.5);
+  *m = (JSAMPLE)(255.0 - mtmp * 255.0 + 0.5);
+  *y = (JSAMPLE)(255.0 - ytmp * 255.0 + 0.5);
+  *k = (JSAMPLE)(255.0 - ktmp * 255.0 + 0.5);
+}
+
+
+/* Fully reversible only for C/M/Y/K values generated with rgb_to_cmyk() */
+
+INLINE
+LOCAL(void)
+cmyk_to_rgb(JSAMPLE c, JSAMPLE m, JSAMPLE y, JSAMPLE k, JSAMPLE *r, JSAMPLE *g,
+            JSAMPLE *b)
+{
+  *r = (JSAMPLE)((double)c * (double)k / 255.0 + 0.5);
+  *g = (JSAMPLE)((double)m * (double)k / 255.0 + 0.5);
+  *b = (JSAMPLE)((double)y * (double)k / 255.0 + 0.5);
+}
+
+
+#endif /* CMYK_H */
diff --git a/djpeg.c b/djpeg.c
index 2fbb0b792de88bd0e37fa07698b573290cf9a6c2..78bfb6f68a490207745dc35cf8f05c80ffc61d44 100644 (file)
--- a/djpeg.c
+++ b/djpeg.c
@@ -642,10 +642,10 @@ main (int argc, char **argv)
   switch (requested_fmt) {
 #ifdef BMP_SUPPORTED
   case FMT_BMP:
-    dest_mgr = jinit_write_bmp(&cinfo, FALSE);
+    dest_mgr = jinit_write_bmp(&cinfo, FALSE, TRUE);
     break;
   case FMT_OS2:
-    dest_mgr = jinit_write_bmp(&cinfo, TRUE);
+    dest_mgr = jinit_write_bmp(&cinfo, TRUE, TRUE);
     break;
 #endif
 #ifdef GIF_SUPPORTED
index c2ae185adbe4f0100879873931b0d9e6f4e246b8..402b858b2a7a9fa146392c96e405a84081c66d33 100644 (file)
@@ -205,7 +205,9 @@ Enumerations</h2></td></tr>
 &#160;&#160;<a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aac037ff1845cf9b74bb81a3659c2b9fb4">TJPF_BGRA</a>, 
 <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa1ba1a7f1631dbeaa49a0a85fc4a40081">TJPF_ABGR</a>, 
 <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aae8f846ed9d9de99b6e1dfe448848765c">TJPF_ARGB</a>, 
-<a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b">TJPF_CMYK</a>
+<a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b">TJPF_CMYK</a>, 
+<br/>
+&#160;&#160;<a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed">TJPF_UNKNOWN</a>
 <br/>
  }</td></tr>
 <tr class="memdesc:gac916144e26c3817ac514e64ae5d12e2a"><td class="mdescLeft">&#160;</td><td class="mdescRight">Pixel formats.  <a href="group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a">More...</a><br/></td></tr>
@@ -312,6 +314,12 @@ Functions</h2></td></tr>
 <tr class="memitem:ga5c9234bda6d993cdaffdd89bf81a00ff"><td class="memItemLeft" align="right" valign="top">DLLEXPORT unsigned char *DLLCALL&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="group___turbo_j_p_e_g.html#ga5c9234bda6d993cdaffdd89bf81a00ff">tjAlloc</a> (int bytes)</td></tr>
 <tr class="memdesc:ga5c9234bda6d993cdaffdd89bf81a00ff"><td class="mdescLeft">&#160;</td><td class="mdescRight">Allocate an image buffer for use with TurboJPEG.  <a href="#ga5c9234bda6d993cdaffdd89bf81a00ff">More...</a><br/></td></tr>
 <tr class="separator:ga5c9234bda6d993cdaffdd89bf81a00ff"><td class="memSeparator" colspan="2">&#160;</td></tr>
+<tr class="memitem:ga144b981d6b281ecca4cbb4709de75749"><td class="memItemLeft" align="right" valign="top">DLLEXPORT unsigned char *DLLCALL&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="group___turbo_j_p_e_g.html#ga144b981d6b281ecca4cbb4709de75749">tjLoadImage</a> (const char *filename, int *width, int align, int *height, int *pixelFormat, int flags)</td></tr>
+<tr class="memdesc:ga144b981d6b281ecca4cbb4709de75749"><td class="mdescLeft">&#160;</td><td class="mdescRight">Load an uncompressed image from disk into memory.  <a href="#ga144b981d6b281ecca4cbb4709de75749">More...</a><br/></td></tr>
+<tr class="separator:ga144b981d6b281ecca4cbb4709de75749"><td class="memSeparator" colspan="2">&#160;</td></tr>
+<tr class="memitem:ga2e78b7b79796e74584028da880a6a29c"><td class="memItemLeft" align="right" valign="top">DLLEXPORT int DLLCALL&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="group___turbo_j_p_e_g.html#ga2e78b7b79796e74584028da880a6a29c">tjSaveImage</a> (const char *filename, unsigned char *buffer, int width, int pitch, int height, int pixelFormat, int flags)</td></tr>
+<tr class="memdesc:ga2e78b7b79796e74584028da880a6a29c"><td class="mdescLeft">&#160;</td><td class="mdescRight">Save an uncompressed image from memory to disk.  <a href="#ga2e78b7b79796e74584028da880a6a29c">More...</a><br/></td></tr>
+<tr class="separator:ga2e78b7b79796e74584028da880a6a29c"><td class="memSeparator" colspan="2">&#160;</td></tr>
 <tr class="memitem:ga8c4a1231dc06a450514c835f6471f137"><td class="memItemLeft" align="right" valign="top">DLLEXPORT void DLLCALL&#160;</td><td class="memItemRight" valign="bottom"><a class="el" href="group___turbo_j_p_e_g.html#ga8c4a1231dc06a450514c835f6471f137">tjFree</a> (unsigned char *buffer)</td></tr>
 <tr class="memdesc:ga8c4a1231dc06a450514c835f6471f137"><td class="mdescLeft">&#160;</td><td class="mdescRight">Free an image buffer previously allocated by TurboJPEG.  <a href="#ga8c4a1231dc06a450514c835f6471f137">More...</a><br/></td></tr>
 <tr class="separator:ga8c4a1231dc06a450514c835f6471f137"><td class="memSeparator" colspan="2">&#160;</td></tr>
@@ -822,6 +830,10 @@ Variables</h2></td></tr>
 <p>CMYK pixel format. </p>
 <p>Unlike RGB, which is an additive color model used primarily for display, CMYK (Cyan/Magenta/Yellow/Key) is a subtractive color model used primarily for printing. In the CMYK color model, the value of each color component typically corresponds to an amount of cyan, magenta, yellow, or black ink that is applied to a white background. In order to convert between CMYK and RGB, it is necessary to use a color management system (CMS.) A CMS will attempt to map colors within the printer's gamut to perceptually similar colors in the display's gamut and vice versa, but the mapping is typically not 1:1 or reversible, nor can it be defined with a simple formula. Thus, such a conversion is out of scope for a codec library. However, the TurboJPEG API allows for compressing CMYK pixels into a YCCK JPEG image (see <a class="el" href="group___turbo_j_p_e_g.html#gga4f83ad3368e0e29d1957be0efa7c3720a53839e0fe867b76b58d16b0a1a7c598e" title="YCCK colorspace.">TJCS_YCCK</a>) and decompressing YCCK JPEG images into CMYK pixels. </p>
 </td></tr>
+<tr><td class="fieldname"><em><a class="anchor" id="ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed"></a>TJPF_UNKNOWN</em>&nbsp;</td><td class="fielddoc">
+<p>Unknown pixel format. </p>
+<p>Currently this is only used by <a class="el" href="group___turbo_j_p_e_g.html#ga144b981d6b281ecca4cbb4709de75749" title="Load an uncompressed image from disk into memory.">tjLoadImage()</a>. </p>
+</td></tr>
 </table>
 
 </div>
@@ -2202,6 +2214,70 @@ If you choose option 1, <code>*jpegSize</code> should be set to the size of your
 <p>Create a new TurboJPEG transformer instance. </p>
 <dl class="section return"><dt>Returns</dt><dd>a handle to the newly-created instance, or NULL if an error occurred (see <a class="el" href="group___turbo_j_p_e_g.html#ga94a235bd4f1088f61ad87b4eadb64c9c" title="Returns a descriptive error message explaining why the last command failed.">tjGetErrorStr2()</a>.) </dd></dl>
 
+</div>
+</div>
+<a class="anchor" id="ga144b981d6b281ecca4cbb4709de75749"></a>
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">DLLEXPORT unsigned char* DLLCALL tjLoadImage </td>
+          <td>(</td>
+          <td class="paramtype">const char *&#160;</td>
+          <td class="paramname"><em>filename</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int *&#160;</td>
+          <td class="paramname"><em>width</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>align</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int *&#160;</td>
+          <td class="paramname"><em>height</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int *&#160;</td>
+          <td class="paramname"><em>pixelFormat</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>flags</em>&#160;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td>
+        </tr>
+      </table>
+</div><div class="memdoc">
+
+<p>Load an uncompressed image from disk into memory. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+  <table class="params">
+    <tr><td class="paramname">filename</td><td>name of a file containing an uncompressed image in Windows BMP or PBMPLUS (PPM/PGM) format</td></tr>
+    <tr><td class="paramname">width</td><td>pointer to an integer variable that will receive the width (in pixels) of the uncompressed image</td></tr>
+    <tr><td class="paramname">align</td><td>row alignment of the image buffer to be returned (must be a power of 2.) For instance, setting this parameter to 4 will cause all rows in the image buffer to be padded to the nearest 32-bit boundary, and setting this parameter to 1 will cause all rows in the image buffer to be unpadded.</td></tr>
+    <tr><td class="paramname">height</td><td>pointer to an integer variable that will receive the height (in pixels) of the uncompressed image</td></tr>
+    <tr><td class="paramname">pixelFormat</td><td>pointer to an integer variable that specifies or will receive the pixel format of the uncompressed image buffer. If <code>*pixelFormat</code> is set to <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed">TJPF_UNKNOWN</a> prior to calling this function, then the uncompressed image buffer returned by the function will use the most optimal pixel format for the file type, and <code>*pixelFormat</code> will contain the ID of this pixel format upon successful return from the function. Otherwise, the uncompressed image buffer will use the <a class="el" href="group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a">pixel format</a> specified in <code>*pixelFormat</code>, and pixel format conversion will be performed if necessary. If <code>*pixelFormat</code> is set to <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b">TJPF_CMYK</a>, then the RGB or grayscale pixels stored in the file will be converted using a quick &amp; dirty algorithm that is suitable only for testing purposes (proper conversion between CMYK and other formats requires a color management system.)</td></tr>
+    <tr><td class="paramname">flags</td><td>the bitwise OR of one or more of the <a class="el" href="group___turbo_j_p_e_g.html#ga72ecf4ebe6eb702d3c6f5ca27455e1ec">flags</a>.</td></tr>
+  </table>
+  </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>a pointer to a newly-allocated buffer containing the uncompressed image, converted to the chosen pixel format and with the chosen row alignment, or NULL if an error occurred (see <a class="el" href="group___turbo_j_p_e_g.html#ga94a235bd4f1088f61ad87b4eadb64c9c" title="Returns a descriptive error message explaining why the last command failed.">tjGetErrorStr2()</a>.) This buffer should be freed using <a class="el" href="group___turbo_j_p_e_g.html#ga8c4a1231dc06a450514c835f6471f137" title="Free an image buffer previously allocated by TurboJPEG.">tjFree()</a>. </dd></dl>
+
 </div>
 </div>
 <a class="anchor" id="ga1a209696c6a80748f20e134b3c64789f"></a>
@@ -2347,6 +2423,77 @@ If you choose option 1, <code>*jpegSize</code> should be set to the size of your
 </dl>
 <dl class="section return"><dt>Returns</dt><dd>the plane width of a YUV image plane with the given parameters, or -1 if the arguments are out of bounds. </dd></dl>
 
+</div>
+</div>
+<a class="anchor" id="ga2e78b7b79796e74584028da880a6a29c"></a>
+<div class="memitem">
+<div class="memproto">
+      <table class="memname">
+        <tr>
+          <td class="memname">DLLEXPORT int DLLCALL tjSaveImage </td>
+          <td>(</td>
+          <td class="paramtype">const char *&#160;</td>
+          <td class="paramname"><em>filename</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">unsigned char *&#160;</td>
+          <td class="paramname"><em>buffer</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>width</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>pitch</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>height</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>pixelFormat</em>, </td>
+        </tr>
+        <tr>
+          <td class="paramkey"></td>
+          <td></td>
+          <td class="paramtype">int&#160;</td>
+          <td class="paramname"><em>flags</em>&#160;</td>
+        </tr>
+        <tr>
+          <td></td>
+          <td>)</td>
+          <td></td><td></td>
+        </tr>
+      </table>
+</div><div class="memdoc">
+
+<p>Save an uncompressed image from memory to disk. </p>
+<dl class="params"><dt>Parameters</dt><dd>
+  <table class="params">
+    <tr><td class="paramname">filename</td><td>name of a file to which to save the uncompressed image. The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format, depending on the file extension.</td></tr>
+    <tr><td class="paramname">buffer</td><td>pointer to an image buffer containing RGB, grayscale, or CMYK pixels to be saved</td></tr>
+    <tr><td class="paramname">width</td><td>width (in pixels) of the uncompressed image</td></tr>
+    <tr><td class="paramname">pitch</td><td>bytes per line in the image buffer. Setting this parameter to 0 is the equivalent of setting it to <code>width * <a class="el" href="group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c" title="Pixel size (in bytes) for a given pixel format.">tjPixelSize</a>[pixelFormat]</code>.</td></tr>
+    <tr><td class="paramname">height</td><td>height (in pixels) of the uncompressed image</td></tr>
+    <tr><td class="paramname">pixelFormat</td><td>pixel format of the image buffer (see <a class="el" href="group___turbo_j_p_e_g.html#gac916144e26c3817ac514e64ae5d12e2a">Pixel formats</a>.) If this parameter is set to <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa5431b54b015337705f13118073711a1a">TJPF_GRAY</a>, then the image will be stored in PGM or 8-bit (indexed color) BMP format. Otherwise, the image will be stored in PPM or 24-bit BMP format. If this parameter is set to <a class="el" href="group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7f5100ec44c91994e243f1cf55553f8b">TJPF_CMYK</a>, then the CMYK pixels will be converted to RGB using a quick &amp; dirty algorithm that is suitable only for testing (proper conversion between CMYK and other formats requires a color management system.)</td></tr>
+    <tr><td class="paramname">flags</td><td>the bitwise OR of one or more of the <a class="el" href="group___turbo_j_p_e_g.html#ga72ecf4ebe6eb702d3c6f5ca27455e1ec">flags</a>.</td></tr>
+  </table>
+  </dd>
+</dl>
+<dl class="section return"><dt>Returns</dt><dd>0 if successful, or -1 if an error occurred (see <a class="el" href="group___turbo_j_p_e_g.html#ga94a235bd4f1088f61ad87b4eadb64c9c" title="Returns a descriptive error message explaining why the last command failed.">tjGetErrorStr2()</a>.) </dd></dl>
+
 </div>
 </div>
 <a class="anchor" id="gad02cd42b69f193a0623a9c801788df3a"></a>
index a211e3d97c4cc8ee7fd87d0c023cc18bb8fdb4c7..e952119baf433cae466d2eee6c37ba95d20d64bf 100644 (file)
@@ -46,6 +46,7 @@ var searchData=
   ['tjinitcompress',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga3d10c47fbe4a2489a2b30c931551d01a',1,'turbojpeg.h']]],
   ['tjinitdecompress',['tjInitDecompress',['../group___turbo_j_p_e_g.html#gae5408179d041e2a2f7199c8283cf649e',1,'turbojpeg.h']]],
   ['tjinittransform',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga3155b775bfbac9dbba869b95a0367902',1,'turbojpeg.h']]],
+  ['tjloadimage',['tjLoadImage',['../group___turbo_j_p_e_g.html#ga144b981d6b281ecca4cbb4709de75749',1,'turbojpeg.h']]],
   ['tjmcuheight',['tjMCUHeight',['../group___turbo_j_p_e_g.html#gabd247bb9fecb393eca57366feb8327bf',1,'turbojpeg.h']]],
   ['tjmcuwidth',['tjMCUWidth',['../group___turbo_j_p_e_g.html#ga9e61e7cd47a15a173283ba94e781308c',1,'turbojpeg.h']]],
   ['tjpad',['TJPAD',['../group___turbo_j_p_e_g.html#ga0aba955473315e405295d978f0c16511',1,'turbojpeg.h']]],
@@ -60,6 +61,7 @@ var searchData=
   ['tjpf_5frgb',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]],
   ['tjpf_5frgba',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]],
   ['tjpf_5frgbx',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]],
+  ['tjpf_5funknown',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]],
   ['tjpf_5fxbgr',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]],
   ['tjpf_5fxrgb',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]],
   ['tjpixelsize',['tjPixelSize',['../group___turbo_j_p_e_g.html#gad77cf8fe5b2bfd3cb3f53098146abb4c',1,'turbojpeg.h']]],
@@ -75,6 +77,7 @@ var searchData=
   ['tjsamp_5f440',['TJSAMP_440',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074accf740e6f3aa6ba20ba922cad13cb974',1,'turbojpeg.h']]],
   ['tjsamp_5f444',['TJSAMP_444',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074afb8da4f44197837bdec0a4f593dacae3',1,'turbojpeg.h']]],
   ['tjsamp_5fgray',['TJSAMP_GRAY',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a3f1c9504842ddc7a48d0f690754b6248',1,'turbojpeg.h']]],
+  ['tjsaveimage',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga2e78b7b79796e74584028da880a6a29c',1,'turbojpeg.h']]],
   ['tjscaled',['TJSCALED',['../group___turbo_j_p_e_g.html#ga84878bb65404204743aa18cac02781df',1,'turbojpeg.h']]],
   ['tjscalingfactor',['tjscalingfactor',['../structtjscalingfactor.html',1,'']]],
   ['tjtransform',['tjtransform',['../structtjtransform.html',1,'tjtransform'],['../group___turbo_j_p_e_g.html#gaa29f3189c41be12ec5dee7caec318a31',1,'tjtransform():&#160;turbojpeg.h'],['../group___turbo_j_p_e_g.html#gad02cd42b69f193a0623a9c801788df3a',1,'tjTransform(tjhandle handle, const unsigned char *jpegBuf, unsigned long jpegSize, int n, unsigned char **dstBufs, unsigned long *dstSizes, tjtransform *transforms, int flags):&#160;turbojpeg.h']]],
index 799f9acd2c5afab2ebd030e809f8fa6862d9bb19..e683856978dddc32ffe99a0fdb6b1e0400f34a0c 100644 (file)
@@ -17,6 +17,7 @@ var searchData=
   ['tjpf_5frgb',['TJPF_RGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa7ce93230bff449518ce387c17e6ed37c',1,'turbojpeg.h']]],
   ['tjpf_5frgba',['TJPF_RGBA',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa88d2e88fab67f6503cf972e14851cc12',1,'turbojpeg.h']]],
   ['tjpf_5frgbx',['TJPF_RGBX',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa83973bebb7e2dc6fa8bae89ff3f42e01',1,'turbojpeg.h']]],
+  ['tjpf_5funknown',['TJPF_UNKNOWN',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aa84c1a6cead7952998e2fb895844a21ed',1,'turbojpeg.h']]],
   ['tjpf_5fxbgr',['TJPF_XBGR',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aaf6603b27147de47e212e75dac027b2af',1,'turbojpeg.h']]],
   ['tjpf_5fxrgb',['TJPF_XRGB',['../group___turbo_j_p_e_g.html#ggac916144e26c3817ac514e64ae5d12e2aadae996905efcfa3b42a0bb3bea7f9d84',1,'turbojpeg.h']]],
   ['tjsamp_5f411',['TJSAMP_411',['../group___turbo_j_p_e_g.html#gga1d047060ea80bb9820d540bb928e9074a28ec62575e5ea295c3fde3001dc628e2',1,'turbojpeg.h']]],
index d2164bb5fc6e358e538b7837e41645bba400073c..0c9a3c1bbc52b5905be3e31da5f0e5138adcafd1 100644 (file)
@@ -22,8 +22,10 @@ var searchData=
   ['tjinitcompress',['tjInitCompress',['../group___turbo_j_p_e_g.html#ga3d10c47fbe4a2489a2b30c931551d01a',1,'turbojpeg.h']]],
   ['tjinitdecompress',['tjInitDecompress',['../group___turbo_j_p_e_g.html#gae5408179d041e2a2f7199c8283cf649e',1,'turbojpeg.h']]],
   ['tjinittransform',['tjInitTransform',['../group___turbo_j_p_e_g.html#ga3155b775bfbac9dbba869b95a0367902',1,'turbojpeg.h']]],
+  ['tjloadimage',['tjLoadImage',['../group___turbo_j_p_e_g.html#ga144b981d6b281ecca4cbb4709de75749',1,'turbojpeg.h']]],
   ['tjplaneheight',['tjPlaneHeight',['../group___turbo_j_p_e_g.html#ga1a209696c6a80748f20e134b3c64789f',1,'turbojpeg.h']]],
   ['tjplanesizeyuv',['tjPlaneSizeYUV',['../group___turbo_j_p_e_g.html#ga6f98d977bfa9d167c97172e876ba61e2',1,'turbojpeg.h']]],
   ['tjplanewidth',['tjPlaneWidth',['../group___turbo_j_p_e_g.html#ga63fb66bb1e36c74008c4634360becbb1',1,'turbojpeg.h']]],
+  ['tjsaveimage',['tjSaveImage',['../group___turbo_j_p_e_g.html#ga2e78b7b79796e74584028da880a6a29c',1,'turbojpeg.h']]],
   ['tjtransform',['tjTransform',['../group___turbo_j_p_e_g.html#gad02cd42b69f193a0623a9c801788df3a',1,'turbojpeg.h']]]
 ];
index 551e252a036e0388805c765b1fbf10d89d74e074..dd351766fb8bdf3a6f53861d1a5cbe77f3d1680c 100644 (file)
--- a/md5/md5.h
+++ b/md5/md5.h
@@ -28,6 +28,8 @@ documentation and/or software.
 #ifndef _SYS_MD5_H_
 #define _SYS_MD5_H_
 
+#include <sys/types.h>
+
 #define MD5_BLOCK_LENGTH               64
 #define MD5_DIGEST_LENGTH              16
 #define MD5_DIGEST_STRING_LENGTH       (MD5_DIGEST_LENGTH * 2 + 1)
index dfd60bd396d3a7cd1170ae5330949c082afa2adf..5716297fde739dbaa36324520aeff14026924cee 100644 (file)
@@ -28,7 +28,6 @@
 
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
 #include "./md5.h"
 #include "../tjutil.h"
 
diff --git a/rdbmp.c b/rdbmp.c
index eaa708645fe065a5be257ceb0c4d45878b3003f5..49616d01f3e55743619d1f720bcb57218c86817d 100644 (file)
--- a/rdbmp.c
+++ b/rdbmp.c
@@ -6,7 +6,7 @@
  * Modified 2009-2010 by Guido Vollbeding.
  * libjpeg-turbo Modifications:
  * Modified 2011 by Siarhei Siamashka.
- * Copyright (C) 2015, D. R. Commander.
+ * Copyright (C) 2015, 2017, D. R. Commander.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
  *
@@ -27,6 +27,7 @@
  */
 
 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
+#include "cmyk.h"
 
 #ifdef BMP_SUPPORTED
 
@@ -49,6 +50,10 @@ typedef char U_CHAR;
 
 #define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len)))
 
+static int alpha_index[JPEG_NUMCS] = {
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1
+};
+
 
 /* Private version of data source object */
 
@@ -66,6 +71,17 @@ typedef struct _bmp_source_struct {
   JDIMENSION row_width;         /* Physical width of scanlines in file */
 
   int bits_per_pixel;           /* remembers 8- or 24-bit format */
+
+  boolean use_inversion_array;  /* TRUE = preload the whole image, which is
+                                   stored in bottom-up order, and feed it to
+                                   the calling program in top-down order
+
+                                   FALSE = the calling program will maintain
+                                   its own image buffer and read the rows in
+                                   bottom-up order */
+
+  U_CHAR *iobuffer;             /* I/O buffer (used to buffer a single row from
+                                   disk if use_inversion_array == FALSE) */
 } bmp_source_struct;
 
 
@@ -131,20 +147,58 @@ get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
   register JSAMPROW inptr, outptr;
   register JDIMENSION col;
 
-  /* Fetch next row from virtual array */
-  source->source_row--;
-  image_ptr = (*cinfo->mem->access_virt_sarray)
-    ((j_common_ptr) cinfo, source->whole_image,
-     source->source_row, (JDIMENSION) 1, FALSE);
+  if (source->use_inversion_array) {
+    /* Fetch next row from virtual array */
+    source->source_row--;
+    image_ptr = (*cinfo->mem->access_virt_sarray)
+      ((j_common_ptr) cinfo, source->whole_image,
+       source->source_row, (JDIMENSION) 1, FALSE);
+    inptr = image_ptr[0];
+  } else {
+    if (! ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
+      ERREXIT(cinfo, JERR_INPUT_EOF);
+    inptr = source->iobuffer;
+  }
 
   /* Expand the colormap indexes to real data */
-  inptr = image_ptr[0];
   outptr = source->pub.buffer[0];
-  for (col = cinfo->image_width; col > 0; col--) {
-    t = GETJSAMPLE(*inptr++);
-    *outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */
-    *outptr++ = colormap[1][t];
-    *outptr++ = colormap[2][t];
+  if (cinfo->in_color_space == JCS_GRAYSCALE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      t = GETJSAMPLE(*inptr++);
+      *outptr++ = RGB2GRAY(colormap[0][t], colormap[1][t], colormap[2][t]);
+    }
+  } else if (cinfo->in_color_space == JCS_CMYK) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      t = GETJSAMPLE(*inptr++);
+      rgb_to_cmyk(colormap[0][t], colormap[1][t], colormap[2][t], outptr,
+                  outptr + 1, outptr + 2, outptr + 3);
+      outptr += 4;
+    }
+  } else {
+    register int rindex = rgb_red[cinfo->in_color_space];
+    register int gindex = rgb_green[cinfo->in_color_space];
+    register int bindex = rgb_blue[cinfo->in_color_space];
+    register int aindex = alpha_index[cinfo->in_color_space];
+    register int ps = rgb_pixelsize[cinfo->in_color_space];
+
+    if (aindex >= 0) {
+      for (col = cinfo->image_width; col > 0; col--) {
+        t = GETJSAMPLE(*inptr++);
+        outptr[rindex] = colormap[0][t];
+        outptr[gindex] = colormap[1][t];
+        outptr[bindex] = colormap[2][t];
+        outptr[aindex] = 0xFF;
+        outptr += ps;
+      }
+    } else {
+      for (col = cinfo->image_width; col > 0; col--) {
+        t = GETJSAMPLE(*inptr++);
+        outptr[rindex] = colormap[0][t];
+        outptr[gindex] = colormap[1][t];
+        outptr[bindex] = colormap[2][t];
+        outptr += ps;
+      }
+    }
   }
 
   return 1;
@@ -160,22 +214,61 @@ get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
   register JSAMPROW inptr, outptr;
   register JDIMENSION col;
 
-  /* Fetch next row from virtual array */
-  source->source_row--;
-  image_ptr = (*cinfo->mem->access_virt_sarray)
-    ((j_common_ptr) cinfo, source->whole_image,
-     source->source_row, (JDIMENSION) 1, FALSE);
+  if (source->use_inversion_array) {
+    /* Fetch next row from virtual array */
+    source->source_row--;
+    image_ptr = (*cinfo->mem->access_virt_sarray)
+      ((j_common_ptr) cinfo, source->whole_image,
+       source->source_row, (JDIMENSION) 1, FALSE);
+    inptr = image_ptr[0];
+  } else {
+    if (! ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
+      ERREXIT(cinfo, JERR_INPUT_EOF);
+    inptr = source->iobuffer;
+  }
 
   /* Transfer data.  Note source values are in BGR order
    * (even though Microsoft's own documents say the opposite).
    */
-  inptr = image_ptr[0];
   outptr = source->pub.buffer[0];
-  for (col = cinfo->image_width; col > 0; col--) {
-    outptr[2] = *inptr++;       /* can omit GETJSAMPLE() safely */
-    outptr[1] = *inptr++;
-    outptr[0] = *inptr++;
-    outptr += 3;
+  if (cinfo->in_color_space == JCS_EXT_BGR) {
+    MEMCOPY(outptr, inptr, source->row_width);
+  } else if (cinfo->in_color_space == JCS_GRAYSCALE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      /* can omit GETJSAMPLE() safely */
+      JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
+      *outptr++ = RGB2GRAY(r, g, b);
+    }
+  } else if (cinfo->in_color_space == JCS_CMYK) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      /* can omit GETJSAMPLE() safely */
+      JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
+      rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
+      outptr += 4;
+    }
+  } else {
+    register int rindex = rgb_red[cinfo->in_color_space];
+    register int gindex = rgb_green[cinfo->in_color_space];
+    register int bindex = rgb_blue[cinfo->in_color_space];
+    register int aindex = alpha_index[cinfo->in_color_space];
+    register int ps = rgb_pixelsize[cinfo->in_color_space];
+
+    if (aindex >= 0) {
+      for (col = cinfo->image_width; col > 0; col--) {
+        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
+        outptr[gindex] = *inptr++;
+        outptr[rindex] = *inptr++;
+        outptr[aindex] = 0xFF;
+        outptr += ps;
+      }
+    } else {
+      for (col = cinfo->image_width; col > 0; col--) {
+        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
+        outptr[gindex] = *inptr++;
+        outptr[rindex] = *inptr++;
+        outptr += ps;
+      }
+    }
   }
 
   return 1;
@@ -191,22 +284,64 @@ get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
   register JSAMPROW inptr, outptr;
   register JDIMENSION col;
 
-  /* Fetch next row from virtual array */
-  source->source_row--;
-  image_ptr = (*cinfo->mem->access_virt_sarray)
-    ((j_common_ptr) cinfo, source->whole_image,
-     source->source_row, (JDIMENSION) 1, FALSE);
+  if (source->use_inversion_array) {
+    /* Fetch next row from virtual array */
+    source->source_row--;
+    image_ptr = (*cinfo->mem->access_virt_sarray)
+      ((j_common_ptr) cinfo, source->whole_image,
+       source->source_row, (JDIMENSION) 1, FALSE);
+    inptr = image_ptr[0];
+  } else {
+    if (! ReadOK(source->pub.input_file, source->iobuffer, source->row_width))
+      ERREXIT(cinfo, JERR_INPUT_EOF);
+    inptr = source->iobuffer;
+  }
+
   /* Transfer data.  Note source values are in BGR order
    * (even though Microsoft's own documents say the opposite).
    */
-  inptr = image_ptr[0];
   outptr = source->pub.buffer[0];
-  for (col = cinfo->image_width; col > 0; col--) {
-    outptr[2] = *inptr++;       /* can omit GETJSAMPLE() safely */
-    outptr[1] = *inptr++;
-    outptr[0] = *inptr++;
-    inptr++;                    /* skip the 4th byte (Alpha channel) */
-    outptr += 3;
+  if (cinfo->in_color_space == JCS_EXT_BGRX ||
+      cinfo->in_color_space == JCS_EXT_BGRA) {
+    MEMCOPY(outptr, inptr, source->row_width);
+  } else if (cinfo->in_color_space == JCS_GRAYSCALE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      /* can omit GETJSAMPLE() safely */
+      JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
+      *outptr++ = RGB2GRAY(r, g, b);
+    }
+  } else if (cinfo->in_color_space == JCS_CMYK) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      /* can omit GETJSAMPLE() safely */
+      JSAMPLE b = *inptr++, g = *inptr++, r = *inptr++;
+      rgb_to_cmyk(r, g, b, outptr, outptr + 1, outptr + 2, outptr + 3);
+      inptr++;                          /* skip the 4th byte (Alpha channel) */
+      outptr += 4;
+    }
+  } else {
+    register int rindex = rgb_red[cinfo->in_color_space];
+    register int gindex = rgb_green[cinfo->in_color_space];
+    register int bindex = rgb_blue[cinfo->in_color_space];
+    register int aindex = alpha_index[cinfo->in_color_space];
+    register int ps = rgb_pixelsize[cinfo->in_color_space];
+
+    if (aindex >= 0) {
+      for (col = cinfo->image_width; col > 0; col--) {
+        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
+        outptr[gindex] = *inptr++;
+        outptr[rindex] = *inptr++;
+        outptr[aindex] = *inptr++;
+        outptr += ps;
+      }
+    } else {
+      for (col = cinfo->image_width; col > 0; col--) {
+        outptr[bindex] = *inptr++;      /* can omit GETJSAMPLE() safely */
+        outptr[gindex] = *inptr++;
+        outptr[rindex] = *inptr++;
+        inptr++;                        /* skip the 4th byte (Alpha channel) */
+        outptr += ps;
+      }
+    }
   }
 
   return 1;
@@ -297,7 +432,7 @@ start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
   unsigned int biClrUsed = 0;
   int mapentrysize = 0;         /* 0 indicates no colormap */
   int bPad;
-  JDIMENSION row_width;
+  JDIMENSION row_width = 0;
 
   /* Read and verify the bitmap file header */
   if (! ReadOK(source->pub.input_file, bmpfileheader, 14))
@@ -415,33 +550,72 @@ start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
   }
 
   /* Compute row width in file, including padding to 4-byte boundary */
-  if (source->bits_per_pixel == 24)
+  switch (source->bits_per_pixel) {
+  case 8:
+    if (cinfo->in_color_space == JCS_UNKNOWN)
+      cinfo->in_color_space = JCS_EXT_RGB;
+    row_width = (JDIMENSION) biWidth;
+    break;
+  case 24:
+    if (cinfo->in_color_space == JCS_UNKNOWN)
+      cinfo->in_color_space = JCS_EXT_BGR;
     row_width = (JDIMENSION) (biWidth * 3);
-  else if (source->bits_per_pixel == 32)
+    break;
+  case 32:
+    if (cinfo->in_color_space == JCS_UNKNOWN)
+      cinfo->in_color_space = JCS_EXT_BGRA;
     row_width = (JDIMENSION) (biWidth * 4);
-  else
-    row_width = (JDIMENSION) biWidth;
+    break;
+  default:
+    ERREXIT(cinfo, JERR_BMP_BADDEPTH);
+  }
   while ((row_width & 3) != 0) row_width++;
   source->row_width = row_width;
 
-  /* Allocate space for inversion array, prepare for preload pass */
-  source->whole_image = (*cinfo->mem->request_virt_sarray)
-    ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
-     row_width, (JDIMENSION) biHeight, (JDIMENSION) 1);
-  source->pub.get_pixel_rows = preload_image;
-  if (cinfo->progress != NULL) {
-    cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
-    progress->total_extra_passes++; /* count file input as separate pass */
+  if (source->use_inversion_array) {
+    /* Allocate space for inversion array, prepare for preload pass */
+    source->whole_image = (*cinfo->mem->request_virt_sarray)
+      ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
+       row_width, (JDIMENSION) biHeight, (JDIMENSION) 1);
+    source->pub.get_pixel_rows = preload_image;
+    if (cinfo->progress != NULL) {
+      cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
+      progress->total_extra_passes++; /* count file input as separate pass */
+    }
+  } else {
+    source->iobuffer = (U_CHAR *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+                                  row_width);
+    switch (source->bits_per_pixel) {
+    case 8:
+      source->pub.get_pixel_rows = get_8bit_row;
+      break;
+    case 24:
+      source->pub.get_pixel_rows = get_24bit_row;
+      break;
+    case 32:
+      source->pub.get_pixel_rows = get_32bit_row;
+      break;
+    default:
+      ERREXIT(cinfo, JERR_BMP_BADDEPTH);
+    }
   }
 
+  if (IsExtRGB(cinfo->in_color_space))
+    cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
+  else if (cinfo->in_color_space == JCS_GRAYSCALE)
+    cinfo->input_components = 1;
+  else if (cinfo->in_color_space == JCS_CMYK)
+    cinfo->input_components = 4;
+  else
+    ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+
   /* Allocate one-row buffer for returned data */
   source->pub.buffer = (*cinfo->mem->alloc_sarray)
     ((j_common_ptr) cinfo, JPOOL_IMAGE,
-     (JDIMENSION) (biWidth * 3), (JDIMENSION) 1);
+     (JDIMENSION) (biWidth * cinfo->input_components), (JDIMENSION) 1);
   source->pub.buffer_height = 1;
 
-  cinfo->in_color_space = JCS_RGB;
-  cinfo->input_components = 3;
   cinfo->data_precision = 8;
   cinfo->image_width = (JDIMENSION) biWidth;
   cinfo->image_height = (JDIMENSION) biHeight;
@@ -464,7 +638,7 @@ finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
  */
 
 GLOBAL(cjpeg_source_ptr)
-jinit_read_bmp (j_compress_ptr cinfo)
+jinit_read_bmp (j_compress_ptr cinfo, boolean use_inversion_array)
 {
   bmp_source_ptr source;
 
@@ -477,6 +651,8 @@ jinit_read_bmp (j_compress_ptr cinfo)
   source->pub.start_input = start_input_bmp;
   source->pub.finish_input = finish_input_bmp;
 
+  source->use_inversion_array = use_inversion_array;
+
   return (cjpeg_source_ptr) source;
 }
 
diff --git a/rdppm.c b/rdppm.c
index 33ff7495725af61c830d9f7ea3e8e3af2f584317..24987cf1123ad6b0d7dad6fc743aff72381ccb2f 100644 (file)
--- a/rdppm.c
+++ b/rdppm.c
@@ -5,7 +5,7 @@
  * Copyright (C) 1991-1997, Thomas G. Lane.
  * Modified 2009 by Bill Allombert, Guido Vollbeding.
  * libjpeg-turbo Modifications:
- * Copyright (C) 2015, 2016, D. R. Commander.
+ * Copyright (C) 2015-2017, D. R. Commander.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
  *
@@ -23,6 +23,7 @@
  */
 
 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
+#include "cmyk.h"
 
 #ifdef PPM_SUPPORTED
 
@@ -58,6 +59,10 @@ typedef char U_CHAR;
 
 #define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len)))
 
+static int alpha_index[JPEG_NUMCS] = {
+  -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1
+};
+
 
 /* Private version of data source object */
 
@@ -155,6 +160,89 @@ get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
 }
 
 
+#define GRAY_RGB_READ_LOOP(read_op, alpha_set_op) {  \
+  for (col = cinfo->image_width; col > 0; col--) {  \
+    ptr[rindex] = ptr[gindex] = ptr[bindex] = read_op;  \
+    alpha_set_op  \
+    ptr += ps;  \
+  }  \
+}
+
+METHODDEF(JDIMENSION)
+get_text_gray_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading text-format PGM files with any maxval and
+   converting to extended RGB */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  FILE *infile = source->pub.input_file;
+  register JSAMPROW ptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+  register int rindex = rgb_red[cinfo->in_color_space];
+  register int gindex = rgb_green[cinfo->in_color_space];
+  register int bindex = rgb_blue[cinfo->in_color_space];
+  register int aindex = alpha_index[cinfo->in_color_space];
+  register int ps = rgb_pixelsize[cinfo->in_color_space];
+
+  ptr = source->pub.buffer[0];
+  if (maxval == MAXJSAMPLE) {
+    if (aindex >= 0)
+      GRAY_RGB_READ_LOOP(read_pbm_integer(cinfo, infile, maxval),
+                         ptr[aindex] = 0xFF;)
+    else
+      GRAY_RGB_READ_LOOP(read_pbm_integer(cinfo, infile, maxval),)
+  } else {
+    if (aindex >= 0)
+      GRAY_RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)],
+                         ptr[aindex] = 0xFF;)
+    else
+      GRAY_RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)],)
+  }
+  return 1;
+}
+
+
+METHODDEF(JDIMENSION)
+get_text_gray_cmyk_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading text-format PGM files with any maxval and
+   converting to CMYK */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  FILE *infile = source->pub.input_file;
+  register JSAMPROW ptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+
+  ptr = source->pub.buffer[0];
+  if (maxval == MAXJSAMPLE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE gray = read_pbm_integer(cinfo, infile, maxval);
+      rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
+  } else {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE gray = rescale[read_pbm_integer(cinfo, infile, maxval)];
+      rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
+  }
+  return 1;
+}
+
+
+#define RGB_READ_LOOP(read_op, alpha_set_op) {  \
+  for (col = cinfo->image_width; col > 0; col--) {  \
+    ptr[rindex] = read_op;  \
+    ptr[gindex] = read_op;  \
+    ptr[bindex] = read_op;  \
+    alpha_set_op  \
+    ptr += ps;  \
+  }  \
+}
+
 METHODDEF(JDIMENSION)
 get_text_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
 /* This version is for reading text-format PPM files with any maxval */
@@ -165,12 +253,91 @@ get_text_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
   register JSAMPLE *rescale = source->rescale;
   JDIMENSION col;
   unsigned int maxval = source->maxval;
+  register int rindex = rgb_red[cinfo->in_color_space];
+  register int gindex = rgb_green[cinfo->in_color_space];
+  register int bindex = rgb_blue[cinfo->in_color_space];
+  register int aindex = alpha_index[cinfo->in_color_space];
+  register int ps = rgb_pixelsize[cinfo->in_color_space];
 
   ptr = source->pub.buffer[0];
-  for (col = cinfo->image_width; col > 0; col--) {
-    *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)];
-    *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)];
-    *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)];
+  if (maxval == MAXJSAMPLE) {
+    if (aindex >= 0)
+      RGB_READ_LOOP(read_pbm_integer(cinfo, infile, maxval),
+                    ptr[aindex] = 0xFF;)
+    else
+      RGB_READ_LOOP(read_pbm_integer(cinfo, infile, maxval),)
+  } else {
+    if (aindex >= 0)
+      RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)],
+                    ptr[aindex] = 0xFF;)
+    else
+      RGB_READ_LOOP(rescale[read_pbm_integer(cinfo, infile, maxval)],)
+  }
+  return 1;
+}
+
+
+METHODDEF(JDIMENSION)
+get_text_rgb_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading text-format PPM files with any maxval and
+   converting to grayscale */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  FILE *infile = source->pub.input_file;
+  register JSAMPROW ptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+
+  ptr = source->pub.buffer[0];
+  if (maxval == MAXJSAMPLE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = read_pbm_integer(cinfo, infile, maxval);
+      JSAMPLE g = read_pbm_integer(cinfo, infile, maxval);
+      JSAMPLE b = read_pbm_integer(cinfo, infile, maxval);
+      *ptr++ = RGB2GRAY(r, g, b);
+    }
+  } else {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = rescale[read_pbm_integer(cinfo, infile, maxval)];
+      JSAMPLE g = rescale[read_pbm_integer(cinfo, infile, maxval)];
+      JSAMPLE b = rescale[read_pbm_integer(cinfo, infile, maxval)];
+      *ptr++ = RGB2GRAY(r, g, b);
+    }
+  }
+  return 1;
+}
+
+
+METHODDEF(JDIMENSION)
+get_text_rgb_cmyk_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading text-format PPM files with any maxval and
+   converting to CMYK */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  FILE *infile = source->pub.input_file;
+  register JSAMPROW ptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+
+  ptr = source->pub.buffer[0];
+  if (maxval == MAXJSAMPLE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = read_pbm_integer(cinfo, infile, maxval);
+      JSAMPLE g = read_pbm_integer(cinfo, infile, maxval);
+      JSAMPLE b = read_pbm_integer(cinfo, infile, maxval);
+      rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
+  } else {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = rescale[read_pbm_integer(cinfo, infile, maxval)];
+      JSAMPLE g = rescale[read_pbm_integer(cinfo, infile, maxval)];
+      JSAMPLE b = rescale[read_pbm_integer(cinfo, infile, maxval)];
+      rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
   }
   return 1;
 }
@@ -198,7 +365,76 @@ get_scaled_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
 
 
 METHODDEF(JDIMENSION)
-get_scaled_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+get_gray_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading raw-byte-format PGM files with any maxval
+   and converting to extended RGB */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  register JSAMPROW ptr;
+  register U_CHAR *bufferptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+  register int rindex = rgb_red[cinfo->in_color_space];
+  register int gindex = rgb_green[cinfo->in_color_space];
+  register int bindex = rgb_blue[cinfo->in_color_space];
+  register int aindex = alpha_index[cinfo->in_color_space];
+  register int ps = rgb_pixelsize[cinfo->in_color_space];
+
+  if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
+    ERREXIT(cinfo, JERR_INPUT_EOF);
+  ptr = source->pub.buffer[0];
+  bufferptr = source->iobuffer;
+  if (maxval == MAXJSAMPLE) {
+    if (aindex >= 0)
+      GRAY_RGB_READ_LOOP(*bufferptr++, ptr[aindex] = 0xFF;)
+    else
+      GRAY_RGB_READ_LOOP(*bufferptr++,)
+  } else {
+    if (aindex >= 0)
+      GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = 0xFF;)
+    else
+      GRAY_RGB_READ_LOOP(rescale[UCH(*bufferptr++)],)
+  }
+  return 1;
+}
+
+
+METHODDEF(JDIMENSION)
+get_gray_cmyk_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading raw-byte-format PGM files with any maxval
+   and converting to CMYK */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  register JSAMPROW ptr;
+  register U_CHAR *bufferptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+
+  if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
+    ERREXIT(cinfo, JERR_INPUT_EOF);
+  ptr = source->pub.buffer[0];
+  bufferptr = source->iobuffer;
+  if (maxval == MAXJSAMPLE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE gray = *bufferptr++;
+      rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
+  } else {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE gray = rescale[UCH(*bufferptr++)];
+      rgb_to_cmyk(gray, gray, gray, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
+  }
+  return 1;
+}
+
+
+METHODDEF(JDIMENSION)
+get_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
 /* This version is for reading raw-byte-format PPM files with any maxval */
 {
   ppm_source_ptr source = (ppm_source_ptr) sinfo;
@@ -206,15 +442,99 @@ get_scaled_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
   register U_CHAR *bufferptr;
   register JSAMPLE *rescale = source->rescale;
   JDIMENSION col;
+  unsigned int maxval = source->maxval;
+  register int rindex = rgb_red[cinfo->in_color_space];
+  register int gindex = rgb_green[cinfo->in_color_space];
+  register int bindex = rgb_blue[cinfo->in_color_space];
+  register int aindex = alpha_index[cinfo->in_color_space];
+  register int ps = rgb_pixelsize[cinfo->in_color_space];
 
   if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
     ERREXIT(cinfo, JERR_INPUT_EOF);
   ptr = source->pub.buffer[0];
   bufferptr = source->iobuffer;
-  for (col = cinfo->image_width; col > 0; col--) {
-    *ptr++ = rescale[UCH(*bufferptr++)];
-    *ptr++ = rescale[UCH(*bufferptr++)];
-    *ptr++ = rescale[UCH(*bufferptr++)];
+  if (maxval == MAXJSAMPLE) {
+    if (aindex >= 0)
+      RGB_READ_LOOP(*bufferptr++, ptr[aindex] = 0xFF;)
+    else
+      RGB_READ_LOOP(*bufferptr++,)
+  } else {
+    if (aindex >= 0)
+      RGB_READ_LOOP(rescale[UCH(*bufferptr++)], ptr[aindex] = 0xFF;)
+    else
+      RGB_READ_LOOP(rescale[UCH(*bufferptr++)],)
+  }
+  return 1;
+}
+
+
+METHODDEF(JDIMENSION)
+get_rgb_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading raw-byte-format PPM files with any maxval and
+   converting to grayscale */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  register JSAMPROW ptr;
+  register U_CHAR *bufferptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+
+  if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
+    ERREXIT(cinfo, JERR_INPUT_EOF);
+  ptr = source->pub.buffer[0];
+  bufferptr = source->iobuffer;
+  if (maxval == MAXJSAMPLE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = *bufferptr++;
+      JSAMPLE g = *bufferptr++;
+      JSAMPLE b = *bufferptr++;
+      *ptr++ = RGB2GRAY(r, g, b);
+    }
+  } else {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = rescale[UCH(*bufferptr++)];
+      JSAMPLE g = rescale[UCH(*bufferptr++)];
+      JSAMPLE b = rescale[UCH(*bufferptr++)];
+      *ptr++ = RGB2GRAY(r, g, b);
+    }
+  }
+  return 1;
+}
+
+
+METHODDEF(JDIMENSION)
+get_rgb_cmyk_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
+/* This version is for reading raw-byte-format PPM files with any maxval and
+   converting to CMYK */
+{
+  ppm_source_ptr source = (ppm_source_ptr) sinfo;
+  register JSAMPROW ptr;
+  register U_CHAR *bufferptr;
+  register JSAMPLE *rescale = source->rescale;
+  JDIMENSION col;
+  unsigned int maxval = source->maxval;
+
+  if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width))
+    ERREXIT(cinfo, JERR_INPUT_EOF);
+  ptr = source->pub.buffer[0];
+  bufferptr = source->iobuffer;
+  if (maxval == MAXJSAMPLE) {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = *bufferptr++;
+      JSAMPLE g = *bufferptr++;
+      JSAMPLE b = *bufferptr++;
+      rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
+  } else {
+    for (col = cinfo->image_width; col > 0; col--) {
+      JSAMPLE r = rescale[UCH(*bufferptr++)];
+      JSAMPLE g = rescale[UCH(*bufferptr++)];
+      JSAMPLE b = rescale[UCH(*bufferptr++)];
+      rgb_to_cmyk(r, g, b, ptr, ptr + 1, ptr + 2, ptr + 3);
+      ptr += 4;
+    }
   }
   return 1;
 }
@@ -348,56 +668,100 @@ start_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo)
 
   switch (c) {
   case '2':                     /* it's a text-format PGM file */
-    cinfo->input_components = 1;
-    cinfo->in_color_space = JCS_GRAYSCALE;
+    if (cinfo->in_color_space == JCS_UNKNOWN)
+      cinfo->in_color_space = JCS_GRAYSCALE;
     TRACEMS2(cinfo, 1, JTRC_PGM_TEXT, w, h);
-    source->pub.get_pixel_rows = get_text_gray_row;
+    if (cinfo->in_color_space == JCS_GRAYSCALE)
+      source->pub.get_pixel_rows = get_text_gray_row;
+    else if (IsExtRGB(cinfo->in_color_space))
+      source->pub.get_pixel_rows = get_text_gray_rgb_row;
+    else if (cinfo->in_color_space == JCS_CMYK)
+      source->pub.get_pixel_rows = get_text_gray_cmyk_row;
+    else
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     need_iobuffer = FALSE;
     break;
 
   case '3':                     /* it's a text-format PPM file */
-    cinfo->input_components = 3;
-    cinfo->in_color_space = JCS_RGB;
+    if (cinfo->in_color_space == JCS_UNKNOWN)
+      cinfo->in_color_space = JCS_EXT_RGB;
     TRACEMS2(cinfo, 1, JTRC_PPM_TEXT, w, h);
-    source->pub.get_pixel_rows = get_text_rgb_row;
+    if (IsExtRGB(cinfo->in_color_space))
+      source->pub.get_pixel_rows = get_text_rgb_row;
+    else if (cinfo->in_color_space == JCS_CMYK)
+      source->pub.get_pixel_rows = get_text_rgb_cmyk_row;
+    else if (cinfo->in_color_space == JCS_GRAYSCALE)
+      source->pub.get_pixel_rows = get_text_rgb_gray_row;
+    else
+      ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     need_iobuffer = FALSE;
     break;
 
   case '5':                     /* it's a raw-format PGM file */
-    cinfo->input_components = 1;
-    cinfo->in_color_space = JCS_GRAYSCALE;
+    if (cinfo->in_color_space == JCS_UNKNOWN)
+      cinfo->in_color_space = JCS_GRAYSCALE;
     TRACEMS2(cinfo, 1, JTRC_PGM, w, h);
     if (maxval > 255) {
       source->pub.get_pixel_rows = get_word_gray_row;
-    } else if (maxval == MAXJSAMPLE && sizeof(JSAMPLE) == sizeof(U_CHAR)) {
+    } else if (maxval == MAXJSAMPLE && sizeof(JSAMPLE) == sizeof(U_CHAR) &&
+               cinfo->in_color_space == JCS_GRAYSCALE) {
       source->pub.get_pixel_rows = get_raw_row;
       use_raw_buffer = TRUE;
       need_rescale = FALSE;
     } else {
-      source->pub.get_pixel_rows = get_scaled_gray_row;
+      if (cinfo->in_color_space == JCS_GRAYSCALE)
+        source->pub.get_pixel_rows = get_scaled_gray_row;
+      else if (IsExtRGB(cinfo->in_color_space))
+        source->pub.get_pixel_rows = get_gray_rgb_row;
+      else if (cinfo->in_color_space == JCS_CMYK)
+        source->pub.get_pixel_rows = get_gray_cmyk_row;
+      else
+        ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     }
     break;
 
   case '6':                     /* it's a raw-format PPM file */
-    cinfo->input_components = 3;
-    cinfo->in_color_space = JCS_RGB;
+    if (cinfo->in_color_space == JCS_UNKNOWN)
+      cinfo->in_color_space = JCS_EXT_RGB;
     TRACEMS2(cinfo, 1, JTRC_PPM, w, h);
     if (maxval > 255) {
       source->pub.get_pixel_rows = get_word_rgb_row;
-    } else if (maxval == MAXJSAMPLE && sizeof(JSAMPLE) == sizeof(U_CHAR)) {
+    } else if (maxval == MAXJSAMPLE && sizeof(JSAMPLE) == sizeof(U_CHAR) &&
+               (cinfo->in_color_space == JCS_EXT_RGB
+#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
+                || cinfo->in_color_space == JCS_RGB)) {
+#endif
       source->pub.get_pixel_rows = get_raw_row;
       use_raw_buffer = TRUE;
       need_rescale = FALSE;
     } else {
-      source->pub.get_pixel_rows = get_scaled_rgb_row;
+      if (IsExtRGB(cinfo->in_color_space))
+        source->pub.get_pixel_rows = get_rgb_row;
+      else if (cinfo->in_color_space == JCS_CMYK)
+        source->pub.get_pixel_rows = get_rgb_cmyk_row;
+      else if (cinfo->in_color_space == JCS_GRAYSCALE)
+        source->pub.get_pixel_rows = get_rgb_gray_row;
+      else
+        ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
     }
     break;
   }
 
+  if (IsExtRGB(cinfo->in_color_space))
+    cinfo->input_components = rgb_pixelsize[cinfo->in_color_space];
+  else if (cinfo->in_color_space == JCS_GRAYSCALE)
+    cinfo->input_components = 1;
+  else if (cinfo->in_color_space == JCS_CMYK)
+    cinfo->input_components = 4;
+
   /* Allocate space for I/O buffer: 1 or 3 bytes or words/pixel. */
   if (need_iobuffer) {
-    source->buffer_width = (size_t) w * cinfo->input_components *
-      ((maxval <= 255) ? sizeof(U_CHAR) : (2 * sizeof(U_CHAR)));
+    if (c == '6')
+      source->buffer_width = (size_t) w * 3 *
+        ((maxval <= 255) ? sizeof(U_CHAR) : (2 * sizeof(U_CHAR)));
+    else
+      source->buffer_width = (size_t) w *
+        ((maxval <= 255) ? sizeof(U_CHAR) : (2 * sizeof(U_CHAR)));
     source->iobuffer = (U_CHAR *)
       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
                                   source->buffer_width);
index 9b7a48bc8a2b29d002c8b9f05766451d2169e525..70c172fcd27a655dde1923ce12f03e4d52f8af35 100644 (file)
--- a/tjbench.c
+++ b/tjbench.c
@@ -33,7 +33,6 @@
 #include <math.h>
 #include <errno.h>
 #include <cdjpeg.h>
-#include "./bmp.h"
 #include "./tjutil.h"
 #include "./turbojpeg.h"
 
 char tjErrorStr[JMSG_LENGTH_MAX]="\0", tjErrorMsg[JMSG_LENGTH_MAX]="\0";
 int tjErrorLine=-1, tjErrorCode=-1;
 
+#define _throwtjg(m) {  \
+       printf("ERROR in line %d while %s:\n%s\n", __LINE__, m,  \
+               tjGetErrorStr2(NULL));  \
+       retval=-1;  goto bailout;  \
+}
+
 #define _throwtj(m)  \
 {  \
        int _tjErrorCode=tjGetErrorCode(handle);  \
@@ -76,8 +81,6 @@ int tjErrorLine=-1, tjErrorCode=-1;
        }  \
 }
 
-#define _throwbmp(m) _throw(m, bmpgeterr())
-
 int flags=TJFLAG_NOREALLOC, componly=0, decomponly=0, doyuv=0, quiet=0,
        dotile=0, pf=TJPF_BGR, yuvpad=1, dowrite=1;
 char *ext="ppm";
@@ -265,9 +268,8 @@ int decomp(unsigned char *srcbuf, unsigned char **jpegbuf,
                snprintf(tempstr, 1024, "%s_%s%s_%s.%s", filename, subName[subsamp],
                        qualstr, sizestr, ext);
 
-       if(savebmp(tempstr, dstbuf, scaledw, scaledh, pf,
-               (flags&TJFLAG_BOTTOMUP)!=0)==-1)
-               _throwbmp("saving bitmap");
+       if(tjSaveImage(tempstr, dstbuf, scaledw, 0, scaledh, pf, flags)==-1)
+               _throwtjg("saving bitmap");
        ptr=strrchr(tempstr, '.');
        snprintf(ptr, 1024-(ptr-tempstr), "-err.%s", ext);
        if(srcbuf && sf.num==1 && sf.denom==1)
@@ -301,9 +303,8 @@ int decomp(unsigned char *srcbuf, unsigned char **jpegbuf,
                                        dstbuf[pitch*row+col]
                                                =abs(dstbuf[pitch*row+col]-srcbuf[pitch*row+col]);
                }
-               if(savebmp(tempstr, dstbuf, w, h, pf,
-                       (flags&TJFLAG_BOTTOMUP)!=0)==-1)
-                       _throwbmp("saving bitmap");
+               if(tjSaveImage(tempstr, dstbuf, w, 0, h, pf, flags)==-1)
+                       _throwtjg("saving bitmap");
        }
 
        bailout:
@@ -998,8 +999,8 @@ int main(int argc, char *argv[])
 
        if(!decomponly)
        {
-               if(loadbmp(argv[1], &srcbuf, &w, &h, pf, (flags&TJFLAG_BOTTOMUP)!=0)==-1)
-                       _throwbmp("loading bitmap");
+               if((srcbuf=tjLoadImage(argv[1], &w, 1, &h, &pf, flags))==NULL)
+                       _throwtjg("loading bitmap");
                temp=strrchr(argv[1], '.');
                if(temp!=NULL) *temp='\0';
        }
@@ -1052,6 +1053,6 @@ int main(int argc, char *argv[])
        }
 
        bailout:
-       if(srcbuf) free(srcbuf);
+       if(srcbuf) tjFree(srcbuf);
        return retval;
 }
index f793796166ae186d3c82d38d53b5ce6e1e1742fe..ffe6fc707216ef638ca8c36cf511a29bcf65d843 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include "./tjutil.h"
-#include "./turbojpeg.h"
+#include "tjutil.h"
+#include "turbojpeg.h"
+#include "md5/md5.h"
+#include "cmyk.h"
 #ifdef _WIN32
  #include <time.h>
  #define random() rand()
+#else
+ #include <unistd.h>
 #endif
 
 
@@ -49,7 +53,8 @@ void usage(char *progName)
        printf("-yuv = test YUV encoding/decoding support\n");
        printf("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest\n");
        printf("            4-byte boundary\n");
-       printf("-alloc = test automatic buffer allocation\n\n");
+       printf("-alloc = test automatic buffer allocation\n");
+       printf("-bmp = tjLoadImage()/tjSaveImage() unit test\n\n");
        exit(1);
 }
 
@@ -58,6 +63,14 @@ void usage(char *progName)
        bailout();}
 #define _tj(f) {if((f)==-1) _throwtj();}
 #define _throw(m) {printf("ERROR: %s\n", m);  bailout();}
+#define _throwmd5(filename, md5sum, ref) {  \
+       printf("\n%s has an MD5 sum of %s.\n   Should be %s.\n", filename,  \
+               md5sum, ref);  \
+       bailout();  \
+}
+
+#define RGB2GRAY(r, g, b)  \
+       (unsigned char)((double)(r)*0.299+(double)(g)*0.587+(double)(b)*0.114+0.5)
 
 const char *subNameLong[TJ_NUMSAMP]=
 {
@@ -686,6 +699,231 @@ void bufSizeTest(void)
 }
 
 
+void initBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
+       int flags)
+{
+       int roffset=tjRedOffset[pf];
+       int goffset=tjGreenOffset[pf];
+       int boffset=tjBlueOffset[pf];
+       int ps=tjPixelSize[pf];
+       int i, j;
+
+       for(j=0; j<height; j++)
+       {
+               int row=(flags&TJFLAG_BOTTOMUP)? height-j-1:j;
+               for(i=0; i<width; i++)
+               {
+                       unsigned char r=(i*256/width)%256;
+                       unsigned char g=(j*256/height)%256;
+                       unsigned char b=(j*256/height+i*256/width)%256;
+                       memset(&buf[row*pitch+i*ps], 0, ps);
+                       if(pf==TJPF_GRAY) buf[row*pitch+i*ps]=RGB2GRAY(r, g, b);
+                       else if(pf==TJPF_CMYK)
+                               rgb_to_cmyk(r, g, b, &buf[row*pitch+i*ps+0], &buf[row*pitch+i*ps+1],
+                                       &buf[row*pitch+i*ps+2], &buf[row*pitch+i*ps+3]);
+                       else
+                       {
+                               buf[row*pitch+i*ps+roffset]=r;
+                               buf[row*pitch+i*ps+goffset]=g;
+                               buf[row*pitch+i*ps+boffset]=b;
+                       }
+               }
+       }
+}
+
+
+int cmpBitmap(unsigned char *buf, int width, int pitch, int height, int pf,
+       int flags, int gray2rgb)
+{
+       int roffset=tjRedOffset[pf];
+       int goffset=tjGreenOffset[pf];
+       int boffset=tjBlueOffset[pf];
+       int aoffset=alphaOffset[pf];
+       int ps=tjPixelSize[pf];
+       int i, j;
+
+       for(j=0; j<height; j++)
+       {
+               int row=(flags&TJFLAG_BOTTOMUP)? height-j-1:j;
+               for(i=0; i<width; i++)
+               {
+                       unsigned char r=(i*256/width)%256;
+                       unsigned char g=(j*256/height)%256;
+                       unsigned char b=(j*256/height+i*256/width)%256;
+                       if(pf==TJPF_GRAY)
+                       {
+                               if(buf[row*pitch+i*ps]!=RGB2GRAY(r, g, b))
+                                       return 0;
+                       }
+                       else if(pf==TJPF_CMYK)
+                       {
+                               unsigned char rf, gf, bf;
+                               cmyk_to_rgb(buf[row*pitch+i*ps+0], buf[row*pitch+i*ps+1],
+                                       buf[row*pitch+i*ps+2], buf[row*pitch+i*ps+3], &rf, &gf,
+                                       &bf);
+                               if(gray2rgb)
+                               {
+                                       unsigned char gray=RGB2GRAY(r, g, b);
+                                       if(rf!=gray || gf!=gray || bf!=gray)
+                                               return 0;
+                               }
+                               else if(rf!=r || gf!=g || bf!=b) return 0;
+                       }
+                       else
+                       {
+                               if(gray2rgb)
+                               {
+                                       unsigned char gray=RGB2GRAY(r, g, b);
+                                       if(buf[row*pitch+i*ps+roffset]!=gray ||
+                                               buf[row*pitch+i*ps+goffset]!=gray ||
+                                               buf[row*pitch+i*ps+boffset]!=gray)
+                                               return 0;
+                               }
+                               else if(buf[row*pitch+i*ps+roffset]!=r ||
+                                       buf[row*pitch+i*ps+goffset]!=g ||
+                                       buf[row*pitch+i*ps+boffset]!=b)
+                                       return 0;
+                               if(aoffset>=0 && buf[row*pitch+i*ps+aoffset]!=0xFF)
+                                       return 0;
+                       }
+               }
+       }
+       return 1;
+}
+
+
+int doBmpTest(const char *ext, int width, int align, int height, int pf,
+       int flags)
+{
+       char filename[80], *md5sum, md5buf[65];
+       int ps=tjPixelSize[pf], pitch=PAD(width*ps, align),
+               loadWidth=0, loadHeight=0, retval=0;
+       unsigned char *buf=NULL;
+       char *md5ref;
+
+       if(pf==TJPF_GRAY)
+       {
+               md5ref=!strcasecmp(ext, "ppm")? "bc77dea8eaf006aa187582b301f67e02":
+                       "2670a3f8cf19d855183c02ccf18d2a35";
+       }
+       else
+       {
+               md5ref=!strcasecmp(ext, "ppm")? "c0c9f772b464d1896326883a5c79c545":
+                       "6d659071b9bfcdee2def22cb58ddadca";
+       }
+
+       if((buf=(unsigned char *)tjAlloc(pitch*height))==NULL)
+               _throw("Could not allocate memory");
+       initBitmap(buf, width, pitch, height, pf, flags);
+
+       snprintf(filename, 80, "test_bmp_%s_%d_%s.%s", pixFormatStr[pf], align,
+               (flags&TJFLAG_BOTTOMUP)? "bu":"td", ext);
+       _tj(tjSaveImage(filename, buf, width, pitch, height, pf, flags));
+       md5sum=MD5File(filename, md5buf);
+       if(strcasecmp(md5sum, md5ref))
+               _throwmd5(filename, md5sum, md5ref);
+
+       tjFree(buf);  buf=NULL;
+       if((buf=tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
+               flags))==NULL)
+               _throwtj();
+       if(width!=loadWidth || height!=loadHeight)
+       {
+               printf("\n   Image dimensions of %s are bogus\n", filename);
+               retval=-1;  goto bailout;
+       }
+       if(!cmpBitmap(buf, width, pitch, height, pf, flags, 0))
+       {
+               printf("\n   Pixel data in %s is bogus\n", filename);
+               retval=-1;  goto bailout;
+       }
+       if(pf==TJPF_GRAY)
+       {
+               tjFree(buf);  buf=NULL;
+               pf=TJPF_XBGR;
+               if((buf=tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
+                       flags))==NULL)
+                       _throwtj();
+               pitch=PAD(width*tjPixelSize[pf], align);
+               if(!cmpBitmap(buf, width, pitch, height, pf, flags, 1))
+               {
+                       printf("\n   Converting %s to RGB failed\n", filename);
+                       retval=-1;  goto bailout;
+               }
+
+               tjFree(buf);  buf=NULL;
+               pf=TJPF_CMYK;
+               if((buf=tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
+                       flags))==NULL)
+                       _throwtj();
+               pitch=PAD(width*tjPixelSize[pf], align);
+               if(!cmpBitmap(buf, width, pitch, height, pf, flags, 1))
+               {
+                       printf("\n   Converting %s to CMYK failed\n", filename);
+                       retval=-1;  goto bailout;
+               }
+       }
+       else if(pf!=TJPF_CMYK)
+       {
+               tjFree(buf);  buf=NULL;
+               pf=TJPF_GRAY;
+               if((buf=tjLoadImage(filename, &loadWidth, align, &loadHeight, &pf,
+                       flags))==NULL)
+                       _throwtj();
+               pitch=PAD(width, align);
+               if(!cmpBitmap(buf, width, pitch, height, pf, flags, 0))
+               {
+                       printf("\n   Converting %s to grayscale failed\n", filename);
+                       retval=-1;  goto bailout;
+               }
+       }
+       unlink(filename);
+
+       bailout:
+       if(buf) tjFree(buf);
+       if(exitStatus<0) return exitStatus;
+       return retval;
+}
+
+
+int bmpTest(void)
+{
+       int align, width=35, height=39, format;
+
+       for(align=1; align<=8; align*=2)
+       {
+               for(format=0; format<TJ_NUMPF; format++)
+               {
+                       printf("%s Top-Down BMP (row alignment = %d bytes)  ...  ",
+                               pixFormatStr[format], align);
+                       if(doBmpTest("bmp", width, align, height, format, 0)==-1)
+                               return -1;
+                       printf("OK.\n");
+
+                       printf("%s Top-Down PPM (row alignment = %d bytes)  ...  ",
+                               pixFormatStr[format], align);
+                       if(doBmpTest("ppm", width, align, height, format, TJFLAG_BOTTOMUP)==-1)
+                               return -1;
+                       printf("OK.\n");
+
+                       printf("%s Bottom-Up BMP (row alignment = %d bytes)  ...  ",
+                               pixFormatStr[format], align);
+                       if(doBmpTest("bmp", width, align, height, format, 0)==-1)
+                               return -1;
+                       printf("OK.\n");
+
+                       printf("%s Bottom-Up PPM (row alignment = %d bytes)  ...  ",
+                               pixFormatStr[format], align);
+                       if(doBmpTest("ppm", width, align, height, format, TJFLAG_BOTTOMUP)==-1)
+                               return -1;
+                       printf("OK.\n");
+               }
+       }
+
+       return 0;
+}
+
+
 int main(int argc, char *argv[])
 {
        int i, num4bf=5;
@@ -699,6 +937,7 @@ int main(int argc, char *argv[])
                        if(!strcasecmp(argv[i], "-yuv")) doyuv=1;
                        else if(!strcasecmp(argv[i], "-noyuvpad")) pad=1;
                        else if(!strcasecmp(argv[i], "-alloc")) alloc=1;
+                       else if(!strcasecmp(argv[i], "-bmp")) return bmpTest();
                        else usage(argv[0]);
                }
        }
index 8df3687d0e3a10a8c0c144bdc9e01613f6796cff..cfb981d0a922a25d17fe6696d880fddedc03b043 100755 (executable)
@@ -60,4 +60,6 @@ TURBOJPEG_1.6
        global:
                tjGetErrorCode;
                tjGetErrorStr2;
+               tjLoadImage;
+               tjSaveImage;
 } TURBOJPEG_1.4;
index 9395dca51be0831f29c316f5ef2002f45284f335..eccebc9ebfb886f1b3e73ef214b56fc200c05afc 100755 (executable)
@@ -96,4 +96,6 @@ TURBOJPEG_1.6
        global:
                tjGetErrorCode;
                tjGetErrorStr2;
+               tjLoadImage;
+               tjSaveImage;
 } TURBOJPEG_1.4;
index 5f0439c81ce2d2942246da7299cd3f684d27270f..46ea36b9add660db152b617c038c78d36f29eb67 100644 (file)
 #include <jpeglib.h>
 #include <jerror.h>
 #include <setjmp.h>
+#include <errno.h>
 #include "./turbojpeg.h"
 #include "./tjutil.h"
 #include "transupp.h"
 #include "./jpegcomp.h"
+#include "./cdjpeg.h"
 
 extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **,
        unsigned long *, boolean);
@@ -132,8 +134,40 @@ static const tjscalingfactor sf[NUMSF]={
        {1, 8}
 };
 
+static J_COLOR_SPACE pf2cs[TJ_NUMPF] =
+{
+       JCS_EXT_RGB, JCS_EXT_BGR, JCS_EXT_RGBX, JCS_EXT_BGRX, JCS_EXT_XBGR,
+       JCS_EXT_XRGB, JCS_GRAYSCALE, JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ABGR,
+       JCS_EXT_ARGB, JCS_CMYK
+};
+
+static int cs2pf[JPEG_NUMCS] =
+{
+       TJPF_UNKNOWN, TJPF_GRAY,
+#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
+       TJPF_RGB,
+#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 3
+       TJPF_BGR,
+#elif RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 4
+       TJPF_RGBX,
+#elif RGB_RED == 2 && RGB_GREEN == 1 && RGB_BLUE == 0 && RGB_PIXELSIZE == 4
+       TJPF_BGRX,
+#elif RGB_RED == 3 && RGB_GREEN == 2 && RGB_BLUE == 1 && RGB_PIXELSIZE == 4
+       TJPF_XBGR,
+#elif RGB_RED == 1 && RGB_GREEN == 2 && RGB_BLUE == 3 && RGB_PIXELSIZE == 4
+       TJPF_XRGB,
+#endif
+       TJPF_UNKNOWN, TJPF_CMYK, TJPF_UNKNOWN, TJPF_RGB, TJPF_RGBX, TJPF_BGR,
+       TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_RGBA, TJPF_BGRA, TJPF_ABGR, TJPF_ARGB,
+       TJPF_UNKNOWN
+};
+
 #define _throwg(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;  \
+}
 #define _throw(m) {snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m);  \
        this->isInstanceError=TRUE;  _throwg(m);}
 #define getinstance(handle) tjinstance *this=(tjinstance *)handle;  \
@@ -188,45 +222,7 @@ static int setCompDefaults(struct jpeg_compress_struct *cinfo,
        int retval=0;
        char *env=NULL;
 
-       switch(pixelFormat)
-       {
-               case TJPF_GRAY:
-                       cinfo->in_color_space=JCS_GRAYSCALE;  break;
-               #if JCS_EXTENSIONS==1
-               case TJPF_RGB:
-                       cinfo->in_color_space=JCS_EXT_RGB;  break;
-               case TJPF_BGR:
-                       cinfo->in_color_space=JCS_EXT_BGR;  break;
-               case TJPF_RGBX:
-               case TJPF_RGBA:
-                       cinfo->in_color_space=JCS_EXT_RGBX;  break;
-               case TJPF_BGRX:
-               case TJPF_BGRA:
-                       cinfo->in_color_space=JCS_EXT_BGRX;  break;
-               case TJPF_XRGB:
-               case TJPF_ARGB:
-                       cinfo->in_color_space=JCS_EXT_XRGB;  break;
-               case TJPF_XBGR:
-               case TJPF_ABGR:
-                       cinfo->in_color_space=JCS_EXT_XBGR;  break;
-               #else
-               case TJPF_RGB:
-               case TJPF_BGR:
-               case TJPF_RGBX:
-               case TJPF_BGRX:
-               case TJPF_XRGB:
-               case TJPF_XBGR:
-               case TJPF_RGBA:
-               case TJPF_BGRA:
-               case TJPF_ARGB:
-               case TJPF_ABGR:
-                       cinfo->in_color_space=JCS_RGB;  pixelFormat=TJPF_RGB;
-                       break;
-               #endif
-               case TJPF_CMYK:
-                       cinfo->in_color_space=JCS_CMYK;  break;
-       }
-
+       cinfo->in_color_space=pf2cs[pixelFormat];
        cinfo->input_components=tjPixelSize[pixelFormat];
        jpeg_set_defaults(cinfo);
 
@@ -285,62 +281,6 @@ static int setCompDefaults(struct jpeg_compress_struct *cinfo,
        return retval;
 }
 
-static int setDecompDefaults(tjinstance *this, int pixelFormat, int flags)
-{
-       int retval=0;
-
-       switch(pixelFormat)
-       {
-               case TJPF_GRAY:
-                       this->dinfo.out_color_space=JCS_GRAYSCALE;  break;
-               #if JCS_EXTENSIONS==1
-               case TJPF_RGB:
-                       this->dinfo.out_color_space=JCS_EXT_RGB;  break;
-               case TJPF_BGR:
-                       this->dinfo.out_color_space=JCS_EXT_BGR;  break;
-               case TJPF_RGBX:
-                       this->dinfo.out_color_space=JCS_EXT_RGBX;  break;
-               case TJPF_BGRX:
-                       this->dinfo.out_color_space=JCS_EXT_BGRX;  break;
-               case TJPF_XRGB:
-                       this->dinfo.out_color_space=JCS_EXT_XRGB;  break;
-               case TJPF_XBGR:
-                       this->dinfo.out_color_space=JCS_EXT_XBGR;  break;
-               #if JCS_ALPHA_EXTENSIONS==1
-               case TJPF_RGBA:
-                       this->dinfo.out_color_space=JCS_EXT_RGBA;  break;
-               case TJPF_BGRA:
-                       this->dinfo.out_color_space=JCS_EXT_BGRA;  break;
-               case TJPF_ARGB:
-                       this->dinfo.out_color_space=JCS_EXT_ARGB;  break;
-               case TJPF_ABGR:
-                       this->dinfo.out_color_space=JCS_EXT_ABGR;  break;
-               #endif
-               #else
-               case TJPF_RGB:
-               case TJPF_BGR:
-               case TJPF_RGBX:
-               case TJPF_BGRX:
-               case TJPF_XRGB:
-               case TJPF_XBGR:
-               case TJPF_RGBA:
-               case TJPF_BGRA:
-               case TJPF_ARGB:
-               case TJPF_ABGR:
-                       this->dinfo.out_color_space=JCS_RGB;  break;
-               #endif
-               case TJPF_CMYK:
-                       this->dinfo.out_color_space=JCS_CMYK;  break;
-               default:
-                       _throw("Unsupported pixel format");
-       }
-
-       if(flags&TJFLAG_FASTDCT) this->dinfo.dct_method=JDCT_FASTEST;
-
-       bailout:
-       return retval;
-}
-
 
 static int getSubsamp(j_decompress_ptr dinfo)
 {
@@ -409,149 +349,6 @@ static int getSubsamp(j_decompress_ptr dinfo)
 }
 
 
-#ifndef JCS_EXTENSIONS
-
-/* Conversion functions to emulate the colorspace extensions.  This allows the
-   TurboJPEG wrapper to be used with libjpeg */
-
-#define TORGB(PS, ROFFSET, GOFFSET, BOFFSET) {  \
-       int rowPad=pitch-width*PS;  \
-       while(height--)  \
-       {  \
-               unsigned char *endOfRow=src+width*PS;  \
-               while(src<endOfRow)  \
-               {  \
-                       dst[RGB_RED]=src[ROFFSET];  \
-                       dst[RGB_GREEN]=src[GOFFSET];  \
-                       dst[RGB_BLUE]=src[BOFFSET];  \
-                       dst+=RGB_PIXELSIZE;  src+=PS;  \
-               }  \
-               src+=rowPad;  \
-       }  \
-}
-
-static unsigned char *toRGB(unsigned char *src, int width, int pitch,
-       int height, int pixelFormat, unsigned char *dst)
-{
-       unsigned char *retval=src;
-       switch(pixelFormat)
-       {
-               case TJPF_RGB:
-                       #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
-                       retval=dst;  TORGB(3, 0, 1, 2);
-                       #endif
-                       break;
-               case TJPF_BGR:
-                       #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
-                       retval=dst;  TORGB(3, 2, 1, 0);
-                       #endif
-                       break;
-               case TJPF_RGBX:
-               case TJPF_RGBA:
-                       #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
-                       retval=dst;  TORGB(4, 0, 1, 2);
-                       #endif
-                       break;
-               case TJPF_BGRX:
-               case TJPF_BGRA:
-                       #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
-                       retval=dst;  TORGB(4, 2, 1, 0);
-                       #endif
-                       break;
-               case TJPF_XRGB:
-               case TJPF_ARGB:
-                       #if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
-                       retval=dst;  TORGB(4, 1, 2, 3);
-                       #endif
-                       break;
-               case TJPF_XBGR:
-               case TJPF_ABGR:
-                       #if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
-                       retval=dst;  TORGB(4, 3, 2, 1);
-                       #endif
-                       break;
-       }
-       return retval;
-}
-
-#define FROMRGB(PS, ROFFSET, GOFFSET, BOFFSET, SETALPHA) {  \
-       int rowPad=pitch-width*PS;  \
-       while(height--)  \
-       {  \
-               unsigned char *endOfRow=dst+width*PS;  \
-               while(dst<endOfRow)  \
-               {  \
-                       dst[ROFFSET]=src[RGB_RED];  \
-                       dst[GOFFSET]=src[RGB_GREEN];  \
-                       dst[BOFFSET]=src[RGB_BLUE];  \
-                       SETALPHA  \
-                       dst+=PS;  src+=RGB_PIXELSIZE;  \
-               }  \
-               dst+=rowPad;  \
-       }  \
-}
-
-static void fromRGB(unsigned char *src, unsigned char *dst, int width,
-       int pitch, int height, int pixelFormat)
-{
-       switch(pixelFormat)
-       {
-               case TJPF_RGB:
-                       #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3
-                       FROMRGB(3, 0, 1, 2,);
-                       #endif
-                       break;
-               case TJPF_BGR:
-                       #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3
-                       FROMRGB(3, 2, 1, 0,);
-                       #endif
-                       break;
-               case TJPF_RGBX:
-                       #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 0, 1, 2,);
-                       #endif
-                       break;
-               case TJPF_RGBA:
-                       #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 0, 1, 2, dst[3]=0xFF;);
-                       #endif
-                       break;
-               case TJPF_BGRX:
-                       #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 2, 1, 0,);
-                       #endif
-                       break;
-               case TJPF_BGRA:
-                       #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 2, 1, 0, dst[3]=0xFF;);  return;
-                       #endif
-                       break;
-               case TJPF_XRGB:
-                       #if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 1, 2, 3,);  return;
-                       #endif
-                       break;
-               case TJPF_ARGB:
-                       #if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 1, 2, 3, dst[0]=0xFF;);  return;
-                       #endif
-                       break;
-               case TJPF_XBGR:
-                       #if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 3, 2, 1,);  return;
-                       #endif
-                       break;
-               case TJPF_ABGR:
-                       #if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4
-                       FROMRGB(4, 3, 2, 1, dst[0]=0xFF;);  return;
-                       #endif
-                       break;
-       }
-}
-
-#endif
-
-
 /* General API functions */
 
 DLLEXPORT char* DLLCALL tjGetErrorStr2(tjhandle handle)
@@ -792,9 +589,6 @@ DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, const unsigned char *srcBuf,
        unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags)
 {
        int i, retval=0, alloc=1;  JSAMPROW *row_pointer=NULL;
-       #ifndef JCS_EXTENSIONS
-       unsigned char *rgbBuf=NULL;
-       #endif
 
        getcinstance(handle)
        this->jerr.stopOnWarning=(flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
@@ -808,16 +602,6 @@ DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, const unsigned char *srcBuf,
 
        if(pitch==0) pitch=width*tjPixelSize[pixelFormat];
 
-       #ifndef JCS_EXTENSIONS
-       if(pixelFormat!=TJPF_GRAY && pixelFormat!=TJPF_CMYK)
-       {
-               rgbBuf=(unsigned char *)malloc(width*height*RGB_PIXELSIZE);
-               if(!rgbBuf) _throw("tjCompress2(): Memory allocation failure");
-               srcBuf=toRGB(srcBuf, width, pitch, height, pixelFormat, rgbBuf);
-               pitch=width*RGB_PIXELSIZE;
-       }
-       #endif
-
        if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL)
                _throw("tjCompress2(): Memory allocation failure");
 
@@ -858,9 +642,6 @@ DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, const unsigned char *srcBuf,
 
        bailout:
        if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo);
-       #ifndef JCS_EXTENSIONS
-       if(rgbBuf) free(rgbBuf);
-       #endif
        if(row_pointer) free(row_pointer);
        if(this->jerr.warning) retval=-1;
        this->jerr.stopOnWarning=FALSE;
@@ -901,9 +682,6 @@ DLLEXPORT int DLLCALL tjEncodeYUVPlanes(tjhandle handle,
        int row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
        JSAMPLE *ptr;
        jpeg_component_info *compptr;
-       #ifndef JCS_EXTENSIONS
-       unsigned char *rgbBuf=NULL;
-       #endif
 
        getcinstance(handle);
        this->jerr.stopOnWarning=(flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
@@ -929,16 +707,6 @@ DLLEXPORT int DLLCALL tjEncodeYUVPlanes(tjhandle handle,
 
        if(pitch==0) pitch=width*tjPixelSize[pixelFormat];
 
-       #ifndef JCS_EXTENSIONS
-       if(pixelFormat!=TJPF_GRAY && pixelFormat!=TJPF_CMYK)
-       {
-               rgbBuf=(unsigned char *)malloc(width*height*RGB_PIXELSIZE);
-               if(!rgbBuf) _throw("tjEncodeYUVPlanes(): Memory allocation failure");
-               srcBuf=toRGB(srcBuf, width, pitch, height, pixelFormat, rgbBuf);
-               pitch=width*RGB_PIXELSIZE;
-       }
-       #endif
-
        if(setjmp(this->jerr.setjmp_buffer))
        {
                /* If we get here, the JPEG code has signaled an error. */
@@ -1042,9 +810,6 @@ DLLEXPORT int DLLCALL tjEncodeYUVPlanes(tjhandle handle,
 
        bailout:
        if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo);
-       #ifndef JCS_EXTENSIONS
-       if(rgbBuf) free(rgbBuf);
-       #endif
        if(row_pointer) free(row_pointer);
        for(i=0; i<MAX_COMPONENTS; i++)
        {
@@ -1427,10 +1192,6 @@ DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle,
 {
        int i, retval=0;  JSAMPROW *row_pointer=NULL;
        int jpegwidth, jpegheight, scaledw, scaledh;
-       #ifndef JCS_EXTENSIONS
-       unsigned char *rgbBuf=NULL;
-       unsigned char *_dstBuf=NULL;  int _pitch=0;
-       #endif
 
        getdinstance(handle);
        this->jerr.stopOnWarning=(flags & TJFLAG_STOPONWARNING) ? TRUE : FALSE;
@@ -1453,11 +1214,8 @@ DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle,
 
        jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
        jpeg_read_header(dinfo, TRUE);
-       if(setDecompDefaults(this, pixelFormat, flags)==-1)
-       {
-               retval=-1;  goto bailout;
-       }
-
+       this->dinfo.out_color_space=pf2cs[pixelFormat];
+       if(flags&TJFLAG_FASTDCT) this->dinfo.dct_method=JDCT_FASTEST;
        if(flags&TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling=FALSE;
 
        jpegwidth=dinfo->image_width;  jpegheight=dinfo->image_height;
@@ -1479,20 +1237,6 @@ DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle,
        jpeg_start_decompress(dinfo);
        if(pitch==0) pitch=dinfo->output_width*tjPixelSize[pixelFormat];
 
-       #ifndef JCS_EXTENSIONS
-       if(pixelFormat!=TJPF_GRAY && pixelFormat!=TJPF_CMYK &&
-               (RGB_RED!=tjRedOffset[pixelFormat] ||
-                       RGB_GREEN!=tjGreenOffset[pixelFormat] ||
-                       RGB_BLUE!=tjBlueOffset[pixelFormat] ||
-                       RGB_PIXELSIZE!=tjPixelSize[pixelFormat]))
-       {
-               rgbBuf=(unsigned char *)malloc(width*height*3);
-               if(!rgbBuf) _throw("tjDecompress2(): Memory allocation failure");
-               _pitch=pitch;  pitch=width*3;
-               _dstBuf=dstBuf;  dstBuf=rgbBuf;
-       }
-       #endif
-
        if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)
                *dinfo->output_height))==NULL)
                _throw("tjDecompress2(): Memory allocation failure");
@@ -1514,15 +1258,8 @@ DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle,
        }
        jpeg_finish_decompress(dinfo);
 
-       #ifndef JCS_EXTENSIONS
-       fromRGB(rgbBuf, _dstBuf, width, _pitch, height, pixelFormat);
-       #endif
-
        bailout:
        if(dinfo->global_state>DSTATE_START) jpeg_abort_decompress(dinfo);
-       #ifndef JCS_EXTENSIONS
-       if(rgbBuf) free(rgbBuf);
-       #endif
        if(row_pointer) free(row_pointer);
        if(this->jerr.warning) retval=-1;
        this->jerr.stopOnWarning=FALSE;
@@ -1605,10 +1342,6 @@ DLLEXPORT int DLLCALL tjDecodeYUVPlanes(tjhandle handle,
        int row, pw0, ph0, pw[MAX_COMPONENTS], ph[MAX_COMPONENTS];
        JSAMPLE *ptr;
        jpeg_component_info *compptr;
-       #ifndef JCS_EXTENSIONS
-       unsigned char *rgbBuf=NULL;
-       unsigned char *_dstBuf=NULL;  int _pitch=0;
-       #endif
        int (*old_read_markers)(j_decompress_ptr);
        void (*old_reset_marker_reader)(j_decompress_ptr);
 
@@ -1659,10 +1392,8 @@ DLLEXPORT int DLLCALL tjDecodeYUVPlanes(tjhandle handle,
        dinfo->marker->read_markers=old_read_markers;
        dinfo->marker->reset_marker_reader=old_reset_marker_reader;
 
-       if(setDecompDefaults(this, pixelFormat, flags)==-1)
-       {
-               retval=-1;  goto bailout;
-       }
+       this->dinfo.out_color_space=pf2cs[pixelFormat];
+       if(flags&TJFLAG_FASTDCT) this->dinfo.dct_method=JDCT_FASTEST;
        dinfo->do_fancy_upsampling=FALSE;
        dinfo->Se=DCTSIZE2-1;
        jinit_master_decompress(dinfo);
@@ -1673,20 +1404,6 @@ DLLEXPORT int DLLCALL tjDecodeYUVPlanes(tjhandle handle,
 
        if(pitch==0) pitch=dinfo->output_width*tjPixelSize[pixelFormat];
 
-       #ifndef JCS_EXTENSIONS
-       if(pixelFormat!=TJPF_GRAY && pixelFormat!=TJPF_CMYK &&
-               (RGB_RED!=tjRedOffset[pixelFormat] ||
-                       RGB_GREEN!=tjGreenOffset[pixelFormat] ||
-                       RGB_BLUE!=tjBlueOffset[pixelFormat] ||
-                       RGB_PIXELSIZE!=tjPixelSize[pixelFormat]))
-       {
-               rgbBuf=(unsigned char *)malloc(width*height*3);
-               if(!rgbBuf) _throw("tjDecodeYUVPlanes(): Memory allocation failure");
-               _pitch=pitch;  pitch=width*3;
-               _dstBuf=dstBuf;  dstBuf=rgbBuf;
-       }
-       #endif
-
        if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*ph0))==NULL)
                _throw("tjDecodeYUVPlanes(): Memory allocation failure");
        for(i=0; i<height; i++)
@@ -1743,15 +1460,8 @@ DLLEXPORT int DLLCALL tjDecodeYUVPlanes(tjhandle handle,
        }
        jpeg_abort_decompress(dinfo);
 
-       #ifndef JCS_EXTENSIONS
-       fromRGB(rgbBuf, _dstBuf, width, _pitch, height, pixelFormat);
-       #endif
-
        bailout:
        if(dinfo->global_state>DSTATE_START) jpeg_abort_decompress(dinfo);
-       #ifndef JCS_EXTENSIONS
-       if(rgbBuf) free(rgbBuf);
-       #endif
        if(row_pointer) free(row_pointer);
        for(i=0; i<MAX_COMPONENTS; i++)
        {
@@ -2240,3 +1950,168 @@ DLLEXPORT int DLLCALL tjTransform(tjhandle handle,
        this->jerr.stopOnWarning=FALSE;
        return retval;
 }
+
+
+DLLEXPORT unsigned char* DLLCALL tjLoadImage(const char *filename, int *width,
+       int align, int *height, int *pixelFormat, int flags)
+{
+       int retval=0, tempc, pitch;
+       tjhandle handle=NULL;
+       tjinstance *this;
+       j_compress_ptr cinfo=NULL;
+       cjpeg_source_ptr src;
+       unsigned char *dstBuf=NULL;
+       FILE *file=NULL;
+       boolean invert;
+
+       if(!filename || !width || align<1 || !height || !pixelFormat ||
+               *pixelFormat<TJPF_UNKNOWN || *pixelFormat>=TJ_NUMPF)
+               _throwg("tjLoadImage(): Invalid argument");
+       if((align&(align-1))!=0)
+               _throwg("tjLoadImage(): Alignment must be a power of 2");
+
+       if((handle=tjInitCompress())==NULL) return NULL;
+       this=(tjinstance *)handle;
+       cinfo=&this->cinfo;
+
+       if((file=fopen(filename, "rb"))==NULL)
+               _throwunix("tjLoadImage(): Cannot open input file");
+
+       if((tempc=getc(file))<0 || ungetc(tempc, file)==EOF)
+               _throwunix("tjLoadImage(): Could not read input file")
+       else if(tempc==EOF) _throwg("tjLoadImage(): Input file contains no data");
+
+       if(setjmp(this->jerr.setjmp_buffer))
+       {
+               /* If we get here, the JPEG code has signaled an error. */
+               retval=-1;  goto bailout;
+       }
+
+       cinfo->in_color_space=pf2cs[*pixelFormat];
+       if(tempc=='B')
+       {
+               if((src=jinit_read_bmp(cinfo, FALSE))==NULL)
+                       _throwg("tjLoadImage(): Could not initialize bitmap loader");
+               invert=(flags&TJFLAG_BOTTOMUP)==0;
+       }
+       else if(tempc=='P')
+       {
+               if((src=jinit_read_ppm(cinfo))==NULL)
+                       _throwg("tjLoadImage(): Could not initialize bitmap loader");
+               invert=(flags&TJFLAG_BOTTOMUP)!=0;
+       }
+       else _throwg("tjLoadImage(): Unsupported file type");
+
+       src->input_file=file;
+       (*src->start_input)(cinfo, src);
+       (*cinfo->mem->realize_virt_arrays)((j_common_ptr)cinfo);
+
+       *width=cinfo->image_width;  *height=cinfo->image_height;
+       *pixelFormat=cs2pf[cinfo->in_color_space];
+
+       pitch=PAD((*width)*tjPixelSize[*pixelFormat], align);
+       if((dstBuf=(unsigned char *)malloc(pitch*(*height)))==NULL)
+               _throwg("tjLoadImage(): Memory allocation failure");
+
+       if(setjmp(this->jerr.setjmp_buffer))
+       {
+               /* If we get here, the JPEG code has signaled an error. */
+               retval=-1;  goto bailout;
+       }
+
+       while(cinfo->next_scanline<cinfo->image_height)
+       {
+               int i, nlines=(*src->get_pixel_rows)(cinfo, src);
+               for(i=0; i<nlines; i++)
+               {
+                       unsigned char *dstptr;  int row;
+                       row=cinfo->next_scanline+i;
+                       if(invert) dstptr=&dstBuf[((*height)-row-1)*pitch];
+                       else dstptr=&dstBuf[row*pitch];
+                       memcpy(dstptr, src->buffer[i], (*width)*tjPixelSize[*pixelFormat]);
+               }
+               cinfo->next_scanline+=nlines;
+       }
+
+       (*src->finish_input)(cinfo, src);
+
+       bailout:
+       if(handle) tjDestroy(handle);
+       if(file) fclose(file);
+       if(retval<0 && dstBuf) {free(dstBuf);  dstBuf=NULL;}
+       return dstBuf;
+}
+
+
+DLLEXPORT int DLLCALL tjSaveImage(const char *filename, unsigned char *buffer,
+       int width, int pitch, int height, int pixelFormat, int flags)
+{
+       int retval=0;
+       tjhandle handle=NULL;
+       tjinstance *this;
+       j_decompress_ptr dinfo=NULL;
+       djpeg_dest_ptr dst;
+       FILE *file=NULL;
+       char *ptr=NULL;
+       boolean invert;
+
+       if(!filename || !buffer || width<1 || pitch<0 || height<1 || pixelFormat<0 ||
+               pixelFormat>=TJ_NUMPF)
+               _throwg("tjSaveImage(): Invalid argument");
+
+       if((handle=tjInitDecompress())==NULL)
+               return -1;
+       this=(tjinstance *)handle;
+       dinfo=&this->dinfo;
+
+       if((file=fopen(filename, "wb"))==NULL)
+               _throwunix("tjSaveImage(): Cannot open output file");
+
+       if(setjmp(this->jerr.setjmp_buffer))
+       {
+               /* If we get here, the JPEG code has signaled an error. */
+               retval=-1;  goto bailout;
+       }
+
+       this->dinfo.out_color_space=pf2cs[pixelFormat];
+       dinfo->image_width=width;  dinfo->image_height=height;
+       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, FALSE, FALSE))==NULL)
+                       _throwg("tjSaveImage(): Could not initialize bitmap writer");
+               invert=(flags&TJFLAG_BOTTOMUP)==0;
+       }
+       else
+       {
+               if((dst=jinit_write_ppm(dinfo))==NULL)
+                       _throwg("tjSaveImage(): Could not initialize PPM writer");
+               invert=(flags&TJFLAG_BOTTOMUP)!=0;
+       }
+
+       dst->output_file=file;
+       (*dst->start_output)(dinfo, dst);
+       (*dinfo->mem->realize_virt_arrays)((j_common_ptr)dinfo);
+
+       if(pitch==0) pitch=width*tjPixelSize[pixelFormat];
+
+       while(dinfo->output_scanline<dinfo->output_height)
+       {
+               unsigned char *rowptr;
+               if(invert) rowptr=&buffer[(height-dinfo->output_scanline-1)*pitch];
+               else rowptr=&buffer[dinfo->output_scanline*pitch];
+               memcpy(dst->buffer[0], rowptr, width*tjPixelSize[pixelFormat]);
+               (*dst->put_pixel_rows)(dinfo, dst, 1);
+               dinfo->output_scanline++;
+       }
+
+       (*dst->finish_output)(dinfo, dst);
+
+       bailout:
+       if(handle) tjDestroy(handle);
+       if(file) fclose(file);
+       return retval;
+}
index f7fef7b322191bed05c9e0f20bdf947b8a7f3078..36740eb44b5090356898d863dfc457275dd57e32 100644 (file)
@@ -249,7 +249,11 @@ enum TJPF
    * CMYK pixels into a YCCK JPEG image (see #TJCS_YCCK) and decompressing YCCK
    * JPEG images into CMYK pixels.
    */
-  TJPF_CMYK
+  TJPF_CMYK,
+  /**
+   * Unknown pixel format.  Currently this is only used by #tjLoadImage().
+   */
+  TJPF_UNKNOWN = -1
 };
 
 
@@ -1514,6 +1518,86 @@ DLLEXPORT int DLLCALL tjDestroy(tjhandle handle);
 DLLEXPORT unsigned char* DLLCALL tjAlloc(int bytes);
 
 
+/**
+ * Load an uncompressed image from disk into memory.
+ *
+ * @param filename name of a file containing an uncompressed image in Windows
+ * BMP or PBMPLUS (PPM/PGM) format
+ *
+ * @param width pointer to an integer variable that will receive the width (in
+ * pixels) of the uncompressed image
+ *
+ * @param align row alignment of the image buffer to be returned (must be a
+ * power of 2.)  For instance, setting this parameter to 4 will cause all rows
+ * in the image buffer to be padded to the nearest 32-bit boundary, and setting
+ * this parameter to 1 will cause all rows in the image buffer to be unpadded.
+ *
+ * @param height pointer to an integer variable that will receive the height
+ * (in pixels) of the uncompressed image
+ *
+ * @param pixelFormat pointer to an integer variable that specifies or will
+ * receive the pixel format of the uncompressed image buffer.  If
+ * <tt>*pixelFormat</tt> is set to @ref TJPF_UNKNOWN prior to calling this
+ * function, then the uncompressed image buffer returned by the function will
+ * use the most optimal pixel format for the file type, and
+ * <tt>*pixelFormat</tt> will contain the ID of this pixel format upon
+ * successful return from the function.  Otherwise, the uncompressed image
+ * buffer will use the @ref TJPF "pixel format" specified in
+ * <tt>*pixelFormat</tt>, and pixel format conversion will be performed if
+ * necessary.  If <tt>*pixelFormat</tt> is set to @ref TJPF_CMYK, then the RGB
+ * or grayscale pixels stored in the file will be converted using a quick &
+ * dirty algorithm that is suitable only for testing purposes (proper
+ * conversion between CMYK and other formats requires a color management
+ * system.)
+ *
+ * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
+ * "flags".
+ *
+ * @return a pointer to a newly-allocated buffer containing the uncompressed
+ * image, converted to the chosen pixel format and with the chosen row
+ * alignment, or NULL if an error occurred (see #tjGetErrorStr2().)  This
+ * buffer should be freed using #tjFree().
+ */
+DLLEXPORT unsigned char* DLLCALL tjLoadImage(const char *filename, int *width,
+  int align, int *height, int *pixelFormat, int flags);
+
+
+/**
+ * Save an uncompressed image from memory to disk.
+ *
+ * @param filename name of a file to which to save the uncompressed image.
+ * The image will be stored in Windows BMP or PBMPLUS (PPM/PGM) format,
+ * depending on the file extension.
+ *
+ * @param buffer pointer to an image buffer containing RGB, grayscale, or
+ * CMYK pixels to be saved
+ *
+ * @param width width (in pixels) of the uncompressed image
+ *
+ * @param pitch bytes per line in the image buffer.  Setting this parameter to
+ * 0 is the equivalent of setting it to
+ * <tt>width * #tjPixelSize[pixelFormat]</tt>.
+ *
+ * @param height height (in pixels) of the uncompressed image
+ *
+ * @param pixelFormat pixel format of the image buffer (see @ref TJPF
+ * "Pixel formats".)  If this parameter is set to @ref TJPF_GRAY, then the
+ * image will be stored in PGM or 8-bit (indexed color) BMP format.  Otherwise,
+ * the image will be stored in PPM or 24-bit BMP format.  If this parameter
+ * is set to @ref TJPF_CMYK, then the CMYK pixels will be converted to RGB
+ * using a quick & dirty algorithm that is suitable only for testing (proper
+ * conversion between CMYK and other formats requires a color management
+ * system.)
+ *
+ * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP
+ * "flags".
+ *
+ * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr2().)
+ */
+DLLEXPORT int DLLCALL tjSaveImage(const char *filename, unsigned char *buffer,
+  int width, int pitch, int height, int pixelFormat, int flags);
+
+
 /**
  * Free an image buffer previously allocated by TurboJPEG.  You should always
  * use this function to free JPEG destination buffer(s) that were automatically
diff --git a/wrbmp.c b/wrbmp.c
index 728bbadd751b886632902ef170a3d61592a634aa..d2e43f5423e4f60ff8800d0c0b4841ffff54596e 100644 (file)
--- a/wrbmp.c
+++ b/wrbmp.c
@@ -23,6 +23,7 @@
 
 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
 #include "jconfigint.h"
+#include "cmyk.h"
 
 #ifdef BMP_SUPPORTED
 
@@ -56,6 +57,18 @@ typedef struct {
   JDIMENSION row_width;         /* physical width of one row in the BMP file */
   int pad_bytes;                /* number of padding bytes needed per row */
   JDIMENSION cur_output_row;    /* next row# to write to virtual array */
+
+  boolean use_inversion_array;  /* TRUE = buffer the whole image, which is
+                                   stored to disk in bottom-up order, and
+                                   receive rows from the calling program in
+                                   top-down order
+
+                                   FALSE = the calling program will maintain
+                                   its own image buffer and write the rows in
+                                   bottom-up order */
+
+  JSAMPLE *iobuffer;            /* I/O buffer (used to buffer a single row to
+                                   disk if use_inversion_array == FALSE) */
 } bmp_dest_struct;
 
 typedef bmp_dest_struct *bmp_dest_ptr;
@@ -92,19 +105,26 @@ put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
   register JDIMENSION col;
   int pad;
 
-  /* Access next row in virtual array */
-  image_ptr = (*cinfo->mem->access_virt_sarray)
-    ((j_common_ptr) cinfo, dest->whole_image,
-     dest->cur_output_row, (JDIMENSION) 1, TRUE);
-  dest->cur_output_row++;
+  if (dest->use_inversion_array) {
+    /* Access next row in virtual array */
+    image_ptr = (*cinfo->mem->access_virt_sarray)
+      ((j_common_ptr) cinfo, dest->whole_image,
+       dest->cur_output_row, (JDIMENSION) 1, TRUE);
+    dest->cur_output_row++;
+    outptr = image_ptr[0];
+  } else {
+    outptr = dest->iobuffer;
+  }
 
   /* Transfer data.  Note destination values must be in BGR order
    * (even though Microsoft's own documents say the opposite).
    */
   inptr = dest->pub.buffer[0];
-  outptr = image_ptr[0];
 
-  if (cinfo->out_color_space == JCS_RGB565) {
+  if (cinfo->out_color_space == JCS_EXT_BGR) {
+    MEMCOPY(outptr, inptr, dest->row_width);
+    outptr += cinfo->output_width * 3;
+  } else if (cinfo->out_color_space == JCS_RGB565) {
     boolean big_endian = is_big_endian();
     unsigned short *inptr2 = (unsigned short *)inptr;
     for (col = cinfo->output_width; col > 0; col--) {
@@ -120,19 +140,35 @@ put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
       outptr += 3;
       inptr2++;
     }
-  } else {
+  } else if (cinfo->out_color_space == JCS_CMYK) {
     for (col = cinfo->output_width; col > 0; col--) {
-      outptr[2] = *inptr++;       /* can omit GETJSAMPLE() safely */
-      outptr[1] = *inptr++;
-      outptr[0] = *inptr++;
+      /* can omit GETJSAMPLE() safely */
+      JSAMPLE c = *inptr++, m = *inptr++, y = *inptr++, k = *inptr++;
+      cmyk_to_rgb(c, m, y, k, outptr + 2, outptr + 1, outptr);
       outptr += 3;
     }
+  } else {
+    register int rindex = rgb_red[cinfo->out_color_space];
+    register int gindex = rgb_green[cinfo->out_color_space];
+    register int bindex = rgb_blue[cinfo->out_color_space];
+    register int ps = rgb_pixelsize[cinfo->out_color_space];
+
+    for (col = cinfo->output_width; col > 0; col--) {
+      /* can omit GETJSAMPLE() safely */
+      outptr[0] = inptr[bindex];
+      outptr[1] = inptr[gindex];
+      outptr[2] = inptr[rindex];
+      outptr += 3;  inptr += ps;
+    }
   }
 
   /* Zero out the pad bytes. */
   pad = dest->pad_bytes;
   while (--pad >= 0)
     *outptr++ = 0;
+
+  if (!dest->use_inversion_array)
+    (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->row_width);
 }
 
 METHODDEF(void)
@@ -143,38 +179,31 @@ put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
   bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
   JSAMPARRAY image_ptr;
   register JSAMPROW inptr, outptr;
-  register JDIMENSION col;
   int pad;
 
-  /* Access next row in virtual array */
-  image_ptr = (*cinfo->mem->access_virt_sarray)
-    ((j_common_ptr) cinfo, dest->whole_image,
-     dest->cur_output_row, (JDIMENSION) 1, TRUE);
-  dest->cur_output_row++;
+  if (dest->use_inversion_array) {
+    /* Access next row in virtual array */
+    image_ptr = (*cinfo->mem->access_virt_sarray)
+      ((j_common_ptr) cinfo, dest->whole_image,
+       dest->cur_output_row, (JDIMENSION) 1, TRUE);
+    dest->cur_output_row++;
+    outptr = image_ptr[0];
+  } else {
+    outptr = dest->iobuffer;
+  }
 
   /* Transfer data. */
   inptr = dest->pub.buffer[0];
-  outptr = image_ptr[0];
-  for (col = cinfo->output_width; col > 0; col--) {
-    *outptr++ = *inptr++;       /* can omit GETJSAMPLE() safely */
-  }
+  MEMCOPY(outptr, inptr, cinfo->output_width);
+  outptr += cinfo->output_width;
 
   /* Zero out the pad bytes. */
   pad = dest->pad_bytes;
   while (--pad >= 0)
     *outptr++ = 0;
-}
-
-
-/*
- * Startup: normally writes the file header.
- * In this module we may as well postpone everything until finish_output.
- */
 
-METHODDEF(void)
-start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
-{
-  /* no work here */
+  if (!dest->use_inversion_array)
+    (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->row_width);
 }
 
 
@@ -204,7 +233,7 @@ write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
   int bits_per_pixel, cmap_entries;
 
   /* Compute colormap size and total file size */
-  if (cinfo->out_color_space == JCS_RGB) {
+  if (IsExtRGB(cinfo->out_color_space)) {
     if (cinfo->quantize_colors) {
       /* Colormapped RGB */
       bits_per_pixel = 8;
@@ -214,7 +243,8 @@ write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
       bits_per_pixel = 24;
       cmap_entries = 0;
     }
-  } else if (cinfo->out_color_space == JCS_RGB565) {
+  } else if (cinfo->out_color_space == JCS_RGB565 ||
+             cinfo->out_color_space == JCS_CMYK) {
     bits_per_pixel = 24;
     cmap_entries   = 0;
   } else {
@@ -272,7 +302,9 @@ write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
   int bits_per_pixel, cmap_entries;
 
   /* Compute colormap size and total file size */
-  if (cinfo->out_color_space == JCS_RGB) {
+  if (cinfo->out_color_space == JCS_RGB ||
+      (cinfo->out_color_space >= JCS_EXT_RGB &&
+       cinfo->out_color_space <= JCS_EXT_ARGB)) {
     if (cinfo->quantize_colors) {
       /* Colormapped RGB */
       bits_per_pixel = 8;
@@ -282,7 +314,8 @@ write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
       bits_per_pixel = 24;
       cmap_entries = 0;
     }
-  } else if (cinfo->out_color_space == JCS_RGB565) {
+  } else if (cinfo->out_color_space == JCS_RGB565 ||
+             cinfo->out_color_space == JCS_CMYK) {
     bits_per_pixel = 24;
     cmap_entries   = 0;
   } else {
@@ -379,6 +412,25 @@ write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest,
 }
 
 
+/*
+ * Startup: write the file header unless the inversion array is being used.
+ */
+
+METHODDEF(void)
+start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
+{
+  bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
+
+  if (!dest->use_inversion_array) {
+    /* Write the header and colormap */
+    if (dest->is_os2)
+      write_os2_header(cinfo, dest);
+    else
+      write_bmp_header(cinfo, dest);
+  }
+}
+
+
 METHODDEF(void)
 finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
 {
@@ -390,29 +442,31 @@ finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
   register JDIMENSION col;
   cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
 
-  /* Write the header and colormap */
-  if (dest->is_os2)
-    write_os2_header(cinfo, dest);
-  else
-    write_bmp_header(cinfo, dest);
-
-  /* Write the file body from our virtual array */
-  for (row = cinfo->output_height; row > 0; row--) {
-    if (progress != NULL) {
-      progress->pub.pass_counter = (long) (cinfo->output_height - row);
-      progress->pub.pass_limit = (long) cinfo->output_height;
-      (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
-    }
-    image_ptr = (*cinfo->mem->access_virt_sarray)
-      ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE);
-    data_ptr = image_ptr[0];
-    for (col = dest->row_width; col > 0; col--) {
-      putc(GETJSAMPLE(*data_ptr), outfile);
-      data_ptr++;
+  if (dest->use_inversion_array) {
+    /* Write the header and colormap */
+    if (dest->is_os2)
+      write_os2_header(cinfo, dest);
+    else
+      write_bmp_header(cinfo, dest);
+
+    /* Write the file body from our virtual array */
+    for (row = cinfo->output_height; row > 0; row--) {
+      if (progress != NULL) {
+        progress->pub.pass_counter = (long) (cinfo->output_height - row);
+        progress->pub.pass_limit = (long) cinfo->output_height;
+        (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
+      }
+      image_ptr = (*cinfo->mem->access_virt_sarray)
+        ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE);
+      data_ptr = image_ptr[0];
+      for (col = dest->row_width; col > 0; col--) {
+        putc(GETJSAMPLE(*data_ptr), outfile);
+        data_ptr++;
+      }
     }
+    if (progress != NULL)
+      progress->completed_extra_passes++;
   }
-  if (progress != NULL)
-    progress->completed_extra_passes++;
 
   /* Make sure we wrote the output file OK */
   fflush(outfile);
@@ -426,7 +480,8 @@ finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
  */
 
 GLOBAL(djpeg_dest_ptr)
-jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2)
+jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2,
+                 boolean use_inversion_array)
 {
   bmp_dest_ptr dest;
   JDIMENSION row_width;
@@ -442,12 +497,15 @@ jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2)
 
   if (cinfo->out_color_space == JCS_GRAYSCALE) {
     dest->pub.put_pixel_rows = put_gray_rows;
-  } else if (cinfo->out_color_space == JCS_RGB) {
+  } else if (cinfo->out_color_space == JCS_RGB ||
+             (cinfo->out_color_space >= JCS_EXT_RGB &&
+              cinfo->out_color_space <= JCS_EXT_ARGB)) {
     if (cinfo->quantize_colors)
       dest->pub.put_pixel_rows = put_gray_rows;
     else
       dest->pub.put_pixel_rows = put_pixel_rows;
-  } else if (cinfo->out_color_space == JCS_RGB565) {
+  } else if (cinfo->out_color_space == JCS_RGB565 ||
+             cinfo->out_color_space == JCS_CMYK) {
       dest->pub.put_pixel_rows = put_pixel_rows;
   } else {
     ERREXIT(cinfo, JERR_BMP_COLORSPACE);
@@ -460,28 +518,35 @@ jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2)
   if (cinfo->out_color_space == JCS_RGB565) {
     row_width = cinfo->output_width * 2;
     dest->row_width = dest->data_width = cinfo->output_width * 3;
+    while ((row_width & 3) != 0) row_width++;
+  } else if (!cinfo->quantize_colors &&
+             (IsExtRGB(cinfo->out_color_space) ||
+              cinfo->out_color_space == JCS_CMYK)) {
+    row_width = cinfo->output_width * cinfo->output_components;
+    dest->row_width = dest->data_width = cinfo->output_width * 3;
   } else {
     row_width = cinfo->output_width * cinfo->output_components;
     dest->row_width = dest->data_width = row_width;
   }
   while ((dest->row_width & 3) != 0) dest->row_width++;
   dest->pad_bytes = (int) (dest->row_width - dest->data_width);
-  if (cinfo->out_color_space == JCS_RGB565) {
-    while ((row_width & 3) != 0) row_width++;
-  } else {
-    row_width = dest->row_width;
-  }
 
 
-  /* Allocate space for inversion array, prepare for write pass */
-  dest->whole_image = (*cinfo->mem->request_virt_sarray)
-    ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
-     dest->row_width, cinfo->output_height, (JDIMENSION) 1);
-  dest->cur_output_row = 0;
-  if (cinfo->progress != NULL) {
-    cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
-    progress->total_extra_passes++; /* count file input as separate pass */
+  if (use_inversion_array) {
+    /* Allocate space for inversion array, prepare for write pass */
+    dest->whole_image = (*cinfo->mem->request_virt_sarray)
+      ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
+       dest->row_width, cinfo->output_height, (JDIMENSION) 1);
+    dest->cur_output_row = 0;
+    if (cinfo->progress != NULL) {
+      cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
+      progress->total_extra_passes++; /* count file input as separate pass */
+    }
+  } else {
+    dest->iobuffer = (JSAMPLE *) (*cinfo->mem->alloc_small)
+      ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->row_width);
   }
+  dest->use_inversion_array = use_inversion_array;
 
   /* Create decompressor output buffer. */
   dest->pub.buffer = (*cinfo->mem->alloc_sarray)
diff --git a/wrppm.c b/wrppm.c
index 91cb10b39ae3b2250990c1b40be935f73f665f4e..6b94f24f104c6b77e33ffb6510d2b6bbb6a44a79 100644 (file)
--- a/wrppm.c
+++ b/wrppm.c
@@ -20,6 +20,7 @@
  */
 
 #include "cdjpeg.h"             /* Common decls for cjpeg/djpeg applications */
+#include "cmyk.h"
 
 #ifdef PPM_SUPPORTED
 
@@ -107,13 +108,74 @@ copy_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
   ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
   register char *bufferptr;
   register JSAMPROW ptr;
+#if BITS_IN_JSAMPLE != 8 || (!defined(HAVE_UNSIGNED_CHAR) && !defined(__CHAR_UNSIGNED__))
   register JDIMENSION col;
+#endif
 
   ptr = dest->pub.buffer[0];
   bufferptr = dest->iobuffer;
+#if BITS_IN_JSAMPLE == 8 && (defined(HAVE_UNSIGNED_CHAR) || defined(__CHAR_UNSIGNED__))
+  MEMCOPY(bufferptr, ptr, dest->samples_per_row);
+#else
   for (col = dest->samples_per_row; col > 0; col--) {
     PUTPPMSAMPLE(bufferptr, GETJSAMPLE(*ptr++));
   }
+#endif
+  (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width);
+}
+
+
+/*
+ * Convert extended RGB to RGB.
+ */
+
+METHODDEF(void)
+put_rgb (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
+         JDIMENSION rows_supplied)
+{
+  ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
+  register char *bufferptr;
+  register JSAMPROW ptr;
+  register JDIMENSION col;
+  register int rindex = rgb_red[cinfo->out_color_space];
+  register int gindex = rgb_green[cinfo->out_color_space];
+  register int bindex = rgb_blue[cinfo->out_color_space];
+  register int ps = rgb_pixelsize[cinfo->out_color_space];
+
+  ptr = dest->pub.buffer[0];
+  bufferptr = dest->iobuffer;
+  for (col = cinfo->output_width; col > 0; col--) {
+    PUTPPMSAMPLE(bufferptr, ptr[rindex]);
+    PUTPPMSAMPLE(bufferptr, ptr[gindex]);
+    PUTPPMSAMPLE(bufferptr, ptr[bindex]);
+    ptr += ps;
+  }
+  (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width);
+}
+
+
+/*
+ * Convert CMYK to RGB.
+ */
+
+METHODDEF(void)
+put_cmyk (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
+          JDIMENSION rows_supplied)
+{
+  ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
+  register char *bufferptr;
+  register JSAMPROW ptr;
+  register JDIMENSION col;
+
+  ptr = dest->pub.buffer[0];
+  bufferptr = dest->iobuffer;
+  for (col = cinfo->output_width; col > 0; col--) {
+    JSAMPLE r, g, b, c = *ptr++, m = *ptr++, y = *ptr++, k = *ptr++;
+    cmyk_to_rgb(c, m, y, k, &r, &g, &b);
+    PUTPPMSAMPLE(bufferptr, r);
+    PUTPPMSAMPLE(bufferptr, g);
+    PUTPPMSAMPLE(bufferptr, b);
+  }
   (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width);
 }
 
@@ -185,6 +247,17 @@ start_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
             PPM_MAXVAL);
     break;
   case JCS_RGB:
+  case JCS_EXT_RGB:
+  case JCS_EXT_RGBX:
+  case JCS_EXT_BGR:
+  case JCS_EXT_BGRX:
+  case JCS_EXT_XBGR:
+  case JCS_EXT_XRGB:
+  case JCS_EXT_RGBA:
+  case JCS_EXT_BGRA:
+  case JCS_EXT_ABGR:
+  case JCS_EXT_ARGB:
+  case JCS_CMYK:
     /* emit header for raw PPM format */
     fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n",
             (long) cinfo->output_width, (long) cinfo->output_height,
@@ -219,7 +292,10 @@ calc_buffer_dimensions_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
 {
   ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
 
-  dest->samples_per_row = cinfo->output_width * cinfo->out_color_components;
+  if (cinfo->out_color_space == JCS_GRAYSCALE)
+    dest->samples_per_row = cinfo->output_width * cinfo->out_color_components;
+  else
+    dest->samples_per_row = cinfo->output_width * 3;
   dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * sizeof(char));
 }
 
@@ -250,7 +326,12 @@ jinit_write_ppm (j_decompress_ptr cinfo)
     ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width);
 
   if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 ||
-      sizeof(JSAMPLE) != sizeof(char)) {
+      sizeof(JSAMPLE) != sizeof(char) ||
+      (cinfo->out_color_space != JCS_EXT_RGB
+#if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
+       && cinfo->out_color_space != JCS_RGB
+#endif
+      )) {
     /* When quantizing, we need an output buffer for colormap indexes
      * that's separate from the physical I/O buffer.  We also need a
      * separate buffer if pixel format translation must take place.
@@ -259,7 +340,11 @@ jinit_write_ppm (j_decompress_ptr cinfo)
       ((j_common_ptr) cinfo, JPOOL_IMAGE,
        cinfo->output_width * cinfo->output_components, (JDIMENSION) 1);
     dest->pub.buffer_height = 1;
-    if (! cinfo->quantize_colors)
+    if (IsExtRGB(cinfo->out_color_space))
+      dest->pub.put_pixel_rows = put_rgb;
+    else if (cinfo->out_color_space == JCS_CMYK)
+      dest->pub.put_pixel_rows = put_cmyk;
+    else if (! cinfo->quantize_colors)
       dest->pub.put_pixel_rows = copy_pixel_rows;
     else if (cinfo->out_color_space == JCS_GRAYSCALE)
       dest->pub.put_pixel_rows = put_demapped_gray;