diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | generic/tkImgPhoto.c | 267 |
2 files changed, 257 insertions, 16 deletions
@@ -1,3 +1,9 @@ +2003-10-30 Jeff Hobbs <jeffh@ActiveState.com> + + * generic/tkImgPhoto.c (ImgPhotoBlendComplexAlpha): add alpha + blending for images with partial transparency. Only operates for + 15bpp+ display. [Bug 809157] + 2003-10-29 Donal K. Fellows <donal.k.fellows@man.ac.uk> * unix/tkUnixRFont.c: Some cleaning up to get the file more in diff --git a/generic/tkImgPhoto.c b/generic/tkImgPhoto.c index 56a26cf..1176cf9 100644 --- a/generic/tkImgPhoto.c +++ b/generic/tkImgPhoto.c @@ -8,6 +8,7 @@ * Copyright (c) 1994 The Australian National University. * Copyright (c) 1994-1997 Sun Microsystems, Inc. * Copyright (c) 2002-2003 Donal K. Fellows + * Copyright (c) 2003 ActiveState Corporation. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. @@ -16,7 +17,7 @@ * Department of Computer Science, * Australian National University. * - * RCS: @(#) $Id: tkImgPhoto.c,v 1.40 2003/08/15 10:54:59 dkf Exp $ + * RCS: @(#) $Id: tkImgPhoto.c,v 1.41 2003/10/31 02:05:21 hobbs Exp $ */ #include "tkInt.h" @@ -178,10 +179,13 @@ typedef struct PhotoMaster { * components. * IMAGE_CHANGED: 1 means that the instances of this image * need to be redithered. + * COMPLEX_ALPHA: 1 means that the instances of this image + * have alpha values that aren't 0 or 255. */ #define COLOR_IMAGE 1 #define IMAGE_CHANGED 2 +#define COMPLEX_ALPHA 4 /* * The following data structure represents all of the instances of @@ -389,6 +393,11 @@ static int ImgPhotoConfigureMaster _ANSI_ARGS_(( int objc, Tcl_Obj *CONST objv[], int flags)); static void ImgPhotoConfigureInstance _ANSI_ARGS_(( PhotoInstance *instancePtr)); +static int ToggleComplexAlphaIfNeeded _ANSI_ARGS_(( + PhotoMaster *mPtr)); +static void ImgPhotoBlendComplexAlpha _ANSI_ARGS_(( + XImage *bgImg, PhotoInstance *iPtr, + int xOffset, int yOffset, int width, int height)); static int ImgPhotoSetSize _ANSI_ARGS_((PhotoMaster *masterPtr, int width, int height)); static void ImgPhotoInstanceSetSize _ANSI_ARGS_(( @@ -2113,6 +2122,9 @@ ImgPhotoConfigureMaster(interp, masterPtr, objc, objv, flags) if (oldFormat != NULL) { Tcl_DecrRefCount(oldFormat); } + + ToggleComplexAlphaIfNeeded(masterPtr); + return TCL_OK; errorExit: @@ -2265,7 +2277,6 @@ ImgPhotoConfigureInstance(instancePtr) validBox.width, validBox.height); } } - } /* @@ -2465,6 +2476,200 @@ ImgPhotoGet(tkwin, masterData) /* *---------------------------------------------------------------------- * + * ToggleComplexAlphaIfNeeded -- + * + * This procedure is called when an image is modified to + * check if any partially transparent pixels exist, which + * requires blending instead of straight copy. + * + * Results: + * None. + * + * Side effects: + * (Re)sets COMPLEX_ALPHA flag of master. + * + *---------------------------------------------------------------------- + */ + +static int +ToggleComplexAlphaIfNeeded(PhotoMaster *mPtr) +{ + size_t len = MAX(mPtr->userWidth, mPtr->width) * + MAX(mPtr->userHeight, mPtr->height) * 4; + unsigned char *c = mPtr->pix32; + unsigned char *end = c + len; + + /* + * Set the COMPLEX_ALPHA flag if we have an image with partially + * transparent bits. + */ + mPtr->flags &= ~COMPLEX_ALPHA; + c += 3; /* start at first alpha byte */ + for (; c < end; c += 4) { + if (*c && *c != 255) { + mPtr->flags |= COMPLEX_ALPHA; + break; + } + } + return (mPtr->flags & COMPLEX_ALPHA); +} + +/* + *---------------------------------------------------------------------- + * + * ImgPhotoBlendComplexAlpha -- + * + * This procedure is called when an image with partially + * transparent pixels must be drawn over another image. + * + * Results: + * None. + * + * Side effects: + * Background image passed in gets drawn over with image data. + * + *---------------------------------------------------------------------- + */ +/* + * This should work on all platforms that set mask and shift data properly + * from the visualInfo. + * RGB is really only a 24+ bpp version whereas RGB15 is the correct version + * and works for 15bpp+, but it slower, so it's only used for 15bpp+. + */ +#ifndef __WIN32__ +#define GetRValue(rgb) (UCHAR((rgb & red_mask) >> red_shift)) +#define GetGValue(rgb) (UCHAR((rgb & green_mask) >> green_shift)) +#define GetBValue(rgb) (UCHAR((rgb & blue_mask) >> blue_shift)) +#define RGB(r,g,b) ((unsigned)((UCHAR(r)<<red_shift)|(UCHAR(g)<<green_shift)|(UCHAR(b)<<blue_shift))) +#define RGB15(r,g,b) ((unsigned)(((r*red_mask/255)&red_mask)|((g*green_mask/255)&green_mask)|((b*blue_mask/255)&blue_mask))) +#endif + +static void ImgPhotoBlendComplexAlpha ( + XImage *bgImg, /* background image to draw on */ + PhotoInstance *iPtr, /* image instance to draw */ + int xOffset, int yOffset, /* X & Y offset into image instance to draw */ + int width, int height /* width & height of image to draw */ + ) +{ + int x, y, line; + unsigned long pixel; + unsigned char r, g, b, alpha, unalpha; + unsigned char *alphaAr = iPtr->masterPtr->pix32; + unsigned char *masterPtr; + +#ifndef __WIN32__ + /* + * We have to get the mask and shift info from the visual. + * This might be cached for better performance. + */ + unsigned long red_mask, green_mask, blue_mask; + unsigned long red_shift, green_shift, blue_shift; + Visual *visual = iPtr->visualInfo.visual; + + red_mask = visual->red_mask; + green_mask = visual->green_mask; + blue_mask = visual->blue_mask; + red_shift = 0; + green_shift = 0; + blue_shift = 0; + while ((0x0001 & (red_mask >> red_shift)) == 0) red_shift++; + while ((0x0001 & (green_mask >> green_shift)) == 0) green_shift++; + while ((0x0001 & (blue_mask >> blue_shift)) == 0) blue_shift++; +#endif + +#define ALPHA_BLEND(bgPix, imgPix, alpha, unalpha) \ + ((bgPix * unalpha + imgPix * alpha) / 255) + +#ifndef __WIN32__ + /* + * Only unix requires the special case for <24bpp. It varies with + * 3 extra shifts and uses RGB15. The 24+bpp version could also + * then be further optimized. + */ + if (bgImg->depth < 24) { + unsigned char red_mlen, green_mlen, blue_mlen; + + red_mlen = 8 - CountBits(red_mask >> red_shift); + green_mlen = 8 - CountBits(green_mask >> green_shift); + blue_mlen = 8 - CountBits(blue_mask >> blue_shift); + for (y = 0; y < height; y++) { + line = (y + yOffset) * iPtr->masterPtr->width; + for (x = 0; x < width; x++) { + masterPtr = alphaAr + ((line + x + xOffset) * 4); + alpha = masterPtr[3]; + /* + * Ignore pixels that are fully transparent + */ + if (alpha) { + /* + * We could perhaps be more efficient than XGetPixel for + * 24 and 32 bit displays, but this seems "fast enough". + */ + r = masterPtr[0]; + g = masterPtr[1]; + b = masterPtr[2]; + if (alpha != 255) { + /* + * Only blend pixels that have some transparency + */ + unsigned char ra, ga, ba; + + pixel = XGetPixel(bgImg, x, y); + ra = GetRValue(pixel) << red_mlen; + ga = GetGValue(pixel) << green_mlen; + ba = GetBValue(pixel) << blue_mlen; + unalpha = 255 - alpha; + r = ALPHA_BLEND(ra, r, alpha, unalpha); + g = ALPHA_BLEND(ga, g, alpha, unalpha); + b = ALPHA_BLEND(ba, b, alpha, unalpha); + } + XPutPixel(bgImg, x, y, RGB15(r, g, b)); + } + } + } + } else +#endif + for (y = 0; y < height; y++) { + line = (y + yOffset) * iPtr->masterPtr->width; + for (x = 0; x < width; x++) { + masterPtr = alphaAr + ((line + x + xOffset) * 4); + alpha = masterPtr[3]; + /* + * Ignore pixels that are fully transparent + */ + if (alpha) { + /* + * We could perhaps be more efficient than XGetPixel for + * 24 and 32 bit displays, but this seems "fast enough". + */ + r = masterPtr[0]; + g = masterPtr[1]; + b = masterPtr[2]; + if (alpha != 255) { + /* + * Only blend pixels that have some transparency + */ + unsigned char ra, ga, ba; + + pixel = XGetPixel(bgImg, x, y); + ra = GetRValue(pixel); + ga = GetGValue(pixel); + ba = GetBValue(pixel); + unalpha = 255 - alpha; + r = ALPHA_BLEND(ra, r, alpha, unalpha); + g = ALPHA_BLEND(ga, g, alpha, unalpha); + b = ALPHA_BLEND(ba, b, alpha, unalpha); + } + XPutPixel(bgImg, x, y, RGB(r, g, b)); + } + } + } +#undef ALPHA_BLEND +} + +/* + *---------------------------------------------------------------------- + * * ImgPhotoDisplay -- * * This procedure is invoked to draw a photo image. @@ -2492,6 +2697,7 @@ ImgPhotoDisplay(clientData, display, drawable, imageX, imageY, width, * correspond to imageX and imageY. */ { PhotoInstance *instancePtr = (PhotoInstance *) clientData; + XVisualInfo visInfo = instancePtr->visualInfo; /* * If there's no pixmap, it means that an error occurred @@ -2502,21 +2708,48 @@ ImgPhotoDisplay(clientData, display, drawable, imageX, imageY, width, return; } - /* - * masterPtr->region describes which parts of the image contain - * valid data. We set this region as the clip mask for the gc, - * setting its origin appropriately, and use it when drawing the - * image. - */ + if ((instancePtr->masterPtr->flags & COMPLEX_ALPHA) + && visInfo.depth >= 15 + && (visInfo.class == DirectColor || visInfo.class == TrueColor)) { + XImage *bgImg = NULL; + + /* + * Pull the current background from the display to blend with + */ + bgImg = XGetImage(display, drawable, drawableX, drawableY, + (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap); + if (bgImg == NULL) { + return; + } + + ImgPhotoBlendComplexAlpha(bgImg, instancePtr, + imageX, imageY, width, height); - TkSetRegion(display, instancePtr->gc, instancePtr->masterPtr->validRegion); - XSetClipOrigin(display, instancePtr->gc, drawableX - imageX, - drawableY - imageY); - XCopyArea(display, instancePtr->pixels, drawable, instancePtr->gc, - imageX, imageY, (unsigned) width, (unsigned) height, - drawableX, drawableY); - XSetClipMask(display, instancePtr->gc, None); - XSetClipOrigin(display, instancePtr->gc, 0, 0); + /* + * Color info is unimportant as we only do this operation for + * depth >= 15. + */ + TkPutImage(NULL, 0, display, drawable, instancePtr->gc, + bgImg, 0, 0, drawableX, drawableY, + (unsigned int) width, (unsigned int) height); + XDestroyImage(bgImg); + } else { + /* + * masterPtr->region describes which parts of the image contain + * valid data. We set this region as the clip mask for the gc, + * setting its origin appropriately, and use it when drawing the + * image. + */ + TkSetRegion(display, instancePtr->gc, instancePtr->masterPtr->validRegion); + XSetClipOrigin(display, instancePtr->gc, drawableX - imageX, + drawableY - imageY); + XCopyArea(display, instancePtr->pixels, drawable, instancePtr->gc, + imageX, imageY, (unsigned) width, (unsigned) height, + drawableX, drawableY); + XSetClipMask(display, instancePtr->gc, None); + XSetClipOrigin(display, instancePtr->gc, 0, 0); + } + XFlush (display); } /* @@ -2821,6 +3054,8 @@ ImgPhotoSetSize(masterPtr, width, height) } } + ToggleComplexAlphaIfNeeded(masterPtr); + /* * Now adjust the sizes of the pixmaps for all of the instances. */ |