Билинейная фильтрация на JavaScript

Билинейная фильтрация — это способ масштабирования изображения с относительно хорошим качеством. Для каждого пикселя нового изображения выбирается четыре пикселя из старого и хитрым образом интерполируются между собой. Подробнее можно прочитать в википедии.

Давно хочу написать длинную и подробную статью про билинейную фильтрацию с помощью NEON, но как-то все не нахожу времени и желания. Поэтому решил начать с малого — реализация алгоритма на JavaScript.

Идея такая: у нас есть изображение размером три на три пикселя и мы его неограниченно увеличиваем. В зеленом квадрате показывается цвет пикселя для нового изображения.

Пример не работает в Internet Explorer 8, даже не пытайтесь.

Исходники программы на javascript

var canvas;
var context;
var mousePos;
 
var width = 3;
var height = 3;
 
var image = [
    0xC0, 0x40, 0x60,
    0x40, 0xFF, 0x20,
    0x10, 0x80, 0xC0
];
 
function colorToString(color) {
    if (color === undefined) {
        return "#000000";
    }
    var c = color.toString(16);
    return "#" + c + "" + c + "" + c;
 
}
 
function render() {
    var screenW = canvas.width;
    var screenH = canvas.height;
 
    // Рисуем девять квадратов разным цветом
    for (var y = 0; y < height; ++y) {
        for (var x = 0; x < width; ++x) {
            var l = x * (screenW / width);
            var t = y * (screenH / height);
 
            context.fillStyle = colorToString(image[y * width + x]);
            context.fillRect(l, t, screenW / width, screenH / height);
        }
    }
 
    if (mousePos) {
        var sx = screenW / width;
        var sy = screenH / height;
 
        var x = mousePos.x / screenW * width;
        var y = mousePos.y / screenH * height;
 
        if (x < 0.5) {             
            x = 0.5;         
        }         
        else if (x > width - 0.5) {
            x = width - 0.5;
        }
 
        if (y < 0.5) {             
            y = 0.5;         
        }         
        else if(y > height - 0.5) {
            y = height - 0.5;
        }
 
        x -= 0.5;
        y -= 0.5;
 
        var intX = Math.floor(x);
        var intY = Math.floor(y);
 
        var u = x - intX;
        var v = y - intY;
 
        // Выход за пределы справа
        var outX = (intX + 1 > width - 1);
        // Выход за пределы снизу
        var outY = (intY + 1 > height - 1);
 
        // Центральный пиксель
        var p1 = image[intY * width + intX];
        // Справа
        var p2 = p1;
        // Снизу
        var p3 = p1;
        // Справа и снизу
        var p4 = p1
 
        if (!outX && !outY) {
            p2 = image[intY * width + intX + 1];
            p3 = image[(intY + 1) * width + intX];
            p4 = image[(intY + 1) * width + intX + 1]
 
        }                
        else if (outX && outY) {
            // Nothing to do
        }
        else if (outX) {
            p3 = image[(intY + 1) * width + intX];
            p4 = p3;
        }
        else if (outY) {
            p2 = image[intY * width + intX + 1];
            p4 = p2;
        }
 
        // Интерполяция между горизонтальными соседями
        var x0 = p1 + u * (p2 - p1);
        var x1 = p3 + u * (p4 - p3);
        // Интерполяция по вертикали
        var c = x0 + v * (x1 - x0);
 
        var rectX = mousePos.x - sx / 4;
        var rectY = mousePos.y - sy / 4
        var rectW = sx / 2;
        var rectH = sy / 2;
 
        // Рисуем зеленый прямоугольник
        context.beginPath();
        context.lineWidth = "5";
        context.strokeStyle = "green";
        context.rect(rectX, rectY, rectW, rectH);
        context.stroke();
 
        // Закрашиваем зеленый прямоугольник
        context.fillStyle = colorToString(Math.ceil(c));
        context.fillRect(rectX, rectY, rectW, rectH);
    }
 
    requestAnimationFrame(render);
 
}
 
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
 
canvas.onmousemove = function(event) {
    var rect = canvas.getBoundingClientRect();
 
    mousePos = {
        x : (event.clientX - rect.left) * (canvas.width / rect.width),
        y : (event.clientY - rect.top) * (canvas.height / rect.height)
    };
}
 
render();

Комментариев нет

Добавить комментарий

Ваш e-mail не будет опубликован.