summaryrefslogtreecommitdiffstats
path: root/src/libs/dimg/loaders/pngloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dimg/loaders/pngloader.cpp')
-rw-r--r--src/libs/dimg/loaders/pngloader.cpp993
1 files changed, 993 insertions, 0 deletions
diff --git a/src/libs/dimg/loaders/pngloader.cpp b/src/libs/dimg/loaders/pngloader.cpp
new file mode 100644
index 00000000..402cf944
--- /dev/null
+++ b/src/libs/dimg/loaders/pngloader.cpp
@@ -0,0 +1,993 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-11-01
+ * Description : a PNG image loader for DImg framework.
+ *
+ * Copyright (C) 2005-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.
+ *
+ * ============================================================ */
+
+// This line must be commented to prevent any latency time
+// when we use threaded image loader interface for each image
+// files io. Uncomment this line only for debugging.
+//#define ENABLE_DEBUG_MESSAGES
+
+#define PNG_BYTES_TO_CHECK 4
+
+// C Ansi includes.
+
+extern "C"
+{
+#include <unistd.h>
+#include <stdarg.h>
+}
+
+// C++ includes.
+
+#include <cstdlib>
+#include <cstdio>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqcstring.h>
+
+// Local includes.
+
+#include "daboutdata.h"
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "pngloader.h"
+
+namespace Digikam
+{
+
+#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 )
+ typedef png_bytep iCCP_data;
+#else
+ typedef png_charp iCCP_data;
+#endif
+
+PNGLoader::PNGLoader(DImg* image)
+ : DImgLoader(image)
+{
+ m_hasAlpha = false;
+ m_sixteenBit = false;
+}
+
+bool PNGLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ png_uint_32 w32, h32;
+ int width, height;
+ FILE *f;
+ int bit_depth, color_type, interlace_type;
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+
+ readMetadata(filePath, DImg::PNG);
+
+ // -------------------------------------------------------------------
+ // Open the file
+
+ f = fopen(TQFile::encodeName(filePath), "rb");
+ if ( !f )
+ {
+ DDebug() << k_funcinfo << "Cannot open image file." << endl;
+ return false;
+ }
+
+ unsigned char buf[PNG_BYTES_TO_CHECK];
+
+ fread(buf, 1, PNG_BYTES_TO_CHECK, f);
+ if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK))
+ {
+ DDebug() << k_funcinfo << "Not a PNG image file." << endl;
+ fclose(f);
+ return false;
+ }
+ rewind(f);
+
+ // -------------------------------------------------------------------
+ // Initialize the internal structures
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ DDebug() << k_funcinfo << "Invalid PNG image file structure." << endl;
+ fclose(f);
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ DDebug() << k_funcinfo << "Cannot reading PNG image file structure." << endl;
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ fclose(f);
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // PNG error handling. If an error occurs during reading, libpng
+ // will jump here
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ DDebug() << k_funcinfo << "Internal libPNG error during reading file. Process aborted!" << endl;
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(f);
+ return false;
+ }
+
+ png_init_io(png_ptr, f);
+
+ // -------------------------------------------------------------------
+ // Read all PNG info up to image data
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32),
+ (png_uint_32 *) (&h32), &bit_depth, &color_type,
+ &interlace_type, NULL, NULL);
+
+ width = (int)w32;
+ height = (int)h32;
+
+ // TODO: Endianness:
+ // You may notice that the code for little and big endian
+ // below is now identical. This was found to work by PPC users.
+ // If this proves right, all the conditional clauses can be removed.
+
+ if (bit_depth == 16)
+ {
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in 16 bits/color/pixel." << endl;
+#endif
+ m_sixteenBit = true;
+
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_RGB : // RGB
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB" << endl;
+#endif
+ m_hasAlpha = false;
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA : // RGBA
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA" << endl;
+#endif
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY : // Grayscale
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY" << endl;
+#endif
+ png_set_gray_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA : // Grayscale + Alpha
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA" << endl;
+#endif
+ png_set_gray_to_rgb(png_ptr);
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE : // Indexed
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_PALETTE" << endl;
+#endif
+ png_set_palette_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ default:
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << "PNG color type unknown." << endl;
+#endif
+ return false;
+ }
+ }
+ else
+ {
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << "PNG in >=8 bits/color/pixel." << endl;
+#endif
+ m_sixteenBit = false;
+ png_set_packing(png_ptr);
+
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_RGB : // RGB
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB" << endl;
+#endif
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA : // RGBA
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA" << endl;
+#endif
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY : // Grayscale
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY" << endl;
+#endif
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ png_set_gray_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA : // Grayscale + alpha
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA" << endl;
+#endif
+ png_set_gray_to_rgb(png_ptr);
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE : // Indexed
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_PALETTE" << endl;
+#endif
+ png_set_packing(png_ptr);
+ png_set_palette_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = true;
+ break;
+
+ default:
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << "PNG color type unknown." << endl;
+#endif
+ return false;
+ }
+ }
+
+ if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_bgr(png_ptr);
+ else // PPC
+ png_set_bgr(png_ptr);
+ //png_set_swap_alpha(png_ptr);
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ // -------------------------------------------------------------------
+ // Get image data.
+
+ // Call before png_read_update_info and png_start_read_image()
+ // For non-interlaced images number_passes will be 1
+ int number_passes = png_set_interlace_handling(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ uchar *data = 0;
+
+ if (m_sixteenBit)
+ data = new uchar[width*height*8]; // 16 bits/color/pixel
+ else
+ data = new uchar[width*height*4]; // 8 bits/color/pixel
+
+ uchar **lines = 0;
+ lines = (uchar **)malloc(height * sizeof(uchar *));
+ if (!lines)
+ {
+ DDebug() << k_funcinfo << "Cannot allocate memory to load PNG image data." << endl;
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+ delete [] data;
+ return false;
+ }
+
+ for (int i = 0; i < height; i++)
+ {
+ if (m_sixteenBit)
+ lines[i] = data + (i * width * 8);
+ else
+ lines[i] = data + (i * width * 4);
+ }
+
+ // The easy way to read the whole image
+ // png_read_image(png_ptr, lines);
+ // The other way to read images is row by row. Necessary for observer.
+ // Now we need to deal with interlacing.
+
+ for (int pass = 0; pass < number_passes; pass++)
+ {
+ int y;
+ int checkPoint = 0;
+ for (y = 0; y < height; y++)
+ {
+ if (observer && y == checkPoint)
+ {
+ checkPoint += granularity(observer, height, 0.7);
+ if (!observer->continueQuery(m_image))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+ delete [] data;
+ free(lines);
+ return false;
+ }
+ // use 10% - 80% for progress while reading rows
+ observer->progressInfo(m_image, 0.1 + (0.7 * ( ((float)y)/((float)height) )) );
+ }
+
+ png_read_rows(png_ptr, lines+y, NULL, 1);
+ }
+ }
+
+ free(lines);
+
+ // Swap bytes in 16 bits/color/pixel for DImg
+
+ if (m_sixteenBit)
+ {
+ uchar ptr[8]; // One pixel to swap
+
+ for (int p = 0; p < width*height*8; p+=8)
+ {
+ memcpy (&ptr[0], &data[p], 8); // Current pixel
+
+ data[ p ] = ptr[1]; // Blue
+ data[p+1] = ptr[0];
+ data[p+2] = ptr[3]; // Green
+ data[p+3] = ptr[2];
+ data[p+4] = ptr[5]; // Red
+ data[p+5] = ptr[4];
+ data[p+6] = ptr[7]; // Alpha
+ data[p+7] = ptr[6];
+ }
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 0.9);
+
+ // -------------------------------------------------------------------
+ // Read image ICC profile
+
+ TQMap<int, TQByteArray>& metaData = imageMetaData();
+
+#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 )
+ png_charp profile_name;
+ iCCP_data profile_data=NULL;
+#else
+ png_charp profile_name, profile_data=NULL;
+#endif
+ png_uint_32 profile_size;
+ int compression_type;
+
+ png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_size);
+
+ if (profile_data != NULL)
+ {
+ TQByteArray profile_rawdata(profile_size);
+ memcpy(profile_rawdata.data(), profile_data, profile_size);
+ metaData.insert(DImg::ICC, profile_rawdata);
+ }
+ else
+ {
+ // If ICC profile is null, check Exif metadata.
+ checkExifWorkingColorSpace();
+ }
+
+ // -------------------------------------------------------------------
+ // Get embbeded text data.
+
+ png_text* text_ptr;
+ int num_comments = png_get_text(png_ptr, info_ptr, &text_ptr, NULL);
+
+ /*
+ Standard Embedded text includes in PNG :
+
+ Title Short (one line) title or caption for image
+ Author Name of image's creator
+ Description Description of image (possibly long)
+ Copyright Copyright notice
+ Creation Time Time of original image creation
+ Software Software used to create the image
+ Disclaimer Legal disclaimer
+ Warning Warning of nature of content
+ Source Device used to create the image
+ Comment Miscellaneous comment; conversion from GIF comment
+
+ Extra Raw profiles tag are used by ImageMAgick and defines at this URL :
+ http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-5.87/html/TagNames/PNG.html#TextualData
+ */
+
+ for (int i = 0; i < num_comments; i++)
+ {
+ // Check if we have a Raw profile embedded using ImageMagick technic.
+
+ if (memcmp(text_ptr[i].key, "Raw profile type exif", 21) != 0 ||
+ memcmp(text_ptr[i].key, "Raw profile type APP1", 21) != 0 ||
+ memcmp(text_ptr[i].key, "Raw profile type iptc", 21) != 0)
+ {
+ imageSetEmbbededText(text_ptr[i].key, text_ptr[i].text);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "Reading PNG Embedded text: key=" << text_ptr[i].key
+ << " text=" << text_ptr[i].text << endl;
+#endif
+ }
+ }
+
+ // -------------------------------------------------------------------
+
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageWidth() = width;
+ imageHeight() = height;
+ imageData() = data;
+ imageSetAttribute("format", "PNG");
+
+ return true;
+}
+
+bool PNGLoader::save(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ FILE *f;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ uchar *ptr, *data = 0;
+ uint x, y, j;
+ png_bytep row_ptr;
+ png_color_8 sig_bit;
+ int quality = 75;
+ int compression = 3;
+
+ // -------------------------------------------------------------------
+ // Open the file
+
+ f = fopen(TQFile::encodeName(filePath), "wb");
+ if ( !f )
+ {
+ DDebug() << k_funcinfo << "Cannot open target image file." << endl;
+ return false;
+ }
+
+
+ // -------------------------------------------------------------------
+ // Initialize the internal structures
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ DDebug() << k_funcinfo << "Invalid target PNG image file structure." << endl;
+ fclose(f);
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ {
+ DDebug() << k_funcinfo << "Cannot create PNG image file structure." << endl;
+ png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+ fclose(f);
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // PNG error handling. If an error occurs during writing, libpng
+ // will jump here
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ DDebug() << k_funcinfo << "Internal libPNG error during writing file. Process aborted!" << endl;
+ fclose(f);
+ png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr);
+ png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr);
+ return false;
+ }
+
+ png_init_io(png_ptr, f);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_bgr(png_ptr);
+ else // PPC
+ png_set_swap_alpha(png_ptr);
+
+ if (imageHasAlpha())
+ {
+ png_set_IHDR(png_ptr, info_ptr, imageWidth(), imageHeight(), imageBitsDepth(),
+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (imageSixteenBit())
+ data = new uchar[imageWidth() * 8 * sizeof(uchar)];
+ else
+ data = new uchar[imageWidth() * 4 * sizeof(uchar)];
+ }
+ else
+ {
+ png_set_IHDR(png_ptr, info_ptr, imageWidth(), imageHeight(), imageBitsDepth(),
+ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (imageSixteenBit())
+ data = new uchar[imageWidth() * 6 * sizeof(uchar)];
+ else
+ data = new uchar[imageWidth() * 3 * sizeof(uchar)];
+ }
+
+ sig_bit.red = imageBitsDepth();
+ sig_bit.green = imageBitsDepth();
+ sig_bit.blue = imageBitsDepth();
+ sig_bit.alpha = imageBitsDepth();
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+ // -------------------------------------------------------------------
+ // Quality to convert to compression
+
+ TQVariant qualityAttr = imageGetAttribute("quality");
+ quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90;
+
+ if (quality < 1)
+ quality = 1;
+ if (quality > 99)
+ quality = 99;
+
+ quality = quality / 10;
+ compression = 9 - quality;
+
+ if (compression < 0)
+ compression = 0;
+ if (compression > 9)
+ compression = 9;
+
+ png_set_compression_level(png_ptr, compression);
+
+ // -------------------------------------------------------------------
+ // Write ICC profil.
+
+ TQByteArray profile_rawdata = m_image->getICCProfil();
+
+ if (!profile_rawdata.isEmpty())
+ {
+#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 )
+ png_set_iCCP(png_ptr, info_ptr, (png_charp)("icc"), PNG_COMPRESSION_TYPE_BASE, reinterpret_cast<iCCP_data>(profile_rawdata.data()), profile_rawdata.size());
+#else
+ png_set_iCCP(png_ptr, info_ptr, (png_charp)"icc", PNG_COMPRESSION_TYPE_BASE, profile_rawdata.data(), profile_rawdata.size());
+#endif
+ }
+
+ // -------------------------------------------------------------------
+ // Write embbeded Text
+
+ typedef TQMap<TQString, TQString> EmbeddedTextMap;
+ EmbeddedTextMap map = imageEmbeddedText();
+
+ for (EmbeddedTextMap::iterator it = map.begin(); it != map.end(); ++it)
+ {
+ if (it.key() != TQString("Software") && it.key() != TQString("Comment"))
+ {
+ png_text text;
+ text.key = (char*)it.key().ascii();
+ text.text = (char*)it.data().ascii();
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "Writing PNG Embedded text: key=" << text.key << " text=" << text.text << endl;
+#endif
+ text.compression = PNG_TEXT_COMPRESSION_zTXt;
+ png_set_text(png_ptr, info_ptr, &(text), 1);
+ }
+ }
+
+ // Update 'Software' text tag.
+ TQString software("digiKam ");
+ software.append(digikam_version);
+ TQString libpngver(PNG_HEADER_VERSION_STRING);
+ libpngver.replace('\n', ' ');
+ software.append(TQString(" (%1)").arg(libpngver));
+ png_text text;
+ text.key = (png_charp)("Software");
+ text.text = (char *)software.ascii();
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "Writing PNG Embedded text: key=" << text.key << " text=" << text.text << endl;
+#endif
+ text.compression = PNG_TEXT_COMPRESSION_zTXt;
+ png_set_text(png_ptr, info_ptr, &(text), 1);
+
+ // Write embedded Raw profiles metadata (Exif/Iptc) in text tag using ImageMagick technic.
+ // Write digiKam comment like an iTXt chunk using UTF8 encoding.
+ // NOTE: iTXt will be enable by default with libpng >= 1.3.0.
+
+ typedef TQMap<int, TQByteArray> MetaDataMap;
+ MetaDataMap metaDataMap = imageMetaData();
+
+ for (MetaDataMap::iterator it = metaDataMap.begin(); it != metaDataMap.end(); ++it)
+ {
+ TQByteArray ba = it.data();
+
+ switch (it.key())
+ {
+
+#ifdef PNG_iTXt_SUPPORTED
+
+ // TODO : this code is not yet tested. It require libpng 1.3.0.
+
+ case(DImg::COM):
+ {
+ png_text comment;
+ comment.key = "Comment";
+ comment.text = ba.data();
+ comment.itxt_length = ba.size();
+ comment.compression = PNG_ITXT_COMPRESSION_zTXt;
+ png_set_text(png_ptr, info_ptr, &(comment), 1);
+
+ DDebug() << "Writing digiKam comment into iTXt PNG chunk : " << ba << endl;
+ break;
+ }
+#endif
+
+ case(DImg::EXIF):
+ {
+ const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
+ TQByteArray profile;
+
+ // If bytes array do not start with ImageMagick header, Exif metadata have been created from
+ // scratch using Exiv2. In this case, we need to add Exif header from start.
+ if (memcmp(ba.data(), "exif", 4) != 0 &&
+ memcmp(ba.data(), "iptc", 4) != 0 &&
+ memcmp(ba.data(), "profile", 7) != 0)
+ {
+ profile = TQByteArray(ba.size() + sizeof(ExifHeader));
+ memcpy(profile.data(), ExifHeader, sizeof(ExifHeader));
+ memcpy(profile.data()+sizeof(ExifHeader), ba.data(), ba.size());
+ }
+ else
+ {
+ profile = ba;
+ }
+
+ writeRawProfile(png_ptr, info_ptr, (png_charp)("exif"), profile.data(), (png_uint_32) profile.size());
+ break;
+ }
+ case(DImg::IPTC):
+ {
+ writeRawProfile(png_ptr, info_ptr, (png_charp)("iptc"), ba.data(), (png_uint_32) ba.size());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 0.2);
+
+ // -------------------------------------------------------------------
+ // Write image data
+
+ png_write_info(png_ptr, info_ptr);
+ png_set_shift(png_ptr, &sig_bit);
+ png_set_packing(png_ptr);
+ ptr = imageData();
+
+ uint checkPoint = 0;
+ for (y = 0; y < imageHeight(); y++)
+ {
+
+ if (observer && y == checkPoint)
+ {
+ checkPoint += granularity(observer, imageHeight(), 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ fclose(f);
+ png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr);
+ png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.2 + (0.8 * ( ((float)y)/((float)imageHeight()) )));
+ }
+
+ j = 0;
+
+ for (x = 0; x < imageWidth()*imageBytesDepth(); x+=imageBytesDepth())
+ {
+ if (imageSixteenBit())
+ {
+ if (imageHasAlpha())
+ {
+ data[j++] = ptr[x+1]; // Blue
+ data[j++] = ptr[ x ];
+ data[j++] = ptr[x+3]; // Green
+ data[j++] = ptr[x+2];
+ data[j++] = ptr[x+5]; // Red
+ data[j++] = ptr[x+4];
+ data[j++] = ptr[x+7]; // Alpha
+ data[j++] = ptr[x+6];
+ }
+ else
+ {
+ data[j++] = ptr[x+1]; // Blue
+ data[j++] = ptr[ x ];
+ data[j++] = ptr[x+3]; // Green
+ data[j++] = ptr[x+2];
+ data[j++] = ptr[x+5]; // Red
+ data[j++] = ptr[x+4];
+ }
+ }
+ else
+ {
+ if (imageHasAlpha())
+ {
+ data[j++] = ptr[ x ]; // Blue
+ data[j++] = ptr[x+1]; // Green
+ data[j++] = ptr[x+2]; // Red
+ data[j++] = ptr[x+3]; // Alpha
+ }
+ else
+ {
+ data[j++] = ptr[ x ]; // Blue
+ data[j++] = ptr[x+1]; // Green
+ data[j++] = ptr[x+2]; // Red
+ }
+ }
+ }
+
+ row_ptr = (png_bytep) data;
+
+ png_write_rows(png_ptr, &row_ptr, 1);
+ ptr += (imageWidth() * imageBytesDepth());
+ }
+
+ delete [] data;
+
+ // -------------------------------------------------------------------
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr);
+ png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr);
+
+ fclose(f);
+
+ imageSetAttribute("savedformat", "PNG");
+
+ saveMetadata(filePath);
+
+ return true;
+}
+
+bool PNGLoader::hasAlpha() const
+{
+ return m_hasAlpha;
+}
+
+bool PNGLoader::sixteenBit() const
+{
+ return m_sixteenBit;
+}
+
+void PNGLoader::writeRawProfile(png_struct *ping, png_info *ping_info, char *profile_type,
+ char *profile_data, png_uint_32 length)
+{
+ png_textp text;
+
+ long i;
+
+ uchar *sp;
+
+ png_charp dp;
+
+ png_uint_32 allocated_length, description_length;
+
+ const uchar hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+ DDebug() << "Writing Raw profile: type=" << profile_type << ", length=" << length << endl;
+
+ text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text));
+ description_length = strlen((const char *) profile_type);
+ allocated_length = (png_uint_32) (length*2 + (length >> 5) + 20 + description_length);
+
+ text[0].text = (png_charp) png_malloc(ping, allocated_length);
+ text[0].key = (png_charp) png_malloc(ping, (png_uint_32) 80);
+ text[0].key[0] = '\0';
+
+ concatenateString(text[0].key, "Raw profile type ", 4096);
+ concatenateString(text[0].key, (const char *) profile_type, 62);
+
+ sp = (uchar*)profile_data;
+ dp = text[0].text;
+ *dp++='\n';
+
+ copyString(dp, (const char *) profile_type, allocated_length);
+
+ dp += description_length;
+ *dp++='\n';
+
+ formatString(dp, allocated_length-strlen(text[0].text), "%8lu ", length);
+
+ dp += 8;
+
+ for (i=0; i < (long) length; i++)
+ {
+ if (i%36 == 0)
+ *dp++='\n';
+
+ *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
+ *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
+ }
+
+ *dp++='\n';
+ *dp='\0';
+ text[0].text_length = (png_size_t) (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);
+}
+
+size_t PNGLoader::concatenateString(char *destination, const char *source, const size_t length)
+{
+ char *q;
+
+ const char *p;
+
+ size_t i;
+
+ size_t count;
+
+ if ( !destination || !source || length == 0 )
+ return 0;
+
+ p = source;
+ q = destination;
+ i = length;
+
+ while ((i-- != 0) && (*q != '\0'))
+ q++;
+
+ count = (size_t) (q-destination);
+ i = length-count;
+
+ if (i == 0)
+ return(count+strlen(p));
+
+ while (*p != '\0')
+ {
+ if (i != 1)
+ {
+ *q++=(*p);
+ i--;
+ }
+ p++;
+ }
+
+ *q='\0';
+
+ return(count+(p-source));
+}
+
+size_t PNGLoader::copyString(char *destination, const char *source, const size_t length)
+{
+ char *q;
+
+ const char *p;
+
+ size_t i;
+
+ if ( !destination || !source || length == 0 )
+ return 0;
+
+ p = source;
+ q = destination;
+ i = length;
+
+ if ((i != 0) && (--i != 0))
+ {
+ do
+ {
+ if ((*q++=(*p++)) == '\0')
+ break;
+ }
+ while (--i != 0);
+ }
+
+ if (i == 0)
+ {
+ if (length != 0)
+ *q='\0';
+
+ do
+ {
+ }
+ while (*p++ != '\0');
+ }
+
+ return((size_t) (p-source-1));
+}
+
+long PNGLoader::formatString(char *string, const size_t length, const char *format,...)
+{
+ long n;
+
+ va_list operands;
+
+ va_start(operands,format);
+ n = (long) formatStringList(string, length, format, operands);
+ va_end(operands);
+ return(n);
+}
+
+long PNGLoader::formatStringList(char *string, const size_t length, const char *format, va_list operands)
+{
+ int n = vsnprintf(string, length, format, operands);
+
+ if (n < 0)
+ string[length-1] = '\0';
+
+ return((long) n);
+}
+
+} // NameSpace Digikam