void save_as_png8(T1 & file, T2 const& image, T3 const & tree, std::vector<mapnik::rgb> const& palette, std::vector<unsigned> const& alphaTable, int compression = Z_DEFAULT_COMPRESSION, int strategy = Z_DEFAULT_STRATEGY) { unsigned width = image.width(); unsigned height = image.height(); if (palette.size() > 16 ) { // >16 && <=256 colors -> write 8-bit color depth image_data_8 reduced_image(width, height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); for (unsigned x = 0; x < width; ++x) { row_out[x] = tree.quantize(row[x]); } } save_as_png(file, palette, reduced_image, width, height, 8, compression, strategy, alphaTable); } else if (palette.size() == 1) { // 1 color image -> write 1-bit color depth PNG unsigned image_width = width > 7 ? (int(0.125*width) + 1)&~1 : 1; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); reduced_image.set(0); save_as_png(file, palette, reduced_image, width, height, 1, compression, strategy, alphaTable); } else { // <=16 colors -> write 4-bit color depth PNG unsigned image_width = width > 3 ? (int(0.5*width) + 3)&~3 : 4; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); byte index = 0; for (unsigned x = 0; x < width; ++x) { index = tree.quantize(row[x]); if (x%2 == 0) index = index<<4; row_out[x>>1] |= index; } } save_as_png(file, palette, reduced_image, width, height, 4, compression, strategy, alphaTable); } }
void process_rgba8_png(T const& image, std::string const& t, std::ostream & stream) { #if defined(HAVE_PNG) png_options opts; handle_png_options(t, opts); if (opts.paletted) { if (opts.use_hextree) { save_as_png8_hex(stream, image, opts); } else { save_as_png8_oct(stream, image, opts); } } else { save_as_png(stream, image, opts); } #else throw ImageWriterException("png output is not enabled in your build of Mapnik"); #endif }
int main() { const uint32_t samples_per_pixel = 16; const uint32_t width = 1280; const uint32_t height = 720; const vec3 cam_pos(0, 0, 25); const vec3 look_at(0, 0, -1); const vec3 up(0, 1, 0); camera cam(cam_pos, look_at, up, 90, (float)width / height); image img(width, height); sphere s(vec3(0, 0, -0.5), 1); vec3 light_dir = normalize(vec3(-1, -1, -1)); vec3 ambient_color = vec3(1.0, 0, 0); vec3 *pixels = img.pixels; for (uint32_t y = 0; y < height; ++y) { for (uint32_t x = 0; x < width; ++x) { vec3 color(0, 0, 0); for (uint32_t sample = 0; sample < samples_per_pixel; ++sample) { const ray r = make_ray( cam, (x + rand_float(generator)) / width, (height - (y + rand_float(generator))) / height ); vec3 hit; vec3 normal; if (intersect(s, r, &hit, &normal)) { vec3 dir_light_color = fmaxf(0, dot(normal, -light_dir)) * vec3(1.0, 1.0, 1.0); color += saturate(ambient_color + dir_light_color); } } color /= samples_per_pixel; *pixels++ = color; } } save_as_png(img, "test.png"); return 0; }
void save_as_png8_oct(T1 & file, T2 const& image, png_options const& opts) { // number of alpha ranges in png8 format; 2 results in smallest image with binary transparency // 3 is minimum for semitransparency, 4 is recommended, anything else is worse const unsigned TRANSPARENCY_LEVELS = (opts.trans_mode==2||opts.trans_mode<0)?MAX_OCTREE_LEVELS:2; unsigned width = image.width(); unsigned height = image.height(); unsigned alphaHist[256];//transparency histogram unsigned semiCount = 0;//sum of semitransparent pixels unsigned meanAlpha = 0; if (opts.trans_mode == 0) { meanAlpha = 255; } else { for(int i=0; i<256; i++) { alphaHist[i] = 0; } for (unsigned y = 0; y < height; ++y) { for (unsigned x = 0; x < width; ++x) { unsigned val = U2ALPHA(static_cast<unsigned>(image.get_row(y)[x])); alphaHist[val]++; meanAlpha += val; if (val>0 && val<255) { semiCount++; } } } meanAlpha /= width*height; } // transparency ranges division points unsigned limits[MAX_OCTREE_LEVELS+1]; limits[0] = 0; limits[1] = (opts.trans_mode!=0 && alphaHist[0]>0)?1:0; limits[TRANSPARENCY_LEVELS] = 256; for(unsigned j=2; j<TRANSPARENCY_LEVELS; j++) { limits[j] = limits[1]; } if (opts.trans_mode != 0) { unsigned alphaHistSum = 0; for(unsigned i=1; i<256; i++) { alphaHistSum += alphaHist[i]; for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { if (alphaHistSum<semiCount*(j)/4) { limits[j] = i; } } } } // avoid too wide full transparent range if (limits[1]>256/(TRANSPARENCY_LEVELS-1)) { limits[1]=256/(TRANSPARENCY_LEVELS-1); } // avoid too wide full opaque range if (limits[TRANSPARENCY_LEVELS-1]<212) { limits[TRANSPARENCY_LEVELS-1]=212; } if (TRANSPARENCY_LEVELS==2) { limits[1]=127; } // estimated number of colors from palette assigned to chosen ranges unsigned cols[MAX_OCTREE_LEVELS]; // count colors if (opts.trans_mode == 0) { for (unsigned j=0; j<TRANSPARENCY_LEVELS; j++) { cols[j] = 0; } cols[TRANSPARENCY_LEVELS-1] = width * height; } else { for (unsigned j=0; j<TRANSPARENCY_LEVELS; j++) { cols[j] = 0; for (unsigned i=limits[j]; i<limits[j+1]; i++) { cols[j] += alphaHist[i]; } } } unsigned divCoef = width*height-cols[0]; if (divCoef==0) { divCoef = 1; } cols[0] = cols[0]>0?1:0; // fully transparent color (one or not at all) if (opts.colors>=64) { // give chance less populated but not empty cols to have at least few colors(12) unsigned minCols = (12+1)*divCoef/(opts.colors-cols[0]); for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { if (cols[j]>12 && cols[j]<minCols) { divCoef += minCols-cols[j]; cols[j] = minCols; } } } unsigned usedColors = cols[0]; for(unsigned j=1; j<TRANSPARENCY_LEVELS-1; j++) { cols[j] = cols[j]*(opts.colors-cols[0])/divCoef; usedColors += cols[j]; } // use rest for most opaque group of pixels cols[TRANSPARENCY_LEVELS-1] = opts.colors-usedColors; //no transparency if (opts.trans_mode == 0) { limits[1] = 0; cols[0] = 0; cols[1] = opts.colors; } // octree table for separate alpha range with 1-based index (0 is fully transparent: no color) octree<rgb> trees[MAX_OCTREE_LEVELS]; for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { trees[j].setMaxColors(cols[j]); } for (unsigned y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.get_row(y); for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; // insert to proper tree based on alpha range for(unsigned j=TRANSPARENCY_LEVELS-1; j>0; j--) { if (cols[j]>0 && U2ALPHA(val)>=limits[j]) { trees[j].insert(mapnik::rgb(U2RED(val), U2GREEN(val), U2BLUE(val))); break; } } } } unsigned leftovers = 0; std::vector<rgb> palette; palette.reserve(opts.colors); if (cols[0]) { palette.push_back(rgb(0,0,0)); } for(unsigned j=1; j<TRANSPARENCY_LEVELS; j++) { if (cols[j]>0) { if (leftovers>0) { cols[j] += leftovers; trees[j].setMaxColors(cols[j]); leftovers = 0; } std::vector<rgb> pal; trees[j].setOffset( static_cast<unsigned>(palette.size())); trees[j].create_palette(pal); leftovers = cols[j] - static_cast<unsigned>(pal.size()); cols[j] = static_cast<unsigned>(pal.size()); palette.insert(palette.end(), pal.begin(), pal.end()); } } //transparency values per palette index std::vector<unsigned> alphaTable; //alphaTable.resize(palette.size());//allow semitransparency also in almost opaque range if (opts.trans_mode != 0) { alphaTable.resize(palette.size() - cols[TRANSPARENCY_LEVELS-1]); } if (palette.size() > 16 ) { // >16 && <=256 colors -> write 8-bit color depth image_gray8 reduced_image(width,height); reduce_8(image, reduced_image, trees, limits, TRANSPARENCY_LEVELS, alphaTable); save_as_png(file,palette,reduced_image,width,height,8,alphaTable,opts); } else if (palette.size() == 1) { // 1 color image -> write 1-bit color depth PNG unsigned image_width = ((width + 15) >> 3) & ~1U; // 1-bit image, round up to 16-bit boundary unsigned image_height = height; image_gray8 reduced_image(image_width,image_height); reduce_1(image,reduced_image,trees, limits, alphaTable); if (meanAlpha<255 && cols[0]==0) { alphaTable.resize(1); alphaTable[0] = meanAlpha; } save_as_png(file,palette,reduced_image,width,height,1,alphaTable,opts); }
void save_to_stream(T const& image, std::ostream & stream, std::string const& type) { if (stream) { //all this should go into image_writer factory if (type == "png") save_as_png(stream, image); else if (boost::algorithm::istarts_with(type, std::string("png256")) || boost::algorithm::istarts_with(type, std::string("png8")) ) { int colors = 256; int trans_mode = -1; double gamma = -1; bool use_octree = true; if (type.length() > 6){ boost::char_separator<char> sep(":"); boost::tokenizer< boost::char_separator<char> > tokens(type, sep); BOOST_FOREACH(string t, tokens) { if (t == "m=h") { use_octree = false; } if (t == "m=o") { use_octree = true; } if (boost::algorithm::istarts_with(t,std::string("c="))) { try { colors = boost::lexical_cast<int>(t.substr(2)); if (colors < 0 || colors > 256) throw ImageWriterException("invalid color parameter: " + t.substr(2) + " out of bounds"); } catch(boost::bad_lexical_cast &) { throw ImageWriterException("invalid color parameter: " + t.substr(2)); } } if (boost::algorithm::istarts_with(t, std::string("t="))) { try { trans_mode= boost::lexical_cast<int>(t.substr(2)); if (trans_mode < 0 || trans_mode > 2) throw ImageWriterException("invalid trans_mode parameter: " + t.substr(2) + " out of bounds"); } catch(boost::bad_lexical_cast &) { throw ImageWriterException("invalid trans_mode parameter: " + t.substr(2)); } } if (boost::algorithm::istarts_with(t, std::string("g="))) { try { gamma= boost::lexical_cast<double>(t.substr(2)); if (gamma < 0) throw ImageWriterException("invalid gamma parameter: " + t.substr(2) + " out of bounds"); } catch(boost::bad_lexical_cast &) { throw ImageWriterException("invalid gamma parameter: " + t.substr(2)); } } } } if (use_octree) save_as_png256(stream, image, colors); else save_as_png256_hex(stream, image, colors, trans_mode, gamma); }
void save_as_png256_hex(T1 & file, T2 const& image, int colors = 256, int trans_mode = -1, double gamma = 2.0) { unsigned width = image.width(); unsigned height = image.height(); // structure for color quantization hextree<mapnik::rgba> tree(colors); if (trans_mode >= 0) tree.setTransMode(trans_mode); if (gamma > 0) tree.setGamma(gamma); for (unsigned y = 0; y < height; ++y) { typename T2::pixel_type const * row = image.getRow(y); for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; tree.insert(mapnik::rgba(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val))); } } //transparency values per palette index std::vector<mapnik::rgba> pal; tree.create_palette(pal); assert(int(pal.size()) <= colors); std::vector<mapnik::rgb> palette; std::vector<unsigned> alphaTable; for(unsigned i=0; i<pal.size(); i++) { palette.push_back(rgb(pal[i].r, pal[i].g, pal[i].b)); alphaTable.push_back(pal[i].a); } if (palette.size() > 16 ) { // >16 && <=256 colors -> write 8-bit color depth image_data_8 reduced_image(width, height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); row_out[x] = tree.quantize(c); } } save_as_png(file, palette, reduced_image, width, height, 8, alphaTable); } else if (palette.size() == 1) { // 1 color image -> write 1-bit color depth PNG unsigned image_width = (int(0.125*width) + 7)&~7; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); reduced_image.set(0); save_as_png(file, palette, reduced_image, width, height, 1, alphaTable); } else { // <=16 colors -> write 4-bit color depth PNG unsigned image_width = (int(0.5*width) + 3)&~3; unsigned image_height = height; image_data_8 reduced_image(image_width, image_height); for (unsigned y = 0; y < height; ++y) { mapnik::image_data_32::pixel_type const * row = image.getRow(y); mapnik::image_data_8::pixel_type * row_out = reduced_image.getRow(y); byte index = 0; for (unsigned x = 0; x < width; ++x) { unsigned val = row[x]; mapnik::rgba c(U2RED(val), U2GREEN(val), U2BLUE(val), U2ALPHA(val)); index = tree.quantize(c); if (x%2 == 0) index = index<<4; row_out[x>>1] |= index; } } save_as_png(file, palette, reduced_image, width, height, 4, alphaTable); } }