PorterDuffXferMode不正确的真正原因PorterDuffXferMode深入试验)

2023-11-28 04:10

本文主要是介绍PorterDuffXferMode不正确的真正原因PorterDuffXferMode深入试验),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

菜鸡wing遇敌PorterDuffXferMode,不料过于轻敌,应战吃力。随后与其大战三天三夜,三百余回合不分胜负。幸得 @咪咪控 相助,侥幸获胜。

关键字:PorterDuffXferMode  错误 不正确  不达到预期  bug


上一篇带来一个使用PorterDuffXferMode  做的 水波纹loadingview,中间遇到了点小困难。


(说人话)  PorterDuffXferMode总是不能按照效果图预期的效果执行。关于PorterDuffXferMode的错误显示是一个对初学者十分深的坑,到底是bug呢,还是有需要注意的地方呢。这里就跟随我 带上手电筒,去一探究竟。


转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50534175


首先,大家都知道有一个图片:


然后,大部分时候 是看到了觉得很神奇,就跃跃欲试,尤其是src_in  和dstIn可以实现遮罩效果,例如圆角图片 圆形图片都用了这种模式。

于是就挨个测试各种模式是否生效,结果往往不能达到预期效果。我们来做个测试。

从最简单的开始:

1.直接在canvas上面绘制图形

  @Overrideprotected void onDraw(Canvas canvas) {//dstcanvas.drawRect(20,20,80,80,mDstPaint);//srccanvas.drawCircle(30,30,30,mSrcPaint);}

原图效果是这样的:


现在加一个mode上来,XOR

    @Overrideprotected void onDraw(Canvas canvas) {//dstcanvas.drawRect(20,20,80,80,mDstPaint);mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));//srccanvas.drawCircle(30,30,30,mSrcPaint);}
跑起来的结果是这样的:

WTF!!?? 这是什么鬼。不应该是相交部分消失吗。 网上说“硬件加速”对这个有影响,那么在构造器里关闭硬件加速试一下:

 public TestView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mDstPaint = new Paint();mSrcPaint = new Paint();mDstPaint.setColor(Color.YELLOW);mSrcPaint.setColor(Color.BLUE);setLayerType(View.LAYER_TYPE_SOFTWARE, null);}

运行的结果:

这下正常了。相交的部分消失了。

结论1:硬件加速对PorterDuffXferMode有影响,使用前请关闭硬件加速。

那么这下真的天下太平了吗?nonono~不要太天真,不然怎么能叫万丈深渊呢。

继续试验其他模式:  将模式改为SRC_IN


WTF?????跟效果图根本不一致好吗!!!! 在试试DST_IN


你确定你没有在逗我????  怎么是这个鬼东西。  (当时鼓捣了我三天四夜,一直在日狗,不过先别急,慢慢来。)

为什么一定要按照那个效果图来呢。。。 因为特么的那个图是官方的一个demo。。 那么我们就来看看这个demo长什么样!

package io.appium.android.apis.graphics;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Xfermode;
import android.os.Bundle;
import android.view.View;public class Xfermodes extends GraphicsActivity {// create a bitmap with a circle, used for the "dst" imagestatic Bitmap makeDst(int w, int h) {Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(bm);Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);p.setColor(0xFFFFCC44);c.drawOval(new RectF(0, 0, w*3/4, h*3/4), p);return bm;}// create a bitmap with a rect, used for the "src" imagestatic Bitmap makeSrc(int w, int h) {Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);Canvas c = new Canvas(bm);Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);p.setColor(0xFF66AAFF);c.drawRect(w/3, h/3, w*19/20, h*19/20, p);return bm;}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(new SampleView(this));}private static class SampleView extends View {private static final int W = 64;private static final int H = 64;private static final int ROW_MAX = 4;   // number of samples per rowprivate Bitmap mSrcB;private Bitmap mDstB;private Shader mBG;     // background checker-board patternprivate static final Xfermode[] sModes = {new PorterDuffXfermode(PorterDuff.Mode.CLEAR),new PorterDuffXfermode(PorterDuff.Mode.SRC),new PorterDuffXfermode(PorterDuff.Mode.DST),new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),new PorterDuffXfermode(PorterDuff.Mode.DST_IN),new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),new PorterDuffXfermode(PorterDuff.Mode.XOR),new PorterDuffXfermode(PorterDuff.Mode.DARKEN),new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),new PorterDuffXfermode(PorterDuff.Mode.SCREEN)};private static final String[] sLabels = {"Clear", "Src", "Dst", "SrcOver","DstOver", "SrcIn", "DstIn", "SrcOut","DstOut", "SrcATop", "DstATop", "Xor","Darken", "Lighten", "Multiply", "Screen"};public SampleView(Context context) {super(context);mSrcB = makeSrc(W, H);mDstB = makeDst(W, H);// make a ckeckerboard patternBitmap bm = Bitmap.createBitmap(new int[] { 0xFFFFFFFF, 0xFFCCCCCC,0xFFCCCCCC, 0xFFFFFFFF }, 2, 2,Bitmap.Config.RGB_565);mBG = new BitmapShader(bm,Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);Matrix m = new Matrix();m.setScale(6, 6);mBG.setLocalMatrix(m);}@Override protected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE);Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);labelP.setTextAlign(Paint.Align.CENTER);Paint paint = new Paint();paint.setFilterBitmap(false);canvas.translate(15, 35);int x = 0;int y = 0;for (int i = 0; i < sModes.length; i++) {// draw the borderpaint.setStyle(Paint.Style.STROKE);paint.setShader(null);canvas.drawRect(x - 0.5f, y - 0.5f,x + W + 0.5f, y + H + 0.5f, paint);// draw the checker-board patternpaint.setStyle(Paint.Style.FILL);paint.setShader(mBG);canvas.drawRect(x, y, x + W, y + H, paint);// draw the src/dst example into our offscreen bitmapint sc = canvas.saveLayer(x, y, x + W, y + H, null,Canvas.MATRIX_SAVE_FLAG |Canvas.CLIP_SAVE_FLAG |Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |Canvas.FULL_COLOR_LAYER_SAVE_FLAG |Canvas.CLIP_TO_LAYER_SAVE_FLAG);canvas.translate(x, y);canvas.drawBitmap(mDstB, 0, 0, paint);paint.setXfermode(sModes[i]);canvas.drawBitmap(mSrcB, 0, 0, paint);paint.setXfermode(null);canvas.restoreToCount(sc);// draw the labelcanvas.drawText(sLabels[i],x + W/2, y - labelP.getTextSize()/2, labelP);x += W + 10;// wrap around when we've drawn enough for one rowif ((i % ROW_MAX) == ROW_MAX - 1) {x = 0;y += H + 30;}}}}
}


一点一点看,截取onDraw里面的片段,这里

canvas.drawBitmap(mDstB, 0, 0, paint);paint.setXfermode(sModes[i]);canvas.drawBitmap(mSrcB, 0, 0, paint);paint.setXfermode(null);
他是画了两个bitmap。网上有人说好像只对bitmap生效,那到底是不是这样呢。我们来试验一下。


我们新定义一个canvas  再定义一个bitmap   现在bitmap上画出来src  然后将bitmap画到canvas上:

 public TestView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);mDstPaint = new Paint();mSrcPaint = new Paint();mDstPaint.setColor(Color.YELLOW);mSrcPaint.setColor(Color.BLUE);setLayerType(View.LAYER_TYPE_SOFTWARE, null);mSrcBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);mCanvas = new Canvas(mSrcBitmap);}

 @Overrideprotected void onDraw(Canvas canvas) {//dstcanvas.drawRect(20,20,80,80,mDstPaint);//src
//        mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));mCanvas.drawCircle(25,25,25,mSrcPaint);canvas.drawBitmap(mSrcBitmap,0,0,null);}
现在的效果是这样的:



加一个XOR 试试。。

日了狗了!!!!!没反应啊,到底是什么鬼。

是不是两个都需要bitmap才可以呢,再创建一个dstBitmap和dstCanvas?

        mDstBitmap =  Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888);mDstCanvas = new Canvas(mDstBitmap);

加一个XOR 试试

 @Overrideprotected void onDraw(Canvas canvas) {//dstmDstCanvas.drawRect(20,20,80,80,mDstPaint);canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);//srcmSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));mSrcCanvas.drawCircle(25,25,25,mSrcPaint);canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);}
效果如下:


终于他妈的出来了!!!!那其他效果呢?

clear


一致的!!!!好激动有没有!!!!搞了4天 越来越接近结论了!!!


结论2:只有两个bitmap的时候,才可以生效。

不要高兴太早。。如果坑到这里就完了,那还叫坑么。

继续试。。嗯 好多模式都是一致的。

直到!!!SRC_IN和DST_IN ,会发现。。。都消失了。 为毛呢??

检查代码  发现 在往bitmap画圆之前就set了mode  这样会有影响

纠正

 @Overrideprotected void onDraw(Canvas canvas) {//dstmDstCanvas.drawRect(20,20,80,80,mDstPaint);canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);//srcmSrcCanvas.drawCircle(25,25,25,mSrcPaint);//再画圆之后 设置modemSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);}

发现


艹!!!!!!终于好了!!!!!!!!经过不懈努力!!!撒花!*★,°*:.☆\( ̄▽ ̄)/$:*.°★* 。 





其实我们刚才bitmap的大小是一样的。 然后都是从0,0开始 完全覆盖了。

那么错开一点点 是什么效果呢,调整代码如下

protected void onDraw(Canvas canvas) {//dstmDstCanvas.drawRect(20,20,80,80,mDstPaint);canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);//srcmSrcCanvas.drawCircle(25,25,25,mSrcPaint);mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);}



效果如下:



可以看有效果了!!!! 但是是一个什么鬼!!!  矩形缺角??加蓝色一点??


这样看是很难看出效果的。。来调整一下bitmap的底色 和矩形的大小:

把两个bitmap的底色都画成灰色, 矩形不要占满画布 留出空间

mDstCanvas.drawColor(Color.GRAY);

  @Overrideprotected void onDraw(Canvas canvas) {//dst  黄色mDstCanvas.drawRect(0,0,40,40,mDstPaint);canvas.drawBitmap(mDstBitmap,20,20,mDstPaint);//src   蓝色mSrcCanvas.drawCircle(25,25,25,mSrcPaint);canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);}


效果如下。。  嗯 这样就是两个bitmap了。。很明了  bitmap的内容 位置,

然后再搞SRC_IN模式

dst_in

 

那么把bitmap底色去掉。 改成透明的呢?

dst_in:


SRC_IN:




总结3:两个bitmap位置不完全重叠的效果如上,并不能总结出效果,要按实际效果来。


---------------------------------------------------------------------------------------------------------华丽丽的分割线-----------------------------------------------------------------------------------------

最终大总结,如果想让PorterDuffXferMode按照预期Demo(或者效果图)的效果图像实现,必须满足以下条件:


1、关闭硬件加速。

2、两个bitmap大小尽量一样。

3、背景色为透明色。

4、如果两个bitmap位置不完全一样,可能也是预期效果,只不过你看到的效果和你自己脑补的预期效果不一致。


最后想再说几句。鼓捣这个模式鼓捣了几乎一周,每天晚上下班都在搞。查了无数资料。但是好多不完整,甚至有一些误导性。所以为了避免后来者入坑。亲自试验,尽量总结。 如果有说的不正确的地方请及时向我提出。我会及时改正。


如果本文帮助到了你,请点一个顶,或者评论一下,蟹蟹!!!!


这篇关于PorterDuffXferMode不正确的真正原因PorterDuffXferMode深入试验)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/429031

相关文章

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

SpringSecurity显示用户账号已被锁定的原因及解决方案

《SpringSecurity显示用户账号已被锁定的原因及解决方案》SpringSecurity中用户账号被锁定问题源于UserDetails接口方法返回值错误,解决方案是修正isAccountNon... 目录SpringSecurity显示用户账号已被锁定的解决方案1.问题出现前的工作2.问题出现原因各

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

IDEA中Maven Dependencies出现红色波浪线的原因及解决方法

《IDEA中MavenDependencies出现红色波浪线的原因及解决方法》在使用IntelliJIDEA开发Java项目时,尤其是基于Maven的项目,您可能会遇到MavenDependenci... 目录一、问题概述二、解决步骤2.1 检查 Maven 配置2.2 更新 Maven 项目2.3 清理本

Java空指针异常NullPointerException的原因与解决方案

《Java空指针异常NullPointerException的原因与解决方案》在Java开发中,NullPointerException(空指针异常)是最常见的运行时异常之一,通常发生在程序尝试访问或... 目录一、空指针异常产生的原因1. 变量未初始化2. 对象引用被显式置为null3. 方法返回null