代码之家  ›  专栏  ›  技术社区  ›  JaanTohver

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

  •  4
  • JaanTohver  · 技术社区  · 7 年前

    我整晚都在试图解决这个问题,但在谷歌上找到的答案涉及到关于安卓画布的非常具体的问题,我还没有找到任何关于这个主题的101个解释。即使Android文档也使用位图而不是绘制形状。

    具体问题:

    我需要在画布上画一个椭圆和一条路径。根据 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应该绘制的内容。

    这正如预期的那样有效。

    这绘制了源代码应该绘制的内容。

    这就引出了目的地应该是什么。

    在本文中,源和目标指的是什么?直观地说,我假设目标是画布位图的当前状态,源是画布添加的矩阵。绘制*并绘制PortedDuff。模式但事实似乎并非如此。

    这基本上就是我想要的效果,其中“星”是一条动态路径。根据重叠程度给三种不同的颜色上色。

    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;
    }
    
    1 回复  |  直到 7 年前
        1
  •  10
  •   York Shen    6 年前

    经过一些深入的研究,我写了几个演示来深入解释这一点。为了帮助您理解什么是源和目标,请参阅。

    首先,看下面的代码 :

    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) 帆布 使用单一颜色时,画布中的每个像素都具有相同的颜色 ARGB 价值: (255, 139, 197, 186) 是255而不是0,因此每个像素都是不透明的。

    • canvas.DrawCircle(r, r, r, paint) (255,139,197,186) 在此圆中,将替换为黄色像素。 (255,139,197,186) 是目的地。 我稍后会解释。

    • 第三,在执行 canvas.DrawRect(r, r, r * 2.7f, r * 2.7f, paint) 方法,Android将绘制一个蓝色的矩形,该矩形中的所有像素都是蓝色的,这些蓝色像素将替换相同位置的其他像素。因此可以绘制蓝色矩形 帆布 .

    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

    • 帆布图纸argb(255、139、197、186) 绘制整体的方法 帆布

    • 第二,我们打电话 帆布DrawCircle(r,r,r,paint) 帆布 .

    • paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.Clear)) ,设置油漆 PorterDuff 模型到 Clear .

    • 第四,呼叫 绘制一个蓝色矩形,最后显示一个白色矩形。

    为什么显示白色矩形 canvas.DrawXXX() 方法我们将通过一个 Paint 参数,当Android执行draw方法时,它将检查绘制是否具有 Xfermode 模式如果没有,那么图形将直接覆盖 帆布 帆布 根据 模式

    在我的示例中,当执行 canvas.DrawCirlce() 没有一个 模型,因此黄色圆圈直接覆盖 帆布 canvas.DrawRect() 要绘制矩形, 油漆 Xfermode PorterDuff。模式清楚的 然后Android将在内存中绘制一个矩形,该矩形中的像素有一个名称:Source . 内存中的矩形在 ,相应的矩形称为: 目的地

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

    在我的示例中 ,它需要目标像素 变成 (0,0,0,0) ,这意味着它是透明的。所以我们使用 帆布 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