android - 在 Canvas 上绘图时,PorterDuff 源和目标指的是什么?

标签 android xamarin android-canvas

我整晚都在试图弄清楚这一点,但在谷歌上找到的答案与关于 Android Canvas 的非常具体的问题有关,我还没有找到关于这个主题的任何 101 解释。甚至 Android 文档也使用位图而不是绘制形状。

具体问题:

我需要在 Canvas 上绘制一个椭圆和一条路径。并根据 documentation 颜色源输出一种颜色,输出​​另一种颜色和重叠区域,无论是源输入还是目标输入,第三种颜色。我正在尝试在屏幕外 Canvas 中完成所有这些工作。但是上面的一些步骤会出现问题,并且在尝试以任何方式组合它们时会变得更糟。

  • 代码 -
        Bitmap bmp = Bitmap.CreateBitmap (720, 720, Bitmap.Config.Argb8888);
        Canvas c = new Canvas (bmp);
    
        Paint paint = new Paint ();
        paint.SetARGB (255, 255, 0, 0);
        c.DrawOval (200, 200, 520, 520, paint); //assumed destination
    
        paint.SetARGB (255, 0, 0, 255);
        paint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.*)); //replace mode here
        paint.SetStyle (Paint.Style.Fill);
    
        Path path = new Path ();
        path.MoveTo (c.Width / 2f, c.Height / 2f);
    
        foreach (var m in measurements) {
            //calculations
    
            float x = xCalculatedValue
            float y = yCalculatedValue
    
            path.LineTo (x, y);
        }
    
        path.LineTo (c.Width / 2f, c.Height / 2f);
    
        c.DrawPath (path, paint); //assumed source
    
  • 源出 -

  • 这反而绘制了 XOR 应该绘制的内容。
  • 目的地输出 -

  • 这按预期工作。
  • 来源 -

  • 这绘制了顶部应该的源。
  • 目的地在 -

  • 这画出了目的地应该是什么。

    更一般的问题:

    在这种情况下,源和目标指的是什么?直观地说,我会假设目标是 Canvas 位图的当前状态,源是由 canvas.Draw* 和 Paint PortedDuff.Mode 添加的矩阵。但情况似乎并非如此。

    编辑: 这基本上是我所追求的效果,其中“星”是动态路径。上色取决于重叠的三种不同颜色。

    Crude drawing

    编辑 2: York Shen 在回答实际问题方面做得很好。但是对于任何想要获得类似效果的人来说,这里是最终代码。
    Bitmap DrawGraphBitmapOffscreen ()
    {
        Bitmap bmp = Bitmap.CreateBitmap (720, 720, Bitmap.Config.Argb8888);
        Canvas c = new Canvas (bmp);
    
        // Replace with calculated path
        Path path = new Path ();
        path.MoveTo (c.Width / 2f, c.Height / 2f);
        path.LineTo (263, 288);
        path.LineTo (236, 202);
        path.LineTo (312, 249);
        path.LineTo (331, 162);
        path.LineTo (374, 240);
        path.LineTo (434, 174);
        path.LineTo (431, 263);
        path.LineTo (517, 236);
        path.LineTo (470, 312);
        path.LineTo (557, 331);
        path.LineTo (479, 374);
        path.LineTo (545, 434);
        path.LineTo (456, 431);
        path.LineTo (483, 517);
        path.LineTo (407, 470);
        path.LineTo (388, 557);
        path.LineTo (345, 479);
        path.LineTo (285, 545);
        path.LineTo (288, 456);
        path.LineTo (202, 483);
        path.LineTo (249, 407);
        path.LineTo (162, 388);
        path.LineTo (240, 345);
        path.LineTo (174, 285);
        path.LineTo (263, 288);
        path.Close ();
    
        Paint paint = new Paint ();
    
        paint.SetARGB (255, 255, 0, 0);
        paint.SetStyle (Paint.Style.Fill);
    
        c.DrawPath (path, paint);
    
        paint.SetARGB (255, 0, 0, 255);
        paint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.SrcIn));
    
        c.DrawOval (200, 200, 520, 520, paint);
    
        paint.SetARGB (255, 255, 255, 255);
        paint.SetXfermode (new PorterDuffXfermode (PorterDuff.Mode.DstOver));
    
        c.DrawOval (200, 200, 520, 520, paint);
    
        return bmp;
    }
    

    最佳答案

    What do PorterDuff source and destination refer to when drawing on canvas?



    经过一些深入的研究,我写了几个demo来深入解释一下。为了帮助您了解源和目标指的是什么。

    首先看下面的代码 :
    protected override void OnDraw(Canvas canvas)
    {
        base.OnDraw(canvas);
    
        Paint paint = new Paint();
    
        //Set the background color
        canvas.DrawARGB(255, 139, 197, 186);
    
        int canvasWidth = canvas.Width;
        int r = canvasWidth / 3;
    
        //Draw a yellow circle
        paint.Color = Color.Yellow;
        canvas.DrawCircle(r, r, r, paint);
    
        //Draw a blue rectangle
        paint.Color = Color.Blue;
        canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint);
    } 
    

    我覆盖了 OnDraw方法,设置一个绿色背景,然后画一个黄色圆圈和一个蓝色矩形,效果:

    enter image description here

    以上是我们绘制 Canvas 时的正常程序。 ,我没有使用任何PorterDuffXfermode ,我们来分析一下它的过程:
  • 首先,我们调用canvas.DrawARGB(255, 139, 197, 186)方法绘制整体Canvas使用单一颜色时,此 Canvas 中的每个像素都具有相同的 ARGB值(value):(255, 139, 197, 186) .由于 ARGB 中的 alpha 值是 255 而不是 0,所以每个像素都是不透明的。
  • 二、当我们执行canvas.DrawCircle(r, r, r, paint)方法,Android会在你定义的位置画一个黄色圆圈。 ARGB值为(255,139,197,186)的所有像素在这个圆圈中将替换为黄色像素。
    黄色像素为源,ARGB 值为(255,139,197,186) 的像素。是目的地。 我稍后会解释。
  • 三、执行canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint)后方法,Android会绘制一个蓝色矩形,这个矩形中的所有像素都是蓝色的,这些蓝色像素会替换相同位置的其他像素。所以蓝色矩形可以在Canvas上绘制.

  • 二、我使用的模式是Xfermode , PorterDuff.Mode.Clear :
     protected override void OnDraw(Canvas canvas)
        {
            base.OnDraw(canvas);
    
            Paint paint = new Paint();
    
            //Set the background color
            canvas.DrawARGB(255, 139, 197, 186);
    
            int canvasWidth = canvas.Width;
            int r = canvasWidth / 3;
    
            //Draw a yellow circle
            paint.Color = Color.Yellow;
            canvas.DrawCircle(r, r, r, paint);
    
            //Use Clear as PorterDuffXfermode to draw a blue rectangle
            paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear));
    
            paint.Color = Color.Blue;
            canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint);
    
            paint.SetXfermode(null);
            this.SetLayerType(LayerType.Software, null);
    
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //I found that PorterDuff.Mode.Clear doesn't work with hardware acceleration, so you have add this code
      }
    

    影响 :

    enter image description here

    我们来分析一下它的过程:
  • 首先,我们调用canvas.DrawARGB(255, 139, 197, 186)绘制整体的方法Canvas作为单一颜色,每个像素都是不透明的。
  • 二、我们调用canvas.DrawCircle(r, r, r, paint)画黄色的方法
    圈进Canvas .
  • 三、执行paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear)) ,设置油漆PorterDuff型号为Clear .
  • 四、调用canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint)绘制一个蓝色矩形,最后它显示一个白色矩形。

  • 为什么它显示一个白色矩形 ?通常,当我们调用 canvas.DrawXXX()方法我们将通过 Paint参数,Android执行draw方法时会检查paint是否有Xfermode模式。如果没有,则图形将直接覆盖Canvas 中的像素。在同一个位置。否则,它将更新 Canvas 中的像素。根据Xfermode模式。

    在我的例子中,当执行 canvas.DrawCirlce()方法,Paint没有 Xfermode模型,所以黄色圆圈直接覆盖了Canvas中的像素.但是当我们调用 canvas.DrawRect()绘制一个矩形,Paint有一个 Xfermode值(value) PorterDuff.Mode.Clear . 然后Android会在内存中绘制一个矩形,这个矩形中的像素有一个名字:Source . 内存中的矩形在Canvas中有对应的矩形,对应的矩形称为:
    目的地 。

    ARGB 的值源像素的值和 ARGB 的值根据 Xfermode 定义的规则计算目标像素的,它将计算最终的 ARGB 值。然后更新 ARGB最终 ARGB 的目标像素的值值(value)。

    在我的示例中,XfermodePorterDuff.Mode.Clear ,它需要目标像素 ARGB变成 (0,0,0,0) ,这意味着它是透明的。所以我们使用canvas.DrawRect()方法在 Canvas 中绘制一个透明矩形, 自 Activity本身具有白色背景颜色,因此它将显示一个白色矩形。

    编辑:

    为了实现您在图片中发布的功能,我编写了一个演示:
    protected override void OnDraw(Canvas canvas)
    {
        base.OnDraw(canvas);
    
        Paint paint = new Paint();
        paint.SetARGB(255, 255, 0, 0);
        RectF oval2 = new RectF(60, 100, 300, 200);
        canvas.DrawOval(oval2, paint);
    
        paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.*));
    
        Path path = new Path();
        paint.SetStyle(Paint.Style.Fill);
        paint.SetARGB(255, 0, 0, 255);
    
        path.MoveTo(180, 50);
        path.LineTo(95, 240);
        path.LineTo(255, 240);
        path.Close();
    
        this.SetLayerType(LayerType.Software, null);
        canvas.DrawPath(path, paint);
        paint.SetXfermode(null);
    }
    

    当使用不同的Xfermode ,它们的作用:

    Xor , SrcOut , Screen , Lighten , Darken , Add .

    如您所见,您可以使用不同的颜色和不同的Xfermode达到你的效果。

    关于android - 在 Canvas 上绘图时,PorterDuff 源和目标指的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46227332/

    相关文章:

    java - 用特定时间JAVA初始化计时器

    java - 显示菜单但未调用 onCreateOptionsMenu()

    android - 不使用数据包检测蓝牙 SPP 断开连接

    c# - 使用 Place API 在 xamarin android 中填充附近地点的 map

    java - java android 中的 AndroidPlot

    Android Paint setShadowLayer() 忽略其 Paint 的颜色

    android - 从 JSON 分页文件 Android 在 AsyncTask 中加载第 2、3、4 页

    Xamarin iOS - 缺少所需的文件图标错误 ITMS-90022 和 ITMS-90023

    android - 如何在android的Activity中设置单独的颜色?

    android - Wowza 示例链接在 android 中不起作用