在基本类型模板特化之间转换

时间:2013-09-02 00:00:40

标签: c++ templates colors struct

我正在研究一个简单的图像编辑器,我需要能够在像素类型之间进行转换(例如8位RGB和32位RGBA)。我设置的用于表示像素的是模板结构:

template<unsigned int bit_depth, PIXEL_FORMAT fmt> struct Pixel {};

PIXEL_FORMATenum class

目标是能够在处理图像时在不同像素格式之间自由转换。但是,如果我尝试从例如8位RGB转换为16位RGB,那么:

    Pixel<16, PIXEL_FORMAT::RGB> convert_to_16rgb() {
        char red, grn, blu;
        red = ((color & RED_8_BIT) >> 5);
        grn = ((color & GRN_8_BIT) >> 2);
        blu = (color & BLU_8_BIT);

        red = ((red/7) * 31); //(red/(2^3)-1) * ((2^5)-1)
        grn = ((grn/7) * 63);
        blu = ((blu/3) * 31); //these are to maintain the ratio but bring up the actual value

        Pixel<16, PIXEL_FORMAT::RGB> _16rgb(red, grn, blu); //Pixel<16, RGB has a 3-uchar c'tor available, but it's down below

        return _16rgb;
    }

我收到一条错误,指出Pixel<16, PIXEL_FORMAT::RGB>在声明之前已实例化。没问题,只需向前声明它(构造函数)。然后我得到一个错误,说它是一个部分结构,无法使用。

那么我将如何尝试在各种专业化之间进行转换?在结构定义之外创建模板函数?我需要专注于每一个组合,但如果我在每个专业化中定义了两个转换,那么我可以简单地“链接”它们,从一个转换到下一个直到我达到我想要的结果(例如从8转换) -bit RGB到24位RGB我可以定义一个_8rgb - &gt; _16rgb转换然后_16rgb到_24rgb转换,而不是Pixel<8, RGB>内的_16rgb和_24rgb转换

根据要求,small example

2 个答案:

答案 0 :(得分:1)

由于Pixel的一个专业化需要另一个用于转换目的的定义,因此您需要在实现转换算法之前定义所有这些。这可以通过推迟转换函数的定义来实现,如下所示,我编辑了一段从您提供的示例中获取的代码:

template<unsigned bit_depth, PIXEL_FORMAT fmt> struct Pixel {};

template<> struct Pixel<8, PIXEL_FORMAT::RGB> {
    unsigned char color;
    Pixel(unsigned char red, unsigned char green, unsigned char blue) {
        color = 0xFF & (((red << 5) & RED_3_3_2) & ((green << 2) & GRN_3_3_2) & (blue & BLU_3_3_2));
    }

    Pixel(unsigned char clr) { color = clr; }

    Pixel<16, PIXEL_FORMAT::RGB> convert_to_rgb16();
};

template<> struct Pixel<16, PIXEL_FORMAT::RGB> {
    unsigned short color;
    Pixel(unsigned char red, unsigned char green, unsigned char blue) {
        color = (0xFFFF & (((red << 11) & RED_5_6_5) & ((green << 5) & GRN_5_6_5) & (blue & BLU_5_6_5)));
    }
    Pixel(short clr) { color = clr; }
};

Pixel<16, PIXEL_FORMAT::RGB> Pixel<8, PIXEL_FORMAT::RGB>::convert_to_rgb16() {
    unsigned char red, grn, blu;
    red = ((color & RED_3_3_2) >> 5);
    grn = ((color & GRN_3_3_2) >> 2);
    blu = (color & BLU_3_3_2);

    red = ((red/7) * 31); //5
    grn = ((grn/7) * 63); //6
    blu = ((blu/3) * 31); //5

    Pixel<16, PIXEL_FORMAT::RGB> _16rgb(red, grn, blu);
    return _16rgb;
}

请注意,在定义了16位像素的特化之后,{8}在Pixel的8位专门化之外定义。

答案 1 :(得分:0)

通过您提供的代码段,我可以实现一些您应该解决的警告:

首先,red是一个严格小于7的整数,red = ((red/7) * 31)将始终为0,这是因为以这种方式写,你指示编译器执行整数除法后跟一个整数乘法,即忽略小数部分。

现在关于您提供的示例,在第68行,我相信color = (0xFFFF & (((red << 11) & RED_5_6_5) & ((green << 5) & GRN_5_6_5) & (blue & BLU_5_6_5)));也将始终为0,即假定RED_5_6_5 & GRN_5_6_5 & BLU_5_6_5 = 0 = 0应该是。我相信你的意思是color = (0xFFFF & (((red << 11) & RED_5_6_5) | ((green << 5) & GRN_5_6_5) | (blue & BLU_5_6_5)));

此外,您使用unsigned short作为'std :: uint16_t'的同义词,同样使用unsigned int作为'std :: uint32_t'的同义词,不需要在每个平台上保留甚至不需要在同一平台上的不同编译器版本之间,所以我强烈建议使用上面提到的固定大小的整数。

通过在整个代码中广泛使用常量,以及公平的代码重复,我可以预览,几乎可以肯定,您很快就会遇到很多难以维护代码甚至无法使用它。也就是说,我希望这听起来并不傲慢,但我冒昧地除了解决你来这里寻求建议的具体问题外,还向你提出了一种处理这些像素抽象的全新方法。

在下面的代码片段中,我重写了用于处理RGB像素的api,通过使用元编程将代码重复保持为我认为最小的代码。你会意识到,例如,我没有专门研究类template<...> struct Pixel来处理像素格式和深度的每种可能组合,因为我将血腥细节留在帮助类template<...> struct pixel_tools中。对于给定像素格式的任何可能的深度,从而减少代码的复制。

我并不认为我的解决方案对于这种情况是最好的,事实上我最确定它不是,但我相信这是对你的代码维护的改进。从现在开始,如果您决定采纳我的任何建议,我相信可以直接扩展您正在使用的其他像素abstration的想法。

注意:此代码段很可能包含错误,请原谅,但我没有时间对其进行彻底检查。

#include <cstddef>
#include <cstdint>
#include <iostream>

template <std::size_t bit_depth>
struct underlying_type;

template <>
struct underlying_type<8U>
{
    typedef std::uint8_t type;
};

template <>
struct underlying_type<16U>
{
    typedef std::uint16_t type;
};

template <>
struct underlying_type<32U>
{
    typedef std::uint32_t type;
};

enum class COLOR
{
    RED = 0,
    GREEN = 1,
    BLUE = 2,
    ALPHA = 3
};

enum class PIXEL_FORMAT : char
{
    PALETTE,
    RGB,
    RGBA
};

template<PIXEL_FORMAT fmt>
struct pixel_tools;

template<>
struct pixel_tools<PIXEL_FORMAT::RGB>
{
    //this metafunction avoids the use of #defines, which do get nasty quickly and should be avoided at all costs
    //luckly c++ has strong metaprogramming idioms and boost is your best friend
    //the third argument is necessary because total template specialization is not allowed inside templated structures
    template <COLOR color, std::size_t bit_depth, typename _ = void>
    struct color_mask;

    template <typename _>
    struct color_mask<COLOR::RED, 8U, _>
    {
        static typename underlying_type<8U>::type const value = 0xE0;
    };

    template <typename _>
    struct color_mask<COLOR::GREEN, 8U, _>
    {
        static typename underlying_type<8U>::type const value = 0x1C;
    };

    template <typename _>
    struct color_mask<COLOR::BLUE, 8U, _>
    {
        static typename underlying_type<8U>::type const value = 0x03;
    };

    template <typename _>
    struct color_mask<COLOR::RED, 16U, _>
    {
        static typename underlying_type<16U>::type const value = 0xF800;
    };

    template <typename _>
    struct color_mask<COLOR::GREEN, 16U, _>
    {
        static typename underlying_type<16U>::type const value = 0x07E0;
    };

    template <typename _>
    struct color_mask<COLOR::BLUE, 16U, _>
    {
        static typename underlying_type<16U>::type const value = 0x001F;
    };

    template <typename _>
    struct color_mask<COLOR::RED, 32U, _>
    {
        static typename underlying_type<32U>::type const value = 0xFFC00000;
    };

    template <typename _>
    struct color_mask<COLOR::GREEN, 32U, _>
    {
        static typename underlying_type<32U>::type const value = 0x003FF000;
    };

    template <typename _>
    struct color_mask<COLOR::BLUE, 32U, _>
    {
        static typename underlying_type<32U>::type const value = 0x00000FFC;
    };

    //the third argument is necessary because total template specialization is not allowed inside templated structures
    template <COLOR color, std::size_t bit_depth, typename _ = void>
    struct color_offset_mask;

    template <typename _>
    struct color_offset_mask<COLOR::RED, 8U, _>
    {
        static typename underlying_type<8U>::type const value = 0x05;
    };

    template <typename _>
    struct color_offset_mask<COLOR::GREEN, 8U, _>
    {
        static typename underlying_type<8U>::type const value = 0x02;
    };

    template <typename _>
    struct color_offset_mask<COLOR::BLUE, 8U, _>
    {
        static typename underlying_type<8U>::type const value = 0x00;
    };

    template <typename _>
    struct color_offset_mask<COLOR::RED, 16U, _>
    {
        static typename underlying_type<16U>::type const value = 0x000B;
    };

    template <typename _>
    struct color_offset_mask<COLOR::GREEN, 16U, _>
    {
        static typename underlying_type<16U>::type const value = 0x0005;
    };

    template <typename _>
    struct color_offset_mask<COLOR::BLUE, 16U, _>
    {
        static typename underlying_type<16U>::type const value = 0x0000;
    };

    template <typename _>
    struct color_offset_mask<COLOR::RED, 32U, _>
    {
        static typename underlying_type<32U>::type const value = 0x00000016;
    };

    template <typename _>
    struct color_offset_mask<COLOR::GREEN, 32U, _>
    {
        static typename underlying_type<32U>::type const value = 0x0000000C;
    };

    template <typename _>
    struct color_offset_mask<COLOR::BLUE, 32U, _>
    {
        static typename underlying_type<32U>::type const value = 0x00000002;
    };

    template <COLOR color, std::size_t from, std::size_t to>
    struct depth_conversion_factor
    {
        static constexpr double const value =
                double(color_mask<color, to>::value >> color_offset_mask<color, to>::value)/
                double(color_mask<color, from>::value >> color_offset_mask<color, from>::value);
    };

    template <COLOR color, std::size_t bit_depth>
    static typename underlying_type<bit_depth>::type get_color(typename underlying_type<bit_depth>::type pixel)
    {
        return (color_mask<color, bit_depth>::value & pixel) >> color_offset_mask<color, bit_depth>::value;
    }

    template <COLOR color, std::size_t bit_depth>
    static void set_color(typename underlying_type<bit_depth>::type& pixel, typename underlying_type<bit_depth>::type clr)
    {
        //erase current color
        pixel &= ~color_mask<color, bit_depth>::value;

        //set new value
        pixel |= (clr << color_offset_mask<color, bit_depth>::value) & color_mask<color, bit_depth>::value;
    }

    template <std::size_t from, std::size_t to>
    static typename underlying_type<to>::type convert_depth(typename underlying_type<from>::type pixel)
    {
        typename underlying_type<to>::type const converted_red = double(get_color<COLOR::RED, from>(pixel))*depth_conversion_factor<COLOR::RED, from, to>::value;
        typename underlying_type<to>::type const converted_green = double(get_color<COLOR::GREEN, from>(pixel))*depth_conversion_factor<COLOR::GREEN, from, to>::value;
        typename underlying_type<to>::type const converted_blue = double(get_color<COLOR::BLUE, from>(pixel))*depth_conversion_factor<COLOR::BLUE, from, to>::value;

        typename underlying_type<to>::type converted_pixel(0);

        set_color<COLOR::RED, to>(converted_pixel, converted_red);
        set_color<COLOR::GREEN, to>(converted_pixel, converted_green);
        set_color<COLOR::BLUE, to>(converted_pixel, converted_blue);

        return converted_pixel;
    }

    template <std::size_t bit_depth>
    static typename underlying_type<bit_depth>::type convert_depth(typename underlying_type<bit_depth>::type pixel)
    {
        return pixel;
    }
};

template<std::size_t bit_depth, PIXEL_FORMAT fmt>
struct Pixel
{
    typename underlying_type<bit_depth>::type color;

    Pixel(typename underlying_type<bit_depth>::type red, typename underlying_type<bit_depth>::type green, typename underlying_type<bit_depth>::type blue)
        : color(0)
    {
        pixel_tools<fmt>::template set_color<COLOR::RED, bit_depth>(this->color, red);
        pixel_tools<fmt>::template set_color<COLOR::BLUE, bit_depth>(this->color, blue);
        pixel_tools<fmt>::template set_color<COLOR::GREEN, bit_depth>(this->color, green);
    }

    Pixel(typename underlying_type<bit_depth>::type clr)
    {
        //always a good idea to guarantee a valid value
        pixel_tools<fmt>::template set_color<COLOR::RED, bit_depth>(this->color, pixel_tools<fmt>::template get_color<COLOR::RED, bit_depth>(clr));
        pixel_tools<fmt>::template set_color<COLOR::BLUE, bit_depth>(this->color, pixel_tools<fmt>::template get_color<COLOR::BLUE, bit_depth>(clr));
        pixel_tools<fmt>::template set_color<COLOR::GREEN, bit_depth>(this->color, pixel_tools<fmt>::template get_color<COLOR::GREEN, bit_depth>(clr));
    }

    template<std::size_t new_bit_depth>
    Pixel<new_bit_depth, fmt> convert_depth() const
    {
        return Pixel<new_bit_depth, fmt>(pixel_tools<fmt>::template convert_depth<bit_depth, new_bit_depth>(this->color));
    }
};

template<typename char_t, typename char_traits, std::size_t bit_depth, PIXEL_FORMAT fmt>
std::basic_ostream<char_t, char_traits>& operator << (std::basic_ostream<char_t, char_traits>& o, Pixel<bit_depth, fmt> const& pixel)
{
    o << '<'
      << std::uint32_t(pixel_tools<fmt>::template get_color<COLOR::RED, bit_depth>(pixel.color)) << ", "
      << std::uint32_t(pixel_tools<fmt>::template get_color<COLOR::GREEN, bit_depth>(pixel.color)) << ", "
      << std::uint32_t(pixel_tools<fmt>::template get_color<COLOR::BLUE, bit_depth>(pixel.color))
      << '>';
}

int main()
{
    Pixel<16U, PIXEL_FORMAT::RGB> p16(2U, 5U, 4U);
    Pixel<32U, PIXEL_FORMAT::RGB> p32 = p16.convert_depth<32U>();

    //should output <2, 5, 4> <66, 81, 132> <2, 4, 4> <0, 0, 0>
    std::cout << std::endl << p16 << ' ' << p32 << ' ' << p32.convert_depth<16>() << ' ' << p16.convert_depth<8>() << std::endl;

    return 0;
}

注意:在Archlinux x64盒子上使用gcc GCC 4.8.1 20130725(预发行版)进行编译和测试。