在上一篇文章中,说到了使用css3的滤镜来处理图片效果,虽然看上去很强大,通过拖动range控件也很方便来设置其对应值。但是有个问题,即你只能看到对应的效果,却无法右键保存处理的图片。你当然可以通过截图来保存,但不仅不方便,截的图也不精确。

显然,本篇文章就是为了解决以上问题而来的。

若你对canvas了解,你会发现它处理图片的功能完全不亚于css3。首先来看个demo:canvas滤镜处理图片

看完以上demo,我们就来逐一说说这些滤镜处理的实现原理:

在说明实现原理前,我们首先需要知道一些关于canvas操作图片的知识。就是对于一个绘制了图片的canvas,有以下属性:

图像数据中的每个像素都是以4个8位二进制整数来保存的,它们分别表示像素的红、绿、蓝以及Alpha分量,每个分量的取值范围是0~255。如图,加上canvas里的小方框是一个像素点:

反相(负片)

由上面的介绍我们可以知道,每个canvas像素点都有r、g、b、a四个点,对于r、g、b,它们的取值都是0-255。因此所谓的反相,就是取255与r、g、b值得差值(这里假设obj为像素信息数组):

function invert(obj , i){
obj[i] = 255 - obj[i];
obj[i+1] = 255 - obj[i+1];
obj[i+2] = 255 - obj[i+2];
}

灰化

对于灰化,一般来说是取r、g、b三个点的平均值,你也可以通过以下代码里的公式。我经过测试,效果相同:

function grayscale(obj,i){
var average = (obj[i] + obj[i+1] + obj[i+2]) / 3;
//var average = 0.2126*obj[i] + 0.7152*obj[i+1] + 0.0722*obj[i+2]; 或者
obj[i] = obj[i+1] = obj[i+2] = average;
}

复古(怀旧)

复古的滤镜效果是通过一组特点的公式:

function sepia(obj , i){
var r = obj[i],
g = obj[i+1],
b = obj[i+2];
obj[i] = (r*0.393)+(g*0.769)+(b*0.189);
obj[i+1] = (r*0.349)+(g*0.686)+(b*0.168);
obj[i+2] = (r*0.272)+(g*0.534)+(b*0.131);
}

变亮

想要让图片变亮,最简单的方法就是给每个像素点的r、g、b三个点加上一定的数值,这个数值在这里可以作为一个参数:

function brightness(obj , i , brightVal){
var r = obj[i],
g = obj[i+1],
b = obj[i+2];
obj[i] += brightVal;
obj[i+1] += brightVal;
obj[i+2] += brightVal;
}

阈值

何为阈值,一开始我也是不太了解。但当你看到上面的效果时,你就会明白了。网上的解释是:“阈值”命令将灰度或彩色图像转换为高对比度的黑白图像。您可以指定某个色阶作为阈值。所有比阈值亮的像素转换为白色;而所有比阈值暗的像素转换为黑色。“阈值”命令对确定图像的最亮和最暗区域很有用。

那么问题来了,如何才能做出阈值的效果呢?

想要得到阈值的效果,可以将灰度值(r、g、b三个点的平均值)与设定的阈值比较,如果大于等于阈值,则将该点设置为255,否则设置为0。

function threshold(obj , i , thresholdVal){
var average = (obj[i] + obj[i+1] + obj[i+2]) / 3;
obj[i] = obj[i+1] = obj[i+2] = average > thresholdVal ? 255 : 0;
}

模糊

用css3的滤镜可以很轻松的实现模糊效果。但是在canvas里则略显复杂,因此,在这我使用了一个相对成熟js库—stackblur。你可以查看文章最后的地址了解它的详情:

stackBlurCanvasRGBA( "canvas", 0, 0, canvas.width, canvas.height, 10 );

它的用法有以下三种,在这里我采用的是第二种。

Usage: stackBlurImage( sourceImageID, targetCanvasID, radius, blurAlphaChannel );
or: stackBlurCanvasRGBA( targetCanvasID, top_x, top_y, width, height, radius );
or: stackBlurCanvasRGB( targetCanvasID, top_x, top_y, width, height, radius );

浮雕

浮雕的滤镜效果处理起来还是蛮复杂,因为像素点的计算要根据它后一个点和下一行的同列点,并且只对canvas中每个像素点的r、g、b三个点作处理。所以在计算时,要对像素点的最后一列和最后一行做特殊处理。

function relief(obj , i , canvas){
if ((i+1) % 4 !== 0) { // 每个像素点的第四个(0,1,2,3 4,5,6,7)是透明度。这里取消对透明度的处理
if ((i+4) % (canvas.width*4) == 0) { // 每行最后一个点,特殊处理。因为它后面没有边界点,所以变通下,取它前一个点
obj[i] = obj[i-4];
obj[i+1] = obj[i-3];
obj[i+2] = obj[i-2];
obj[i+3] = obj[i-1];
i+=4;
}
else{ // 取下一个点和下一行的同列点
obj[i] = 255/2 // 平均值
+ 2*obj[i] // 当前像素点
- obj[i+4] // 下一点
- obj[i+canvas.width*4]; // 下一行的同列点
}
}
else { // 最后一行,特殊处理
if ((i+1) % 4 !== 0) {
obj[i] = obj[i-canvas.width*4];
}
}
}

以上就是canvas中滤镜的七种效果,当然还有一些其他效果也非常值得研究,待有时间尽量把它整全!七月底了,祝自己要做的事情都能如愿以偿!

参考