about summary refs log tree commit diff stats
path: root/js/leibovitz/balance.js
blob: aeff62e69c9a1fbf3fec262245a6dc8ece6cbc9b (plain) (blame)
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
/**
 * White balance management module implementing temperature-based color adjustment.
 * 
 * Implements white balance adjustment using temperature-based RGB channel scaling.
 * Provides non-linear temperature adjustment for natural color correction.
 * 
 * Implements the following design patterns:
 * - Observer Pattern: state management and effect application
 * - Factory Pattern: UI initialization
 * - Strategy Pattern: temperature adjustment algorithm
 * - Command Pattern: state reset operations
 * 
 * White balance adjustment process:
 * 1. Convert temperature to ratio relative to neutral (6500K)
 * 2. Apply non-linear scaling (0.2 factor) to red and blue channels
 * 3. Warmer temps (<6500K) increase red, decrease blue
 * 4. Cooler temps (>6500K) increase blue, decrease red
 * 
 * Features:
 * - Temperature-based color adjustment
 * - Non-linear response curve
 * - Preserves green channel
 * - Real-time updates
 */

const BalanceManager = {
    // Private state
    _observers: new Set(),
    _slider: null,
    _value: null,

    /**
     * Initializes the balance manager and sets up UI controls
     */
    init() {
        this._slider = document.getElementById('balance-slider');
        this._value = document.getElementById('balance-value');
        this._setupEventListeners();
    },

    _setupEventListeners() {
        this._slider.addEventListener('input', () => {
            const value = this._slider.value;
            this._value.textContent = `${value}K`;
            this._notifyObservers();
        });
    },

    _notifyObservers() {
        this._observers.forEach(observer => observer(this.getCurrentBalance()));
    },

    /**
     * Subscribes to balance state changes
     * @param {Function} observer - Callback function for state changes
     * @returns {Function} Unsubscribe function
     */
    subscribe(observer) {
        this._observers.add(observer);
        return () => this._observers.delete(observer);
    },

    /**
     * Gets the current white balance temperature
     * @returns {number} Current temperature in Kelvin (2000K-10000K)
     */
    getCurrentBalance() {
        return parseInt(this._slider.value);
    },

    /**
     * Applies white balance adjustment to an image
     * And implements temperature-based RGB channel scaling with non-linear response
     * @param {ImageData} imageData - Source image data
     * @returns {ImageData} White balanced image data
     */
    applyBalance(imageData) {
        const balance = this.getCurrentBalance();
        if (!balance || balance === 6500) return imageData; // 6500K is neutral

        const data = imageData.data;
        const temperature = balance / 6500; // Convert to temperature ratio

        for (let i = 0; i < data.length; i += 4) {
            // Adjust red and blue channels based on temperature
            // Warmer (lower K) increases red, decreases blue
            // Cooler (higher K) increases blue, decreases red
            data[i] = Math.min(255, data[i] * (1 + (temperature - 1) * 0.2));     // Red
            data[i + 2] = Math.min(255, data[i + 2] * (1 + (1 - temperature) * 0.2)); // Blue
        }

        return imageData;
    },

    /**
     * Resets balance effect to default state
     */
    reset() {
        this._slider.value = 6500;
        this._value.textContent = '6500K';
        this._notifyObservers();
    }
};