2

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

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

PIXEL_FORMAT作为一个enum 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 -> _16rgb 转换,然后是 _16rgb 到 _24rgb 转换,而不是 _16rgb 和 _24rgb 内部转换Pixel<8, RGB>

应要求,一个小例子

4

2 回答 2

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;
}

注意convert_to_rgb16是定义了Pixel之外的特化为8位,之后定义了Pixel的特化为16位。

于 2013-09-02T12:50:23.180 回答
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中,该类适用于给定像素格式的任何可能的深度,从而减少代码的复制。

我不会假装我的解决方案在这种情况下是最好的,事实上我很确定它不是,但我相信它在代码维护方面比你的解决方案有所改进。从现在开始,如果你决定采纳我的任何建议,我相信将这个想法扩展到你正在使用的其他像素抽象上是很简单的。

注意:这个代码片段很可能包含错误,请原谅,但我没有时间彻底检查它。

#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 (prerelease) 编译和测试。

于 2013-09-02T04:31:51.237 回答