实现渐变色与图像叠加效果

作者: 阮建辉

在图象图形的编程中, 经常会见到渐变色以及各种图片的叠加等效果. 这篇文章就是要对这些效果的原理加以分析, 并在Elastos© 操作系统[1]Mobile Edition  SDK上和Windows 200 Professional上使用Visual C++ 6.0 编程实现.

一.   RGB三维模型与渐变色的原理及实现

1.     RGB三维模型

作为计算机图形学中重要的原色混合系统, RGB(红绿蓝)加色系统广泛应用于发光体, 如彩色CRT显示或彩色灯光. 这三种单色是得以匹配或生成可见光谱中几乎所有颜色的最小数量的原色.

为了适应不同的颜色深度, 使用0-1来表示R, G, B颜色深浅. 使用三维坐标分别表示RGB. 如下图1所示. 这样原点RGB(0, 0, 0)就表示黑色, 而原点对应的顶点RGB(1, 1, 1)就表示白色. 使用三维表示的好处就在于直观, 以及在颜色变化过程中容易得到颜色变化的规律.

  (图1)

在图1所表示的空间中(点表示为 (R, G, B), 0≤R≤1, 0≤G≤1,0≤B≤1). 现在假设有某颜色(比如图1中的点Q), 其值为(r, g, b). 那么从黑色到该颜色的点(在图1中就对应从点(0,0,0)到点Q(r, g, b)的线段)从视觉上是越来越浅. 设这些点的坐标为(x, y, z)(0≤x≤r, 0≤y≤≤g, 0≤z≤b), 那么在我们的RGB三维坐标空间中就有方程:

(公式1)

(注意: 这里处理的点都是三维模型所表示的立体内的点, 其中边界情况请读者自行考虑)

从该点到白色的点(在图1中就对应从点Q(r, g, b)到点(1, 1, 1)的线段)的颜色继续变浅. 设这些点的坐标为(x, y, z)(r≤x≤1, g≤y≤1, b≤z≤1), 那么这些点满足方程:

(公式2)

(注意: 同上, 这里处理的点都是三维模型所表示的立体内的点, 其中边界情况请读者自行考虑)

2.      渐变色的实现思路

    渐变色是我们经常见到的颜色处理方式, 最常见的如Windows的标题栏就是使用的渐变处理效果.

    比如要实现如下图所示的渐变效果,

  (图2)

我们可以采取这样的方式来达到目的: 从这个矩形的上面到下面一条一条地

画线, 而从上往下的线的颜色就越来越浅. 当然, 使用这样的思路进行扩展, 我们还能做出更多的渐变效果, 比如从两边往中间, 或者倾斜45度角进行渐变等等.

3.      编程实现

我们在一个按钮上实现这种渐变效果, 那么只要重载这个按钮的OnPaint()函数即可. 在下面代码所在的设备环境中, 一个COLORREF为一个32位数, 从最高到最底的每个8位所代表的含义分别为为: 未定义(31-24bit), 红色(23-16bit), 绿色(15-8bit), 蓝色(7-0bit). 由于考虑到代码的平台移植, 能够自己实现的功能将不使用系统所提供的API.

定义宏, 将RGB转化为COLORREF值(在Windows中有相应的RGB宏):

#define RGBTOCOLORREF(R, G, B) (R << 16) | /

(G <<  8) | /

(B)

再定义一个宏, 从COLORREF中取得R, G, B三种颜色(在Windows中相应的GetRValue, GetGValue, GetBValue三个API可以实现下面宏的功能):

#define COLORREFTORGB(COLORREF, R, G, B) R = (COLORREF & 0x00FF0000) >> 16; /

G = (COLORREF & 0x0000FF00) >>  8; /

B = (COLORREF & 0x000000FF)

编程思路:

首先得到当前环境的设备上下文指针(DC指针, 对应于Elastos中为GDC指针, 对应于Windows中为CDC指针), 然后得到按钮所在的矩形, 从上往下一条一条线地进行填充, 线的颜色逐渐变浅.

(1). 在Elastos Mobile Edition SDK中具体代码如下:

void CButtonIndex::OnPaint()

{

GDC* pdc = GetDC();

RECT* rcOld = GetRect();

PEN pen = {SOLID_LINE, 1, m_Color};

POINT point1 = {0, 0};

POINT point2 = {rcOld->right - rcOld->left, 0};

int nColorRed, nColorGreen, nColorBlue;

COLORREFTORGB(pen.color, nColorRed, nColorGreen, nColorBlue);

double alpha = 1.0;

while (point1.y < rcOld->bottom - rcOld->top) {

pen.color = (((int)(nColorRed   * alpha)) << 16) |

(((int)(nColorGreen * alpha)) <<  8) |

(int)(nColorBlue * alpha);

/* 注意: 此处将RGB颜色数只是简单进行线性增加, 而并未严格按照公式2进行修正, 目的是为了节约计算量*/

pdc->DrawLine(&point1, &point2, &pen);

alpha += 0.025;

point1.y += 1;

point2.y += 1;

}

}

(2). 在Windows 2000上使用Visual C++ 6.0实现

新建一个基于对话框的MFC工程, 使用ClassWizard新建一个类CButtonIndex, 选择它的基类为CButton. 向导将为你生成这个类的ButtonIndex.h和ButtonIndex.cpp文件. 使用ClassWizard为CButtonIndex类增加一个消息处理, 选择WM_PAINT消息, 向导将为CButtonIndex类增加一个 OnPaint()函数, 在此函数中填充代码如下:

void CButtonIndex::OnPaint()

{

CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here

RECT rcRgn;

GetClientRect(&rcRgn);

POINT ptStart = {rcRgn.left, rcRgn.top};

POINT ptEnd   = {rcRgn.right, rcRgn.top};

COLORREF color = 0x456789;

COLORREF nColorRed, nColorGreen, nColorBlue;

COLORREFTORGB(color, nColorRed, nColorGreen, nColorBlue);

double alpha = 1.0;  //定义最初的颜色增长基数

while (ptEnd.y < rcRgn.bottom - rcRgn.top)

{

color = (((int)(nColorRed   * alpha)) << 16) |

(((int)(nColorGreen * alpha)) <<  8) |

(int)(nColorBlue * alpha);

CPen pen(PS_SOLID, 1, color);

CPen* ppen = dc.SelectObject(&pen);

dc.MoveTo(ptStart);

dc.LineTo(ptEnd);

dc.SelectObject(ppen);

alpha += 0.025;

ptStart.y += 1;

ptEnd.y += 1;

}

}

二.   Alpha通道的原理及实现

1. alpha通道的原理

在各种广告招贴画, 游戏, 电影海报中经常看到图片叠加之后的深入浅出的效果, 两副图片叠在一起的时候, 看起来感觉“你中有我”, “我中有你”. 这种将两副或者更多的画面叠加在一起所使用的技术就是alpha通道. 它的原理就是将两副图片对应的同一个位置的点的颜色, 各取一部分, 然后叠加. Alpha通道是各种图片处理程序的原理基础.

现在假设有N1, N2, N3, …, Nn副图片要进行叠加(其中n >= 2, 一副图片当然也就谈不上叠加, 也就无所谓alpha通道了), 他们相同坐标(在二维平面中)的某个点的颜色使用RGB表示分别为(R1, G1, B1), (R2, G2, B2), (R3, G3, B3), …, (Rn, Gn, Bn). 如果这N副图片在最终合成的图片中占的比率分别为α1, α2, α3, …, αn(必须满足α1 + α2 + α3 + … + αn = 1), 那么最终的合成的图片的在这一坐标点的颜色的R, G, B就分别为

R = R11 + R22 + R33 + … + Rnn;

G = G11 + G22 + G33 + … + Gnn;

B = B11 + B22 + B33 + … + Bnn;  (公式3)

2. 编程实现

为了简化程序的编码, 下面的实例中将只叠加两副图片.

程序的最后结果如下所示:

  (图3)

其中左上角的图片为长城, 右边的图片的夕阳中的金字塔, 下面的图片为将“两副图片的颜色各取一半”而合成后的结果. 可以看到主画面的的长城, 当然也可以看到落暮的夕阳和金字塔, 还有树木的轮廓.

编程思路:

首先分别得到两副图片(这里两副图片的尺寸是一样大的)中相同位置的点的颜色, 然后使用alpha混合之后打印到屏幕上, 即得到了合成后的图片.

(1). 在Elastos Mobile Edtion SDK上实现的具体代码如下:

程序代码如下(这回在FORM的OnPaint()函数中实现):

void CFormAlpha::OnPaint()

{

CForm::OnPaint();

RECT rcPic1 = {0, 0, 86, 108};

RECT rcPic2 = {89, 0, 175, 108};

POINT ptPicStart = {45, 110};

GDC* pdc = GetDC();

//将指定图片载入指定矩形

pdc->DrawImageAdapt("/bin/picture/4.jpg", &rcPic1);

pdc->DrawImageAdapt("/bin/picture/7.jpg", &rcPic2);

COLORREF nColor1, nRed1, nGreen1, nBlue1;

COLORREF nColor2, nRed2, nGreen2, nBlue2;

COLORREF nColor, nRed, nGreen, nBlue;

double alpha = 0.5;  //定义的alpha值

for (int y = 0; y < 108; y++) {

for (int x = 0; x < 87; x++) {

nColor1 = pdc->GetPixel(rcPic1.left + x, rcPic1.top + y);

nColor2 = pdc->GetPixel(rcPic2.left + x, rcPic2.top + y);

COLORREFTORGB(nColor1, nRed1, nGreen1, nBlue1);

COLORREFTORGB(nColor2, nRed2, nGreen2, nBlue2);

//计算得到目标图片的RGB三原色

nRed = (int)(nRed1 * alpha + nRed2 * (1 - alpha));

nGreen = (int)(nGreen1 * alpha + nGreen2 * (1 - alpha));

nBlue = (int)(nBlue1 * alpha + nBlue2 * (1 - alpha));

nColor = RGBTOCOLORREF(nRed, nGreen, nBlue);

//合成并打印到屏幕

pdc->SetPixel(x + ptPicStart.x, y + ptPicStart.y, nColor);

}

}

}

(2). 在Windows 2000上使用Visual C++ 6.0实现

新建一个SDI的MFC工程, 将要混合的两副图片导入, 然后填充View类的OnDraw函数如下:

void CAlphaPlusView::OnDraw(CDC* pDC)

{

CAlphaPlusDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

CDC memDC1;

memDC1.CreateCompatibleDC(pDC);

CBitmap bitmap1;

bitmap1.LoadBitmap(IDB_BITMAP1);

BITMAP bmpInfo1;

bitmap1.GetBitmap(&bmpInfo1);

CBitmap* pBitmap1 = memDC1.SelectObject(&bitmap1);

pDC->BitBlt(0,

0,

bmpInfo1.bmWidth,

bmpInfo1.bmHeight,

&memDC1,

0,

0,

SRCCOPY);

CDC memDC2;

memDC2.CreateCompatibleDC(pDC);

CBitmap bitmap2;

bitmap2.LoadBitmap(IDB_BITMAP2);

BITMAP bmpInfo2;

bitmap2.GetBitmap(&bmpInfo2);

CBitmap* pBitmap2 = memDC2.SelectObject(&bitmap2);

pDC->BitBlt(bmpInfo1.bmWidth + 1,

0,

bmpInfo2.bmWidth,

bmpInfo2.bmHeight,

&memDC2,

0,

0,

SRCCOPY);

COLORREF nColor1, nRed1, nGreen1, nBlue1;

COLORREF nColor2, nRed2, nGreen2, nBlue2;

COLORREF nColor, nRed, nGreen, nBlue;

double alpha = 0.5;

for (int y = 0; y < bmpInfo1.bmHeight; y++)

{

for (int x = 0; x < bmpInfo1.bmWidth; x++)

{

nColor1 = memDC1.GetPixel(x, y);

nColor2 = memDC2.GetPixel(x, y);

COLORREFTORGB(nColor1, nRed1, nGreen1, nBlue1);

COLORREFTORGB(nColor2, nRed2, nGreen2, nBlue2);

nRed = (int)(nRed1 * alpha + nRed2 * (1.0 - alpha));

nGreen = (int)(nGreen1 * alpha + nGreen2 * (1.0 - alpha));

nBlue = (int)(nBlue1 * alpha + nBlue2 * (1.0 - alpha));

nColor = RGBTOCOLORREF(nRed, nGreen, nBlue);

pDC->SetPixel(x, y + bmpInfo1.bmHeight + 1, nColor);

}

}

memDC2.SelectObject(pBitmap2);

memDC1.SelectObject(pBitmap1);

}

 后记: 作为图形图象处理技术的基本算法原理, 渐变色和alpha通道的使用能为图形图象设计和处理带来各种生动的效果, 希望本文能给你在此方面有所帮助. 有任何问题或者指教或者索取源代码, mailto:tigger_211@sina.com


[1] Elastos©和欣©操作系统是科泰世纪科技有限公司独立开发, 拥有自主知识产权的嵌入式操作系统.  它是基于微内核结构, 支持多进程, 多线程的嵌入式操作系统; 基于ezCOM©技术的操作系统新型体系结构, 灵活内核技术. 其Mobile Edition是专为智能手持设备所开发的版本.

实现渐变色与图像叠加效果相关推荐

  1. 自制操作系统(十) 图像叠加处理

    2016.07.12 参考书籍:<30天自制操作系统>.<自己动手写操作系统> qq:992591601  欢迎交流 图像叠加处理的原理很简单,就是给图像分层,从低下往上面画, ...

  2. Java 为图形填充渐变色

    1. 首先在项目中创建一个继承 JFrame 类的 FillGradientFrame 窗体类 2. 在 FillGradientFrame 窗体类中,创建内部面板类 FillGradientFram ...

  3. 完美免费在线去背景图片,便捷变速。在5秒内消除或者替换图像背景,智能调整颜色,所有操作都在浏览器完成,无需上传图像 - BgSub

    相信很多小伙伴都有去图片背景图片的需求,有时候不想打开PS,又不想麻烦美工,还是自己来吧. 今天推荐的这个网站是 bgsub,它是基于机器学习的图像修复方法,自动去除图片背景. 功能 完全自动化,免费 ...

  4. Python3绘制分形图像

    如何使用Python3计算.绘制.保存分形图像呢?下面以Mandelbrot分形图像为例介绍. 一.计算分形图像点集 Mandelbrot集由一个复变函数f(z) = z*z + c生成,其中c为当前 ...

  5. 前端必备的Canvas接口和动画效果的总结

    来源 | https://segmentfault.com/a/1190000021998875 概述 <canvas>元素用于生成图像.它本身就像一个画布,JavaScript 通过操作 ...

  6. 必备的Canvas接口和动画效果大全

    来源 | https://segmentfault.com/a/1190000021998875 1.概述 <canvas>元素用于生成图像.它本身就像一个画布,JavaScript 通过 ...

  7. three.js 贴图只显示颜色_C4D作品“花里胡哨”?我怀疑你贴图方式有问题……

    关于C4D,行业内有一句很流行的话:"三分模型七分贴图",模型当然指的是前期的建模工作,而贴图,指的就是"纹理贴图". 纹理化的过程,不仅仅只是为3D模型添加颜 ...

  8. 基于python的空域变换

    基于python的空域变换 空域变换 加法运算 减法运算 乘法运算 逻辑运算 缩放 平移 旋转 后续 空域变换 空域:是指图像所在的平面,即像素位置所在的空间. 空域变换:对像素点的位置和灰度值根据图 ...

  9. Google I/O:谷歌AR看似不紧不慢,实则暗藏玄机

    在今天举行的Google I/O大会上,尽管AI是全场最大的关注点,也还是有一系列AR相关技术和应用更新,比如:ARCore进行更新.推出新的Geospatial Creator等等. ARCore面 ...

最新文章

  1. WCF4.0新特性体验(3):标准终结点(Standard Endpoints)
  2. BEC攻击危害惊人 3年造成23亿美元损失
  3. 为什么 K8s 在阿里能成功?| 问底中国 IT 技术演进
  4. Systick 延时函数详解
  5. C++第10周项目2扩展之2参考——迭代求和
  6. 面向对象的代码生成方法
  7. 教程:如何实现Java OAuth 2.0以使用GitHub和Google登录
  8. 查看地区的ip段_「教程」CloudFlare 自选 IP优化网站速度
  9. 如何应对视觉深度学习存在的问题
  10. 数据可视化系列(二):艺术画笔见乾坤
  11. vue.3.0 dom赋值_Vue 3.0 快速入门
  12. undefined symbol: PyFPE_jbuf
  13. 机器学习基础算法15-回归实例-线性回归、Ridge回归、LASSO、ElasticNet的高阶参数与过拟合以及TSS>=ESS+RSS代码验证
  14. # 笔记2021-11
  15. Java学习笔记之设计模式(7)单例模式
  16. 辅助进度控制的计算机软件是指,辅助进度控制的计算机软件是指以( )为核心的项目管理软件包。...
  17. 微波雷达感应模块,人体存在感应雷达技术,广告屏智能感应显示
  18. Tmux(-yank,-cssh,-xpanes)使用指南
  19. 360wifi驱动 linux ap,360wifi驱动
  20. 21 年年度最佳开源软件!

热门文章

  1. Maven命令报错读取jar时出错
  2. python 之 面向对象(成长笔记)
  3. SAP GUI 770 windows 免费下载
  4. 使用网线通过远程桌面实现局域网内快速传输文件
  5. 求生之路2 游戏资料详细与细节
  6. 计算机毕设 SpringBoot+Vue影院售票系统 影院线上购票系统 影厅订票系统 电影院售票系统Java Vue MySQL数据库 远程调试 代码讲解
  7. MySQL数据库实例教程实训4_数据库管理系统MySQL实验4教程.doc
  8. 英特尔固态硬盘测试软件,SSD优化软件:Intel SSD Toolbox
  9. 第三十、Java面向对象之接口
  10. 软件随想录:程序员部落酋长Joel谈软件(local.joelonsoftware.com/wiki)-23