21#include "kis_png_converter.h"
27#include <tdeapplication.h>
28#include <tdemessagebox.h>
31#include <KoDocumentInfo.h>
33#include <tdeio/netaccess.h>
35#include <kis_abstract_colorspace.h>
36#include <kis_colorspace_factory_registry.h>
39#include <kis_iterators_pixel.h>
41#include <kis_meta_registry.h>
42#include <kis_profile.h>
43#include <kis_paint_layer.h>
44#include <kis_group_layer.h>
50 const TQ_UINT8 PIXEL_BLUE = 0;
51 const TQ_UINT8 PIXEL_GREEN = 1;
52 const TQ_UINT8 PIXEL_RED = 2;
53 const TQ_UINT8 PIXEL_ALPHA = 3;
56 int getColorTypeforColorSpace( KisColorSpace * cs ,
bool alpha)
58 if ( cs->id() == KisID(
"GRAYA") || cs->id() == KisID(
"GRAYA16") )
60 return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY;
62 if ( cs->id() == KisID(
"RGBA") || cs->id() == KisID(
"RGBA16") )
64 return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
67 KMessageBox::error(0, i18n(
"Cannot export images in %1.\n").arg(cs->id().name()) ) ;
73 TQString getColorSpaceForColorType(
int color_type,
int color_nb_bits) {
74 if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
83 }
else if(color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) {
91 }
else if(color_type == PNG_COLOR_TYPE_PALETTE) {
98 void fillText(png_text* p_text,
char* key, TQString& text)
100 p_text->compression = PNG_TEXT_COMPRESSION_zTXt;
102 char* textc =
new char[text.length()+1];
103 strcpy(textc, text.ascii());
104 p_text->text = textc;
105 p_text->text_length = text.length()+1;
110KisPNGConverter::KisPNGConverter(KisDoc *doc, KisUndoAdapter *adapter)
122KisPNGConverter::~KisPNGConverter()
128 KisPNGStream(TQ_UINT8* buf, TQ_UINT32 depth ) : m_posinc(8),m_depth(depth), m_buf(buf) { *m_buf = 0;};
137 return (( (*m_buf) >> (m_posinc) ) & ( ( 1 << m_depth ) - 1 ) );
139 void setNextValue(
int v)
148 *m_buf = (v << m_posinc) | *m_buf;
151 TQ_UINT32 m_posinc, m_depth;
155KisImageBuilder_Result KisPNGConverter::decode(
const KURL& uri)
157 kdDebug(41008) <<
"Start decoding PNG File" << endl;
159 kdDebug(41008) << TQString(TQFile::encodeName(uri.path())) <<
" " << uri.path() <<
" " << uri << endl;
160 FILE *fp = fopen(TQFile::encodeName(uri.path()),
"rb");
163 return (KisImageBuilder_RESULT_NOT_EXIST);
165 png_byte signature[8];
166 fread(signature, 1, 8, fp);
167 if (!png_check_sig(signature, 8))
169 return (KisImageBuilder_RESULT_BAD_FETCH);
173 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
174 if (!KisImageBuilder_RESULT_FAILURE)
175 return (KisImageBuilder_RESULT_FAILURE);
177 png_infop info_ptr = png_create_info_struct(png_ptr);
180 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
181 return (KisImageBuilder_RESULT_FAILURE);
184 png_infop end_info = png_create_info_struct(png_ptr);
187 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
188 return (KisImageBuilder_RESULT_FAILURE);
192 if (setjmp(png_jmpbuf(png_ptr)))
194 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
196 return (KisImageBuilder_RESULT_FAILURE);
199 png_init_io(png_ptr, fp);
200 png_set_sig_bytes(png_ptr, 8);
203 png_read_info(png_ptr, info_ptr);
206 png_uint_32 width, height;
207 int color_nb_bits, color_type, interlace_type;
208 png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, NULL, NULL);
209 kdDebug(41008) <<
"it's an " << color_nb_bits <<
" depth image" << endl;
212 #ifndef WORDS_BIGENDIAN
213 if (color_nb_bits > 8 )
214 png_set_swap(png_ptr);
218 TQString csName = getColorSpaceForColorType(color_type, color_nb_bits);
219 if(csName.isEmpty()) {
220 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
221 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
223 bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA);
225 int number_of_passes = 1;
226 if (interlace_type == PNG_INTERLACE_ADAM7)
227 number_of_passes = png_set_interlace_handling(png_ptr);
230 KisProfile* profile = 0;
231 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
232 png_charp profile_name;
233#if PNG_LIBPNG_VER < 10500
234 png_charp profile_data;
236 png_bytep profile_data;
238 int compression_type;
241 if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen))
243 TQByteArray profile_rawdata;
245 if (TQString::compare(profile_name,
"icc") == 0) {
246 profile_rawdata.resize(proflen);
247 memcpy(profile_rawdata.data(), profile_data, proflen);
248 profile =
new KisProfile(profile_rawdata);
249 TQ_CHECK_PTR(profile);
251 kdDebug(41008) <<
"profile name: " << profile->productName() <<
" profile description: " << profile->productDescription() <<
" information sur le produit: " << profile->productInfo() << endl;
252 if(!profile->isSuitableForOutput())
254 kdDebug(41008) <<
"the profile is not suitable for output and therefore cannot be used in chalk, we need to convert the image to a standard profile" << endl;
263 if (profile && profile->isSuitableForOutput())
265 kdDebug(41008) <<
"image has embedded profile: " << profile -> productName() <<
"\n";
266 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
269 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,
""),
"");
273 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
274 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
278 cmsHTRANSFORM transform = 0;
279 if(profile && !profile->isSuitableForOutput())
281 transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(),
282 cs->getProfile()->profile() , cs->colorSpaceType(),
283 INTENT_PERCEPTUAL, 0);
289 png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
290 KoDocumentInfo * info = m_doc->documentInfo();
291 KoDocumentInfoAbout * aboutPage =
static_cast<KoDocumentInfoAbout *
>(info->page(
"about" ));
292 KoDocumentInfoAuthor * authorPage =
static_cast<KoDocumentInfoAuthor *
>(info->page(
"author"));
293 kdDebug(41008) <<
"There are " << num_comments <<
" comments in the text" << endl;
294 for(
int i = 0; i < num_comments; i++)
296 kdDebug(41008) <<
"key is " << text_ptr[i].key <<
" containing " << text_ptr[i].text << endl;
297 if(TQString::compare(text_ptr[i].key,
"title") == 0)
299 aboutPage->setTitle(text_ptr[i].text);
300 }
else if(TQString::compare(text_ptr[i].key,
"abstract") == 0)
302 aboutPage->setAbstract(text_ptr[i].text);
303 }
else if(TQString::compare(text_ptr[i].key,
"author") == 0)
305 authorPage->setFullName(text_ptr[i].text);
310 png_bytep row_pointer = 0;
313 png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
314 row_pointer =
new png_byte[rowbytes];
316 catch(std::bad_alloc& e)
320 kdDebug(41008) <<
"bad alloc: " << e.what() << endl;
322 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
323 return (KisImageBuilder_RESULT_FAILURE);
329 if(color_type == PNG_COLOR_TYPE_PALETTE) {
330 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
342 m_img =
new KisImage(m_doc->undoAdapter(), width, height, cs,
"built image");
343 m_img->blockSignals(
true);
345 if(profile && !profile->isSuitableForOutput())
347 m_img -> addAnnotation( profile->annotation() );
351 KisPaintLayer* layer =
new KisPaintLayer(m_img, m_img -> nextLayerName(), TQ_UINT8_MAX);
352 for (
int i = 0; i < number_of_passes; i++)
354 for (png_uint_32 y = 0; y < height; y++) {
355 KisHLineIterator it = layer -> paintDevice() -> createHLineIterator(0, y, width,
true);
356 png_read_rows(png_ptr, &row_pointer, NULL, 1);
360 case PNG_COLOR_TYPE_GRAY:
361 case PNG_COLOR_TYPE_GRAY_ALPHA:
362 if(color_nb_bits == 16)
364 TQ_UINT16 *src =
reinterpret_cast<TQ_UINT16 *
>(row_pointer);
365 while (!it.isDone()) {
366 TQ_UINT16 *d =
reinterpret_cast<TQ_UINT16 *
>(it.rawData());
368 if(transform) cmsDoTransform(transform, d, d, 1);
369 if(hasalpha) d[1] = *(src++);
370 else d[1] = TQ_UINT16_MAX;
374 TQ_UINT8 *src = row_pointer;
375 while (!it.isDone()) {
376 TQ_UINT8 *d = it.rawData();
378 if(transform) cmsDoTransform(transform, d, d, 1);
379 if(hasalpha) d[1] = *(src++);
380 else d[1] = TQ_UINT8_MAX;
386 case PNG_COLOR_TYPE_RGB:
387 case PNG_COLOR_TYPE_RGB_ALPHA:
388 if(color_nb_bits == 16)
390 TQ_UINT16 *src =
reinterpret_cast<TQ_UINT16 *
>(row_pointer);
391 while (!it.isDone()) {
392 TQ_UINT16 *d =
reinterpret_cast<TQ_UINT16 *
>(it.rawData());
396 if(transform) cmsDoTransform(transform, d, d, 1);
397 if(hasalpha) d[3] = *(src++);
398 else d[3] = TQ_UINT16_MAX;
402 TQ_UINT8 *src = row_pointer;
403 while (!it.isDone()) {
404 TQ_UINT8 *d = it.rawData();
408 if(transform) cmsDoTransform(transform, d, d, 1);
409 if(hasalpha) d[3] = *(src++);
410 else d[3] = TQ_UINT8_MAX;
415 case PNG_COLOR_TYPE_PALETTE:
417 KisPNGStream stream(row_pointer, color_nb_bits);
418 while (!it.isDone()) {
419 TQ_UINT8 *d = it.rawData();
420 png_color c = palette[ stream.nextValue() ];
430 return KisImageBuilder_RESULT_UNSUPPORTED;
434 m_img->addLayer(layer, m_img->rootLayer(), 0);
436 png_read_end(png_ptr, end_info);
440 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
442 delete [] row_pointer;
444 return KisImageBuilder_RESULT_OK;
448KisImageBuilder_Result KisPNGConverter::buildImage(
const KURL& uri)
450 kdDebug(41008) << TQString(TQFile::encodeName(uri.path())) <<
" " << uri.path() <<
" " << uri << endl;
452 return KisImageBuilder_RESULT_NO_URI;
454 if (!TDEIO::NetAccess::exists(uri,
false, tqApp -> mainWidget())) {
455 return KisImageBuilder_RESULT_NOT_EXIST;
459 KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
462 if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) {
464 uriTF.setPath( tmpFile );
465 result = decode(uriTF);
466 TDEIO::NetAccess::removeTempFile(tmpFile);
473KisImageSP KisPNGConverter::image()
478KisImageBuilder_Result KisPNGConverter::buildFile(
const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd,
int compression,
bool interlace,
bool alpha)
480 kdDebug(41008) <<
"Start writing PNG File" << endl;
482 return KisImageBuilder_RESULT_INVALID_ARG;
484 KisImageSP img = layer -> image();
486 return KisImageBuilder_RESULT_EMPTY;
489 return KisImageBuilder_RESULT_NO_URI;
491 if (!uri.isLocalFile())
492 return KisImageBuilder_RESULT_NOT_LOCAL;
494 FILE *fp = fopen(TQFile::encodeName(uri.path()),
"wb");
497 return (KisImageBuilder_RESULT_FAILURE);
499 int height = img->height();
500 int width = img->width();
502 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
506 return (KisImageBuilder_RESULT_FAILURE);
509 png_infop info_ptr = png_create_info_struct(png_ptr);
513 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
514 return (KisImageBuilder_RESULT_FAILURE);
518 if (setjmp(png_jmpbuf(png_ptr)))
521 png_destroy_write_struct(&png_ptr, &info_ptr);
523 return (KisImageBuilder_RESULT_FAILURE);
526 png_init_io(png_ptr, fp);
533 png_set_compression_level(png_ptr, compression);
536 png_set_compression_mem_level(png_ptr, 8);
537 png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
538 png_set_compression_window_bits(png_ptr, 15);
539 png_set_compression_method(png_ptr, 8);
540 png_set_compression_buffer_size(png_ptr, 8192);
542 int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
543 int color_type = getColorTypeforColorSpace(layer->paintDevice()->colorSpace(), alpha);
547 return KisImageBuilder_RESULT_UNSUPPORTED;
553 if(!alpha && layer->paintDevice()->colorSpace()->id() == KisID(
"RGBA") )
555 palette =
new png_color[255];
556 KisRectIteratorPixel it = layer->paintDevice()->createRectIterator(0,0, img->width(), img->height(),
false);
557 bool toomuchcolor =
false;
558 while( !it.isDone() )
560 const TQ_UINT8* c = it.rawData();
562 for(
int i = 0; i < num_palette; i++)
564 if(palette[i].red == c[2] &&
565 palette[i].green == c[1] &&
566 palette[i].blue == c[0] )
574 if( num_palette == 255)
579 palette[num_palette].red = c[2];
580 palette[num_palette].green = c[1];
581 palette[num_palette].blue = c[0];
588 kdDebug(41008) <<
"Found a palette of " << num_palette <<
" colors" << endl;
589 color_type = PNG_COLOR_TYPE_PALETTE;
590 if( num_palette <= 2)
593 }
else if( num_palette <= 4)
596 }
else if( num_palette <= 16)
607 int interlacetype = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
609 png_set_IHDR(png_ptr, info_ptr,
613 color_type, interlacetype,
614 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
616 png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
618 if( color_type == PNG_COLOR_TYPE_PALETTE)
620 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
623 vKisAnnotationSP_it it = annotationsStart;
624 while(it != annotationsEnd) {
625 if (!(*it) || (*it) -> type() == TQString()) {
626 kdDebug(41008) <<
"Warning: empty annotation" << endl;
631 kdDebug(41008) <<
"Trying to store annotation of type " << (*it) -> type() <<
" of size " << (*it) -> annotation() . size() << endl;
633 if ((*it) -> type().startsWith(
"chalk_attribute:")) {
635 kdDebug(41008) <<
"can't save this annotation : " << (*it) -> type() << endl;
637 char* name =
new char[(*it)->type().length()+1];
638 strcpy(name, (*it)->type().ascii());
639#if PNG_LIBPNG_VER < 10500
640 png_set_iCCP(png_ptr, info_ptr, name, PNG_COMPRESSION_TYPE_BASE, (png_charp)(*it)->annotation().data(), (*it) -> annotation() . size());
642 png_set_iCCP(png_ptr, info_ptr, name, PNG_COMPRESSION_TYPE_BASE, (png_bytep)(*it)->annotation().data(), (*it) -> annotation() . size());
651 KoDocumentInfo * info = m_doc->documentInfo();
652 KoDocumentInfoAbout * aboutPage =
static_cast<KoDocumentInfoAbout *
>(info->page(
"about" ));
653 TQString title = aboutPage->title();
656 fillText(texts+nbtexts,
"title", title);
659 TQString abstract = aboutPage->abstract();
660 if(!abstract.isEmpty())
662 fillText(texts+nbtexts,
"abstract", abstract);
665 KoDocumentInfoAuthor * authorPage =
static_cast<KoDocumentInfoAuthor *
>(info->page(
"author" ));
666 TQString author = authorPage->fullName();
667 if(!author.isEmpty())
669 fillText(texts+nbtexts,
"author", author);
673 png_set_text(png_ptr, info_ptr, texts, nbtexts);
676 png_write_info(png_ptr, info_ptr);
677 png_write_flush(png_ptr);
680 #ifndef WORDS_BIGENDIAN
681 if (color_nb_bits > 8 )
682 png_set_swap(png_ptr);
689 png_byte** row_pointers=
new png_byte*[height];
691 for (
int y = 0; y < height; y++) {
692 KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width,
false);
693 row_pointers[y] =
new png_byte[width*layer->paintDevice()->pixelSize()];
696 case PNG_COLOR_TYPE_GRAY:
697 case PNG_COLOR_TYPE_GRAY_ALPHA:
698 if(color_nb_bits == 16)
700 TQ_UINT16 *dst =
reinterpret_cast<TQ_UINT16 *
>(row_pointers[y]);
701 while (!it.isDone()) {
702 const TQ_UINT16 *d =
reinterpret_cast<const TQ_UINT16 *
>(it.rawData());
704 if(alpha) *(dst++) = d[1];
708 TQ_UINT8 *dst = row_pointers[y];
709 while (!it.isDone()) {
710 const TQ_UINT8 *d = it.rawData();
712 if(alpha) *(dst++) = d[1];
717 case PNG_COLOR_TYPE_RGB:
718 case PNG_COLOR_TYPE_RGB_ALPHA:
719 if(color_nb_bits == 16)
721 TQ_UINT16 *dst =
reinterpret_cast<TQ_UINT16 *
>(row_pointers[y]);
722 while (!it.isDone()) {
723 const TQ_UINT16 *d =
reinterpret_cast<const TQ_UINT16 *
>(it.rawData());
727 if(alpha) *(dst++) = d[3];
731 TQ_UINT8 *dst = row_pointers[y];
732 while (!it.isDone()) {
733 const TQ_UINT8 *d = it.rawData();
737 if(alpha) *(dst++) = d[3];
742 case PNG_COLOR_TYPE_PALETTE:
744 TQ_UINT8 *dst = row_pointers[y];
745 KisPNGStream writestream(dst, color_nb_bits);
746 while (!it.isDone()) {
747 const TQ_UINT8 *d = it.rawData();
749 for(i = 0; i < num_palette; i++)
751 if(palette[i].red == d[2] &&
752 palette[i].green == d[1] &&
753 palette[i].blue == d[0] )
758 writestream.setNextValue(i);
764 kdDebug(41008) <<
"Unsupported color type for writting : " << color_type << endl;
766 return KisImageBuilder_RESULT_UNSUPPORTED;
770 png_write_image(png_ptr, row_pointers);
774 png_write_end(png_ptr, info_ptr);
777 png_destroy_write_struct(&png_ptr, &info_ptr);
778 for (
int y = 0; y < height; y++) {
779 delete[] row_pointers[y];
781 delete[] row_pointers;
783 if( color_type == PNG_COLOR_TYPE_PALETTE)
790 return KisImageBuilder_RESULT_OK;
794void KisPNGConverter::cancel()
799void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number,
int pass)
801 if(png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7)
return;
806#include "kis_png_converter.moc"