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>宣言される前にインスタンス化されたというエラーが表示されます。問題ありません。前方宣言するだけです(コンストラクターも)。すると、部分構造体で使えないというエラーが出ました。

では、さまざまな専門分野の間で変換を試みるにはどうすればよいでしょうか? 構造体定義の外でテンプレート関数を作成しますか? ただし、すべての組み合わせを専門化する必要がありますが、各専門化で 2 つの変換を定義した場合は、単純にそれらを「連鎖」させ、必要な結果が得られるまで次から次へと変換することができます (たとえば、8 から-bit RGB から 24 ビット RGB 内部で _16rgb と _24rgb の両方の変換を行う代わりに、_8rgb -> _16rgb の変換を定義してから、_16rgb から _24rgb への変換を定義できますPixel<8, RGB>) 。

リクエストにより、小さな例です。

4

2 に答える 2

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」の同義語として使用し、同様に「std::uint32_t」の同義語として使用しますunsigned int。これは、すべてのプラットフォームで保持する必要はなく、同じプラットフォームの異なるコンパイラ バージョン間でも保持する必要がないため、強くにある前述の固定サイズの整数を使用することをお勧めします。

コード全体で定数を多用していることと、かなりの量のコードが重複していることから、ほぼ確実に、コードを保守したり、コードを機能させたりするのに多くの困難に直面することになるでしょう。そうは言っても、それが私の傲慢に聞こえないことを願っていますが、あなたがアドバイスを求めてここに来た特定の問題を修正することは別として、これらのピクセル抽象化を処理するまったく新しい方法を提案する自由を取りました.

以下のコード スニペットでは、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 (プレリリース) を使用してコンパイルおよびテストされています。

于 2013-09-02T04:31:51.237 に答える