Bug 130483 - PNG export does not allow lower bit depths for smaller images
Summary: PNG export does not allow lower bit depths for smaller images
Status: RESOLVED FIXED
Alias: None
Product: krita
Classification: Applications
Component: General (show other bugs)
Version: unspecified
Platform: unspecified Linux
: NOR normal
Target Milestone: ---
Assignee: Halla Rempt
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-07-08 23:27 UTC by Thomas Zander
Modified: 2006-09-09 01:38 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas Zander 2006-07-08 23:27:34 UTC
Version:           1.5.9 (using KDE 3.5.3, compiled sources)
Compiler:          Target: x86_64-linux-gnu
OS:                Linux (x86_64) release 2.6.13.2

If I have a 3 color picture krita always will it as 24 bit (or 32, I don't know) which creates an image that is a lot bigger in size then it has to be.

I suggest to count the total amount of colors and automatically alter the output format to create the smallest possible size.
Comment 1 Cyrille Berger 2006-09-09 01:37:48 UTC
SVN commit 582298 by berger:

forward port of: analyse the image and try to create a palette to reduce the size of the png
CCBUG:130483


 M  +109 -11   kis_png_converter.cc  


--- trunk/koffice/filters/krita/png/kis_png_converter.cc #582297:582298
@@ -122,7 +122,7 @@
 
 class KisPNGStream {
     public:
-        KisPNGStream(Q_UINT8* buf,  Q_UINT32 depth ) : m_depth(depth), m_posinc(8), m_buf(buf) {};
+        KisPNGStream(quint8* buf,  quint32 depth ) : m_posinc(8),m_depth(depth), m_buf(buf) {};
         int nextValue()
         {
             if( m_posinc == 0)
@@ -133,9 +133,20 @@
             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:
-        Q_UINT32 m_posinc, m_depth;
-        Q_UINT8* m_buf;
+        quint32 m_posinc, m_depth;
+        quint8* m_buf;
 };
 
 KisImageBuilder_Result KisPNGConverter::decode(const KURL& uri)
@@ -342,13 +353,13 @@
                 case PNG_COLOR_TYPE_GRAY_ALPHA:
                     if(color_nb_bits == 16)
                     {
-                        Q_UINT16 *src = reinterpret_cast<Q_UINT16 *>(row_pointer);
+                        quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
                         while (!it.isDone()) {
-                            Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData());
+                            quint16 *d = reinterpret_cast<quint16 *>(it.rawData());
                             d[0] = *(src++);
                             if(transform) cmsDoTransform(transform, d, d, 1);
                             if(hasalpha) d[1] = *(src++);
-                            else d[1] = Q_UINT16_MAX;
+                            else d[1] = quint16_MAX;
                             ++it;
                         }
                     } else {
@@ -368,15 +379,15 @@
                 case PNG_COLOR_TYPE_RGB_ALPHA:
                     if(color_nb_bits == 16)
                     {
-                        Q_UINT16 *src = reinterpret_cast<Q_UINT16 *>(row_pointer);
+                        quint16 *src = reinterpret_cast<quint16 *>(row_pointer);
                         while (!it.isDone()) {
-                            Q_UINT16 *d = reinterpret_cast<Q_UINT16 *>(it.rawData());
+                            quint16 *d = reinterpret_cast<quint16 *>(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] = Q_UINT16_MAX;
+                            else d[3] = quint16_MAX;
                             ++it;
                         }
                     } else {
@@ -522,13 +533,70 @@
     png_set_compression_buffer_size(png_ptr, 8192);
     
     int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
-    int color_type = getColorTypeforColorSpace(img->colorSpace(), alpha);
+    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 Q_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,
@@ -539,7 +607,11 @@
                  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) {
@@ -654,6 +726,27 @@
                     }
                 }
                 break;
+            case PNG_COLOR_TYPE_PALETTE:
+            {
+                Q_UINT8 *dst = row_pointers[y];
+                KisPNGStream writestream(dst, color_nb_bits);
+                while (!it.isDone()) {
+                    const Q_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:
                 KIO::del(uri);
                 return KisImageBuilder_RESULT_UNSUPPORTED;
@@ -672,6 +765,11 @@
     }
     delete[] row_pointers;
 
+    if( color_type == PNG_COLOR_TYPE_PALETTE)
+    {
+        delete palette;
+    }
+
     fclose(fp);
     
     return KisImageBuilder_RESULT_OK;
Comment 2 Cyrille Berger 2006-09-09 01:37:59 UTC
SVN commit 582299 by berger:

analyse the image and try to create a palette to reduce the size of the png
BUG:130483


 M  +104 -5    kis_png_converter.cc  


--- branches/koffice/1.6/koffice/filters/krita/png/kis_png_converter.cc #582298:582299
@@ -123,7 +123,7 @@
 
 class KisPNGStream {
     public:
-        KisPNGStream(Q_UINT8* buf,  Q_UINT32 depth ) : m_depth(depth), m_posinc(8), m_buf(buf) {};
+        KisPNGStream(Q_UINT8* buf,  Q_UINT32 depth ) : m_posinc(8),m_depth(depth), m_buf(buf) {};
         int nextValue()
         {
             if( m_posinc == 0)
@@ -134,6 +134,17 @@
             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:
         Q_UINT32 m_posinc, m_depth;
         Q_UINT8* m_buf;
@@ -455,7 +466,6 @@
     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;
@@ -508,7 +518,7 @@
     // 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);
@@ -521,13 +531,70 @@
     png_set_compression_buffer_size(png_ptr, 8192);
     
     int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
-    int color_type = getColorTypeforColorSpace(img->colorSpace(), alpha);
+    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 Q_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,
@@ -538,7 +605,11 @@
                  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) {
@@ -655,7 +726,29 @@
                     }
                 }
                 break;
+            case PNG_COLOR_TYPE_PALETTE:
+            {
+                Q_UINT8 *dst = row_pointers[y];
+                KisPNGStream writestream(dst, color_nb_bits);
+                while (!it.isDone()) {
+                    const Q_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;
                 KIO::del(uri);
                 return KisImageBuilder_RESULT_UNSUPPORTED;
         }
@@ -663,6 +756,7 @@
     
     png_write_image(png_ptr, row_pointers);
 
+
     // Writting is over
     png_write_end(png_ptr, info_ptr);
     
@@ -673,6 +767,11 @@
     }
     delete[] row_pointers;
 
+    if( color_type == PNG_COLOR_TYPE_PALETTE)
+    {
+        delete palette;
+    }
+
     fclose(fp);
     
     return KisImageBuilder_RESULT_OK;