1/* 2 * tkUnix3d.c -- 3 * 4 * This file contains the platform specific routines for drawing 3d 5 * borders in the Motif style. 6 * 7 * Copyright (c) 1996 by Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id$ 13 */ 14 15#include "tkInt.h" 16#include "tk3d.h" 17 18#if !(defined(__WIN32__) || defined(MAC_OSX_TK)) 19#include "tkUnixInt.h" 20#endif 21 22/* 23 * This structure is used to keep track of the extra colors used by Unix 3D 24 * borders. 25 */ 26 27typedef struct { 28 TkBorder info; 29 GC solidGC; /* Used to draw solid relief. */ 30} UnixBorder; 31 32/* 33 *---------------------------------------------------------------------- 34 * 35 * TkpGetBorder -- 36 * 37 * This function allocates a new TkBorder structure. 38 * 39 * Results: 40 * Returns a newly allocated TkBorder. 41 * 42 * Side effects: 43 * None. 44 * 45 *---------------------------------------------------------------------- 46 */ 47 48TkBorder * 49TkpGetBorder(void) 50{ 51 UnixBorder *borderPtr = (UnixBorder *) ckalloc(sizeof(UnixBorder)); 52 borderPtr->solidGC = None; 53 return (TkBorder *) borderPtr; 54} 55 56/* 57 *---------------------------------------------------------------------- 58 * 59 * TkpFreeBorder -- 60 * 61 * This function frees any colors allocated by the platform specific part 62 * of this module. 63 * 64 * Results: 65 * None. 66 * 67 * Side effects: 68 * May deallocate some colors. 69 * 70 *---------------------------------------------------------------------- 71 */ 72 73void 74TkpFreeBorder( 75 TkBorder *borderPtr) 76{ 77 UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr; 78 Display *display = DisplayOfScreen(borderPtr->screen); 79 80 if (unixBorderPtr->solidGC != None) { 81 Tk_FreeGC(display, unixBorderPtr->solidGC); 82 } 83} 84/* 85 *-------------------------------------------------------------- 86 * 87 * Tk_3DVerticalBevel -- 88 * 89 * This procedure draws a vertical bevel along one side of an object. The 90 * bevel is always rectangular in shape: 91 * ||| 92 * ||| 93 * ||| 94 * ||| 95 * ||| 96 * ||| 97 * An appropriate shadow color is chosen for the bevel based on the 98 * leftBevel and relief arguments. Normally this procedure is called 99 * first, then Tk_3DHorizontalBevel is called next to draw neat corners. 100 * 101 * Results: 102 * None. 103 * 104 * Side effects: 105 * Graphics are drawn in drawable. 106 * 107 *-------------------------------------------------------------- 108 */ 109 110void 111Tk_3DVerticalBevel( 112 Tk_Window tkwin, /* Window for which border was allocated. */ 113 Drawable drawable, /* X window or pixmap in which to draw. */ 114 Tk_3DBorder border, /* Token for border to draw. */ 115 int x, int y, int width, int height, 116 /* Area of vertical bevel. */ 117 int leftBevel, /* Non-zero means this bevel forms the left 118 * side of the object; 0 means it forms the 119 * right side. */ 120 int relief) /* Kind of bevel to draw. For example, 121 * TK_RELIEF_RAISED means interior of object 122 * should appear higher than exterior. */ 123{ 124 TkBorder *borderPtr = (TkBorder *) border; 125 GC left, right; 126 Display *display = Tk_Display(tkwin); 127 128 if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) { 129 TkpGetShadows(borderPtr, tkwin); 130 } 131 132 if (relief == TK_RELIEF_RAISED) { 133 XFillRectangle(display, drawable, 134 (leftBevel) ? borderPtr->lightGC : borderPtr->darkGC, 135 x, y, (unsigned) width, (unsigned) height); 136 } else if (relief == TK_RELIEF_SUNKEN) { 137 XFillRectangle(display, drawable, 138 (leftBevel) ? borderPtr->darkGC : borderPtr->lightGC, 139 x, y, (unsigned) width, (unsigned) height); 140 } else if (relief == TK_RELIEF_RIDGE) { 141 int half; 142 143 left = borderPtr->lightGC; 144 right = borderPtr->darkGC; 145 ridgeGroove: 146 half = width/2; 147 if (!leftBevel && (width & 1)) { 148 half++; 149 } 150 XFillRectangle(display, drawable, left, x, y, (unsigned) half, 151 (unsigned) height); 152 XFillRectangle(display, drawable, right, x+half, y, 153 (unsigned) (width-half), (unsigned) height); 154 } else if (relief == TK_RELIEF_GROOVE) { 155 left = borderPtr->darkGC; 156 right = borderPtr->lightGC; 157 goto ridgeGroove; 158 } else if (relief == TK_RELIEF_FLAT) { 159 XFillRectangle(display, drawable, borderPtr->bgGC, x, y, 160 (unsigned) width, (unsigned) height); 161 } else if (relief == TK_RELIEF_SOLID) { 162 UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr; 163 if (unixBorderPtr->solidGC == None) { 164 XGCValues gcValues; 165 166 gcValues.foreground = BlackPixelOfScreen(borderPtr->screen); 167 unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 168 } 169 XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y, 170 (unsigned) width, (unsigned) height); 171 } 172} 173 174/* 175 *-------------------------------------------------------------- 176 * 177 * Tk_3DHorizontalBevel -- 178 * 179 * This procedure draws a horizontal bevel along one side of an object. 180 * The bevel has mitered corners (depending on leftIn and rightIn 181 * arguments). 182 * 183 * Results: 184 * None. 185 * 186 * Side effects: 187 * None. 188 * 189 *-------------------------------------------------------------- 190 */ 191 192void 193Tk_3DHorizontalBevel( 194 Tk_Window tkwin, /* Window for which border was allocated. */ 195 Drawable drawable, /* X window or pixmap in which to draw. */ 196 Tk_3DBorder border, /* Token for border to draw. */ 197 int x, int y, int width, int height, 198 /* Bounding box of area of bevel. Height gives 199 * width of border. */ 200 int leftIn, int rightIn, /* Describes whether the left and right edges 201 * of the bevel angle in or out as they go 202 * down. For example, if "leftIn" is true, the 203 * left side of the bevel looks like this: 204 * ___________ 205 * __________ 206 * _________ 207 * ________ 208 */ 209 int topBevel, /* Non-zero means this bevel forms the top 210 * side of the object; 0 means it forms the 211 * bottom side. */ 212 int relief) /* Kind of bevel to draw. For example, 213 * TK_RELIEF_RAISED means interior of object 214 * should appear higher than exterior. */ 215{ 216 TkBorder *borderPtr = (TkBorder *) border; 217 Display *display = Tk_Display(tkwin); 218 int bottom, halfway, x1, x2, x1Delta, x2Delta; 219 UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr; 220 GC topGC = None, bottomGC = None; 221 /* Initializations needed only to prevent 222 * compiler warnings. */ 223 224 if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT) && 225 (relief != TK_RELIEF_SOLID)) { 226 TkpGetShadows(borderPtr, tkwin); 227 } 228 229 /* 230 * Compute a GC for the top half of the bevel and a GC for the bottom half 231 * (they're the same in many cases). 232 */ 233 234 switch (relief) { 235 case TK_RELIEF_FLAT: 236 topGC = bottomGC = borderPtr->bgGC; 237 break; 238 case TK_RELIEF_GROOVE: 239 topGC = borderPtr->darkGC; 240 bottomGC = borderPtr->lightGC; 241 break; 242 case TK_RELIEF_RAISED: 243 topGC = bottomGC = (topBevel? borderPtr->lightGC : borderPtr->darkGC); 244 break; 245 case TK_RELIEF_RIDGE: 246 topGC = borderPtr->lightGC; 247 bottomGC = borderPtr->darkGC; 248 break; 249 case TK_RELIEF_SOLID: 250 if (unixBorderPtr->solidGC == None) { 251 XGCValues gcValues; 252 253 gcValues.foreground = BlackPixelOfScreen(borderPtr->screen); 254 unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 255 } 256 XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y, 257 (unsigned) width, (unsigned) height); 258 return; 259 case TK_RELIEF_SUNKEN: 260 topGC = bottomGC = (topBevel? borderPtr->darkGC : borderPtr->lightGC); 261 break; 262 } 263 264 /* 265 * Compute various other geometry-related stuff. 266 */ 267 268 x1 = x; 269 if (!leftIn) { 270 x1 += height; 271 } 272 x2 = x+width; 273 if (!rightIn) { 274 x2 -= height; 275 } 276 x1Delta = (leftIn) ? 1 : -1; 277 x2Delta = (rightIn) ? -1 : 1; 278 halfway = y + height/2; 279 if (!topBevel && (height & 1)) { 280 halfway++; 281 } 282 bottom = y + height; 283 284 /* 285 * Draw one line for each y-coordinate covered by the bevel. 286 */ 287 288 for ( ; y < bottom; y++) { 289 /* 290 * X Dimensions are 16-bit, so avoid wraparound or display errors by 291 * limiting these here. 292 */ 293 294 if (x1 < -32767) { 295 x1 = -32767; 296 } 297 if (x2 > 32767) { 298 x2 = 32767; 299 } 300 301 /* 302 * In some weird cases (such as large border widths for skinny 303 * rectangles) x1 can be >= x2. Don't draw the lines in these cases. 304 */ 305 306 if (x1 < x2) { 307 XFillRectangle(display, drawable, 308 (y < halfway) ? topGC : bottomGC, x1, y, 309 (unsigned) (x2-x1), (unsigned) 1); 310 } 311 x1 += x1Delta; 312 x2 += x2Delta; 313 } 314} 315 316/* 317 *---------------------------------------------------------------------- 318 * 319 * TkpGetShadows -- 320 * 321 * This procedure computes the shadow colors for a 3-D border and fills 322 * in the corresponding fields of the Border structure. It's called 323 * lazily, so that the colors aren't allocated until something is 324 * actually drawn with them. That way, if a border is only used for flat 325 * backgrounds the shadow colors will never be allocated. 326 * 327 * Results: 328 * None. 329 * 330 * Side effects: 331 * The lightGC and darkGC fields in borderPtr get filled in, if they 332 * weren't already. 333 * 334 *---------------------------------------------------------------------- 335 */ 336 337void 338TkpGetShadows( 339 TkBorder *borderPtr, /* Information about border. */ 340 Tk_Window tkwin) /* Window where border will be used for 341 * drawing. */ 342{ 343 XColor lightColor, darkColor; 344 int stressed, tmp1, tmp2; 345 int r, g, b; 346 XGCValues gcValues; 347 348 if (borderPtr->lightGC != None) { 349 return; 350 } 351 stressed = TkpCmapStressed(tkwin, borderPtr->colormap); 352 353 /* 354 * First, handle the case of a color display with lots of colors. The 355 * shadow colors get computed using whichever formula results in the 356 * greatest change in color: 357 * 1. Lighter shadow is half-way to white, darker shadow is half way to 358 * dark. 359 * 2. Lighter shadow is 40% brighter than background, darker shadow is 40% 360 * darker than background. 361 */ 362 363 if (!stressed && (Tk_Depth(tkwin) >= 6)) { 364 /* 365 * This is a color display with lots of colors. For the dark shadow, 366 * cut 40% from each of the background color components. But if the 367 * background is already very dark, make the dark color a little 368 * lighter than the background by increasing each color component 369 * 1/4th of the way to MAX_INTENSITY. 370 * 371 * For the light shadow, boost each component by 40% or half-way to 372 * white, whichever is greater (the first approach works better for 373 * unsaturated colors, the second for saturated ones). But if the 374 * background is already very bright, instead choose a slightly darker 375 * color for the light shadow by reducing each color component by 10%. 376 * 377 * Compute the colors using integers, not using lightColor.red etc.: 378 * these are shorts and may have problems with integer overflow. 379 */ 380 381 /* 382 * Compute the dark shadow color 383 */ 384 385 r = (int) borderPtr->bgColorPtr->red; 386 g = (int) borderPtr->bgColorPtr->green; 387 b = (int) borderPtr->bgColorPtr->blue; 388 389 if (r*0.5*r + g*1.0*g + b*0.28*b < MAX_INTENSITY*0.05*MAX_INTENSITY) { 390 darkColor.red = (MAX_INTENSITY + 3*r)/4; 391 darkColor.green = (MAX_INTENSITY + 3*g)/4; 392 darkColor.blue = (MAX_INTENSITY + 3*b)/4; 393 } else { 394 darkColor.red = (60 * r)/100; 395 darkColor.green = (60 * g)/100; 396 darkColor.blue = (60 * b)/100; 397 } 398 399 /* 400 * Allocate the dark shadow color and its GC 401 */ 402 403 borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor); 404 gcValues.foreground = borderPtr->darkColorPtr->pixel; 405 borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 406 407 /* 408 * Compute the light shadow color 409 */ 410 411 if (g > MAX_INTENSITY*0.95) { 412 lightColor.red = (90 * r)/100; 413 lightColor.green = (90 * g)/100; 414 lightColor.blue = (90 * b)/100; 415 } else { 416 tmp1 = (14 * r)/10; 417 if (tmp1 > MAX_INTENSITY) { 418 tmp1 = MAX_INTENSITY; 419 } 420 tmp2 = (MAX_INTENSITY + r)/2; 421 lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2; 422 tmp1 = (14 * g)/10; 423 if (tmp1 > MAX_INTENSITY) { 424 tmp1 = MAX_INTENSITY; 425 } 426 tmp2 = (MAX_INTENSITY + g)/2; 427 lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2; 428 tmp1 = (14 * b)/10; 429 if (tmp1 > MAX_INTENSITY) { 430 tmp1 = MAX_INTENSITY; 431 } 432 tmp2 = (MAX_INTENSITY + b)/2; 433 lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2; 434 } 435 436 /* 437 * Allocate the light shadow color and its GC 438 */ 439 440 borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor); 441 gcValues.foreground = borderPtr->lightColorPtr->pixel; 442 borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 443 return; 444 } 445 446 if (borderPtr->shadow == None) { 447 borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin, 448 Tk_GetUid("gray50")); 449 if (borderPtr->shadow == None) { 450 Tcl_Panic("TkpGetShadows couldn't allocate bitmap for border"); 451 } 452 } 453 if (borderPtr->visual->map_entries > 2) { 454 /* 455 * This isn't a monochrome display, but the colormap either ran out of 456 * entries or didn't have very many to begin with. Generate the light 457 * shadows with a white stipple and the dark shadows with a black 458 * stipple. 459 */ 460 461 gcValues.foreground = borderPtr->bgColorPtr->pixel; 462 gcValues.background = BlackPixelOfScreen(borderPtr->screen); 463 gcValues.stipple = borderPtr->shadow; 464 gcValues.fill_style = FillOpaqueStippled; 465 borderPtr->darkGC = Tk_GetGC(tkwin, 466 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); 467 gcValues.background = WhitePixelOfScreen(borderPtr->screen); 468 borderPtr->lightGC = Tk_GetGC(tkwin, 469 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); 470 return; 471 } 472 473 /* 474 * This is just a measly monochrome display, hardly even worth its 475 * existence on this earth. Make one shadow a 50% stipple and the other 476 * the opposite of the background. 477 */ 478 479 gcValues.foreground = WhitePixelOfScreen(borderPtr->screen); 480 gcValues.background = BlackPixelOfScreen(borderPtr->screen); 481 gcValues.stipple = borderPtr->shadow; 482 gcValues.fill_style = FillOpaqueStippled; 483 borderPtr->lightGC = Tk_GetGC(tkwin, 484 GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); 485 if (borderPtr->bgColorPtr->pixel 486 == WhitePixelOfScreen(borderPtr->screen)) { 487 gcValues.foreground = BlackPixelOfScreen(borderPtr->screen); 488 borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 489 } else { 490 borderPtr->darkGC = borderPtr->lightGC; 491 borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); 492 } 493} 494 495/* 496 * Local Variables: 497 * mode: c 498 * c-basic-offset: 4 499 * fill-column: 78 500 * End: 501 */ 502