Version 5 of bevel 3d

Updated 2010-05-14 10:26:17 by MGS

MGS [2003/04/06] - In order to draw rectangles on a canvas with a raised/sunken relief, you need to know how to compute the colors of the 3d beveled edges - the light and dark shadows. I converted tk8.4.0/unix/tkUnix3d.c to pure tcl code:

# ======================================================================

  namespace eval color {
    variable MAX_INTENSITY 65535
  }

# ======================================================================

# shadow:dark --

# Compute the dark shadow color for a 3d border.

# Cut 40% from each of the color components. If the background is
# already very dark, make the dark color a little lighter than the
# background by increasing each color component 1/4th of the way to
# MAX_INTENSITY.

# This adapted from tk8.4.0/unix/tkUnix3d.c

proc color::shadow:dark {R G B} {

# ----------------------------------------------------------------------

  variable MAX_INTENSITY

# ----------------------------------------------------------------------

  if { expr {($R*0.5*$R) + ($G*1.0*$G) + ($B*0.28*$B)} <
       expr {$MAX_INTENSITY*0.05*$MAX_INTENSITY} } {
    set r expr {($MAX_INTENSITY + 3*$R)/4}
    set g expr {($MAX_INTENSITY + 3*$G)/4}
    set b expr {($MAX_INTENSITY + 3*$B)/4}
  } else {
    set r expr {(60 * $R)/100}
    set g expr {(60 * $G)/100}
    set b expr {(60 * $B)/100}
  }

# ----------------------------------------------------------------------

  return format #%04X%04X%04X $r $g $b

}

# ======================================================================

# shadow:light --

# Compute the light shadow color for a 3d border.

# Boost each component by 40% or half-way to white, whichever is greater
# (the first approach works better for unsaturated colors, the second
# for saturated ones). If the background is already very bright, instead
# choose a slightly darker color for the light shadow by reducing each
# color component by 10%.

# This adapted from tk8.4.0/unix/tkUnix3d.c

proc color::shadow:light {R G B} {

# ----------------------------------------------------------------------

  variable MAX_INTENSITY

# ----------------------------------------------------------------------

  if { $G > expr {$MAX_INTENSITY*0.95} } {
    set r expr {(90 * $R)/100}
    set g expr {(90 * $G)/100}
    set b expr {(90 * $B)/100}
  } else {
    set tmp1 expr {(14 * $R)/10}

    if { $tmp1 > $MAX_INTENSITY } { set tmp1 $MAX_INTENSITY }

    set tmp2 expr {($MAX_INTENSITY + $R)/2}
    set r    expr {($tmp1 > $tmp2) ? $tmp1 : $tmp2}
    set tmp1 expr {(14 * $G)/10}

    if { $tmp1 > $MAX_INTENSITY } { set tmp1 $MAX_INTENSITY }

    set tmp2 expr {($MAX_INTENSITY + $G)/2}
    set g    expr {($tmp1 > $tmp2) ? $tmp1 : $tmp2}
    set tmp1 expr {(14 * $B)/10}

    if { $tmp1 > $MAX_INTENSITY } { set tmp1 $MAX_INTENSITY }

    set tmp2 expr {($MAX_INTENSITY + $B)/2}
    set b    expr {($tmp1 > $tmp2) ? $tmp1 : $tmp2}
  }

# ----------------------------------------------------------------------

  return format #%04X%04X%04X $r $g $b

}

# ======================================================================

# demo code
proc rect {c x1 y1 x2 y2 bd color} {

  set dark  [eval color::shadow:dark  [winfo rgb $c $color]
  set light [eval color::shadow:light [winfo rgb $c $color]
  puts "dark  $color = [$dark\]"
  puts "light $color = [$light\]"

  $c create polygon \
    $x1 $y1 \
    $x2 $y1 \
    expr $x2 - $bd expr $y1 + $bd \
    expr $x1 + $bd expr $y1 + $bd \
    expr $x1 + $bd expr $y2 - $bd \
    $x1 $y2 \
    -fill    $light \
    -outline $light

  $c create polygon \
    $x2 $y1 \
    $x2 $y2 \
    $x1 $y2 \
    expr $x1 + $bd expr $y2 - $bd \
    expr $x2 - $bd expr $y2 - $bd \
    expr $x2 - $bd expr $y1 + $bd \
    -fill    $dark \
    -outline $dark

  $c create rectangle \
    expr $x1 + $bd expr $y1 + $bd \
    expr $x2 - $bd expr $y2 - $bd \
    -fill    $color \
    -outline $color

  return

}

  button .b -text "Hello World" -bg red -fg white -bd 10
  canvas .c

  pack .b
  pack .c -expand 1 -fill both

  update idletasks

  rect .c 10 10 \
    expr [winfo reqwidth  .b + 10] \
    expr [winfo reqheight .b + 10] \
    10 red

# ======================================================================

Note that RGB values are specified as 16-bit values, as returned from [winfo rgb].


Note that at least on Microsoft Windows, I changed the winfo width/height to winfo reqwidth/reqheight. Otherwise you get an incorrect width/height. (I would have thought the update would have taken care of this??) Brett Schwarz

Try binding to <Configure> and use %w and %h (more robust).

MGS [2003/04/08] - Yeah, the emphasis here was not on the correctness of the demo code :-) It was really just for a comparison of the hilight/shadow colors. I think I meant to use reqwidth/reqheight, but I was in a rush to get it posted, so now I've changed it.