/* * Copyright (c) 2005-2006 Cyrille Berger * * 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 of the License, 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // 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 #include "kis_png_converter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { const TQ_UINT8 PIXEL_BLUE = 0; const TQ_UINT8 PIXEL_GREEN = 1; const TQ_UINT8 PIXEL_RED = 2; const TQ_UINT8 PIXEL_ALPHA = 3; int getColorTypeforColorSpace( KisColorSpace * cs , bool alpha) { if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) { return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) { return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } KMessageBox::error(0, i18n("Cannot export images in %1.\n").arg(cs->id().name()) ) ; return -1; } TQString getColorSpaceForColorType(int color_type,int color_nb_bits) { if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { switch(color_nb_bits) { case 8: return "GRAYA"; case 16: return "GRAYA16"; } } else if(color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) { switch(color_nb_bits) { case 8: return "RGBA"; case 16: return "RGBA16"; } } else if(color_type == PNG_COLOR_TYPE_PALETTE) { return "RGBA"; // <-- we will convert the index image to RGBA } return ""; } void fillText(png_text* p_text, char* key, TQString& text) { p_text->compression = PNG_TEXT_COMPRESSION_zTXt; p_text->key = key; char* textc = new char[text.length()+1]; strcpy(textc, text.ascii()); p_text->text = textc; p_text->text_length = text.length()+1; } } KisPNGConverter::KisPNGConverter(KisDoc *doc, KisUndoAdapter *adapter) { Q_ASSERT(doc); Q_ASSERT(adapter); m_doc = doc; m_adapter = adapter; m_stop = false; m_max_row = 0; m_img = 0; } KisPNGConverter::~KisPNGConverter() { } class KisPNGStream { public: KisPNGStream(TQ_UINT8* buf, TQ_UINT32 depth ) : m_posinc(8),m_depth(depth), m_buf(buf) { *m_buf = 0;}; int nextValue() { if( m_posinc == 0) { m_posinc = 8; m_buf++; } m_posinc -= m_depth; return (( (*m_buf) >> (m_posinc) ) & ( ( 1 << m_depth ) - 1 ) ); } void setNextValue(int v) { if( m_posinc == 0) { m_posinc = 8; m_buf++; *m_buf = 0; } m_posinc -= m_depth; *m_buf = (v << m_posinc) | *m_buf; } private: TQ_UINT32 m_posinc, m_depth; TQ_UINT8* m_buf; }; KisImageBuilder_Result KisPNGConverter::decode(const KURL& uri) { kdDebug(41008) << "Start decoding PNG File" << endl; // open the file kdDebug(41008) << TQString(TQFile::encodeName(uri.path())) << " " << uri.path() << " " << uri << endl; FILE *fp = fopen(TQFile::encodeName(uri.path()), "rb"); if (!fp) { return (KisImageBuilder_RESULT_NOT_EXIST); } png_byte signature[8]; fread(signature, 1, 8, fp); if (!png_check_sig(signature, 8)) { return (KisImageBuilder_RESULT_BAD_FETCH); } // Initialize the internal structures png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); if (!KisImageBuilder_RESULT_FAILURE) return (KisImageBuilder_RESULT_FAILURE); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return (KisImageBuilder_RESULT_FAILURE); } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return (KisImageBuilder_RESULT_FAILURE); } // Catch errors if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(fp); return (KisImageBuilder_RESULT_FAILURE); } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); // read all PNG info up to image data png_read_info(png_ptr, info_ptr); // Read information about the png png_uint_32 width, height; int color_nb_bits, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, NULL, NULL); kdDebug(41008) << "it's an " << color_nb_bits << " depth image" << endl; // swap byteorder on little endian machines. #ifndef WORDS_BIGENDIAN if (color_nb_bits > 8 ) png_set_swap(png_ptr); #endif // Determine the colorspace TQString csName = getColorSpaceForColorType(color_type, color_nb_bits); if(csName.isEmpty()) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA); // Read image profile png_charp profile_name, profile_data; #if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 ) unsigned char* uprofile_data = reinterpret_cast(profile_data); #endif int compression_type; png_uint_32 proflen; int number_of_passes = 1; if (interlace_type == PNG_INTERLACE_ADAM7) number_of_passes = png_set_interlace_handling(png_ptr); KisProfile* profile = 0; #if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 ) if(png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &uprofile_data, &proflen)) #else if(png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen)) #endif { TQByteArray profile_rawdata; // XXX: Hardcoded for icc type -- is that correct for us? if (TQString::compare(profile_name, "icc") == 0) { profile_rawdata.resize(proflen); memcpy(profile_rawdata.data(), profile_data, proflen); profile = new KisProfile(profile_rawdata); TQ_CHECK_PTR(profile); if (profile) { kdDebug(41008) << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo() << endl; if(!profile->isSuitableForOutput()) { 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 } } } } // Retrieve a pointer to the colorspace KisColorSpace* cs; if (profile && profile->isSuitableForOutput()) { kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n"; cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile); } else cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),""); if(cs == 0) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } // Create the cmsTransform if needed cmsHTRANSFORM transform = 0; if(profile && !profile->isSuitableForOutput()) { transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(), cs->getProfile()->profile() , cs->colorSpaceType(), INTENT_PERCEPTUAL, 0); } // Read comments/texts... png_text* text_ptr; int num_comments; png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments); KoDocumentInfo * info = m_doc->documentInfo(); KoDocumentInfoAbout * aboutPage = static_cast(info->page( "about" )); KoDocumentInfoAuthor * authorPage = static_cast(info->page( "author")); kdDebug(41008) << "There are " << num_comments << " comments in the text" << endl; for(int i = 0; i < num_comments; i++) { kdDebug(41008) << "key is " << text_ptr[i].key << " containing " << text_ptr[i].text << endl; if(TQString::compare(text_ptr[i].key, "title") == 0) { aboutPage->setTitle(text_ptr[i].text); } else if(TQString::compare(text_ptr[i].key, "abstract") == 0) { aboutPage->setAbstract(text_ptr[i].text); } else if(TQString::compare(text_ptr[i].key, "author") == 0) { authorPage->setFullName(text_ptr[i].text); } } // Read image data png_bytep row_pointer = 0; try { png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr); row_pointer = new png_byte[rowbytes]; } catch(std::bad_alloc& e) { // new png_byte[] may raise such an exception if the image // is invalid / to large. kdDebug(41008) << "bad alloc: " << e.what() << endl; // Free only the already allocated png_byte instances. png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (KisImageBuilder_RESULT_FAILURE); } // Read the palette if the file is indexed png_colorp palette ; int num_palette; if(color_type == PNG_COLOR_TYPE_PALETTE) { png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); } // png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL ); // png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); // By using this function libpng will take care of freeing memory // png_read_image(png_ptr, row_pointers); // Finish reading the file // png_read_end(png_ptr, end_info); // fclose(fp); // Creating the KisImageSP if( ! m_img) { m_img = new KisImage(m_doc->undoAdapter(), width, height, cs, "built image"); m_img->blockSignals(true); // Don't send out signals while we're building the image TQ_CHECK_PTR(m_img); if(profile && !profile->isSuitableForOutput()) { m_img -> addAnnotation( profile->annotation() ); } } KisPaintLayer* layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), TQ_UINT8_MAX); for (int i = 0; i < number_of_passes; i++) { for (png_uint_32 y = 0; y < height; y++) { KisHLineIterator it = layer -> paintDevice() -> createHLineIterator(0, y, width, true); png_read_rows(png_ptr, &row_pointer, NULL, 1); switch(color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if(color_nb_bits == 16) { TQ_UINT16 *src = reinterpret_cast(row_pointer); while (!it.isDone()) { TQ_UINT16 *d = reinterpret_cast(it.rawData()); d[0] = *(src++); if(transform) cmsDoTransform(transform, d, d, 1); if(hasalpha) d[1] = *(src++); else d[1] = TQ_UINT16_MAX; ++it; } } else { TQ_UINT8 *src = row_pointer; while (!it.isDone()) { TQ_UINT8 *d = it.rawData(); d[0] = *(src++); if(transform) cmsDoTransform(transform, d, d, 1); if(hasalpha) d[1] = *(src++); else d[1] = TQ_UINT8_MAX; ++it; } } //FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if(color_nb_bits == 16) { TQ_UINT16 *src = reinterpret_cast(row_pointer); while (!it.isDone()) { TQ_UINT16 *d = reinterpret_cast(it.rawData()); d[2] = *(src++); d[1] = *(src++); d[0] = *(src++); if(transform) cmsDoTransform(transform, d, d, 1); if(hasalpha) d[3] = *(src++); else d[3] = TQ_UINT16_MAX; ++it; } } else { TQ_UINT8 *src = row_pointer; while (!it.isDone()) { TQ_UINT8 *d = it.rawData(); d[2] = *(src++); d[1] = *(src++); d[0] = *(src++); if(transform) cmsDoTransform(transform, d, d, 1); if(hasalpha) d[3] = *(src++); else d[3] = TQ_UINT8_MAX; ++it; } } break; case PNG_COLOR_TYPE_PALETTE: { KisPNGStream stream(row_pointer, color_nb_bits); while (!it.isDone()) { TQ_UINT8 *d = it.rawData(); png_color c = palette[ stream.nextValue() ]; d[2] = c.red; d[1] = c.green; d[0] = c.blue; d[3] = TQ_UINT8_MAX; ++it; } } break; default: return KisImageBuilder_RESULT_UNSUPPORTED; } } } m_img->addLayer(layer, m_img->rootLayer(), 0); png_read_end(png_ptr, end_info); fclose(fp); // Freeing memory png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); delete [] row_pointer; return KisImageBuilder_RESULT_OK; } KisImageBuilder_Result KisPNGConverter::buildImage(const KURL& uri) { kdDebug(41008) << TQString(TQFile::encodeName(uri.path())) << " " << uri.path() << " " << uri << endl; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!TDEIO::NetAccess::exists(uri, false, tqApp -> mainWidget())) { return KisImageBuilder_RESULT_NOT_EXIST; } // We're not set up to handle asynchronous loading at the moment. KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE; TQString tmpFile; if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) { KURL uriTF; uriTF.setPath( tmpFile ); result = decode(uriTF); TDEIO::NetAccess::removeTempFile(tmpFile); } return result; } KisImageSP KisPNGConverter::image() { return m_img; } KisImageBuilder_Result KisPNGConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, int compression, bool interlace, bool alpha) { kdDebug(41008) << "Start writing PNG File" << endl; if (!layer) return KisImageBuilder_RESULT_INVALID_ARG; KisImageSP img = layer -> image(); if (!img) return KisImageBuilder_RESULT_EMPTY; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_LOCAL; // Open file for writing FILE *fp = fopen(TQFile::encodeName(uri.path()), "wb"); if (!fp) { return (KisImageBuilder_RESULT_FAILURE); } int height = img->height(); int width = img->width(); // Initialize structures png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); if (!png_ptr) { TDEIO::del(uri); return (KisImageBuilder_RESULT_FAILURE); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { TDEIO::del(uri); png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return (KisImageBuilder_RESULT_FAILURE); } // If an error occurs during writing, libpng will jump here if (setjmp(png_jmpbuf(png_ptr))) { TDEIO::del(uri); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return (KisImageBuilder_RESULT_FAILURE); } // Initialize the writing png_init_io(png_ptr, fp); // Setup the progress function // FIXME png_set_write_status_fn(png_ptr, progress); // setProgressTotalSteps(100/*height*/); /* set the zlib compression level */ png_set_compression_level(png_ptr, compression); /* set other zlib parameters */ png_set_compression_mem_level(png_ptr, 8); png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); png_set_compression_window_bits(png_ptr, 15); png_set_compression_method(png_ptr, 8); png_set_compression_buffer_size(png_ptr, 8192); int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels(); int color_type = getColorTypeforColorSpace(layer->paintDevice()->colorSpace(), alpha); if(color_type == -1) { return KisImageBuilder_RESULT_UNSUPPORTED; } // Try to compute a table of color if the colorspace is RGB8f png_colorp palette ; int num_palette = 0; if(!alpha && layer->paintDevice()->colorSpace()->id() == KisID("RGBA") ) { // png doesn't handle indexed images and alpha, and only have indexed for RGB8 palette = new png_color[255]; KisRectIteratorPixel it = layer->paintDevice()->createRectIterator(0,0, img->width(), img->height(), false); bool toomuchcolor = false; while( !it.isDone() ) { const TQ_UINT8* c = it.rawData(); bool findit = false; for(int i = 0; i < num_palette; i++) { if(palette[i].red == c[2] && palette[i].green == c[1] && palette[i].blue == c[0] ) { findit = true; break; } } if(!findit) { if( num_palette == 255) { toomuchcolor = true; break; } palette[num_palette].red = c[2]; palette[num_palette].green = c[1]; palette[num_palette].blue = c[0]; num_palette++; } ++it; } if(!toomuchcolor) { kdDebug(41008) << "Found a palette of " << num_palette << " colors" << endl; color_type = PNG_COLOR_TYPE_PALETTE; if( num_palette <= 2) { color_nb_bits = 1; } else if( num_palette <= 4) { color_nb_bits = 2; } else if( num_palette <= 16) { color_nb_bits = 4; } else { color_nb_bits = 8; } } else { delete palette; } } int interlacetype = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR(png_ptr, info_ptr, width, height, color_nb_bits, color_type, interlacetype, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); // set the palette if( color_type == PNG_COLOR_TYPE_PALETTE) { png_set_PLTE(png_ptr, info_ptr, palette, num_palette); } // Save annotation vKisAnnotationSP_it it = annotationsStart; while(it != annotationsEnd) { if (!(*it) || (*it) -> type() == TQString()) { kdDebug(41008) << "Warning: empty annotation" << endl; ++it; continue; } kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl; if ((*it) -> type().startsWith("chalk_attribute:")) { // Attribute // FIXME: it should be possible to save chalk_attributes in the "CHUNKs" kdDebug(41008) << "can't save this annotation : " << (*it) -> type() << endl; } else { // Profile char* name = new char[(*it)->type().length()+1]; strcpy(name, (*it)->type().ascii()); #if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 ) png_set_iCCP(png_ptr, info_ptr, name, PNG_COMPRESSION_TYPE_BASE, (unsigned char*)(*it)->annotation().data(), (*it) -> annotation() . size()); #else png_set_iCCP(png_ptr, info_ptr, name, PNG_COMPRESSION_TYPE_BASE, (char*)(*it)->annotation().data(), (*it) -> annotation() . size()); #endif } ++it; } // read comments from the document information png_text texts[3]; int nbtexts = 0; KoDocumentInfo * info = m_doc->documentInfo(); KoDocumentInfoAbout * aboutPage = static_cast(info->page( "about" )); TQString title = aboutPage->title(); if(!title.isEmpty()) { fillText(texts+nbtexts, "title", title); nbtexts++; } TQString abstract = aboutPage->abstract(); if(!abstract.isEmpty()) { fillText(texts+nbtexts, "abstract", abstract); nbtexts++; } KoDocumentInfoAuthor * authorPage = static_cast(info->page( "author" )); TQString author = authorPage->fullName(); if(!author.isEmpty()) { fillText(texts+nbtexts, "author", author); nbtexts++; } png_set_text(png_ptr, info_ptr, texts, nbtexts); // Save the information to the file png_write_info(png_ptr, info_ptr); png_write_flush(png_ptr); // swap byteorder on little endian machines. #ifndef WORDS_BIGENDIAN if (color_nb_bits > 8 ) png_set_swap(png_ptr); #endif // Write the PNG // png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); // Fill the data structure png_byte** row_pointers= new png_byte*[height]; for (int y = 0; y < height; y++) { KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false); row_pointers[y] = new png_byte[width*layer->paintDevice()->pixelSize()]; switch(color_type) { case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if(color_nb_bits == 16) { TQ_UINT16 *dst = reinterpret_cast(row_pointers[y]); while (!it.isDone()) { const TQ_UINT16 *d = reinterpret_cast(it.rawData()); *(dst++) = d[0]; if(alpha) *(dst++) = d[1]; ++it; } } else { TQ_UINT8 *dst = row_pointers[y]; while (!it.isDone()) { const TQ_UINT8 *d = it.rawData(); *(dst++) = d[0]; if(alpha) *(dst++) = d[1]; ++it; } } break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if(color_nb_bits == 16) { TQ_UINT16 *dst = reinterpret_cast(row_pointers[y]); while (!it.isDone()) { const TQ_UINT16 *d = reinterpret_cast(it.rawData()); *(dst++) = d[2]; *(dst++) = d[1]; *(dst++) = d[0]; if(alpha) *(dst++) = d[3]; ++it; } } else { TQ_UINT8 *dst = row_pointers[y]; while (!it.isDone()) { const TQ_UINT8 *d = it.rawData(); *(dst++) = d[2]; *(dst++) = d[1]; *(dst++) = d[0]; if(alpha) *(dst++) = d[3]; ++it; } } break; case PNG_COLOR_TYPE_PALETTE: { TQ_UINT8 *dst = row_pointers[y]; KisPNGStream writestream(dst, color_nb_bits); while (!it.isDone()) { const TQ_UINT8 *d = it.rawData(); int i; for(i = 0; i < num_palette; i++) { if(palette[i].red == d[2] && palette[i].green == d[1] && palette[i].blue == d[0] ) { break; } } writestream.setNextValue(i); ++it; } } break; default: kdDebug(41008) << "Unsupported color type for writting : " << color_type << endl; TDEIO::del(uri); return KisImageBuilder_RESULT_UNSUPPORTED; } } png_write_image(png_ptr, row_pointers); // Writting is over png_write_end(png_ptr, info_ptr); // Free memory png_destroy_write_struct(&png_ptr, &info_ptr); for (int y = 0; y < height; y++) { delete[] row_pointers[y]; } delete[] row_pointers; if( color_type == PNG_COLOR_TYPE_PALETTE) { delete palette; } fclose(fp); return KisImageBuilder_RESULT_OK; } void KisPNGConverter::cancel() { m_stop = true; } void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass) { if(png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) return; // setProgress(row_number); } #include "kis_png_converter.moc"