Пример #1
0
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);
    }
}
Пример #2
0
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
}
Пример #3
0
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;
}
Пример #4
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);
    }
Пример #5
0
    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);
            }
Пример #6
0
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);
    }
}