安卓PorterDuffXfermode之正片叠底 PorterDuff.Mode.MULTIPLY

news2025/2/6 7:52:40

setXfermode

Paint#setXfermode()接口是在绘制时设置画笔的图形混合模式的,下面是官网的介绍:

public Xfermode setXfermode (Xfermode xfermode)

Set or clear the transfer mode object. A transfer mode defines how source pixels (generate by a drawing command) are composited with the destination pixels (content of the render target).

Pass null to clear any previous transfer mode. As a convenience, the parameter passed is also returned.

PorterDuffXfermode is the most common transfer mode.

Parameters
xfermodeXfermode: May be null. The xfermode to be installed in the paint

翻译一下,setXfermode()函数就是用来为画笔设置Xfermode对象,Xfermode将决定绘制时源图形和目标图形的混合模式。其中源图形是指你将要绘制的内容,目标图形是指View中已有的内容,最终根据Xfermode中的模式将源图形和目标图形混合后的图形展示出来。

目前Xfermode的子类只有一个 PorterDuffXfermode ,所以只需要关注这一个类就行了,看官网:

Specialized implementation of Paint's transfer mode. Refer to the documentation of the PorterDuff.Mode enum for more information on the available alpha compositing and blending modes.

Public constructors

PorterDuffXfermode(PorterDuff.Mode mode)

Create an xfermode that uses the specified porter-duff mode.

构建PorterDuffXfermode需要传入一个枚举类 PorterDuff.Mode ,该类一共有18个枚举值,每一个枚举值都代表一种图像混合模式,“正片叠底”就是其中一种混合模式,关于每种混合模式更详细的内容可以看谷歌官网的介绍: PorterDuff.Mode  |  Android Developers

现在主要看 正片叠底模式:

public static final PorterDuff.Mode MULTIPLY     
Multiplies the source and destination pixels.

对应的图像合成计算公式是:[Sa * Da, Sc * Dc],其中 Sa Da 分别代表源图像和目标图像透明度,Sc Dc 分别代表源图像和目标图像的颜色值,通过正片叠底模式混合后的图像效果一般比混合前的图像颜色较深,并且任何颜色和黑色正片叠底得到的仍然是黑色,任何颜色和白色进行正片叠底得到的颜色依然是原来的颜色保持不变,听起来好像是两个很高大上的结论,在谷歌官网也只给了一个公式,具体怎么计算并没有详细介绍,接下来通过一个例子实际操作一下就清楚了。

写代码前首先还需要关注两个点:硬件加速和离屏绘制

如果:1.你的应用跑在API14的版本以后  2.你又要用到那些不支持硬件加速的函数,这个时候就需要禁用硬件加速。关于硬件加速更详细的内容,可以看下这篇文章:自定义控件三部曲之绘图篇(十)——Paint之setXfermode(一)_启舰-CSDN博客

离屏绘制先了解一种比较常用的可以实现离屏绘制的方法,在绘制的代码前后加上下面两句

        //绘制代码前加上这一句
        int saveId = canvas.saveLayer(0,0,width*2,
                height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        ......//中间是绘制的代码
        canvas.drawBitmap(mDestBitmap,0,0,mPaint);
        mPaint.setXfermode(xfermode);
        canvas.drawBitmap(mSrcBitmap,width/2,height/2,mPaint);
      
        //绘制代码后加上这句
        canvas.restoreToCount(saveId);

具体离屏绘制的原因,这篇文章里有介绍:HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解

好了接下来看一个很简单的正片叠底的例子,最后会一步步了解前面的公式到底是怎么计算的

package com.example.xfermodedemo.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

/**
 * @author 
 */
public class PorterDuffView extends View {

    private Bitmap mSrcBitmap;
    private Bitmap mDestBitmap;
    private Paint mPaint = new Paint();
    private int width = 500;
    private int height = 500;

    private int bg_green = 0xFF00FF00;
    private int mSrcColor = 0xFF66AAFF;//源图像的颜色值
    private int mDestColor = 0xFFFFCC44;//目标图像的颜色值 最高位FF 代表 Alpha = 1

    private final int multyColor = 0XFF668844;
    private final int multy_src_bg = 0xFF00AA00;
    private RectF rectF = new RectF(0,500,500,1000);

    /**
     * Mode.SRC_OUT
     * 计算公式为:[Sa * (1 - Da), Sc * (1 - Da)]
     *
     * Mode.SRC_IN
     * 计算公式为:[Sa * Da, Sc * Da]
     *
     * Mode.MULTIPLY(正片叠底)
     * 公式是:[Sa * Da, Sc * Dc]
     *
     */
    private PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);

    public PorterDuffView(Context context) {
        super(context);
        init();
    }

    public PorterDuffView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mSrcBitmap = makeSrcBitmap();
        mDestBitmap = makeDestBitmap();
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //save() restore() 不生效
        //canvas.save();
        //不在 canvas.saveLayer()和 canvas.restoreToCount()之间的代码都不会
        //参与正片叠底
        //canvas.drawColor(bg_green);//未离屏绘制不会参与正片叠底
        int saveId = canvas.saveLayer(0,0,width*2,
                height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        //canvas.drawColor(bg_green);//会参与正片叠底
        canvas.drawBitmap(mDestBitmap,0,0,mPaint);
        mPaint.setXfermode(xfermode);
        canvas.drawBitmap(mSrcBitmap,width/2,height/2,mPaint);
        mPaint.setXfermode(null);

        //canvas.restore();
        canvas.restoreToCount(saveId);

        mPaint.setColor(multyColor);
        mPaint.setStyle(Paint.Style.FILL);
        
        //以正片叠底最终合成后的颜色作为画笔颜色值 画四分之一圆作为对比
        canvas.drawArc(rectF,180,90,true,mPaint);
        //canvas.drawCircle(250f,750f,250f,mPaint);
    }
    //创建目标图像
    private Bitmap makeDestBitmap() {
        Paint paint = new Paint();
        paint.setColor(mDestColor);
        Bitmap bitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawOval(0,0,width,height,paint);
        return bitmap;
    }
    //创建源图像
    private Bitmap makeSrcBitmap() {
        Paint paint = new Paint();
        paint.setColor(mSrcColor);
        Bitmap bitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawRect(0,0,width,height,paint);
        return bitmap;
    }

}

正片叠底最终的颜色值是怎么计算的?

执行正片叠底混合模式后的最终颜色是通过下面两个颜色计算得来的

private int mSrcColor = 0xFF66AAFF;//源图像的颜色值
private int mDestColor = 0xFFFFCC44;//目标图像的颜色值 最高位FF 代表 Alpha = 1

正片叠底的计算公式是:[Sa * Da, Sc * Dc],如果直接套用这个公式 mSrcColor * mDestColor,这样计算出的结果会是一个很大的值,并且结果也不能通过一个int值来定义,所以不能这么直接地套用公式,这里有涉及到一些图像处理技术的知识,既然不能直接一起相乘来计算,那就分开来计算。每一个颜色值从高位到低位,依次代表 ARGB,其中A是透明度,RGB代表颜色值,在RGB模式下每一个像素点的色阶范围都是 0~255,纯黑色阶值是 0,纯白色阶值是 255 也就是16进制的FF,所以我们需要依次计算出 A R G B的值,并且要通过下面的公式来计算:

正片叠底(Multiply):C = A * B/255  (注意255换成16进制就是FF)

比如还是上面的两个颜色值进行正片叠底,先算透明度 A = 0xFF * 0xFF/255 = FF,然后R = 0x66 * 0xFF/255 = 0X66,依次类推最终得出源图像和目标图像进行正片叠底后的颜色值为 0XFF668844,也就是0XFF668844,代码里定义为了  multyColor,现在这两个结论是不是也很好理解了:任何颜色和黑色正片叠底得到的仍然是黑色,任何颜色和白色进行正片叠底得到的颜色依然是原来的颜色保持不变,其他模式的计算方法跟正片叠底也是一样的,最后来看下效果。

正片叠底的绘制区域

如果画笔通过 setXfermode()设置了一个Xfermode那么在绘制的时候,会按照你传入的Xfermode的规则,在 源图像和目标图像区域相交的区域根据对应的规则进行运算,先把相交区域清空,再把运算结果覆盖到相交的区域。如果没有设置任何Xfermode,绘制的时候会直接按顺序覆盖上去。

接下来根据对应的代码看下效果

1. 直接绘制(每个代码块下方是对应的效果图)

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawBitmap(mDestBitmap,0,0,mPaint);
        canvas.drawBitmap(mSrcBitmap,width/2,height/2,mPaint);
        
    }

2.设置正片叠底模式(注意:要离屏绘制)

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int saveId = canvas.saveLayer(0,0,width*2,height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(mDestBitmap,0,0,mPaint);
        mPaint.setXfermode(xfermode);
        canvas.drawBitmap(mSrcBitmap,width/2,height/2,mPaint);
        
        canvas.restoreToCount(saveId);
    }

 3.如果我们在离屏绘制前加一个背景色,这个背景色将不会参与正片叠底的运算

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(bg_green);//未离屏绘制不会参与正片叠底
        int saveId = canvas.saveLayer(0,0,width*2,height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(mDestBitmap,0,0,mPaint);
        mPaint.setXfermode(xfermode);
        canvas.drawBitmap(mSrcBitmap,width/2,height/2,mPaint);
        
        canvas.restoreToCount(saveId);
    }

 

如果我们要背景参与正片叠底,只需改一下位置

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

      
        //canvas.drawColor(bg_green);//未离屏绘制不会参与正片叠底
        int saveId = canvas.saveLayer(0,0,width*2,height*2,mPaint,Canvas.ALL_SAVE_FLAG);

        canvas.drawColor(bg_green);//会参与正片叠底
        canvas.drawBitmap(mDestBitmap,0,0,mPaint);
        mPaint.setXfermode(xfermode);
        canvas.drawBitmap(mSrcBitmap,width/2,height/2,mPaint);
        
        canvas.restoreToCount(saveId);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/102971.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

three.js之旋转成型(车削缓冲几何体)

文章目录简介例子解释专栏目录请点击 简介 一条曲线围绕空间中的某个轴进行旋转&#xff0c;会形成一个几何体这个在three.js中也是可以实现的&#xff0c;主要就是使用到了three.js中的LatheGeometry&#xff0c;官网 例子 <!DOCTYPE html> <html lang"en&qu…

SpringBoot2.X+Vue+UniAPP 全栈开发医疗小程序 中间件搭建

文章目录一、安装Docker环境1. 关闭SELINUX服务2. 更新yum程序3. 安装Docker4. 管理Docker二、搭建HBasePhoenix大数据平台2.1. 加载镜像2.2. 创建容器2.3. 创建逻辑库2.4. 创建数据表与导入数据2.5. 配置JDBC连接信息三、Redis服务3.1. 加载镜像3.2. 配置文件3.3. 创建Redis容…

专注性能的多端研发框架 - ice.js 3 正式发布!

ice.js 框架在之前的版本中&#xff0c;主要服务于中后台 / PC 的项目研发&#xff0c;而随着无线端以及多端能力的拓展&#xff0c;ice.js 3 将成为一套面向大淘宝技术的终端应用框架。因此在 ice.js 3 的版本中除了「开发者体验」之外&#xff0c;还围绕「用户体验」探索了大…

拥抱实体经济,可以说是当下互联网参与者的首要选择

拥抱实体经济&#xff0c;绝对是当下互联网玩家们的首要选择。无论是头部的互联网企业来讲&#xff0c;还是新生的互联网玩家而言&#xff0c;它们都不约而同地将关注的焦点聚焦在了这样一个方向上。   透过这一点&#xff0c;我们可以非常明显地感受到&#xff0c;一个全新的…

1574_AURIX_TC275_SCU中的杂项控制

全部学习汇总&#xff1a; GitHub - GreyZhang/g_TC275: happy hacking for TC275! 接下来的这一段算是SCU章节最后的一次内容梳理了&#xff0c;剩下的内容也不是很多了。杂项并不是英文的直接翻译&#xff0c;算是我自己选择的一个表达方式。 这里涉及到的功能有CCU6&#x…

基于机器学习算法与历史数据预测未来的站点关闭(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 应用背景&#xff1a; 通过分析序列进行合理预测&#xff0c;做到提前掌握未来的发展趋势&#xff0c;为业务决策提供依据&am…

2023材料科学与工程国际会议(CoMSE 2023)

2023材料科学与工程国际会议&#xff08;CoMSE 2023&#xff09; 重要信息 会议网址&#xff1a;www.icomse.org 会议时间&#xff1a;2023年3月24-26日 召开地点&#xff1a;中国上海 截稿时间&#xff1a;2023年1月25日 录用通知&#xff1a;投稿后2周内 收录检索&…

手把手教你使用Appium进行IOS真机自动化测试

讲解方式&#xff1a; 课程内容条理清晰&#xff0c;目标明确&#xff0c;由浅入深&#xff0c;环环相扣。重点部分进行额外梳理和总结&#xff0c;更易理解和吸收。 教程推荐&#xff1a;使用Appium进行IOS真机自动化测试 课程亮点&#xff1a;1&#xff0c;讲解清晰&a…

VRRP多备份组+策略路由实现主备负载

上篇文章&#xff0c;我们介绍了VRRP单备份组和策略路由之间主备切换的差异&#xff08;&#xff09;&#xff0c;整体上看&#xff0c;单备份组VRRP的主备切换速度非常快&#xff0c;用过调整配置&#xff0c;可以轻轻松松将切换时间压缩到1秒钟以内&#xff1b;但是主备之间无…

【大数据技术Spark】DStream编程操作讲解实战(图文解释 附源码)

DStream编程 批处理引擎Spark Core把输入的数据按照一定的时间片&#xff08;如1s&#xff09;分成一段一段的数据&#xff0c;每一段数据都会转换成RDD输入到Spark Core中&#xff0c;然后将DStream操作转换为RDD算子的相关操作&#xff0c;即转换操作、窗口操作以及输出操作…

和冬天有关的歌

我写过关于四季《新朝花夕拾》&#xff0c;我也写过关于夏天的歌&#xff1a;《和夏天有关的歌》&#xff0c;所以我也想写写冬天。&#xff08;1&#xff09;北风当你想到冬天的时候&#xff0c;你会想起什么&#xff1f;我会想到&#xff1a;北风、北方。正好有这么一首歌&am…

神经网络架构搜索

神经网络架构搜索 定义内涵 神经网络架构搜索是为给定数据集自动找到一个或多个架构的任务&#xff0c;这些架构将为给定 的数据集生成具有良好结果的模型&#xff0c;其本质是在高维空间的最优参数搜索问题。 技术背景 深度学习模型的使用越来越大众化&#xff0c;在很多行…

免费内网穿透工具测评对比,谁更好用 3

文章目录1. 前言2. 对比内容2.1 TCP协议功能及操作对比2.1.1 网云穿的TCP设置2.1.2 cpolar的TCP设置1.2 使用感受对比3. 结语1. 前言 发布本地网页通常是内网穿透的主要作用之一&#xff0c;但并不是唯一作用&#xff0c;内网穿透还能将本地硬件发布到公共互联网上&#xff0c…

ASP.NET企业HR人力资源管理系统源码(带使用手册和操作说明)

中小型企业HR人力资源管理系统源码带使用手册和操作说明 【程序语言】&#xff1a;.NET 【数据库】&#xff1a;SQL SERVER 2008 【运行环境】&#xff1a;WINDOWSIIS 【其他】&#xff1a;前端bootstrap框架 了解更多HR源码事宜可私信我&#xff01; 系统运行环境&#x…

微信小程序云开发博客系统源代码,让写博客像发朋友圈一样简单,含使用部署教程

博客就两种&#xff1a;一是随笔&#xff0c;记录自己的成长历程&#xff0c;二是有目的的发文&#xff0c;例如搬运各种网赚文&#xff0c;我想大部分朋友做博客的初衷都是有一块自己的心灵净土&#xff0c;于是催生了wxapp-blog这款小程序。 完整代码下载地址&#xff1a;微…

零基础如何系统地自学Python?Python应该怎么学?

这次疫情让很多人&#xff0c;都发现自己真的适合群居&#xff0c;也让部分人觉得独居是个不错的选择&#xff0c;但是大部分人都会更明白工作的重要性。 如果没有收入&#xff0c;没有朋友&#xff0c;没有同事&#xff0c;没有那些鸡毛蒜皮的小事&#xff0c;会发现&#xf…

C++ Primer 第六章 函数

C Primer 第六章 函数6.1. Function BasicsParameters and Arguments6.2. Argument Passing6.2.3. const Parameters and Arguments6.2.4. Array Parameters6.2.6. Functions with Varying Parametersinitializer_list ParametersEllipsis Parameters6.3. Return Types and the…

Java多线程-线程的创建(Thread类的基本使用)

文章目录一. 线程和Thread类1. 线程和Thread类1.1 Thread类的构造方法1.2 启用线程的相关方法2. 创建第一个Java多线程程序3. 使用Runnable对象创建线程4. 使用内部类创建线程5. 使用Lambada表达式创建线程6. 多线程并发执行简单演示7. 多线程并发执行的优势二. Thread类的属性…

【专业数据】六.2020~2022年北京交通大学【新一代电子信息技术】专业复试线/分数线差/计划招生数/复试数/录取数/复试比例/录取率

文章目录 1.专业介绍2.2020-2022年国家线/复试线/分数线差2.1.数据总览2.2.数据指标2.2.1.复试分数线2.2.2.分数线差3.2020-2022年计划招生数/复试数/录取数/复试比例/录取率3.1.数据总览3.2.数据指标3.2.1.复试比例3.2.2.录取率4.参考资料欢迎订阅本专栏:《北交计算机复试经验…

【数据结构】树和二叉树

半山腰很挤&#xff0c;你得去山顶看看 目录 1.树 1.1 树的概念 1.2 树的特征 1.3 树每个结点的关系 1.4 树的表示形式 2.二叉树 2.1 二叉树的概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储 2.5 二叉树的基本操作 2.5.1 判断二叉树是否为空 2.…