接下来几篇文章我们将稍微简单的探索下PS中多种图层混合模式的算法内部原理,因为毕竟没有这方面的官方资料,所以很多方面也只是本人自己的探索和实践,有可能和实际的情况有着较大的差异。

在PS的实践中,图层样式的存在使得一个简单的图形蜕变为一个丰富的样式成为可能,而在PS的各个版本中,图层样式的选项也越来越丰富, 功能也越来越强大。作为一个成功的图形和图形编辑软件,图层样式功能是否缺失也可以看成其是否具有强大生命力的一个典型标志,比如作为图像开源界的扛把子 GIMP 就没有这个功能。而平时我们能看到的商业软件也鲜有这个功能。

  在我使用的CS6版本的PS中,提供了斜面和浮雕、描边、内阴影、内发光、光泽、颜色叠加、渐变叠加、图案叠加、外发光、投影等10中图层样式,在我后续的文章中将分别讲述除了 外发光和内发光 之外的其他8种样式的原理和实现。

本文将简单讲述投影样式的原理,投影样式的可控参数界面如下所示:

参数包含了混合模式、不透明度、角度、距离、扩展、大小‘等高线、消除锯齿、杂色等。我们先从大的方向开始讲起。

在PS中,如果我们打开一幅JPG图像(一般为RGB格式的),我们会发现PS为该图像所其的名字为背景层,而且层右侧有一个锁的符号,如下所示:

       

  如果此时我们双击这个层,出现的是新建图层的界面,而不是图层样式的旋向,如上图所示。

但是,如果我们打开的是一副带透明通道的32位PNG图像,此时系统默认就是用图层0为该图像命名,而且后侧没有锁的符号。

此时双击图层符号,则打开了图层样式对话框。

通过这个现象可以做个简单的猜测,图层样式需要Alpha通道,而实际的研究也表明,大部分的图层样式(除颜色叠加、渐变叠加、图案叠加,我局的应该把他们从样式中开除)都是对Alpha通道的数据进行一定处理后,再配合某种颜色和原图进行一定程度的融合。

完美甚至可以沿用另外一种流行的说法,图层样式其内在实际上是按照一定的规则虚拟了1个或几个图层,然后通过不同的图层位置(位于上部或下部)、混合样式、不透明度等和原图进行混合。这个也是所有的样式里的混合模式、不透明度的概念源头所在。

再次回到这个投影样式吧。 在PS里随意的弄下这个效果,可以直观的感觉到这个样式的作用是根据所选参数在当前层下部虚拟一个阴影层。

那么我的实现思路核心如下:

第一步: 按照指定的角度将原图的Alpha信息偏移一定的角度,偏移后无效的区域Alpha设置为0。

      

        原始图像                                               原始图像的Alpha通道信息                           按照指定的角度偏离后的Alpha信息(角度30, 距离20)

  简单的代码如下所示:

    float SinV = -sinf(Angle / 180.0 * 3.1415926f);
float CosV = cosf(Angle / 180.0 * 3.1415926f);
int Left = (int)(Distance * CosV + 0.499999f);
int Top = (int)(Distance * SinV + 0.499999f);
// 计算Alpha通道的偏移信息
for (int Y = 0; Y < Height; Y++)
{
int NewY = Y + Top;
if ((NewY < 0) || (NewY >= Height))
{
memset(ShiftA + Y * Width, 0, Width);
}
else
{
unsigned char *LinePD = ShiftA + Y * Width;
for (int X = 0; X < Width; X++)
{
int NewX = X + Left;
if ((NewX < 0) || (NewX >= Width))
{
LinePD[X] = 0;
}
else
{
int Index = NewY * Stride + NewX * 4 + 3;
LinePD[X] = Src[Index];
}
}
}
}

  界面中的角度和距离共同决定了这个Alpha通道偏离的程度。

  对面后面的大小和扩展参数,我们结合网络中的一些参考资料,通过本人的实践,基本上可以确定是使用的如下算法。

  首先我们把大小设置为10,然后把扩展设置为100%,对于上面的图,可达到如下效果:

     

        大小为10,扩展为100%时的结果                          大小为0时的结果

  可以看到,当大小为10,扩展100%时,阴影部分变的更为粗大,通过测试,我们发现这个实际上应该是对前述偏移后的Alpha选区进行了一定程度的圆形最大值算法,我们是是圆形,我们可以比较下同样半径的圆形和矩形最大值的结果区别:

       

        半径为10的矩形最大值                          半径为10的圆形最大值

很明显的可以看到,矩形最值不能保留原来光滑的圆角,而圆形可以。

因此,我们推测扩展就是对选区进行圆形的最大值算法,而最大值的半径和大小以及扩展的数据有关,根据PS界面扩展后面的% 百分比可以认定他为大小的 百分比。

而大小参数,明显可以看到,随着大小的变大,阴影越来越模糊,因此,可以猜测这个为对Alpha进行模糊。不过我测试所,似乎并不是高斯模糊,不晓得实际为何种模糊。

    //    第二步对这个Alpha进行下堵窒,算法上就是圆形的最大值算法
int ChokeSize = (Size * Choke + 49) / 100;
if (ChokeSize != 0) // 堵窒
{
Status = IM_MaxFilter_Round_Gray(ShiftA, ShiftA, Width, Height, Width, ChokeSize);
if (Status != IM_STATUS_OK) goto FreeMemory;
}
// 第三步对Alpha进行羽化了,高斯模糊(但是PS的不晓得属于那种模糊)
if ((Size != 0) && (Size != ChokeSize))
{
Status = IM_GaussBlur(ShiftA, ShiftA, Width, Height, Width, Size - ChokeSize);
if (Status != IM_STATUS_OK) goto FreeMemory;
}

  那么下面还有一个关键的东西,就是那个等高线,这个东西网络上把他说的好神奇,各路大神都有发表感言。我看啊,都是假神,那个东西其实就是如他表面所表现出来的东西,就是一个曲线调整,而且和PS本身的曲线也是一个意思,只不过他调整的不是图像里的RGB,而是这里的Alpha,通过动态调整这个Alpha获得不同的结果。

    //    第四步对选区进行等高线算法,实际上就是一个查表
for (int Y = 0; Y < Height * Width; Y++)
{
ShiftA[Y] = Table[ShiftA[Y]];
}

  那么最后一步,就是根据不透明度、混合模式以及用户提供的背景色来创建一个新的图层,这个图层位于当前层下方,进行图层混合了。如果是一个单独的图层,由于这个图层下面没有其他图层,混合样式在这里其实是起不到作用的(除了那个另类的溶解),这个时候一个简单的混合代码如下所示:

    for (int Y = 0; Y < Height; Y++)
{
unsigned char *LinePD = Dest + Y * Stride;
unsigned char *LinePS = Src + Y * Stride;
unsigned char *LinePA = ShiftA + Y * Width;
for (int X = 0; X < Width; X++)
{
int B1 = BackColor_B, G1 = BackColor_G, R1 = BackColor_R, A1 = LinePA[X];
int B2 = LinePS[0], G2 = LinePS[1], R2 = LinePS[2], A2 = LinePS[3];
int NewA1 = A1 * Opacity;
int BlendAlpha = IM_Div255(A2 * NewA1);
int Alpha = A2 * 255 + NewA1 - BlendAlpha;
if (Alpha != 0)
{
LinePD[0] = (B1 * NewA1 + B2 * A2 * 255 - BlendAlpha * B1) / Alpha;
LinePD[1] = (G1 * NewA1 + G2 * A2 * 255 - BlendAlpha * G1) / Alpha;
LinePD[2] = (R1 * NewA1 + R2 * A2 * 255 - BlendAlpha * R1) / Alpha;
}
else
{
LinePD[0] = LinePS[0];
LinePD[1] = LinePS[1];
LinePD[2] = LinePS[2];
}
LinePD[3] = IM_Div255(Alpha);
LinePS += 4;
LinePD += 4;
}
}

  注意这里的混合的Alpha需要改变。

至于界面里的消除锯齿应该是针对曲线的,这个就是在曲线插值时加上抗锯齿功能,那个什么杂色之类的无所谓,就是在Alpha信息里加上一些随机噪音。没啥好难的。

当然,经过一些其他测试,发现PS里的投影还有一些更为复杂的逻辑,和本文的讲述不一致,但是本文的效果也在一定程度上能局部复原结果,对于一些普通的应用是足以完成任务了

提供一个链接工大家测试:https://files.cnblogs.com/files/Imageshop/DropShadow.rar

如果想时刻关注本人的最新文章,也可关注公众号:

【PS算法理论探讨二】 Photoshop中图层样式之 投影样式 算法原理初探讨。的更多相关文章

  1. 装载:对CSS中的Position、Float属性的一些深入探讨

    对CSS中的Position.Float属性的一些深入探讨   对CSS中的Position.Float属性的一些深入探讨 对于Position.Float我们在平时使用上可以说是使用频率非常高的两个 ...

  2. 【转】shell命令中>/dev/null 2>&1的实现原理

    异步执行 exec("/alidata/server/php/bin/php /nas/wxdoctor/index.php App/Common/WordsPic/user_id/&quo ...

  3. (二十)WebGIS中图层树功能的设计和实现

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在GIS的桌面工具中,比如arcgis desktop或者S ...

  4. (十二) WebGIS中矢量图层的设计

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.前言 在前几章中我们已经了解了什么是矢量查询.屏幕坐标与地理坐标之 ...

  5. ps中图层混合模式、多图层叠加、不透明度、填充、图层样式详解

    图像领域中,通过进行一下想法的时候,都要通过用ps看下是不是合理,而ps中图层是必用的一个功能,下面详解一下图层有关的叠加原理. 基本顺序是图层从下往上继续, 先计算图层的填充,再计算样式.最后计算不 ...

  6. SLAM中的优化理论(二)- 非线性最小二乘

    本篇博客为系列博客第二篇,主要介绍非线性最小二乘相关内容,线性最小二乘介绍请参见SLAM中的优化理论(一)-- 线性最小二乘.本篇博客期望通过下降法和信任区域法引出高斯牛顿和LM两种常用的非线性优化方 ...

  7. [Java 泥水匠] Java Components 之二:算法篇之项目实践中的位运算符(有你不懂的哦)

    作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节. 交流QQ群:[编程之美 365234583] ...

  8. 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法

    若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...

  9. [转载] 散列表(Hash Table)从理论到实用(中)

    转载自:白话算法(6) 散列表(Hash Table)从理论到实用(中) 不用链接法,还有别的方法能处理碰撞吗?扪心自问,我不敢问这个问题.链接法如此的自然.直接,以至于我不敢相信还有别的(甚至是更好 ...

随机推荐

  1. [TJOI2007] 可爱的质数

    题意 求最小的\(x\)满足\(a^x \equiv b\mod p\) 想法 这个是标准的板子题,\(BSGS\)算法可以用来解决\(a^x \equiv b\mod p\) 和 \(x^a \eq ...

  2. [Linux] Miniconda安装及其使用

    集群环境下安装conda进行软件管理.Miniconda是Anaconda的简化版,对于一般需求而言就够用了.因此,我这里安装Minconda3进行软件安装管理. 安装 Miniconda下载地址,版 ...

  3. 41-Climbing Stairs-leetcode

    Climbing Stairs My Submissions QuestionEditorial Solution Total Accepted: 106498 Total Submissions: ...

  4. mysql-计算排名

    mysql计算排名,获取行号rowno 学生成绩表数据 SELECT * FROM table_score ORDER BY score DESC; 获取某个学生成绩排名并计算该学生和上一名学生成绩差 ...

  5. CPU大小端模式及转换

    通信协议中的数据传输.数组的存储方式.数据的强制转换等这些都会牵涉到大小端问题. CPU的大端和小端模式很多地方都会用到,但还是有许多朋友不知道,今天暂且普及一下. 一.为什么会有大小端模式之分呢? ...

  6. Yarn 容量调度器多队列提交案例

    目录 Yarn 容量调度器多队列提交案例 需求 配置多队列的容量调度器 1 修改如下配置 SecureCRT的上传和下载 2 上传到集群并分发 3 重启Yarn或yarn rmadmin -refre ...

  7. openwrt装载固件

    方法1. 确定串口号以后(在设备管理器可以查看) 打开SecureCRT软件,选择串口,设置合适的波特率(我用的115200),然后快速连接, 板子通电启动,在启动的时候会提示按任意键中断,这时按下任 ...

  8. Can references refer to invalid location in C++?

    在C++中,引用比指针更加的安全,一方面是因为引用咋定义时必须进行初始化,另一方面是引用一旦被初始化就无法使其与其他对象相关联. 但是,在使用引用的地方仍然会有一些例外. (1)Reference t ...

  9. 使用 ACE 库框架在 UNIX 中开发高性能并发应用

    使用 ACE 库框架在 UNIX 中开发高性能并发应用来源:developerWorks 中国 作者:Arpan Sen ACE 开放源码工具包可以帮助开发人员创建健壮的可移植多线程应用程序.本文讨论 ...

  10. js实现递归菜单无限层

    /*动态加载菜单*/ function dynamicMenu(data){ if (userID != "admin"){ //1.清空所有菜单 $("#menuLis ...