在HTML5画布中调整图片大小
我试图在客户端使用javascript和canvas元素创建缩略图,但是当我缩小图像时,它看起来很糟糕。 看起来好像它在photoshop中缩小了,重新采样设置为“最近邻居”而不是Bicubic。 我知道它有可能让这看起来很正确,因为这个网站可以很好地使用画布。 我试过使用与“[Source]”链接中显示的相同的代码,但它看起来仍然很糟糕。 有什么我失踪,需要设置一些设置或什么?
编辑:
我正在尝试调整JPG格式。 我曾尝试在链接网站和photoshop上调整相同的JPG大小,并在缩小尺寸时看起来很好。
这里是相关的代码:
reader.onloadend = function(e)
{
var img = new Image();
var ctx = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
img.onload = function()
{
var ratio = 1;
if(img.width > maxWidth)
ratio = maxWidth / img.width;
else if(img.height > maxHeight)
ratio = maxHeight / img.height;
canvasCopy.width = img.width;
canvasCopy.height = img.height;
copyContext.drawImage(img, 0, 0);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);
};
img.src = reader.result;
}
EDIT2:
似乎我错了,链接的网站在缩小图像方面没有做得更好。 我尝试了其他建议的方法,没有一个看起来更好。 这就是不同方法的结果:
Photoshop中:
帆布:
具有图像渲染的图像:optimizeQuality集合,并按照宽度/高度缩放:
带图像渲染的图像:optimizeQuality集合并使用-moz-transform进行缩放:
帆布在pixastic上调整大小:
我想这意味着Firefox并没有像它应该的那样使用双三次采样。 我只需要等到他们实际添加它。
EDIT3:
原始图像
那么如果所有的浏览器(实际上,Chrome 5给了我相当好的浏览器)不会给你足够好的重采样质量,你会怎么做? 然后你自己实现它们! 哦,来吧,我们正在进入Web 3.0的新时代,符合HTML5的浏览器,超级优化的JIT javascript编译器,多核(†)机器,内存大量,您担心什么? 嘿,在JavaScript中有java这个词,所以应该保证性能,对吧? 看哪,缩略图生成代码:
// returns a function that calculates lanczos weight
function lanczosCreate(lobes) {
return function(x) {
if (x > lobes)
return 0;
x *= Math.PI;
if (Math.abs(x) < 1e-16)
return 1;
var xx = x / lobes;
return Math.sin(x) * Math.sin(xx) / x / xx;
};
}
// elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes) {
this.canvas = elem;
elem.width = img.width;
elem.height = img.height;
elem.style.display = "none";
this.ctx = elem.getContext("2d");
this.ctx.drawImage(img, 0, 0);
this.img = img;
this.src = this.ctx.getImageData(0, 0, img.width, img.height);
this.dest = {
width : sx,
height : Math.round(img.height * sx / img.width),
};
this.dest.data = new Array(this.dest.width * this.dest.height * 3);
this.lanczos = lanczosCreate(lobes);
this.ratio = img.width / sx;
this.rcp_ratio = 2 / this.ratio;
this.range2 = Math.ceil(this.ratio * lobes / 2);
this.cacheLanc = {};
this.center = {};
this.icenter = {};
setTimeout(this.process1, 0, this, 0);
}
thumbnailer.prototype.process1 = function(self, u) {
self.center.x = (u + 0.5) * self.ratio;
self.icenter.x = Math.floor(self.center.x);
for (var v = 0; v < self.dest.height; v++) {
self.center.y = (v + 0.5) * self.ratio;
self.icenter.y = Math.floor(self.center.y);
var a, r, g, b;
a = r = g = b = 0;
for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
if (i < 0 || i >= self.src.width)
continue;
var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
if (!self.cacheLanc[f_x])
self.cacheLanc[f_x] = {};
for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
if (j < 0 || j >= self.src.height)
continue;
var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
if (self.cacheLanc[f_x][f_y] == undefined)
self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2)
+ Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
weight = self.cacheLanc[f_x][f_y];
if (weight > 0) {
var idx = (j * self.src.width + i) * 4;
a += weight;
r += weight * self.src.data[idx];
g += weight * self.src.data[idx + 1];
b += weight * self.src.data[idx + 2];
}
}
}
var idx = (v * self.dest.width + u) * 3;
self.dest.data[idx] = r / a;
self.dest.data[idx + 1] = g / a;
self.dest.data[idx + 2] = b / a;
}
if (++u < self.dest.width)
setTimeout(self.process1, 0, self, u);
else
setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self) {
self.canvas.width = self.dest.width;
self.canvas.height = self.dest.height;
self.ctx.drawImage(self.img, 0, 0, self.dest.width, self.dest.height);
self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
var idx, idx2;
for (var i = 0; i < self.dest.width; i++) {
for (var j = 0; j < self.dest.height; j++) {
idx = (j * self.dest.width + i) * 3;
idx2 = (j * self.dest.width + i) * 4;
self.src.data[idx2] = self.dest.data[idx];
self.src.data[idx2 + 1] = self.dest.data[idx + 1];
self.src.data[idx2 + 2] = self.dest.data[idx + 2];
}
}
self.ctx.putImageData(self.src, 0, 0);
self.canvas.style.display = "block";
};
...你可以用这些产生这样的结果!
无论如何,这里是你的例子的一个“固定”版本:
img.onload = function() {
var canvas = document.createElement("canvas");
new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
// but feel free to raise it up to 8. Your client will appreciate
// that the program makes full use of his machine.
document.body.appendChild(canvas);
};
现在是时候让你最好的浏览器出现在那里,看看哪一个最不可能增加你的客户的血压!
呃,我的讽刺标签在哪里?
(因为代码的很多部分都是基于Anrieff Gallery Generator,它也包含在GPL2下?我不知道)
†实际上由于javascript的限制,不支持多核。
使用带JavaScript的Hermite滤镜快速调整图像大小/重采样算法。 支持透明度,提供良好的质量。 预习:
更新 :在GitHub上添加了2.0版(更快,web工作者+可转移对象)。 最后我得到它的工作!
Git:https://github.com/viliusle/Hermite-resize
演示:http://viliusle.github.io/miniPaint/
/**
* Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
*
* @param {HtmlElement} canvas
* @param {int} width
* @param {int} height
* @param {boolean} resize_canvas if true, canvas will be resized. Optional.
*/
function resample_single(canvas, width, height, resize_canvas) {
var width_source = canvas.width;
var height_source = canvas.height;
width = Math.round(width);
height = Math.round(height);
var ratio_w = width_source / width;
var ratio_h = height_source / height;
var ratio_w_half = Math.ceil(ratio_w / 2);
var ratio_h_half = Math.ceil(ratio_h / 2);
var ctx = canvas.getContext("2d");
var img = ctx.getImageData(0, 0, width_source, height_source);
var img2 = ctx.createImageData(width, height);
var data = img.data;
var data2 = img2.data;
for (var j = 0; j < height; j++) {
for (var i = 0; i < width; i++) {
var x2 = (i + j * width) * 4;
var weight = 0;
var weights = 0;
var weights_alpha = 0;
var gx_r = 0;
var gx_g = 0;
var gx_b = 0;
var gx_a = 0;
var center_y = (j + 0.5) * ratio_h;
var yy_start = Math.floor(j * ratio_h);
var yy_stop = Math.ceil((j + 1) * ratio_h);
for (var yy = yy_start; yy < yy_stop; yy++) {
var dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half;
var center_x = (i + 0.5) * ratio_w;
var w0 = dy * dy; //pre-calc part of w
var xx_start = Math.floor(i * ratio_w);
var xx_stop = Math.ceil((i + 1) * ratio_w);
for (var xx = xx_start; xx < xx_stop; xx++) {
var dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half;
var w = Math.sqrt(w0 + dx * dx);
if (w >= 1) {
//pixel too far
continue;
}
//hermite filter
weight = 2 * w * w * w - 3 * w * w + 1;
var pos_x = 4 * (xx + yy * width_source);
//alpha
gx_a += weight * data[pos_x + 3];
weights_alpha += weight;
//colors
if (data[pos_x + 3] < 255)
weight = weight * data[pos_x + 3] / 250;
gx_r += weight * data[pos_x];
gx_g += weight * data[pos_x + 1];
gx_b += weight * data[pos_x + 2];
weights += weight;
}
}
data2[x2] = gx_r / weights;
data2[x2 + 1] = gx_g / weights;
data2[x2 + 2] = gx_b / weights;
data2[x2 + 3] = gx_a / weights_alpha;
}
}
//clear and resize canvas
if (resize_canvas === true) {
canvas.width = width;
canvas.height = height;
} else {
ctx.clearRect(0, 0, width_source, height_source);
}
//draw
ctx.putImageData(img2, 0, 0);
}
尝试异食癖 - 这是一个高度优化的调整器与可选algoryory。 看演示。
例如,第一篇文章的原始图像在Lanczos滤镜和3px窗口的120ms内调整大小,在Box滤镜和0.5px窗口中调整大小为60ms。 对于庞大的17MB图像5000x3000px调整大小在桌面上需要1秒,在手机上需要3秒。
所有调整原则在这个主题中都有很好的描述,异食癖并没有增加火箭科学。 但是它对现代JIT-s的优化非常好,并且可以使用开箱即用(通过npm或bower)。 此外,它可以使用webworkers,以避免界面冻结。
我还计划很快添加不敏锐的蒙版支持,因为它在缩减后非常有用。
链接地址: http://www.djcxy.com/p/44961.html