Skip to content
Christian Henning edited this page Mar 22, 2018 · 19 revisions

Gil strength is its expressiveness by using concepts and plenty of meta-programming. This makes gil hard to learn and worse hard to extend without creating a mess.

This wiki will show some of the power of gil.

// Always assume
#include <vector>
#include <boost/gil/gil_all.hpp>

using namespace std;
using namespace boost::gil;

Simple

How to get the color space type from an image/view?

typedef rgb8_image_t image_t;
typedef typename color_space_type<image_t::view_t::value_type>::type colour_space_t;

There are rgb8_pixel_t, bgr8_pixel_t, and any other number of channel combinations. So, how do you get the red channel value?

auto get_red(pixel_t p)
{
    return get_color(p, red_t());
}

Each pixel type is defined with a bunch of channel types. Look here for the correct way of defining a new pixel type.

Damn it, my image is interleaved but I only want the red values!

argb8_image_t img( 640, 480 );
fill_pixels(view(img), argb8_pixel_t(0, 255, 0, 0 ));
    
// get a view to the first channel
auto v = nth_channel_view( view(img), 1 );

// convert the first channel view to gray8
auto c = color_converted_view<gray8_pixel_t>( v );

Heterogeneous Pixel

Such a pixel has a bunch of channels which might be a different data type. For instance, for a color space like rgb we want red to be uint8 but need more space for green and blue, maybe uint32_t?

bit aligned image example

#include <boost\gil\extension\toolbox\gil_extensions.hpp>

typedef bit_aligned_image4_type<4, 2, 2, 4, bgra_layout_t>::type image_t;
typedef image_t::view_t view_t;

image_t img( 640, 480 );
auto x_it = view(img).row_begin(0);
auto data = (uint8_t*) &at_c<0>(*it);

Create your own bit aligned image

typedef bit_aligned_pixel_reference< uint64_t // needs to be at least 40bits hence uint64_t
                                    , mpl::vector4_c<uint16_t, 10, 10, 10, 10>
                                    , rgba_layout_t
                                    , true
                                    >  rgba10_ref_t;

// A mutable iterator over RGBA10 pixels
typedef bit_aligned_pixel_iterator< rgba10_ref_t > rgba10_ptr_t;

typedef std::iterator_traits< rgba10_ptr_t >::value_type rgba10_pixel_t;

rgba10_pixel_t src_10( 20, 30, 40, 50 );
assert( get_color( src_10, red_t()   ) == 20 );
assert( get_color( src_10, green_t() ) == 30 );
assert( get_color( src_10, blue_t()  ) == 40 );
assert( get_color( src_10, alpha_t() ) == 50 );

Create a pixel type from a bunch of channels with each different ranges (aka different type).

struct double_0 { static double apply() { return 0.0; } }; 
struct double_1 { static double apply() { return 1.0; } }; 
struct double_100 { static double apply() { return 100.0; } }; 
struct double_255 { static double apply() { return 255.0; } };

typedef scoped_channel_value<double, double_0, double_1> channel_0_1_t; 
typedef scoped_channel_value<double, double_0, double_255> channel_0_255_t; 
typedef scoped_channel_value<double, double_0, double_100> channel_0_100_t; 

//color space 
typedef mpl::vector3< channel_0_1_t, channel_0_255_t, channel_0_100_t> my_cs_t; 

//layout 
typedef layout<my_cs_t> my_layout_t; 

//pixel 
typedef pixel< double, my_layout_t > my_pixel_t; my_pixel_t op;

Be sure to define the correct integral data type when defining a bit aligned image. It must be able to contain all channels

typedef mpl::vector5_c<unsigned, 16, 16, 16, 8, 8> CBSV;
int sum = mpl::accumulate< CBSV, mpl::int_<0>, mpl::plus<mpl::_1, mpl::_2> >::type::value;

static_assert(mpl::accumulate< CBSV, mpl::int_<0>, mpl::plus<mpl::_1, mpl::_2> >::type::value < sizeof(uint64_t));

Raw Memory

You got memory and need to run through an orderly fashion? gil got you covered!

Create a bit_aligned iterator from raw memory

std::vector<uint8_t> buf( 100 );
typedef bit_aligned_image1_type<1, gray_layout_t>::type::view_t view1_t;
typedef color_converted_view_type< view1_t, view1_t::reference >::type cc_view_t;
typedef cc_view_t::x_iterator cc_it_t;

auto cc_it = cc_it_t( view1_t::x_iterator(buf.date()) );
int Width = 640;
int Height = 480;

// create a rgb float buffer
float* src_buffer = new float[ 3 * Width * Height ];

// create a gil view of the src buffer
rgb32f_view_t v = interleaved_view(
    Width
    , Height
    , (rgb32f_pixel_t*) src_buffer
    , 3 * 4 * Width // row length in bytes
    );
    
// set pixel values
fill_pixels(v, rgb32f_pixel_t( 1.f ));

// let's create a rgb8 view
typedef color_converted_view_type<rgb32f_view_t, rgb8_pixel_t>::type ccv_t;
ccv_t dst = color_converted_view<rgb8_pixel_t>(v);

// all channels should be 255 which is gil's default behavior
rgb8_pixel_t p = *dst.xy_at(0,0);

Color Conversion

Can be tricky

// bits32f is a scoped value channel
typedef pixel<bits32f, gray_layout_t> pixel_t;
gray8_pixel_t src(23);
pixel_t dst(0);

// 23 / 255 <-- uint8 max value
color_convert(src, dst);

// dst[0] is ~0.09..

Generic way for hashing pixels. We are using static_for_each() to loop through all channels of a pixel.

#include <unordered_map>

template< typename Pixel >
struct pixel_hasher
{
    std::size_t operator()( const Pixel& p ) const
    {
        typedef channel_type<Pixel>::type channel_t;

        std::size_t hash = 0;
        static_for_each( p, [&] ( const channel_t& c )
        {
            boost::hash_combine(hash, c);
        });

        return hash;
    }
};

void test()
{
    typedef rgb8_pixel_t pixel_t;
    pixel_t p( 1,2,3);
    unordered_map< pixel_t, int, pixel_hasher< pixel_t > > n;
    n[p] = 99;
}

Using gil with other toolkits

SFML

#include <SFML/Graphics.hpp>

#include <cassert>
#include <complex>
#include <iostream>

#include <boost/gil/gil_all.hpp>
#include <boost/gil/extension/toolbox/color_spaces/hsv.hpp>

#ifdef _DEBUG
const int width  = 640; //1024;
const int height = 480; //768;
#else
const int width  = 1024;
const int height = 768;
#endif

template< typename T = double >
struct coloring_info
{
    coloring_info()
    : _inside( true )
    {}

    coloring_info( bool inside, const std::complex< T >& value, int iterations, int max_iterations )
    : _inside( inside )
    , _value( value )
    , _iterations( iterations )
    , _max_iterations( max_iterations )
    {}

    sf::Color get_color() const
    {
        sf:: Color color;
        color.a = 255;

        using namespace boost;
        using namespace gil;
       
        hsv32f_pixel_t src( ( _iterations % 256 ) / 255.f                 // hue
                          , 1.f                                           // (full) saturation
                          , ( _iterations < _max_iterations ) ? 1.f : 0.f // value
                          );

        rgb8_pixel_t dst;
        color_convert(src, dst);
       
        color.r = gil::get_color(dst, red_t());
        color.g = gil::get_color(dst, green_t());
        color.b = gil::get_color(dst, blue_t());

        if( _inside )
        {
            color = sf::Color::Black;
        }
        else
        {
            if( _iterations < _max_iterations )
            {
                // point does not belong to Mandelbrot set
        
                int r = static_cast<int>(( 255.0 / _max_iterations ) * _iterations );
        
                if( _iterations < ( _max_iterations / 2 ))
                {
                    color.r = r;
                    color.g = 0;
                    color.b = 0;
                }
                else
                {
                    color.r = r;
                    color.g = 255;
                    color.b = 255;
                }
        
                color.r;
                color.g;
                color.b;
            }
        }

        return color;
    }

    bool _inside;

    std::complex< T > _value;

    int _iterations;
    int _max_iterations;
};

template< typename T = double >
struct fractal_parameters
{
    // set all parameters by hand
    fractal_parameters( T r_min, T r_max, T i_min, T i_max, int max_iterations, int width , int height )
    : _real_min(r_min)
    , _real_max(r_max)
    , _imag_min(i_min)
    , _imag_max(i_max)

    , _max_iterations( max_iterations )

    , _real( 0 )
    , _imag( 0 )
    {
        // real_max =  1.0
        // real_min = -2.0

        // imag_max =  2.0
        // imag_min = -1.2

        // width  = 640
        // height = 480
       
        // x_step = ( 1 +   2 ) / 639 = 0.005
        // y_step = ( 2 + 1.2 ) / 479 = 0.007
       
        _x_step = ( _real_max - _real_min ) / ( width  - 1 );
        _y_step = ( _imag_max - _imag_min ) / ( height - 1 );
    }

    // scales to include aspect ration
    fractal_parameters( T r_min, T r_max, T i_min, int max_iterations, int width , int height )
    : _real_min(r_min)
    , _real_max(r_max)
    , _imag_min(i_min)

    , _max_iterations( max_iterations )

    , _real( 0 )
    , _imag( 0 )
    {
        _imag_max = _imag_min + ( _real_max - _real_min ) * height / width;
       
        _x_step = ( _real_max - _real_min ) / ( width  - 1 );
        _y_step = ( _imag_max - _imag_min ) / ( height - 1 );
    }


    // real_min represents x = 0 or the left boundary of the image
    void set_x( int x ) { _real = _real_min + x * _x_step; }
   
    // imag_max represents y = 0 or the upper boundary of the image
    void set_y( int y ) { _imag = _imag_max - y * _y_step; }

    coloring_info<> calc() const
    {
        coloring_info<> ci;

        std:: complex< T> c( _real, _imag );
        std:: complex< T> Z( c );

        int n;
        for( n = 0; n < _max_iterations; ++n )
        {
            if( std::abs( Z ) > 2.0 )
            {
                ci._inside = false;
                break;
            }

            Z = Z * Z + c;
        }
       
        ci._iterations = n;
        ci._max_iterations = _max_iterations;
       
        return ci;
    }


    T _real_min;
    T _real_max;

    T _imag_min;
    T _imag_max;

    T _x_step;
    T _y_step;

    int _max_iterations;

    T _real;
    T _imag;
};


// upper left is 0,0
void set_pixel( sf::Image & image , int x , int y , unsigned char r = 255, unsigned char g = 255, unsigned char b = 255 )
{
    image.setPixel( x, y, sf:: Color( r, g, b, 255));
}

void set_pixel( sf::Image & image , int x , int y , sf::Color c )
{
    image.setPixel( x, y, c );
}

void paint( sf::Image & image , sf::Color c )
{
    for( int y = 0; y < height; ++y )
    {
        for( int x = 0; x < width; ++x )
        {
            set_pixel( image, x, y, c.r, c.g, c.b );
        }
    }
}

void redraw( sf::Image & image , fractal_parameters <> mandel_brot )
{
    //paint(image, sf::Color::Black);


    sf::Color color;

    for( int y = 0; y < height; ++y )
    {
        mandel_brot.set_y( y );
       
        for( int x = 0; x < width; ++x )
        {
            mandel_brot.set_x( x );
           
            auto ci = mandel_brot.calc();

            set_pixel( image, x, y, ci.get_color() );
        }
    }
}


int main()
{
    sf::RenderWindow window(sf:: VideoMode(width, height), "Title");
    window.setFramerateLimit( 30 );

    sf::Texture texture;
    if( !texture.create(width, height))
    {
        exit(1);
    }

    sf::Image image;
    image.create(width, height);

   
    fractal_parameters<> mandel_brot_1( -2.0, 1.0, -1.2, 50, width, height );
    fractal_parameters<> mandel_brot_2( -2.0, 1.0, -1.2, 300, width, height );
   
    //max_iterations++;
    redraw( image, mandel_brot_1 );

    while (window.isOpen())
    {
        sf:: Event event;
        while (window.pollEvent(event))
        {
           
            switch(event.type)
            {
                case sf:: Event:: Closed:
                {
                    window.close();

                    break;
                }

                case sf:: Event:: KeyPressed:
                {
                    if( sf:: Keyboard::isKeyPressed( sf:: Keyboard:: Right ))
                    {
                        std::cout << "right" << std::endl;
                        std::cout << "begin" << std::endl;
                        redraw( image, mandel_brot_2 );
                        std::cout << "end" << std::endl;
                    }
                    else if( sf:: Keyboard::isKeyPressed( sf:: Keyboard:: Left ))
                    {
                        std::cout << "left" << std::endl;
                        std::cout << "begin" << std::endl;
                        redraw( image, mandel_brot_1 );
                        std::cout << "end" << std::endl;
                    }
              
                    break;
                }
            }
        }

        texture.update(image);
        sf:: Sprite sprite(texture);

        window.clear();
        window.draw(sprite);
        window.display();
    }



    return 0;
}

SDL

#include <boost/test/unit_test.hpp>
#include <boost/gil/gil_all.hpp>


using namespace std;

unsigned int width  = 640;
unsigned int height = 480;

// format is ARGB8888 -> 4 bytes

// scanline size in bytes
unsigned int scanline = width  * sizeof( Uint32 );
unsigned int frame_size = scanline * height;

SDL_Renderer* ren = NULL;
SDL_Texture* tex = NULL;
Uint32* pixels = NULL;

Uint32 draw( Uint32 interval, void* param )
{
    using namespace boost;
    using namespace gil;

    argb8_view_t v = interleaved_view( width, height, (argb8_pixel_t*) pixels, scanline );
    fill_pixels( v, argb8_pixel_t( 255, 255, 0 , 0 ));

    //memset( pixels, 0, frame_size );

    SDL_UpdateTexture( tex, NULL, pixels, scanline );

    SDL_RenderClear( ren );
   
    SDL_RenderCopy( ren, tex, NULL, NULL );   

    SDL_RenderPresent( ren );

    return interval;
}

BOOST_AUTO_TEST_CASE( sdl_test )
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
    {
        cout << SDL_GetError() << endl;
    }

    // Create Window

    SDL_Window* win = SDL_CreateWindow( "First"
                                      , 100
                                      , 100
                                      , width
                                      , height
                                      , SDL_WINDOW_SHOWN
                                      );

    if( win == NULL )
    {
        cout << SDL_GetError() << endl;
    }

    // Create Renderer

    ren = SDL_CreateRenderer( win
                            , -1 // SDL selects video driver
                            , SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
                            );

    if( ren == NULL )
    {
        cout << SDL_GetError() << endl;
    }

    tex = SDL_CreateTexture( ren
                           , SDL_PIXELFORMAT_ARGB8888
                           , SDL_TEXTUREACCESS_STREAMING
                           , width
                           , height
                           );

    if( tex == NULL )
    {
        cout << SDL_GetError() << endl;
    }


    pixels = (Uint32*) malloc( frame_size );

    // Add Timer
    SDL_AddTimer( 100, draw, NULL );

    // Wait for user to quit
    bool quit = false;
    SDL_Event e;

    while( quit == false )
    {
        while( SDL_PollEvent( &e ))
        {
            if( e.type == SDL_WINDOWEVENT )
            {
                auto id = e.window.windowID;

                break;
            }

            if( e.type == SDL_QUIT )
            {
                quit = true;
                break;
            }

            if( e.type == SDL_KEYDOWN )
            {
                quit = true;
                break;
            }
        }
    }


    // Clean up
    SDL_DestroyTexture( tex );
    SDL_DestroyRenderer( ren );
    SDL_DestroyWindow( win );

    SDL_Quit();
}

Clone this wiki locally