summaryrefslogtreecommitdiffstats
path: root/kipi-plugins/jpeglossless/jpegtransform.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kipi-plugins/jpeglossless/jpegtransform.cpp')
-rw-r--r--kipi-plugins/jpeglossless/jpegtransform.cpp441
1 files changed, 441 insertions, 0 deletions
diff --git a/kipi-plugins/jpeglossless/jpegtransform.cpp b/kipi-plugins/jpeglossless/jpegtransform.cpp
new file mode 100644
index 0000000..7009e47
--- /dev/null
+++ b/kipi-plugins/jpeglossless/jpegtransform.cpp
@@ -0,0 +1,441 @@
+/* ============================================================
+ *
+ * This file is a part of kipi-plugins project
+ * http://www.kipi-plugins.org
+ *
+ * Date : 2004-06-08
+ * Description : Loss less JPEG files transformations.
+ *
+ * Copyright (C) 2004 by Ralf Hoelzer <kde at ralfhoelzer.com>
+ * Copyright (C) 2004-2005 by Marcel Wiesweg <marcel.wiesweg@gmx.de>
+ * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * NOTE: Do not use kdDebug() in this implementation because
+ * it will be multithreaded. Use qDebug() instead.
+ * See B.K.O #133026 for details.
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ============================================================ */
+
+// C++ includes.
+
+#include <cstdio>
+#include <cstdlib>
+
+// C Ansi includes.
+
+extern "C"
+{
+#include <sys/types.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <jpeglib.h>
+}
+
+// Qt includes.
+
+#include <qstring.h>
+#include <qwmatrix.h>
+#include <qfile.h>
+
+// KDE includes.
+
+#include <klocale.h>
+#include <ktempfile.h>
+
+// Local includes.
+
+#include "pluginsversion.h"
+#include "transupp.h"
+#include "jpegtransform.h"
+
+namespace KIPIJPEGLossLessPlugin
+{
+
+const Matrix Matrix::none ( 1, 0, 0, 1);
+const Matrix Matrix::rotate90 ( 0, -1, 1, 0);
+const Matrix Matrix::rotate180 (-1, 0, 0, -1);
+const Matrix Matrix::rotate270 ( 0, 1, -1, 0);
+const Matrix Matrix::flipHorizontal (-1, 0, 0, 1);
+const Matrix Matrix::flipVertical ( 1, 0, 0, -1);
+const Matrix Matrix::rotate90flipHorizontal ( 0, 1, 1, 0);
+const Matrix Matrix::rotate90flipVertical ( 0, -1, -1, 0);
+
+
+// To manage Errors/Warnings handling provide by libjpeg
+
+//#define ENABLE_DEBUG_MESSAGES
+
+struct jpegtransform_jpeg_error_mgr : public jpeg_error_mgr
+{
+ jmp_buf setjmp_buffer;
+};
+
+static void jpegtransform_jpeg_error_exit(j_common_ptr cinfo);
+static void jpegtransform_jpeg_emit_message(j_common_ptr cinfo, int msg_level);
+static void jpegtransform_jpeg_output_message(j_common_ptr cinfo);
+
+static void jpegtransform_jpeg_error_exit(j_common_ptr cinfo)
+{
+ jpegtransform_jpeg_error_mgr* myerr = (jpegtransform_jpeg_error_mgr*) cinfo->err;
+
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ qDebug("%s", buffer)
+#endif
+
+ longjmp(myerr->setjmp_buffer, 1);
+}
+
+static void jpegtransform_jpeg_emit_message(j_common_ptr cinfo, int msg_level)
+{
+ Q_UNUSED(msg_level)
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ qDebug("%s (%i)", buffer, msg_level);
+#endif
+}
+
+static void jpegtransform_jpeg_output_message(j_common_ptr cinfo)
+{
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ qDebug("%s", buffer)
+#endif
+}
+
+bool transformJPEG(const QString& src, const QString& destGiven,
+ Matrix &userAction, QString& err)
+{
+ //may be modified
+ QString dest(destGiven);
+
+ JCOPY_OPTION copyoption = JCOPYOPT_ALL;
+ jpeg_transform_info transformoption;
+
+ transformoption.force_grayscale = false;
+ transformoption.trim = false;
+
+ struct jpeg_decompress_struct srcinfo;
+ struct jpeg_compress_struct dstinfo;
+ struct jpegtransform_jpeg_error_mgr jsrcerr, jdsterr;
+ jvirt_barray_ptr * src_coef_arrays;
+ jvirt_barray_ptr * dst_coef_arrays;
+
+ Matrix exifAction, action;
+ JXFORM_CODE flip, rotate;
+
+ // Initialize the JPEG decompression object with default error handling
+ srcinfo.err = jpeg_std_error(&jsrcerr);
+ srcinfo.err->error_exit = jpegtransform_jpeg_error_exit;
+ srcinfo.err->emit_message = jpegtransform_jpeg_emit_message;
+ srcinfo.err->output_message = jpegtransform_jpeg_output_message;
+
+ // Initialize the JPEG compression object with default error handling
+ dstinfo.err = jpeg_std_error(&jdsterr);
+ dstinfo.err->error_exit = jpegtransform_jpeg_error_exit;
+ dstinfo.err->emit_message = jpegtransform_jpeg_emit_message;
+ dstinfo.err->output_message = jpegtransform_jpeg_output_message;
+
+ FILE *input_file;
+ FILE *output_file;
+
+ input_file = fopen(QFile::encodeName(src), "rb");
+ if (!input_file)
+ {
+ qDebug("ImageRotate/ImageFlip: Error in opening input file");
+ err = i18n("Error in opening input file");
+ return false;
+ }
+
+ output_file = fopen(QFile::encodeName(dest), "wb");
+ if (!output_file)
+ {
+ fclose(input_file);
+ qDebug("ImageRotate/ImageFlip: Error in opening output file");
+ err = i18n("Error in opening output file");
+ return false;
+ }
+
+ if (setjmp(jsrcerr.setjmp_buffer) || setjmp(jdsterr.setjmp_buffer))
+ {
+ jpeg_destroy_decompress(&srcinfo);
+ jpeg_destroy_compress(&dstinfo);
+ fclose(input_file);
+ fclose(output_file);
+ return false;
+ }
+
+ jpeg_create_decompress(&srcinfo);
+ jpeg_create_compress(&dstinfo);
+
+ jpeg_stdio_src(&srcinfo, input_file);
+ jcopy_markers_setup(&srcinfo, copyoption);
+
+ (void) jpeg_read_header(&srcinfo, true);
+
+ // Get Exif orientation action to do.
+ KExiv2Iface::KExiv2 exiv2Iface;
+ exiv2Iface.load(src);
+ getExifAction(exifAction, exiv2Iface.getImageOrientation());
+
+ // Compose actions: first exif, then user
+ action*=exifAction;
+ action*=userAction;
+
+ // Convert action into flip+rotate action
+ convertTransform(action, flip, rotate);
+ qDebug("Transforming with option %i %i", flip, rotate);
+ if (flip == JXFORM_NONE && rotate == JXFORM_NONE)
+ {
+ err = "nothing to do"; // magic string
+ fclose(output_file);
+ fclose(input_file);
+ return false;
+ }
+
+ bool twoPass = (flip != JXFORM_NONE);
+
+ // If twoPass is true, we need another file (src -> tempFile -> destGiven)
+ if (twoPass)
+ {
+ KTempFile tempFile;
+ tempFile.setAutoDelete(false);
+ dest=tempFile.name();
+ }
+
+ output_file = fopen(QFile::encodeName(dest), "wb");
+ if (!output_file)
+ {
+ fclose(input_file);
+ qDebug("ImageRotate/ImageFlip: Error in opening output file");
+ err = i18n("Error in opening output file");
+ return false;
+ }
+
+ // First rotate - execute even if rotate is JXFORM_NONE to apply new EXIF settings
+ transformoption.transform=rotate;
+
+ jtransform_request_workspace(&srcinfo, &transformoption);
+
+ // Read source file as DCT coefficients
+ src_coef_arrays = jpeg_read_coefficients(&srcinfo);
+
+ // Initialize destination compression parameters from source values
+ jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
+
+ dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo,
+ src_coef_arrays, &transformoption);
+
+ // Specify data destination for compression
+ jpeg_stdio_dest(&dstinfo, output_file);
+
+ // Do not write a JFIF header if previously the image did not contain it
+ dstinfo.write_JFIF_header = false;
+
+ // Start compressor (note no image data is actually written here)
+ jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
+
+ // Copy to the output file any extra markers that we want to preserve
+ jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
+
+ jtransform_execute_transformation(&srcinfo, &dstinfo,
+ src_coef_arrays, &transformoption);
+
+ // Finish compression and release memory
+ jpeg_finish_compress(&dstinfo);
+ jpeg_destroy_compress(&dstinfo);
+ (void) jpeg_finish_decompress(&srcinfo);
+ jpeg_destroy_decompress(&srcinfo);
+
+ fclose(input_file);
+ fclose(output_file);
+
+ // Flip if needed
+ if (twoPass)
+ {
+ // Initialize the JPEG decompression object with default error handling
+ srcinfo.err = jpeg_std_error(&jsrcerr);
+ jpeg_create_decompress(&srcinfo);
+
+ // Initialize the JPEG compression object with default error handling
+ dstinfo.err = jpeg_std_error(&jdsterr);
+ jpeg_create_compress(&dstinfo);
+
+ input_file = fopen(QFile::encodeName(dest), "rb");
+ if (!input_file)
+ {
+ qDebug("ImageRotate/ImageFlip: Error in opening input file");
+ err = i18n("Error in opening input file");
+ return false;
+ }
+
+ output_file = fopen(QFile::encodeName(destGiven), "wb");
+ if (!output_file)
+ {
+ fclose(input_file);
+ qDebug("ImageRotate/ImageFlip: Error in opening output file");
+ err = i18n("Error in opening output file");
+ return false;
+ }
+
+ jpeg_stdio_src(&srcinfo, input_file);
+ jcopy_markers_setup(&srcinfo, copyoption);
+
+ (void) jpeg_read_header(&srcinfo, true);
+
+ transformoption.transform=flip;
+ jtransform_request_workspace(&srcinfo, &transformoption);
+
+ // Read source file as DCT coefficients
+ src_coef_arrays = jpeg_read_coefficients(&srcinfo);
+
+ // Initialize destination compression parameters from source values
+ jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
+
+ dst_coef_arrays = jtransform_adjust_parameters(&srcinfo,
+ &dstinfo,
+ src_coef_arrays,
+ &transformoption);
+
+ // Specify data destination for compression
+ jpeg_stdio_dest(&dstinfo, output_file);
+
+ // Do not write a JFIF header if previously the image did not contain it
+ dstinfo.write_JFIF_header = false;
+
+ // Start compressor (note no image data is actually written here)
+ jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
+
+ // Copy to the output file any extra markers that we want to preserve
+ jcopy_markers_execute(&srcinfo, &dstinfo, copyoption);
+
+ jtransform_execute_transformation(&srcinfo, &dstinfo,
+ src_coef_arrays, &transformoption);
+
+ // Finish compression and release memory
+ jpeg_finish_compress(&dstinfo);
+ jpeg_destroy_compress(&dstinfo);
+ (void) jpeg_finish_decompress(&srcinfo);
+ jpeg_destroy_decompress(&srcinfo);
+
+ fclose(input_file);
+ fclose(output_file);
+
+ // Unlink temp file
+ unlink(QFile::encodeName(dest));
+ }
+
+ // And set finaly update the metadata to target file.
+
+ QImage img(destGiven);
+ QImage exifThumbnail = img.scale(160, 120, QImage::ScaleMin);
+ exiv2Iface.load(destGiven);
+ exiv2Iface.setImageOrientation(KExiv2Iface::KExiv2::ORIENTATION_NORMAL);
+ exiv2Iface.setImageProgramId(QString("Kipi-plugins"), QString(kipiplugins_version));
+ exiv2Iface.setImageDimensions(img.size());
+ exiv2Iface.setExifThumbnail(exifThumbnail);
+ exiv2Iface.save(destGiven);
+
+ return true;
+}
+
+/** Converts the mathematically correct description
+ into the primitive operations that can be carried out losslessly.
+*/
+void convertTransform(Matrix &action, JXFORM_CODE &flip, JXFORM_CODE &rotate)
+{
+ flip = JXFORM_NONE;
+ rotate = JXFORM_NONE;
+
+ if (action == Matrix::rotate90)
+ {
+ rotate = JXFORM_ROT_90;
+ }
+ else if (action == Matrix::rotate180)
+ {
+ rotate = JXFORM_ROT_180;
+ }
+ else if (action == Matrix::rotate270)
+ {
+ rotate = JXFORM_ROT_270;
+ }
+ else if (action == Matrix::flipHorizontal)
+ {
+ flip = JXFORM_FLIP_H;
+ }
+ else if (action == Matrix::flipVertical)
+ {
+ flip = JXFORM_FLIP_V;
+ }
+ else if (action == Matrix::rotate90flipHorizontal)
+ {
+ //first rotate, then flip!
+ rotate = JXFORM_ROT_90;
+ flip = JXFORM_FLIP_H;
+ }
+ else if (action == Matrix::rotate90flipVertical)
+ {
+ //first rotate, then flip!
+ rotate = JXFORM_ROT_90;
+ flip = JXFORM_FLIP_V;
+ }
+}
+
+void getExifAction(Matrix &action, KExiv2Iface::KExiv2::ImageOrientation exifOrientation)
+{
+ switch (exifOrientation)
+ {
+ case KExiv2Iface::KExiv2::ORIENTATION_NORMAL:
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_HFLIP:
+ action*=Matrix::flipHorizontal;
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_ROT_180:
+ action*=Matrix::rotate180;
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_VFLIP:
+ action*=Matrix::flipVertical;
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_ROT_90_HFLIP:
+ action*=Matrix::rotate90flipHorizontal;
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_ROT_90:
+ action*=Matrix::rotate90;
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_ROT_90_VFLIP:
+ action*=Matrix::rotate90flipVertical;
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_ROT_270:
+ action*=Matrix::rotate270;
+ break;
+
+ case KExiv2Iface::KExiv2::ORIENTATION_UNSPECIFIED:
+ action*=Matrix::none;
+ break;
+ }
+}
+
+} // NameSpace KIPIJPEGLossLessPlugin