关闭

PhotoShop图层混合模式的Canvas实现

标签: photoshophtml5javascriptcanvashtml5游戏开发
1809人阅读 评论(0) 收藏 举报
分类:

前端开发人员可能会遇到这样一个问题。

当设计人员给到一个PSD以后,会发现其中有些图层是有图层混合效果的。

这样会产生一个情况就是,我们为了这个效果而不得不将背景+带混合的图层切到一起。

当这样的元素多了以后,我们所切出的图片就会越来越大并且难以修改。


那么,本章我们将讲解如何使用Canvas元素来实现混合模式。

以达到减少图片体量和数量的效果。


首先,我们来讲一下基本实现原理。

大家可能都知道,Canvas是位图处理,Svg是矢量处理。那么什么是位图,什么是矢量图呢?

请看以下图片进行对比:

图像引用自互联网,原文地址:http://jingyan.baidu.com/album/54b6b9c0dbef682d583b4722.html?picindex=2


从上图可知,100%大小的矢量图在缩放到800%的时候并没有出现失真的情况,即:没有马赛克。

而位图则出现了万恶的马赛克,图像已经变得模糊不清了。


那么,为什么我们要使用Canvas呢?

因为网上太多类似的文章了,所以这里直接外链

就本章节的内容来说,我们使用Canvas是为了使用像素处理。


为了方便读者实践,这里给出两张实验图片。(图片取自网络,请勿用于商业用途,如有侵犯请联系本人进行处理,感激不尽)

原图(假设图片名为:background.jpg)
混合图(假设图片名为:butterfly.png



实现效果的前置代码:

// 指定要使用的处理器,这里暂时不指定
var processor = "";
// 所有的处理器都在这个对象下
var pixProcessor = {};

var imgBackground = new Image();
var imgButterfly = new Image();
imgBackground.onload = imgButterfly.onload = function () {
    this.loaded = true;
    if (imgBackground.loaded && imgButterfly.loaded) {
        process();
    }
};
imgBackground.src = "background.jpg";
imgButterfly.src = "butterfly.png";

var canvas = document.createElement("canvas");
canvas.width = 610;
canvas.height = 502;
var context = canvas.getContext("2d");
以上代码十分好理解。

1.将两张图片加载进来。

2.创建一个canvas元素并获取其context。

3.图片加载完成后触发名为process的函数。



好的,现在我们来仔细看看process函数里都做了什么操作。

function process() {
    // 计算蝴蝶图片相对背景图片的中心位置
    var centerX = (imgBackground.width - imgButterfly.width) / 2;
    var centerY = (imgBackground.height - imgButterfly.height) / 2;

    // 绘制背景图
    context.drawImage(imgBackground, 0, 0);
    // 通过getImageData函数获取背景图片中,蝴蝶图片所应该在的区域的像素数据
    var backgroundData = context.getImageData(centerX, centerY, imgButterfly.width, imgButterfly.height);
    // 缓存ImageData中的data数组,这才是我们要操作的东西
    var backgroundPixs = backgroundData.data;

    // 清空一次画布
    context.clearRect(0, 0, canvas.width, canvas.height);

    // 绘制蝴蝶图
    context.drawImage(imgButterfly, centerX, centerY);
    // 不解释
    var butterflyData = context.getImageData(centerX, centerY, imgButterfly.width, imgButterfly.height);
    // 不解释
    var butterflyPixs = butterflyData.data;

    // 再次绘制背景图
    context.drawImage(imgBackground, 0, 0);
    // 再次绘制蝴蝶图(反正这句话也没什么卵用,忘了当时为啥要写这句话了)
    context.drawImage(imgButterfly, centerX, centerY);

    // 若没有定义处理器则不进行处理
    if (typeof processor != "undefined") {
        var newPix;
        for (var i = 0; i < backgroundPixs.length; i += 4) {
            // 跳过全透明像素
            if (butterflyPixs[i + 3] == 0) continue;

            // 传入两个图像对应的像素进行处理
            newPix = pixProcesser[processor]({ red: backgroundPixs[i], green: backgroundPixs[i + 1], blue: backgroundPixs[i + 2], alpha: backgroundPixs[i + 3] },
                                             { red: butterflyPixs[i], green: butterflyPixs[i + 1], blue: butterflyPixs[i + 2], alpha: butterflyPixs[i + 3] });

            if (newPix) {
                // 将处理好的像素赋值给背景图ImageData(实际上你传给蝴蝶图也没问题,只是下面putImageData的时候需要指向蝴蝶图罢了)
                backgroundPixs[i] = newPix.red;
                backgroundPixs[i + 1] = newPix.green;
                backgroundPixs[i + 2] = newPix.blue;
            }
        }

        // 好的,将处理结果交给浏览器
        context.putImageData(backgroundData, centerX, centerY);
    }
    if (document.body) {
        document.appendChild(canvas);
    } else {
        window.addEventListener("load", function () {
            document.appendChild(canvas);
        }, false);
    }
}

好的,现在我们进入华丽时间!~

1.正常模式,processor需要指定为 normal

pixProcesser.normal = function (background, butterfly) {
        /// <summary>正常模式</summary>

        var alpha = butterfly.alpha;
        return {
            red: butterfly.red * alpha + background.red * (1 - alpha),
            green: butterfly.green * alpha + background.green * (1 - alpha),
            blue: butterfly.blue * alpha + background.blue * (1 - alpha),
            alpha: butterfly.alpha * alpha + background.alpha * (1 - alpha)
        }
    };

2.溶解,processor需要指定为 dissolve


pixProcesser.dissolve: function (background, butterfly) {
        /// <summary>溶解</summary>

        // 正式用判断条件
        //if (Math.floor(Math.random() * 100) > (butterfly.alpha / 255 * 100)) {

        // 测试用判断条件
        if (Math.floor(Math.random() * 100) > 50) {
            return background;
        } else {
            return butterfly;
        }
    };

3.变暗,processor需要指定为 darken


pixProcesser.darken: function (background, butterfly) {
        /// <summary>变暗</summary>

        return {
            red: Math.min(background.red, butterfly.red),
            green: Math.min(background.green, butterfly.green),
            blue: Math.min(background.blue, butterfly.blue),
            alpha: Math.min(background.alpha, butterfly.alpha)
        };
    };

4.正片叠底,processor需要指定为 multiply


pixProcesser.multiply: function (background, butterfly) {
        /// <summary>正片叠底</summary>

        return {
            red: butterfly.red * background.red / 255,
            green: butterfly.green * background.green / 255,
            blue: butterfly.blue * background.blue / 255,
            alpha: butterfly.alpha * background.alpha / 255
        };
    };

5.颜色加深,processor需要指定为 colorBurn


pixProcesser.colorBurn: function (background, butterfly) {
        /// <summary>颜色加深</summary>

        return {
            red: Math.max(0, background.red + butterfly.red - 255) * 255 / butterfly.red,
            green: Math.max(0, background.green + butterfly.green - 255) * 255 / butterfly.green,
            blue: Math.max(0, background.blue + butterfly.blue - 255) * 255 / butterfly.blue,
            alpha: Math.max(0, background.alpha + butterfly.alpha - 255) * 255 / butterfly.alpha
        };
    };

6.线性加深,processor需要指定为 linearBurn


pixProcesser.linearBurn: function (background, butterfly) {
        /// <summary>线性加深</summary>

        return {
            red: Math.max(0, background.red + butterfly.red - 255),
            green: Math.max(0, background.green + butterfly.green - 255),
            blue: Math.max(0, background.blue + butterfly.blue - 255),
            alpha: Math.max(0, background.alpha + butterfly.alpha - 255)
        };
    };

7.深色,processor需要指定为 darkerColor


pixProcesser.darkerColor: function (background, butterfly) {
        /// <summary>深色</summary>

        if ((background.red + background.green + background.blue + background.alpha) < (butterfly.red + butterfly.green + butterfly.blue + butterfly.alpha)) {
            return background;
        } else {
            return butterfly;
        }
    };

8.变亮,processor需要指定为 lighten


pixProcesser.lighten: function (background, butterfly) {
        /// <summary>变亮</summary>

        return {
            red: Math.max(background.red, butterfly.red),
            green: Math.max(background.green, butterfly.green),
            blue: Math.max(background.blue, butterfly.blue),
            alpha: Math.max(background.alpha, butterfly.alpha)
        };
    };

9.滤色,processor需要指定为 screen


pixProcesser.screen: function (background, butterfly) {
        /// <summary>滤色</summary>

        return {
            red: 255 - (255 - butterfly.red) * (255 - background.red) / 255,
            green: 255 - (255 - butterfly.green) * (255 - background.green) / 255,
            blue: 255 - (255 - butterfly.blue) * (255 - background.blue) / 255,
            alpha: 255 - (255 - butterfly.alpha) * (255 - background.alpha) / 255
        };
    };

10.颜色减淡,processor需要指定为 colorDodge


pixProcesser.colorDodge: function (background, butterfly) {
        /// <summary>颜色减淡</summary>

        return {
            red: background.red + butterfly.red * background.red / (255 - butterfly.red),
            green: background.green + butterfly.green * background.green / (255 - butterfly.green),
            blue: background.blue + butterfly.blue * background.blue / (255 - butterfly.blue),
            alpha: background.alpha + butterfly.alpha * background.alpha / (255 - butterfly.alpha)
        };
    };

11.线性减淡,processor需要指定为 linearDodge


pixProcesser.linearDodge: function (background, butterfly) {
        /// <summary>线性减淡</summary>

        return {
            red: Math.min(background.red + butterfly.red, 255),
            green: Math.min(background.green + butterfly.green, 255),
            blue: Math.min(background.blue + butterfly.blue, 255),
            alpha: Math.min(background.alpha + butterfly.alpha, 255)
        };
    };

12.浅色,processor需要指定为 lighterColor


pixProcesser.lighterColor: function (background, butterfly) {
        /// <summary>浅色</summary>

        if ((background.red + background.green + background.blue + background.alpha) > (butterfly.red + butterfly.green + butterfly.blue + butterfly.alpha)) {
            return background;
        } else {
            return butterfly;
        }
    };

13.叠加,processor需要指定为 overlay


pixProcesser.overlay: function (background, butterfly) {
        /// <summary>叠加</summary>

        return {
            red: 255 - (255 - butterfly.red) * (255 - background.red) / 128,
            green: 255 - (255 - butterfly.green) * (255 - background.green) / 128,
            blue: 255 - (255 - butterfly.blue) * (255 - background.blue) / 128,
            alpha: 255 - (255 - butterfly.alpha) * (255 - background.alpha) / 128
        };
    };

14.柔光,processor需要指定为 softLight


pixProcesser.softLight: function (background, butterfly) {
        /// <summary>柔光</summary>

        return {
            red: background.red + (2 * butterfly.red - 255) * (Math.sqrt(background.red / 255) * 255 - background.red) / 255,
            green: background.green + (2 * butterfly.green - 255) * (Math.sqrt(background.green / 255) * 255 - background.green) / 255,
            blue: background.blue + (2 * butterfly.blue - 255) * (Math.sqrt(background.blue / 255) * 255 - background.blue) / 255,
            alpha: background.alpha + (2 * butterfly.alpha - 255) * (Math.sqrt(background.alpha / 255) * 255 - background.alpha) / 255
        };
    };

15.强光,processor需要指定为 hardLight


pixProcesser.hardLight: function (background, butterfly) {
        /// <summary>强光</summary>

        return {
            red: butterfly.red > 128 ? 255 - (255 - butterfly.red) * (255 - background.red) / 128 : butterfly.red * background.red / 128,
            green: butterfly.green > 128 ? 255 - (255 - butterfly.green) * (255 - background.green) / 128 : butterfly.green * background.green / 128,
            blue: butterfly.blue > 128 ? 255 - (255 - butterfly.blue) * (255 - background.blue) / 128 : butterfly.blue * background.blue / 128,
            alpha: butterfly.alpha > 128 ? 255 - (255 - butterfly.alpha) * (255 - background.alpha) / 128 : butterfly.alpha * background.alpha / 128
        };
    };

16.亮光,processor需要指定为 vividLight


pixProcesser.vividLight: function (background, butterfly) {
        /// <summary>亮光</summary>

        return {
            red: butterfly.red <= 128 ? 255 - (255 - background.red) / (2 * butterfly.red) * 255 : background.red / (2 * (255 - butterfly.red)) * 255,
            green: butterfly.green <= 128 ? 255 - (255 - background.green) / (2 * butterfly.green) * 255 : background.green / (2 * (255 - butterfly.green)) * 255,
            blue: butterfly.blue <= 128 ? 255 - (255 - background.blue) / (2 * butterfly.blue) * 255 : background.blue / (2 * (255 - butterfly.blue)) * 255,
            alpha: butterfly.alpha <= 128 ? 255 - (255 - background.alpha) / (2 * butterfly.alpha) * 255 : background.alpha / (2 * (255 - butterfly.alpha)) * 255
        };
    };

17.线性光,processor需要指定为 linearLight


pixProcesser.linearLight: function (background, butterfly) {
        /// <summary>线性光</summary>

        return {
            red: Math.min(2 * butterfly.red + background.red - 255, 255),
            green: Math.min(2 * butterfly.green + background.green - 255, 255),
            blue: Math.min(2 * butterfly.blue + background.blue - 255, 255),
            alpha: Math.min(2 * butterfly.alpha + background.alpha - 255, 255)
        };
    };

18.点光,processor需要指定为 pinLight


pixProcesser.pinLight: function (background, butterfly) {
        /// <summary>点光</summary>

        if (typeof pixProcesser.pinLightProcess == "undefined") {
            pixProcesser.pinLightProcess = function (sourceColor, blendColor) {
                return blendColor <= 128 ? Math.min(sourceColor, 2 * blendColor) : Math.max(sourceColor, 2 * blendColor - 255);
            };
        }

        return {
            red: pixProcesser.pinLightProcess(background.red, butterfly.red),
            green: pixProcesser.pinLightProcess(background.green, butterfly.green),
            blue: pixProcesser.pinLightProcess(background.blue, butterfly.blue),
            alpha: pixProcesser.pinLightProcess(background.alpha, butterfly.alpha)
        };
    };

19.实色混合,processor需要指定为 hardMix


pixProcesser.hardMix: function (background, butterfly) {
        /// <summary>实色混合</summary>

        return {
            red: (background.red + butterfly.red) < 255 ? 0 : 255,
            green: (background.green + butterfly.green) < 255 ? 0 : 255,
            blue: (background.blue + butterfly.blue) < 255 ? 0 : 255,
            alpha: (background.alpha + butterfly.alpha) < 255 ? 0 : 255
        };
    };

20.差值,processor需要指定为 difference


pixProcesser.difference: function (background, butterfly) {
        /// <summary>差值</summary>

        return {
            red: Math.abs(butterfly.red - background.red),
            green: Math.abs(butterfly.green - background.green),
            blue: Math.abs(butterfly.blue - background.blue),
            alpha: Math.abs(butterfly.alpha - background.alpha),
        };
    };

21.排除,processor需要指定为 exclusion


pixProcesser.exclusion: function (background, butterfly) {
        /// <summary>排除</summary>

        return {
            red: (butterfly.red + background.red) - butterfly.red * background.red / 128,
            green: (butterfly.green + background.green) - butterfly.green * background.green / 128,
            blue: (butterfly.blue + background.blue) - butterfly.blue * background.blue / 128,
            alpha: (butterfly.alpha + background.alpha) - butterfly.alpha * background.alpha / 128
        };
    };

22.减去,processor需要指定为 subtract


pixProcesser.subtract: function (background, butterfly) {
        /// <summary>减去</summary>

        return {
            red: Math.max(0, background.red - butterfly.red),
            green: Math.max(0, background.green - butterfly.green),
            blue: Math.max(0, background.blue - butterfly.blue),
            alpha: Math.max(0, background.alpha - butterfly.alpha)
        };
    };

23.划分,processor需要指定为 divide


pixProcesser.divide: function (background, butterfly) {
        /// <summary>划分</summary>

        return {
            red: (background.red / butterfly.red) * 255,
            green: (background.green / butterfly.green) * 255,
            blue: (background.blue / butterfly.blue) * 255,
            alpha: (background.alpha / butterfly.alpha) * 255,
        };
    };


至此完成。您可以自己动手实验啦~~~

非常感谢您的阅读!您的支持是我的动力!



   
1
0
查看评论

PS中图层混合模式的Blend公式

自己的总结:(伪代码+注释) // 混合模式的代入式:// 颜色的数据类型,里头分另有(RGBA/XYZW)的分量组合在, // 与单个标量的运算时:未指定分量运算时 // 默认是 float4(RGBA/XYZW)// 旧的已绘制出来的图层颜色 // 也叫:DESTINATION COL...
  • linjf520
  • linjf520
  • 2016-04-07 15:21
  • 2652

实现photoshop 正常混合的shader

实际上就是openGl里面的混合模式 {GL_SRC_ALPHA,GL_ONE_MIUNS_SRC_ALPHA} 但是因为直接在cocos里面用混合模式就会多创建一个精灵,由于图片较大没有加入图集所以会增加draw call。 就用shader实现了一个。 vec4 BlendNormal(ve...
  • skillart
  • skillart
  • 2016-08-25 17:27
  • 520

photoshop 图层混合模式

ps图层不透明度设置 图层的不透明度决定它显示自身图层的程度:如果不透明度为 1% 的图层显得几乎是透明的,而透明度为 100% 的图层显得完全不透明(这里注意不要弄反了)。图层的不透明度的设置方法是在图层面板中“不透明度”选项中设定透明度的数值,100%为完全显示 除了设置图层的不透明度以...
  • chad20080808
  • chad20080808
  • 2014-05-29 15:07
  • 290

Photoshop图层混合模式计算公式

关于photoshop的图层混合模式,大家一定都非常熟悉了,我在这里把各种混合模式的计算公式都详细的描述一便,希望能够对大家理解图层的混合模式有所帮助,编写仓促,不足之处请多批评指正。 混合模式可以将两个图层的色彩值紧密结合在一起,从而创造出大量的效果。在这些效果的背后实际是一些简单的数学公式在起...
  • gjq_1988
  • gjq_1988
  • 2013-01-30 20:46
  • 685

Canvas 关于混合模式 PorterDuff.Mode.MULTIPLY(正片叠底)的使用

在做图片处理的时候,特别是两张图片处理的时候,需要把他们重叠在一起,需要显示出不同的重叠效果,PS中有多种图片的处理方式。自己可以尝试的使用一下。
  • wb175208
  • wb175208
  • 2016-10-22 17:19
  • 371

PHOTOSHOP图层混合模式的计算公式

PS和Nuke的叠加模式计算算法相差甚远,最近想在Nuke中模拟ps叠加模式。老外已经有个gizmo了么免费的功能少,得付费购买,先百度找了下PS的图层混合模式的计算公式收藏慢慢想想怎么在Nuke中实现最好。 注释: 1.混合模式的数学计算公式,另外还介绍了不透明度。 2.这些公式仅适用于RG...
  • wang_390486690
  • wang_390486690
  • 2015-06-03 21:49
  • 391

Photoshop图层混合模式的计算公式

转自 http://blog.csdn.net/pizi0475/article/details/8241774PS和Nuke的叠加模式计算算法相差甚远,最近想在Nuke中模拟ps叠加模式。老外已经有个gizmo了么免费的功能少,得付费购买,先百度找了下PS的图层混合模式的计算公式收藏慢慢想想怎么在...
  • chy555chy
  • chy555chy
  • 2017-01-04 09:27
  • 285

ps的颜色混合模式算法

先附上中英文对比 C++实现 #define ChannelBlend_Normal(A,B) ((uint8)(A)) #define ChannelBlend_Lighten(A,B) ((uint8)((B > A) ? B:A)) #defin...
  • hacson
  • hacson
  • 2016-09-26 14:53
  • 939

Photoshop PS图层混合模式详解

Photoshop 7.0的图层混合选项中增添了“线性加深”模式、“线性减淡”模式、“亮光”模式、“线性光”模式和“点光”模式,这样在制作一些效果时更为方便。   这里我们便以中文版Photoshop 7.0为例来介绍一下Photoshop “图层混合模式”,除了新增的少许混合模式,其他...
  • pizi0475
  • pizi0475
  • 2012-11-30 07:30
  • 1283

Photoshop图层混合模式计算公式大全

Photoshop图层混合模式计算公式大全关于photoshop的图层混合模式,大家一定都非常熟悉了,我在这里把各种混合模式的计算公式都详细的描述一便,希望能够对大家理解图层的混合模式有所帮助,编写仓促,不足之处请多批评指正。混合模式可以将两个图层的色彩值紧密结合在一起,从而创造出大量的效果。在这些...
  • wallimn
  • wallimn
  • 2008-03-15 11:31
  • 3196
    个人资料
    • 访问:54069次
    • 积分:1004
    • 等级:
    • 排名:千里之外
    • 原创:22篇
    • 转载:0篇
    • 译文:8篇
    • 评论:53条
    博客专栏
    最新评论