1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
// Convert-feature TU: built only when PIXPAT_FEATURE_CONVERT is on
// (controlled by the meson source list). pixpat.cpp's pixpat_convert
// entry calls into dispatch_convert() below via if-constexpr; when the
// feature is off this file isn't compiled, the discarded if-constexpr
// branch emits no symbol reference, and the .so simply lacks these
// symbols.
#include <cassert>
#include <cstdint>
#include <cstring>
#include <vector>
#include "color.h"
#include "error.h"
#include "format_catalog.h"
#include "formats.h"
#include "io.h"
#include "layout.h"
#include "pattern.h"
#include "pipeline.h"
#include "pixpat_internal.h"
namespace pixpat
{
template <typename Src, typename Snk>
static void run_convert_impl(const pixpat_buffer* src, const pixpat_buffer* dst,
size_t W, size_t H,
size_t by_start, size_t by_end,
ColorSpec spec)
{
using SL = typename Src::Layout;
using DL = typename Snk::Layout;
// Entry point (pixpat_convert) validates W/H against each layout's
// h_sub / v_sub, plus the sink's block dims.
assert(W % SL::h_sub == 0 && W % DL::h_sub == 0);
assert(H % SL::v_sub == 0 && H % DL::v_sub == 0);
auto sb = make_buffer<SL>(src);
auto db = make_buffer<DL>(dst);
Converter<Src, Snk>::run(sb, db, W, H, by_start, by_end, spec);
}
static void run_norm(FormatId src_id, FormatId dst_id,
const pixpat_buffer* src, const pixpat_buffer* dst,
size_t W, size_t H,
size_t by_start, size_t by_end,
ColorSpec spec)
{
const auto& si = s_format_info[size_t(src_id)];
const auto& di = s_format_info[size_t(dst_id)];
const size_t bh = di.snk_block_h;
// Entry point (pixpat_convert) guarantees W/H alignment to each
// of si.h_sub / si.v_sub and di.snk_block_w / di.snk_block_h.
assert(W % si.h_sub == 0 && W % di.snk_block_w == 0);
assert(H % si.v_sub == 0 && H % bh == 0);
// Per-thread normalized line buffer. RGB16 and YUV16 are both 8
// bytes, so one allocation works for both. thread_local gives each
// worker its own buffer when called from run_stripes.
thread_local std::vector<uint8_t> norm;
norm.resize(bh * W * sizeof(RGB16));
const ColorCoeffs c = coeffs_for(spec);
for (size_t by = by_start; by < by_end; by += bh) {
si.unpack(norm.data(), src, by, bh, W);
if (si.kind != di.kind) {
const size_t n = bh * W;
if (si.kind == ColorKind::RGB)
norm_rgb_to_yuv(norm.data(), n, c);
else
norm_yuv_to_rgb(norm.data(), n, c);
}
di.pack(dst, norm.data(), by, W);
}
}
// Generated: FormatCaps + s_format_caps[] (per-format readable/writable
// + hot_src/hot_dst), plus s_pattern_* / DefaultPattern.
#include "pixpat_caps.inc"
// Per-Src dispatch: pick the right Sink for `dst_id` and call
// run_convert_impl. The X-macro emits one case per catalog format;
// `if constexpr (...writable)` discards the body for non-writable
// formats — those cases fall to the trailing throw.
template <typename Src>
static void dispatch_dst_convert(FormatId dst_id,
const pixpat_buffer* src, const pixpat_buffer* dst,
size_t W, size_t H,
size_t by_start, size_t by_end,
ColorSpec spec)
{
switch (dst_id) {
#define CAPS(name) s_format_caps[size_t(FormatId::name)]
#define X(name) \
case FormatId::name: \
if constexpr (CAPS(name).writable) { \
run_convert_impl<Src, formats::name::Sink>( \
src, dst, W, H, by_start, by_end, spec); \
return; \
} \
break;
PIXPAT_FORMAT_LIST(X)
#undef X
#undef CAPS
default:
break;
}
throw invalid_argument("destination format not enabled in this build");
}
// Per-Snk dispatch: mirror of dispatch_dst_convert.
template <typename Snk>
static void dispatch_src_convert(FormatId src_id,
const pixpat_buffer* src, const pixpat_buffer* dst,
size_t W, size_t H,
size_t by_start, size_t by_end,
ColorSpec spec)
{
switch (src_id) {
#define CAPS(name) s_format_caps[size_t(FormatId::name)]
#define X(name) \
case FormatId::name: \
if constexpr (CAPS(name).readable) { \
run_convert_impl<formats::name::Source, Snk>( \
src, dst, W, H, by_start, by_end, spec); \
return; \
} \
break;
PIXPAT_FORMAT_LIST(X)
#undef X
#undef CAPS
default:
break;
}
throw invalid_argument("source format not enabled in this build");
}
// Hot-pivot probes. The wrapper has to be a template so that the
// discarded `if constexpr` branch is not instantiated — otherwise
// dispatch_dst_convert<formats::X::Source> would be instantiated for
// every catalog format, not just hot pivots.
template <bool HotSrc, FormatId Id, typename Source>
static bool try_hot_src(FormatId src_id, FormatId dst_id,
const pixpat_buffer* src, const pixpat_buffer* dst,
size_t W, size_t H,
size_t by_start, size_t by_end,
ColorSpec spec)
{
if constexpr (HotSrc) {
if (src_id == Id) {
dispatch_dst_convert<Source>(
dst_id, src, dst, W, H, by_start, by_end, spec);
return true;
}
}
return false;
}
template <bool HotDst, FormatId Id, typename Sink>
static bool try_hot_dst(FormatId src_id, FormatId dst_id,
const pixpat_buffer* src, const pixpat_buffer* dst,
size_t W, size_t H,
size_t by_start, size_t by_end,
ColorSpec spec)
{
if constexpr (HotDst) {
if (dst_id == Id) {
dispatch_src_convert<Sink>(
src_id, src, dst, W, H, by_start, by_end, spec);
return true;
}
}
return false;
}
void dispatch_convert(FormatId src_id, FormatId dst_id,
const pixpat_buffer* src, const pixpat_buffer* dst,
size_t W, size_t H,
size_t by_start, size_t by_end,
ColorSpec spec)
{
#define CAPS(name) s_format_caps[size_t(FormatId::name)]
#define X(name) \
if (try_hot_src<CAPS(name).hot_src, FormatId::name, \
formats::name::Source>( \
src_id, dst_id, src, dst, W, H, by_start, by_end, spec)) \
return; \
if (try_hot_dst<CAPS(name).hot_dst, FormatId::name, \
formats::name::Sink>( \
src_id, dst_id, src, dst, W, H, by_start, by_end, spec)) \
return;
PIXPAT_FORMAT_LIST(X)
#undef X
#undef CAPS
run_norm(src_id, dst_id, src, dst, W, H, by_start, by_end, spec);
}
} // namespace pixpat
|