@@ -926,6 +926,77 @@ int ImageIO::loadPPMFromMemory(const char* buffer, int width, int height, bool s
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// Taken from Darktable -- src/imageio/format/png.c
|
||||
//
|
||||
/* Write EXIF data to PNG file.
|
||||
* Code copied from DigiKam's libs/dimg/loaders/pngloader.cpp.
|
||||
* The EXIF embedding is defined by ImageMagicK.
|
||||
* It is documented in the ExifTool page:
|
||||
* http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html
|
||||
*
|
||||
* ..and in turn copied from ufraw. thanks to udi and colleagues
|
||||
* for making useful code much more readable and discoverable ;)
|
||||
*/
|
||||
|
||||
void PNGwriteRawProfile(png_struct *ping, png_info *ping_info, const char *profile_type, guint8 *profile_data, png_uint_32 length)
|
||||
{
|
||||
png_textp text;
|
||||
long i;
|
||||
guint8 *sp;
|
||||
png_charp dp;
|
||||
png_uint_32 allocated_length, description_length;
|
||||
|
||||
const guint8 hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
text = static_cast<png_textp>(png_malloc(ping, sizeof(png_text)));
|
||||
description_length = strlen(profile_type);
|
||||
allocated_length = length * 2 + (length >> 5) + 20 + description_length;
|
||||
|
||||
text[0].text = static_cast<png_charp>(png_malloc(ping, allocated_length));
|
||||
text[0].key = static_cast<png_charp>(png_malloc(ping, 80));
|
||||
text[0].key[0] = '\0';
|
||||
|
||||
g_strlcat(text[0].key, "Raw profile type ", 80);
|
||||
g_strlcat(text[0].key, profile_type, 80);
|
||||
|
||||
sp = profile_data;
|
||||
dp = text[0].text;
|
||||
*dp++ = '\n';
|
||||
|
||||
g_strlcpy(dp, profile_type, allocated_length);
|
||||
|
||||
dp += description_length;
|
||||
*dp++ = '\n';
|
||||
*dp = '\0';
|
||||
|
||||
g_snprintf(dp, allocated_length - strlen(text[0].text), "%8lu ", static_cast<unsigned long int>(length));
|
||||
|
||||
dp += 8;
|
||||
|
||||
for(i = 0; i < long(length); i++)
|
||||
{
|
||||
if(i % 36 == 0) *dp++ = '\n';
|
||||
|
||||
*(dp++) = hex[((*sp >> 4) & 0x0f)];
|
||||
*(dp++) = hex[((*sp++) & 0x0f)];
|
||||
}
|
||||
|
||||
*dp++ = '\n';
|
||||
*dp = '\0';
|
||||
text[0].text_length = (dp - text[0].text);
|
||||
text[0].compression = -1;
|
||||
|
||||
if(text[0].text_length <= allocated_length) png_set_text(ping, ping_info, text, 1);
|
||||
|
||||
png_free(ping, text[0].text);
|
||||
png_free(ping, text[0].key);
|
||||
png_free(ping, text);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int ImageIO::savePNG (Glib::ustring fname, volatile int bps)
|
||||
{
|
||||
if (getWidth() < 1 || getHeight() < 1) {
|
||||
@@ -994,6 +1065,29 @@ int ImageIO::savePNG (Glib::ustring fname, volatile int bps)
|
||||
png_set_iCCP(png, info, const_cast<png_charp>("icc"), 0, profdata, profileLength);
|
||||
}
|
||||
|
||||
{
|
||||
// buffer for the exif and iptc
|
||||
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) {
|
||||
iptc_data_free_buf (iptc, iptcdata);
|
||||
iptcdata = nullptr;
|
||||
}
|
||||
|
||||
int size = rtexif::ExifManager::createPNGMarker(exifRoot, exifChange, width, height, bps, (char*)iptcdata, iptclen, buffer, bufferSize);
|
||||
|
||||
if (iptcdata) {
|
||||
iptc_data_free_buf (iptc, iptcdata);
|
||||
}
|
||||
if (buffer && size) {
|
||||
PNGwriteRawProfile(png, info, "exif", buffer, size);
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int rowlen = width * 3 * bps / 8;
|
||||
unsigned char *row = new unsigned char [rowlen];
|
||||
|
@@ -3312,6 +3312,83 @@ int ExifManager::createTIFFHeader (const TagDirectory* root, const rtengine::pro
|
||||
return endOffs;
|
||||
}
|
||||
|
||||
|
||||
int ExifManager::createPNGMarker(const TagDirectory* root, const rtengine::procparams::ExifPairs &changeList, int W, int H, int bps, 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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@@ -361,6 +361,7 @@ public:
|
||||
static std::vector<Tag*> getDefaultTIFFTags (TagDirectory* forthis);
|
||||
static int createJPEGMarker (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, unsigned char* buffer);
|
||||
static int 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);
|
||||
static int createPNGMarker(const TagDirectory *root, const rtengine::procparams::ExifPairs &changeList, int W, int H, int bps, const char *iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize);
|
||||
};
|
||||
|
||||
class Interpreter
|
||||
|
Reference in New Issue
Block a user