true-color version.
(PHP imagelayereffect) extended alpha-channel mixing effects for the
bundled GD library
@Added ImageColorMatch() and ImageLayerEffect() functions
@which work with the bundled GD library (ttoohey)
#ifdef HAVE_GD_WBMP
PHP_FE(image2wbmp, NULL)
#endif
+#if HAVE_GD_BUNDLED
+ PHP_FE(imagelayereffect, NULL)
+ PHP_FE(imagecolormatch, NULL)
+#endif
{NULL, NULL, NULL}
};
/* }}} */
REGISTER_LONG_CONSTANT("IMG_ARC_CHORD", gdChord, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_ARC_NOFILL", gdNoFill, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMG_ARC_EDGED", gdEdged, CONST_CS | CONST_PERSISTENT);
+#endif
+#if 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;
}
}
/* }}} */
+#if HAVE_GD_BUNDLED
+/* {{{ 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)
+{
+ 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:
+ php_error_docref(NULL, E_ERROR, "Image1 must be TrueColor" );
+ RETURN_FALSE;
+ break;
+ case -2:
+ php_error_docref(NULL, E_ERROR, "Image2 must be Palette" );
+ RETURN_FALSE;
+ break;
+ case -3:
+ php_error_docref(NULL, E_ERROR, "Image1 and Image2 must be the same size" );
+ RETURN_FALSE;
+ break;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+#endif
+
/* {{{ proto void imagesetthickness(resource im, int thickness)
Set line thickness for drawing lines, ellipses, rectangles, polygons etc. */
PHP_FUNCTION(imagesetthickness)
}
/* }}} */
+#if HAVE_GD_BUNDLED
+/* {{{ proto void imagelayereffect(resource im, int effect)
+ Set the alpha blending flag to use the bundled libgd layering effects */
+PHP_FUNCTION(imagelayereffect)
+{
+ 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;
+}
+/* }}} */
+#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)
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;
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);
_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);
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) {
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;
}
}
{
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;
+ }
+}
#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
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
considered 100% transparent. FOR TRUECOLOR IMAGES,
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;
+}
PHP_FUNCTION(png2wbmp);
PHP_FUNCTION(image2wbmp);
+#if HAVE_GD_BUNDLED
+PHP_FUNCTION(imagelayereffect);
+PHP_FUNCTION(imagecolormatch);
+#endif
+
PHP_GD_API int phpi_get_le_gd(void);
#else