Sets UTF-8 as default charset for IPTC + suppress one of the 2 methods
for saving TIFF images.
This commit is contained in:
parent
157da7f9ba
commit
96863bb956
@ -75,15 +75,6 @@ FILE* g_fopen_withBinaryAndLock(const Glib::ustring& fname)
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Glib::ustring to_utf8 (const std::string& str)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return Glib::locale_to_utf8 (str);
|
|
||||||
} catch (Glib::Error&) {
|
|
||||||
return Glib::convert_with_fallback (str, "UTF-8", "ISO-8859-1", "?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.", "Error while reading header.", "File reading error", "Image format not supported."};
|
Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.", "Error while reading header.", "File reading error", "Image format not supported."};
|
||||||
@ -136,13 +127,19 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr
|
|||||||
|
|
||||||
iptc = iptc_data_new ();
|
iptc = iptc_data_new ();
|
||||||
|
|
||||||
|
const unsigned char utf8Esc[] = {0x1B, '%', 'G'};
|
||||||
|
IptcDataSet * ds = iptc_dataset_new ();
|
||||||
|
iptc_dataset_set_tag (ds, IPTC_RECORD_OBJECT_ENV, IPTC_TAG_CHARACTER_SET);
|
||||||
|
iptc_dataset_set_data (ds, utf8Esc, 3, IPTC_DONT_VALIDATE);
|
||||||
|
iptc_data_add_dataset (iptc, ds);
|
||||||
|
iptc_dataset_unref (ds);
|
||||||
|
|
||||||
for (rtengine::procparams::IPTCPairs::const_iterator i = iptcc.begin(); i != iptcc.end(); ++i) {
|
for (rtengine::procparams::IPTCPairs::const_iterator i = iptcc.begin(); i != iptcc.end(); ++i) {
|
||||||
if (i->first == "Keywords" && !(i->second.empty())) {
|
if (i->first == "Keywords" && !(i->second.empty())) {
|
||||||
for (unsigned int j = 0; j < i->second.size(); j++) {
|
for (unsigned int j = 0; j < i->second.size(); j++) {
|
||||||
IptcDataSet * ds = iptc_dataset_new ();
|
IptcDataSet * ds = iptc_dataset_new ();
|
||||||
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS);
|
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS);
|
||||||
std::string loc = to_utf8(i->second.at(j));
|
iptc_dataset_set_data (ds, (unsigned char*)i->second.at(j).c_str(), min(static_cast<size_t>(64), i->second.at(j).bytes()), IPTC_DONT_VALIDATE);
|
||||||
iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(static_cast<size_t>(64), loc.size()), IPTC_DONT_VALIDATE);
|
|
||||||
iptc_data_add_dataset (iptc, ds);
|
iptc_data_add_dataset (iptc, ds);
|
||||||
iptc_dataset_unref (ds);
|
iptc_dataset_unref (ds);
|
||||||
}
|
}
|
||||||
@ -152,8 +149,7 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr
|
|||||||
for (unsigned int j = 0; j < i->second.size(); j++) {
|
for (unsigned int j = 0; j < i->second.size(); j++) {
|
||||||
IptcDataSet * ds = iptc_dataset_new ();
|
IptcDataSet * ds = iptc_dataset_new ();
|
||||||
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY);
|
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY);
|
||||||
std::string loc = to_utf8(i->second.at(j));
|
iptc_dataset_set_data (ds, (unsigned char*)i->second.at(j).c_str(), min(static_cast<size_t>(32), i->second.at(j).bytes()), IPTC_DONT_VALIDATE);
|
||||||
iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(static_cast<size_t>(32), loc.size()), IPTC_DONT_VALIDATE);
|
|
||||||
iptc_data_add_dataset (iptc, ds);
|
iptc_data_add_dataset (iptc, ds);
|
||||||
iptc_dataset_unref (ds);
|
iptc_dataset_unref (ds);
|
||||||
}
|
}
|
||||||
@ -165,8 +161,7 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr
|
|||||||
if (i->first == strTags[j].field && !(i->second.empty())) {
|
if (i->first == strTags[j].field && !(i->second.empty())) {
|
||||||
IptcDataSet * ds = iptc_dataset_new ();
|
IptcDataSet * ds = iptc_dataset_new ();
|
||||||
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, strTags[j].tag);
|
iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, strTags[j].tag);
|
||||||
std::string loc = to_utf8(i->second.at(0));
|
iptc_dataset_set_data (ds, (unsigned char*)i->second.at(0).c_str(), min(strTags[j].size, i->second.at(0).bytes()), IPTC_DONT_VALIDATE);
|
||||||
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_data_add_dataset (iptc, ds);
|
||||||
iptc_dataset_unref (ds);
|
iptc_dataset_unref (ds);
|
||||||
}
|
}
|
||||||
@ -1121,13 +1116,13 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp)
|
|||||||
int bytes = 0;
|
int bytes = 0;
|
||||||
|
|
||||||
if (!error && (bytes = iptc_jpeg_ps3_save_iptc (nullptr, 0, iptcdata, size, buffer, 65532)) < 0) {
|
if (!error && (bytes = iptc_jpeg_ps3_save_iptc (nullptr, 0, iptcdata, size, buffer, 65532)) < 0) {
|
||||||
if (iptcdata) {
|
|
||||||
iptc_data_free_buf (iptc, iptcdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iptcdata) {
|
||||||
|
iptc_data_free_buf (iptc, iptcdata);
|
||||||
|
}
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
jpeg_write_marker(&cinfo, JPEG_APP0 + 13, buffer, bytes);
|
jpeg_write_marker(&cinfo, JPEG_APP0 + 13, buffer, bytes);
|
||||||
}
|
}
|
||||||
@ -1212,219 +1207,198 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed)
|
|||||||
int lineWidth = width * 3 * bps / 8;
|
int lineWidth = width * 3 * bps / 8;
|
||||||
unsigned char* linebuffer = new unsigned char[lineWidth];
|
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 ?
|
// little hack to get libTiff to use proper byte order (see TIFFClienOpen()):
|
||||||
if (exifRoot && uncompressed) {
|
const char *mode = !exifRoot ? "w" : (exifRoot->getOrder() == rtexif::INTEL ? "wl" : "wb");
|
||||||
FILE *file = g_fopen_withBinaryAndLock (fname);
|
#ifdef WIN32
|
||||||
|
FILE *file = g_fopen_withBinaryAndLock (fname);
|
||||||
|
int fileno = _fileno(file);
|
||||||
|
int osfileno = _get_osfhandle(fileno);
|
||||||
|
TIFF* out = TIFFFdOpen (osfileno, fname.c_str(), mode);
|
||||||
|
#else
|
||||||
|
TIFF* out = TIFFOpen(fname.c_str(), mode);
|
||||||
|
int fileno = TIFFFileno (out);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!file) {
|
if (!out) {
|
||||||
delete [] linebuffer;
|
delete [] linebuffer;
|
||||||
return IMIO_CANNOTWRITEFILE;
|
return IMIO_CANNOTWRITEFILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl) {
|
||||||
|
pl->setProgressStr ("PROGRESSBAR_SAVETIFF");
|
||||||
|
pl->setProgress (0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exifRoot) {
|
||||||
|
rtexif::TagDirectory* cl = (const_cast<rtexif::TagDirectory*> (exifRoot))->clone (nullptr);
|
||||||
|
|
||||||
|
// ------------------ remove some unknown top level tags which produce warnings when opening a tiff (might be useless) -----------------
|
||||||
|
|
||||||
|
rtexif::Tag *removeTag = cl->getTag (0x9003);
|
||||||
|
|
||||||
|
if (removeTag) {
|
||||||
|
removeTag->setKeep (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pl) {
|
removeTag = cl->getTag (0x9211);
|
||||||
pl->setProgressStr ("PROGRESSBAR_SAVETIFF");
|
|
||||||
pl->setProgress (0.0);
|
if (removeTag) {
|
||||||
|
removeTag->setKeep (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// buffer for the exif and iptc
|
// ------------------ Apply list of change -----------------
|
||||||
unsigned int bufferSize;
|
|
||||||
unsigned char* buffer = nullptr; // buffer will be allocated in createTIFFHeader
|
|
||||||
unsigned char* iptcdata = nullptr;
|
|
||||||
unsigned int iptclen = 0;
|
|
||||||
|
|
||||||
if (iptc && iptc_data_save (iptc, &iptcdata, &iptclen) && iptcdata) {
|
for (auto currExifChange : exifChange) {
|
||||||
|
cl->applyChange (currExifChange.first, currExifChange.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
rtexif::Tag *tag = cl->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 (fileno, 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 screwed.
|
||||||
|
|
||||||
|
if ((tag = cl->getTag (TIFFTAG_MODEL)) != nullptr) {
|
||||||
|
TIFFSetField (out, TIFFTAG_MODEL, tag->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tag = cl->getTag (TIFFTAG_MAKE)) != nullptr) {
|
||||||
|
TIFFSetField (out, TIFFTAG_MAKE, tag->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tag = cl->getTag (TIFFTAG_DATETIME)) != nullptr) {
|
||||||
|
TIFFSetField (out, TIFFTAG_DATETIME, tag->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tag = cl->getTag (TIFFTAG_ARTIST)) != nullptr) {
|
||||||
|
TIFFSetField (out, TIFFTAG_ARTIST, tag->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((tag = cl->getTag (TIFFTAG_COPYRIGHT)) != nullptr) {
|
||||||
|
TIFFSetField (out, TIFFTAG_COPYRIGHT, tag->getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
delete cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char* iptcdata = nullptr;
|
||||||
|
unsigned int iptclen = 0;
|
||||||
|
|
||||||
|
if (iptc && iptc_data_save (iptc, &iptcdata, &iptclen)) {
|
||||||
|
if (iptcdata) {
|
||||||
iptc_data_free_buf (iptc, iptcdata);
|
iptc_data_free_buf (iptc, iptcdata);
|
||||||
iptcdata = nullptr;
|
iptcdata = nullptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int size = rtexif::ExifManager::createTIFFHeader (exifRoot, exifChange, width, height, bps, profileData, profileLength, (char*)iptcdata, iptclen, buffer, bufferSize);
|
if (iptcdata) {
|
||||||
|
rtexif::Tag* iptcTag = new rtexif::Tag (nullptr, rtexif::lookupAttrib (rtexif::ifdAttribs, "IPTCData"));
|
||||||
if (iptcdata) {
|
iptcTag->initLongArray((char*)iptcdata, iptclen);
|
||||||
iptc_data_free_buf (iptc, iptcdata);
|
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
||||||
|
bool needsReverse = exifRoot && exifRoot->getOrder() == rtexif::MOTOROLA;
|
||||||
|
#else
|
||||||
|
bool needsReverse = exifRoot && exifRoot->getOrder() == rtexif::INTEL;
|
||||||
|
#endif
|
||||||
|
if (needsReverse) {
|
||||||
|
unsigned char *ptr = iptcTag->getValue();
|
||||||
|
for (int a = 0; a < iptcTag->getCount(); ++a) {
|
||||||
|
unsigned char cc;
|
||||||
|
cc = ptr[3];
|
||||||
|
ptr[3] = ptr[0];
|
||||||
|
ptr[0] = cc;
|
||||||
|
cc = ptr[2];
|
||||||
|
ptr[2] = ptr[1];
|
||||||
|
ptr[1] = cc;
|
||||||
|
ptr += 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
TIFFSetField (out, TIFFTAG_RICHTIFFIPTC, iptcTag->getCount(), (long*)iptcTag->getValue());
|
||||||
|
iptc_data_free_buf (iptc, iptcdata);
|
||||||
|
}
|
||||||
|
|
||||||
// The maximum lenght is strangely not the same than for the JPEG file...
|
TIFFSetField (out, TIFFTAG_SOFTWARE, "RawTherapee " RTVERSION);
|
||||||
// Which maximum length is the good one ?
|
TIFFSetField (out, TIFFTAG_IMAGEWIDTH, width);
|
||||||
if (size > 0 && size <= static_cast<int>(bufferSize)) {
|
TIFFSetField (out, TIFFTAG_IMAGELENGTH, height);
|
||||||
fwrite (buffer, size, 1, file);
|
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);
|
||||||
|
TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT);
|
||||||
|
|
||||||
|
if (!uncompressed) {
|
||||||
|
TIFFSetField (out, TIFFTAG_PREDICTOR, bps == 32 ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profileData) {
|
||||||
|
TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData);
|
||||||
|
}
|
||||||
|
|
||||||
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
||||||
bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA;
|
bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA;
|
||||||
#else
|
#else
|
||||||
bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL;
|
bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for (int i = 0; i < height; i++) {
|
for (int row = 0; row < height; row++) {
|
||||||
getScanline (i, linebuffer, bps);
|
getScanline (row, linebuffer, bps);
|
||||||
|
|
||||||
if (needsReverse) {
|
if (needsReverse) {
|
||||||
if (bps == 16) {
|
if (bps == 16) {
|
||||||
for (int i = 0; i < lineWidth; i += 2) {
|
for (int i = 0; i < lineWidth; i += 2) {
|
||||||
char c = linebuffer[i];
|
char c = linebuffer[i];
|
||||||
linebuffer[i] = linebuffer[i + 1];
|
linebuffer[i] = linebuffer[i + 1];
|
||||||
linebuffer[i + 1] = c;
|
linebuffer[i + 1] = c;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < lineWidth; i += 4) {
|
for (int i = 0; i < lineWidth; i += 4) {
|
||||||
std::swap(linebuffer[i], linebuffer[i+3]);
|
std::swap(linebuffer[i], linebuffer[i+3]);
|
||||||
std::swap(linebuffer[i+1], linebuffer[i+2]);
|
std::swap(linebuffer[i+1], linebuffer[i+2]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite (linebuffer, lineWidth, 1, file);
|
|
||||||
|
|
||||||
if (pl && !(i % 100)) {
|
|
||||||
pl->setProgress ((double)(i + 1) / height);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(buffer) {
|
if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) {
|
||||||
delete [] buffer;
|
TIFFClose (out);
|
||||||
}
|
|
||||||
|
|
||||||
if (ferror(file)) {
|
|
||||||
writeOk = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
FILE *file = g_fopen_withBinaryAndLock (fname);
|
|
||||||
int fileno = _fileno(file);
|
|
||||||
int osfileno = _get_osfhandle(fileno);
|
|
||||||
TIFF* out = TIFFFdOpen (osfileno, fname.c_str(), mode);
|
|
||||||
#else
|
|
||||||
TIFF* out = TIFFOpen(fname.c_str(), mode);
|
|
||||||
int fileno = TIFFFileno (out);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!out) {
|
|
||||||
delete [] linebuffer;
|
delete [] linebuffer;
|
||||||
return IMIO_CANNOTWRITEFILE;
|
return IMIO_CANNOTWRITEFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pl) {
|
if (pl && !(row % 100)) {
|
||||||
pl->setProgressStr ("PROGRESSBAR_SAVETIFF");
|
pl->setProgress ((double)(row + 1) / height);
|
||||||
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 (fileno, 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 screwed.
|
|
||||||
|
|
||||||
if ((tag = exifRoot->getTag (TIFFTAG_MODEL)) != nullptr) {
|
|
||||||
TIFFSetField (out, TIFFTAG_MODEL, tag->getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tag = exifRoot->getTag (TIFFTAG_MAKE)) != nullptr) {
|
|
||||||
TIFFSetField (out, TIFFTAG_MAKE, tag->getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tag = exifRoot->getTag (TIFFTAG_DATETIME)) != nullptr) {
|
|
||||||
TIFFSetField (out, TIFFTAG_DATETIME, tag->getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tag = exifRoot->getTag (TIFFTAG_ARTIST)) != nullptr) {
|
|
||||||
TIFFSetField (out, TIFFTAG_ARTIST, tag->getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tag = exifRoot->getTag (TIFFTAG_COPYRIGHT)) != nullptr) {
|
|
||||||
TIFFSetField (out, TIFFTAG_COPYRIGHT, tag->getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TIFFSetField (out, TIFFTAG_SOFTWARE, "RawTherapee " RTVERSION);
|
|
||||||
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);
|
|
||||||
TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT);
|
|
||||||
|
|
||||||
if (!uncompressed) {
|
|
||||||
TIFFSetField (out, TIFFTAG_PREDICTOR, bps == 32 ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profileData) {
|
|
||||||
TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
|
||||||
bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA;
|
|
||||||
#else
|
|
||||||
bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int row = 0; row < height; row++) {
|
|
||||||
getScanline (row, linebuffer, bps);
|
|
||||||
|
|
||||||
if (needsReverse) {
|
|
||||||
if (bps == 16) {
|
|
||||||
for (int i = 0; i < lineWidth; i += 2) {
|
|
||||||
char c = linebuffer[i];
|
|
||||||
linebuffer[i] = linebuffer[i + 1];
|
|
||||||
linebuffer[i + 1] = c;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < lineWidth; i += 4) {
|
|
||||||
std::swap(linebuffer[i], linebuffer[i+3]);
|
|
||||||
std::swap(linebuffer[i+1], linebuffer[i+2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) {
|
|
||||||
TIFFClose (out);
|
|
||||||
delete [] linebuffer;
|
|
||||||
return IMIO_CANNOTWRITEFILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pl && !(row % 100)) {
|
|
||||||
pl->setProgress ((double)(row + 1) / height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TIFFFlush(out) != 1) {
|
|
||||||
writeOk = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
TIFFClose (out);
|
|
||||||
#ifdef WIN32
|
|
||||||
fclose (file);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (TIFFFlush(out) != 1) {
|
||||||
|
writeOk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TIFFClose (out);
|
||||||
|
#ifdef WIN32
|
||||||
|
fclose (file);
|
||||||
|
#endif
|
||||||
|
|
||||||
delete [] linebuffer;
|
delete [] linebuffer;
|
||||||
|
|
||||||
if (pl) {
|
if (pl) {
|
||||||
|
126
rtexif/rtexif.cc
126
rtexif/rtexif.cc
@ -739,11 +739,12 @@ void TagDirectory::applyChange (std::string name, Glib::ustring value)
|
|||||||
} else {
|
} else {
|
||||||
const TagAttrib* attrib = nullptr;
|
const TagAttrib* attrib = nullptr;
|
||||||
|
|
||||||
for (int i = 0; attribs[i].ignore != -1; i++)
|
for (int i = 0; attribs[i].ignore != -1; i++) {
|
||||||
if (!strcmp (attribs[i].name, fseg.c_str())) {
|
if (!strcmp (attribs[i].name, fseg.c_str())) {
|
||||||
attrib = &attribs[i];
|
attrib = &attribs[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (attrib) {
|
if (attrib) {
|
||||||
Tag* nt = new Tag (this, attrib);
|
Tag* nt = new Tag (this, attrib);
|
||||||
@ -1663,15 +1664,11 @@ void Tag::toString (char* buffer, int ofs)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t maxcount = 4;
|
size_t maxcount = rtengine::min<size_t>(count, 10);
|
||||||
|
|
||||||
if (count < 4) {
|
|
||||||
maxcount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy (buffer, "");
|
strcpy (buffer, "");
|
||||||
|
|
||||||
for (ssize_t i = 0; i < std::min<int>(maxcount, valuesize - ofs); i++) {
|
for (ssize_t i = 0; i < rtengine::min<int>(maxcount, valuesize - ofs); i++) {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
strcat (buffer, ", ");
|
strcat (buffer, ", ");
|
||||||
}
|
}
|
||||||
@ -3199,121 +3196,6 @@ int ExifManager::createJPEGMarker (const TagDirectory* root, const rtengine::pro
|
|||||||
return size + 6;
|
return size + 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExifManager::createTIFFHeader (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, int bps, const char* profiledata, int profilelen, const char* iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize)
|
|
||||||
{
|
|
||||||
|
|
||||||
// write tiff header
|
|
||||||
int offs = 0;
|
|
||||||
ByteOrder order = HOSTORDER;
|
|
||||||
|
|
||||||
if (root) {
|
|
||||||
order = root->getOrder ();
|
|
||||||
}
|
|
||||||
|
|
||||||
TagDirectory* cl;
|
|
||||||
|
|
||||||
if (root) {
|
|
||||||
cl = (const_cast<TagDirectory*> (root))->clone (nullptr);
|
|
||||||
// remove some unknown top level tags which produce warnings when opening a tiff
|
|
||||||
Tag *removeTag = cl->getTag (0x9003);
|
|
||||||
|
|
||||||
if (removeTag) {
|
|
||||||
removeTag->setKeep (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeTag = cl->getTag (0x9211);
|
|
||||||
|
|
||||||
if (removeTag) {
|
|
||||||
removeTag->setKeep (false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cl = new TagDirectory (nullptr, ifdAttribs, HOSTORDER);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add tiff strip data
|
|
||||||
int rps = 8;
|
|
||||||
int strips = ceil ((double)H / rps);
|
|
||||||
cl->replaceTag (new Tag (cl, lookupAttrib (ifdAttribs, "RowsPerStrip"), rps, LONG));
|
|
||||||
Tag* stripBC = new Tag (cl, lookupAttrib (ifdAttribs, "StripByteCounts"));
|
|
||||||
stripBC->initInt (0, LONG, strips);
|
|
||||||
cl->replaceTag (stripBC);
|
|
||||||
Tag* stripOffs = new Tag (cl, lookupAttrib (ifdAttribs, "StripOffsets"));
|
|
||||||
stripOffs->initInt (0, LONG, strips);
|
|
||||||
cl->replaceTag (stripOffs);
|
|
||||||
Tag *sampleFormat = new Tag (cl, lookupAttrib (ifdAttribs, "SampleFormat"), bps == 32 ? 3 : 1, SHORT);
|
|
||||||
cl->replaceTag (sampleFormat);
|
|
||||||
|
|
||||||
for (int i = 0; i < strips - 1; i++) {
|
|
||||||
stripBC->setInt (rps * W * 3 * bps / 8, i * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
int remaining = (H - rps * floor ((double)H / rps)) * W * 3 * bps / 8;
|
|
||||||
|
|
||||||
if (remaining) {
|
|
||||||
stripBC->setInt (remaining, (strips - 1) * 4);
|
|
||||||
} else {
|
|
||||||
stripBC->setInt (rps * W * 3 * bps / 8, (strips - 1) * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profiledata) {
|
|
||||||
Tag* icc = new Tag (cl, lookupAttrib (ifdAttribs, "ICCProfile"));
|
|
||||||
icc->initUndefArray (profiledata, profilelen);
|
|
||||||
cl->replaceTag (icc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iptcdata) {
|
|
||||||
Tag* iptc = new Tag (cl, lookupAttrib (ifdAttribs, "IPTCData"));
|
|
||||||
iptc->initLongArray (iptcdata, iptclen);
|
|
||||||
cl->replaceTag (iptc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply list of changes
|
|
||||||
for (rtengine::procparams::ExifPairs::const_iterator i = changeList.begin(); i != changeList.end(); ++i) {
|
|
||||||
cl->applyChange (i->first, i->second);
|
|
||||||
}
|
|
||||||
|
|
||||||
// append default properties
|
|
||||||
const std::vector<Tag*> defTags = getDefaultTIFFTags (cl);
|
|
||||||
|
|
||||||
defTags[0]->setInt (W, 0, LONG);
|
|
||||||
defTags[1]->setInt (H, 0, LONG);
|
|
||||||
defTags[8]->initInt (0, SHORT, 3);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
defTags[8]->setInt (bps, i * 2, SHORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = defTags.size() - 1; i >= 0; i--) {
|
|
||||||
Tag* defTag = defTags[i];
|
|
||||||
cl->replaceTag (defTag->clone (cl));
|
|
||||||
delete defTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate strip offsets
|
|
||||||
int size = cl->calculateSize ();
|
|
||||||
int byps = bps / 8;
|
|
||||||
|
|
||||||
for (int i = 0; i < strips; i++) {
|
|
||||||
stripOffs->setInt (size + 8 + i * rps * W * 3 * byps, i * 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
cl->sort ();
|
|
||||||
bufferSize = cl->calculateSize() + 8;
|
|
||||||
buffer = new unsigned char[bufferSize]; // this has to be deleted in caller
|
|
||||||
sset2 ((unsigned short)order, buffer + offs, order);
|
|
||||||
offs += 2;
|
|
||||||
sset2 (42, buffer + offs, order);
|
|
||||||
offs += 2;
|
|
||||||
sset4 (8, buffer + offs, order);
|
|
||||||
|
|
||||||
int endOffs = cl->write (8, buffer);
|
|
||||||
|
|
||||||
// cl->printAll();
|
|
||||||
delete cl;
|
|
||||||
|
|
||||||
return endOffs;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// global functions to read byteorder dependent data
|
// global functions to read byteorder dependent data
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -809,10 +809,10 @@ const TagAttrib ifdAttribs[] = {
|
|||||||
{0, AC_WRITE, 0, nullptr, 0x828e, AUTO, "CFAPattern", &cfaInterpreter},
|
{0, AC_WRITE, 0, nullptr, 0x828e, AUTO, "CFAPattern", &cfaInterpreter},
|
||||||
{0, AC_WRITE, 0, kodakIfdAttribs, 0x8290, AUTO, "KodakIFD", &stdInterpreter},
|
{0, AC_WRITE, 0, kodakIfdAttribs, 0x8290, AUTO, "KodakIFD", &stdInterpreter},
|
||||||
{0, AC_WRITE, 1, nullptr, 0x8298, AUTO, "Copyright", &stdInterpreter},
|
{0, AC_WRITE, 1, nullptr, 0x8298, AUTO, "Copyright", &stdInterpreter},
|
||||||
|
{0, AC_SYSTEM, 0, nullptr, 0x83BB, AUTO, "IPTCData", &stdInterpreter},
|
||||||
{0, AC_DONTWRITE, 0, nullptr, 0x8606, AUTO, "LeafData", &stdInterpreter}, // is actually a subdir, but a proprietary format
|
{0, AC_DONTWRITE, 0, nullptr, 0x8606, AUTO, "LeafData", &stdInterpreter}, // is actually a subdir, but a proprietary format
|
||||||
{0, AC_WRITE, 0, exifAttribs, 0x8769, AUTO, "Exif", &stdInterpreter},
|
{0, AC_WRITE, 0, exifAttribs, 0x8769, AUTO, "Exif", &stdInterpreter},
|
||||||
{0, AC_SYSTEM, 0, nullptr, 0x8773, AUTO, "ICCProfile", &stdInterpreter},
|
{0, AC_SYSTEM, 0, nullptr, 0x8773, AUTO, "ICCProfile", &stdInterpreter},
|
||||||
{0, AC_SYSTEM, 0, nullptr, 0x83BB, AUTO, "IPTCData", &stdInterpreter},
|
|
||||||
{0, AC_WRITE, 0, gpsAttribs, 0x8825, AUTO, "GPSInfo", &stdInterpreter},
|
{0, AC_WRITE, 0, gpsAttribs, 0x8825, AUTO, "GPSInfo", &stdInterpreter},
|
||||||
{0, AC_WRITE, 0, nullptr, 0x9003, AUTO, "DateTimeOriginal", &stdInterpreter},
|
{0, AC_WRITE, 0, nullptr, 0x9003, AUTO, "DateTimeOriginal", &stdInterpreter},
|
||||||
{0, AC_WRITE, 0, nullptr, 0x9004, AUTO, "DateTimeDigitized", &stdInterpreter},
|
{0, AC_WRITE, 0, nullptr, 0x9004, AUTO, "DateTimeDigitized", &stdInterpreter},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user