if no b/a area enabled some speed up when opening images
This commit is contained in:
791
rtengine/imageio.cc
Normal file
791
rtengine/imageio.cc
Normal file
@@ -0,0 +1,791 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
||||
*
|
||||
* RawTherapee 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 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* RawTherapee 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <png.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <imageio.h>
|
||||
#include <tiff.h>
|
||||
#include <tiffio.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
extern "C" {
|
||||
#include "jdatasrc.c"
|
||||
#include <jpeglib.h>
|
||||
#include <iccjpeg.h>
|
||||
}
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <iptcpairs.h>
|
||||
#include <libiptcdata/iptc-jpeg.h>
|
||||
|
||||
Glib::ustring safe_locale_to_utf8 (const std::string& src);
|
||||
|
||||
using namespace rtengine;
|
||||
using namespace rtengine::procparams;
|
||||
|
||||
Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.","Error while reading header.","File reading error", "Image format not supported."};
|
||||
|
||||
void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const std::vector<ExifPair>& exif, const std::vector<IPTCPair>& iptcc) {
|
||||
|
||||
// store exif info
|
||||
exifChange.resize (exif.size());
|
||||
for (int i=0; i<exif.size(); i++) {
|
||||
exifChange[i].first = exif[i].field;
|
||||
exifChange[i].second = exif[i].value;
|
||||
}
|
||||
delete exifRoot;
|
||||
|
||||
exifRoot = NULL;
|
||||
if (eroot)
|
||||
exifRoot = ((rtexif::TagDirectory*)eroot)->clone (NULL);
|
||||
|
||||
if (iptc)
|
||||
iptc_data_free (iptc);
|
||||
iptc = NULL;
|
||||
|
||||
// build iptc structures for libiptcdata
|
||||
if (iptcc.size()==0)
|
||||
return;
|
||||
|
||||
iptc = iptc_data_new ();
|
||||
for (int i=0; i<iptcc.size(); i++) {
|
||||
if (iptcc[i].field == "Keywords" && iptcc[i].values.size()>0) {
|
||||
for (int j=0; j<iptcc[i].values.size(); j++) {
|
||||
IptcDataSet * ds = iptc_dataset_new ();
|
||||
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS);
|
||||
std::string loc = safe_locale_to_utf8(iptcc[i].values[j]);
|
||||
iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), MIN(64,loc.size()), IPTC_DONT_VALIDATE);
|
||||
iptc_data_add_dataset (iptc, ds);
|
||||
iptc_dataset_unref (ds);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (iptcc[i].field == "SupplementalCategories" && iptcc[i].values.size()>0) {
|
||||
for (int j=0; j<iptcc[i].values.size(); j++) {
|
||||
IptcDataSet * ds = iptc_dataset_new ();
|
||||
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY);
|
||||
std::string loc = safe_locale_to_utf8(iptcc[i].values[j]);
|
||||
iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), MIN(32,loc.size()), IPTC_DONT_VALIDATE);
|
||||
iptc_data_add_dataset (iptc, ds);
|
||||
iptc_dataset_unref (ds);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (int j=0; j<16; j++)
|
||||
if (iptcc[i].field == strTags[j].field && iptcc[i].values.size()>0) {
|
||||
IptcDataSet * ds = iptc_dataset_new ();
|
||||
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, strTags[j].tag);
|
||||
std::string loc = safe_locale_to_utf8(iptcc[i].values[0]);
|
||||
iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), MIN(strTags[j].size,loc.size()), IPTC_DONT_VALIDATE);
|
||||
iptc_data_add_dataset (iptc, ds);
|
||||
iptc_dataset_unref (ds);
|
||||
}
|
||||
}
|
||||
iptc_data_sort (iptc);
|
||||
}
|
||||
|
||||
void ImageIO::setOutputProfile (char* pdata, int plen) {
|
||||
|
||||
delete [] profileData;
|
||||
if (pdata) {
|
||||
profileData = new char [plen];
|
||||
memcpy (profileData, pdata, plen);
|
||||
}
|
||||
else
|
||||
profileData = NULL;
|
||||
profileLength = plen;
|
||||
}
|
||||
|
||||
ImageIO::~ImageIO () {
|
||||
|
||||
if (embProfile)
|
||||
cmsCloseProfile(embProfile);
|
||||
delete loadedProfileData;
|
||||
delete exifRoot;
|
||||
delete profileData;
|
||||
}
|
||||
|
||||
void png_read_data(png_struct_def *png_ptr, unsigned char *data, size_t length);
|
||||
void png_write_data(png_struct_def *png_ptr, unsigned char *data, size_t length);
|
||||
void png_flush(png_struct_def *png_ptr);
|
||||
|
||||
int ImageIO::loadPNG (Glib::ustring fname) {
|
||||
|
||||
FILE *file = g_fopen (fname.c_str(),"rb");
|
||||
if (!file)
|
||||
return IMIO_CANNOTREADFILE;
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Loading PNG file...");
|
||||
pl->setProgress (0.0);
|
||||
}
|
||||
|
||||
//reading PNG header
|
||||
unsigned char header[8];
|
||||
fread (header, 1, 8, file);
|
||||
if (png_sig_cmp (header, 0, 8)) {
|
||||
fclose(file);
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
//initializing main structures
|
||||
png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
||||
if (!png) {
|
||||
fclose (file);
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
png_infop info = png_create_info_struct (png);
|
||||
png_infop end_info = png_create_info_struct (png);
|
||||
if (!end_info || !info) {
|
||||
png_destroy_read_struct (&png, &info, &end_info);
|
||||
fclose (file);
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
|
||||
if (setjmp (png_jmpbuf(png))) {
|
||||
png_destroy_read_struct (&png, &info, &end_info);
|
||||
fclose (file);
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
|
||||
//set up png read
|
||||
png_set_read_fn (png, file, png_read_data);
|
||||
png_set_sig_bytes (png,8);
|
||||
|
||||
png_read_info(png,info);
|
||||
|
||||
embProfile = NULL;
|
||||
|
||||
//retrieving image information
|
||||
png_uint_32 width,height;
|
||||
int bit_depth,color_type,interlace_type,compression_type,filter_method;
|
||||
png_get_IHDR(png,info,&width,&height,&bit_depth,&color_type,&interlace_type,
|
||||
&compression_type, &filter_method);
|
||||
|
||||
//converting to 32bpp format
|
||||
if (color_type==PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
|
||||
|
||||
if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png);
|
||||
|
||||
if (png_get_valid(png,info,PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png);
|
||||
|
||||
if (interlace_type!=PNG_INTERLACE_NONE) {
|
||||
png_destroy_read_struct (&png, &info, &end_info);
|
||||
fclose (file);
|
||||
return IMIO_VARIANTNOTSUPPORTED;
|
||||
}
|
||||
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA)
|
||||
png_set_strip_alpha(png);
|
||||
|
||||
//setting gamma
|
||||
double gamma;
|
||||
if (png_get_gAMA(png,info,&gamma))
|
||||
png_set_gamma(png, 2.0, gamma);
|
||||
else
|
||||
png_set_gamma(png,2.0, 0.45455);
|
||||
|
||||
int bps = getBPS ();
|
||||
|
||||
|
||||
// if (bps==8 && bit_depth==16) png_set_strip_16(png);
|
||||
|
||||
//updating png info struct
|
||||
png_read_update_info(png,info);
|
||||
png_get_IHDR(png,info,&width,&height,&bit_depth,&color_type,&interlace_type,
|
||||
&compression_type, &filter_method);
|
||||
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA)
|
||||
png_set_strip_alpha(png);
|
||||
|
||||
png_read_update_info(png,info);
|
||||
png_get_IHDR(png,info,&width,&height,&bit_depth,&color_type,&interlace_type,
|
||||
&compression_type, &filter_method);
|
||||
|
||||
allocate (width, height);
|
||||
|
||||
int rowlen = width*3*bit_depth/8;
|
||||
unsigned char *row = new unsigned char [rowlen];
|
||||
|
||||
for (unsigned int i=0;i<height;i++) {
|
||||
|
||||
png_read_row (png, (png_byte*)row, NULL);
|
||||
if (bit_depth==16) { // convert scanline to host byte order
|
||||
unsigned short* srow = (unsigned short*)row;
|
||||
for (int j=0; j<width*3; j++)
|
||||
srow[j] = ntohs (srow[j]);
|
||||
}
|
||||
setScanline (i, row, bit_depth);
|
||||
|
||||
// if (bps==16 && bit_depth==8)
|
||||
// setScanline (i, row, 8);
|
||||
// else
|
||||
// setScanline (i, row, bps);
|
||||
|
||||
if (pl && !(i%100))
|
||||
pl->setProgress ((double)(i+1)/height);
|
||||
}
|
||||
|
||||
png_read_end (png, 0);
|
||||
png_destroy_read_struct (&png, &info, &end_info);
|
||||
|
||||
delete [] row;
|
||||
fclose(file);
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Ready.");
|
||||
pl->setProgress (1.0);
|
||||
}
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
|
||||
extern jmp_buf jpeg_jmp_buf;
|
||||
|
||||
int ImageIO::loadJPEG (Glib::ustring fname) {
|
||||
|
||||
FILE *file=g_fopen(fname.c_str(),"rb");
|
||||
if (!file)
|
||||
return IMIO_CANNOTREADFILE;
|
||||
|
||||
jpeg_decompress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
cinfo.err = my_jpeg_std_error(&jerr);
|
||||
jpeg_create_decompress(&cinfo);
|
||||
my_jpeg_stdio_src (&cinfo,file);
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Loading JPEG file...");
|
||||
pl->setProgress (0.0);
|
||||
|
||||
}
|
||||
|
||||
setup_read_icc_profile (&cinfo);
|
||||
|
||||
if (!setjmp(jpeg_jmp_buf)) {
|
||||
jpeg_stdio_src(&cinfo,file);
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
|
||||
unsigned int proflen;
|
||||
delete loadedProfileData;
|
||||
loadedProfileData = NULL;
|
||||
bool hasprofile = read_icc_profile (&cinfo, (JOCTET**)&loadedProfileData, (unsigned int*)&loadedProfileLength);
|
||||
if (hasprofile)
|
||||
embProfile = cmsOpenProfileFromMem (loadedProfileData, loadedProfileLength);
|
||||
else
|
||||
embProfile = NULL;
|
||||
|
||||
jpeg_start_decompress(&cinfo);
|
||||
|
||||
int width = cinfo.output_width;
|
||||
int height = cinfo.output_height;
|
||||
|
||||
allocate (width, height);
|
||||
|
||||
unsigned char *row=new unsigned char[width*3];
|
||||
while (cinfo.output_scanline < height) {
|
||||
if (jpeg_read_scanlines(&cinfo,&row,1) < 1) {
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
delete [] row;
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
setScanline (cinfo.output_scanline-1, row, 8);
|
||||
|
||||
if (pl && !(cinfo.output_scanline%100))
|
||||
pl->setProgress ((double)(cinfo.output_scanline)/cinfo.output_height);
|
||||
}
|
||||
delete [] row;
|
||||
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
fclose(file);
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Ready.");
|
||||
pl->setProgress (1.0);
|
||||
}
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
else {
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int ImageIO::loadTIFF (Glib::ustring fname) {
|
||||
|
||||
#ifdef WIN32
|
||||
wchar_t *wfilename = (wchar_t*)g_utf8_to_utf16 (fname.c_str(), -1, NULL, NULL, NULL);
|
||||
TIFF* in = TIFFOpenW (wfilename, "r");
|
||||
g_free (wfilename);
|
||||
#else
|
||||
TIFF* in = TIFFOpen(fname.c_str(), "r");
|
||||
#endif
|
||||
if (in == NULL)
|
||||
return IMIO_CANNOTREADFILE;
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Loading TIFF file...");
|
||||
pl->setProgress (0.0);
|
||||
}
|
||||
|
||||
int width, height;
|
||||
TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &width);
|
||||
TIFFGetField(in, TIFFTAG_IMAGELENGTH, &height);
|
||||
|
||||
uint16 bitspersample, samplesperpixel;
|
||||
TIFFGetField(in, TIFFTAG_BITSPERSAMPLE, &bitspersample);
|
||||
TIFFGetField(in, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
|
||||
uint16 photometric;
|
||||
if (!TIFFGetField(in, TIFFTAG_PHOTOMETRIC, &photometric) ||
|
||||
photometric != PHOTOMETRIC_RGB || samplesperpixel < 3) {
|
||||
TIFFClose(in);
|
||||
return IMIO_VARIANTNOTSUPPORTED;
|
||||
}
|
||||
|
||||
uint16 config;
|
||||
TIFFGetField(in, TIFFTAG_PLANARCONFIG, &config);
|
||||
if (config != PLANARCONFIG_CONTIG) {
|
||||
TIFFClose(in);
|
||||
return IMIO_VARIANTNOTSUPPORTED;
|
||||
}
|
||||
|
||||
char* profdata;
|
||||
delete loadedProfileData;
|
||||
loadedProfileData = NULL;
|
||||
if (TIFFGetField(in, TIFFTAG_ICCPROFILE, &loadedProfileLength, &profdata)) {
|
||||
embProfile = cmsOpenProfileFromMem (profdata, loadedProfileLength);
|
||||
loadedProfileData = new char [loadedProfileLength];
|
||||
memcpy (loadedProfileData, profdata, loadedProfileLength);
|
||||
}
|
||||
else
|
||||
embProfile = NULL;
|
||||
|
||||
|
||||
allocate (width, height);
|
||||
|
||||
unsigned char* linebuffer = new unsigned char[TIFFScanlineSize(in)];
|
||||
for (int row = 0; row < height; row++) {
|
||||
if (TIFFReadScanline(in, linebuffer, row, 0) <0) {
|
||||
TIFFClose(in);
|
||||
delete [] linebuffer;
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
if (samplesperpixel>3)
|
||||
for (int i=0; i<width; i++)
|
||||
memcpy (linebuffer+i*3*bitspersample/8, linebuffer+i*samplesperpixel*bitspersample/8, 3*bitspersample/8);
|
||||
setScanline (row, linebuffer, bitspersample);
|
||||
|
||||
if (pl && !(row%100))
|
||||
pl->setProgress ((double)(row+1)/height);
|
||||
}
|
||||
TIFFClose(in);
|
||||
delete [] linebuffer;
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Ready.");
|
||||
pl->setProgress (1.0);
|
||||
}
|
||||
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int ImageIO::savePNG (Glib::ustring fname, int compression, int bps) {
|
||||
|
||||
FILE* file=g_fopen(fname.c_str (),"wb");
|
||||
|
||||
if (!file)
|
||||
return IMIO_CANNOTREADFILE;
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Saving PNG file...");
|
||||
pl->setProgress (0.0);
|
||||
}
|
||||
|
||||
png_structp png = png_create_write_struct (PNG_LIBPNG_VER_STRING,0,0,0);
|
||||
if (!png) {
|
||||
fclose (file);
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
png_infop info = png_create_info_struct(png);
|
||||
if (!info) {
|
||||
png_destroy_write_struct (&png,0);
|
||||
fclose (file);
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png))) {
|
||||
png_destroy_write_struct (&png,&info);
|
||||
fclose(file);
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
|
||||
png_set_write_fn (png, file, png_write_data, png_flush);
|
||||
|
||||
png_set_compression_level(png,compression);
|
||||
|
||||
int width = getW ();
|
||||
int height = getH ();
|
||||
if (bps<0)
|
||||
bps = getBPS ();
|
||||
|
||||
png_set_IHDR(png, info, width, height, bps, PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_BASE);
|
||||
|
||||
|
||||
int rowlen = width*3*bps/8;
|
||||
unsigned char *row = new unsigned char [rowlen];
|
||||
|
||||
png_write_info(png,info);
|
||||
for (unsigned int i=0;i<height;i++) {
|
||||
getScanline (i, row, bps);
|
||||
if (bps==16) {
|
||||
// convert to network byte order
|
||||
for (int j=0; j<width*6; j+=2) {
|
||||
unsigned char tmp = row[j];
|
||||
row[j] = row[j+1];
|
||||
row[j+1] = tmp;
|
||||
}
|
||||
}
|
||||
png_write_row (png, (png_byte*)row);
|
||||
|
||||
if (pl && !(i%100))
|
||||
pl->setProgress ((double)(i+1)/height);
|
||||
}
|
||||
|
||||
png_write_end(png,info);
|
||||
png_destroy_write_struct(&png,&info);
|
||||
|
||||
delete [] row;
|
||||
fclose (file);
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Ready.");
|
||||
pl->setProgress (1.0);
|
||||
}
|
||||
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
int ImageIO::saveJPEG (Glib::ustring fname, int quality) {
|
||||
|
||||
jpeg_compress_struct cinfo;
|
||||
jpeg_error_mgr jerr;
|
||||
|
||||
cinfo.err = jpeg_std_error (&jerr);
|
||||
jpeg_create_compress (&cinfo);
|
||||
|
||||
FILE *file = g_fopen (fname.c_str (), "wb");
|
||||
|
||||
if (!file)
|
||||
return IMIO_CANNOTREADFILE;
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Saving JPEG file...");
|
||||
pl->setProgress (0.0);
|
||||
}
|
||||
|
||||
jpeg_stdio_dest (&cinfo, file);
|
||||
|
||||
int width = getW ();
|
||||
int height = getH ();
|
||||
|
||||
cinfo.image_width = width;
|
||||
cinfo.image_height = height;
|
||||
cinfo.in_color_space = JCS_RGB;
|
||||
cinfo.input_components = 3;
|
||||
jpeg_set_defaults (&cinfo);
|
||||
cinfo.write_JFIF_header = FALSE;
|
||||
|
||||
|
||||
if (quality>=0 && quality<=100)
|
||||
jpeg_set_quality (&cinfo, quality, true);
|
||||
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
|
||||
// buffer for exif and iptc markers
|
||||
unsigned char buffer[165535];
|
||||
unsigned int size;
|
||||
// assemble and write exif marker
|
||||
if (exifRoot) {
|
||||
int size = rtexif::ExifManager::createJPEGMarker (exifRoot, exifChange, cinfo.image_width, cinfo.image_height, buffer);
|
||||
if (size>0 && size<65530)
|
||||
jpeg_write_marker(&cinfo, JPEG_APP0+1, buffer, size);
|
||||
}
|
||||
// assemble and write iptc marker
|
||||
if (iptc) {
|
||||
unsigned char* iptcdata;
|
||||
bool error = false;
|
||||
if (iptc_data_save (iptc, &iptcdata, &size)) {
|
||||
if (iptcdata)
|
||||
iptc_data_free_buf (iptc, iptcdata);
|
||||
error = true;
|
||||
}
|
||||
int bytes = 0;
|
||||
if (!error && (bytes = iptc_jpeg_ps3_save_iptc (NULL, 0, iptcdata, size, buffer, 65532)) < 0) {
|
||||
if (iptcdata)
|
||||
iptc_data_free_buf (iptc, iptcdata);
|
||||
error = true;
|
||||
}
|
||||
if (!error)
|
||||
jpeg_write_marker(&cinfo, JPEG_APP0+13, buffer, bytes);
|
||||
}
|
||||
// write icc profile to the output
|
||||
if (profileData)
|
||||
write_icc_profile (&cinfo, (JOCTET*)profileData, profileLength);
|
||||
|
||||
// write image data
|
||||
int rowlen = width*3;
|
||||
unsigned char *row = new unsigned char [rowlen];
|
||||
|
||||
while (cinfo.next_scanline < cinfo.image_height) {
|
||||
|
||||
getScanline (cinfo.next_scanline, row, 8);
|
||||
|
||||
if (jpeg_write_scanlines (&cinfo, &row, 1) < 1) {
|
||||
jpeg_finish_compress (&cinfo);
|
||||
jpeg_destroy_compress (&cinfo);
|
||||
fclose (file);
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
|
||||
if (pl && !(cinfo.next_scanline%100))
|
||||
pl->setProgress ((double)(cinfo.next_scanline)/cinfo.image_height);
|
||||
}
|
||||
|
||||
jpeg_finish_compress (&cinfo);
|
||||
jpeg_destroy_compress (&cinfo);
|
||||
|
||||
delete [] row;
|
||||
fclose (file);
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Ready.");
|
||||
pl->setProgress (1.0);
|
||||
}
|
||||
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
|
||||
int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) {
|
||||
|
||||
int width = getW ();
|
||||
int height = getH ();
|
||||
|
||||
if (bps<0)
|
||||
bps = getBPS ();
|
||||
|
||||
int lineWidth = width*3*bps/8;
|
||||
unsigned char* linebuffer = new unsigned char[lineWidth];
|
||||
// TODO the following needs to be looked into - do we really need two ways to write a Tiff file ?
|
||||
if (exifRoot && uncompressed) {
|
||||
FILE *file = g_fopen (fname.c_str (), "wb");
|
||||
|
||||
if (!file)
|
||||
return IMIO_CANNOTREADFILE;
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Saving TIFF file ...");
|
||||
pl->setProgress (0.0);
|
||||
}
|
||||
|
||||
// buffer for the exif and iptc
|
||||
unsigned char buffer[165535];
|
||||
unsigned char* iptcdata = NULL;
|
||||
unsigned int iptclen = 0;
|
||||
if (iptc && iptc_data_save (iptc, &iptcdata, &iptclen) && iptcdata) {
|
||||
iptc_data_free_buf (iptc, iptcdata);
|
||||
iptcdata = NULL;
|
||||
}
|
||||
int size = rtexif::ExifManager::createTIFFHeader (exifRoot, exifChange, width, height, bps, profileData, profileLength, (char*)iptcdata, iptclen, buffer);
|
||||
if (iptcdata)
|
||||
iptc_data_free_buf (iptc, iptcdata);
|
||||
if (size>0 && size<165530)
|
||||
fwrite (buffer, size, 1, file);
|
||||
|
||||
bool needsReverse = bps==16 && exifRoot->getOrder()==rtexif::MOTOROLA;
|
||||
|
||||
for (int i=0; i<height; i++) {
|
||||
getScanline (i, linebuffer, bps);
|
||||
if (needsReverse)
|
||||
for (int i=0; i<lineWidth; i+=2) {
|
||||
char c = linebuffer[i];
|
||||
linebuffer[i] = linebuffer[i+1];
|
||||
linebuffer[i+1] = c;
|
||||
}
|
||||
fwrite (linebuffer, lineWidth, 1, file);
|
||||
if (pl && !(i%100))
|
||||
pl->setProgress ((double)(i+1)/height);
|
||||
}
|
||||
|
||||
fclose (file);
|
||||
}
|
||||
else {
|
||||
// little hack to get libTiff to use proper byte order (see TIFFClienOpen()):
|
||||
const char *mode = !exifRoot ? "w" : (exifRoot->getOrder()==rtexif::INTEL ? "wl":"wb");
|
||||
#ifdef WIN32
|
||||
wchar_t *wfilename = (wchar_t*)g_utf8_to_utf16 (fname.c_str(), -1, NULL, NULL, NULL);
|
||||
TIFF* out = TIFFOpenW (wfilename, mode);
|
||||
g_free (wfilename);
|
||||
#else
|
||||
TIFF* out = TIFFOpen(fname.c_str(), mode);
|
||||
#endif
|
||||
if (!out)
|
||||
return IMIO_CANNOTREADFILE;
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Saving TIFF file ...");
|
||||
pl->setProgress (0.0);
|
||||
}
|
||||
|
||||
if (exifRoot){
|
||||
rtexif::Tag *tag = exifRoot->getTag (TIFFTAG_EXIFIFD);
|
||||
if (tag && tag->isDirectory()){
|
||||
rtexif::TagDirectory *exif = tag->getDirectory();
|
||||
if (exif) {
|
||||
int exif_size = exif->calculateSize();
|
||||
unsigned char *buffer = new unsigned char[exif_size+8];
|
||||
// TIFFOpen writes out the header and sets file pointer at position 8
|
||||
|
||||
exif->write (8, buffer);
|
||||
write (TIFFFileno (out), buffer+8, exif_size);
|
||||
delete buffer;
|
||||
// let libtiff know that scanlines or any other following stuff should go
|
||||
// at a different offset:
|
||||
TIFFSetWriteOffset (out, exif_size+8);
|
||||
TIFFSetField (out, TIFFTAG_EXIFIFD, 8);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO Even though we are saving EXIF IFD - MakerNote still comes out screwy.
|
||||
|
||||
if ((tag = exifRoot->getTag (TIFFTAG_MODEL)) != NULL)
|
||||
TIFFSetField (out, TIFFTAG_MODEL, tag->getValue());
|
||||
if ((tag = exifRoot->getTag (TIFFTAG_MAKE)) != NULL)
|
||||
TIFFSetField (out, TIFFTAG_MAKE, tag->getValue());
|
||||
if ((tag = exifRoot->getTag (TIFFTAG_DATETIME)) != NULL)
|
||||
TIFFSetField (out, TIFFTAG_DATETIME, tag->getValue());
|
||||
if ((tag = exifRoot->getTag (TIFFTAG_ARTIST)) != NULL)
|
||||
TIFFSetField (out, TIFFTAG_ARTIST, tag->getValue());
|
||||
if ((tag = exifRoot->getTag (TIFFTAG_COPYRIGHT)) != NULL)
|
||||
TIFFSetField (out, TIFFTAG_COPYRIGHT, tag->getValue());
|
||||
|
||||
}
|
||||
|
||||
TIFFSetField (out, TIFFTAG_SOFTWARE, "RawTherapee 3");
|
||||
TIFFSetField (out, TIFFTAG_IMAGEWIDTH, width);
|
||||
TIFFSetField (out, TIFFTAG_IMAGELENGTH, height);
|
||||
TIFFSetField (out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||
TIFFSetField (out, TIFFTAG_SAMPLESPERPIXEL, 3);
|
||||
TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, height);
|
||||
TIFFSetField (out, TIFFTAG_BITSPERSAMPLE, bps);
|
||||
TIFFSetField (out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
|
||||
TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
|
||||
TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_DEFLATE);
|
||||
if (!uncompressed)
|
||||
TIFFSetField (out, TIFFTAG_PREDICTOR, PREDICTOR_NONE);
|
||||
|
||||
if (profileData)
|
||||
TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData);
|
||||
|
||||
for (int row = 0; row < height; row++) {
|
||||
getScanline (row, linebuffer, bps);
|
||||
|
||||
if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) {
|
||||
TIFFClose (out);
|
||||
delete [] linebuffer;
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
if (pl && !(row%100))
|
||||
pl->setProgress ((double)(row+1)/height);
|
||||
}
|
||||
TIFFClose (out);
|
||||
}
|
||||
|
||||
delete [] linebuffer;
|
||||
if (pl) {
|
||||
pl->setProgressStr ("Ready.");
|
||||
pl->setProgress (1.0);
|
||||
}
|
||||
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
|
||||
// PNG read and write routines:
|
||||
|
||||
void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
png_size_t check;
|
||||
|
||||
/* fread() returns 0 on error, so it is OK to store this in a png_size_t
|
||||
* instead of an int, which is what fread() actually returns.
|
||||
*/
|
||||
check = (png_size_t)fread(data, (png_size_t)1, length, (FILE *)png_ptr->io_ptr);
|
||||
|
||||
if (check != length)
|
||||
{
|
||||
png_error(png_ptr, "Read Error");
|
||||
}
|
||||
}
|
||||
|
||||
void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
png_uint_32 check;
|
||||
|
||||
check = fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr));
|
||||
if (check != length)
|
||||
{
|
||||
png_error(png_ptr, "Write Error");
|
||||
}
|
||||
}
|
||||
|
||||
void png_flush(png_structp png_ptr) {
|
||||
FILE *io_ptr;
|
||||
io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr));
|
||||
if (io_ptr != NULL)
|
||||
fflush(io_ptr);
|
||||
}
|
||||
|
||||
int ImageIO::load (Glib::ustring fname) {
|
||||
|
||||
int lastdot = fname.find_last_of ('.');
|
||||
|
||||
if (!fname.casefold().compare (lastdot, 4, ".png"))
|
||||
return loadPNG (fname);
|
||||
else if (!fname.casefold().compare (lastdot, 4, ".jpg"))
|
||||
return loadJPEG (fname);
|
||||
else if (!fname.casefold().compare (lastdot, 4, ".tif"))
|
||||
return loadTIFF (fname);
|
||||
else return IMIO_FILETYPENOTSUPPORTED;
|
||||
}
|
||||
|
||||
int ImageIO::save (Glib::ustring fname) {
|
||||
|
||||
int lastdot = fname.find_last_of ('.');
|
||||
|
||||
if (!fname.casefold().compare (lastdot, 4, ".png"))
|
||||
return savePNG (fname);
|
||||
else if (!fname.casefold().compare (lastdot, 4, ".jpg"))
|
||||
return saveJPEG (fname);
|
||||
else if (!fname.casefold().compare (lastdot, 4, ".tif"))
|
||||
return saveTIFF (fname);
|
||||
else return IMIO_FILETYPENOTSUPPORTED;
|
||||
}
|
||||
|
Reference in New Issue
Block a user