Changeset 1680

Show
Ignore:
Timestamp:
03/11/10 18:16:17 (5 months ago)
Author:
dane
Message:

apply mapnik-0.7.1.mr.hextree2.diff (slighly modified to accept format='png:key=val' as well as format='png256:key=val' while still maintaining default behavior of just format 'png' or 'png256' - also updated rundemo.py with a few examples

Location:
branches/0.7.1-dev
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • branches/0.7.1-dev/demo/python/rundemo.py

    r1542 r1680  
    329329im.save('demo.png', 'png') # true-colour RGBA 
    330330images_.append('demo.png') 
    331 im.save('demo256.png', 'png256') # save to palette based (max 256 colours) png  
     331 
     332# old behavior, now can do 'png:c=256' 
     333im.save('demo256.png', 'png256') # save to palette based (max 256 colours) png 
    332334images_.append('demo256.png') 
     335 
     336im.save('demo64_binary_transparency.png', 'png:c=64:t=1') 
     337images_.append('demo64_binary_transparency.png') 
     338 
     339im.save('demo128_colors_hextree_no_alpha.png', 'png:c=100:m=h:t=0') 
     340images_.append('demo128_colors_hextree_no_alpha.png') 
     341 
    333342im.save('demo_high.jpg', 'jpeg100') 
    334343images_.append('demo_high.jpg') 
     344 
    335345im.save('demo_low.jpg', 'jpeg50') 
    336346images_.append('demo_low.jpg') 
  • branches/0.7.1-dev/include/mapnik/octree.hpp

    r1663 r1680  
    4444         byte g; 
    4545         byte b; 
    46          rgb(byte r_, byte b_, byte g_) 
     46         rgb(byte r_, byte g_, byte b_) 
    4747            : r(r_), g(g_), b(b_) {} 
    4848   }; 
  • branches/0.7.1-dev/include/mapnik/png_io.hpp

    r1661 r1680  
    2424#include <mapnik/global.hpp> 
    2525#include <mapnik/octree.hpp> 
     26#include <mapnik/hextree.hpp> 
    2627#include <mapnik/global.hpp> 
    2728 
     
    3132} 
    3233 
    33 // TODO - consider exposing this option to user 
    34 // static number of alpha ranges in png256 format 
    35 // 2 results in smallest image, 3 is minimum for semitransparency, 4 is recommended, anything else is worse 
    36 #define TRANSPARENCY_LEVELS 4 
     34#define MAX_OCTREE_LEVELS 4 
    3735 
    3836#ifdef MAPNIK_BIG_ENDIAN 
     
    6361      out->flush(); 
    6462   } 
    65     
     63 
    6664   template <typename T1, typename T2> 
    6765   void save_as_png(T1 & file , T2 const& image) 
     
    110108 
    111109   template <typename T> 
    112    void reduce_8  (T const& in, ImageData8 & out, octree<rgb> trees[], unsigned limits[], std::vector<unsigned> & alpha) 
     110   void reduce_8  (T const& in, ImageData8 & out, octree<rgb> trees[], unsigned limits[], unsigned levels, std::vector<unsigned> & alpha) 
    113111   { 
    114112      unsigned width = in.width(); 
     
    133131            byte index = 0; 
    134132            int idx = -1; 
    135             for(int j=TRANSPARENCY_LEVELS-1; j>0; j--){ 
     133            for(int j=levels-1; j>0; j--){ 
    136134               if (U2ALPHA(val)>=limits[j]) { 
    137135                  index = idx = trees[j].quantize(c); 
     
    155153 
    156154   template <typename T> 
    157    void reduce_4 (T const& in, ImageData8 & out, octree<rgb> trees[], unsigned limits[], std::vector<unsigned> & alpha) 
     155   void reduce_4 (T const& in, ImageData8 & out, octree<rgb> trees[], unsigned limits[], unsigned levels, std::vector<unsigned> & alpha) 
    158156   { 
    159157      unsigned width = in.width(); 
     
    179177            byte index = 0; 
    180178            int idx=-1; 
    181             for(int j=TRANSPARENCY_LEVELS-1; j>0; j--){ 
     179            for(int j=levels-1; j>0; j--){ 
    182180               if (U2ALPHA(val)>=limits[j]) { 
    183181                  index = idx = trees[j].quantize(c); 
     
    275273 
    276274   template <typename T1,typename T2> 
    277    void save_as_png256(T1 & file, T2 const& image) 
    278    { 
     275   void save_as_png256(T1 & file, T2 const& image, const unsigned max_colors = 256, int trans_mode = -1) 
     276   { 
     277      // number of alpha ranges in png256 format; 2 results in smallest image with binary transparency 
     278      // 3 is minimum for semitransparency, 4 is recommended, anything else is worse 
     279      const unsigned TRANSPARENCY_LEVELS = (trans_mode==2||trans_mode<0)?MAX_OCTREE_LEVELS:2; 
    279280      unsigned width = image.width(); 
    280281      unsigned height = image.height(); 
     
    288289         for (unsigned x = 0; x < width; ++x){ 
    289290            unsigned val = U2ALPHA((unsigned)image.getRow(y)[x]); 
     291            if (trans_mode==0) 
     292               val=255; 
    290293            alphaHist[val]++; 
    291294            meanAlpha += val; 
     
    297300 
    298301      // transparency ranges division points 
    299       unsigned limits[TRANSPARENCY_LEVELS+1]; 
     302      unsigned limits[MAX_OCTREE_LEVELS+1]; 
    300303      limits[0] = 0; 
    301304      limits[1] = (alphaHist[0]>0)?1:0; 
    302305      limits[TRANSPARENCY_LEVELS] = 256; 
    303306      unsigned alphaHistSum = 0; 
    304       for(int j=1; j<TRANSPARENCY_LEVELS; j++) 
     307      for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) 
    305308         limits[j] = limits[1]; 
    306       for(int i=1; i<256; i++){ 
     309      for(unsigned i=1; i<256; i++){ 
    307310          alphaHistSum += alphaHist[i]; 
    308           for(int j=1; j<TRANSPARENCY_LEVELS; j++){ 
     311          for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++){ 
    309312              if (alphaHistSum<semiCount*(j)/4) 
    310313                limits[j] = i; 
     
    321324      } 
    322325      // estimated number of colors from palette assigned to chosen ranges 
    323       unsigned cols[TRANSPARENCY_LEVELS]; 
     326      unsigned cols[MAX_OCTREE_LEVELS]; 
    324327      // count colors 
    325       for(int j=1; j<=TRANSPARENCY_LEVELS; j++) { 
     328      for(unsigned j=1; j<=TRANSPARENCY_LEVELS; j++) { 
    326329         cols[j-1] = 0; 
    327330         for(unsigned i=limits[j-1]; i<limits[j]; i++){ 
     
    334337      cols[0] = cols[0]>0?1:0; // fully transparent color (one or not at all) 
    335338 
    336       // give chance less populated but not empty cols to have at least few colors(12) 
    337       unsigned minCols = (12+1)*divCoef/(256-cols[0]); 
    338       for(int j=1; j<TRANSPARENCY_LEVELS; j++) if (cols[j]>12 && cols[j]<minCols) { 
    339          divCoef += minCols-cols[j]; 
    340          cols[j] = minCols; 
     339      if (max_colors>=64) { 
     340          // give chance less populated but not empty cols to have at least few colors(12) 
     341          unsigned minCols = (12+1)*divCoef/(max_colors-cols[0]); 
     342          for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) if (cols[j]>12 && cols[j]<minCols) { 
     343             divCoef += minCols-cols[j]; 
     344             cols[j] = minCols; 
     345          } 
    341346      } 
    342347      unsigned usedColors = cols[0]; 
    343       for(int j=1; j<TRANSPARENCY_LEVELS-1; j++){ 
    344          cols[j] = cols[j]*(256-cols[0])/divCoef; 
     348      for(unsigned j=1; j<TRANSPARENCY_LEVELS-1; j++){ 
     349         cols[j] = cols[j]*(max_colors-cols[0])/divCoef; 
    345350         usedColors += cols[j]; 
    346351      } 
    347352      // use rest for most opaque group of pixels 
    348       cols[TRANSPARENCY_LEVELS-1] = 256-usedColors; 
     353      cols[TRANSPARENCY_LEVELS-1] = max_colors-usedColors; 
     354 
     355      //no transparency 
     356      if (trans_mode == 0) 
     357      { 
     358         limits[1] = 0; 
     359         cols[0] = 0; 
     360         cols[1] = max_colors; 
     361      } 
    349362 
    350363      // octree table for separate alpha range with 1-based index (0 is fully transparent: no color) 
    351       octree<rgb> trees[TRANSPARENCY_LEVELS]; 
    352       for(int j=1; j<TRANSPARENCY_LEVELS; j++) 
     364      octree<rgb> trees[MAX_OCTREE_LEVELS]; 
     365      for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) 
    353366         trees[j].setMaxColors(cols[j]); 
    354367      for (unsigned y = 0; y < height; ++y) 
     
    360373             
    361374            // insert to proper tree based on alpha range 
    362             for(int j=TRANSPARENCY_LEVELS-1; j>0; j--){ 
     375            for(unsigned j=TRANSPARENCY_LEVELS-1; j>0; j--){ 
    363376               if (cols[j]>0 && U2ALPHA(val)>=limits[j]) { 
    364377                  trees[j].insert(mapnik::rgb(U2RED(val), U2GREEN(val), U2BLUE(val))); 
     
    370383      unsigned leftovers = 0; 
    371384      std::vector<rgb> palette; 
    372       palette.reserve(256); 
     385      palette.reserve(max_colors); 
    373386      if (cols[0]) 
    374387         palette.push_back(rgb(0,0,0)); 
    375388 
    376       for(int j=1; j<TRANSPARENCY_LEVELS; j++) { 
     389      for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { 
    377390         if (cols[j]>0) { 
    378391            if (leftovers>0) { 
     
    384397            trees[j].setOffset(palette.size()); 
    385398            trees[j].create_palette(pal); 
    386             assert(pal.size() <= 256); 
     399            assert(pal.size() <= max_colors); 
    387400            leftovers = cols[j]-pal.size(); 
    388401            cols[j] = pal.size(); 
     
    397410      std::vector<unsigned> alphaTable; 
    398411      //alphaTable.resize(palette.size());//allow semitransparency also in almost opaque range 
    399       alphaTable.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]); 
     412      if (trans_mode != 0) 
     413         alphaTable.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]); 
    400414       
    401415      if (palette.size() > 16 ) 
     
    403417         // >16 && <=256 colors -> write 8-bit color depth 
    404418         ImageData8 reduced_image(width,height); 
    405          reduce_8(image,reduced_image,trees, limits, alphaTable); 
     419         reduce_8(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable); 
    406420         save_as_png(file,palette,reduced_image,width,height,8,alphaTable); 
    407421      } 
     
    425439         unsigned image_height = height; 
    426440         ImageData8 reduced_image(image_width,image_height); 
    427          reduce_4(image,reduced_image,trees, limits, alphaTable); 
     441         reduce_4(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable); 
    428442         save_as_png(file,palette,reduced_image,width,height,4,alphaTable); 
    429443      } 
    430444   } 
     445 
     446   template <typename T1,typename T2> 
     447   void save_as_png256_hex(T1 & file, T2 const& image, int colors = 256, int trans_mode = -1, double gamma = 2.0) 
     448   { 
     449      unsigned width = image.width(); 
     450      unsigned height = image.height(); 
     451 
     452      // structure for color quantization 
     453      hextree<mapnik::rgba> tree(colors); 
     454      if (trans_mode >= 0) 
     455         tree.setTransMode(trans_mode); 
     456      if (gamma > 0) 
     457         tree.setGamma(gamma); 
     458 
     459      for (unsigned y = 0; y < height; ++y) 
     460      { 
     461         typename T2::pixel_type const * row = image.getRow(y); 
     462         for (unsigned x = 0; x < width; ++x) 
     463         { 
     464            unsigned val = row[x]; 
     465            tree.insert(mapnik::rgba(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val))); 
     466         } 
     467      } 
     468 
     469      //transparency values per palette index 
     470      std::vector<mapnik::rgba> pal; 
     471      tree.create_palette(pal); 
     472      assert(pal.size() <= colors); 
     473 
     474      std::vector<mapnik::rgb> palette; 
     475      std::vector<unsigned> alphaTable; 
     476      for(unsigned i=0; i<pal.size(); i++) 
     477      { 
     478          palette.push_back(rgb(pal[i].r, pal[i].g, pal[i].b)); 
     479          alphaTable.push_back(pal[i].a); 
     480      } 
     481 
     482      if (palette.size() > 16 ) 
     483      { 
     484          // >16 && <=256 colors -> write 8-bit color depth 
     485          ImageData8 reduced_image(width, height); 
     486 
     487          for (unsigned y = 0; y < height; ++y) 
     488          { 
     489             mapnik::ImageData32::pixel_type const * row = image.getRow(y); 
     490             mapnik::ImageData8::pixel_type  * row_out = reduced_image.getRow(y); 
     491 
     492             for (unsigned x = 0; x < width; ++x) 
     493             { 
     494                unsigned val = row[x]; 
     495                mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); 
     496                row_out[x] = tree.quantize(c); 
     497             } 
     498          } 
     499          save_as_png(file, palette, reduced_image, width, height, 8, alphaTable); 
     500      } 
     501      else if (palette.size() == 1) 
     502      { 
     503         // 1 color image ->  write 1-bit color depth PNG 
     504         unsigned image_width  = (int(0.125*width) + 7)&~7; 
     505         unsigned image_height = height; 
     506         ImageData8 reduced_image(image_width, image_height); 
     507         reduced_image.set(0); 
     508         save_as_png(file, palette, reduced_image, width, height, 1, alphaTable); 
     509      } 
     510      else 
     511      { 
     512         // <=16 colors -> write 4-bit color depth PNG 
     513         unsigned image_width  = (int(0.5*width) + 3)&~3; 
     514         unsigned image_height = height; 
     515         ImageData8 reduced_image(image_width, image_height); 
     516          for (unsigned y = 0; y < height; ++y) 
     517          { 
     518             mapnik::ImageData32::pixel_type const * row = image.getRow(y); 
     519             mapnik::ImageData8::pixel_type  * row_out = reduced_image.getRow(y); 
     520             byte index = 0; 
     521 
     522             for (unsigned x = 0; x < width; ++x) 
     523             { 
     524                unsigned val = row[x]; 
     525                mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); 
     526                index = tree.quantize(c); 
     527                if (x%2 == 0) index = index<<4; 
     528                row_out[x>>1] |= index; 
     529             } 
     530          } 
     531         save_as_png(file, palette, reduced_image, width, height, 4, alphaTable); 
     532      } 
     533   }    
    431534} 
  • branches/0.7.1-dev/src/image_util.cpp

    r1544 r1680  
    4040#endif 
    4141 
     42#include <boost/foreach.hpp> 
     43#include <boost/tokenizer.hpp> 
     44 
    4245// stl 
    4346#include <string> 
     
    5356    { 
    5457        std::ostringstream ss(std::ios::out|std::ios::binary); 
    55         //all this should go into image_writer factory 
    56         if (type=="png")  save_as_png(ss,image); 
    57         else if (type == "png256") save_as_png256(ss,image); 
    58         else if (boost::algorithm::istarts_with(type,std::string("jpeg"))) 
    59         { 
    60             int quality = 85; 
    61             try  
    62             { 
    63                 if(type.substr(4).length() != 0) 
    64                 { 
    65                     quality = boost::lexical_cast<int>(type.substr(4)); 
    66                     if(quality<1 || quality>100) 
    67                         throw ImageWriterException("invalid jpeg quality: " + type.substr(4)); 
    68                 } 
    69                 save_as_jpeg(ss,quality,image);  
    70             }  
    71             catch(boost::bad_lexical_cast &) 
    72             { 
    73                 throw ImageWriterException("invalid jpeg quality: " + type.substr(4)); 
    74             } 
    75         } 
    76         else throw ImageWriterException("unknown file type: " + type); 
     58        save_to_stream(image, ss, type); 
    7759        return ss.str(); 
    7860    } 
     
    8668        if (file) 
    8769        { 
     70            save_to_stream(image, file, type); 
     71        } 
     72        else throw ImageWriterException("Could not write file to " + filename ); 
     73    } 
     74 
     75    template <typename T> 
     76    void save_to_stream(T const& image, 
     77                      std::ostream & stream, 
     78                      std::string const& type) 
     79    { 
     80        if (stream) 
     81        { 
    8882            //all this should go into image_writer factory 
    89             if (type=="png")  save_as_png(file,image); 
    90             else if (type == "png256") save_as_png256(file,image); 
     83            if (type == "png")  save_as_png(stream, image); 
     84            else if (boost::algorithm::istarts_with(type, std::string("png256")) || 
     85                     boost::algorithm::istarts_with(type, std::string("png:")) 
     86                     )  
     87            { 
     88                int colors  = 256; 
     89                int trans_mode = -1; 
     90                double gamma = -1; 
     91                bool use_octree = true; 
     92                if (type.length() > 6){ 
     93                    boost::char_separator<char> sep(":"); 
     94                    boost::tokenizer< boost::char_separator<char> > tokens(type, sep); 
     95                    BOOST_FOREACH(string t, tokens) 
     96                    { 
     97                        if (t == "m=h") 
     98                        { 
     99                            use_octree = false; 
     100                        } 
     101                        if (t == "m=o") 
     102                        { 
     103                            use_octree = true; 
     104                        } 
     105                        if (boost::algorithm::istarts_with(t,std::string("c="))) 
     106                        { 
     107                            try  
     108                            { 
     109                                colors = boost::lexical_cast<int>(t.substr(2)); 
     110                                if (colors < 0 || colors > 256) 
     111                                    throw ImageWriterException("invalid color parameter: " + t.substr(2) + " out of bounds"); 
     112                            } 
     113                            catch(boost::bad_lexical_cast &) 
     114                            { 
     115                                throw ImageWriterException("invalid color parameter: " + t.substr(2)); 
     116                            } 
     117                        } 
     118                        if (boost::algorithm::istarts_with(t, std::string("t="))) 
     119                        { 
     120                            try  
     121                            { 
     122                                trans_mode= boost::lexical_cast<int>(t.substr(2)); 
     123                                if (trans_mode < 0 || trans_mode > 2) 
     124                                    throw ImageWriterException("invalid trans_mode parameter: " + t.substr(2) + " out of bounds"); 
     125                            } 
     126                            catch(boost::bad_lexical_cast &) 
     127                            { 
     128                                throw ImageWriterException("invalid trans_mode parameter: " + t.substr(2)); 
     129                            } 
     130                        } 
     131                        if (boost::algorithm::istarts_with(t, std::string("g="))) 
     132                        { 
     133                            try  
     134                            { 
     135                                gamma= boost::lexical_cast<double>(t.substr(2)); 
     136                                if (gamma < 0) 
     137                                    throw ImageWriterException("invalid gamma parameter: " + t.substr(2) + " out of bounds"); 
     138                            } 
     139                            catch(boost::bad_lexical_cast &) 
     140                            { 
     141                                throw ImageWriterException("invalid gamma parameter: " + t.substr(2)); 
     142                            } 
     143                        } 
     144                    } 
     145 
     146                } 
     147                if (use_octree) 
     148                    save_as_png256(stream, image, colors); 
     149                else 
     150                    save_as_png256_hex(stream, image, colors, trans_mode, gamma); 
     151            } 
    91152            else if (boost::algorithm::istarts_with(type,std::string("jpeg"))) 
    92153            { 
     
    100161                            throw ImageWriterException("invalid jpeg quality: " + type.substr(4) + " out of bounds"); 
    101162                    } 
    102                     save_as_jpeg(file,quality,image);  
     163                    save_as_jpeg(stream, quality, image);  
    103164                }  
    104165                catch(boost::bad_lexical_cast &) 
     
    109170            else throw ImageWriterException("unknown file type: " + type); 
    110171        } 
    111         else throw ImageWriterException("Could not write file to " + filename ); 
     172        else throw ImageWriterException("Could not write to empty stream" ); 
    112173    } 
    113174