Skip to content

Instantly share code, notes, and snippets.

@JohnnyonFlame
Last active April 27, 2017 19:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JohnnyonFlame/21f6230e518c680dcec1a2803be43dda to your computer and use it in GitHub Desktop.
Save JohnnyonFlame/21f6230e518c680dcec1a2803be43dda to your computer and use it in GitHub Desktop.
Color conversion C++14 stuff
#include <iostream>
#include <ios>
#include <iomanip>
#include <stdint.h>
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
template <typename T, T Mask>
struct Channel
{
public:
static constexpr u32 get_shift()
{
T msk = mask;
u32 r = 0;
for (; !(msk & 1); r++)
msk >>= 1;
return r;
}
static constexpr u32 get_count()
{
T msk = mask >> shift;
u32 r = 0;
for (; msk & 1; r++)
msk >>= 1;
return r;
}
Channel operator=(u32 val)
{
data = (data & ~mask) | (val << shift);
}
Channel operator=(Channel other)
{
data = (data & ~mask) | (other.data & other.mask);
}
template <typename T_, T_ m>
Channel operator=(Channel<T_, m> other)
{
T nmask = mask;
//If left channel is bigger than right channel, it makes sense to filter out garbage from the lower channels.
if (count > other.count)
nmask &= ~((1 << (count - other.count + shift)) - 1);
//Calculate displacement
int nshift = other.shift - shift + (other.count - count);
//Use previous values to displace & mask the desired value
T masked;
if (nshift > 0)
masked = (other.data >> nshift) & nmask;
else
masked = (other.data << (-nshift)) & nmask;
//Set value.
data = (data & ~mask) | masked;
}
T get()
{
return (data & mask) >> shift;
}
static const T mask = Mask;
static const u32 shift = get_shift();
static const u32 count = get_count();
T data;
};
template <typename T, T Rmask, T Gmask, T Bmask>
struct Color
{
public:
Color operator=(const u8 (&arr)[3])
{
r = arr[2];
g = arr[1];
b = arr[0];
}
//Same type
Color operator=(Color other)
{
data = other.data;
}
//Different encodings
template <typename T_, T_ rm_, T_ gm_, T_ bm_>
Color operator=(Color<T_, rm_, gm_, bm_> other)
{
r = other.r;
g = other.g;
b = other.b;
}
union
{
T data;
Channel<T, Rmask> r;
Channel<T, Gmask> g;
Channel<T, Bmask> b;
};
};
__attribute__((noinline)) auto builda(u8 r, u8 g, u8 b)
{
Color<u32, 0xff << 16, 0xff << 8, 0xff> c1;
c1 = {r, g, b};
return c1;
}
template <typename T>
__attribute__((noinline)) auto builda2(T color)
{
Color<u32, 0xff << 16, 0xff << 8, 0xff> c;
c = color;
return c;
}
template <typename T>
__attribute__((noinline)) auto buildb(T color)
{
Color<u16, 0x1f << 11, 0x3f << 5, 0x1f> c2;
c2 = color;
return c2;
}
template <typename T>
__attribute__((noinline)) auto buildman(T color)
{
Color<u16, 0x1f << 11, 0x3f << 5, 0x1f> c2;
c2.data = ((color.data >> 8) & c2.r.mask);
c2.data |= ((color.data >> 5) & c2.g.mask);
c2.data |= ((color.data >> 3) & c2.b.mask);
return c2;
}
void print(auto t)
{
int n = sizeof(t);
for (int i = 0; i < n; i++)
{
std::cout << std::setfill('0') << std::setw(2) << ((t >> (((n-1)-i) * 8)) & 0xff);
std::cout << ((i == n-1) ? '\n' : ' ');
}
}
int main()
{
u32 r, g, b;
std::cin >> r >> g >> b;
auto c1 = builda((u8)r, (u8)g, (u8)b);
auto c2 = buildb(c1);
auto c3 = buildman(c1);
std::cout << std::hex;
std::cout << c1.r.get() << ' '
<< c1.g.get() << ' '
<< c1.b.get() << '\n';
std::cout << c2.r.get() << ' '
<< c2.g.get() << ' '
<< c2.b.get() << '\n';
std::cout << c3.r.get() << ' '
<< c3.g.get() << ' '
<< c3.b.get() << '\n';
auto c4 = builda2(c2);
std::cout << c4.r.get() << ' '
<< c4.g.get() << ' '
<< c4.b.get() << '\n';
}
@JohnnyonFlame
Copy link
Author

JohnnyonFlame commented Apr 27, 2017

x86-64 g++ 6.3, opts -O2 -std=c++17

auto buildb<Color<unsigned int, 16711680u, 65280u, 255u> >(Color<unsigned int, 16711680u, 65280u, 255u>):
        mov     eax, edi
        mov     edx, edi
        shr     edi, 3
        shr     eax, 8
        shr     edx, 5
        and     edi, 31
        and     ax, -2048
        and     dx, 2016
        or      eax, edx
        or      eax, edi
        ret
auto buildman<Color<unsigned int, 16711680u, 65280u, 255u> >(Color<unsigned int, 16711680u, 65280u, 255u>):
        mov     eax, edi
        mov     edx, edi
        shr     edi, 3
        shr     eax, 8
        shr     edx, 5
        and     edi, 31
        and     ax, -2048
        and     dx, 2016
        or      eax, edx
        or      eax, edi
        ret

@JohnnyonFlame
Copy link
Author

JohnnyonFlame commented Apr 27, 2017

Create 32bpp color from u8 array:

builda(unsigned char, unsigned char, unsigned char):
        movzx   eax, dl
        movzx   esi, sil
        movzx   edi, dil
        sal     eax, 16
        sal     esi, 8
        or      eax, esi
        or      eax, edi
        ret

Convert 16bpp to 32bpp:

auto builda2<Color<unsigned short, (unsigned short)63488, (unsigned short)2016, (unsigned short)31> >(Color<unsigned short, (unsigned short)63488, (unsigned short)2016, (unsigned short)31>):
        movzx   edi, di
        mov     eax, edi
        mov     edx, edi
        sal     edi, 3
        sal     eax, 8
        sal     edx, 5
        and     eax, 16252928
        and     edx, 64512
        or      edx, eax
        movzx   eax, dil
        or      eax, edx
        ret

@JohnnyonFlame
Copy link
Author

JohnnyonFlame commented Apr 27, 2017

mips32-r2 mipsel-linux-g++ version 6.2.1:

Dump of assembler code for function buildb<Color<unsigned int, 16711680u, 65280u, 255u> >(Color<unsigned int, 16711680u, 65280u, 255u>):
   0x00400e20 <+0>:     srl     v1,a1,0x8
   0x00400e24 <+4>:     srl     a2,a1,0x5
   0x00400e28 <+8>:     li      v0,-2048
   0x00400e2c <+12>:    andi    a2,a2,0x7e0
   0x00400e30 <+16>:    and     v1,v1,v0
   0x00400e34 <+20>:    ext     a1,a1,0x3,0x5
   0x00400e38 <+24>:    or      v1,v1,a2
   0x00400e3c <+28>:    or      v1,v1,a1
   0x00400e40 <+32>:    move    v0,a0
   0x00400e44 <+36>:    jr      ra
   0x00400e48 <+40>:    sh      v1,0(a0)
Dump of assembler code for function buildman<Color<unsigned int, 16711680u, 65280u, 255u> >(Color<unsigned int, 16711680u, 65280u, 255u>):
   0x00400e4c <+0>:     srl     v1,a1,0x8
   0x00400e50 <+4>:     srl     a2,a1,0x5
   0x00400e54 <+8>:     li      v0,-2048
   0x00400e58 <+12>:    andi    a2,a2,0x7e0
   0x00400e5c <+16>:    and     v1,v1,v0
   0x00400e60 <+20>:    ext     a1,a1,0x3,0x5
   0x00400e64 <+24>:    or      v1,v1,a2
   0x00400e68 <+28>:    or      v1,v1,a1
   0x00400e6c <+32>:    move    v0,a0
   0x00400e70 <+36>:    jr      ra
   0x00400e74 <+40>:    sh      v1,0(a0)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment