about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2025-03-30 09:34:25 -0400
committerelioat <elioat@tilde.institute>2025-03-30 09:34:25 -0400
commita564738c865b271a1d9a0161121e176627630141 (patch)
treea0e978c17e1cb9fc92ccd899eaa40d9ce002646d
parentc169f89e17f4a241ac128c236876d244ba392d4e (diff)
downloadtour-a564738c865b271a1d9a0161121e176627630141.tar.gz
*
-rw-r--r--js/leibovitz/blur.js129
1 files changed, 77 insertions, 52 deletions
diff --git a/js/leibovitz/blur.js b/js/leibovitz/blur.js
index 6c7aff9..08d1ec5 100644
--- a/js/leibovitz/blur.js
+++ b/js/leibovitz/blur.js
@@ -6,20 +6,21 @@ const BlurManager = {
     _currentBlur: 0, // Default blur (no blur)
     _observers: new Set(),
     _slider: null,
+    _value: null,
 
     // Initialize the blur manager
     init() {
+        this._slider = document.getElementById('blur-slider');
+        this._value = document.getElementById('blur-value');
         this._setupEventListeners();
     },
 
     // Private methods
     _setupEventListeners() {
-        this._slider = document.getElementById('blur-slider');
-        this._slider.addEventListener('input', (e) => {
-            this._currentBlur = parseInt(e.target.value);
-            // Update the display value as a percentage
-            document.getElementById('blur-value').textContent = 
-                `${Math.round((this._currentBlur / 20) * 100)}%`;
+        this._slider.addEventListener('input', () => {
+            const value = this._slider.value;
+            this._value.textContent = `${value}%`;
+            this._currentBlur = parseInt(value);
             this._notifyObservers();
         });
     },
@@ -40,72 +41,95 @@ const BlurManager = {
 
     // Apply Gaussian blur to an image
     applyBlur(imageData, radius) {
-        if (!radius || radius <= 0) return imageData;
+        if (!radius) return imageData;
 
         const { data, width, height } = imageData;
-        const newData = new Uint8ClampedArray(data);
+        const tempData = new Uint8ClampedArray(data);
         
-        // Create Gaussian kernel
-        const kernel = this._createGaussianKernel(radius);
-        const kernelSize = kernel.length;
-        const kernelRadius = Math.floor(kernelSize / 2);
+        // Calculate the actual image boundaries
+        let minX = width, minY = height, maxX = 0, maxY = 0;
+        let hasContent = false;
 
-        // Apply horizontal blur
+        // Find the actual image boundaries
         for (let y = 0; y < height; y++) {
             for (let x = 0; x < width; x++) {
-                const idx = (y * width + x) * 4;
+                const i = (y * width + x) * 4;
+                if (data[i + 3] > 0) { // Check alpha channel
+                    hasContent = true;
+                    minX = Math.min(minX, x);
+                    minY = Math.min(minY, y);
+                    maxX = Math.max(maxX, x);
+                    maxY = Math.max(maxY, y);
+                }
+            }
+        }
+
+        if (!hasContent) return imageData;
+
+        // Add padding to boundaries to prevent edge artifacts
+        minX = Math.max(0, minX - radius);
+        minY = Math.max(0, minY - radius);
+        maxX = Math.min(width - 1, maxX + radius);
+        maxY = Math.min(height - 1, maxY + radius);
+
+        // Optimized box blur implementation
+        // First pass: horizontal blur
+        for (let y = minY; y <= maxY; y++) {
+            for (let x = minX; x <= maxX; x++) {
                 let r = 0, g = 0, b = 0, a = 0;
-                let weightSum = 0;
-
-                for (let i = -kernelRadius; i <= kernelRadius; i++) {
-                    const px = x + i;
-                    if (px >= 0 && px < width) {
-                        const pixelIdx = (y * width + px) * 4;
-                        const weight = kernel[i + kernelRadius];
-                        r += data[pixelIdx] * weight;
-                        g += data[pixelIdx + 1] * weight;
-                        b += data[pixelIdx + 2] * weight;
-                        a += data[pixelIdx + 3] * weight;
-                        weightSum += weight;
+                let count = 0;
+
+                // Calculate horizontal blur for this pixel
+                for (let dx = -radius; dx <= radius; dx++) {
+                    const nx = x + dx;
+                    if (nx >= 0 && nx < width) {
+                        const i = (y * width + nx) * 4;
+                        r += data[i];
+                        g += data[i + 1];
+                        b += data[i + 2];
+                        a += data[i + 3];
+                        count++;
                     }
                 }
 
-                newData[idx] = r / weightSum;
-                newData[idx + 1] = g / weightSum;
-                newData[idx + 2] = b / weightSum;
-                newData[idx + 3] = a / weightSum;
+                // Store horizontal blur result
+                const i = (y * width + x) * 4;
+                tempData[i] = r / count;
+                tempData[i + 1] = g / count;
+                tempData[i + 2] = b / count;
+                tempData[i + 3] = a / count;
             }
         }
 
-        // Apply vertical blur
-        const finalData = new Uint8ClampedArray(newData);
-        for (let y = 0; y < height; y++) {
-            for (let x = 0; x < width; x++) {
-                const idx = (y * width + x) * 4;
+        // Second pass: vertical blur
+        for (let y = minY; y <= maxY; y++) {
+            for (let x = minX; x <= maxX; x++) {
                 let r = 0, g = 0, b = 0, a = 0;
-                let weightSum = 0;
-
-                for (let i = -kernelRadius; i <= kernelRadius; i++) {
-                    const py = y + i;
-                    if (py >= 0 && py < height) {
-                        const pixelIdx = (py * width + x) * 4;
-                        const weight = kernel[i + kernelRadius];
-                        r += newData[pixelIdx] * weight;
-                        g += newData[pixelIdx + 1] * weight;
-                        b += newData[pixelIdx + 2] * weight;
-                        a += newData[pixelIdx + 3] * weight;
-                        weightSum += weight;
+                let count = 0;
+
+                // Calculate vertical blur for this pixel
+                for (let dy = -radius; dy <= radius; dy++) {
+                    const ny = y + dy;
+                    if (ny >= 0 && ny < height) {
+                        const i = (ny * width + x) * 4;
+                        r += tempData[i];
+                        g += tempData[i + 1];
+                        b += tempData[i + 2];
+                        a += tempData[i + 3];
+                        count++;
                     }
                 }
 
-                finalData[idx] = r / weightSum;
-                finalData[idx + 1] = g / weightSum;
-                finalData[idx + 2] = b / weightSum;
-                finalData[idx + 3] = a / weightSum;
+                // Store final blur result
+                const i = (y * width + x) * 4;
+                data[i] = r / count;
+                data[i + 1] = g / count;
+                data[i + 2] = b / count;
+                data[i + 3] = a / count;
             }
         }
 
-        return new ImageData(finalData, width, height);
+        return imageData;
     },
 
     // Create a 1D Gaussian kernel
@@ -133,6 +157,7 @@ const BlurManager = {
     reset() {
         this._currentBlur = 0;
         this._slider.value = 0;
+        this._value.textContent = '0%';
         this._notifyObservers();
     }
 }; 
\ No newline at end of file