filters

kis_png_converter.cpp
1/*
2 * Copyright (c) 2005-2006 Cyrille Berger <cberger@cberger.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 // A big thank to Glenn Randers-Pehrson for it's wonderfull documentation of libpng available at http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
21#include "kis_png_converter.h"
22
23#include <stdio.h>
24
25#include <tqfile.h>
26
27#include <tdeapplication.h>
28#include <tdemessagebox.h>
29#include <tdelocale.h>
30
31#include <KoDocumentInfo.h>
32
33#include <tdeio/netaccess.h>
34
35#include <kis_abstract_colorspace.h>
36#include <kis_colorspace_factory_registry.h>
37#include <kis_doc.h>
38#include <kis_image.h>
39#include <kis_iterators_pixel.h>
40#include <kis_layer.h>
41#include <kis_meta_registry.h>
42#include <kis_profile.h>
43#include <kis_paint_layer.h>
44#include <kis_group_layer.h>
45
46#include <zlib.h>
47
48namespace {
49
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;
54
55
56 int getColorTypeforColorSpace( KisColorSpace * cs , bool alpha)
57 {
58 if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") )
59 {
60 return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY;
61 }
62 if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") )
63 {
64 return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
65 }
66
67 KMessageBox::error(0, i18n("Cannot export images in %1.\n").arg(cs->id().name()) ) ;
68 return -1;
69
70 }
71
72
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)
75 {
76 switch(color_nb_bits)
77 {
78 case 8:
79 return "GRAYA";
80 case 16:
81 return "GRAYA16";
82 }
83 } else if(color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) {
84 switch(color_nb_bits)
85 {
86 case 8:
87 return "RGBA";
88 case 16:
89 return "RGBA16";
90 }
91 } else if(color_type == PNG_COLOR_TYPE_PALETTE) {
92 return "RGBA"; // <-- we will convert the index image to RGBA
93 }
94 return "";
95 }
96
97
98 void fillText(png_text* p_text, char* key, TQString& text)
99 {
100 p_text->compression = PNG_TEXT_COMPRESSION_zTXt;
101 p_text->key = key;
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;
106 }
107
108}
109
110KisPNGConverter::KisPNGConverter(KisDoc *doc, KisUndoAdapter *adapter)
111{
112 Q_ASSERT(doc);
113 Q_ASSERT(adapter);
114
115 m_doc = doc;
116 m_adapter = adapter;
117 m_stop = false;
118 m_max_row = 0;
119 m_img = 0;
120}
121
122KisPNGConverter::~KisPNGConverter()
123{
124}
125
126class KisPNGStream {
127 public:
128 KisPNGStream(TQ_UINT8* buf, TQ_UINT32 depth ) : m_posinc(8),m_depth(depth), m_buf(buf) { *m_buf = 0;};
129 int nextValue()
130 {
131 if( m_posinc == 0)
132 {
133 m_posinc = 8;
134 m_buf++;
135 }
136 m_posinc -= m_depth;
137 return (( (*m_buf) >> (m_posinc) ) & ( ( 1 << m_depth ) - 1 ) );
138 }
139 void setNextValue(int v)
140 {
141 if( m_posinc == 0)
142 {
143 m_posinc = 8;
144 m_buf++;
145 *m_buf = 0;
146 }
147 m_posinc -= m_depth;
148 *m_buf = (v << m_posinc) | *m_buf;
149 }
150 private:
151 TQ_UINT32 m_posinc, m_depth;
152 TQ_UINT8* m_buf;
153};
154
155KisImageBuilder_Result KisPNGConverter::decode(const KURL& uri)
156{
157 kdDebug(41008) << "Start decoding PNG File" << endl;
158 // open the file
159 kdDebug(41008) << TQString(TQFile::encodeName(uri.path())) << " " << uri.path() << " " << uri << endl;
160 FILE *fp = fopen(TQFile::encodeName(uri.path()), "rb");
161 if (!fp)
162 {
163 return (KisImageBuilder_RESULT_NOT_EXIST);
164 }
165 png_byte signature[8];
166 fread(signature, 1, 8, fp);
167 if (!png_check_sig(signature, 8))
168 {
169 return (KisImageBuilder_RESULT_BAD_FETCH);
170 }
171
172 // Initialize the internal structures
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);
176
177 png_infop info_ptr = png_create_info_struct(png_ptr);
178 if (!info_ptr)
179 {
180 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
181 return (KisImageBuilder_RESULT_FAILURE);
182 }
183
184 png_infop end_info = png_create_info_struct(png_ptr);
185 if (!end_info)
186 {
187 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
188 return (KisImageBuilder_RESULT_FAILURE);
189 }
190
191 // Catch errors
192 if (setjmp(png_jmpbuf(png_ptr)))
193 {
194 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
195 fclose(fp);
196 return (KisImageBuilder_RESULT_FAILURE);
197 }
198
199 png_init_io(png_ptr, fp);
200 png_set_sig_bytes(png_ptr, 8);
201
202 // read all PNG info up to image data
203 png_read_info(png_ptr, info_ptr);
204
205 // Read information about the png
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;
210
211 // swap byteorder on little endian machines.
212 #ifndef WORDS_BIGENDIAN
213 if (color_nb_bits > 8 )
214 png_set_swap(png_ptr);
215 #endif
216
217 // Determine the colorspace
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;
222 }
223 bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA);
224
225 int number_of_passes = 1;
226 if (interlace_type == PNG_INTERLACE_ADAM7)
227 number_of_passes = png_set_interlace_handling(png_ptr);
228
229 // Read image profile
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;
235#else
236 png_bytep profile_data;
237#endif
238 int compression_type;
239 png_uint_32 proflen;
240
241 if (png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen))
242 {
243 TQByteArray profile_rawdata;
244 // XXX: Hardcoded for icc type -- is that correct for us?
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);
250 if (profile) {
251 kdDebug(41008) << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo() << endl;
252 if(!profile->isSuitableForOutput())
253 {
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; // TODO: in ko2 popup a selection menu to inform the user
255 }
256 }
257 }
258 }
259 }
260
261 // Retrieve a pointer to the colorspace
262 KisColorSpace* cs;
263 if (profile && profile->isSuitableForOutput())
264 {
265 kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
266 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
267 }
268 else
269 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
270
271 if(cs == 0)
272 {
273 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
274 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
275 }
276
277 // Create the cmsTransform if needed
278 cmsHTRANSFORM transform = 0;
279 if(profile && !profile->isSuitableForOutput())
280 {
281 transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(),
282 cs->getProfile()->profile() , cs->colorSpaceType(),
283 INTENT_PERCEPTUAL, 0);
284 }
285
286 // Read comments/texts...
287 png_text* text_ptr;
288 int num_comments;
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++)
295 {
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)
298 {
299 aboutPage->setTitle(text_ptr[i].text);
300 } else if(TQString::compare(text_ptr[i].key, "abstract") == 0)
301 {
302 aboutPage->setAbstract(text_ptr[i].text);
303 } else if(TQString::compare(text_ptr[i].key, "author") == 0)
304 {
305 authorPage->setFullName(text_ptr[i].text);
306 }
307 }
308
309 // Read image data
310 png_bytep row_pointer = 0;
311 try
312 {
313 png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
314 row_pointer = new png_byte[rowbytes];
315 }
316 catch(std::bad_alloc& e)
317 {
318 // new png_byte[] may raise such an exception if the image
319 // is invalid / to large.
320 kdDebug(41008) << "bad alloc: " << e.what() << endl;
321 // Free only the already allocated png_byte instances.
322 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
323 return (KisImageBuilder_RESULT_FAILURE);
324 }
325
326 // Read the palette if the file is indexed
327 png_colorp palette ;
328 int num_palette;
329 if(color_type == PNG_COLOR_TYPE_PALETTE) {
330 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
331 }
332// png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL );
333// png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); // By using this function libpng will take care of freeing memory
334// png_read_image(png_ptr, row_pointers);
335
336 // Finish reading the file
337// png_read_end(png_ptr, end_info);
338// fclose(fp);
339
340 // Creating the KisImageSP
341 if( ! m_img) {
342 m_img = new KisImage(m_doc->undoAdapter(), width, height, cs, "built image");
343 m_img->blockSignals(true); // Don't send out signals while we're building the image
344 TQ_CHECK_PTR(m_img);
345 if(profile && !profile->isSuitableForOutput())
346 {
347 m_img -> addAnnotation( profile->annotation() );
348 }
349 }
350
351 KisPaintLayer* layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), TQ_UINT8_MAX);
352 for (int i = 0; i < number_of_passes; i++)
353 {
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);
357
358 switch(color_type)
359 {
360 case PNG_COLOR_TYPE_GRAY:
361 case PNG_COLOR_TYPE_GRAY_ALPHA:
362 if(color_nb_bits == 16)
363 {
364 TQ_UINT16 *src = reinterpret_cast<TQ_UINT16 *>(row_pointer);
365 while (!it.isDone()) {
366 TQ_UINT16 *d = reinterpret_cast<TQ_UINT16 *>(it.rawData());
367 d[0] = *(src++);
368 if(transform) cmsDoTransform(transform, d, d, 1);
369 if(hasalpha) d[1] = *(src++);
370 else d[1] = TQ_UINT16_MAX;
371 ++it;
372 }
373 } else {
374 TQ_UINT8 *src = row_pointer;
375 while (!it.isDone()) {
376 TQ_UINT8 *d = it.rawData();
377 d[0] = *(src++);
378 if(transform) cmsDoTransform(transform, d, d, 1);
379 if(hasalpha) d[1] = *(src++);
380 else d[1] = TQ_UINT8_MAX;
381 ++it;
382 }
383 }
384 //FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits
385 break;
386 case PNG_COLOR_TYPE_RGB:
387 case PNG_COLOR_TYPE_RGB_ALPHA:
388 if(color_nb_bits == 16)
389 {
390 TQ_UINT16 *src = reinterpret_cast<TQ_UINT16 *>(row_pointer);
391 while (!it.isDone()) {
392 TQ_UINT16 *d = reinterpret_cast<TQ_UINT16 *>(it.rawData());
393 d[2] = *(src++);
394 d[1] = *(src++);
395 d[0] = *(src++);
396 if(transform) cmsDoTransform(transform, d, d, 1);
397 if(hasalpha) d[3] = *(src++);
398 else d[3] = TQ_UINT16_MAX;
399 ++it;
400 }
401 } else {
402 TQ_UINT8 *src = row_pointer;
403 while (!it.isDone()) {
404 TQ_UINT8 *d = it.rawData();
405 d[2] = *(src++);
406 d[1] = *(src++);
407 d[0] = *(src++);
408 if(transform) cmsDoTransform(transform, d, d, 1);
409 if(hasalpha) d[3] = *(src++);
410 else d[3] = TQ_UINT8_MAX;
411 ++it;
412 }
413 }
414 break;
415 case PNG_COLOR_TYPE_PALETTE:
416 {
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() ];
421 d[2] = c.red;
422 d[1] = c.green;
423 d[0] = c.blue;
424 d[3] = TQ_UINT8_MAX;
425 ++it;
426 }
427 }
428 break;
429 default:
430 return KisImageBuilder_RESULT_UNSUPPORTED;
431 }
432 }
433 }
434 m_img->addLayer(layer, m_img->rootLayer(), 0);
435
436 png_read_end(png_ptr, end_info);
437 fclose(fp);
438
439 // Freeing memory
440 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
441
442 delete [] row_pointer;
443
444 return KisImageBuilder_RESULT_OK;
445
446}
447
448KisImageBuilder_Result KisPNGConverter::buildImage(const KURL& uri)
449{
450 kdDebug(41008) << TQString(TQFile::encodeName(uri.path())) << " " << uri.path() << " " << uri << endl;
451 if (uri.isEmpty())
452 return KisImageBuilder_RESULT_NO_URI;
453
454 if (!TDEIO::NetAccess::exists(uri, false, tqApp -> mainWidget())) {
455 return KisImageBuilder_RESULT_NOT_EXIST;
456 }
457
458 // We're not set up to handle asynchronous loading at the moment.
459 KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
460 TQString tmpFile;
461
462 if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) {
463 KURL uriTF;
464 uriTF.setPath( tmpFile );
465 result = decode(uriTF);
466 TDEIO::NetAccess::removeTempFile(tmpFile);
467 }
468
469 return result;
470}
471
472
473KisImageSP KisPNGConverter::image()
474{
475 return m_img;
476}
477
478KisImageBuilder_Result KisPNGConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, int compression, bool interlace, bool alpha)
479{
480 kdDebug(41008) << "Start writing PNG File" << endl;
481 if (!layer)
482 return KisImageBuilder_RESULT_INVALID_ARG;
483
484 KisImageSP img = layer -> image();
485 if (!img)
486 return KisImageBuilder_RESULT_EMPTY;
487
488 if (uri.isEmpty())
489 return KisImageBuilder_RESULT_NO_URI;
490
491 if (!uri.isLocalFile())
492 return KisImageBuilder_RESULT_NOT_LOCAL;
493 // Open file for writing
494 FILE *fp = fopen(TQFile::encodeName(uri.path()), "wb");
495 if (!fp)
496 {
497 return (KisImageBuilder_RESULT_FAILURE);
498 }
499 int height = img->height();
500 int width = img->width();
501 // Initialize structures
502 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
503 if (!png_ptr)
504 {
505 TDEIO::del(uri);
506 return (KisImageBuilder_RESULT_FAILURE);
507 }
508
509 png_infop info_ptr = png_create_info_struct(png_ptr);
510 if (!info_ptr)
511 {
512 TDEIO::del(uri);
513 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
514 return (KisImageBuilder_RESULT_FAILURE);
515 }
516
517 // If an error occurs during writing, libpng will jump here
518 if (setjmp(png_jmpbuf(png_ptr)))
519 {
520 TDEIO::del(uri);
521 png_destroy_write_struct(&png_ptr, &info_ptr);
522 fclose(fp);
523 return (KisImageBuilder_RESULT_FAILURE);
524 }
525 // Initialize the writing
526 png_init_io(png_ptr, fp);
527 // Setup the progress function
528// FIXME png_set_write_status_fn(png_ptr, progress);
529// setProgressTotalSteps(100/*height*/);
530
531
532 /* set the zlib compression level */
533 png_set_compression_level(png_ptr, compression);
534
535 /* set other zlib parameters */
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);
541
542 int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
543 int color_type = getColorTypeforColorSpace(layer->paintDevice()->colorSpace(), alpha);
544
545 if(color_type == -1)
546 {
547 return KisImageBuilder_RESULT_UNSUPPORTED;
548 }
549
550 // Try to compute a table of color if the colorspace is RGB8f
551 png_colorp palette ;
552 int num_palette = 0;
553 if(!alpha && layer->paintDevice()->colorSpace()->id() == KisID("RGBA") )
554 { // png doesn't handle indexed images and alpha, and only have indexed for RGB8
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() )
559 {
560 const TQ_UINT8* c = it.rawData();
561 bool findit = false;
562 for(int i = 0; i < num_palette; i++)
563 {
564 if(palette[i].red == c[2] &&
565 palette[i].green == c[1] &&
566 palette[i].blue == c[0] )
567 {
568 findit = true;
569 break;
570 }
571 }
572 if(!findit)
573 {
574 if( num_palette == 255)
575 {
576 toomuchcolor = true;
577 break;
578 }
579 palette[num_palette].red = c[2];
580 palette[num_palette].green = c[1];
581 palette[num_palette].blue = c[0];
582 num_palette++;
583 }
584 ++it;
585 }
586 if(!toomuchcolor)
587 {
588 kdDebug(41008) << "Found a palette of " << num_palette << " colors" << endl;
589 color_type = PNG_COLOR_TYPE_PALETTE;
590 if( num_palette <= 2)
591 {
592 color_nb_bits = 1;
593 } else if( num_palette <= 4)
594 {
595 color_nb_bits = 2;
596 } else if( num_palette <= 16)
597 {
598 color_nb_bits = 4;
599 } else {
600 color_nb_bits = 8;
601 }
602 } else {
603 delete palette;
604 }
605 }
606
607 int interlacetype = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
608
609 png_set_IHDR(png_ptr, info_ptr,
610 width,
611 height,
612 color_nb_bits,
613 color_type, interlacetype,
614 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
615
616 png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
617 // set the palette
618 if( color_type == PNG_COLOR_TYPE_PALETTE)
619 {
620 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
621 }
622 // Save annotation
623 vKisAnnotationSP_it it = annotationsStart;
624 while(it != annotationsEnd) {
625 if (!(*it) || (*it) -> type() == TQString()) {
626 kdDebug(41008) << "Warning: empty annotation" << endl;
627 ++it;
628 continue;
629 }
630
631 kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl;
632
633 if ((*it) -> type().startsWith("chalk_attribute:")) { // Attribute
634 // FIXME: it should be possible to save chalk_attributes in the "CHUNKs"
635 kdDebug(41008) << "can't save this annotation : " << (*it) -> type() << endl;
636 } else { // Profile
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());
641#else
642 png_set_iCCP(png_ptr, info_ptr, name, PNG_COMPRESSION_TYPE_BASE, (png_bytep)(*it)->annotation().data(), (*it) -> annotation() . size());
643#endif
644 }
645 ++it;
646 }
647
648 // read comments from the document information
649 png_text texts[3];
650 int nbtexts = 0;
651 KoDocumentInfo * info = m_doc->documentInfo();
652 KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
653 TQString title = aboutPage->title();
654 if(!title.isEmpty())
655 {
656 fillText(texts+nbtexts, "title", title);
657 nbtexts++;
658 }
659 TQString abstract = aboutPage->abstract();
660 if(!abstract.isEmpty())
661 {
662 fillText(texts+nbtexts, "abstract", abstract);
663 nbtexts++;
664 }
665 KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
666 TQString author = authorPage->fullName();
667 if(!author.isEmpty())
668 {
669 fillText(texts+nbtexts, "author", author);
670 nbtexts++;
671 }
672
673 png_set_text(png_ptr, info_ptr, texts, nbtexts);
674
675 // Save the information to the file
676 png_write_info(png_ptr, info_ptr);
677 png_write_flush(png_ptr);
678
679 // swap byteorder on little endian machines.
680 #ifndef WORDS_BIGENDIAN
681 if (color_nb_bits > 8 )
682 png_set_swap(png_ptr);
683 #endif
684
685 // Write the PNG
686// png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
687
688 // Fill the data structure
689 png_byte** row_pointers= new png_byte*[height];
690
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()];
694 switch(color_type)
695 {
696 case PNG_COLOR_TYPE_GRAY:
697 case PNG_COLOR_TYPE_GRAY_ALPHA:
698 if(color_nb_bits == 16)
699 {
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());
703 *(dst++) = d[0];
704 if(alpha) *(dst++) = d[1];
705 ++it;
706 }
707 } else {
708 TQ_UINT8 *dst = row_pointers[y];
709 while (!it.isDone()) {
710 const TQ_UINT8 *d = it.rawData();
711 *(dst++) = d[0];
712 if(alpha) *(dst++) = d[1];
713 ++it;
714 }
715 }
716 break;
717 case PNG_COLOR_TYPE_RGB:
718 case PNG_COLOR_TYPE_RGB_ALPHA:
719 if(color_nb_bits == 16)
720 {
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());
724 *(dst++) = d[2];
725 *(dst++) = d[1];
726 *(dst++) = d[0];
727 if(alpha) *(dst++) = d[3];
728 ++it;
729 }
730 } else {
731 TQ_UINT8 *dst = row_pointers[y];
732 while (!it.isDone()) {
733 const TQ_UINT8 *d = it.rawData();
734 *(dst++) = d[2];
735 *(dst++) = d[1];
736 *(dst++) = d[0];
737 if(alpha) *(dst++) = d[3];
738 ++it;
739 }
740 }
741 break;
742 case PNG_COLOR_TYPE_PALETTE:
743 {
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();
748 int i;
749 for(i = 0; i < num_palette; i++)
750 {
751 if(palette[i].red == d[2] &&
752 palette[i].green == d[1] &&
753 palette[i].blue == d[0] )
754 {
755 break;
756 }
757 }
758 writestream.setNextValue(i);
759 ++it;
760 }
761 }
762 break;
763 default:
764 kdDebug(41008) << "Unsupported color type for writting : " << color_type << endl;
765 TDEIO::del(uri);
766 return KisImageBuilder_RESULT_UNSUPPORTED;
767 }
768 }
769
770 png_write_image(png_ptr, row_pointers);
771
772
773 // Writting is over
774 png_write_end(png_ptr, info_ptr);
775
776 // Free memory
777 png_destroy_write_struct(&png_ptr, &info_ptr);
778 for (int y = 0; y < height; y++) {
779 delete[] row_pointers[y];
780 }
781 delete[] row_pointers;
782
783 if( color_type == PNG_COLOR_TYPE_PALETTE)
784 {
785 delete palette;
786 }
787
788 fclose(fp);
789
790 return KisImageBuilder_RESULT_OK;
791}
792
793
794void KisPNGConverter::cancel()
795{
796 m_stop = true;
797}
798
799void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass)
800{
801 if(png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) return;
802// setProgress(row_number);
803}
804
805
806#include "kis_png_converter.moc"
807