No alpha blending in the FillXXX methods

May 23, 2011 at 11:06 PM

First of all, thanks for the great library!

Just wondering if you plan to support alpha blending in the FillXXX methods?

Cheers,
Andrew 

Coordinator
May 24, 2011 at 7:51 AM

This was left out for efficiency reasons. It might make sense to add this as a separate overload to the methods. Feel free to open a feature at the Issue Tracker.

Contributions are also always welcome. ;)

 

- Rene Schulte

May 24, 2011 at 2:39 PM

Thanks!

I don't think this task calls for separate overloads, analyzing the alpha component once per method will not degrade the performance. I'll implement it if I have time today :)

May 24, 2011 at 6:11 PM

Here's the alpha blending-aware implementation of the FillRectangle method:

public static void FillRectangle2(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
{
   // Use refs for faster access (really important!) speeds up a lot!
   int w = bmp.PixelWidth;
   int h = bmp.PixelHeight;
   int[] pixels = bmp.Pixels;

   // Check boundaries
   if (x1 < 0) { x1 = 0; }
   if (y1 < 0) { y1 = 0; }
   if (x2 < 0) { x2 = 0; }
   if (y2 < 0) { y2 = 0; }
   if (x1 >= w) { x1 = w - 1; }
   if (y1 >= h) { y1 = h - 1; }
   if (x2 >= w) { x2 = w - 1; }
   if (y2 >= h) { y2 = h - 1; }

   unchecked
   {
      for (int y = y1; y <= y2; y++)
      {
         for (int i = y * w + x1; i <= y * w + x2; i++)
         {
            byte oneOverAlpha = (byte)(255 - color.A);
            int c = pixels[i];

            int r = ((byte)(c >> 16) * oneOverAlpha + color.R * color.A) >> 8;
            int g = ((byte)(c >> 8) * oneOverAlpha + color.G * color.A) >> 8;
            int b = ((byte)(c >> 0) * oneOverAlpha + color.B * color.A) >> 8;

            pixels[i] = 255 << 24 | r << 16 | g << 8 | b;
         }
      }
   }
}

Apr 6, 2014 at 12:39 AM
Edited Apr 6, 2014 at 12:46 AM
For VB.net, with modification:
   Public Shared Sub FillRectangle2(ByRef bmp As WriteableBitmap, x1 As Integer, y1 As Integer, x2 As Integer, y2 As Integer, color As Color)
        ' Use refs for faster access (really important!) speeds up a lot!
        Dim w As Integer = bmp.PixelWidth
        Dim h As Integer = bmp.PixelHeight
        Dim pixels As Integer() = bmp.Pixels

        ' Check boundaries
        If x1 < 0 Then
            x1 = 0
        End If
        If y1 < 0 Then
            y1 = 0
        End If
        If x2 < 0 Then
            x2 = 0
        End If
        If y2 < 0 Then
            y2 = 0
        End If
        If x1 >= w Then
            x1 = w - 1
        End If
        If y1 >= h Then
            y1 = h - 1
        End If
        If x2 >= w Then
            x2 = w - 1
        End If
        If y2 >= h Then
            y2 = h - 1
        End If

        For y As Integer = y1 To y2
            For i As Integer = y * w + x1 To y * w + x2
                Dim oneOverAlpha As Byte = CByte(255 - color.A)
                Dim c As Integer = pixels(i)

                Dim r As Integer = ((((c >> 16) And 255) * oneOverAlpha) + (color.R * color.A)) >> 8
                Dim g As Integer = ((((c >> 8) And 255) * oneOverAlpha) + (color.G * color.A)) >> 8
                Dim b As Integer = ((((c >> 0) And 255) * oneOverAlpha) + (color.B * color.A)) >> 8

                pixels(i) = 255 << 24 Or r << 16 Or g << 8 Or b
            Next
        Next

    End Sub
May 8, 2014 at 12:26 PM
Edited May 9, 2014 at 10:09 AM
It works!
when this function will be commited on the repository?


This is for DeBlend!
  public static void FillRectangleDeBlend(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
  {
      using (var context = bmp.GetBitmapContext())
      {
          // Use refs for faster access (really important!) speeds up a lot!
          int w = bmp.PixelWidth;
          int h = bmp.PixelHeight;
          var pixels = context.Pixels;

          // Check boundaries
          if (x1 < 0) { x1 = 0; }
          if (y1 < 0) { y1 = 0; }
          if (x2 < 0) { x2 = 0; }
          if (y2 < 0) { y2 = 0; }
          if (x1 >= w) { x1 = w - 1; }
          if (y1 >= h) { y1 = h - 1; }
          if (x2 >= w) { x2 = w - 1; }
          if (y2 >= h) { y2 = h - 1; }


          unchecked
          {
              for (int y = y1; y <= y2; y++)
              {
                  for (int i = y * w + x1; i < y * w + x2; i++)
                  {
                      byte oneOverAlpha = (byte)(255 - color.A);
                      int c = pixels[i];

                      int r = (((byte)(c >> 16) << 8) / oneOverAlpha);
                      int g = (((byte)(c >> 8) << 8) / oneOverAlpha);
                      int b = (((byte)(c >> 0) << 8) / oneOverAlpha);

                      pixels[i] = 255 << 24 | r << 16 | g << 8 | b;


                  }
              }
          }

      }
  }
May 8, 2014 at 1:24 PM
It works, but it is not 100% precise, in my opinion, since the alpha channel should work on 256 values, not 255.
Suppose you choose a 128 value for your alpha component. It should mean that you'd like to compose evenly your color with what already there, but this algorithm will have it providing 127/255 part of the color against 128/255 of the other. Then you assign the complementary alpha each cycle of the loop, not a big issue, but optimizable.

I changed all fill extensions this way:

As soon as possible:
            int alpha = 1 + (int)((uint)color >> 24);
            if (alpha == 1) return;
Then, before the cycle:
            int alphac = 256 - alpha, p, pal, par, pag, pab;
            if (alphac > 0)
            {
                color &= 0xffffff;
                par = alpha * ((color >> 16) & 255);
                pag = alpha * ((color >> 8) & 255);
                pab = alpha * (color & 255);
                color = ((par >> 8) << 16)
                    | ((pag >> 8) << 8)
                    | (pab >> 8);
            }
In the cycle:
                                p = pixels[offset];
                                pal = 1 + (p >> 24);
                                par = alphac * pal * ((p >> 16) & 255);
                                pag = alphac * pal * ((p >> 8) & 255);
                                pab = alphac * pal * (p & 255);
                                pixels[offset] = (Math.Min(255, alpha + pal - 1) << 24)
                                    | (par & (255 * 256 * 256))
                                    | (pag & (255 * 256 * 256)) >> 8
                                    | (pab >> 16) + color;
If I'm right, the compiler will optimize the 255256256 to its equivalent, but some people find it more understandable.
May 9, 2014 at 9:55 AM
Edited May 9, 2014 at 10:09 AM
I have tested your code but not blend correctly,
  public static void FillRectangleBlend2(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color colore)
  {
      var color = ConvertColor(colore);
      using (var context = bmp.GetBitmapContext())
      {
          // Use refs for faster access (really important!) speeds up a lot!
          var w = context.Width;
          var h = context.Height;
          var pixels = context.Pixels;

          // Check boundaries
          if ((x1 < 0 && x2 < 0) || (y1 < 0 && y2 < 0)
           || (x1 >= w && x2 >= w) || (y1 >= h && y2 >= h))
          {
              return;
          }

          // Clamp boundaries
          if (x1 < 0) { x1 = 0; }
          if (y1 < 0) { y1 = 0; }
          if (x2 < 0) { x2 = 0; }
          if (y2 < 0) { y2 = 0; }
          if (x1 >= w) { x1 = w - 1; }
          if (y1 >= h) { y1 = h - 1; }
          if (x2 >= w) { x2 = w - 1; }
          if (y2 >= h) { y2 = h - 1; }

          int alpha = 1 + (int)((uint)color >> 24);
          if (alpha == 1) return;

          int alphac = 256 - alpha, p, pal, par, pag, pab;
          if (alphac > 0)
          {
              color &= 0xffffff;
              par = alpha * ((color >> 16) & 255);
              pag = alpha * ((color >> 8) & 255);
              pab = alpha * (color & 255);
              color = ((par >> 8) << 16)
                  | ((pag >> 8) << 8)
                  | (pab >> 8);
          }

          unchecked
          {
              for (int y = y1; y <= y2; y++)
              {
                  for (int i = y * w + x1; i < y * w + x2; i++)
                  {
                      p = pixels[i];
                      pal = 1 + (p >> 24);
                      par = alphac * pal * ((p >> 16) & 255);
                      pag = alphac * pal * ((p >> 8) & 255);
                      pab = alphac * pal * (p & 255);
                      pixels[i] = (Math.Min(255, alpha + pal - 1) << 24)
                          | (par & (255 * 256 * 256))
                          | (pag & (255 * 256 * 256)) >> 8
                          | (pab >> 16) + color;

                  }
              }
          }

      }
  }