bool KIPIWriteImage::write2TIFF(const QString& destPath) { uint32 w = d->width; uint32 h = d->height; uchar* const data = (uchar*)d->data.data(); // TIFF error handling. If an errors/warnings occurs during reading, // libtiff will call these methods TIFFSetWarningHandler(kipi_tiff_warning); TIFFSetErrorHandler(kipi_tiff_error); // Open target file TIFF* const tif = TIFFOpen((const char*)(QFile::encodeName(destPath)).constData(), "w"); if (!tif) { qDebug() << "Failed to open TIFF file for writing" ; return false; } int bitsDepth = d->sixteenBit ? 16 : 8; TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, 9); // NOTE : this tag values aren't defined in libtiff 3.6.1. '2' is PREDICTOR_HORIZONTAL. // Use horizontal differencing for images which are // likely to be continuous tone. The TIFF spec says that this // usually leads to better compression. // See this url for more details: // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html TIFFSetField(tif, TIFFTAG_PREDICTOR, 2); uint16 sampleinfo[1]; if (d->hasAlpha) { sampleinfo[0] = EXTRASAMPLE_ASSOCALPHA; TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, sampleinfo); } else { TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); } TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16)bitsDepth); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); QString libtiffver(QLatin1String(TIFFLIB_VERSION_STR)); libtiffver.replace(QLatin1Char('\n'), QLatin1Char(' ')); TIFFSetField(tif, TIFFTAG_SOFTWARE, (const char*)libtiffver.toLatin1().data()); // Write full image data in tiff directory IFD0 uchar* pixel=0; double alpha_factor; uint32 x, y; uint8 r8, g8, b8, a8=0; uint16 r16, g16, b16, a16=0; int i=0; uint8* const buf = (uint8 *)_TIFFmalloc(TIFFScanlineSize(tif)); if (!buf) { qDebug() << "Cannot allocate memory buffer for main TIFF image." ; TIFFClose(tif); return false; } for (y = 0; y < h; ++y) { if (cancel()) { _TIFFfree(buf); TIFFClose(tif); return false; } i = 0; for (x = 0; x < w; ++x) { pixel = &data[((y * w) + x) * bytesDepth()]; if ( d->sixteenBit ) // 16 bits image. { b16 = (uint16)(pixel[0]+256*pixel[1]); g16 = (uint16)(pixel[2]+256*pixel[3]); r16 = (uint16)(pixel[4]+256*pixel[5]); if (d->hasAlpha) { // TIFF makes you pre-multiply the rgb components by alpha a16 = (uint16)(pixel[6]+256*pixel[7]); alpha_factor = ((double)a16 / 65535.0); r16 = (uint16)(r16*alpha_factor); g16 = (uint16)(g16*alpha_factor); b16 = (uint16)(b16*alpha_factor); } // This might be endian dependent buf[i++] = (uint8)(r16); buf[i++] = (uint8)(r16 >> 8); buf[i++] = (uint8)(g16); buf[i++] = (uint8)(g16 >> 8); buf[i++] = (uint8)(b16); buf[i++] = (uint8)(b16 >> 8); if (d->hasAlpha) { buf[i++] = (uint8)(a16) ; buf[i++] = (uint8)(a16 >> 8) ; } } else // 8 bits image. {
bool KPWriteImage::write2TIFF(const QString& destPath) { uint32 w = d->width; uint32 h = d->height; uchar* data = (uchar*)d->data.data(); // TIFF error handling. If an errors/warnings occurs during reading, // libtiff will call these methods TIFFSetWarningHandler(kipi_tiff_warning); TIFFSetErrorHandler(kipi_tiff_error); // Open target file TIFF *tif = TIFFOpen(QFile::encodeName(destPath), "w"); if (!tif) { kDebug( 51000 ) << "Failed to open TIFF file for writing" << endl; return false; } int bitsDepth = d->sixteenBit ? 16 : 8; TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w); TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h); TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE); TIFFSetField(tif, TIFFTAG_ZIPQUALITY, 9); // NOTE : this tag values aren't defined in libtiff 3.6.1. '2' is PREDICTOR_HORIZONTAL. // Use horizontal differencing for images which are // likely to be continuous tone. The TIFF spec says that this // usually leads to better compression. // See this url for more details: // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html TIFFSetField(tif, TIFFTAG_PREDICTOR, 2); if (d->hasAlpha) { TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, EXTRASAMPLE_ASSOCALPHA); } else { TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); } TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16)bitsDepth); TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); // Store Iptc data. QByteArray ba2 = d->metadata.getIptc(true); #if defined(TIFFTAG_PHOTOSHOP) TIFFSetField (tif, TIFFTAG_PHOTOSHOP, (uint32)ba2.size(), (uchar *)ba2.data()); #endif // Store Xmp data. QByteArray ba3 = d->metadata.getXmp(); #if defined(TIFFTAG_XMLPACKET) TIFFSetField(tif, TIFFTAG_XMLPACKET, (uint32)ba3.size(), (uchar *)ba3.data()); #endif // Standard Exif Ascii tags (available with libtiff 3.6.1) tiffSetExifAsciiTag(tif, TIFFTAG_DOCUMENTNAME, d->metadata, "Exif.Image.DocumentName"); tiffSetExifAsciiTag(tif, TIFFTAG_IMAGEDESCRIPTION, d->metadata, "Exif.Image.ImageDescription"); tiffSetExifAsciiTag(tif, TIFFTAG_MAKE, d->metadata, "Exif.Image.Make"); tiffSetExifAsciiTag(tif, TIFFTAG_MODEL, d->metadata, "Exif.Image.Model"); tiffSetExifAsciiTag(tif, TIFFTAG_DATETIME, d->metadata, "Exif.Image.DateTime"); tiffSetExifAsciiTag(tif, TIFFTAG_ARTIST, d->metadata, "Exif.Image.Artist"); tiffSetExifAsciiTag(tif, TIFFTAG_COPYRIGHT, d->metadata, "Exif.Image.Copyright"); QString libtiffver(TIFFLIB_VERSION_STR); libtiffver.replace('\n', ' '); QString soft = d->kipipluginsVer; soft.append(QString(" ( %1 )").arg(libtiffver)); TIFFSetField(tif, TIFFTAG_SOFTWARE, (const char*)soft.toAscii().data()); // Write ICC profil. if (!d->iccProfile.isEmpty()) { #if defined(TIFFTAG_ICCPROFILE) TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32)d->iccProfile.size(), (uchar *)d->iccProfile.data()); #endif } // Write full image data in tiff directory IFD0 uchar *pixel; double alpha_factor; uint32 x, y; uint8 r8, g8, b8, a8=0; uint16 r16, g16, b16, a16=0; int i=0; uint8 *buf = (uint8 *)_TIFFmalloc(TIFFScanlineSize(tif)); if (!buf) { kDebug( 51000 ) << "Cannot allocate memory buffer for main TIFF image." << endl; TIFFClose(tif); return false; } for (y = 0; y < h; y++) { if (cancel()) { _TIFFfree(buf); TIFFClose(tif); return false; } i = 0; for (x = 0; x < w; x++) { pixel = &data[((y * w) + x) * bytesDepth()]; if ( d->sixteenBit ) // 16 bits image. { b16 = (uint16)(pixel[0]+256*pixel[1]); g16 = (uint16)(pixel[2]+256*pixel[3]); r16 = (uint16)(pixel[4]+256*pixel[5]); if (d->hasAlpha) { // TIFF makes you pre-mutiply the rgb components by alpha a16 = (uint16)(pixel[6]+256*pixel[7]); alpha_factor = ((double)a16 / 65535.0); r16 = (uint16)(r16*alpha_factor); g16 = (uint16)(g16*alpha_factor); b16 = (uint16)(b16*alpha_factor); } // This might be endian dependent buf[i++] = (uint8)(r16); buf[i++] = (uint8)(r16 >> 8); buf[i++] = (uint8)(g16); buf[i++] = (uint8)(g16 >> 8); buf[i++] = (uint8)(b16); buf[i++] = (uint8)(b16 >> 8); if (d->hasAlpha) { buf[i++] = (uint8)(a16) ; buf[i++] = (uint8)(a16 >> 8) ; } } else // 8 bits image. {
bool KIPIWriteImage::write2PNG(const QString& destPath) { /* check this out for b/w support: http://lxr.kde.org/source/playground/graphics/krita-exp/kis_png_converter.cpp#607 */ QFile file(destPath); if (!file.open(QIODevice::ReadWrite)) { qDebug() << "Failed to open PNG file for writing" ; return false; } uchar* data = 0; int bitsDepth = d->sixteenBit ? 16 : 8; png_color_8 sig_bit; png_bytep row_ptr; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_set_write_fn(png_ptr, (void*)&file, kipi_png_write_fn, kipi_png_flush_fn); if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) // Intel png_set_bgr(png_ptr); else // PPC png_set_swap_alpha(png_ptr); if (d->hasAlpha) { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 8 * sizeof(uchar)]; else data = new uchar[d->width * 4 * sizeof(uchar)]; } else { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 6 * sizeof(uchar)]; else data = new uchar[d->width * 3 * sizeof(uchar)]; } sig_bit.red = bitsDepth; sig_bit.green = bitsDepth; sig_bit.blue = bitsDepth; sig_bit.alpha = bitsDepth; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_set_compression_level(png_ptr, 9); // Write Software info. QString libpngver(QLatin1String(PNG_HEADER_VERSION_STRING)); libpngver.replace(QLatin1Char('\n'), QLatin1Char(' ')); QByteArray softAscii = libpngver.toLatin1(); png_text text; text.key = (png_charp)"Software"; text.text = softAscii.data(); text.compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, &(text), 1); png_write_info(png_ptr, info_ptr); png_set_shift(png_ptr, &sig_bit); png_set_packing(png_ptr); uchar* ptr = (uchar*)d->data.data(); uint x, y, j; for (y = 0; y < d->height; ++y) { if (cancel()) { delete [] data; file.close(); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); return false; } j = 0; for (x = 0; x < d->width*bytesDepth(); x+=bytesDepth()) { if (d->sixteenBit) { if (d->hasAlpha) { 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 (d->hasAlpha) { 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 += (d->width * bytesDepth()); } 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); file.close(); return true; }
bool KPWriteImage::write2PNG(const QString& destPath) { FILE* file = fopen(QFile::encodeName(destPath), "wb"); if (!file) { kDebug( 51000 ) << "Failed to open PNG file for writing" << endl; return false; } uchar *data = 0; int bitsDepth = d->sixteenBit ? 16 : 8; png_color_8 sig_bit; png_bytep row_ptr; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, file); if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) // Intel png_set_bgr(png_ptr); else // PPC png_set_swap_alpha(png_ptr); if (d->hasAlpha) { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 8 * sizeof(uchar)]; else data = new uchar[d->width * 4 * sizeof(uchar)]; } else { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 6 * sizeof(uchar)]; else data = new uchar[d->width * 3 * sizeof(uchar)]; } sig_bit.red = bitsDepth; sig_bit.green = bitsDepth; sig_bit.blue = bitsDepth; sig_bit.alpha = bitsDepth; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_set_compression_level(png_ptr, 9); // Write ICC profil. if (!d->iccProfile.isEmpty()) { png_set_iCCP(png_ptr, info_ptr, (png_charp)"icc", PNG_COMPRESSION_TYPE_BASE, d->iccProfile.data(), d->iccProfile.size()); } // Write Software info. QString libpngver(PNG_HEADER_VERSION_STRING); libpngver.replace('\n', ' '); QString soft = d->kipipluginsVer; soft.append(QString(" (%1)").arg(libpngver)); png_text text; text.key = (png_charp)"Software"; text.text = soft.toAscii().data(); text.compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, &(text), 1); // Store Exif data. QByteArray ba = d->metadata.getExif(true); writeRawProfile(png_ptr, info_ptr, (png_charp)"exif", ba.data(), (png_uint_32) ba.size()); // Store Iptc data. QByteArray ba2 = d->metadata.getIptc(); writeRawProfile(png_ptr, info_ptr, (png_charp)"iptc", ba2.data(), (png_uint_32) ba2.size()); // Store Xmp data. QByteArray ba3 = d->metadata.getXmp(); writeRawProfile(png_ptr, info_ptr, (png_charp)("xmp"), ba3.data(), (png_uint_32) ba3.size()); png_write_info(png_ptr, info_ptr); png_set_shift(png_ptr, &sig_bit); png_set_packing(png_ptr); uchar* ptr = (uchar*)d->data.data(); uint x, y, j; for (y = 0; y < d->height; y++) { if (cancel()) { delete [] data; fclose(file); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); return false; } j = 0; for (x = 0; x < d->width*bytesDepth(); x+=bytesDepth()) { if (d->sixteenBit) { if (d->hasAlpha) { 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 (d->hasAlpha) { 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 += (d->width * bytesDepth()); } 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(file); return true; }