This is a patch to add nicer alpha-channel mixing to the GD module.

It introduces two new PHP functions:
ImageLayerEffect() & ImageColorMatch()

ImageLayerEffect() is similar to the existing ImageAlphaBlending() function in 
that it affects the way pixel drawing will be done during any kind of drawing 
function. It extends the functionality by allowing you to use new effect 
modes. There are four modes: replace, alpha, normal and overlay. Replace and 
alpha are like ImageAlphaBlending(FALSE) and ImageAlphaBlending(TRUE) 
respectively. "Normal" mode is like alpha but works on transparent and 
semi-transparent backgrounds. "Overlay" is like the overlay layer effect in 
graphics programs.

The ImagePSText() function can now draw on arbitrary backgrounds using the 
normal layer effect (my technique is to use a text background colour that has 
the same RGB as the text foreground but a transparent alpha.)

ImageColorMatch() is a function I've been using to tweak the 
ImageTrueColorToPalette() results. I find that the generated palette gets 
close, but doesn't quite match sometimes. This function fixes that "problem" 
(at least it works for me).

Any thoughts? Should I go ahead and commit this? I've never committed to PHP 
before so go easy on me :-). I've been using essentially the same code on a 
PHP-4.1.0 installation for a while now and I believe it works OK.

Tim

diff -aur php4/ext/gd/gd.c php4-tt/ext/gd/gd.c
--- php4/ext/gd/gd.c	Sun Jul 28 20:00:37 2002
+++ php4-tt/ext/gd/gd.c	Wed Aug 21 18:24:12 2002
@@ -260,6 +260,10 @@
 #ifdef HAVE_GD_WBMP
 	PHP_FE(image2wbmp,								NULL)
 #endif	
+#ifdef HAVE_GD_BUNDLED
+	PHP_FE(imagelayereffect,						NULL)
+	PHP_FE(imagecolormatch,							NULL)
+#endif
 	{NULL, NULL, NULL}
 };
 /* }}} */
@@ -337,6 +341,12 @@
 	REGISTER_LONG_CONSTANT("IMG_ARC_NOFILL", gdNoFill, CONST_CS | CONST_PERSISTENT);
 	REGISTER_LONG_CONSTANT("IMG_ARC_EDGED", gdEdged, CONST_CS | CONST_PERSISTENT);
 #endif
+#ifdef HAVE_GD_BUNDLED
+	REGISTER_LONG_CONSTANT("IMG_EFFECT_REPLACE", gdEffectReplace, CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("IMG_EFFECT_ALPHABLEND", gdEffectAlphaBlend, CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("IMG_EFFECT_NORMAL", gdEffectNormal, CONST_CS | CONST_PERSISTENT);
+	REGISTER_LONG_CONSTANT("IMG_EFFECT_OVERLAY", gdEffectOverlay, CONST_CS | CONST_PERSISTENT);
+#endif
 	return SUCCESS;
 }
 /* }}} */
@@ -639,6 +649,46 @@
 }
 /* }}} */
 
+/* {{{ proto void imagecolormatch(resource im1, resource im2)
+	Makes the colors of the palette version of an image more closely match the true color version */
+PHP_FUNCTION(imagecolormatch)
+{
+#if HAVE_GD_BUNDLED
+	zval **IM1, **IM2;
+	gdImagePtr im1, im2;
+	int result;
+
+	if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &IM1, &IM2 ) == FAILURE)  {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	ZEND_FETCH_RESOURCE(im1, gdImagePtr, IM1, -1, "Image", le_gd);
+	ZEND_FETCH_RESOURCE(im2, gdImagePtr, IM2, -1, "Image", le_gd);
+
+	result = gdImageColorMatch(im1, im2);
+	switch( result )
+	{
+	case -1:
+		zend_error(E_ERROR, "%s(): Image1 must be TrueColor", get_active_function_name(TSRMLS_C));
+		RETURN_FALSE;
+		break;
+	case -2:
+		zend_error(E_ERROR, "%s(): Image2 must be Palette", get_active_function_name(TSRMLS_C));
+		RETURN_FALSE;
+		break;
+	case -3:
+		zend_error(E_ERROR, "%s(): Image1 and Image2 must be the same size", get_active_function_name(TSRMLS_C));
+		RETURN_FALSE;
+		break;
+	}
+
+	RETURN_TRUE;
+#else
+	zend_error(E_ERROR, "%s(): requires GD 2.0 or later", get_active_function_name(TSRMLS_C));
+#endif
+}
+/* }}} */
+
 /* {{{ proto void imagesetthickness(resource im, int thickness)
    Set line thickness for drawing lines, ellipses, rectangles, polygons etc. */
 PHP_FUNCTION(imagesetthickness)
@@ -738,6 +788,30 @@
 }
 /* }}} */
 
+/* {{{ proto void imagelayereffect(resource im, bool on)
+   Set the alpha blending flag to use the PHP libgd layering effects */
+PHP_FUNCTION(imagelayereffect)
+{
+#ifdef HAVE_GD_BUNDLED
+	zval **IM, **effect;
+	gdImagePtr im;
+
+	if (ZEND_NUM_ARGS() != 2 ||	zend_get_parameters_ex(2, &IM, &effect) == FAILURE) {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	ZEND_FETCH_RESOURCE(im, gdImagePtr, IM, -1, "Image", le_gd);
+	convert_to_long_ex(effect);
+
+	gdImageAlphaBlending(im, Z_LVAL_PP(effect) );
+
+	RETURN_TRUE;
+#else
+	zend_error(E_ERROR, "%s(): requires PHP libgd extensions", get_active_function_name(TSRMLS_C));
+#endif
+}
+/* }}} */
+
 /* {{{ proto int imagecolorresolvealpha(resource im, int red, int green, int blue, int alpha)
    Resolve/Allocate a colour with an alpha level.  Works for true colour and palette based images */
 PHP_FUNCTION(imagecolorresolvealpha)
@@ -2975,7 +3049,7 @@
 	int space;
 	int *f_ind;
 	int h_lines, v_lines, c_ind;
-	int rd, gr, bl, fg_rd, fg_gr, fg_bl, bg_rd, bg_gr, bg_bl, _fg, _bg;
+	int rd, gr, bl, al, fg_rd, fg_gr, fg_bl, fg_al, bg_rd, bg_gr, bg_bl, bg_al, _fg, _bg;
 	int aa[16], aa_steps;
 	int width, amount_kern, add_width;
 	double angle, extend;
@@ -3024,16 +3098,19 @@
 	fg_rd = gdImageRed  (bg_img, _fg);
 	fg_gr = gdImageGreen(bg_img, _fg);
 	fg_bl = gdImageBlue (bg_img, _fg);
+	fg_al = gdImageAlpha(bg_img, _fg);
 
 	bg_rd = gdImageRed  (bg_img, _bg);
 	bg_gr = gdImageGreen(bg_img, _bg);
 	bg_bl = gdImageBlue (bg_img, _bg);
+	bg_al = gdImageAlpha(bg_img, _bg);
 
 	for (i = 0; i < aa_steps; i++) {
 		rd = bg_rd+(double)(fg_rd-bg_rd)/aa_steps*(i+1);
 		gr = bg_gr+(double)(fg_gr-bg_gr)/aa_steps*(i+1);
 		bl = bg_bl+(double)(fg_bl-bg_bl)/aa_steps*(i+1);
-		aa[i] = gdImageColorResolve(bg_img, rd, gr, bl);
+		al = bg_al+(double)(fg_al-bg_al)/aa_steps*(i+1);
+		aa[i] = gdImageColorResolveAlpha(bg_img, rd, gr, bl, al);
 	}
 
 	T1_AASetBitsPerPixel(8);
@@ -3058,7 +3135,7 @@
 
 	_str = Z_STRVAL_PP(str);
 
-	if (width) {
+	{
 		extend = T1_GetExtend(*f_ind);
 		str_path = T1_GetCharOutline(*f_ind, _str[0], Z_LVAL_PP(sz), transform);
 
@@ -3074,8 +3151,6 @@
 			str_path = T1_ConcatOutlines(str_path, char_path);
 		}
 		str_img = T1_AAFillOutline(str_path, 0);
-	} else {
-		str_img = T1_AASetString(*f_ind, _str,  Z_STRLEN_PP(str), space, T1_KERNING, Z_LVAL_PP(sz), transform);
 	}
 
 	if (T1_errno) {
diff -aur php4/ext/gd/libgd/gd.c php4-tt/ext/gd/libgd/gd.c
--- php4/ext/gd/libgd/gd.c	Tue Aug 13 05:09:26 2002
+++ php4-tt/ext/gd/libgd/gd.c	Wed Aug 21 18:24:12 2002
@@ -665,26 +665,28 @@
       gdImageTileApply (im, x, y);
       break;
     default:
-      if (gdImageBoundsSafe (im, x, y))
-	{
-	  if (im->trueColor)
-	    {
-	      if (im->alphaBlendingFlag)
-		{
-		  im->tpixels[y][x] =
-		    gdAlphaBlend (im->tpixels[y][x],
-				  color);
-		}
-	      else
-		{
-		  im->tpixels[y][x] = color;
+      if (gdImageBoundsSafe (im, x, y)) {
+		if (im->trueColor) {
+			switch( im->alphaBlendingFlag )
+			{
+			default:
+			case gdEffectReplace :
+				im->tpixels[y][x] = color;
+				break;
+			case gdEffectAlphaBlend :
+				im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color);
+				break;
+			case gdEffectNormal :
+				im->tpixels[y][x] = gdFullAlphaBlend(im->tpixels[y][x], color);
+				break;
+			case gdEffectOverlay :
+				im->tpixels[y][x] = gdLayerOverlay(im->tpixels[y][x], color);
+				break;
+			}
+		} else {
+			im->pixels[y][x] = color;
 		}
-	    }
-	  else
-	    {
-	      im->pixels[y][x] = color;
-	    }
-	}
+	  }
       break;
     }
 }
@@ -2573,4 +2575,87 @@
 gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
 {
   im->saveAlphaFlag = saveAlphaArg;
+}
+
+int
+gdFullAlphaBlend (int dst, int src)
+{
+	int a1, a2;
+	a1 = gdAlphaTransparent - gdTrueColorGetAlpha(src);
+	a2 = gdAlphaTransparent - gdTrueColorGetAlpha(dst);
+
+	return ( ((gdAlphaTransparent - ((a1+a2)-(a1*a2/gdAlphaMax))) << 24) +
+		(gdAlphaBlendColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), a1, a2 ) << 16) +
+		(gdAlphaBlendColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), a1, a2 ) << 8) +
+		(gdAlphaBlendColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), a1, a2 ))
+		);
+}
+
+int
+gdAlphaBlendColor( int b1, int b2, int a1, int a2 )
+{
+	int c;
+	int w;
+
+	/* deal with special cases */
+
+	if( (gdAlphaMax == a1) || (0 == a2) )
+	{
+		/* the back pixel can't be seen */
+		return b1;
+	}
+	else if( (0 == a1)  )
+	{
+		/* the front pixel can't be seen */
+		return b2;
+	}
+	else if( gdAlphaMax == a2)
+	{
+		/* the back pixel is opaque */
+		return ( a1 * b1 + ( gdAlphaMax - a1 ) * b2 ) / gdAlphaMax;
+	}
+
+	/* the general case */
+	w = ( a1 * ( gdAlphaMax - a2 ) / ( gdAlphaMax - a1 * a2 / gdAlphaMax ) * b1 + \
+	 	  a2 * ( gdAlphaMax - a1 ) / ( gdAlphaMax - a1 * a2 / gdAlphaMax ) * b2 ) / gdAlphaMax;
+	c = (a2 * b2  +  ( gdAlphaMax - a2 ) * w ) / gdAlphaMax;
+	return ( a1 * b1 + ( gdAlphaMax - a1 ) * c ) / gdAlphaMax;
+}
+
+int
+gdLayerOverlay (int dst, int src)
+{
+	int a1, a2;
+	a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
+	a2 = gdAlphaMax - gdTrueColorGetAlpha(src);
+	return ( ((gdAlphaMax - a1*a2/gdAlphaMax) << 24) +
+		(gdAlphaOverlayColor( gdTrueColorGetRed(src), gdTrueColorGetRed(dst), gdRedMax ) << 16) +
+		(gdAlphaOverlayColor( gdTrueColorGetGreen(src), gdTrueColorGetGreen(dst), gdGreenMax ) << 8) +
+		(gdAlphaOverlayColor( gdTrueColorGetBlue(src), gdTrueColorGetBlue(dst), gdBlueMax ))
+		);
+}
+
+int
+gdAlphaOverlayColor( int src, int dst, int max )
+{
+	/* this function implements the algorithm
+	 * 
+	 * for dst[rgb] < 0.5,
+	 *   c[rgb] = 2.src[rgb].dst[rgb]
+	 * and for dst[rgb] > 0.5,
+	 *   c[rgb] = -2.src[rgb].dst[rgb] + 2.dst[rgb] + 2.src[rgb] - 1
+	 *   
+	 */
+
+	dst = dst << 1;
+	if( dst > max )
+	{
+		/* in the "light" zone */
+		return dst + (src << 1) - (dst * src / max) - max;
+	}
+	else 
+	{
+		/* in the "dark" zone */
+		return dst * src / max;
+	}
 }
diff -aur php4/ext/gd/libgd/gd.h php4-tt/ext/gd/libgd/gd.h
--- php4/ext/gd/libgd/gd.h	Sat Jun 15 00:48:33 2002
+++ php4-tt/ext/gd/libgd/gd.h	Wed Aug 21 18:24:12 2002
@@ -67,6 +67,11 @@
 #define gdTrueColorGetRed(c) (((c) & 0xFF0000) >> 16)
 #define gdTrueColorGetGreen(c) (((c) & 0x00FF00) >> 8)
 #define gdTrueColorGetBlue(c) ((c) & 0x0000FF)
+#define gdEffectReplace 0
+#define gdEffectAlphaBlend 1
+#define gdEffectNormal 2
+#define gdEffectOverlay 3
+
 
 /* This function accepts truecolor pixel values only. The 
 	source color is composited with the destination color
@@ -334,6 +339,11 @@
         it can be negative) and the quality loss is ugly. */
 
 void gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted);
+
+/* An attempt at getting the results of gdImageTrueColorToPalette
+	to look a bit more like the original (im1 is the original
+	and im2 is the palette version */
+int gdImageColorMatch(gdImagePtr im1, gdImagePtr im2);
 
 /* Specifies a color index (if a palette image) or an
 	RGB color (if a truecolor image) which should be
diff -aur php4/ext/gd/libgd/gd_topal.c php4-tt/ext/gd/libgd/gd_topal.c
--- php4/ext/gd/libgd/gd_topal.c	Mon Aug 12 00:05:30 2002
+++ php4-tt/ext/gd/libgd/gd_topal.c	Wed Aug 21 18:24:12 2002
@@ -1682,3 +1682,65 @@
       gdFree (cquantize);
     }
 }
+
+/* bring the palette colors in im2 to be closer to im1
+ *
+ */
+int
+gdImageColorMatch (gdImagePtr im1, gdImagePtr im2)
+{
+	unsigned long *buf; /* stores our calculations */
+	unsigned long *bp; /* buf ptr */
+	int color, rgb;
+	int x,y;
+	int count;
+
+	if( !im1->trueColor )
+	{
+		return -1; /* im1 must be True Color */
+	}
+	if( im2->trueColor )
+	{
+		return -2; /* im2 must be indexed */
+	}
+	if( (im1->sx != im2->sx) || (im1->sy != im2->sy) )
+	{
+		return -3; /* the images are meant to be the same dimensions */
+	}
+
+	buf = (unsigned long *)malloc( sizeof(unsigned long) * 5 * im2->colorsTotal );
+	memset( buf, 0, sizeof(unsigned long) * 5 * im2->colorsTotal );
+
+	for( x=0; x<im1->sx; x++ )
+	{
+		for( y=0; y<im1->sy; y++ )
+		{
+			color = im2->pixels[y][x];
+			rgb = im1->tpixels[y][x];
+			bp = buf + (color * 5);
+			(*(bp++))++;
+			*(bp++) += gdTrueColorGetRed(rgb);
+			*(bp++) += gdTrueColorGetGreen(rgb);
+			*(bp++) += gdTrueColorGetBlue(rgb);
+			*(bp++) += gdTrueColorGetAlpha(rgb);
+		}
+	}
+	bp = buf;
+	for( color=0; color<im2->colorsTotal; color++ )
+	{
+		count = *(bp++);
+		if( count > 0 )
+		{
+			im2->red[color]		= *(bp++) / count;
+			im2->green[color]	= *(bp++) / count;
+			im2->blue[color]	= *(bp++) / count;
+			im2->alpha[color]	= *(bp++) / count;
+		}
+		else
+		{
+			bp += 4;
+		}
+	}
+	free(buf);
+	return 0;
+}
diff -aur php4/ext/gd/php_gd.h php4-tt/ext/gd/php_gd.h
--- php4/ext/gd/php_gd.h	Fri Jun 28 22:35:30 2002
+++ php4-tt/ext/gd/php_gd.h	Wed Aug 21 18:24:12 2002
@@ -155,6 +155,9 @@
 PHP_FUNCTION(png2wbmp);
 PHP_FUNCTION(image2wbmp);
 
+PHP_FUNCTION(imagelayereffect);
+PHP_FUNCTION(imagecolormatch);
+
 PHP_GD_API int phpi_get_le_gd(void);
 
 #else

-- 
PHP Development Mailing List <http://www.php.net/>
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to