summaryrefslogtreecommitdiff
path: root/pixpat-native/src/pixpat_pattern.cpp
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2026-05-08 17:22:58 +0300
committerTomi Valkeinen <tomi.valkeinen@ideasonboard.com>2026-05-08 17:22:58 +0300
commite0b7d30fd437292c88141fb08d60681870b86c6e (patch)
tree7d7f4e94cbec0f4f494042f7cbf39c7c8e7234fe /pixpat-native/src/pixpat_pattern.cpp
Squashed 'subprojects/pixpat/' content from commit d444626
git-subtree-dir: subprojects/pixpat git-subtree-split: d444626e6ba988ec6d487800721e447f94b1eaf5
Diffstat (limited to 'pixpat-native/src/pixpat_pattern.cpp')
-rw-r--r--pixpat-native/src/pixpat_pattern.cpp168
1 files changed, 168 insertions, 0 deletions
diff --git a/pixpat-native/src/pixpat_pattern.cpp b/pixpat-native/src/pixpat_pattern.cpp
new file mode 100644
index 0000000..e8ac780
--- /dev/null
+++ b/pixpat-native/src/pixpat_pattern.cpp
@@ -0,0 +1,168 @@
+// Pattern-feature TU: built only when PIXPAT_FEATURE_PATTERN is on
+// (controlled by the meson source list). pixpat.cpp's pixpat_draw_pattern
+// entry calls into dispatch_draw_pattern() 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 <string_view>
+#include <type_traits>
+#include <vector>
+
+#include "color.h"
+#include "error.h"
+#include "params.h"
+#include "pattern.h"
+#include "pattern_catalog.h"
+#include "pipeline.h"
+#include "pixpat_internal.h"
+
+namespace pixpat
+{
+
+// Generated: s_pattern_* enable flags + DefaultPattern alias. Included
+// inside namespace pixpat so the unqualified FormatId / s_format_catalog_count
+// references resolve.
+#include "pixpat_caps.inc"
+
+// Cold pattern path: fill a per-thread normalized line buffer with
+// Pattern samples in the pattern's native color kind, run a cross-
+// color-kind pass over the buffer if the sink wants the other kind,
+// then hand the buffer to the destination's per-format pack via
+// s_format_info. Same shape as run_norm in pixpat_convert.cpp.
+template <typename Pattern>
+static void run_pattern_norm(const Pattern& pat,
+ FormatId dst_id, const pixpat_buffer* dst,
+ size_t W, size_t H,
+ size_t by_start, size_t by_end,
+ ColorSpec spec)
+{
+ using P = typename Pattern::Pixel;
+ constexpr bool pat_is_rgb = std::is_same_v<P, RGB16>;
+
+ const auto& di = s_format_info[size_t(dst_id)];
+ const size_t bh = di.snk_block_h;
+ // Entry point (pixpat_draw_pattern) validates W%bw / H%bh.
+ assert(W % di.snk_block_w == 0 && H % bh == 0);
+
+ thread_local std::vector<uint8_t> norm;
+ norm.resize(bh * W * sizeof(RGB16)); // RGB16 / YUV16 same size
+
+ const ColorCoeffs c = coeffs_for(spec);
+ const bool need_xfm = (pat_is_rgb && di.kind == ColorKind::YUV) ||
+ (!pat_is_rgb && di.kind == ColorKind::RGB);
+
+ for (size_t by = by_start; by < by_end; by += bh) {
+ auto* px = reinterpret_cast<P*>(norm.data());
+ for (size_t dy = 0; dy < bh; ++dy)
+ for (size_t x = 0; x < W; ++x)
+ px[dy * W + x] = pat.sample(x, by + dy, W, H);
+ if (need_xfm) {
+ const size_t n = bh * W;
+ if constexpr (pat_is_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);
+ }
+}
+
+// Construct, ready-check, and run a pattern. Patterns whose colors
+// depend on the call's ColorSpec (e.g. native-YUV bar variants) opt
+// in by exposing a (Params, ColorSpec) constructor; the rest take
+// Params only and stay unchanged.
+template <typename Pattern>
+static void run_one_pattern(const Params& params,
+ FormatId id, const pixpat_buffer* dst,
+ size_t W, size_t H,
+ size_t by_start, size_t by_end,
+ ColorSpec spec)
+{
+ auto pat = [&] {
+ if constexpr (std::is_constructible_v<
+ Pattern, const Params&, ColorSpec>)
+ return Pattern(params, spec);
+ else
+ return Pattern(params);
+ }();
+ if constexpr (requires { pat.ready(); }) {
+ if (!pat.ready())
+ throw invalid_argument("pattern parameters not accepted");
+ }
+ run_pattern_norm(pat, id, dst, W, H, by_start, by_end, spec);
+}
+
+// Per-pattern dispatch arm. Templated on the catalog row's RGB and
+// YUV variants (either may be `void` if the pattern has no variant
+// in that kind). When both are present, the sink kind picks the
+// matching variant so the cross-kind pass is a no-op; when only one
+// is present, the pipeline runs the cross-kind pass for opposite-
+// kind sinks.
+//
+// Wrapping in a templated helper is what keeps the binary size down:
+// `if constexpr (Enabled = false)` discards the run_pattern_norm
+// reference, and because try_pattern is itself a template, the
+// discarded branch is *not instantiated* — so disabled patterns
+// emit no code, and the `void` arms of partial patterns never
+// instantiate `Pattern::Pixel` or run_pattern_norm<void>.
+template <bool Enabled, typename Rgb, typename Yuv>
+static bool try_pattern(std::string_view name, std::string_view want,
+ const Params& params,
+ FormatId id, ColorKind sink_kind,
+ const pixpat_buffer* dst,
+ size_t W, size_t H,
+ size_t by_start, size_t by_end,
+ ColorSpec spec)
+{
+ if constexpr (Enabled) {
+ if (name == want) {
+ constexpr bool has_rgb = !std::is_void_v<Rgb>;
+ constexpr bool has_yuv = !std::is_void_v<Yuv>;
+ static_assert(has_rgb || has_yuv,
+ "pattern needs at least one variant");
+ if constexpr (has_rgb && has_yuv) {
+ if (sink_kind == ColorKind::YUV)
+ run_one_pattern<Yuv>(params, id, dst, W, H,
+ by_start, by_end, spec);
+ else
+ run_one_pattern<Rgb>(params, id, dst, W, H,
+ by_start, by_end, spec);
+ } else if constexpr (has_rgb) {
+ run_one_pattern<Rgb>(params, id, dst, W, H,
+ by_start, by_end, spec);
+ } else {
+ run_one_pattern<Yuv>(params, id, dst, W, H,
+ by_start, by_end, spec);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void dispatch_draw_pattern(FormatId id, const char* pattern_name,
+ const Params& params,
+ const pixpat_buffer* dst,
+ size_t W, size_t H,
+ size_t by_start, size_t by_end,
+ ColorSpec spec)
+{
+ using namespace patterns;
+ // NULL pattern_name selects the default ("kmstest"); see pixpat.h.
+ const std::string_view name = pattern_name ? pattern_name : "kmstest";
+ const ColorKind kind = s_format_info[size_t(id)].kind;
+
+#define X(label, rgb, yuv, str) \
+ if (try_pattern<s_pattern_caps[size_t(PatternId::label)].enabled, rgb, yuv>( \
+ name, str, params, id, kind, dst, W, H, by_start, by_end, spec)) \
+ return;
+ PIXPAT_PATTERN_LIST(X)
+#undef X
+
+ throw invalid_argument("unknown or disabled pattern name");
+}
+
+} // namespace pixpat