[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/
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);
}
}
}
- Alpha compositing in gnustep-back,
Jeff Teunissen <=