File indexing completed on 2024-05-12 04:33:19
0001 /* 0002 SPDX-FileCopyrightText: 1994-1996 Thomas G. Lane. 0003 SPDX-FileCopyrightText: 2009-2010 Patrick Spendrin <ps_ml@gmx.de> 0004 SPDX-FileCopyrightText: 2007-2018 Gilles Caulier <caulier dot gilles at gmail dot com> 0005 0006 This file is based on jdatadst.c from libjpeg. 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 //#define ENABLE_DEBUG_MESSAGES 1 0012 0013 #include "kipiwritehelp.h" 0014 0015 // Qt includes 0016 0017 #include <QIODevice> 0018 #include <QDebug> 0019 0020 // LibJPEG includes 0021 0022 extern "C" 0023 { 0024 #include <jerror.h> 0025 } 0026 0027 /* choose an efficiently fwrite'able size */ 0028 #define BUFFER_SIZE 4096 0029 0030 namespace KXMLKipiCmd 0031 { 0032 0033 //-- JPG helper methods --------------------------------------------------------------------- 0034 0035 /** 0036 Expanded data destination object for input/output for jpeg 0037 */ 0038 typedef struct 0039 { 0040 struct jpeg_destination_mgr pub; /* public fields */ 0041 0042 QIODevice* outDevice; /* target stream */ 0043 JOCTET* buffer; /* start of buffer */ 0044 } my_destination_mgr; 0045 0046 typedef my_destination_mgr* my_dest_ptr; 0047 0048 typedef struct 0049 { 0050 struct jpeg_source_mgr pub; /* public fields */ 0051 0052 QIODevice* inDevice; 0053 JOCTET buffer[BUFFER_SIZE]; 0054 } my_source_mgr; 0055 0056 /** 0057 * Initialize destination --- called by jpeg_start_compress 0058 * before any data is actually written. 0059 */ 0060 void init_destination (j_compress_ptr cinfo) 0061 { 0062 my_dest_ptr dest = (my_dest_ptr) cinfo->dest; 0063 0064 /* Allocate the output buffer --- it will be released when done with image */ 0065 dest->buffer = (JOCTET*) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 0066 BUFFER_SIZE * (size_t)sizeof(JOCTET)); 0067 0068 dest->pub.next_output_byte = dest->buffer; 0069 dest->pub.free_in_buffer = BUFFER_SIZE; 0070 } 0071 0072 /** 0073 * Empty the output buffer --- called whenever buffer fills up. 0074 */ 0075 boolean empty_output_buffer (j_compress_ptr cinfo) 0076 { 0077 my_dest_ptr dest = (my_dest_ptr) cinfo->dest; 0078 0079 if (dest->outDevice->write((char*)dest->buffer, BUFFER_SIZE) != (size_t) BUFFER_SIZE) 0080 ERREXIT(cinfo, JERR_FILE_WRITE); 0081 0082 dest->pub.next_output_byte = dest->buffer; 0083 dest->pub.free_in_buffer = BUFFER_SIZE; 0084 0085 return true; 0086 } 0087 0088 /** 0089 * Terminate destination --- called by jpeg_finish_compress 0090 * after all data has been written. Usually needs to flush buffer. 0091 * 0092 * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding 0093 * application must deal with any cleanup that should happen even 0094 * for error exit. 0095 */ 0096 void term_destination (j_compress_ptr cinfo) 0097 { 0098 my_dest_ptr dest = (my_dest_ptr) cinfo->dest; 0099 size_t datacount = BUFFER_SIZE - dest->pub.free_in_buffer; 0100 0101 /* Write any data remaining in the buffer */ 0102 if (datacount > 0) 0103 { 0104 if ((size_t)dest->outDevice->write((char*)dest->buffer, datacount) != (size_t)datacount) 0105 ERREXIT(cinfo, JERR_FILE_WRITE); 0106 } 0107 } 0108 0109 void kipi_jpeg_qiodevice_dest (j_compress_ptr cinfo, QIODevice* const outDevice) 0110 { 0111 my_dest_ptr dest; 0112 0113 /* The destination object is made permanent so that multiple JPEG images 0114 * can be written to the same file without re-executing jpeg_stdio_dest. 0115 * This makes it dangerous to use this manager and a different destination 0116 * manager serially with the same JPEG object, because their private object 0117 * sizes may be different. Caveat programmer. 0118 */ 0119 if (cinfo->dest == nullptr) 0120 { 0121 /* first time for this JPEG object? */ 0122 cinfo->dest = (struct jpeg_destination_mgr*) 0123 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, 0124 (size_t)sizeof(my_destination_mgr)); 0125 } 0126 0127 dest = (my_dest_ptr) cinfo->dest; 0128 dest->pub.init_destination = init_destination; 0129 dest->pub.empty_output_buffer = empty_output_buffer; 0130 dest->pub.term_destination = term_destination; 0131 dest->outDevice = outDevice; 0132 } 0133 0134 boolean fill_input_buffer(j_decompress_ptr cinfo) 0135 { 0136 my_source_mgr* const src = (my_source_mgr*)cinfo->src; 0137 Q_ASSERT(src->inDevice); 0138 int readSize = src->inDevice->read((char*)src->buffer, BUFFER_SIZE); 0139 0140 if (readSize > 0) 0141 { 0142 src->pub.next_input_byte = src->buffer; 0143 src->pub.bytes_in_buffer = readSize; 0144 } 0145 else 0146 { 0147 /** 0148 * JPEG file is broken. We feed the decoder with fake EOI, as specified 0149 * in the libjpeg documentation. 0150 */ 0151 static JOCTET fakeEOI[2] = { JOCTET(0xFF), JOCTET(JPEG_EOI)}; 0152 qWarning() << "Image is incomplete"; 0153 src->pub.next_input_byte = fakeEOI; 0154 src->pub.bytes_in_buffer = 2; 0155 } 0156 0157 return true; 0158 } 0159 0160 void init_source(j_decompress_ptr cinfo) 0161 { 0162 fill_input_buffer(cinfo); 0163 } 0164 0165 void skip_input_data(j_decompress_ptr cinfo, long num_bytes) 0166 { 0167 my_source_mgr* const src = (my_source_mgr*)cinfo->src; 0168 0169 if (num_bytes > 0) 0170 { 0171 while (num_bytes > (long) src->pub.bytes_in_buffer) 0172 { 0173 num_bytes -= (long) src->pub.bytes_in_buffer; 0174 fill_input_buffer(cinfo); 0175 /** 0176 * we assume that fill_input_buffer will never return FALSE, so 0177 * suspension need not be handled. 0178 */ 0179 } 0180 src->pub.next_input_byte += (size_t) num_bytes; 0181 src->pub.bytes_in_buffer -= (size_t) num_bytes; 0182 } 0183 } 0184 0185 void term_source(j_decompress_ptr) 0186 { 0187 } 0188 0189 void kipi_jpeg_qiodevice_src(j_decompress_ptr cinfo, QIODevice* const ioDevice) 0190 { 0191 Q_ASSERT(!cinfo->src); 0192 my_source_mgr* const src = (my_source_mgr*) 0193 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, 0194 sizeof(my_source_mgr)); 0195 cinfo->src = (jpeg_source_mgr*)src; 0196 0197 src->pub.init_source = init_source; 0198 src->pub.fill_input_buffer = fill_input_buffer; 0199 src->pub.skip_input_data = skip_input_data; 0200 src->pub.resync_to_restart = jpeg_resync_to_restart; 0201 src->pub.term_source = term_source; 0202 src->inDevice = ioDevice; 0203 } 0204 0205 //-- PNG helper methods --------------------------------------------------------------------- 0206 0207 void kipi_png_write_fn(png_structp png_ptr, png_bytep data, png_size_t length) 0208 { 0209 QIODevice* const out = (QIODevice*)png_get_io_ptr(png_ptr); 0210 uint nr = out->write((char*)data, length); 0211 0212 if (nr != length) 0213 { 0214 png_error(png_ptr, "Write Error"); 0215 return; 0216 } 0217 } 0218 0219 void kipi_png_flush_fn(png_structp png_ptr) 0220 { 0221 Q_UNUSED(png_ptr); 0222 } 0223 0224 //-- TIF helper methods --------------------------------------------------------------------- 0225 0226 void kipi_tiff_warning(const char* module, const char* format, va_list warnings) 0227 { 0228 #ifdef ENABLE_DEBUG_MESSAGES 0229 char message[4096]; 0230 vsnprintf(message, 4096, format, warnings); 0231 qDebug() << module << "::" << message ; 0232 #else 0233 Q_UNUSED(module); 0234 Q_UNUSED(format); 0235 Q_UNUSED(warnings); 0236 #endif 0237 } 0238 0239 void kipi_tiff_error(const char* module, const char* format, va_list errors) 0240 { 0241 #ifdef ENABLE_DEBUG_MESSAGES 0242 char message[4096]; 0243 vsnprintf(message, 4096, format, errors); 0244 qDebug() << module << "::" << message ; 0245 #else 0246 Q_UNUSED(module); 0247 Q_UNUSED(format); 0248 Q_UNUSED(errors); 0249 #endif 0250 } 0251 0252 } // namespace KXMLKipiCmd