From: DRC Date: Fri, 17 Nov 2017 00:09:07 +0000 (-0600) Subject: TurboJPEG C API: Add BMP/PPM load/save functions X-Git-Tag: 1.5.90~54 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=aa7459050d7a50e1d8a99488902d41fbc118a50f;p=libjpeg-turbo TurboJPEG C API: Add BMP/PPM load/save functions 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. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 943c155..fed6a4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -541,7 +541,8 @@ endif() if(WITH_TURBOJPEG) if(ENABLE_SHARED) set(TURBOJPEG_SOURCES ${JPEG_SOURCES} $ ${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} $ - ${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) diff --git a/ChangeLog.md b/ChangeLog.md index 2ac91e2..11dcd16 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 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 -#include -#include -#include -#include "cdjpeg.h" -#include -#include -#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; row1.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; row255.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=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_scanlineget_pixel_rows)(&cinfo, src); - for(i=0; ibuffer[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_scanlinebuffer_height; - for(i=0; ibuffer[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 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 diff --git a/cdjpeg.h b/cdjpeg.h index bb49fbf..8c3b3b5 100644 --- 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 da9af73..90f8b86 100644 --- 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 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 +#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 2fbb0b7..78bfb6f 100644 --- 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 diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index c2ae185..402b858 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -205,7 +205,9 @@ Enumerations   TJPF_BGRA, TJPF_ABGR, TJPF_ARGB, -TJPF_CMYK +TJPF_CMYK, +
+  TJPF_UNKNOWN
}  Pixel formats. More...
@@ -312,6 +314,12 @@ Functions DLLEXPORT unsigned char *DLLCALL tjAlloc (int bytes)  Allocate an image buffer for use with TurboJPEG. More...
  +DLLEXPORT unsigned char *DLLCALL tjLoadImage (const char *filename, int *width, int align, int *height, int *pixelFormat, int flags) + Load an uncompressed image from disk into memory. More...
+  +DLLEXPORT int DLLCALL tjSaveImage (const char *filename, unsigned char *buffer, int width, int pitch, int height, int pixelFormat, int flags) + Save an uncompressed image from memory to disk. More...
+  DLLEXPORT void DLLCALL tjFree (unsigned char *buffer)  Free an image buffer previously allocated by TurboJPEG. More...
  @@ -822,6 +830,10 @@ Variables

CMYK pixel format.

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 TJCS_YCCK) and decompressing YCCK JPEG images into CMYK pixels.

+TJPF_UNKNOWN  +

Unknown pixel format.

+

Currently this is only used by tjLoadImage().

+ @@ -2202,6 +2214,70 @@ If you choose option 1, *jpegSize should be set to the size of your

Create a new TurboJPEG transformer instance.

Returns
a handle to the newly-created instance, or NULL if an error occurred (see tjGetErrorStr2().)
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DLLEXPORT unsigned char* DLLCALL tjLoadImage (const char * filename,
int * width,
int align,
int * height,
int * pixelFormat,
int flags 
)
+
+ +

Load an uncompressed image from disk into memory.

+
Parameters
+ + + + + + + +
filenamename of a file containing an uncompressed image in Windows BMP or PBMPLUS (PPM/PGM) format
widthpointer to an integer variable that will receive the width (in pixels) of the uncompressed image
alignrow 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.
heightpointer to an integer variable that will receive the height (in pixels) of the uncompressed image
pixelFormatpointer to an integer variable that specifies or will receive the pixel format of the uncompressed image buffer. If *pixelFormat is set to 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 *pixelFormat will contain the ID of this pixel format upon successful return from the function. Otherwise, the uncompressed image buffer will use the pixel format specified in *pixelFormat, and pixel format conversion will be performed if necessary. If *pixelFormat is set to 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.)
flagsthe bitwise OR of one or more of the flags.
+
+
+
Returns
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().
+
@@ -2347,6 +2423,77 @@ If you choose option 1, *jpegSize should be set to the size of your
Returns
the plane width of a YUV image plane with the given parameters, or -1 if the arguments are out of bounds.
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DLLEXPORT int DLLCALL tjSaveImage (const char * filename,
unsigned char * buffer,
int width,
int pitch,
int height,
int pixelFormat,
int flags 
)
+
+ +

Save an uncompressed image from memory to disk.

+
Parameters
+ + + + + + + + +
filenamename 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.
bufferpointer to an image buffer containing RGB, grayscale, or CMYK pixels to be saved
widthwidth (in pixels) of the uncompressed image
pitchbytes per line in the image buffer. Setting this parameter to 0 is the equivalent of setting it to width * tjPixelSize[pixelFormat].
heightheight (in pixels) of the uncompressed image
pixelFormatpixel format of the image buffer (see Pixel formats.) If this parameter is set to 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 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.)
flagsthe bitwise OR of one or more of the flags.
+
+
+
Returns
0 if successful, or -1 if an error occurred (see tjGetErrorStr2().)
+
diff --git a/doc/html/search/all_74.js b/doc/html/search/all_74.js index a211e3d..e952119 100644 --- a/doc/html/search/all_74.js +++ b/doc/html/search/all_74.js @@ -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(): 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): turbojpeg.h']]], diff --git a/doc/html/search/enumvalues_74.js b/doc/html/search/enumvalues_74.js index 799f9ac..e683856 100644 --- a/doc/html/search/enumvalues_74.js +++ b/doc/html/search/enumvalues_74.js @@ -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']]], diff --git a/doc/html/search/functions_74.js b/doc/html/search/functions_74.js index d2164bb..0c9a3c1 100644 --- a/doc/html/search/functions_74.js +++ b/doc/html/search/functions_74.js @@ -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']]] ]; diff --git a/md5/md5.h b/md5/md5.h index 551e252..dd35176 100644 --- 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 + #define MD5_BLOCK_LENGTH 64 #define MD5_DIGEST_LENGTH 16 #define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) diff --git a/md5/md5cmp.c b/md5/md5cmp.c index dfd60bd..5716297 100644 --- a/md5/md5cmp.c +++ b/md5/md5cmp.c @@ -28,7 +28,6 @@ #include #include -#include #include "./md5.h" #include "../tjutil.h" diff --git a/rdbmp.c b/rdbmp.c index eaa7086..49616d0 100644 --- 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 33ff749..24987cf 100644 --- 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); diff --git a/tjbench.c b/tjbench.c index 9b7a48b..70c172f 100644 --- a/tjbench.c +++ b/tjbench.c @@ -33,7 +33,6 @@ #include #include #include -#include "./bmp.h" #include "./tjutil.h" #include "./turbojpeg.h" @@ -48,6 +47,12 @@ 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; } diff --git a/tjunittest.c b/tjunittest.c index f793796..ffe6fc7 100644 --- a/tjunittest.c +++ b/tjunittest.c @@ -34,11 +34,15 @@ #include #include #include -#include "./tjutil.h" -#include "./turbojpeg.h" +#include "tjutil.h" +#include "turbojpeg.h" +#include "md5/md5.h" +#include "cmyk.h" #ifdef _WIN32 #include #define random() rand() +#else + #include #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=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 #include #include +#include #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(srcjerr.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; ijerr.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; iglobal_state>DSTATE_START) jpeg_abort_decompress(dinfo); - #ifndef JCS_EXTENSIONS - if(rgbBuf) free(rgbBuf); - #endif if(row_pointer) free(row_pointer); for(i=0; ijerr.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=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_scanlineimage_height) + { + int i, nlines=(*src->get_pixel_rows)(cinfo, src); + for(i=0; inext_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_scanlineoutput_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; +} diff --git a/turbojpeg.h b/turbojpeg.h index f7fef7b..36740eb 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -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 + * *pixelFormat 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 + * *pixelFormat 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 + * *pixelFormat, and pixel format conversion will be performed if + * necessary. If *pixelFormat 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 + * width * #tjPixelSize[pixelFormat]. + * + * @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 728bbad..d2e43f5 100644 --- 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 91cb10b..6b94f24 100644 --- 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;