インデックスカラーに対応してみた

boost::gil::color_converted_view を使って、色を変換する代わりにインデックス値から目的の色に変換することでインデックスカラーを実現することができる。

#include <boost/gil/gil_all.hpp>
#include <iterator>

// 8-bit index
struct index_color_t {};
typedef boost::mpl::vector1<index_color_t>                      index_t;
typedef boost::gil::layout<index_t>                             index_layout_t;
typedef boost::gil::pixel<boost::gil::bits8, index_layout_t>    index_pixel_t;
typedef index_pixel_t*                                          index_ptr_t;
typedef boost::gil::memory_based_step_iterator<index_ptr_t>     index_step_ptr_t;
typedef boost::gil::memory_based_2d_locator<index_step_ptr_t>   index_loc_t;
typedef boost::gil::image_view<index_loc_t>                     index_view_t;

// 4-bit index
typedef boost::mpl::vector1_c<unsigned int, 4>                  index4_t;
typedef boost::gil::layout<index4_t>                            index4_layout_t;
typedef boost::gil::bit_aligned_pixel_reference<unsigned char, index4_t, index4_layout_t, true> index4_ref_t;
typedef boost::gil::bit_aligned_pixel_iterator<index4_ref_t>    index4_ptr_t;
typedef boost::gil::memory_based_step_iterator<index4_ptr_t>    index4_step_ptr_t;
typedef boost::gil::memory_based_2d_locator<index4_step_ptr_t>  index4_loc_t;
typedef boost::gil::image_view<index4_loc_t>                    index4_view_t;

template<class PixelIterator>
struct index_converter
{
private:
    PixelIterator palette_;

public:
    index_converter(PixelIterator palette) : palette_(palette) { }
    template<class Src, class Dst>
    void operator()(const Src& src, Dst& dst) const
    {
        dst = palette_[boost::gil::at_c<0>(src)];
    }
};

template<class IndexView, class PixelIterator>
struct palette_view_type
{
    typedef typename std::iterator_traits<PixelIterator>::value_type palette_pixel_t;
    typedef typename index_converter<PixelIterator> index_converter_t;
    typedef typename boost::gil::color_converted_view_type<IndexView, palette_pixel_t, index_converter_t>::type type;
};

template<class IndexView, class PixelIterator>
typename palette_view_type<IndexView, PixelIterator>::type palette_view(
    const IndexView& view,
    PixelIterator palette)
{
    typedef typename std::iterator_traits<PixelIterator>::value_type PalettePixel;
    return boost::gil::color_converted_view<PalettePixel>(view, index_converter<PixelIterator>(palette));
}

使い方はこんな感じ。

#include <vector>
#include <algorithm>

namespace g = boost::gil;

void main()
{
    {
        // 適当にインデックスデータを作成する
        std::vector<index_pixel_t> rowdata;
        for (int i = 0; i < 100 * 100; i++)
        {
            rowdata.push_back(index_pixel_t(i % 256));
        }
        // 適当にパレットデータを作成する
        std::vector<g::rgb8_pixel_t> palette;
        for (int i = 0; i < 256; i++)
        {
            palette.push_back(g::rgb8_pixel_t(i));
        }

        // インデックスデータのビューを作成する
        index_view_t view = g::interleaved_view(100, 100, &rowdata[0], 100);
        // パレット付きのビューを作成する
        palette_view_type<index_view_t, g::rgb8_ptr_t>::type view2 = palette_view(view, &palette[0]);

        // rgb8 である dest へコピー
        std::vector<g::rgb8_pixel_t> dest(100 * 100);
        std::copy(view2.begin(), view2.end(), dest.begin());
    }

    {
        // 適当にインデックスデータを作成する
        std::vector<unsigned char> rowdata(100 * 100 / 2);
        index4_ptr_t it(&rowdata[0]), end(&rowdata[0] + rowdata.size());
        int n = 0;
        while (it != end)
        {
            *it++ = std::iterator_traits<index4_ptr_t>::value_type(n++ % 16);
        }
        // 適当にパレットデータを作成する
        std::vector<g::rgb8_pixel_t> palette;
        for (int i = 0; i < 16; i++)
        {
            palette.push_back(g::rgb8_pixel_t(i));
        }

        // インデックスデータのビューを作成する
        // 最後の引数はビット単位で指定する
        index4_view_t view = g::interleaved_view(100, 100, index4_ptr_t(&rowdata[0]), 50 * 8);
        // パレット付きのビューを作成する
        palette_view_type<index4_view_t, g::rgb8_ptr_t>::type view2 = palette_view(view, &palette[0]);

        // rgb8 である dest へコピー
        std::vector<g::rgb8_pixel_t> dest(100 * 100);
        std::copy(view2.begin(), view2.end(), dest.begin());
    }
}

view2 がまるで rgb8(R,G,B がそれぞれ 8 ビット)の画像であるかのように扱うことが出来るようになる(実際はインデックスデータとパレットデータしか持っていない)。


palette_view に渡す PixelIterator は PixelIteratorConcept モデルのコンセプトを満たしていればいいので、R,G,B がそれぞれ別の領域になっている planar なパレットでも動作させることが出来る。

    {
        // 適当にインデックスデータを作成する
        std::vector<index_pixel_t> rowdata;
        for (int i = 0; i < 100 * 100; i++)
        {
            rowdata.push_back(index_pixel_t(i % 256));
        }
        // 適当に planar なパレットデータを作成する
        std::vector<unsigned char> r_pal;
        std::vector<unsigned char> g_pal;
        std::vector<unsigned char> b_pal;
        for (int i = 0; i < 256; i++)
        {
            r_pal.push_back(i);
            g_pal.push_back(i);
            b_pal.push_back(i);
        }

        // インデックスデータのビューを作成する
        index_view_t view = g::interleaved_view(100, 100, &rowdata[0], 100);
        // パレット付きのビューを作成する
        palette_view_type<index_view_t, g::rgb8_planar_ptr_t>::type view2 =
            palette_view(view, g::rgb8_planar_ptr_t(&r_pal[0], &g_pal[0], &b_pal[0]));

        // rgb8 である dest へコピー
        std::vector<g::rgb8_pixel_t> dest(100 * 100);
        std::copy(view2.begin(), view2.end(), dest.begin());
    }

他にも boost::gil::packed_pixel とかを使えば、packed なデータ(例えば RGB565)なんかも動作させることが出来る。


当然インデックスデータの方も、typedef を増やせば 32 ビットインデックスだろうが 2 ビットインデックスだろうが 7 ビットインデックスだろうが動作する。
これだけのコードでこれだけ汎用的に書けるとは……恐るべし Boost.GIL。