在画布上绘图时,PorterDuff 的源和目标指的是什么?

Jaa*_*ver 4 android android-canvas xamarin

我整晚都在试图解决这个问题,但在 Google 上找到的答案与有关 Android 画布的非常具体的问题有关,我还没有找到关于这个主题的 101 条解释。甚至 Android 文档也使用位图而不是绘制形状。

具体问题:

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

  • 代码 -

        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
    
    Run Code Online (Sandbox Code Playgroud)
  • 源出——

这反而绘制了 XOR 应该绘制的内容。

  • 目的地 -

这按预期工作。

  • 来源在 -

这绘制了顶部应有的源。

  • 目的地在 -

这绘制了目的地应该是什么。

更一般的问题:

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

编辑:这基本上是我所追求的效果,其中“明星”是一条动态路径。根据重叠着色三种不同的颜色。

粗制图

编辑 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;
}
Run Code Online (Sandbox Code Playgroud)

Yor*_*hen 7

在画布上绘图时,PorterDuff 的源和目标指的是什么?

经过一些深入的学习,我写了一些演示来深入解释这一点。为了帮助您了解什么是源和目标所指。

首先,看下面的代码

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);
} 
Run Code Online (Sandbox Code Playgroud)

我重写了这个OnDraw方法,设置了一个绿色背景,然后绘制了一个黄色圆圈和一个蓝色矩形,效果:

在此处输入图片说明

以上是我们画a时的正常流程Canvas,我没用过PorterDuffXfermode,我们来分析一下它的流程:

  • 首先,我们调用canvas.DrawARGB(255, 139, 197, 186)方法Canvas用单一颜色绘制整体,这个画布中的每个像素都有相同的ARGB值:(255, 139, 197, 186)。由于 alpha 值ARGB是 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

第二,我使用的一种模式XfermodePorterDuff.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
  }
Run Code Online (Sandbox Code Playgroud)

影响 :

在此处输入图片说明

我们来分析一下它的过程:

  • 首先,我们调用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 方法时,它会检查绘制是否有Xfermode模式。如果不是,则图形将直接覆盖Canvas在同一位置的像素。否则,它将Canvas根据Xfermode模式。

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

的值ARGB的源像素和的值的ARGB目标像素的根据由定义的规则计算的Xfermode,它会计算最终的ARGB值。然后ARGB用最终ARGB值更新目标像素的值。

在我的示例中,Xfermodeis PorterDuff.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);
}
Run Code Online (Sandbox Code Playgroud)

当使用不同时Xfermode,它们的效果:

XorSrcOutScreenLightenDarkenAdd

如您所见,您可以使用不同的颜色和不同的颜色Xfermode来实现您的效果。

  • 杰出的。太感谢了。“然后Android会在内存中绘制一个矩形,这个矩形中的像素有一个名称:Source。内存中的矩形在Canvas中有一个对应的矩形,对应的矩形称为:destination。” 这正是我所追求的。我认为目的地是整个画布。 (3认同)