|
|
|
/* ============================================================
|
|
|
|
*
|
|
|
|
* This file is a part of digiKam project
|
|
|
|
* http://www.digikam.org
|
|
|
|
*
|
|
|
|
* Date : 2004-09-29
|
|
|
|
* Description : perform lossless rotation/flip to JPEG file
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
|
|
|
|
* Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
#define XMD_H
|
|
|
|
|
|
|
|
// C++ includes.
|
|
|
|
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
|
|
|
|
|
|
|
// C Ansi includes.
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
{
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <utime.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include <jpeglib.h>
|
|
|
|
}
|
|
|
|
|
|
|
|
// TQt includes.
|
|
|
|
|
|
|
|
#include <tqcstring.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
|
|
|
|
// Local includes.
|
|
|
|
|
|
|
|
#include "ddebug.h"
|
|
|
|
#include "dmetadata.h"
|
|
|
|
#include "transupp.h"
|
|
|
|
#include "jpegutils.h"
|
|
|
|
|
|
|
|
namespace Digikam
|
|
|
|
{
|
|
|
|
|
|
|
|
// To manage Errors/Warnings handling provide by libjpeg
|
|
|
|
|
|
|
|
//#define ENABLE_DEBUG_MESSAGES
|
|
|
|
|
|
|
|
struct jpegutils_jpeg_error_mgr : public jpeg_error_mgr
|
|
|
|
{
|
|
|
|
jmp_buf setjmp_buffer;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void jpegutils_jpeg_error_exit(j_common_ptr cinfo);
|
|
|
|
static void jpegutils_jpeg_emit_message(j_common_ptr cinfo, int msg_level);
|
|
|
|
static void jpegutils_jpeg_output_message(j_common_ptr cinfo);
|
|
|
|
|
|
|
|
static void jpegutils_jpeg_error_exit(j_common_ptr cinfo)
|
|
|
|
{
|
|
|
|
jpegutils_jpeg_error_mgr* myerr = (jpegutils_jpeg_error_mgr*) cinfo->err;
|
|
|
|
|
|
|
|
char buffer[JMSG_LENGTH_MAX];
|
|
|
|
(*cinfo->err->format_message)(cinfo, buffer);
|
|
|
|
|
|
|
|
#ifdef ENABLE_DEBUG_MESSAGES
|
|
|
|
DDebug() << k_funcinfo << buffer << endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
longjmp(myerr->setjmp_buffer, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void jpegutils_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
|
|
|
|
DDebug() << k_funcinfo << buffer << " (" << msg_level << ")" << endl;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void jpegutils_jpeg_output_message(j_common_ptr cinfo)
|
|
|
|
{
|
|
|
|
char buffer[JMSG_LENGTH_MAX];
|
|
|
|
(*cinfo->err->format_message)(cinfo, buffer);
|
|
|
|
|
|
|
|
#ifdef ENABLE_DEBUG_MESSAGES
|
|
|
|
DDebug() << k_funcinfo << buffer << endl;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool loadJPEGScaled(TQImage& image, const TQString& path, int maximumSize)
|
|
|
|
{
|
|
|
|
TQString format = TQImageIO::imageFormat(path);
|
|
|
|
if (format !="JPEG") return false;
|
|
|
|
|
|
|
|
FILE* inputFile=fopen(TQFile::encodeName(path), "rb");
|
|
|
|
if(!inputFile)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
struct jpeg_decompress_struct cinfo;
|
|
|
|
struct jpegutils_jpeg_error_mgr jerr;
|
|
|
|
|
|
|
|
// JPEG error handling - thanks to Marcus Meissner
|
|
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
|
|
cinfo.err->error_exit = jpegutils_jpeg_error_exit;
|
|
|
|
cinfo.err->emit_message = jpegutils_jpeg_emit_message;
|
|
|
|
cinfo.err->output_message = jpegutils_jpeg_output_message;
|
|
|
|
|
|
|
|
if (setjmp(jerr.setjmp_buffer))
|
|
|
|
{
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
fclose(inputFile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
jpeg_stdio_src(&cinfo, inputFile);
|
|
|
|
jpeg_read_header(&cinfo, true);
|
|
|
|
|
|
|
|
int imgSize = TQMAX(cinfo.image_width, cinfo.image_height);
|
|
|
|
|
|
|
|
// libjpeg supports 1/1, 1/2, 1/4, 1/8
|
|
|
|
int scale=1;
|
|
|
|
while(maximumSize*scale*2<=imgSize)
|
|
|
|
{
|
|
|
|
scale*=2;
|
|
|
|
}
|
|
|
|
if(scale>8) scale=8;
|
|
|
|
|
|
|
|
cinfo.scale_num=1;
|
|
|
|
cinfo.scale_denom=scale;
|
|
|
|
|
|
|
|
switch (cinfo.jpeg_color_space)
|
|
|
|
{
|
|
|
|
case JCS_UNKNOWN:
|
|
|
|
break;
|
|
|
|
case JCS_GRAYSCALE:
|
|
|
|
case JCS_RGB:
|
|
|
|
case JCS_YCbCr:
|
|
|
|
cinfo.out_color_space = JCS_RGB;
|
|
|
|
break;
|
|
|
|
case JCS_CMYK:
|
|
|
|
case JCS_YCCK:
|
|
|
|
cinfo.out_color_space = JCS_CMYK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
|
|
|
|
TQImage img;
|
|
|
|
|
|
|
|
// We only take RGB with 1 or 3 components, or CMYK with 4 components
|
|
|
|
if (!(
|
|
|
|
(cinfo.out_color_space == JCS_RGB && (cinfo.output_components == 3 || cinfo.output_components == 1))
|
|
|
|
|| (cinfo.out_color_space == JCS_CMYK && cinfo.output_components == 4)
|
|
|
|
))
|
|
|
|
{
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
fclose(inputFile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(cinfo.output_components)
|
|
|
|
{
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
img.create( cinfo.output_width, cinfo.output_height, 32 );
|
|
|
|
break;
|
|
|
|
case 1: // B&W image
|
|
|
|
img.create( cinfo.output_width, cinfo.output_height, 8, 256 );
|
|
|
|
for (int i = 0 ; i < 256 ; i++)
|
|
|
|
img.setColor(i, tqRgb(i, i, i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
uchar** lines = img.jumpTable();
|
|
|
|
while (cinfo.output_scanline < cinfo.output_height)
|
|
|
|
jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, cinfo.output_height);
|
|
|
|
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
|
|
|
|
// Expand 24->32 bpp
|
|
|
|
if ( cinfo.output_components == 3 )
|
|
|
|
{
|
|
|
|
for (uint j=0; j<cinfo.output_height; j++)
|
|
|
|
{
|
|
|
|
uchar *in = img.scanLine(j) + cinfo.output_width*3;
|
|
|
|
TQRgb *out = (TQRgb*)( img.scanLine(j) );
|
|
|
|
|
|
|
|
for (uint i=cinfo.output_width; i--; )
|
|
|
|
{
|
|
|
|
in -= 3;
|
|
|
|
out[i] = tqRgb(in[0], in[1], in[2]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( cinfo.output_components == 4 )
|
|
|
|
{
|
|
|
|
// CMYK conversion
|
|
|
|
for (uint j=0; j<cinfo.output_height; j++)
|
|
|
|
{
|
|
|
|
uchar *in = img.scanLine(j) + cinfo.output_width*4;
|
|
|
|
TQRgb *out = (TQRgb*)( img.scanLine(j) );
|
|
|
|
|
|
|
|
for (uint i=cinfo.output_width; i--; )
|
|
|
|
{
|
|
|
|
in -= 4;
|
|
|
|
int k = in[3];
|
|
|
|
out[i] = tqRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int newMax = TQMAX(cinfo.output_width, cinfo.output_height);
|
|
|
|
int newx = maximumSize*cinfo.output_width / newMax;
|
|
|
|
int newy = maximumSize*cinfo.output_height / newMax;
|
|
|
|
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
fclose(inputFile);
|
|
|
|
|
|
|
|
image = img;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool exifRotate(const TQString& file, const TQString& documentName)
|
|
|
|
{
|
|
|
|
TQFileInfo fi(file);
|
|
|
|
if (!fi.exists())
|
|
|
|
{
|
|
|
|
DDebug() << "ExifRotate: file do not exist: " << file << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isJpegImage(file))
|
|
|
|
{
|
|
|
|
DMetadata metaData;
|
|
|
|
if (!metaData.load(file))
|
|
|
|
{
|
|
|
|
DDebug() << "ExifRotate: no Exif data found: " << file << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQString temp(fi.dirPath(true) + "/.digikam-exifrotate-");
|
|
|
|
temp.append(TQString::number(getpid()));
|
|
|
|
temp.append(TQString(".jpg"));
|
|
|
|
|
|
|
|
TQCString in = TQFile::encodeName(file);
|
|
|
|
TQCString out = TQFile::encodeName(temp);
|
|
|
|
|
|
|
|
JCOPY_OPTION copyoption = JCOPYOPT_ALL;
|
|
|
|
jpeg_transform_info transformoption;
|
|
|
|
|
|
|
|
transformoption.force_grayscale = false;
|
|
|
|
transformoption.trim = false;
|
|
|
|
transformoption.transform = JXFORM_NONE;
|
|
|
|
|
|
|
|
// we have the exif info. check the orientation
|
|
|
|
|
|
|
|
switch(metaData.getImageOrientation())
|
|
|
|
{
|
|
|
|
case(DMetadata::ORIENTATION_UNSPECIFIED):
|
|
|
|
case(DMetadata::ORIENTATION_NORMAL):
|
|
|
|
break;
|
|
|
|
case(DMetadata::ORIENTATION_HFLIP):
|
|
|
|
{
|
|
|
|
transformoption.transform = JXFORM_FLIP_H;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case(DMetadata::ORIENTATION_ROT_180):
|
|
|
|
{
|
|
|
|
transformoption.transform = JXFORM_ROT_180;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case(DMetadata::ORIENTATION_VFLIP):
|
|
|
|
{
|
|
|
|
transformoption.transform = JXFORM_FLIP_V;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case(DMetadata::ORIENTATION_ROT_90_HFLIP):
|
|
|
|
{
|
|
|
|
transformoption.transform = JXFORM_TRANSPOSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case(DMetadata::ORIENTATION_ROT_90):
|
|
|
|
{
|
|
|
|
transformoption.transform = JXFORM_ROT_90;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case(DMetadata::ORIENTATION_ROT_90_VFLIP):
|
|
|
|
{
|
|
|
|
transformoption.transform = JXFORM_TRANSVERSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case(DMetadata::ORIENTATION_ROT_270):
|
|
|
|
{
|
|
|
|
transformoption.transform = JXFORM_ROT_270;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (transformoption.transform == JXFORM_NONE)
|
|
|
|
{
|
|
|
|
DDebug() << "ExifRotate: no rotation to perform: " << file << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct jpeg_decompress_struct srcinfo;
|
|
|
|
struct jpeg_compress_struct dstinfo;
|
|
|
|
struct jpegutils_jpeg_error_mgr jsrcerr, jdsterr;
|
|
|
|
jvirt_barray_ptr* src_coef_arrays;
|
|
|
|
jvirt_barray_ptr* dst_coef_arrays;
|
|
|
|
|
|
|
|
// Initialize the JPEG decompression object with default error handling
|
|
|
|
srcinfo.err = jpeg_std_error(&jsrcerr);
|
|
|
|
srcinfo.err->error_exit = jpegutils_jpeg_error_exit;
|
|
|
|
srcinfo.err->emit_message = jpegutils_jpeg_emit_message;
|
|
|
|
srcinfo.err->output_message = jpegutils_jpeg_output_message;
|
|
|
|
|
|
|
|
// Initialize the JPEG compression object with default error handling
|
|
|
|
dstinfo.err = jpeg_std_error(&jdsterr);
|
|
|
|
dstinfo.err->error_exit = jpegutils_jpeg_error_exit;
|
|
|
|
dstinfo.err->emit_message = jpegutils_jpeg_emit_message;
|
|
|
|
dstinfo.err->output_message = jpegutils_jpeg_output_message;
|
|
|
|
|
|
|
|
FILE *input_file;
|
|
|
|
FILE *output_file;
|
|
|
|
|
|
|
|
input_file = fopen(in, "rb");
|
|
|
|
if (!input_file)
|
|
|
|
{
|
|
|
|
DWarning() << "ExifRotate: Error in opening input file: " << input_file << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
output_file = fopen(out, "wb");
|
|
|
|
if (!output_file)
|
|
|
|
{
|
|
|
|
fclose(input_file);
|
|
|
|
DWarning() << "ExifRotate: Error in opening output file: " << output_file << endl;
|
|
|
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
// -- Metadata operations ------------------------------------------------------
|
|
|
|
|
|
|
|
// Reset the Exif orientation tag of the temp image to normal
|
|
|
|
DDebug() << "ExifRotate: set Qt::Orientation tag to normal: " << file << endl;
|
|
|
|
|
|
|
|
metaData.load(temp);
|
|
|
|
metaData.setImageOrientation(DMetadata::ORIENTATION_NORMAL);
|
|
|
|
TQImage img(temp);
|
|
|
|
|
|
|
|
// Get the new image dimension of the temp image. Using a dummy TQImage objet here
|
|
|
|
// has a sense because the Exif dimension information can be missing from original image.
|
|
|
|
// Get new dimensions with TQImage will always work...
|
|
|
|
metaData.setImageDimensions(img.size());
|
|
|
|
|
|
|
|
// Update the image thumbnail.
|
|
|
|
TQImage thumb = img.scale(160, 120, TQ_ScaleMin);
|
|
|
|
metaData.setExifThumbnail(thumb);
|
|
|
|
|
|
|
|
// Update Exif Document Name tag (the orinal file name from camera for example).
|
|
|
|
metaData.setExifTagString("Exif.Image.DocumentName", documentName);
|
|
|
|
|
|
|
|
// We update all new metadata now...
|
|
|
|
metaData.applyChanges();
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// set the file modification time of the temp file to that
|
|
|
|
// of the original file
|
|
|
|
struct stat st;
|
|
|
|
stat(in, &st);
|
|
|
|
|
|
|
|
struct utimbuf ut;
|
|
|
|
ut.modtime = st.st_mtime;
|
|
|
|
ut.actime = st.st_atime;
|
|
|
|
|
|
|
|
utime(out, &ut);
|
|
|
|
|
|
|
|
// now overwrite the original file
|
|
|
|
if (rename(out, in) == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// moving failed. unlink the temp file
|
|
|
|
unlink(out);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not a jpeg image.
|
|
|
|
DDebug() << "ExifRotate: not a JPEG file: " << file << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool jpegConvert(const TQString& src, const TQString& dest, const TQString& documentName, const TQString& format)
|
|
|
|
{
|
|
|
|
TQFileInfo fi(src);
|
|
|
|
if (!fi.exists())
|
|
|
|
{
|
|
|
|
DDebug() << "JpegConvert: file do not exist: " << src << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isJpegImage(src))
|
|
|
|
{
|
|
|
|
DImg image(src);
|
|
|
|
|
|
|
|
// Get image Exif/Iptc data.
|
|
|
|
DMetadata meta;
|
|
|
|
meta.setExif(image.getExif());
|
|
|
|
meta.setIptc(image.getIptc());
|
|
|
|
|
|
|
|
// Update Iptc preview.
|
|
|
|
TQImage preview = image.smoothScale(1280, 1024, TQSize::ScaleMin).copyTQImage();
|
|
|
|
|
|
|
|
// TODO: see B.K.O #130525. a JPEG segment is limited to 64K. If the IPTC byte array is
|
|
|
|
// bigger than 64K duing of image preview tag size, the target JPEG image will be
|
|
|
|
// broken. Note that IPTC image preview tag is limited to 256K!!!
|
|
|
|
// Temp. solution to disable IPTC preview record in JPEG file until a right solution
|
|
|
|
// will be found into Exiv2.
|
|
|
|
// Note : There is no limitation with TIFF and PNG about IPTC byte array size.
|
|
|
|
|
|
|
|
if (format.upper() != TQString("JPG") && format.upper() != TQString("JPEG") &&
|
|
|
|
format.upper() != TQString("JPE"))
|
|
|
|
meta.setImagePreview(preview);
|
|
|
|
|
|
|
|
// Update Exif thumbnail.
|
|
|
|
TQImage thumb = preview.smoothScale(160, 120, TQ_ScaleMin);
|
|
|
|
meta.setExifThumbnail(thumb);
|
|
|
|
|
|
|
|
// Update Exif Document Name tag (the orinal file name from camera for example).
|
|
|
|
meta.setExifTagString("Exif.Image.DocumentName", documentName);
|
|
|
|
|
|
|
|
// Store new Exif/Iptc data into image.
|
|
|
|
image.setExif(meta.getExif());
|
|
|
|
image.setIptc(meta.getIptc());
|
|
|
|
|
|
|
|
// And now save the image to a new file format.
|
|
|
|
|
|
|
|
if ( format.upper() == TQString("PNG") )
|
|
|
|
image.setAttribute("quality", 9);
|
|
|
|
|
|
|
|
if ( format.upper() == TQString("TIFF") || format.upper() == TQString("TIF") )
|
|
|
|
image.setAttribute("compress", true);
|
|
|
|
|
|
|
|
return (image.save(dest, format));
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isJpegImage(const TQString& file)
|
|
|
|
{
|
|
|
|
// Check if the file is an JPEG image
|
|
|
|
TQString format = TQString(TQImage::imageFormat(file)).upper();
|
|
|
|
DDebug() << "mimetype = " << format << endl;
|
|
|
|
if (format !="JPEG") return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // Namespace Digikam
|