Haio 0.2.X
Homebrew Asset IO/CDN
Loading...
Searching...
No Matches
rgb888.cpp
Go to the documentation of this file.
1#include <haio_convert.hpp>
2
3#include <cstring>
4
5#if defined(__x86_64__) || defined(__i386__)
6#define HAIO_X86 1
7#include <immintrin.h>
8#endif
9
10namespace {
11
12void packScalar(const uint8_t* src, uint8_t* dst, size_t pixels) {
13 while (pixels--) {
14 dst[0] = src[0];
15 dst[1] = src[1];
16 dst[2] = src[2];
17 src += 4;
18 dst += 3;
19 }
20}
21
22#if HAIO_X86
23
24/**
25 * twelve bytes out of a sixteen byte register: eight with one store and four with a
26 * memcpy, because there is no twelve byte store and writing sixteen would run past
27 * the end of the last block.
28 */
29inline void store12(uint8_t* dst, __m128i packed) {
30 _mm_storel_epi64(reinterpret_cast<__m128i*>(dst), packed);
31 const auto tail = static_cast<uint32_t>(_mm_cvtsi128_si32(_mm_srli_si128(packed, 8)));
32 std::memcpy(dst + 8, &tail, sizeof(tail));
33}
34
35__attribute__((target("avx2")))
36void packAvx2(const uint8_t* src, uint8_t* dst, size_t pixels) {
37 const __m256i shuffle = _mm256_setr_epi8(
38 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, -1, -1, -1, -1,
39 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, -1, -1, -1, -1);
40
41 const size_t wide = pixels & ~size_t{7};
42 for (size_t at = 0; at < wide; at += 8) {
43 const __m256i loaded = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(src));
44 const __m256i packed = _mm256_shuffle_epi8(loaded, shuffle);
45
46 // the shuffle works on each 128 bit lane on its own, so the halves land apart
47 store12(dst, _mm256_castsi256_si128(packed));
48 store12(dst + 12, _mm256_extracti128_si256(packed, 1));
49
50 src += 32;
51 dst += 24;
52 }
53 packScalar(src, dst, pixels - wide);
54}
55
56__attribute__((target("ssse3")))
57void packSsse3(const uint8_t* src, uint8_t* dst, size_t pixels) {
58 const __m128i shuffle = _mm_setr_epi8(0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, -1, -1, -1, -1);
59
60 const size_t wide = pixels & ~size_t{3};
61 for (size_t at = 0; at < wide; at += 4) {
62 store12(dst, _mm_shuffle_epi8(_mm_loadu_si128(reinterpret_cast<const __m128i*>(src)), shuffle));
63 src += 16;
64 dst += 12;
65 }
66 packScalar(src, dst, pixels - wide);
67}
68
69#endif
70
71/**
72 * ported from BufferPush43 in the c branch, with two of its bugs left behind: it
73 * rounded capacity with "& ~32", which clears one bit instead of aligning, and it
74 * reported an error for an empty input that its callers treated as fatal.
75 */
76void pack(const uint8_t* src, uint8_t* dst, size_t pixels) {
77#if HAIO_X86
78 if (__builtin_cpu_supports("avx2")) return packAvx2(src, dst, pixels);
79 if (__builtin_cpu_supports("ssse3")) return packSsse3(src, dst, pixels);
80#endif
81 packScalar(src, dst, pixels);
82}
83
84}
85
86namespace Haio::Codecs {
87
88/**
89 * @addtogroup move
90 * @{
91 */
92/** drop the alpha channel */
93template <>
94Result<void> Move<Color::RGBA8888, Color::RGB888>(Bytes src, std::span<uint8_t> dst, Size size) {
95 const auto pixels = static_cast<size_t>(size.width) * static_cast<size_t>(size.height);
96 if (src.size() < pixels * 4 || dst.size() < pixels * 3) {
97 HAIO_FAIL(InvalidInput, "rgba8888 to rgb888 got a buffer that is too small");
98 }
99 if (pixels == 0) return {};
100
101 pack(src.data(), dst.data(), pixels);
102 return {};
103}
104/** @} */
105
106/**
107 * @addtogroup convert
108 * @{
109 */
110template <>
112 return convertVia<Color::RGBA8888, Color::RGB888>(std::move(src));
113}
114/** @} */
115
116}
Result< Image< Color::RGB888 > > Convert< Color::RGBA8888, Color::RGB888 >(Image< Color::RGBA8888 > src)
Definition rgb888.cpp:111
Result< void > Move< Color::RGBA8888, Color::RGB888 >(Bytes src, std::span< uint8_t > dst, Size size)
Definition rgb888.cpp:94
#define HAIO_FAIL(why, message)
Result< Image< To > > convertVia(Image< From > src)
std::expected< T, Error > Result
std::span< const uint8_t > Bytes