filters

kis_png_converter.cc

00001 /*
00002  *  Copyright (c) 2005-2006 Cyrille Berger <cberger@cberger.net>
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  */
00019  
00020  // 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
00021 #include "kis_png_converter.h"
00022 
00023 #include <stdio.h>
00024 
00025 #include <qfile.h>
00026 
00027 #include <kapplication.h>
00028 #include <kmessagebox.h>
00029 #include <klocale.h>
00030 
00031 #include <KoDocumentInfo.h>
00032 
00033 #include <kio/netaccess.h>
00034 
00035 #include <kis_abstract_colorspace.h>
00036 #include <kis_colorspace_factory_registry.h>
00037 #include <kis_doc.h>
00038 #include <kis_image.h>
00039 #include <kis_iterators_pixel.h>
00040 #include <kis_layer.h>
00041 #include <kis_meta_registry.h>
00042 #include <kis_profile.h>
00043 #include <kis_paint_layer.h>
00044 #include <kis_group_layer.h>
00045 
00046 namespace {
00047 
00048     const Q_UINT8 PIXEL_BLUE = 0;
00049     const Q_UINT8 PIXEL_GREEN = 1;
00050     const Q_UINT8 PIXEL_RED = 2;
00051     const Q_UINT8 PIXEL_ALPHA = 3;
00052 
00053     
00054     int getColorTypeforColorSpace( KisColorSpace * cs , bool alpha)
00055     {
00056         if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") )
00057         {
00058             return alpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY;
00059         }
00060         if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") )
00061         {
00062             return alpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
00063         }
00064 
00065         KMessageBox::error(0, i18n("Cannot export images in %1.\n").arg(cs->id().name()) ) ;
00066         return -1;
00067 
00068     }
00069 
00070     
00071     QString getColorSpaceForColorType(int color_type,int color_nb_bits) {
00072         if(color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
00073         {
00074             switch(color_nb_bits)
00075             {
00076                 case 8:
00077                     return "GRAYA";
00078                 case 16:
00079                     return "GRAYA16";
00080             }
00081         } else if(color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_RGB) {
00082             switch(color_nb_bits)
00083             {
00084                 case 8:
00085                     return "RGBA";
00086                 case 16:
00087                     return "RGBA16";
00088             }
00089         } else if(color_type ==  PNG_COLOR_TYPE_PALETTE) {
00090             return "RGBA"; // <-- we will convert the index image to RGBA
00091         }
00092         return "";
00093     }
00094 
00095 
00096     void fillText(png_text* p_text, char* key, QString& text)
00097     {
00098         p_text->compression = PNG_TEXT_COMPRESSION_zTXt;
00099         p_text->key = key;
00100         char* textc = new char[text.length()+1];
00101         strcpy(textc, text.ascii());
00102         p_text->text = textc;
00103         p_text->text_length = text.length()+1;
00104     }
00105 
00106 }
00107 
00108 KisPNGConverter::KisPNGConverter(KisDoc *doc, KisUndoAdapter *adapter)
00109 {
00110     Q_ASSERT(doc);
00111     Q_ASSERT(adapter);
00112     
00113     m_doc = doc;
00114     m_adapter = adapter;
00115     m_stop = false;
00116     m_max_row = 0;
00117     m_img = 0;
00118 }
00119 
00120 KisPNGConverter::~KisPNGConverter()
00121 {
00122 }
00123 
00124 class KisPNGStream {
00125     public:
00126         KisPNGStream(Q_UINT8* buf,  Q_UINT32 depth ) : m_posinc(8),m_depth(depth), m_buf(buf) { *m_buf = 0;};
00127         int nextValue()
00128         {
00129             if( m_posinc == 0)
00130             {
00131                 m_posinc = 8;
00132                 m_buf++;
00133             }
00134             m_posinc -= m_depth;
00135             return (( (*m_buf) >> (m_posinc) ) & ( ( 1 << m_depth ) - 1 ) );
00136         }
00137         void setNextValue(int v)
00138         {
00139             if( m_posinc == 0)
00140             {
00141                 m_posinc = 8;
00142                 m_buf++;
00143                 *m_buf = 0;
00144             }
00145             m_posinc -= m_depth;
00146             *m_buf = (v << m_posinc) | *m_buf;
00147         }
00148     private:
00149         Q_UINT32 m_posinc, m_depth;
00150         Q_UINT8* m_buf;
00151 };
00152 
00153 KisImageBuilder_Result KisPNGConverter::decode(const KURL& uri)
00154 {
00155     kdDebug(41008) << "Start decoding PNG File" << endl;
00156     // open the file
00157     kdDebug(41008) << QFile::encodeName(uri.path()) << " " << uri.path() << " " << uri << endl;
00158     FILE *fp = fopen(QFile::encodeName(uri.path()), "rb");
00159     if (!fp)
00160     {
00161         return (KisImageBuilder_RESULT_NOT_EXIST);
00162     }
00163     png_byte signature[8];
00164     fread(signature, 1, 8, fp);
00165     if (!png_check_sig(signature, 8))
00166     {
00167         return (KisImageBuilder_RESULT_BAD_FETCH);
00168     }
00169 
00170     // Initialize the internal structures
00171     png_structp png_ptr =  png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
00172     if (!KisImageBuilder_RESULT_FAILURE)
00173         return (KisImageBuilder_RESULT_FAILURE);
00174 
00175     png_infop info_ptr = png_create_info_struct(png_ptr);
00176     if (!info_ptr)
00177     {
00178         png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00179         return (KisImageBuilder_RESULT_FAILURE);
00180     }
00181 
00182     png_infop end_info = png_create_info_struct(png_ptr);
00183     if (!end_info)
00184     {
00185         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00186         return (KisImageBuilder_RESULT_FAILURE);
00187     }
00188 
00189     // Catch errors
00190     if (setjmp(png_jmpbuf(png_ptr)))
00191     {
00192         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00193         fclose(fp);
00194         return (KisImageBuilder_RESULT_FAILURE);
00195     }
00196     
00197     png_init_io(png_ptr, fp);
00198     png_set_sig_bytes(png_ptr, 8);
00199     
00200     // read all PNG info up to image data
00201     png_read_info(png_ptr, info_ptr);
00202 
00203     // Read information about the png
00204     png_uint_32 width, height;
00205     int color_nb_bits, color_type, interlace_type;
00206     png_get_IHDR(png_ptr, info_ptr, &width, &height, &color_nb_bits, &color_type, &interlace_type, NULL, NULL);
00207     kdDebug(41008) << "it's an " << color_nb_bits << " depth image" << endl;
00208 
00209     // swap byteorder on little endian machines.
00210     #ifndef WORDS_BIGENDIAN
00211     if (color_nb_bits > 8 )
00212         png_set_swap(png_ptr);
00213     #endif
00214 
00215     // Determine the colorspace
00216     QString csName = getColorSpaceForColorType(color_type, color_nb_bits);
00217     if(csName.isEmpty()) {
00218         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00219         return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
00220     }
00221     bool hasalpha = (color_type == PNG_COLOR_TYPE_RGB_ALPHA || color_type == PNG_COLOR_TYPE_GRAY_ALPHA);
00222     
00223     // Read image profile
00224     png_charp profile_name, profile_data;
00225     int compression_type;
00226     png_uint_32 proflen;
00227     int number_of_passes = 1;
00228 
00229     if (interlace_type == PNG_INTERLACE_ADAM7)
00230         number_of_passes = png_set_interlace_handling(png_ptr);
00231 
00232     KisProfile* profile = 0;
00233     if(png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &proflen))
00234     {
00235         QByteArray profile_rawdata;
00236         // XXX: Hardcoded for icc type -- is that correct for us?
00237         if (QString::compare(profile_name, "icc") == 0) {
00238             profile_rawdata.resize(proflen);
00239             memcpy(profile_rawdata.data(), profile_data, proflen);
00240             profile = new KisProfile(profile_rawdata);
00241             Q_CHECK_PTR(profile);
00242             if (profile) {
00243                 kdDebug(41008) << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo() << endl;
00244                 if(!profile->isSuitableForOutput())
00245                 {
00246                     kdDebug(41008) << "the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile" << endl; // TODO: in ko2 popup a selection menu to inform the user
00247                 }
00248             }
00249         }
00250     }
00251 
00252     // Retrieve a pointer to the colorspace
00253     KisColorSpace* cs;
00254     if (profile && profile->isSuitableForOutput())
00255     {
00256         kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
00257         cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
00258     }
00259     else
00260         cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
00261 
00262     if(cs == 0)
00263     {
00264         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00265         return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
00266     }
00267     
00268     // Create the cmsTransform if needed 
00269     cmsHTRANSFORM transform = 0;
00270     if(profile && !profile->isSuitableForOutput())
00271     {
00272         transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(),
00273                                        cs->getProfile()->profile() , cs->colorSpaceType(),
00274                                        INTENT_PERCEPTUAL, 0);
00275     }
00276 
00277     // Read comments/texts...
00278     png_text* text_ptr;
00279     int num_comments;
00280     png_get_text(png_ptr, info_ptr, &text_ptr, &num_comments);
00281     KoDocumentInfo * info = m_doc->documentInfo();
00282     KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
00283     KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author"));
00284     kdDebug(41008) << "There are " << num_comments << " comments in the text" << endl;
00285     for(int i = 0; i < num_comments; i++)
00286     {
00287         kdDebug(41008) << "key is " << text_ptr[i].key << " containing " << text_ptr[i].text << endl;
00288         if(QString::compare(text_ptr[i].key, "title") == 0)
00289         {
00290                 aboutPage->setTitle(text_ptr[i].text);
00291         } else if(QString::compare(text_ptr[i].key, "abstract")  == 0)
00292         {
00293                 aboutPage->setAbstract(text_ptr[i].text);
00294         } else if(QString::compare(text_ptr[i].key, "author") == 0)
00295         {
00296                 authorPage->setFullName(text_ptr[i].text);
00297         }
00298     }
00299     
00300     // Read image data
00301     png_bytep row_pointer = 0;
00302     try
00303     {
00304         png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
00305         row_pointer = new png_byte[rowbytes];
00306     }
00307     catch(std::bad_alloc& e)
00308     {
00309         // new png_byte[] may raise such an exception if the image
00310         // is invalid / to large.
00311         kdDebug(41008) << "bad alloc: " << e.what() << endl;
00312         // Free only the already allocated png_byte instances.
00313         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00314         return (KisImageBuilder_RESULT_FAILURE);
00315     }
00316     
00317     // Read the palette if the file is indexed
00318     png_colorp palette ;
00319     int num_palette;
00320     if(color_type == PNG_COLOR_TYPE_PALETTE) {
00321         png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
00322     }
00323 //     png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL );
00324 //     png_bytepp row_pointers = png_get_rows(png_ptr, info_ptr); // By using this function libpng will take care of freeing memory
00325 //    png_read_image(png_ptr, row_pointers);
00326     
00327     // Finish reading the file
00328 //    png_read_end(png_ptr, end_info);
00329 //    fclose(fp);
00330     
00331     // Creating the KisImageSP
00332     if( ! m_img) {
00333         m_img = new KisImage(m_doc->undoAdapter(), width, height, cs, "built image");
00334         m_img->blockSignals(true); // Don't send out signals while we're building the image
00335         Q_CHECK_PTR(m_img);
00336         if(profile && !profile->isSuitableForOutput())
00337         {
00338             m_img -> addAnnotation( profile->annotation() );
00339         }
00340     }
00341 
00342     KisPaintLayer* layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), Q_UINT8_MAX);
00343     for (int i = 0; i < number_of_passes; i++)
00344     {
00345         for (png_uint_32 y = 0; y < height; y++) {
00346             KisHLineIterator it = layer -> paintDevice() -> createHLineIterator(0, y, width, true);
00347             png_read_rows(png_ptr, &row_pointer, NULL, 1);
00348 
00349             switch(color_type)
00350             {
00351                 case PNG_COLOR_TYPE_GRAY:
00352                 case PNG_COLOR_TYPE_GRAY_ALPHA:
00353                     if(color_nb_bits == 16)
00354                     {
00355                         Q_UINT16 *src = reinterpret_cast<Q_UINT16 *>(row_pointer);
00356                         while (!it.isDone()) {
00357                             Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData());
00358                             d[0] = *(src++);
00359                             if(transform) cmsDoTransform(transform, d, d, 1);
00360                             if(hasalpha) d[1] = *(src++);
00361                             else d[1] = Q_UINT16_MAX;
00362                             ++it;
00363                         }
00364                     } else {
00365                         Q_UINT8 *src = row_pointer;
00366                         while (!it.isDone()) {
00367                             Q_UINT8 *d = it.rawData();
00368                             d[0] = *(src++);
00369                             if(transform) cmsDoTransform(transform, d, d, 1);
00370                             if(hasalpha) d[1] = *(src++);
00371                             else d[1] = Q_UINT8_MAX;
00372                             ++it;
00373                         }
00374                     }
00375                 //FIXME:should be able to read 1 and 4 bits depth and scale them to 8 bits
00376                     break;
00377                 case PNG_COLOR_TYPE_RGB:
00378                 case PNG_COLOR_TYPE_RGB_ALPHA:
00379                     if(color_nb_bits == 16)
00380                     {
00381                         Q_UINT16 *src = reinterpret_cast<Q_UINT16 *>(row_pointer);
00382                         while (!it.isDone()) {
00383                             Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData());
00384                             d[2] = *(src++);
00385                             d[1] = *(src++);
00386                             d[0] = *(src++);
00387                             if(transform) cmsDoTransform(transform, d, d, 1);
00388                             if(hasalpha) d[3] = *(src++);
00389                             else d[3] = Q_UINT16_MAX;
00390                             ++it;
00391                         }
00392                     } else {
00393                         Q_UINT8 *src = row_pointer;
00394                         while (!it.isDone()) {
00395                             Q_UINT8 *d = it.rawData();
00396                             d[2] = *(src++);
00397                             d[1] = *(src++);
00398                             d[0] = *(src++);
00399                             if(transform) cmsDoTransform(transform, d, d, 1);
00400                             if(hasalpha) d[3] = *(src++);
00401                             else d[3] = Q_UINT8_MAX;
00402                             ++it;
00403                         }
00404                     }
00405                     break;
00406                 case PNG_COLOR_TYPE_PALETTE:
00407                     {
00408                         KisPNGStream stream(row_pointer, color_nb_bits);
00409                         while (!it.isDone()) {
00410                             Q_UINT8 *d = it.rawData();
00411                             png_color c = palette[ stream.nextValue() ];
00412                             d[2] = c.red;
00413                             d[1] = c.green;
00414                             d[0] = c.blue;
00415                             d[3] = Q_UINT8_MAX;
00416                             ++it;
00417                         }
00418                     }
00419                     break;
00420                 default:
00421                     return KisImageBuilder_RESULT_UNSUPPORTED;
00422             }
00423         }
00424     }
00425     m_img->addLayer(layer, m_img->rootLayer(), 0);
00426 
00427     png_read_end(png_ptr, end_info);
00428     fclose(fp);
00429 
00430     // Freeing memory
00431     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00432 
00433     delete [] row_pointer;
00434 
00435     return KisImageBuilder_RESULT_OK;
00436 
00437 }
00438 
00439 KisImageBuilder_Result KisPNGConverter::buildImage(const KURL& uri)
00440 {
00441     kdDebug(41008) << QFile::encodeName(uri.path()) << " " << uri.path() << " " << uri << endl;
00442     if (uri.isEmpty())
00443         return KisImageBuilder_RESULT_NO_URI;
00444 
00445     if (!KIO::NetAccess::exists(uri, false, qApp -> mainWidget())) {
00446         return KisImageBuilder_RESULT_NOT_EXIST;
00447     }
00448 
00449     // We're not set up to handle asynchronous loading at the moment.
00450     KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
00451     QString tmpFile;
00452 
00453     if (KIO::NetAccess::download(uri, tmpFile, qApp -> mainWidget())) {
00454         KURL uriTF;
00455         uriTF.setPath( tmpFile );
00456         result = decode(uriTF);
00457         KIO::NetAccess::removeTempFile(tmpFile);
00458     }
00459 
00460     return result;
00461 }
00462 
00463 
00464 KisImageSP KisPNGConverter::image()
00465 {
00466     return m_img;
00467 }
00468 
00469 KisImageBuilder_Result KisPNGConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, int compression, bool interlace, bool alpha)
00470 {
00471     kdDebug(41008) << "Start writing PNG File" << endl;
00472     if (!layer)
00473         return KisImageBuilder_RESULT_INVALID_ARG;
00474 
00475     KisImageSP img = layer -> image();
00476     if (!img)
00477         return KisImageBuilder_RESULT_EMPTY;
00478 
00479     if (uri.isEmpty())
00480         return KisImageBuilder_RESULT_NO_URI;
00481 
00482     if (!uri.isLocalFile())
00483         return KisImageBuilder_RESULT_NOT_LOCAL;
00484     // Open file for writing
00485     FILE *fp = fopen(QFile::encodeName(uri.path()), "wb");
00486     if (!fp)
00487     {
00488         return (KisImageBuilder_RESULT_FAILURE);
00489     }
00490     int height = img->height();
00491     int width = img->width();
00492     // Initialize structures
00493     png_structp png_ptr =  png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL);
00494     if (!png_ptr)
00495     {
00496         KIO::del(uri);
00497         return (KisImageBuilder_RESULT_FAILURE);
00498     }
00499 
00500     png_infop info_ptr = png_create_info_struct(png_ptr);
00501     if (!info_ptr)
00502     {
00503         KIO::del(uri);
00504         png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
00505         return (KisImageBuilder_RESULT_FAILURE);
00506     }
00507 
00508     // If an error occurs during writing, libpng will jump here
00509     if (setjmp(png_jmpbuf(png_ptr)))
00510     {
00511         KIO::del(uri);
00512         png_destroy_write_struct(&png_ptr, &info_ptr);
00513         fclose(fp);
00514         return (KisImageBuilder_RESULT_FAILURE);
00515     }
00516     // Initialize the writing
00517     png_init_io(png_ptr, fp);
00518     // Setup the progress function
00519 // FIXME    png_set_write_status_fn(png_ptr, progress);
00520 //     setProgressTotalSteps(100/*height*/);
00521     
00522 
00523     /* set the zlib compression level */
00524     png_set_compression_level(png_ptr, compression);
00525 
00526     /* set other zlib parameters */
00527     png_set_compression_mem_level(png_ptr, 8);
00528     png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
00529     png_set_compression_window_bits(png_ptr, 15);
00530     png_set_compression_method(png_ptr, 8);
00531     png_set_compression_buffer_size(png_ptr, 8192);
00532     
00533     int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
00534     int color_type = getColorTypeforColorSpace(layer->paintDevice()->colorSpace(), alpha);
00535     
00536     if(color_type == -1)
00537     {
00538         return KisImageBuilder_RESULT_UNSUPPORTED;
00539     }
00540     
00541     // Try to compute a table of color if the colorspace is RGB8f
00542     png_colorp palette ;
00543     int num_palette = 0;
00544     if(!alpha && layer->paintDevice()->colorSpace()->id() == KisID("RGBA") )
00545     { // png doesn't handle indexed images and alpha, and only have indexed for RGB8
00546         palette = new png_color[255];
00547         KisRectIteratorPixel it = layer->paintDevice()->createRectIterator(0,0, img->width(), img->height(), false);
00548         bool toomuchcolor = false;
00549         while( !it.isDone() )
00550         {
00551             const Q_UINT8* c = it.rawData();
00552             bool findit = false;
00553             for(int i = 0; i < num_palette; i++)
00554             {
00555                 if(palette[i].red == c[2] &&
00556                    palette[i].green == c[1] &&
00557                    palette[i].blue == c[0] )
00558                 {
00559                     findit = true;
00560                     break;
00561                 }
00562             }
00563             if(!findit)
00564             {
00565                 if( num_palette == 255)
00566                 {
00567                     toomuchcolor = true;
00568                     break;
00569                 }
00570                 palette[num_palette].red = c[2];
00571                 palette[num_palette].green = c[1];
00572                 palette[num_palette].blue = c[0];
00573                 num_palette++;
00574             }
00575             ++it;
00576         }
00577         if(!toomuchcolor)
00578         {
00579             kdDebug(41008) << "Found a palette of " << num_palette << " colors" << endl;
00580             color_type = PNG_COLOR_TYPE_PALETTE;
00581             if( num_palette <= 2)
00582             {
00583                 color_nb_bits = 1;
00584             } else if( num_palette <= 4)
00585             {
00586                 color_nb_bits = 2;
00587             } else if( num_palette <= 16)
00588             {
00589                 color_nb_bits = 4;
00590             } else {
00591                 color_nb_bits = 8;
00592             }
00593         } else {
00594             delete palette;
00595         }
00596     }
00597     
00598     int interlacetype = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
00599     
00600     png_set_IHDR(png_ptr, info_ptr,
00601                  width,
00602                  height,
00603                  color_nb_bits,
00604                  color_type, interlacetype,
00605                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00606     
00607     png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
00608     // set the palette
00609     if( color_type == PNG_COLOR_TYPE_PALETTE)
00610     {
00611         png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
00612     }
00613     // Save annotation
00614     vKisAnnotationSP_it it = annotationsStart;
00615     while(it != annotationsEnd) {
00616         if (!(*it) || (*it) -> type() == QString()) {
00617             kdDebug(41008) << "Warning: empty annotation" << endl;
00618             ++it;
00619             continue;
00620         }
00621 
00622         kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl;
00623 
00624         if ((*it) -> type().startsWith("krita_attribute:")) { // Attribute
00625             // FIXME: it should be possible to save krita_attributes in the "CHUNKs"
00626             kdDebug(41008) << "can't save this annotation : " << (*it) -> type() << endl;
00627         } else { // Profile
00628             char* name = new char[(*it)->type().length()+1];
00629             strcpy(name, (*it)->type().ascii());
00630             png_set_iCCP(png_ptr, info_ptr, name, PNG_COMPRESSION_TYPE_BASE, (char*)(*it)->annotation().data(), (*it) -> annotation() . size());
00631         }
00632         ++it;
00633     }
00634 
00635     // read comments from the document information
00636     png_text texts[3];
00637     int nbtexts = 0;
00638     KoDocumentInfo * info = m_doc->documentInfo();
00639     KoDocumentInfoAbout * aboutPage = static_cast<KoDocumentInfoAbout *>(info->page( "about" ));
00640     QString title = aboutPage->title();
00641     if(!title.isEmpty())
00642     {
00643         fillText(texts+nbtexts, "title", title);
00644         nbtexts++;
00645     }
00646     QString abstract = aboutPage->abstract();
00647     if(!abstract.isEmpty())
00648     {
00649         fillText(texts+nbtexts, "abstract", abstract);
00650         nbtexts++;
00651     }
00652     KoDocumentInfoAuthor * authorPage = static_cast<KoDocumentInfoAuthor *>(info->page( "author" ));
00653     QString author = authorPage->fullName();
00654     if(!author.isEmpty())
00655     {
00656         fillText(texts+nbtexts, "author", author);
00657         nbtexts++;
00658     }
00659     
00660     png_set_text(png_ptr, info_ptr, texts, nbtexts);
00661     
00662     // Save the information to the file
00663     png_write_info(png_ptr, info_ptr);
00664     png_write_flush(png_ptr);
00665 
00666     // swap byteorder on little endian machines.
00667     #ifndef WORDS_BIGENDIAN
00668     if (color_nb_bits > 8 )
00669         png_set_swap(png_ptr);
00670     #endif
00671 
00672     // Write the PNG
00673 //     png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
00674     
00675     // Fill the data structure
00676     png_byte** row_pointers= new png_byte*[height];
00677     
00678     for (int y = 0; y < height; y++) {
00679         KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false);
00680         row_pointers[y] = new png_byte[width*layer->paintDevice()->pixelSize()];
00681         switch(color_type)
00682         {
00683             case PNG_COLOR_TYPE_GRAY:
00684             case PNG_COLOR_TYPE_GRAY_ALPHA:
00685                 if(color_nb_bits == 16)
00686                 {
00687                     Q_UINT16 *dst = reinterpret_cast<Q_UINT16 *>(row_pointers[y]);
00688                     while (!it.isDone()) {
00689                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00690                         *(dst++) = d[0];
00691                         if(alpha) *(dst++) = d[1];
00692                         ++it;
00693                     }
00694                 } else {
00695                     Q_UINT8 *dst = row_pointers[y];
00696                     while (!it.isDone()) {
00697                         const Q_UINT8 *d = it.rawData();
00698                         *(dst++) = d[0];
00699                         if(alpha) *(dst++) = d[1];
00700                         ++it;
00701                     }
00702                 }
00703                 break;
00704             case PNG_COLOR_TYPE_RGB:
00705             case PNG_COLOR_TYPE_RGB_ALPHA:
00706                 if(color_nb_bits == 16)
00707                 {
00708                     Q_UINT16 *dst = reinterpret_cast<Q_UINT16 *>(row_pointers[y]);
00709                     while (!it.isDone()) {
00710                         const Q_UINT16 *d = reinterpret_cast<const Q_UINT16 *>(it.rawData());
00711                         *(dst++) = d[2];
00712                         *(dst++) = d[1];
00713                         *(dst++) = d[0];
00714                         if(alpha) *(dst++) = d[3];
00715                         ++it;
00716                     }
00717                 } else {
00718                     Q_UINT8 *dst = row_pointers[y];
00719                     while (!it.isDone()) {
00720                         const Q_UINT8 *d = it.rawData();
00721                         *(dst++) = d[2];
00722                         *(dst++) = d[1];
00723                         *(dst++) = d[0];
00724                         if(alpha) *(dst++) = d[3];
00725                         ++it;
00726                     }
00727                 }
00728                 break;
00729             case PNG_COLOR_TYPE_PALETTE:
00730             {
00731                 Q_UINT8 *dst = row_pointers[y];
00732                 KisPNGStream writestream(dst, color_nb_bits);
00733                 while (!it.isDone()) {
00734                     const Q_UINT8 *d = it.rawData();
00735                     int i;
00736                     for(i = 0; i < num_palette; i++)
00737                     {
00738                         if(palette[i].red == d[2] &&
00739                            palette[i].green == d[1] &&
00740                            palette[i].blue == d[0] )
00741                         {
00742                             break;
00743                         }
00744                     }
00745                     writestream.setNextValue(i);
00746                     ++it;
00747                 }
00748             }
00749             break;
00750             default:
00751                 kdDebug(41008) << "Unsupported color type for writting : " << color_type << endl;
00752                 KIO::del(uri);
00753                 return KisImageBuilder_RESULT_UNSUPPORTED;
00754         }
00755     }
00756     
00757     png_write_image(png_ptr, row_pointers);
00758 
00759 
00760     // Writting is over
00761     png_write_end(png_ptr, info_ptr);
00762     
00763     // Free memory
00764     png_destroy_write_struct(&png_ptr, &info_ptr);
00765     for (int y = 0; y < height; y++) {
00766         delete[] row_pointers[y];
00767     }
00768     delete[] row_pointers;
00769 
00770     if( color_type == PNG_COLOR_TYPE_PALETTE)
00771     {
00772         delete palette;
00773     }
00774 
00775     fclose(fp);
00776     
00777     return KisImageBuilder_RESULT_OK;
00778 }
00779 
00780 
00781 void KisPNGConverter::cancel()
00782 {
00783     m_stop = true;
00784 }
00785 
00786 void KisPNGConverter::progress(png_structp png_ptr, png_uint_32 row_number, int pass)
00787 {
00788     if(png_ptr == NULL || row_number > PNG_MAX_UINT || pass > 7) return;
00789 //     setProgress(row_number);
00790 }
00791 
00792 
00793 #include "kis_png_converter.moc"
00794 
KDE Home | KDE Accessibility Home | Description of Access Keys