Билинейная фильтрация — это способ масштабирования изображения с относительно хорошим качеством. Для каждого пикселя нового изображения выбирается четыре пикселя из старого и хитрым образом интерполируются между собой. Подробнее можно прочитать в википедии.
Давно хочу написать длинную и подробную статью про билинейную фильтрацию с помощью 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();