Changeset 1680
- Timestamp:
- 03/11/10 18:16:17 (5 months ago)
- Location:
- branches/0.7.1-dev
- Files:
-
- 4 modified
-
demo/python/rundemo.py (modified) (1 diff)
-
include/mapnik/octree.hpp (modified) (1 diff)
-
include/mapnik/png_io.hpp (modified) (18 diffs)
-
src/image_util.cpp (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/0.7.1-dev/demo/python/rundemo.py
r1542 r1680 329 329 im.save('demo.png', 'png') # true-colour RGBA 330 330 images_.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' 333 im.save('demo256.png', 'png256') # save to palette based (max 256 colours) png 332 334 images_.append('demo256.png') 335 336 im.save('demo64_binary_transparency.png', 'png:c=64:t=1') 337 images_.append('demo64_binary_transparency.png') 338 339 im.save('demo128_colors_hextree_no_alpha.png', 'png:c=100:m=h:t=0') 340 images_.append('demo128_colors_hextree_no_alpha.png') 341 333 342 im.save('demo_high.jpg', 'jpeg100') 334 343 images_.append('demo_high.jpg') 344 335 345 im.save('demo_low.jpg', 'jpeg50') 336 346 images_.append('demo_low.jpg') -
branches/0.7.1-dev/include/mapnik/octree.hpp
r1663 r1680 44 44 byte g; 45 45 byte b; 46 rgb(byte r_, byte b_, byte g_)46 rgb(byte r_, byte g_, byte b_) 47 47 : r(r_), g(g_), b(b_) {} 48 48 }; -
branches/0.7.1-dev/include/mapnik/png_io.hpp
r1661 r1680 24 24 #include <mapnik/global.hpp> 25 25 #include <mapnik/octree.hpp> 26 #include <mapnik/hextree.hpp> 26 27 #include <mapnik/global.hpp> 27 28 … … 31 32 } 32 33 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 37 35 38 36 #ifdef MAPNIK_BIG_ENDIAN … … 63 61 out->flush(); 64 62 } 65 63 66 64 template <typename T1, typename T2> 67 65 void save_as_png(T1 & file , T2 const& image) … … 110 108 111 109 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) 113 111 { 114 112 unsigned width = in.width(); … … 133 131 byte index = 0; 134 132 int idx = -1; 135 for(int j= TRANSPARENCY_LEVELS-1; j>0; j--){133 for(int j=levels-1; j>0; j--){ 136 134 if (U2ALPHA(val)>=limits[j]) { 137 135 index = idx = trees[j].quantize(c); … … 155 153 156 154 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) 158 156 { 159 157 unsigned width = in.width(); … … 179 177 byte index = 0; 180 178 int idx=-1; 181 for(int j= TRANSPARENCY_LEVELS-1; j>0; j--){179 for(int j=levels-1; j>0; j--){ 182 180 if (U2ALPHA(val)>=limits[j]) { 183 181 index = idx = trees[j].quantize(c); … … 275 273 276 274 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; 279 280 unsigned width = image.width(); 280 281 unsigned height = image.height(); … … 288 289 for (unsigned x = 0; x < width; ++x){ 289 290 unsigned val = U2ALPHA((unsigned)image.getRow(y)[x]); 291 if (trans_mode==0) 292 val=255; 290 293 alphaHist[val]++; 291 294 meanAlpha += val; … … 297 300 298 301 // transparency ranges division points 299 unsigned limits[ TRANSPARENCY_LEVELS+1];302 unsigned limits[MAX_OCTREE_LEVELS+1]; 300 303 limits[0] = 0; 301 304 limits[1] = (alphaHist[0]>0)?1:0; 302 305 limits[TRANSPARENCY_LEVELS] = 256; 303 306 unsigned alphaHistSum = 0; 304 for( intj=1; j<TRANSPARENCY_LEVELS; j++)307 for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) 305 308 limits[j] = limits[1]; 306 for( inti=1; i<256; i++){309 for(unsigned i=1; i<256; i++){ 307 310 alphaHistSum += alphaHist[i]; 308 for( intj=1; j<TRANSPARENCY_LEVELS; j++){311 for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++){ 309 312 if (alphaHistSum<semiCount*(j)/4) 310 313 limits[j] = i; … … 321 324 } 322 325 // estimated number of colors from palette assigned to chosen ranges 323 unsigned cols[ TRANSPARENCY_LEVELS];326 unsigned cols[MAX_OCTREE_LEVELS]; 324 327 // count colors 325 for( intj=1; j<=TRANSPARENCY_LEVELS; j++) {328 for(unsigned j=1; j<=TRANSPARENCY_LEVELS; j++) { 326 329 cols[j-1] = 0; 327 330 for(unsigned i=limits[j-1]; i<limits[j]; i++){ … … 334 337 cols[0] = cols[0]>0?1:0; // fully transparent color (one or not at all) 335 338 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 } 341 346 } 342 347 unsigned usedColors = cols[0]; 343 for( intj=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; 345 350 usedColors += cols[j]; 346 351 } 347 352 // 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 } 349 362 350 363 // octree table for separate alpha range with 1-based index (0 is fully transparent: no color) 351 octree<rgb> trees[ TRANSPARENCY_LEVELS];352 for( intj=1; j<TRANSPARENCY_LEVELS; j++)364 octree<rgb> trees[MAX_OCTREE_LEVELS]; 365 for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) 353 366 trees[j].setMaxColors(cols[j]); 354 367 for (unsigned y = 0; y < height; ++y) … … 360 373 361 374 // insert to proper tree based on alpha range 362 for( intj=TRANSPARENCY_LEVELS-1; j>0; j--){375 for(unsigned j=TRANSPARENCY_LEVELS-1; j>0; j--){ 363 376 if (cols[j]>0 && U2ALPHA(val)>=limits[j]) { 364 377 trees[j].insert(mapnik::rgb(U2RED(val), U2GREEN(val), U2BLUE(val))); … … 370 383 unsigned leftovers = 0; 371 384 std::vector<rgb> palette; 372 palette.reserve( 256);385 palette.reserve(max_colors); 373 386 if (cols[0]) 374 387 palette.push_back(rgb(0,0,0)); 375 388 376 for( intj=1; j<TRANSPARENCY_LEVELS; j++) {389 for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { 377 390 if (cols[j]>0) { 378 391 if (leftovers>0) { … … 384 397 trees[j].setOffset(palette.size()); 385 398 trees[j].create_palette(pal); 386 assert(pal.size() <= 256);399 assert(pal.size() <= max_colors); 387 400 leftovers = cols[j]-pal.size(); 388 401 cols[j] = pal.size(); … … 397 410 std::vector<unsigned> alphaTable; 398 411 //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]); 400 414 401 415 if (palette.size() > 16 ) … … 403 417 // >16 && <=256 colors -> write 8-bit color depth 404 418 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); 406 420 save_as_png(file,palette,reduced_image,width,height,8,alphaTable); 407 421 } … … 425 439 unsigned image_height = height; 426 440 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); 428 442 save_as_png(file,palette,reduced_image,width,height,4,alphaTable); 429 443 } 430 444 } 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 } 431 534 } -
branches/0.7.1-dev/src/image_util.cpp
r1544 r1680 40 40 #endif 41 41 42 #include <boost/foreach.hpp> 43 #include <boost/tokenizer.hpp> 44 42 45 // stl 43 46 #include <string> … … 53 56 { 54 57 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); 77 59 return ss.str(); 78 60 } … … 86 68 if (file) 87 69 { 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 { 88 82 //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 } 91 152 else if (boost::algorithm::istarts_with(type,std::string("jpeg"))) 92 153 { … … 100 161 throw ImageWriterException("invalid jpeg quality: " + type.substr(4) + " out of bounds"); 101 162 } 102 save_as_jpeg( file,quality,image);163 save_as_jpeg(stream, quality, image); 103 164 } 104 165 catch(boost::bad_lexical_cast &) … … 109 170 else throw ImageWriterException("unknown file type: " + type); 110 171 } 111 else throw ImageWriterException("Could not write file to " + filename);172 else throw ImageWriterException("Could not write to empty stream" ); 112 173 } 113 174
