From: Tim Toohey Date: Thu, 22 Aug 2002 07:28:26 +0000 (+0000) Subject: (PHP imagecolormatch) makes a palette image match the colours in the X-Git-Tag: RELEASE_0_91~327 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=266193159bfca60a5bd65d611448d9b175ce17fe;p=php (PHP imagecolormatch) makes a palette image match the colours in the 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) --- diff --git a/ext/gd/gd.c b/ext/gd/gd.c index b37af9e633..e1187df28f 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -260,6 +260,10 @@ function_entry gd_functions[] = { #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} }; /* }}} */ @@ -336,6 +340,12 @@ PHP_MINIT_FUNCTION(gd) 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; } @@ -639,6 +649,44 @@ PHP_FUNCTION(imagetruecolortopalette) } /* }}} */ +#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) @@ -738,6 +786,28 @@ PHP_FUNCTION(imagealphablending) } /* }}} */ +#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) @@ -2975,7 +3045,7 @@ PHP_FUNCTION(imagepstext) 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 +3094,19 @@ PHP_FUNCTION(imagepstext) 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 +3131,7 @@ PHP_FUNCTION(imagepstext) _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 +3147,6 @@ PHP_FUNCTION(imagepstext) 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 --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index 364b39e6ab..454f1bf30e 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -665,26 +665,28 @@ gdImageSetPixel (gdImagePtr im, int x, int y, int color) 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; } } @@ -2574,3 +2576,78 @@ 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 --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h index 9f67185649..4c8fb90fe9 100644 --- a/ext/gd/libgd/gd.h +++ b/ext/gd/libgd/gd.h @@ -67,6 +67,11 @@ extern "C" { #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 @@ -335,6 +340,11 @@ void gdImageColorDeallocate(gdImagePtr im, int 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, diff --git a/ext/gd/libgd/gd_topal.c b/ext/gd/libgd/gd_topal.c index 06bfc4100d..33d62741d7 100644 --- a/ext/gd/libgd/gd_topal.c +++ b/ext/gd/libgd/gd_topal.c @@ -1682,3 +1682,56 @@ outOfMemory: 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; xsx; x++ ) { + for( y=0; ysy; 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; colorcolorsTotal; 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 --git a/ext/gd/php_gd.h b/ext/gd/php_gd.h index 44d91cac47..f70afb43a9 100644 --- a/ext/gd/php_gd.h +++ b/ext/gd/php_gd.h @@ -155,6 +155,11 @@ PHP_FUNCTION(jpeg2wbmp); 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