start . me .
Directory path . web , canvas .

HTML Document File : improved-antialiasing.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Improved Anti-aliasing for Pixel Graphics</title>
    <style>
        body {
            display: flex;
            flex-direction: column;
            align-items: center;
            font-family: Arial, sans-serif;
            margin: 20px;
            background-color: #f5f5f5;
        }
        h1 {
            color: #333;
            margin-bottom: 10px;
        }
        h3 {
            color: #555;
            margin-top: 0;
            margin-bottom: 20px;
            font-weight: normal;
        }
        .canvas-container {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        canvas {
            border: 1px solid #333;
            background-color: white;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .controls {
            display: flex;
            gap: 15px;
            margin: 20px 0;
        }
        button {
            padding: 8px 15px;
            cursor: pointer;
            background-color: #4a90e2;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 14px;
            transition: background-color 0.2s;
        }
        button:hover {
            background-color: #3a7bc8;
        }
        .canvas-label {
            text-align: center;
            font-weight: bold;
            margin-top: 5px;
            color: #555;
        }
        .description {
            max-width: 800px;
            margin-top: 20px;
            line-height: 1.5;
            color: #555;
            font-size: 14px;
            text-align: center;
        }
    </style>
</head>
<body>
    <h1>Improved Anti-aliasing for Pixel Graphics</h1>
    <h3>Supersampling & Multi-pass Edge Detection</h3>
    
    <div class="controls">
        <button onclick="drawCircle()">Circle</button>
        <button onclick="drawStar()">Star</button>
        <button onclick="drawCustomShape()">Custom Shape</button>
        <button onclick="toggleZoom()">Toggle Zoom</button>
    </div>
    
    <div class="canvas-container">
        <div>
            <canvas id="basicCanvas" width="200" height="200"></canvas>
            <div class="canvas-label">Standard Pixels</div>
        </div>
        <div>
            <canvas id="aaCanvas" width="200" height="200"></canvas>
            <div class="canvas-label">With Anti-aliasing</div>
        </div>
    </div>
    
    <div class="description">
        This demo uses a supersampling technique with 4x4 subpixels and edge detection to create smooth anti-aliased shapes.
        The left canvas shows standard pixel-by-pixel rendering, while the right shows the anti-aliased version.
    </div>

    <script>
        // Get the canvases and contexts
        const basicCanvas = document.getElementById('basicCanvas');
        const aaCanvas = document.getElementById('aaCanvas');
        const basicCtx = basicCanvas.getContext('2d');
        const aaCtx = aaCanvas.getContext('2d');
        
        // Set initial scale
        let scale = 1;
        let zoomMode = false;
        
        // Clear both canvases
        function clearCanvases() {
            basicCtx.clearRect(0, 0, basicCanvas.width, basicCanvas.height);
            aaCtx.clearRect(0, 0, aaCanvas.width, aaCanvas.height);
            
            // Reset transformations
            basicCtx.setTransform(1, 0, 0, 1, 0, 0);
            aaCtx.setTransform(1, 0, 0, 1, 0, 0);
            
            // Apply zoom if enabled
            if (zoomMode) {
                basicCtx.scale(2, 2);
                aaCtx.scale(2, 2);
            }
        }
        
        // Toggle zoom mode
        function toggleZoom() {
            zoomMode = !zoomMode;
            // Redraw current shape with new zoom setting
            redrawCurrentShape();
        }
        
        // Keep track of current shape
        let currentShape = 'circle';
        
        function redrawCurrentShape() {
            if (currentShape === 'circle') drawCircle();
            else if (currentShape === 'star') drawStar();
            else if (currentShape === 'custom') drawCustomShape();
        }
        
        // Function to set a pixel on the basic canvas (without anti-aliasing)
        function setBasicPixel(x, y, r, g, b) {
            basicCtx.fillStyle = `rgb(${r}, ${g}, ${b})`;
            basicCtx.fillRect(x, y, 1, 1);
        }
        
        // Function to set a pixel on the anti-aliased canvas with opacity
        function setAAPixel(x, y, r, g, b, a) {
            aaCtx.fillStyle = `rgba(${r}, ${g}, ${b}, ${a})`;
            aaCtx.fillRect(x, y, 1, 1);
        }
        
        // Create a supersampled buffer (4x4 subpixels per pixel)
        function createSupersampledBuffer(width, height) {
            const buffer = new Array(width * 4);
            for (let x = 0; x < width * 4; x++) {
                buffer[x] = new Array(height * 4).fill(0);
            }
            return buffer;
        }
        
        // Draw a shape to the supersampled buffer using a shape function
        function drawShapeToBuffer(buffer, width, height, shapeFunc) {
            // Draw to supersampled buffer (4x resolution)
            const bufferWidth = width * 4;
            const bufferHeight = height * 4;
            
            // Use the provided shape function to determine if a point is inside or outside
            for (let y = 0; y < bufferHeight; y++) {
                for (let x = 0; x < bufferWidth; x++) {
                    const realX = x / 4;
                    const realY = y / 4;
                    if (shapeFunc(realX, realY)) {
                        buffer[x][y] = 1;
                    }
                }
            }
            return buffer;
        }
        
        // Create a Gaussian kernel for smoother anti-aliasing
        function createGaussianKernel(size, sigma) {
            const kernel = [];
            const center = Math.floor(size / 2);
            let sum = 0;
            
            // Generate kernel values
            for (let y = 0; y < size; y++) {
                kernel[y] = [];
                for (let x = 0; x < size; x++) {
                    const dx = x - center;
                    const dy = y - center;
                    // Gaussian function
                    const g = Math.exp(-(dx*dx + dy*dy) / (2 * sigma * sigma));
                    kernel[y][x] = g;
                    sum += g;
                }
            }
            
            // Normalize the kernel
            for (let y = 0; y < size; y++) {
                for (let x = 0; x < size; x++) {
                    kernel[y][x] /= sum;
                }
            }
            
            return kernel;
        }
        
        // Apply Gaussian blur to buffer for smoother edges
        function applyGaussianBlur(buffer, width, height) {
            const blurredBuffer = new Array(width);
            for (let i = 0; i < width; i++) {
                blurredBuffer[i] = new Array(height).fill(0);
            }
            
            const kernel = createGaussianKernel(5, 1.0);
            const kernelSize = 5;
            const kernelRadius = Math.floor(kernelSize / 2);
            
            // Apply kernel to each point
            for (let y = 0; y < height; y++) {
                for (let x = 0; x < width; x++) {
                    let sum = 0;
                    
                    // Apply kernel
                    for (let ky = -kernelRadius; ky <= kernelRadius; ky++) {
                        for (let kx = -kernelRadius; kx <= kernelRadius; kx++) {
                            const sampleX = Math.min(Math.max(x + kx, 0), width - 1);
                            const sampleY = Math.min(Math.max(y + ky, 0), height - 1);
                            
                            const kernelValue = kernel[ky + kernelRadius][kx + kernelRadius];
                            sum += buffer[sampleX][sampleY] * kernelValue;
                        }
                    }
                    
                    blurredBuffer[x][y] = sum;
                }
            }
            
            return blurredBuffer;
        }
        
        // Improved edge detection for anti-aliasing
        function detectEdges(buffer, width, height) {
            const edgeBuffer = new Array(width);
            for (let i = 0; i < width; i++) {
                edgeBuffer[i] = new Array(height).fill(0);
            }
            
            const sobelX = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]];
            const sobelY = [[-1, -2, -1], [0, 0, 0], [1, 2, 1]];
            
            for (let y = 1; y < height - 1; y++) {
                for (let x = 1; x < width - 1; x++) {
                    let gx = 0;
                    let gy = 0;
                    
                    // Apply Sobel operators
                    for (let ky = -1; ky <= 1; ky++) {
                        for (let kx = -1; kx <= 1; kx++) {
                            const val = buffer[x + kx][y + ky];
                            gx += val * sobelX[ky + 1][kx + 1];
                            gy += val * sobelY[ky + 1][kx + 1];
                        }
                    }
                    
                    // Calculate gradient magnitude
                    const magnitude = Math.sqrt(gx * gx + gy * gy);
                    edgeBuffer[x][y] = Math.min(magnitude, 1);
                }
            }
            
            return edgeBuffer;
        }
        
        // Downsample the supersampled buffer to actual pixels with anti-aliasing
        function downsampleBuffer(buffer, width, height, r, g, b) {
            // Clear both canvases first
            clearCanvases();
            
            // Apply Gaussian blur for edge smoothing
            const smoothedBuffer = applyGaussianBlur(buffer, width * 4, height * 4);
            
            // Edge detection to enhance boundaries
            const edgeBuffer = detectEdges(smoothedBuffer, width * 4, height * 4);
            
            // Combine original buffer with edge information
            for (let y = 0; y < height * 4; y++) {
                for (let x = 0; x < width * 4; x++) {
                    // Enhance edges while keeping interior solid
                    if (smoothedBuffer[x][y] > 0.1) {
                        // Apply edge enhancement
                        let value = smoothedBuffer[x][y];
                        
                        // If it's an edge pixel, adjust its value for smoother transitions
                        if (edgeBuffer[x][y] > 0.1) {
                            // Apply a more gradual transition at edges
                            value = Math.min(value + edgeBuffer[x][y] * 0.2, 1.0);
                        }
                        
                        smoothedBuffer[x][y] = value;
                    }
                }
            }
            
            // Downsample and draw to both canvases
            for (let y = 0; y < height; y++) {
                for (let x = 0; x < width; x++) {
                    // Calculate average value from 4x4 supersampled grid
                    let sum = 0;
                    for (let sy = 0; sy < 4; sy++) {
                        for (let sx = 0; sx < 4; sx++) {
                            sum += smoothedBuffer[x * 4 + sx][y * 4 + sy];
                        }
                    }
                    
                    // Average of all subpixels
                    const avgValue = sum / 16;
                    
                    // Draw to basic canvas (aliased - either on or off)
                    if (avgValue > 0.5) {
                        setBasicPixel(x, y, r, g, b);
                    }
                    
                    // Draw to anti-aliased canvas with proper alpha
                    if (avgValue > 0) {
                        setAAPixel(x, y, r, g, b, avgValue);
                    }
                }
            }
        }
        
        // Function to draw a circle
        function drawCircle() {
            currentShape = 'circle';
            const width = basicCanvas.width / (zoomMode ? 2 : 1);
            const height = basicCanvas.height / (zoomMode ? 2 : 1);
            const centerX = width / 2;
            const centerY = height / 2;
            const radius = Math.min(width, height) * 0.4;
            
            // Create buffer
            const buffer = createSupersampledBuffer(width, height);
            
            // Define circle shape function
            const circleFunc = (x, y) => {
                const dx = x - centerX;
                const dy = y - centerY;
                return (dx * dx + dy * dy) <= (radius * radius);
            };
            
            // Draw to buffer and render
            const filledBuffer = drawShapeToBuffer(buffer, width, height, circleFunc);
            downsampleBuffer(filledBuffer, width, height, 0, 127, 255);
        }
        
        // Function to draw a star
        function drawStar() {
            currentShape = 'star';
            const width = basicCanvas.width / (zoomMode ? 2 : 1);
            const height = basicCanvas.height / (zoomMode ? 2 : 1);
            const centerX = width / 2;
            const centerY = height / 2;
            const outerRadius = Math.min(width, height) * 0.4;
            const innerRadius = outerRadius * 0.4;
            const points = 5;
            
            // Create buffer
            const buffer = createSupersampledBuffer(width, height);
            
            // Define star shape function
            const starFunc = (x, y) => {
                const dx = x - centerX;
                const dy = y - centerY;
                const angle = Math.atan2(dy, dx);
                const distToCenter = Math.sqrt(dx * dx + dy * dy);
                
                // Determine radius at this angle
                const angleToPoint = (angle % (Math.PI * 2 / points)) / (Math.PI * 2 / points);
                const pointiness = Math.abs(angleToPoint - 0.5) * 2;
                const radius = innerRadius + (outerRadius - innerRadius) * pointiness;
                
                return distToCenter <= radius;
            };
            
            // Draw to buffer and render
            const filledBuffer = drawShapeToBuffer(buffer, width, height, starFunc);
            downsampleBuffer(filledBuffer, width, height, 255, 127, 0);
        }
        
        // Function to draw a custom shape
        function drawCustomShape() {
            currentShape = 'custom';
            const width = basicCanvas.width / (zoomMode ? 2 : 1);
            const height = basicCanvas.height / (zoomMode ? 2 : 1);
            
            // Create buffer
            const buffer = createSupersampledBuffer(width, height);
            
            // Define a custom shape - a heart
            const heartFunc = (x, y) => {
                // Adjust coordinates to center and scale
                const scale = Math.min(width, height) * 0.3;
                const nx = (x - width/2) / scale;
                const ny = (y - height/2) / scale + 0.4;
                
                // Heart equation
                const nx2 = nx * nx;
                const ny2 = ny * ny;
                return ny2 < (nx2 * (1 - nx2 - ny2 + Math.abs(nx)));
            };
            
            // Draw to buffer and render
            const filledBuffer = drawShapeToBuffer(buffer, width, height, heartFunc);
            downsampleBuffer(filledBuffer, width, height, 255, 0, 127);
        }
        
        // Initial drawing
        drawCircle();
    </script>
</body>
</html>