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

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

问题描述:

我一直想弄明白这一点,但在Google上找到的答案涉及Android的画布的非常具体的问题,我还没有找到关于这个主题的任何101解释。即使Android文档使用位图,而不是绘制形状。在画布上绘画时,PorterDuff源和目标是指什么?

具体问题:

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

  • 码 -

    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.Draw *和Paint PortedDuff.Mode添加的矩阵。但似乎并非如此。

编辑:这基本上是我后面的效果,其中“星”是一个动态路径。根据重叠颜色三种不同的颜色。

Crude drawing

编辑2:约克沉做得很好回答的问题实际。但对于任何想获得类似效果的人来说,这是最终的代码。

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; 
} 
+0

你想要什么效果,在画布上画一个椭圆形和一条路径?为什么你使用'PorterDuffXfermode'?如果你只需要绘制一个椭圆形和一条路径,就不需要使用'PorterDuffX'。 –

+0

我添加了一张图片来解释我想要做的事情。我不确定alpha合成是最好的解决方案,但它是唯一想到的。这个问题不仅仅是一个实际的解决方案,我想了解在画布上一个接一个地绘制事物的上下文中引用的源和目的地。 – JaanTohver

+0

所以你想绘制一个椭圆形和多边形,然后使用'PorterDuffXfermode'来实现你所发布图片中的效果? –

什么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); 
} 

我重写OnDraw方法,设置一个绿色的背景,再画一个黄色圆圈和一个蓝色的矩形,效果:

enter image description here

以上是我们在绘制Canvas时的正常程序,我没有使用任何PorterDuffXfermode,我们来分析一下它的过程:

  • 首先,我们调用canvas.DrawARGB(255, 139, 197, 186)方法用单一颜色绘制整个Canvas,在这个画布的每个像素具有相同的ARGB值:(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执行绘制方法时,它会检查绘图是否有Xfermode模式。如果不是,则图形将直接覆盖在相同位置处的Canvas中的像素。否则,将根据Xfermode模式更新Canvas中的像素。

在我的例子,当执行canvas.DrawCirlce()方法,Paint并未有Xfermode模式,让黄圈直接覆盖像素Canvas。但是当我们呼叫canvas.DrawRect()绘制一个矩形时,Paint的值为XfermodePorterDuff.Mode.Clear然后Android会在内存中绘制一个矩形,这个矩形中的像素有一个名字:Source内存中的矩形在Canvas中有一个对应的矩形,对应的矩形称为: 目标。

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

在我的例子中,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,它们的效果:

XorSrcOutScreen,Lighten, Darken,Add

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

+0

@JaanTohver,你解决了你的问题吗? –

+0

辉煌。非常感谢。 “然后Android会在内存中绘制一个矩形,矩形中的像素有一个名字:Source。内存中的矩形在Canvas中有一个对应的矩形,相应的矩形称为:destination。”这正是我所追求的。我认为目的地是整个画布。 – JaanTohver