bug-gnustep
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Alpha compositing in gnustep-back


From: Jeff Teunissen
Subject: Alpha compositing in gnustep-back
Date: Fri, 12 Jul 2002 04:43:39 -0400

XGBitmap's _pixmap_combine_alpha() and _bitmap_combine_alpha() functions
don't quite work right. Specifically, the colors are all wrong.

For some reason, the colors seem to be scaled by the value of alpha
(127/127/127/127 becomes 63/63/63/127 somewhere). In my spare time, I've
been working on them to make them generate correct output.

I don't know if where I've worked is the right place to fix the problem,
but at least now the output is correct. To illustrate, I've attached an
image:

* The first column shows a composition I created using the GIMP, and is as
close to mathematically correct as it can be.

* The second column shows how the CVS version of the backend renders the
image (in Gorm). The white is obviously wrong, but it turns out any pixel
with alpha is not quite right.

* The third column shows how the modified backend renders the image. It's
*very* close to the image from the GIMP, but not perfect because the code
has to perform some multiplication on the colors.

As part of trying to understand the functions, I partially rewrote the
fast paths for them. The code is shorter, but it may be slower since the
math is done in floating point.

-- 
| Jeff Teunissen  -=-  Pres., Dusk To Dawn Computing  -=-  deek @ d2dc.net
| GPG: 1024D/9840105A   7102 808A 7733 C2F3 097B  161B 9222 DAB8 9840 105A
| Core developer, The QuakeForge Project        http://www.quakeforge.net/
| Specializing in Debian GNU/Linux              http://www.d2dc.net/~deek/

PNG image

Index: back/Source/xlib/XGBitmap.m
===================================================================
RCS file: /cvsroot/gnustep/gnustep/core/back/Source/xlib/XGBitmap.m,v
retrieving revision 1.2
diff -u -w -r1.2 XGBitmap.m
--- back/Source/xlib/XGBitmap.m 10 May 2002 15:06:17 -0000      1.2
+++ back/Source/xlib/XGBitmap.m 12 Jul 2002 07:46:19 -0000
@@ -54,26 +54,26 @@
 
 #define InitRGBShiftsAndMasks(rs,rw,gs,gw,bs,bw,as,aw) \
      do { \
-        _rshift = rs;              \
-        _rmask  = (1<<rw) -1;      \
-        _rwidth = rw;              \
-        _gshift = gs;              \
-       _gmask  = (1<<gw) -1;      \
-        _gwidth = gw;              \
-       _bshift = bs;              \
-       _bmask  = (1<<bw) -1;      \
-        _bwidth = bw;              \
-        _amask  = (1<<aw) -1;      \
-        _ashift = as;              \
-        _awidth = aw;              \
+       _rshift = (rs);              \
+       _rmask  = (1<<(rw)) -1;      \
+       _rwidth = (rw);              \
+       _gshift = (gs);              \
+       _gmask  = (1<<(gw)) -1;      \
+       _gwidth = (gw);              \
+       _bshift = (bs);              \
+       _bmask  = (1<<(bw)) -1;      \
+       _bwidth = (bw);              \
+       _amask  = (1<<(aw)) -1;      \
+       _ashift = (as);              \
+       _awidth = (aw);              \
        } while(0)
 
 
 #define PixelToRGB(pixel,r,g,b)  \
      do { \
-        r = (pixel >> _rshift) & _rmask; \
-        g = (pixel >> _gshift) & _gmask; \
-        b = (pixel >> _bshift) & _bmask; \
+       (r) = (pixel >> _rshift) & _rmask; \
+       (g) = (pixel >> _gshift) & _gmask; \
+       (b) = (pixel >> _bshift) & _bmask; \
         } while(0)
 
 /* Note that RGBToPixel assumes that the
@@ -88,6 +88,10 @@
                |((b) << _bshift); \
         } while(0)
 
+#define CLAMP(a) \
+    do { \
+       (a) = MAX(0, MIN(255, (a))); \
+    } while (0)
 
 /* Composite source image (pixmap) onto a destination image with alpha.
    Only works for op=Sover now
@@ -107,14 +111,9 @@
                      float fraction)
 {
   unsigned long  pixel;
-  unsigned short oldAlpha = 0;
   unsigned long  oldPixel = 0;
-  unsigned short oldAr = 0;
-  unsigned short oldAg = 0;
-  unsigned short oldAb = 0;
-  unsigned char         oldBr = 0;
-  unsigned char         oldBg = 0;
-  unsigned char         oldBb = 0;
+
+  fraction = MAX(0.0, MIN(1.0, fraction));
 
   if (drawMechanism == XGDM_FAST15
       || drawMechanism == XGDM_FAST16
@@ -144,68 +143,91 @@
          //which picture goes wrong.
          InitRGBShiftsAndMasks(11,5,5,6,0,5,0,8);
        }
+
       for (row = 0; row < srect.height; row++)
        {
          unsigned      col;
 
          for (col = 0; col < srect.width; col++)
            {
-             unsigned r, g, b, alpha;
-             pixel = XGetPixel(source_im->image, 
-                               col+srect.x, row+srect.y);
-
-             PixelToRGB(pixel,r,g,b);
+             unsigned  sr, sg, sb, sa; // source
+             unsigned  dr, dg, db, da; // dest
+             double    alpha, ialpha;
+
+             // Get the source pixel information
+             pixel = XGetPixel (source_im->image, srect.x+col, srect.y+row);
+             PixelToRGB (pixel, sr, sg, sb);
 
-             pixel = 255;
              if (source_alpha)
+               {
                pixel = XGetPixel(source_alpha->image,
-                                 col+srect.x, row+srect.y);
-
-             if (fraction < 1)
-               pixel *= fraction;
-             alpha = (pixel >> _ashift) & _amask;
+                                   srect.x+col, srect.y+row);
+                 sa = (pixel >> _ashift) & _amask;
+               }
+             else
+               sa = _amask;
 
-             if (alpha == 0)
-               continue;               // background unchanged.
-             if (alpha != _amask)
-               { // We really have something to mix!
-                 unsigned short        ialpha = _amask - alpha;
+             if (sa == 0)      // dest wouldn't be changed
+               continue;
+#if 1
                  /*
-                  * Get the background pixel and convert to RGB.
+              * I don't know why, but colors are scaled by alpha. This
+              * shouldn't be happening, but it is!
+              * We have to do this before the fraction is applied.
                   */
-                 pixel = XGetPixel(dest_im->image, col, row);
-                 if (pixel != oldPixel)
+             if (sa < _amask)
                    {
-                     oldPixel = pixel;
-                     PixelToRGB(pixel, oldBr,oldBg,oldBb);
-                     oldAlpha = 0;
-                   }
-                 if (alpha != oldAlpha)
-                   {
-                     oldAlpha = alpha;
-                     oldAr = ialpha * oldBr;
-                     oldAg = ialpha * oldBg;
-                     oldAb = ialpha * oldBb;
-                   }
+                 double  multiplier = (double) _amask / sa;
                      
-                 // mix in alpha to produce RGB out
-                 r = (oldAr + (r*alpha))/_amask;
-                 g = (oldAg + (g*alpha))/_amask;
-                 b = (oldAb + (b*alpha))/_amask;
+                 sr *= multiplier;
+                 sg *= multiplier;
+                 sb *= multiplier;
                }
-             RGBToPixel(r,g,b,pixel);
-             XPutPixel(dest_im->image, col, row, pixel);
+#endif
+             if (fraction < 1.0)
+               sa *= fraction;
+
+             alpha = ((double) sa) / _amask;
+
+             // Now get dest pixel
+             pixel = XGetPixel (dest_im->image, col, row);
+             PixelToRGB (pixel, dr, dg, db);
+
              if (dest_alpha)
                {
-                 unsigned short dalpha;
-                 unsigned long  dpixel;
-                 /* Alpha gets mixed the same as all the
-                    other color components */
-                 dpixel = XGetPixel(dest_alpha->image, col, row);
-                 dalpha = (dpixel >> _ashift) & _amask;
-                 dalpha = alpha + dalpha * (_amask - alpha)/_amask;
-                 XPutPixel(dest_alpha->image, col, row, dalpha << _ashift );
+                 pixel = XGetPixel(dest_alpha->image, col, row);
+                 da = (pixel >> _ashift) & _amask;
                }
+             else  // no alpha channel, background is opaque
+               da = _amask;
+#if 0
+             printf ("D = {%03d %03d %03d %03d}\t",
+                           dr,  dg,  db,  da);
+#endif
+             ialpha = (1.0 - alpha);
+             dr = (sr * alpha) + (dr * ialpha);
+             dg = (sg * alpha) + (dg * ialpha);
+             db = (sb * alpha) + (db * ialpha);
+
+             // calc final alpha
+             if (sa == _amask || da == _amask)
+               da = _amask;
+             else
+               da = sa + (da * ialpha);
+#if 0
+             printf ("S = {%03d %03d %03d %03d}\tR = {%03d %03d %03d %03d}\n",
+                           sr,  sg,  sb,  sa,     dr,  dg,  db,  da);
+#endif
+
+             CLAMP(dr);
+             CLAMP(dg);
+             CLAMP(db);
+             CLAMP(da);
+
+             RGBToPixel(dr, dg, db, pixel);
+             XPutPixel(dest_im->image, col, row, pixel);
+             if (dest_alpha)
+               XPutPixel(dest_alpha->image, col, row, da << _ashift);
            }
        }
     }
@@ -222,7 +244,6 @@
       pixel = (unsigned long)-1;       // Never valid?
       c2.pixel = pixel;
 
-
        for (row = 0; row < srect.height; row++)
        {
          unsigned      col;
@@ -858,14 +879,6 @@
 
   {
     unsigned long      pixel;
-    unsigned short     oldAlpha = 0;
-    unsigned long      oldPixel = 0;
-    unsigned short     oldAr = 0;
-    unsigned short     oldAg = 0;
-    unsigned short     oldAb = 0;
-    unsigned char      oldBr = 0;
-    unsigned char      oldBg = 0;
-    unsigned char      oldBb = 0;
 
     /* Two cases, the *_FAST* method, which
        is covered in the first a part of the if
@@ -915,74 +928,56 @@
        
            for (col = 0; col < drect.width; col++)
              {
-               unsigned short r = *rptr++;
-               unsigned short g = *gptr++;
-               unsigned short b = *bptr++;
-               unsigned short alpha = *aptr++;;
+               unsigned short  sr = (*rptr++ >> (8 - _rwidth));
+               unsigned short  sg = (*gptr++ >> (8 - _gwidth));
+               unsigned short  sb = (*bptr++ >> (8 - _bwidth));
+               unsigned short  sa = (*aptr++ >> (8 - _awidth));
+               unsigned        dr, dg, db, da;
+               double          alpha = (double) sa / ((1 << _rwidth) - 1);
 
-               /*
-                * Convert 8-bit components down to the 5-bit values
-                * that the display system can actually handle.
-                */
-               r >>= (8 - _rwidth);
-               g >>= (8 - _gwidth);
-               b >>= (8 - _bwidth);
+               if (sa == 0)    // dest wouldn't be changed
+                 continue;
+
+               // get the destination pixel
+               pixel = XGetPixel(dest_im->image, col, row);
+               PixelToRGB(pixel, dr, dg, db);
 
-               if (has_alpha)
-                 {
                    if (dest_alpha)
                      {
-                       unsigned short dalpha;
-                       unsigned long  dpixel;
-                       /* Alpha gets mixed the same as all the
-                          other color components */
-                       dpixel = XGetPixel(dest_alpha->image, col, row);
-                       dalpha = (dpixel >> _ashift) & _amask;
-                       dalpha = alpha + dalpha * (_amask - alpha)/_amask;
-                       XPutPixel(dest_alpha->image, col, row, 
-                                 dalpha << _ashift);
+                   pixel = XGetPixel(dest_alpha->image, col, row);
+                   da = (pixel >> _ashift) & _amask;
                      }
-                   if (alpha == 0)
-                     continue;         // background unchanged.
+               else  // no alpha channel, background is opaque
+                 da = _amask;
                
-                   if (alpha != _amask)
+               if (sa == _amask || da == 0)  // source only
                      {
-                       unsigned short  ialpha = _amask - alpha;
-                       /*
-                        * Get the background pixel and convert to RGB.
-                        */
-                       pixel = XGetPixel(dest_im->image, col, row);
-                       if (pixel != oldPixel)
-                         {
-                           oldPixel = pixel;
-                           PixelToRGB(pixel,oldBr,oldBg,oldBb);
-                           oldAlpha = 0;
-                         }
-                       if (alpha != oldAlpha)
-                         {
-                           oldAlpha = alpha;
-                           oldAr = ialpha * oldBr;
-                           oldAg = ialpha * oldBg;
-                           oldAb = ialpha * oldBb;
-                         }
-                       
-                       // mix in alpha to produce RGB out
-                       r = (oldAr + (r * alpha)) / _amask;
-                       g = (oldAg + (g * alpha)) / _amask;
-                       b = (oldAb + (b * alpha)) / _amask;
-                     }
+                   dr = sr;
+                   dg = sg;
+                   db = sb;
+                   da = sa;
                  }
                else
                  {
-                   /* Not using alpha, but we still have to set it
-                      in the pixmap */
-                   if (dest_alpha)
-                     XPutPixel(dest_alpha->image, col, row, 
-                               _amask << _ashift);
+                   double ialpha = (1.0 - alpha);
+                   dr = (sr * alpha) + (dr * ialpha);
+                   dg = (sg * alpha) + (dg * ialpha);
+                   db = (sb * alpha) + (db * ialpha);
+                   if (da == _amask || da == _amask)
+                     da = _amask;
+                   else
+                     da = sa + (da * ialpha);
                  }
 
-               RGBToPixel(r,g,b,pixel);
+               CLAMP(dr);
+               CLAMP(dg);
+               CLAMP(db);
+               CLAMP(da);
+
+               RGBToPixel(dr, dg, db, pixel);
                XPutPixel(dest_im->image, col, row, pixel);
+               if (dest_alpha)
+                 XPutPixel(dest_alpha->image, col, row, da << _ashift);
              }
          }
       }

reply via email to

[Prev in Thread] Current Thread [Next in Thread]