跳转至

ui/base/color_helper.cpp

Color helper functions for Material Design 3. More...

Namespaces

Name
cf
cf::ui
cf::ui::base

Detailed Description

Color helper functions for Material Design 3.

Author: Charliechen114514 (chengh1922@mails.jlu.edu.cn)

Version: 0.1

Date: 2026-02-23

Copyright: Copyright © 2026

Source code

#include "color_helper.h"
#include "math_helper.h"

namespace cf::ui::base {

// ============================================================================
// Helper functions (internal)
// ============================================================================

namespace {
// Elevation overlay alpha values for elevation levels 0-5
// Material Design 3 specification
constexpr float ELEVATION_ALPHA[] = {
    0.00f, // Level 0
    0.05f, // Level 1
    0.08f, // Level 2
    0.11f, // Level 3
    0.14f, // Level 4
    0.17f  // Level 5
};

// Tonal palette tone values (13 tones from key color)
constexpr float TONAL_VALUES[] = {
    0.0f,  // Near black
    10.0f, // Very dark
    20.0f, // Dark
    30.0f, // Dark medium
    40.0f, // Medium dark
    50.0f, // Mid
    60.0f, // Mid light
    70.0f, // Medium light
    80.0f, // Light
    90.0f, // Very light
    95.0f, // Pale
    99.0f, // Near white
    100.0f // Pure white
};
constexpr int TONAL_COUNT = sizeof(TONAL_VALUES) / sizeof(TONAL_VALUES[0]);

} // anonymous namespace

// ============================================================================
// Color Helper Functions
// ============================================================================

CFColor blend(const CFColor& base, CFColor& overlay, float ratio) {
    // Clamp ratio to [0, 1]
    float t = math::clamp(ratio, 0.0f, 1.0f);

    // Get RGB components
    float baseR = base.native_color().redF();
    float baseG = base.native_color().greenF();
    float baseB = base.native_color().blueF();
    float baseA = base.native_color().alphaF();

    float overlayR = overlay.native_color().redF();
    float overlayG = overlay.native_color().greenF();
    float overlayB = overlay.native_color().blueF();
    float overlayA = overlay.native_color().alphaF();

    // Linear interpolation for each component
    float r = math::lerp(baseR, overlayR, t);
    float g = math::lerp(baseG, overlayG, t);
    float b = math::lerp(baseB, overlayB, t);
    float a = math::lerp(baseA, overlayA, t);

    // Convert to integer RGB for consistent comparison
    int rInt = static_cast<int>(r * 255.0f + 0.5f);
    int gInt = static_cast<int>(g * 255.0f + 0.5f);
    int bInt = static_cast<int>(b * 255.0f + 0.5f);
    int aInt = static_cast<int>(a * 255.0f + 0.5f);

    return CFColor(QColor(rInt, gInt, bInt, aInt));
}

CFColor elevationOverlay(CFColor& surface, CFColor& primary, int elevation) {
    // Clamp elevation to [0, 5]
    int level = math::clamp(elevation, 0, 5);
    float alpha = ELEVATION_ALPHA[level];

    // Blend primary color over surface with elevation alpha
    return blend(surface, primary, alpha);
}

float contrastRatio(CFColor& a, CFColor& b) {
    // WCAG 2.1 contrast ratio formula
    // ratio = (L1 + 0.05) / (L2 + 0.05)
    // where L1 is the lighter (higher luminance) and L2 is the darker

    float lumA = a.relativeLuminance();
    float lumB = b.relativeLuminance();

    float lighter = std::max(lumA, lumB);
    float darker = std::min(lumA, lumB);

    return (lighter + 0.05f) / (darker + 0.05f);
}

QList<CFColor> tonalPalette(CFColor keyColor) {
    QList<CFColor> palette;

    // Get HCT values from key color
    float hue = keyColor.hue();
    float chroma = keyColor.chroma();

    // Generate tonal palette by varying tone while keeping hue and chroma constant
    for (int i = 0; i < TONAL_COUNT; ++i) {
        palette.append(CFColor(hue, chroma, TONAL_VALUES[i]));
    }

    return palette;
}

} // namespace cf::ui::base

Updated on 2026-03-09 at 10:14:01 +0000