summaryrefslogtreecommitdiff
path: root/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2018-01-26 17:38:31 +0300
committerGeorge Hazan <ghazan@miranda.im>2018-01-26 17:38:31 +0300
commitdea9c030340e50324eba97c72a27c151bed12e1c (patch)
tree6fc156f40f52a9fc6e6b29e60001959477ee1a5e /plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp
parentc6e8f8223cab9d799593b7b2cfa22134aa9745d6 (diff)
AdvaImg:
- freeimage extracted to the separate library; - FI_INTERFACE removed, all references to it are replaced with direct calls of FreeImage_* functions; - unified project for AdvaImg
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp')
-rw-r--r--plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp1706
1 files changed, 0 insertions, 1706 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp b/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp
deleted file mode 100644
index 19e4a070a5..0000000000
--- a/plugins/AdvaImg/src/FreeImage/PluginJPEG.cpp
+++ /dev/null
@@ -1,1706 +0,0 @@
-// ==========================================================
-// JPEG Loader and writer
-// Based on code developed by The Independent JPEG Group
-//
-// Design and implementation by
-// - Floris van den Berg (flvdberg@wxs.nl)
-// - Jan L. Nauta (jln@magentammt.com)
-// - Markus Loibl (markus.loibl@epost.de)
-// - Karl-Heinz Bussian (khbussian@moss.de)
-// - Hervé Drolon (drolon@infonie.fr)
-// - Jascha Wetzel (jascha@mainia.de)
-// - Mihail Naydenov (mnaydenov@users.sourceforge.net)
-//
-// This file is part of FreeImage 3
-//
-// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
-// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
-// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
-// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
-// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
-// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
-// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
-// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
-// THIS DISCLAIMER.
-//
-// Use at your own risk!
-// ==========================================================
-
-#ifdef _MSC_VER
-#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
-#endif
-
-extern "C" {
-#define XMD_H
-#undef FAR
-#include <setjmp.h>
-
-#include "../LibJPEG/jinclude.h"
-#include "../LibJPEG/jpeglib.h"
-#include "../LibJPEG/jerror.h"
-}
-
-#include "FreeImage.h"
-#include "Utilities.h"
-
-#include "../Metadata/FreeImageTag.h"
-
-
-// ==========================================================
-// Plugin Interface
-// ==========================================================
-
-static int s_format_id;
-
-// ----------------------------------------------------------
-// Constant declarations
-// ----------------------------------------------------------
-
-#define INPUT_BUF_SIZE 4096 // choose an efficiently fread'able size
-#define OUTPUT_BUF_SIZE 4096 // choose an efficiently fwrite'able size
-
-#define EXIF_MARKER (JPEG_APP0+1) // EXIF marker / Adobe XMP marker
-#define ICC_MARKER (JPEG_APP0+2) // ICC profile marker
-#define IPTC_MARKER (JPEG_APP0+13) // IPTC marker / BIM marker
-
-#define ICC_HEADER_SIZE 14 // size of non-profile data in APP2
-#define MAX_BYTES_IN_MARKER 65533L // maximum data length of a JPEG marker
-#define MAX_DATA_BYTES_IN_MARKER 65519L // maximum data length of a JPEG APP2 marker
-
-#define MAX_JFXX_THUMB_SIZE (MAX_BYTES_IN_MARKER - 5 - 1)
-
-#define JFXX_TYPE_JPEG 0x10 // JFIF extension marker: JPEG-compressed thumbnail image
-#define JFXX_TYPE_8bit 0x11 // JFIF extension marker: palette thumbnail image
-#define JFXX_TYPE_24bit 0x13 // JFIF extension marker: RGB thumbnail image
-
-// ----------------------------------------------------------
-// Typedef declarations
-// ----------------------------------------------------------
-
-typedef struct tagErrorManager {
- /// "public" fields
- struct jpeg_error_mgr pub;
- /// for return to caller
- jmp_buf setjmp_buffer;
-} ErrorManager;
-
-typedef struct tagSourceManager {
- /// public fields
- struct jpeg_source_mgr pub;
- /// source stream
- fi_handle infile;
- FreeImageIO *m_io;
- /// start of buffer
- JOCTET * buffer;
- /// have we gotten any data yet ?
- boolean start_of_file;
-} SourceManager;
-
-typedef struct tagDestinationManager {
- /// public fields
- struct jpeg_destination_mgr pub;
- /// destination stream
- fi_handle outfile;
- FreeImageIO *m_io;
- /// start of buffer
- JOCTET * buffer;
-} DestinationManager;
-
-typedef SourceManager* freeimage_src_ptr;
-typedef DestinationManager* freeimage_dst_ptr;
-typedef ErrorManager* freeimage_error_ptr;
-
-// ----------------------------------------------------------
-// Error handling
-// ----------------------------------------------------------
-
-/** Fatal errors (print message and exit) */
-static inline void
-JPEG_EXIT(j_common_ptr cinfo, int code) {
- freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
- error_ptr->pub.msg_code = code;
- error_ptr->pub.error_exit(cinfo);
-}
-
-/** Nonfatal errors (we can keep going, but the data is probably corrupt) */
-static inline void
-JPEG_WARNING(j_common_ptr cinfo, int code) {
- freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
- error_ptr->pub.msg_code = code;
- error_ptr->pub.emit_message(cinfo, -1);
-}
-
-/**
- Receives control for a fatal error. Information sufficient to
- generate the error message has been stored in cinfo->err; call
- output_message to display it. Control must NOT return to the caller;
- generally this routine will exit() or longjmp() somewhere.
-*/
-METHODDEF(void)
-jpeg_error_exit (j_common_ptr cinfo) {
- freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
-
- // always display the message
- error_ptr->pub.output_message(cinfo);
-
- // allow JPEG with unknown markers
- if(error_ptr->pub.msg_code != JERR_UNKNOWN_MARKER) {
-
- // let the memory manager delete any temp files before we die
- jpeg_destroy(cinfo);
-
- // return control to the setjmp point
- longjmp(error_ptr->setjmp_buffer, 1);
- }
-}
-
-/**
- Actual output of any JPEG message. Note that this method does not know
- how to generate a message, only where to send it.
-*/
-METHODDEF(void)
-jpeg_output_message (j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
- freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err;
-
- // create the message
- error_ptr->pub.format_message(cinfo, buffer);
- // send it to user's message proc
- FreeImage_OutputMessageProc(s_format_id, buffer);
-}
-
-// ----------------------------------------------------------
-// Destination manager
-// ----------------------------------------------------------
-
-/**
- Initialize destination. This is called by jpeg_start_compress()
- before any data is actually written. It must initialize
- next_output_byte and free_in_buffer. free_in_buffer must be
- initialized to a positive value.
-*/
-METHODDEF(void)
-init_destination (j_compress_ptr cinfo) {
- freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
-
- dest->buffer = (JOCTET *)
- (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
- OUTPUT_BUF_SIZE * sizeof(JOCTET));
-
- dest->pub.next_output_byte = dest->buffer;
- dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
-}
-
-/**
- This is called whenever the buffer has filled (free_in_buffer
- reaches zero). In typical applications, it should write out the
- *entire* buffer (use the saved start address and buffer length;
- ignore the current state of next_output_byte and free_in_buffer).
- Then reset the pointer & count to the start of the buffer, and
- return TRUE indicating that the buffer has been dumped.
- free_in_buffer must be set to a positive value when TRUE is
- returned. A FALSE return should only be used when I/O suspension is
- desired.
-*/
-METHODDEF(boolean)
-empty_output_buffer (j_compress_ptr cinfo) {
- freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
-
- if (dest->m_io->write_proc(dest->buffer, 1, OUTPUT_BUF_SIZE, dest->outfile) != OUTPUT_BUF_SIZE) {
- // let the memory manager delete any temp files before we die
- jpeg_destroy((j_common_ptr)cinfo);
-
- JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE);
- }
-
- dest->pub.next_output_byte = dest->buffer;
- dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
-
- return TRUE;
-}
-
-/**
- Terminate destination --- called by jpeg_finish_compress() after all
- data has been written. In most applications, this must flush any
- data remaining in the buffer. Use either next_output_byte or
- free_in_buffer to determine how much data is in the buffer.
-*/
-METHODDEF(void)
-term_destination (j_compress_ptr cinfo) {
- freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
-
- size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
-
- // write any data remaining in the buffer
-
- if (datacount > 0) {
- if (dest->m_io->write_proc(dest->buffer, 1, (unsigned int)datacount, dest->outfile) != datacount) {
- // let the memory manager delete any temp files before we die
- jpeg_destroy((j_common_ptr)cinfo);
-
- JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE);
- }
- }
-}
-
-// ----------------------------------------------------------
-// Source manager
-// ----------------------------------------------------------
-
-/**
- Initialize source. This is called by jpeg_read_header() before any
- data is actually read. Unlike init_destination(), it may leave
- bytes_in_buffer set to 0 (in which case a fill_input_buffer() call
- will occur immediately).
-*/
-METHODDEF(void)
-init_source (j_decompress_ptr cinfo) {
- freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
-
- /* We reset the empty-input-file flag for each image,
- * but we don't clear the input buffer.
- * This is correct behavior for reading a series of images from one source.
- */
-
- src->start_of_file = TRUE;
-}
-
-/**
- This is called whenever bytes_in_buffer has reached zero and more
- data is wanted. In typical applications, it should read fresh data
- into the buffer (ignoring the current state of next_input_byte and
- bytes_in_buffer), reset the pointer & count to the start of the
- buffer, and return TRUE indicating that the buffer has been reloaded.
- It is not necessary to fill the buffer entirely, only to obtain at
- least one more byte. bytes_in_buffer MUST be set to a positive value
- if TRUE is returned. A FALSE return should only be used when I/O
- suspension is desired.
-*/
-METHODDEF(boolean)
-fill_input_buffer (j_decompress_ptr cinfo) {
- freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
-
- size_t nbytes = src->m_io->read_proc(src->buffer, 1, INPUT_BUF_SIZE, src->infile);
-
- if (nbytes <= 0) {
- if (src->start_of_file) {
- // treat empty input file as fatal error
-
- // let the memory manager delete any temp files before we die
- jpeg_destroy((j_common_ptr)cinfo);
-
- JPEG_EXIT((j_common_ptr)cinfo, JERR_INPUT_EMPTY);
- }
-
- JPEG_WARNING((j_common_ptr)cinfo, JWRN_JPEG_EOF);
-
- /* Insert a fake EOI marker */
-
- src->buffer[0] = (JOCTET) 0xFF;
- src->buffer[1] = (JOCTET) JPEG_EOI;
-
- nbytes = 2;
- }
-
- src->pub.next_input_byte = src->buffer;
- src->pub.bytes_in_buffer = nbytes;
- src->start_of_file = FALSE;
-
- return TRUE;
-}
-
-/**
- Skip num_bytes worth of data. The buffer pointer and count should
- be advanced over num_bytes input bytes, refilling the buffer as
- needed. This is used to skip over a potentially large amount of
- uninteresting data (such as an APPn marker). In some applications
- it may be possible to optimize away the reading of the skipped data,
- but it's not clear that being smart is worth much trouble; large
- skips are uncommon. bytes_in_buffer may be zero on return.
- A zero or negative skip count should be treated as a no-op.
-*/
-METHODDEF(void)
-skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
- freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src;
-
- /* Just a dumb implementation for now. Could use fseek() except
- * it doesn't work on pipes. Not clear that being smart is worth
- * any trouble anyway --- large skips are infrequent.
- */
-
- if (num_bytes > 0) {
- while (num_bytes > (long) src->pub.bytes_in_buffer) {
- num_bytes -= (long) src->pub.bytes_in_buffer;
-
- (void) fill_input_buffer(cinfo);
-
- /* note we assume that fill_input_buffer will never return FALSE,
- * so suspension need not be handled.
- */
- }
-
- src->pub.next_input_byte += (size_t) num_bytes;
- src->pub.bytes_in_buffer -= (size_t) num_bytes;
- }
-}
-
-/**
- Terminate source --- called by jpeg_finish_decompress
- after all data has been read. Often a no-op.
-
- NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
- application must deal with any cleanup that should happen even
- for error exit.
-*/
-METHODDEF(void)
-term_source (j_decompress_ptr cinfo) {
- // no work necessary here
-}
-
-// ----------------------------------------------------------
-// Source manager & Destination manager setup
-// ----------------------------------------------------------
-
-/**
- Prepare for input from a stdio stream.
- The caller must have already opened the stream, and is responsible
- for closing it after finishing decompression.
-*/
-GLOBAL(void)
-jpeg_freeimage_src (j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io) {
- freeimage_src_ptr src;
-
- // allocate memory for the buffer. is released automatically in the end
-
- if (cinfo->src == NULL) {
- cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
- ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(SourceManager));
-
- src = (freeimage_src_ptr) cinfo->src;
-
- src->buffer = (JOCTET *) (*cinfo->mem->alloc_small)
- ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET));
- }
-
- // initialize the jpeg pointer struct with pointers to functions
-
- src = (freeimage_src_ptr) cinfo->src;
- src->pub.init_source = init_source;
- src->pub.fill_input_buffer = fill_input_buffer;
- src->pub.skip_input_data = skip_input_data;
- src->pub.resync_to_restart = jpeg_resync_to_restart; // use default method
- src->pub.term_source = term_source;
- src->infile = infile;
- src->m_io = io;
- src->pub.bytes_in_buffer = 0; // forces fill_input_buffer on first read
- src->pub.next_input_byte = NULL; // until buffer loaded
-}
-
-/**
- Prepare for output to a stdio stream.
- The caller must have already opened the stream, and is responsible
- for closing it after finishing compression.
-*/
-GLOBAL(void)
-jpeg_freeimage_dst (j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io) {
- freeimage_dst_ptr dest;
-
- if (cinfo->dest == NULL) {
- cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small)
- ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(DestinationManager));
- }
-
- dest = (freeimage_dst_ptr) cinfo->dest;
- dest->pub.init_destination = init_destination;
- dest->pub.empty_output_buffer = empty_output_buffer;
- dest->pub.term_destination = term_destination;
- dest->outfile = outfile;
- dest->m_io = io;
-}
-
-// ----------------------------------------------------------
-// Special markers read functions
-// ----------------------------------------------------------
-
-/**
- Read JPEG_COM marker (comment)
-*/
-static BOOL
-jpeg_read_comment(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
- size_t length = datalen;
- BYTE *profile = (BYTE*)dataptr;
-
- // read the comment
- char *value = (char*)malloc((length + 1) * sizeof(char));
- if(value == NULL) return FALSE;
- memcpy(value, profile, length);
- value[length] = '\0';
-
- // create a tag
- FITAG *tag = FreeImage_CreateTag();
- if(tag) {
- unsigned int count = (unsigned int)length + 1; // includes the null value
-
- FreeImage_SetTagID(tag, JPEG_COM);
- FreeImage_SetTagKey(tag, "Comment");
- FreeImage_SetTagLength(tag, count);
- FreeImage_SetTagCount(tag, count);
- FreeImage_SetTagType(tag, FIDT_ASCII);
- FreeImage_SetTagValue(tag, value);
-
- // store the tag
- FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
-
- // destroy the tag
- FreeImage_DeleteTag(tag);
- }
-
- free(value);
-
- return TRUE;
-}
-
-/**
- Read JPEG_APP2 marker (ICC profile)
-*/
-
-/**
-Handy subroutine to test whether a saved marker is an ICC profile marker.
-*/
-static BOOL
-marker_is_icc(jpeg_saved_marker_ptr marker) {
- // marker identifying string "ICC_PROFILE" (null-terminated)
- const BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 };
-
- if(marker->marker == ICC_MARKER) {
- // verify the identifying string
- if(marker->data_length >= ICC_HEADER_SIZE) {
- if(memcmp(icc_signature, marker->data, sizeof(icc_signature)) == 0) {
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-/**
- See if there was an ICC profile in the JPEG file being read;
- if so, reassemble and return the profile data.
-
- TRUE is returned if an ICC profile was found, FALSE if not.
- If TRUE is returned, *icc_data_ptr is set to point to the
- returned data, and *icc_data_len is set to its length.
-
- IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
- and must be freed by the caller with free() when the caller no longer
- needs it. (Alternatively, we could write this routine to use the
- IJG library's memory allocator, so that the data would be freed implicitly
- at jpeg_finish_decompress() time. But it seems likely that many apps
- will prefer to have the data stick around after decompression finishes.)
-
- NOTE: if the file contains invalid ICC APP2 markers, we just silently
- return FALSE. You might want to issue an error message instead.
-*/
-static BOOL
-jpeg_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned *icc_data_len) {
- jpeg_saved_marker_ptr marker;
- int num_markers = 0;
- int seq_no;
- JOCTET *icc_data;
- unsigned total_length;
-
- const int MAX_SEQ_NO = 255; // sufficient since marker numbers are bytes
- BYTE marker_present[MAX_SEQ_NO+1]; // 1 if marker found
- unsigned data_length[MAX_SEQ_NO+1]; // size of profile data in marker
- unsigned data_offset[MAX_SEQ_NO+1]; // offset for data in marker
-
- *icc_data_ptr = NULL; // avoid confusion if FALSE return
- *icc_data_len = 0;
-
- /**
- this first pass over the saved markers discovers whether there are
- any ICC markers and verifies the consistency of the marker numbering.
- */
-
- memset(marker_present, 0, (MAX_SEQ_NO + 1));
-
- for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
- if (marker_is_icc(marker)) {
- if (num_markers == 0) {
- // number of markers
- num_markers = GETJOCTET(marker->data[13]);
- }
- else if (num_markers != GETJOCTET(marker->data[13])) {
- return FALSE; // inconsistent num_markers fields
- }
- // sequence number
- seq_no = GETJOCTET(marker->data[12]);
- if (seq_no <= 0 || seq_no > num_markers) {
- return FALSE; // bogus sequence number
- }
- if (marker_present[seq_no]) {
- return FALSE; // duplicate sequence numbers
- }
- marker_present[seq_no] = 1;
- data_length[seq_no] = marker->data_length - ICC_HEADER_SIZE;
- }
- }
-
- if (num_markers == 0)
- return FALSE;
-
- /**
- check for missing markers, count total space needed,
- compute offset of each marker's part of the data.
- */
-
- total_length = 0;
- for(seq_no = 1; seq_no <= num_markers; seq_no++) {
- if (marker_present[seq_no] == 0) {
- return FALSE; // missing sequence number
- }
- data_offset[seq_no] = total_length;
- total_length += data_length[seq_no];
- }
-
- if (total_length <= 0)
- return FALSE; // found only empty markers ?
-
- // allocate space for assembled data
- icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
- if (icc_data == NULL)
- return FALSE; // out of memory
-
- // and fill it in
- for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
- if (marker_is_icc(marker)) {
- JOCTET FAR *src_ptr;
- JOCTET *dst_ptr;
- unsigned length;
- seq_no = GETJOCTET(marker->data[12]);
- dst_ptr = icc_data + data_offset[seq_no];
- src_ptr = marker->data + ICC_HEADER_SIZE;
- length = data_length[seq_no];
- while (length--) {
- *dst_ptr++ = *src_ptr++;
- }
- }
- }
-
- *icc_data_ptr = icc_data;
- *icc_data_len = total_length;
-
- return TRUE;
-}
-
-/**
- Read JPEG_APPD marker (IPTC or Adobe Photoshop profile)
-*/
-static BOOL
-jpeg_read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
- return read_iptc_profile(dib, dataptr, datalen);
-}
-
-/**
- Read JPEG_APP1 marker (XMP profile)
- @param dib Input FIBITMAP
- @param dataptr Pointer to the APP1 marker
- @param datalen APP1 marker length
- @return Returns TRUE if successful, FALSE otherwise
-*/
-static BOOL
-jpeg_read_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
- // marker identifying string for XMP (null terminated)
- const char *xmp_signature = "http://ns.adobe.com/xap/1.0/";
- // XMP signature is 29 bytes long
- const size_t xmp_signature_size = strlen(xmp_signature) + 1;
-
- size_t length = datalen;
- BYTE *profile = (BYTE*)dataptr;
-
- if(length <= xmp_signature_size) {
- // avoid reading corrupted or empty data
- return FALSE;
- }
-
- // verify the identifying string
-
- if(memcmp(xmp_signature, profile, strlen(xmp_signature)) == 0) {
- // XMP profile
-
- profile += xmp_signature_size;
- length -= xmp_signature_size;
-
- // create a tag
- FITAG *tag = FreeImage_CreateTag();
- if(tag) {
- FreeImage_SetTagID(tag, JPEG_APP0+1); // 0xFFE1
- FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName);
- FreeImage_SetTagLength(tag, (DWORD)length);
- FreeImage_SetTagCount(tag, (DWORD)length);
- FreeImage_SetTagType(tag, FIDT_ASCII);
- FreeImage_SetTagValue(tag, profile);
-
- // store the tag
- FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag);
-
- // destroy the tag
- FreeImage_DeleteTag(tag);
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- Read JFIF "JFXX" extension APP0 marker
- @param dib Input FIBITMAP
- @param dataptr Pointer to the APP0 marker
- @param datalen APP0 marker length
- @return Returns TRUE if successful, FALSE otherwise
-*/
-static BOOL
-jpeg_read_jfxx(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) {
- if(datalen < 6) {
- return FALSE;
- }
-
- const int id_length = 5;
- const BYTE *data = dataptr + id_length;
- unsigned remaining = datalen - id_length;
-
- const BYTE type = *data;
- ++data, --remaining;
-
- switch(type) {
- case JFXX_TYPE_JPEG:
- {
- // load the thumbnail
- FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(data), remaining);
- FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem);
- FreeImage_CloseMemory(hmem);
- // store the thumbnail
- FreeImage_SetThumbnail(dib, thumbnail);
- // then delete it
- FreeImage_Unload(thumbnail);
- break;
- }
- case JFXX_TYPE_8bit:
- // colormapped uncompressed thumbnail (no supported)
- break;
- case JFXX_TYPE_24bit:
- // truecolor uncompressed thumbnail (no supported)
- break;
- default:
- break;
- }
-
- return TRUE;
-}
-
-
-/**
- Read JPEG special markers
-*/
-static BOOL
-read_markers(j_decompress_ptr cinfo, FIBITMAP *dib) {
- jpeg_saved_marker_ptr marker;
-
- for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
- switch(marker->marker) {
- case JPEG_APP0:
- // JFIF is handled by libjpeg already, handle JFXX
- if(memcmp(marker->data, "JFIF" , 5) == 0) {
- continue;
- }
- if(memcmp(marker->data, "JFXX" , 5) == 0) {
- if(!cinfo->saw_JFIF_marker || cinfo->JFIF_minor_version < 2) {
- FreeImage_OutputMessageProc(s_format_id, "Warning: non-standard JFXX segment");
- }
- jpeg_read_jfxx(dib, marker->data, marker->data_length);
- }
- // other values such as 'Picasa' : ignore safely unknown APP0 marker
- break;
- case JPEG_COM:
- // JPEG comment
- jpeg_read_comment(dib, marker->data, marker->data_length);
- break;
- case EXIF_MARKER:
- // Exif or Adobe XMP profile
- jpeg_read_exif_profile(dib, marker->data, marker->data_length);
- jpeg_read_xmp_profile(dib, marker->data, marker->data_length);
- jpeg_read_exif_profile_raw(dib, marker->data, marker->data_length);
- break;
- case IPTC_MARKER:
- // IPTC/NAA or Adobe Photoshop profile
- jpeg_read_iptc_profile(dib, marker->data, marker->data_length);
- break;
- }
- }
-
- // ICC profile
- BYTE *icc_profile = NULL;
- unsigned icc_length = 0;
-
- if( jpeg_read_icc_profile(cinfo, &icc_profile, &icc_length) ) {
- // copy ICC profile data
- FreeImage_CreateICCProfile(dib, icc_profile, icc_length);
- // clean up
- free(icc_profile);
- }
-
- return TRUE;
-}
-
-// ----------------------------------------------------------
-// Special markers write functions
-// ----------------------------------------------------------
-
-/**
- Write JPEG_COM marker (comment)
-*/
-static BOOL
-jpeg_write_comment(j_compress_ptr cinfo, FIBITMAP *dib) {
- FITAG *tag = NULL;
-
- // write user comment as a JPEG_COM marker
- FreeImage_GetMetadata(FIMD_COMMENTS, dib, "Comment", &tag);
- if(tag) {
- const char *tag_value = (char*)FreeImage_GetTagValue(tag);
-
- if(NULL != tag_value) {
- for(long i = 0; i < (long)strlen(tag_value); i+= MAX_BYTES_IN_MARKER) {
- jpeg_write_marker(cinfo, JPEG_COM, (BYTE*)tag_value + i, MIN((long)strlen(tag_value + i), MAX_BYTES_IN_MARKER));
- }
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/**
- Write JPEG_APP2 marker (ICC profile)
-*/
-static BOOL
-jpeg_write_icc_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
- // marker identifying string "ICC_PROFILE" (null-terminated)
- BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 };
-
- FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib);
-
- if (iccProfile->size && iccProfile->data) {
- // ICC_HEADER_SIZE: ICC signature is 'ICC_PROFILE' + 2 bytes
-
- BYTE *profile = (BYTE*)malloc((iccProfile->size + ICC_HEADER_SIZE) * sizeof(BYTE));
- if(profile == NULL) return FALSE;
- memcpy(profile, icc_signature, 12);
-
- for(long i = 0; i < (long)iccProfile->size; i += MAX_DATA_BYTES_IN_MARKER) {
- unsigned length = MIN((long)(iccProfile->size - i), MAX_DATA_BYTES_IN_MARKER);
- // sequence number
- profile[12] = (BYTE) ((i / MAX_DATA_BYTES_IN_MARKER) + 1);
- // number of markers
- profile[13] = (BYTE) (iccProfile->size / MAX_DATA_BYTES_IN_MARKER + 1);
-
- memcpy(profile + ICC_HEADER_SIZE, (BYTE*)iccProfile->data + i, length);
- jpeg_write_marker(cinfo, ICC_MARKER, profile, (length + ICC_HEADER_SIZE));
- }
-
- free(profile);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-/**
- Write JPEG_APPD marker (IPTC or Adobe Photoshop profile)
- @return Returns TRUE if successful, FALSE otherwise
-*/
-static BOOL
-jpeg_write_iptc_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
- //const char *ps_header = "Photoshop 3.0\x08BIM\x04\x04\x0\x0\x0\x0";
- const unsigned tag_length = 26;
-
- if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) {
- BYTE *profile = NULL;
- unsigned profile_size = 0;
-
- // create a binary profile
- if(write_iptc_profile(dib, &profile, &profile_size)) {
-
- // write the profile
- for(long i = 0; i < (long)profile_size; i += 65517L) {
- unsigned length = MIN((long)profile_size - i, 65517L);
- unsigned roundup = length & 0x01; // needed for Photoshop
- BYTE *iptc_profile = (BYTE*)malloc(length + roundup + tag_length);
- if(iptc_profile == NULL) break;
- // Photoshop identification string
- memcpy(&iptc_profile[0], "Photoshop 3.0\x0", 14);
- // 8BIM segment type
- memcpy(&iptc_profile[14], "8BIM\x04\x04\x0\x0\x0\x0", 10);
- // segment size
- iptc_profile[24] = (BYTE)(length >> 8);
- iptc_profile[25] = (BYTE)(length & 0xFF);
- // segment data
- memcpy(&iptc_profile[tag_length], &profile[i], length);
- if(roundup)
- iptc_profile[length + tag_length] = 0;
- jpeg_write_marker(cinfo, IPTC_MARKER, iptc_profile, length + roundup + tag_length);
- free(iptc_profile);
- }
-
- // release profile
- free(profile);
-
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/**
- Write JPEG_APP1 marker (XMP profile)
- @return Returns TRUE if successful, FALSE otherwise
-*/
-static BOOL
-jpeg_write_xmp_profile(j_compress_ptr cinfo, FIBITMAP *dib) {
- // marker identifying string for XMP (null terminated)
- const char *xmp_signature = "http://ns.adobe.com/xap/1.0/";
-
- FITAG *tag_xmp = NULL;
- FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp);
-
- if(tag_xmp) {
- const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_xmp);
-
- if(NULL != tag_value) {
- // XMP signature is 29 bytes long
- unsigned int xmp_header_size = (unsigned int)strlen(xmp_signature) + 1;
-
- DWORD tag_length = FreeImage_GetTagLength(tag_xmp);
-
- BYTE *profile = (BYTE*)malloc((tag_length + xmp_header_size) * sizeof(BYTE));
- if(profile == NULL) return FALSE;
- memcpy(profile, xmp_signature, xmp_header_size);
-
- for(DWORD i = 0; i < tag_length; i += 65504L) {
- unsigned length = MIN((long)(tag_length - i), 65504L);
-
- memcpy(profile + xmp_header_size, tag_value + i, length);
- jpeg_write_marker(cinfo, EXIF_MARKER, profile, (length + xmp_header_size));
- }
-
- free(profile);
-
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/**
- Write JPEG_APP1 marker (Exif profile)
- @return Returns TRUE if successful, FALSE otherwise
-*/
-static BOOL
-jpeg_write_exif_profile_raw(j_compress_ptr cinfo, FIBITMAP *dib) {
- // marker identifying string for Exif = "Exif\0\0"
- BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 };
-
- FITAG *tag_exif = NULL;
- FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag_exif);
-
- if(tag_exif) {
- const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_exif);
-
- // verify the identifying string
- if(memcmp(exif_signature, tag_value, sizeof(exif_signature)) != 0) {
- // not an Exif profile
- return FALSE;
- }
-
- if(NULL != tag_value) {
- DWORD tag_length = FreeImage_GetTagLength(tag_exif);
-
- BYTE *profile = (BYTE*)malloc(tag_length * sizeof(BYTE));
- if(profile == NULL) return FALSE;
-
- for(DWORD i = 0; i < tag_length; i += 65504L) {
- unsigned length = MIN((long)(tag_length - i), 65504L);
-
- memcpy(profile, tag_value + i, length);
- jpeg_write_marker(cinfo, EXIF_MARKER, profile, length);
- }
-
- free(profile);
-
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-/**
- Write thumbnail (JFXX segment, JPEG compressed)
-*/
-static BOOL
-jpeg_write_jfxx(j_compress_ptr cinfo, FIBITMAP *dib) {
- // get the thumbnail to be stored
- FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib);
- if(!thumbnail) {
- return TRUE;
- }
- // check for a compatible output format
- if((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 8) && (FreeImage_GetBPP(thumbnail) != 24)) {
- FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL);
- return FALSE;
- }
-
- // stores the thumbnail as a baseline JPEG into a memory block
- // return the memory block only if its size is within JFXX marker size limit!
- FIMEMORY *stream = FreeImage_OpenMemory();
-
- if(FreeImage_SaveToMemory(FIF_JPEG, thumbnail, stream, JPEG_BASELINE)) {
- // check that the memory block size is within JFXX marker size limit
- FreeImage_SeekMemory(stream, 0, SEEK_END);
- const long eof = FreeImage_TellMemory(stream);
- if(eof > MAX_JFXX_THUMB_SIZE) {
- FreeImage_OutputMessageProc(s_format_id, "Warning: attached thumbnail is %d bytes larger than maximum supported size - Thumbnail saving aborted", eof - MAX_JFXX_THUMB_SIZE);
- FreeImage_CloseMemory(stream);
- return FALSE;
- }
- } else {
- FreeImage_CloseMemory(stream);
- return FALSE;
- }
-
- BYTE* thData = NULL;
- DWORD thSize = 0;
-
- FreeImage_AcquireMemory(stream, &thData, &thSize);
-
- BYTE id_length = 5; //< "JFXX"
- BYTE type = JFXX_TYPE_JPEG;
-
- DWORD totalsize = id_length + sizeof(type) + thSize;
- jpeg_write_m_header(cinfo, JPEG_APP0, totalsize);
-
- jpeg_write_m_byte(cinfo, 'J');
- jpeg_write_m_byte(cinfo, 'F');
- jpeg_write_m_byte(cinfo, 'X');
- jpeg_write_m_byte(cinfo, 'X');
- jpeg_write_m_byte(cinfo, '\0');
-
- jpeg_write_m_byte(cinfo, type);
-
- // write thumbnail to destination.
- // We "cram it straight into the data destination module", because write_m_byte is slow
-
- freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest;
-
- BYTE* & out = dest->pub.next_output_byte;
- size_t & bufRemain = dest->pub.free_in_buffer;
-
- const BYTE *thData_end = thData + thSize;
-
- while(thData < thData_end) {
- *(out)++ = *(thData)++;
- if (--bufRemain == 0) {
- // buffer full - flush
- if (!dest->pub.empty_output_buffer(cinfo)) {
- break;
- }
- }
- }
-
- FreeImage_CloseMemory(stream);
-
- return TRUE;
-}
-
-/**
- Write JPEG special markers
-*/
-static BOOL
-write_markers(j_compress_ptr cinfo, FIBITMAP *dib) {
- // write thumbnail as a JFXX marker
- jpeg_write_jfxx(cinfo, dib);
-
- // write user comment as a JPEG_COM marker
- jpeg_write_comment(cinfo, dib);
-
- // write ICC profile
- jpeg_write_icc_profile(cinfo, dib);
-
- // write IPTC profile
- jpeg_write_iptc_profile(cinfo, dib);
-
- // write Adobe XMP profile
- jpeg_write_xmp_profile(cinfo, dib);
-
- // write Exif raw data
- jpeg_write_exif_profile_raw(cinfo, dib);
-
- return TRUE;
-}
-
-// ------------------------------------------------------------
-// Keep original size info when using scale option on loading
-// ------------------------------------------------------------
-static void
-store_size_info(FIBITMAP *dib, JDIMENSION width, JDIMENSION height) {
- char buffer[256];
- // create a tag
- FITAG *tag = FreeImage_CreateTag();
- if(tag) {
- size_t length = 0;
- // set the original width
- sprintf(buffer, "%d", (int)width);
- length = strlen(buffer) + 1; // include the NULL/0 value
- FreeImage_SetTagKey(tag, "OriginalJPEGWidth");
- FreeImage_SetTagLength(tag, (DWORD)length);
- FreeImage_SetTagCount(tag, (DWORD)length);
- FreeImage_SetTagType(tag, FIDT_ASCII);
- FreeImage_SetTagValue(tag, buffer);
- FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
- // set the original height
- sprintf(buffer, "%d", (int)height);
- length = strlen(buffer) + 1; // include the NULL/0 value
- FreeImage_SetTagKey(tag, "OriginalJPEGHeight");
- FreeImage_SetTagLength(tag, (DWORD)length);
- FreeImage_SetTagCount(tag, (DWORD)length);
- FreeImage_SetTagType(tag, FIDT_ASCII);
- FreeImage_SetTagValue(tag, buffer);
- FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag);
- // destroy the tag
- FreeImage_DeleteTag(tag);
- }
-}
-
-// ==========================================================
-// Plugin Implementation
-// ==========================================================
-
-static const char * DLL_CALLCONV
-Format() {
- return "JPEG";
-}
-
-static const char * DLL_CALLCONV
-Description() {
- return "JPEG - JFIF Compliant";
-}
-
-static const char * DLL_CALLCONV
-Extension() {
- return "jpg,jif,jpeg,jpe";
-}
-
-static const char * DLL_CALLCONV
-RegExpr() {
- return "^\377\330\377";
-}
-
-static const char * DLL_CALLCONV
-MimeType() {
- return "image/jpeg";
-}
-
-static BOOL DLL_CALLCONV
-Validate(FreeImageIO *io, fi_handle handle) {
- BYTE jpeg_signature[] = { 0xFF, 0xD8 };
- BYTE signature[2] = { 0, 0 };
-
- io->read_proc(signature, 1, sizeof(jpeg_signature), handle);
-
- return (memcmp(jpeg_signature, signature, sizeof(jpeg_signature)) == 0);
-}
-
-static BOOL DLL_CALLCONV
-SupportsExportDepth(int depth) {
- return (
- (depth == 8) ||
- (depth == 24)
- );
-}
-
-static BOOL DLL_CALLCONV
-SupportsExportType(FREE_IMAGE_TYPE type) {
- return (type == FIT_BITMAP) ? TRUE : FALSE;
-}
-
-static BOOL DLL_CALLCONV
-SupportsICCProfiles() {
- return TRUE;
-}
-
-static BOOL DLL_CALLCONV
-SupportsNoPixels() {
- return TRUE;
-}
-
-// ----------------------------------------------------------
-
-static FIBITMAP * DLL_CALLCONV
-Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
- if (handle) {
- FIBITMAP *dib = NULL;
-
- BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
-
- // set up the jpeglib structures
-
- struct jpeg_decompress_struct cinfo;
- ErrorManager fi_error_mgr;
-
- try {
-
- // step 1: allocate and initialize JPEG decompression object
-
- // we set up the normal JPEG error routines, then override error_exit & output_message
- cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
- fi_error_mgr.pub.error_exit = jpeg_error_exit;
- fi_error_mgr.pub.output_message = jpeg_output_message;
-
- // establish the setjmp return context for jpeg_error_exit to use
- if (setjmp(fi_error_mgr.setjmp_buffer)) {
- // If we get here, the JPEG code has signaled an error.
- // We need to clean up the JPEG object, close the input file, and return.
- jpeg_destroy_decompress(&cinfo);
- throw (const char*)NULL;
- }
-
- jpeg_create_decompress(&cinfo);
-
- // step 2a: specify data source (eg, a handle)
-
- jpeg_freeimage_src(&cinfo, handle, io);
-
- // step 2b: save special markers for later reading
-
- jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
- for(int m = 0; m < 16; m++) {
- jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF);
- }
-
- // step 3: read handle parameters with jpeg_read_header()
-
- jpeg_read_header(&cinfo, TRUE);
-
- // step 4: set parameters for decompression
-
- unsigned int scale_denom = 1; // fraction by which to scale image
- int requested_size = flags >> 16; // requested user size in pixels
- if(requested_size > 0) {
- // the JPEG codec can perform x2, x4 or x8 scaling on loading
- // try to find the more appropriate scaling according to user's need
- double scale = MAX((double)cinfo.image_width, (double)cinfo.image_height) / (double)requested_size;
- if(scale >= 8) {
- scale_denom = 8;
- } else if(scale >= 4) {
- scale_denom = 4;
- } else if(scale >= 2) {
- scale_denom = 2;
- }
- }
- cinfo.scale_num = 1;
- cinfo.scale_denom = scale_denom;
-
- if ((flags & JPEG_ACCURATE) != JPEG_ACCURATE) {
- cinfo.dct_method = JDCT_IFAST;
- cinfo.do_fancy_upsampling = FALSE;
- }
-
- if ((flags & JPEG_GREYSCALE) == JPEG_GREYSCALE) {
- // force loading as a 8-bit greyscale image
- cinfo.out_color_space = JCS_GRAYSCALE;
- }
-
- // step 5a: start decompressor and calculate output width and height
-
- jpeg_start_decompress(&cinfo);
-
- // step 5b: allocate dib and init header
-
- if((cinfo.output_components == 4) && (cinfo.out_color_space == JCS_CMYK)) {
- // CMYK image
- if((flags & JPEG_CMYK) == JPEG_CMYK) {
- // load as CMYK
- dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
- if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
- FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
- } else {
- // load as CMYK and convert to RGB
- dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
- if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
- }
- } else {
- // RGB or greyscale image
- dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 8 * cinfo.output_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
- if(!dib) throw FI_MSG_ERROR_DIB_MEMORY;
-
- if (cinfo.output_components == 1) {
- // build a greyscale palette
- RGBQUAD *colors = FreeImage_GetPalette(dib);
-
- for (int i = 0; i < 256; i++) {
- colors[i].rgbRed = (BYTE)i;
- colors[i].rgbGreen = (BYTE)i;
- colors[i].rgbBlue = (BYTE)i;
- }
- }
- }
- if(scale_denom != 1) {
- // store original size info if a scaling was requested
- store_size_info(dib, cinfo.image_width, cinfo.image_height);
- }
-
- // step 5c: handle metrices
-
- if (cinfo.density_unit == 1) {
- // dots/inch
- FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)cinfo.X_density) / 0.0254000 + 0.5));
- FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)cinfo.Y_density) / 0.0254000 + 0.5));
- } else if (cinfo.density_unit == 2) {
- // dots/cm
- FreeImage_SetDotsPerMeterX(dib, (unsigned) (cinfo.X_density * 100));
- FreeImage_SetDotsPerMeterY(dib, (unsigned) (cinfo.Y_density * 100));
- }
-
- // step 6: read special markers
-
- read_markers(&cinfo, dib);
-
- // --- header only mode => clean-up and return
-
- if (header_only) {
- // release JPEG decompression object
- jpeg_destroy_decompress(&cinfo);
- // return header data
- return dib;
- }
-
- // step 7a: while (scan lines remain to be read) jpeg_read_scanlines(...);
-
- if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) {
- // convert from CMYK to RGB
-
- JSAMPARRAY buffer; // output row buffer
- unsigned row_stride; // physical row width in output buffer
-
- // JSAMPLEs per row in output buffer
- row_stride = cinfo.output_width * cinfo.output_components;
- // make a one-row-high sample array that will go away when done with image
- buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
-
- while (cinfo.output_scanline < cinfo.output_height) {
- JSAMPROW src = buffer[0];
- JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
-
- jpeg_read_scanlines(&cinfo, buffer, 1);
-
- for(unsigned x = 0; x < cinfo.output_width; x++) {
- WORD K = (WORD)src[3];
- dst[FI_RGBA_RED] = (BYTE)((K * src[0]) / 255); // C -> R
- dst[FI_RGBA_GREEN] = (BYTE)((K * src[1]) / 255); // M -> G
- dst[FI_RGBA_BLUE] = (BYTE)((K * src[2]) / 255); // Y -> B
- src += 4;
- dst += 3;
- }
- }
- } else if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) == JPEG_CMYK)) {
- // convert from LibJPEG CMYK to standard CMYK
-
- JSAMPARRAY buffer; // output row buffer
- unsigned row_stride; // physical row width in output buffer
-
- // JSAMPLEs per row in output buffer
- row_stride = cinfo.output_width * cinfo.output_components;
- // make a one-row-high sample array that will go away when done with image
- buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
-
- while (cinfo.output_scanline < cinfo.output_height) {
- JSAMPROW src = buffer[0];
- JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
-
- jpeg_read_scanlines(&cinfo, buffer, 1);
-
- for(unsigned x = 0; x < cinfo.output_width; x++) {
- // CMYK pixels are inverted
- dst[0] = ~src[0]; // C
- dst[1] = ~src[1]; // M
- dst[2] = ~src[2]; // Y
- dst[3] = ~src[3]; // K
- src += 4;
- dst += 4;
- }
- }
-
- } else {
- // normal case (RGB or greyscale image)
-
- while (cinfo.output_scanline < cinfo.output_height) {
- JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1);
-
- jpeg_read_scanlines(&cinfo, &dst, 1);
- }
-
- // step 7b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB_RED, ...)
- // The default behavior of the JPEG library is kept "as is" because LibTIFF uses
- // LibJPEG "as is".
-
-#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
- SwapRedBlue32(dib);
-#endif
- }
-
- // step 8: finish decompression
-
- jpeg_finish_decompress(&cinfo);
-
- // step 9: release JPEG decompression object
-
- jpeg_destroy_decompress(&cinfo);
-
- // check for automatic Exif rotation
- if(!header_only && ((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE)) {
- RotateExif(&dib);
- }
-
- // everything went well. return the loaded dib
-
- return dib;
-
- } catch (const char *text) {
- jpeg_destroy_decompress(&cinfo);
- if(NULL != dib) {
- FreeImage_Unload(dib);
- }
- if(NULL != text) {
- FreeImage_OutputMessageProc(s_format_id, text);
- }
- }
- }
-
- return NULL;
-}
-
-// ----------------------------------------------------------
-
-static BOOL DLL_CALLCONV
-Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
- if ((dib) && (handle)) {
- try {
- // Check dib format
-
- const char *sError = "only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG";
-
- FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib);
- WORD bpp = (WORD)FreeImage_GetBPP(dib);
-
- if ((bpp != 24) && (bpp != 8)) {
- throw sError;
- }
-
- if(bpp == 8) {
- // allow grey, reverse grey and palette
- if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE)) {
- throw sError;
- }
- }
-
-
- struct jpeg_compress_struct cinfo;
- ErrorManager fi_error_mgr;
-
- // Step 1: allocate and initialize JPEG compression object
-
- // we set up the normal JPEG error routines, then override error_exit & output_message
- cinfo.err = jpeg_std_error(&fi_error_mgr.pub);
- fi_error_mgr.pub.error_exit = jpeg_error_exit;
- fi_error_mgr.pub.output_message = jpeg_output_message;
-
- // establish the setjmp return context for jpeg_error_exit to use
- if (setjmp(fi_error_mgr.setjmp_buffer)) {
- // If we get here, the JPEG code has signaled an error.
- // We need to clean up the JPEG object, close the input file, and return.
- jpeg_destroy_compress(&cinfo);
- throw (const char*)NULL;
- }
-
- // Now we can initialize the JPEG compression object
-
- jpeg_create_compress(&cinfo);
-
- // Step 2: specify data destination (eg, a file)
-
- jpeg_freeimage_dst(&cinfo, handle, io);
-
- // Step 3: set parameters for compression
-
- cinfo.image_width = FreeImage_GetWidth(dib);
- cinfo.image_height = FreeImage_GetHeight(dib);
-
- switch(color_type) {
- case FIC_MINISBLACK :
- case FIC_MINISWHITE :
- cinfo.in_color_space = JCS_GRAYSCALE;
- cinfo.input_components = 1;
- break;
-
- default :
- cinfo.in_color_space = JCS_RGB;
- cinfo.input_components = 3;
- break;
- }
-
- jpeg_set_defaults(&cinfo);
-
- // progressive-JPEG support
- if((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) {
- jpeg_simple_progression(&cinfo);
- }
-
- // compute optimal Huffman coding tables for the image
- if((flags & JPEG_OPTIMIZE) == JPEG_OPTIMIZE) {
- cinfo.optimize_coding = TRUE;
- }
-
- // Set JFIF density parameters from the DIB data
-
- cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib));
- cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib));
- cinfo.density_unit = 1; // dots / inch
-
- // thumbnail support (JFIF 1.02 extension markers)
- if(FreeImage_GetThumbnail(dib) != NULL) {
- cinfo.write_JFIF_header = 1; //<### force it, though when color is CMYK it will be incorrect
- cinfo.JFIF_minor_version = 2;
- }
-
- // baseline JPEG support
- if ((flags & JPEG_BASELINE) == JPEG_BASELINE) {
- cinfo.write_JFIF_header = 0; // No marker for non-JFIF colorspaces
- cinfo.write_Adobe_marker = 0; // write no Adobe marker by default
- }
-
- // set subsampling options if required
-
- if(cinfo.in_color_space == JCS_RGB) {
- if((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) {
- // 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100%
- // the horizontal color resolution is quartered
- cinfo.comp_info[0].h_samp_factor = 4; // Y
- cinfo.comp_info[0].v_samp_factor = 1;
- cinfo.comp_info[1].h_samp_factor = 1; // Cb
- cinfo.comp_info[1].v_samp_factor = 1;
- cinfo.comp_info[2].h_samp_factor = 1; // Cr
- cinfo.comp_info[2].v_samp_factor = 1;
- } else if((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) {
- // 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50%
- // the chrominance resolution in both the horizontal and vertical directions is cut in half
- cinfo.comp_info[0].h_samp_factor = 2; // Y
- cinfo.comp_info[0].v_samp_factor = 2;
- cinfo.comp_info[1].h_samp_factor = 1; // Cb
- cinfo.comp_info[1].v_samp_factor = 1;
- cinfo.comp_info[2].h_samp_factor = 1; // Cr
- cinfo.comp_info[2].v_samp_factor = 1;
- } else if((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low)
- // 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100%
- // half of the horizontal resolution in the chrominance is dropped (Cb & Cr),
- // while the full resolution is retained in the vertical direction, with respect to the luminance
- cinfo.comp_info[0].h_samp_factor = 2; // Y
- cinfo.comp_info[0].v_samp_factor = 1;
- cinfo.comp_info[1].h_samp_factor = 1; // Cb
- cinfo.comp_info[1].v_samp_factor = 1;
- cinfo.comp_info[2].h_samp_factor = 1; // Cr
- cinfo.comp_info[2].v_samp_factor = 1;
- }
- else if((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling)
- // 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100%
- // the resolution of chrominance information (Cb & Cr) is preserved
- // at the same rate as the luminance (Y) information
- cinfo.comp_info[0].h_samp_factor = 1; // Y
- cinfo.comp_info[0].v_samp_factor = 1;
- cinfo.comp_info[1].h_samp_factor = 1; // Cb
- cinfo.comp_info[1].v_samp_factor = 1;
- cinfo.comp_info[2].h_samp_factor = 1; // Cr
- cinfo.comp_info[2].v_samp_factor = 1;
- }
- }
-
- // Step 4: set quality
- // the first 7 bits are reserved for low level quality settings
- // the other bits are high level (i.e. enum-ish)
-
- int quality;
-
- if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) {
- quality = 10;
- } else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) {
- quality = 25;
- } else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) {
- quality = 50;
- } else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) {
- quality = 75;
- } else if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) {
- quality = 100;
- } else {
- if ((flags & 0x7F) == 0) {
- quality = 75;
- } else {
- quality = flags & 0x7F;
- }
- }
-
- jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */
-
- // Step 5: Start compressor
-
- jpeg_start_compress(&cinfo, TRUE);
-
- // Step 6: Write special markers
-
- if ((flags & JPEG_BASELINE) != JPEG_BASELINE) {
- write_markers(&cinfo, dib);
- }
-
- // Step 7: while (scan lines remain to be written)
-
- if(color_type == FIC_RGB) {
- // 24-bit RGB image : need to swap red and blue channels
- unsigned pitch = FreeImage_GetPitch(dib);
- BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE));
- if (target == NULL) {
- throw FI_MSG_ERROR_MEMORY;
- }
-
- while (cinfo.next_scanline < cinfo.image_height) {
- // get a copy of the scanline
- memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch);
-#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
- // swap R and B channels
- BYTE *target_p = target;
- for(unsigned x = 0; x < cinfo.image_width; x++) {
- INPLACESWAP(target_p[0], target_p[2]);
- target_p += 3;
- }
-#endif
- // write the scanline
- jpeg_write_scanlines(&cinfo, &target, 1);
- }
- free(target);
- }
- else if(color_type == FIC_MINISBLACK) {
- // 8-bit standard greyscale images
- while (cinfo.next_scanline < cinfo.image_height) {
- JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
-
- jpeg_write_scanlines(&cinfo, &b, 1);
- }
- }
- else if(color_type == FIC_PALETTE) {
- // 8-bit palettized images are converted to 24-bit images
- RGBQUAD *palette = FreeImage_GetPalette(dib);
- BYTE *target = (BYTE*)malloc(cinfo.image_width * 3);
- if (target == NULL) {
- throw FI_MSG_ERROR_MEMORY;
- }
-
- while (cinfo.next_scanline < cinfo.image_height) {
- BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
- FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette);
-
-#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
- // swap R and B channels
- BYTE *target_p = target;
- for(unsigned x = 0; x < cinfo.image_width; x++) {
- INPLACESWAP(target_p[0], target_p[2]);
- target_p += 3;
- }
-#endif
-
-
- jpeg_write_scanlines(&cinfo, &target, 1);
- }
-
- free(target);
- }
- else if(color_type == FIC_MINISWHITE) {
- // reverse 8-bit greyscale image, so reverse grey value on the fly
- unsigned i;
- BYTE reverse[256];
- BYTE *target = (BYTE *)malloc(cinfo.image_width);
- if (target == NULL) {
- throw FI_MSG_ERROR_MEMORY;
- }
-
- for(i = 0; i < 256; i++) {
- reverse[i] = (BYTE)(255 - i);
- }
-
- while(cinfo.next_scanline < cinfo.image_height) {
- BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1);
- for(i = 0; i < cinfo.image_width; i++) {
- target[i] = reverse[ source[i] ];
- }
- jpeg_write_scanlines(&cinfo, &target, 1);
- }
-
- free(target);
- }
-
- // Step 8: Finish compression
-
- jpeg_finish_compress(&cinfo);
-
- // Step 9: release JPEG compression object
-
- jpeg_destroy_compress(&cinfo);
-
- return TRUE;
-
- } catch (const char *text) {
- if(text) {
- FreeImage_OutputMessageProc(s_format_id, text);
- }
- return FALSE;
- }
- }
-
- return FALSE;
-}
-
-// ==========================================================
-// Init
-// ==========================================================
-
-void DLL_CALLCONV
-InitJPEG(Plugin *plugin, int format_id) {
- s_format_id = format_id;
-
- plugin->format_proc = Format;
- plugin->description_proc = Description;
- plugin->extension_proc = Extension;
- plugin->regexpr_proc = RegExpr;
- plugin->open_proc = NULL;
- plugin->close_proc = NULL;
- plugin->pagecount_proc = NULL;
- plugin->pagecapability_proc = NULL;
- plugin->load_proc = Load;
- plugin->save_proc = Save;
- plugin->validate_proc = Validate;
- plugin->mime_proc = MimeType;
- plugin->supports_export_bpp_proc = SupportsExportDepth;
- plugin->supports_export_type_proc = SupportsExportType;
- plugin->supports_icc_profiles_proc = SupportsICCProfiles;
- plugin->supports_no_pixels_proc = SupportsNoPixels;
-}