/* * Copyright (c) 2001 René Scharfe * Copyright (c) 2001,1999 Sasha Vasko * some of the code were taken from XFree86 sources * below is the copyright notice for XFree86 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $TOG: ImUtil.c /main/48 1998/02/06 17:37:38 kaleb $ */ /* Copyright 1986, 1998 The Open Group All Rights Reserved. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. */ /*#define DO_CLOCKING */ /*#define GETPIXEL_PUTPIXEL*/ #define LEGACY_SHADING_ALGORITHM #define ATERM #ifndef ATERM #include "../configure.h" #else #include "../config.h" #endif #if defined(BACKGROUND_IMAGE) || defined(TRANSPARENT) || !defined(ATERM) #include #include #include #ifndef RUINT32T #include #define RUINT32T CARD32 #endif #include #ifdef DO_CLOCKING #include #endif #ifdef __STDC__ #define Const const #else #define Const /**/ #endif #ifndef ATERM #include "../include/aftersteplib.h" #define NO_XIMAGE_PROTO #include "../include/XImage_utils.h" #else #define safemalloc(x) malloc(x) #include "rxvt.h" #endif #ifndef LIBAFTERSTEP_HAS_XIMAGE_UTILS /* * Macros * * The ROUNDUP macro rounds up a quantity to the specified boundary, * then truncates to bytes. * */ #define ROUNDUP(nbytes, pad) ((((nbytes) + ((pad)-1)) / (pad)) * ((pad)>>3)) /* * GetXImageDataSize calculates the size of memory needed to store XImage * pixel's data * */ #define XIMAGE_PLANE_SIZE(img) ((img)->bytes_per_line*(img)->height) unsigned long GetXImageDataSize (XImage * ximage) { unsigned long dsize; dsize = XIMAGE_PLANE_SIZE(ximage); if (ximage->format == XYPixmap) dsize = dsize * ximage->depth; return dsize; } /* * CreateXImageBySample * * Creates a new image that has same characteristics as an existing one. * Allocates the memory necessary for the new XImage data structure. * Pointer to new image is returned. Do not copy pixels from one image * to another * */ /* * _DestroyImage * * Deallocates the memory associated with the ximage data structure. * this version handles the case of the image data being malloc'd * entirely by the library. */ int MyXDestroyImage (ximage) XImage *ximage; { if (ximage->data != NULL) free((char *)ximage->data); if (ximage->obdata != NULL) free((char *)ximage->obdata); free((char *)ximage); return 1; } void _XInitImageFuncPtrs (XImage *); static XImage * CreateXImageBySample (ximage, width, height) XImage *ximage; unsigned int width; /* width in pixels of new subimage */ unsigned int height; /* height in pixels of new subimage */ { register XImage *subimage; unsigned long dsize; char *data; if ((subimage = (XImage *) calloc ( 1, sizeof (XImage))) == NULL) return (XImage *) NULL; subimage->width = width; subimage->height = height; subimage->xoffset = 0; subimage->format = ximage->format; subimage->byte_order = ximage->byte_order; subimage->bitmap_unit = ximage->bitmap_unit; subimage->bitmap_bit_order = ximage->bitmap_bit_order; subimage->bitmap_pad = ximage->bitmap_pad; subimage->bits_per_pixel = ximage->bits_per_pixel; subimage->depth = ximage->depth; /* * compute per line accelerator. */ if (subimage->format == ZPixmap) subimage->bytes_per_line = ROUNDUP (subimage->bits_per_pixel * width, subimage->bitmap_pad); else subimage->bytes_per_line = ROUNDUP (width, subimage->bitmap_pad); subimage->obdata = NULL; _XInitImageFuncPtrs (subimage); subimage->f.destroy_image = MyXDestroyImage ; dsize = GetXImageDataSize (subimage); if (((data = (unsigned char*)calloc ( 1, dsize)) == NULL) && (dsize > 0)) { Xfree ((char *) subimage); return (XImage *) NULL; } subimage->data = data; return subimage; } XImage * CreateXImageAndData (Display * dpy, Visual * visual, unsigned int depth, int format, int offset, unsigned int width, unsigned int height) { register XImage *ximage; unsigned long dsize; char *data; ximage = XCreateImage (dpy, visual, depth, format, offset, NULL, width, height, dpy->bitmap_unit, 0); if (ximage != NULL) { dsize = GetXImageDataSize (ximage); if (((data = calloc (1, dsize)) == NULL) && (dsize > 0)) { Xfree ((char *) ximage); ximage = (XImage *) NULL; } else ximage->data = data; } return ximage; } /* * This function does not perform any error checking becouse of the * performance considerations - do all the checks yourself !!! * * This function will copy entire line from source image into * target image * Can be used for fast XImage transformations * XImages must be of the same type, and horizontal size * * This function does not perform any error checking becouse of the * performance considerations - do all the checks yourself !!! * */ void CopyXImageLine( XImage *src, XImage *trg, Position src_y, Position trg_y ) { if (src->format == ZPixmap) memcpy( trg->data+trg->bytes_per_line*trg_y, src->data+src->bytes_per_line*src_y, src->bytes_per_line ); else { long plane_size = XIMAGE_PLANE_SIZE(src) ; long src_offset = src->bytes_per_line*src_y ; long trg_offset = trg->bytes_per_line*trg_y ; register int i = 0; for(; i < src->depth ; i++ ) { memcpy( trg->data+trg_offset, src->data+src_offset, src->bytes_per_line ); src_offset+=plane_size ; trg_offset+=plane_size ; } } } #endif /* #ifndef LIBAFTERSTEP_HAS_XIMAGE_UTILS */ #ifndef LIBAFTERSTEP_HAS_SCALEXIMAGE /* * This function will create array of size of the target image length/width * filled with x/y coordinates for pixel in source image */ void Scale (Position * target, Position target_size, Position src_start, Position src_end) { Position trg_first_half_end, src_first_half_end; if (target_size > 1) { if (src_start == src_end) { register int i = 0; for (; i < target_size; i++) *(target + i) = src_start; } else { src_first_half_end = (src_start + src_end) >> 1; trg_first_half_end = target_size >> 1; Scale (target, trg_first_half_end, src_start, src_first_half_end); Scale (target + trg_first_half_end, target_size - trg_first_half_end, src_first_half_end + 1, src_end); } } else if (target_size == 1) *target = (src_start + src_end) >> 1; } /* * This function will actually create scaled image from source image */ XImage * ScaleXImageToSize (src, width, height) XImage *src; Position width, height; { XImage *dst; Position y; register int x ; Position *x_net = NULL, *y_net = NULL ; #ifdef DO_CLOCKING clock_t started = clock(); #endif #ifdef ATERM Visual* visual = DefaultVisual(Xdisplay, Xscreen); #else Visual* visual = DefaultVisual(dpy, DefaultScreen(dpy)); #endif #define VISUAL visual if (width == 0 || height == 0) return NULL; if (width == src->width && height == src->height) { dst = XSubImage (src, 0, 0, width, height); } else { /* It would be nice average the skipped pixels. */ if ((dst = CreateXImageBySample (src, width, height)) != NULL) { #ifndef GETPIXEL_PUTPIXEL unsigned char bytes_per_pixel = src->bits_per_pixel >>3 ; unsigned char* dst_start = (unsigned char *)dst->data ; if( src->bits_per_pixel == 15 ) bytes_per_pixel = 2 ; #endif if( width != src->width ) { x_net = (Position *) safemalloc (sizeof (Position) * width); Scale (x_net, width, 0, src->width - 1); #ifndef GETPIXEL_PUTPIXEL if( src->bits_per_pixel >= 8 && src->format == ZPixmap && visual->class == TrueColor ) for (x = 0; x < width; x++) x_net[x] = x_net[x]*bytes_per_pixel ; #endif } y_net = (Position *) safemalloc (sizeof (Position) * height); Scale (y_net, height, 0, src->height - 1); for (y = 0; y < height; y++) { if( y>0 ) if( y_net[y] == y_net[y-1] ) { CopyXImageLine( dst, dst, y-1, y ); #ifndef GETPIXEL_PUTPIXEL dst_start += dst->bytes_per_line ; #endif continue ; } if( width == src->width ) { CopyXImageLine( src, dst, y_net[y], y ); } else #ifndef GETPIXEL_PUTPIXEL if(src->bits_per_pixel >= 8 && src->format == ZPixmap && visual->class == TrueColor ) { /* this should be a faster variant */ unsigned char* src_start = (unsigned char *) (src->data + y_net[y] * src->bytes_per_line) ; unsigned char* dst_pos = dst_start ; unsigned char* src_pos ; register int i ; for (x = 0; x < width; x++) { src_pos = src_start+x_net[x] ; for( i = 0 ; i < bytes_per_pixel ; i++ ) *dst_pos++ = *src_pos++ ; } dst_start += dst->bytes_per_line ; } else #endif if( width > src->width ) { Pixel pix = XGetPixel (src, x_net[0], y_net[y]); XPutPixel (dst, 0, y, pix); for (x = 1; x < width; x++) { if( x_net[x] != x_net[x-1] ) pix = XGetPixel (src, x_net[x], y_net[y]); XPutPixel (dst, x, y, pix); } }else { for (x = 0; x < width; x++) XPutPixel (dst, x, y, XGetPixel (src, x_net[x], y_net[y])); } } if(x_net) free (x_net); if(y_net) free (y_net); } } #ifdef DO_CLOCKING printf( "\n Scaling time (clocks): %lu\n",clock()-started ); #endif return (dst); } XImage * ScaleXImage (src, scale_x, scale_y) XImage *src; double scale_x, scale_y; { return ScaleXImageToSize (src, (Dimension) max (scale_x * src->width, 1), (Dimension) max (scale_y * src->height, 1)); } #endif /*#ifndef LIBAFTERSTEP_HAS_SCALEXIMAGE*/ #ifndef LIBAFTERSTEP_HAS_SHADEXIMAGE #ifdef LEGACY_SHADING_ALGORITHM void ShadeXImage( XImage* srcImage, ShadingInfo* shading, GC gc ) { #ifdef DO_CLOCKING clock_t started = clock(); #endif unsigned int int_rm, int_gm, int_bm ; int shade = 0; #ifdef ATERM Visual* visual = DefaultVisual(Xdisplay, Xscreen); #else Visual* visual = DefaultVisual(dpy, DefaultScreen(dpy)); #endif #define VISUAL visual if( visual->class != TrueColor || srcImage->format != ZPixmap ) return ; if( srcImage->bits_per_pixel <= 8 ) return ; if( shading->shading < 0 ) shade = 100-shading->shading ; /*we want to lighten instead of darken */ else shade = shading->shading ; if( shade > 200 ) shade = 200 ; int_rm = (shading->tintColor.red>>8)*shade/100; int_gm = (shading->tintColor.green>>8)*shade/100; int_bm = (shading->tintColor.blue>>8)*shade/100; #ifdef GETPIXEL_PUTPIXEL { register unsigned long p, x; unsigned long y; unsigned int rb_mask, g_mask; unsigned char r_shift, g_shift, b_shift; g_mask = rb_mask = 0xf8 ; b_shift = 3 ; switch (srcImage->bits_per_pixel) { case 15: r_shift = 7 ; g_shift = 2 ; break ; case 16: g_mask = 0xfc ; r_shift = 8 ; g_shift = 3 ; break ; case 32: case 24: g_mask = rb_mask = 0xff ; r_shift = 16 ; g_shift = 8 ; break ; } if( srcImage->bits_per_pixel<=16 ) /* for better speed we don't want to check for it inside the loop */ { for (y = 0; y < srcImage->height; y++) for (x = 0; x < srcImage->width; x++) { p = XGetPixel(srcImage, x, y); XPutPixel(srcImage, x, y, ((((((p>>r_shift)&rb_mask)*int_rm)/255)&rb_mask)<>g_shift)&g_mask)*int_gm)/255)&g_mask)<>b_shift)); } }else { for (y = 0; y < srcImage->height; y++) for (x = 0; x < srcImage->width; x++) { p = XGetPixel(srcImage, x, y); XPutPixel(srcImage, x, y, ((((((p>>r_shift)&rb_mask)*int_rm)/255)&rb_mask)<>g_shift)&g_mask)*int_gm)/255)&g_mask)<red_mask; RUINT32T gk = VISUAL->green_mask; RUINT32T bk = VISUAL->blue_mask; /* fprintf( stderr, "\n ATERM: RedMask = %x, GreenMask= %x, BlueMask = %x", rk, bk, bk );*/ /* The following code has been written by * Ethan Fischer * for AfterStep window manager. It's much faster then previous */ /* this code only works for TrueColor (and maybe DirectColor) modes; * it would do very odd things in PseudoColor */ switch (srcImage->bits_per_pixel) { case 15: case 16: { unsigned short *p1 = (unsigned short *) srcImage->data; unsigned short *pf = (unsigned short *) (srcImage->data + srcImage->height * srcImage->bytes_per_line); while (p1 < pf) { unsigned short *p = p1; unsigned short *pl = p1 + srcImage->width; for (; p < pl; p++) { RUINT32T v = *p; v = (((v & rk) * int_rm >>8) &rk)| (((v & gk) * int_gm >>8) &gk)| (((v & bk) * int_bm >>8) &bk); *p = v; } p1 = (unsigned short *) ((char *) p1 + srcImage->bytes_per_line); } break; } case 24: { unsigned char *p1 = (unsigned char *) srcImage->data; unsigned char *pf = (unsigned char *) (srcImage->data + srcImage->height * srcImage->bytes_per_line); if( rk >= 0xFF0000 ) /* we need it for some wierd XServers like XFree86 3.3.3.1 */ { unsigned int int_tmp = int_rm ; int_rm = int_bm ; int_bm = int_tmp ; } while (p1 < pf) { unsigned char *p = p1; unsigned char *pl = p1 + srcImage->width * 3; for (; p < pl; p += 3) { p[0] = (unsigned long) p[0] * int_rm >>8; p[1] = (unsigned long) p[1] * int_gm >>8; p[2] = (unsigned long) p[2] * int_bm >>8; } p1 = (unsigned char *) ((char *) p1 + srcImage->bytes_per_line); } break; } case 32: { RUINT32T *p1 = (RUINT32T *) srcImage->data; RUINT32T *pf = (RUINT32T *) (srcImage->data + srcImage->height * srcImage->bytes_per_line); while (p1 < pf) { RUINT32T *p = p1; RUINT32T *pl = p1 + srcImage->width; for (; p < pl; p++) { RUINT32T v = *p; v = (v & ~rk) | (((v & rk) * int_rm / 256) & rk); v = (v & ~gk) | (((v & gk) * int_gm / 256) & gk); v = (v & ~bk) | (((v & bk) * int_bm / 256) & bk); *p = v; } p1 = (RUINT32T *) ((char *) p1 + srcImage->bytes_per_line); } break; } } } #endif #ifdef DO_CLOCKING printf( "\n Shading time (clocks): %lu\n",clock()-started ); #endif } #else /* #ifdef LEGACY_SHADING_ALGORITHM */ void ShadeXImage( XImage* srcImage, ShadingInfo* shading, GC gc ) { int sh_r, sh_g, sh_b; RUINT32T mask_r, mask_g, mask_b; RUINT32T *lookup, *lookup_r, *lookup_g, *lookup_b; unsigned int lower_lim_r, lower_lim_g, lower_lim_b; unsigned int upper_lim_r, upper_lim_g, upper_lim_b; int i; #ifdef DO_CLOCKING clock_t started = clock(); #endif #ifdef ATERM Visual* visual = DefaultVisual(Xdisplay, Xscreen); #else Visual* visual = DefaultVisual(dpy, DefaultScreen(dpy)); #endif if( visual->class != TrueColor || srcImage->format != ZPixmap ) return ; /* for convenience */ mask_r = visual->red_mask; mask_g = visual->green_mask; mask_b = visual->blue_mask; /* boring lookup table pre-initialization */ switch (srcImage->bits_per_pixel) { case 15: if ((mask_r != 0x7c00) || (mask_g != 0x03e0) || (mask_b != 0x001f)) return; lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(32+32+32)); lookup_r = lookup; lookup_g = lookup+32; lookup_b = lookup+32+32; sh_r = 10; sh_g = 5; sh_b = 0; break; case 16: if ((mask_r != 0xf800) || (mask_g != 0x07e0) || (mask_b != 0x001f)) return; lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(32+64+32)); lookup_r = lookup; lookup_g = lookup+32; lookup_b = lookup+32+64; sh_r = 11; sh_g = 5; sh_b = 0; break; case 24: if ((mask_r != 0xff0000) || (mask_g != 0x00ff00) || (mask_b != 0x0000ff)) return; lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(256+256+256)); lookup_r = lookup; lookup_g = lookup+256; lookup_b = lookup+256+256; sh_r = 16; sh_g = 8; sh_b = 0; break; case 32: if ((mask_r != 0xff0000) || (mask_g != 0x00ff00) || (mask_b != 0x0000ff)) return; lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(256+256+256)); lookup_r = lookup; lookup_g = lookup+256; lookup_b = lookup+256+256; sh_r = 16; sh_g = 8; sh_b = 0; break; default: return; /* we do not support this color depth */ } /* prepare limits for color transformation (each channel is handled separately) */ if (shading->shading < 0) { int shade; shade = -shading->shading; if (shade < 0) shade = 0; if (shade > 100) shade = 100; lower_lim_r = 65535-shading->tintColor.red; lower_lim_g = 65535-shading->tintColor.green; lower_lim_b = 65535-shading->tintColor.blue; lower_lim_r = 65535-(unsigned int)(((RUINT32T)lower_lim_r)*((RUINT32T)shade)/100); lower_lim_g = 65535-(unsigned int)(((RUINT32T)lower_lim_g)*((RUINT32T)shade)/100); lower_lim_b = 65535-(unsigned int)(((RUINT32T)lower_lim_b)*((RUINT32T)shade)/100); upper_lim_r = upper_lim_g = upper_lim_b = 65535; } else { int shade; shade = shading->shading; if (shade < 0) shade = 0; if (shade > 100) shade = 100; lower_lim_r = lower_lim_g = lower_lim_b = 0; upper_lim_r = (unsigned int)((((RUINT32T)shading->tintColor.red)*((RUINT32T)shading->shading))/100); upper_lim_g = (unsigned int)((((RUINT32T)shading->tintColor.green)*((RUINT32T)shading->shading))/100); upper_lim_b = (unsigned int)((((RUINT32T)shading->tintColor.blue)*((RUINT32T)shading->shading))/100); } /* switch red and blue bytes if necessary, we need it for some weird XServers like XFree86 3.3.3.1 */ if ((srcImage->bits_per_pixel == 24) && (mask_r >= 0xFF0000 )) { unsigned int tmp; tmp = lower_lim_r; lower_lim_r = lower_lim_b; lower_lim_b = tmp; tmp = upper_lim_r; upper_lim_r = upper_lim_b; upper_lim_b = tmp; } /* fill our lookup tables */ for (i = 0; i <= mask_r>>sh_r; i++) { RUINT32T tmp; tmp = ((RUINT32T)i)*((RUINT32T)(upper_lim_r-lower_lim_r)); tmp += ((RUINT32T)(mask_r>>sh_r))*((RUINT32T)lower_lim_r); lookup_r[i] = (tmp/65535)<>sh_g; i++) { RUINT32T tmp; tmp = ((RUINT32T)i)*((RUINT32T)(upper_lim_g-lower_lim_g)); tmp += ((RUINT32T)(mask_g>>sh_g))*((RUINT32T)lower_lim_g); lookup_g[i] = (tmp/65535)<>sh_b; i++) { RUINT32T tmp; tmp = ((RUINT32T)i)*((RUINT32T)(upper_lim_b-lower_lim_b)); tmp += ((RUINT32T)(mask_b>>sh_b))*((RUINT32T)lower_lim_b); lookup_b[i] = (tmp/65535)<bits_per_pixel) { case 15: { unsigned short *p1, *pf, *p, *pl; p1 = (unsigned short *) srcImage->data; pf = (unsigned short *) (srcImage->data + srcImage->height * srcImage->bytes_per_line); while (p1 < pf) { p = p1; pl = p1 + srcImage->width; for (; p < pl; p++) { *p = lookup_r[(*p & 0x7c00)>>10] | lookup_g[(*p & 0x03e0)>> 5] | lookup_b[(*p & 0x001f)]; } p1 = (unsigned short *) ((char *) p1 + srcImage->bytes_per_line); } break; } case 16: { unsigned short *p1, *pf, *p, *pl; p1 = (unsigned short *) srcImage->data; pf = (unsigned short *) (srcImage->data + srcImage->height * srcImage->bytes_per_line); while (p1 < pf) { p = p1; pl = p1 + srcImage->width; for (; p < pl; p++) { *p = lookup_r[(*p & 0xf800)>>11] | lookup_g[(*p & 0x07e0)>> 5] | lookup_b[(*p & 0x001f)]; } p1 = (unsigned short *) ((char *) p1 + srcImage->bytes_per_line); } break; } case 24: { unsigned char *p1, *pf, *p, *pl; p1 = (unsigned char *) srcImage->data; pf = (unsigned char *) (srcImage->data + srcImage->height * srcImage->bytes_per_line); while (p1 < pf) { p = p1; pl = p1 + srcImage->width * 3; for (; p < pl; p += 3) { p[0] = lookup_r[(p[0] & 0xff0000)>>16]; p[1] = lookup_r[(p[1] & 0x00ff00)>> 8]; p[2] = lookup_r[(p[2] & 0x0000ff)]; } p1 = (unsigned char *) ((char *) p1 + srcImage->bytes_per_line); } break; } case 32: { RUINT32T *p1, *pf, *p, *pl; p1 = (RUINT32T *) srcImage->data; pf = (RUINT32T *) (srcImage->data + srcImage->height * srcImage->bytes_per_line); while (p1 < pf) { p = p1; pl = p1 + srcImage->width; for (; p < pl; p++) { *p = lookup_r[(*p & 0xff0000)>>16] | lookup_g[(*p & 0x00ff00)>> 8] | lookup_b[(*p & 0x0000ff)] | (*p & ~0xffffff); } p1 = (RUINT32T *) ((char *) p1 + srcImage->bytes_per_line); } break; } } free (lookup); #ifdef DO_CLOCKING printf( "\n Shading time (clocks): %lu\n",clock()-started ); #endif } #endif /* #ifdef LEGACY_SHADING_ALGORITHM */ #endif /* #ifndef LIBAFTERSTEP_HAS_SHADEXIMAGE */ #endif /* defined(BACKGROUND_IMAGE) || defined(TRANSPARENT) || !defined(ATERM) */