diff options
author | elioat <elioat@tilde.institute> | 2025-03-30 09:31:14 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-03-30 09:31:14 -0400 |
commit | c169f89e17f4a241ac128c236876d244ba392d4e (patch) | |
tree | aa666f654ea75552761bba01a6760e0e5bd894fd | |
parent | 172a0d1b2a817ce75c1d9d1572b3cc8eee5d9f26 (diff) | |
download | tour-c169f89e17f4a241ac128c236876d244ba392d4e.tar.gz |
*
-rw-r--r-- | js/leibovitz/balance.js | 37 | ||||
-rw-r--r-- | js/leibovitz/color.js | 10 | ||||
-rw-r--r-- | js/leibovitz/index.html | 14 | ||||
-rw-r--r-- | js/leibovitz/leibovitz.js | 27 |
4 files changed, 77 insertions, 11 deletions
diff --git a/js/leibovitz/balance.js b/js/leibovitz/balance.js new file mode 100644 index 0000000..6566176 --- /dev/null +++ b/js/leibovitz/balance.js @@ -0,0 +1,37 @@ +// BalanceManager handles white balance adjustments +class BalanceManager { + static init() { + this.balanceSlider = document.getElementById('balance-slider'); + this.balanceValue = document.getElementById('balance-value'); + this._setupEventListeners(); + } + + static _setupEventListeners() { + this.balanceSlider.addEventListener('input', () => { + const value = this.balanceSlider.value; + this.balanceValue.textContent = `${value}K`; + }); + } + + static getCurrentBalance() { + return parseInt(this.balanceSlider.value); + } + + static 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; + } +} \ No newline at end of file diff --git a/js/leibovitz/color.js b/js/leibovitz/color.js index 136638f..4319a21 100644 --- a/js/leibovitz/color.js +++ b/js/leibovitz/color.js @@ -24,7 +24,7 @@ const ColorManager = { resetButton.addEventListener('click', () => { // Reset color tint this._currentColor = null; - this._colorInput.value = '#000000'; + this._colorInput.value = '#ffffff'; this._notifyObservers(); // Reset contrast @@ -32,6 +32,14 @@ const ColorManager = { // Reset blur BlurManager.reset(); + + // Reset white balance to default (6500K) + const balanceSlider = document.getElementById('balance-slider'); + const balanceValue = document.getElementById('balance-value'); + if (balanceSlider && balanceValue) { + balanceSlider.value = 6500; + balanceValue.textContent = '6500K'; + } }); }, diff --git a/js/leibovitz/index.html b/js/leibovitz/index.html index 451ef9f..c8eb9af 100644 --- a/js/leibovitz/index.html +++ b/js/leibovitz/index.html @@ -376,6 +376,16 @@ <input type="range" id="contrast-slider" min="-255" max="255" value="0" step="1"> <span class="value" id="contrast-value">0</span> </div> + <div class="slider-group"> + <label for="balance-slider">White Balance</label> + <input type="range" id="balance-slider" min="2000" max="10000" value="6500" step="100"> + <span class="value" id="balance-value">6500K</span> + </div> + <div class="slider-group" id="focus-control" style="display: none;"> + <label for="focus-slider">Focus</label> + <input type="range" id="focus-slider" min="0" max="100" step="1" value="50" disabled> + <span class="value" id="focus-value">50%</span> + </div> <div class="slider-group" id="pixel-size-control" style="display: none;"> <label for="block-size-slider">Pixel Size</label> <input type="range" id="block-size-slider" min="1" max="12" value="4" step="1"> @@ -401,9 +411,6 @@ </div> <div id="controls"> - <div id="focus-container"> - <input style="display: none;" type="range" id="focus-slider" min="0" max="100" step="1" value="50" disabled> - </div> <button id="toggle-camera">Camera On</button> <button id="capture" disabled class="capture">Capture Image</button> </div> @@ -412,6 +419,7 @@ <script src="contrast.js"></script> <script src="color.js"></script> <script src="blur.js"></script> +<script src="balance.js"></script> <script src="leibovitz.js"></script> </body> </html> diff --git a/js/leibovitz/leibovitz.js b/js/leibovitz/leibovitz.js index 2f828c2..3b43d86 100644 --- a/js/leibovitz/leibovitz.js +++ b/js/leibovitz/leibovitz.js @@ -3,15 +3,20 @@ const ctx = canvas.getContext('2d'); const video = document.createElement('video'); const toggleCameraButton = document.getElementById('toggle-camera'); const captureButton = document.getElementById('capture'); +const focusControl = document.getElementById('focus-control'); +const focusSlider = document.getElementById('focus-slider'); +const focusValue = document.getElementById('focus-value'); let cameraOn = false; let stream = null; +let track = null; // Initialize managers ColorManager.init(); DitherManager.init(); ContrastManager.init(); BlurManager.init(); +BalanceManager.init(); // Set the canvas dimensions to match the window size function updateCanvasSize() { @@ -48,9 +53,6 @@ window.addEventListener('resize', updateCanvasSize); // Initialize canvas size updateCanvasSize(); -const focusSlider = document.getElementById('focus-slider'); -let track = null; - function startCamera() { navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: 'environment' } } }) .then(s => { @@ -60,24 +62,27 @@ function startCamera() { canvas.style.display = 'block'; // Show the canvas captureButton.disabled = false; captureButton.active = true; - focusSlider.disabled = false; track = stream.getVideoTracks()[0]; const settings = track.getSettings(); // Check if focus control is supported with this browser and device combo if ('focusDistance' in settings) { - focusSlider.style.display = 'block'; + focusControl.style.display = 'flex'; + focusSlider.disabled = false; focusSlider.value = settings.focusDistance || focusSlider.min; + focusValue.textContent = `${focusSlider.value}%`; focusSlider.addEventListener('input', () => { + const value = focusSlider.value; + focusValue.textContent = `${value}%`; track.applyConstraints({ - advanced: [{ focusDistance: focusSlider.value }] + advanced: [{ focusDistance: value }] }); }); } else { console.warn('Focus control is not supported on this device.'); - focusSlider.style.display = 'none'; + focusControl.style.display = 'none'; } // Draw the video feed to the canvas @@ -88,6 +93,7 @@ function startCamera() { applyContrast(); applyColorTint(); applyBlur(); + applyBalance(); applyDither(); requestAnimationFrame(step); } @@ -107,6 +113,7 @@ function stopCamera() { captureButton.disabled = true; captureButton.active = false; focusSlider.disabled = true; + focusControl.style.display = 'none'; stream = null; } } @@ -141,6 +148,12 @@ function applyColorTint() { ctx.putImageData(tintedImageData, 0, 0); } +function applyBalance() { + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const balancedImageData = BalanceManager.applyBalance(imageData); + ctx.putImageData(balancedImageData, 0, 0); +} + function applyContrast() { const currentContrast = ContrastManager.getCurrentContrast(); if (!currentContrast || currentContrast === 1.0) return; |