diff options
Diffstat (limited to 'src/libs/dimg/loaders/pngloader.cpp')
-rw-r--r-- | src/libs/dimg/loaders/pngloader.cpp | 993 |
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 |