// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include "ColorScale.h" #include "JFJochException.h" float luminance(rgb input) { return 0.2126f * input.r + 0.7152f * input.g + 0.0722f * input.b; } void ColorScale::Select(ColorScaleEnum val) { current = val; } rgb ColorScale::Apply(float input, const std::vector &map) { size_t num_colors = map.size(); if (num_colors < 2) { throw std::invalid_argument("Colormap must have at least two colors."); } float scaled_value = input * (num_colors - 1); size_t lower_idx = static_cast(scaled_value); size_t upper_idx = std::min(lower_idx + 1, num_colors - 1); float t = scaled_value - lower_idx; // Fraction for interpolation rgb lower = map[lower_idx]; rgb upper = map[upper_idx]; return { .r = static_cast(lower.r + t * (upper.r - lower.r)), .g = static_cast(lower.g + t * (upper.g - lower.g)), .b = static_cast(lower.b + t * (upper.b - lower.b)) }; } rgb ColorScale::Apply(ColorScaleSpecial input) const { switch (input) { case ColorScaleSpecial::Gap: return gap; default: case ColorScaleSpecial::BadPixel: return bad; } } rgb ColorScale::Apply(float input, float min, float max) const { // Assume min and max is finite if (!std::isfinite(input)) return gap; float f; if (input <= min) f = 0.0f; else if (input >= max) f = 1.0f; else f = (input - min) / (max - min); switch (current) { case ColorScaleEnum::Viridis: return Apply(f, viridis_colormap); case ColorScaleEnum::Heat: return Apply(f, heat_colormap); case ColorScaleEnum::Indigo: return Apply(f, white_to_indigo_colormap); case ColorScaleEnum::BW: return Apply(f, white_to_black_colormap); default: throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Color scale unknown"); } } ColorScale &ColorScale::Gap(rgb input) { gap = input; return *this; } ColorScale &ColorScale::BadPixel(rgb input) { bad = input; return *this; } rgb rainbowColor(float t) { // Ensure t is in [0,1] t = std::max(0.0f, std::min(1.0f, t)); // Convert to hue (0 to 6) float hue = t * 6.0f; int phase = static_cast(hue); float fract = hue - phase; uint8_t p = static_cast(255 * (1.0f - fract)); uint8_t q = static_cast(255 * fract); uint8_t full = 255; switch (phase) { case 0: return {full, q, 0}; // Red to Yellow case 1: return {p, full, 0}; // Yellow to Green case 2: return {0, full, q}; // Green to Cyan case 3: return {0, p, full}; // Cyan to Blue case 4: return {q, 0, full}; // Blue to Magenta case 5: return {full, 0, p}; // Magenta to Red default: return {full, 0, 0}; // Fallback (red) } }