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