Gradient fills on a gnocl::pixbBuf

WJG (07/03/2010) Development of the gnocl::pixBuf module is shaping up nicely and promises to be a great addition to the forthcoming release due around Easter. Today, as part of the testing, I decided to explore the creation of gradient fills and so far so good. When I have time I'll recode these in C and present them as part of a more comprehensive gnocl image manipulation package. If anyone has code for a graduated file on at an arbitrary angle, please add below.

Here's the screenshot and test script.

http://lh5.ggpht.com/_yaFgKvuZ36o/S5QnozetJII/AAAAAAAAAcA/p2hiTZEtpLc/s800/Screenshot-gradients.tcl-1.png

#---------------
# gradients.tcl
#---------------
# author: William J Giddings
# date:   07/03/2010
#---------------

#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

package require Gnocl

# horizontal gradient
#   1 ------ 2
#   |        |
#   |        |
#   1 -------2
proc horizontal_gradient { pb c1 c2} {

    # get size of the buffer
    set w [$pb cget -width]
    set h [$pb cget -height]

    lassign $c1 r1 g1 b1
    lassign $c2 r2 g2 b2

    for {set x 0} {$x < $w} {incr x } {
        for {set y 0} {$y <$h} {incr y } {
            # puts "$x $y"
            set r3 [expr $r1 +  (($r2 - $r1) * $x / 64) ]
            if {$r3>=255} {set r3 255}

            set g3 [expr $g1 +  (($g2 - $g1) * $x / 64) ]
            if {$g3>=255} {set g3 255}

            set b3 [expr $b1 +  (($b2 - $b1) * $x / 64) ]
            if {$b3>=255} {set 3 255}

            $pb setPixel [list $x $y] [dec2rgb $r3 $g3 $b3]
        }
    }
}

# vertical gradient
#   1 ------ 1
#   |        |
#   |        |
#   2 -------2
proc vertical_gradient { pb c1 c2} {

    # get size of the buffer
    set w [$pb cget -width]
    set h [$pb cget -height]

    lassign $c1 r1 g1 b1
    lassign $c2 r2 g2 b2

    for {set y 0} {$y < 64} {incr y } {
        for {set x 0} {$x < 64} {incr x } {
            # puts "$x $y"
            set r3 [expr $r1+  (($r2 - $r1) * $y / 64) ]
            if {$r3>=255} {set r3 255}
            set g3 [expr $g1+  (($g2 - $g1) * $y / 64) ]
            if {$g3>=255} {set g3 255}
            set b3 [expr $b1+  (($b2 - $b1) * $y / 64) ]
            if {$b3>=255} {set 3 255}
            $pb setPixel [list $x $y] [dec2rgb $r3 $g3 $b3]
        }
    }
}

# four corners gradient fill
#   1 ------ 2
#   |        |
#   |        |
#   3 -------4
proc four_corners_gradient { pb c1 c2 c3 c4} {

    # get size of the buffer
    set w [$pb cget -width]
    set h [$pb cget -height]

    lassign $c1 r1 g1 b1
    lassign $c2 r2 g2 b2
    lassign $c3 r3 g3 b3
    lassign $c4 r4 g4 b4

    # working top to bottom, left to right
    for {set y 0} {$y < 64} {incr y } {

        # vertical component (1-3)
        set ra [expr $r1+  (($r3 - $r1) * $y / $h) ]
        if {$r3>=255} {set r3 255}

        set ga [expr $g1+  (($g3 - $g1) * $y / $h) ]
        if {$g3>=255} {set g3 255}

        set ba [expr $b1+  (($b3 - $b1) * $y / $h) ]
        if {$b3>=255} {set b3 255}

        # horizontal component (2-4)
        set rb [expr $r2+  (($r4 - $r2) * $y / $h ) ]
        if {$r3>=255} {set r3 255}

        set gb [expr $g2+  (($g4 - $g2) * $y / $h) ]
        if {$g3>=255} {set g3 255}

        set bb [expr $b2+  (($b4 - $b2) * $y / $h ) ]
        if {$b3>=255} {set b3 255}

        for {set x 0} {$x < $w} {incr x } {

            # calculate spread
            set r [expr $ra+  (($rb - $ra) * $x / $w) ]
            if {$r>=255} {set r 255}

            set g [expr $ga+  (($gb - $ga) * $x / $w) ]
            if {$g>=255} {set g 255}

            set b [expr $ba+  (($bb - $ba) * $x / $w) ]
            if {$b>=255} {set b 255}

            $pb setPixel [list $x $y] [dec2rgb $r $g $b]
        }

    }
}

# dec2rgb --
#
#   Takes a color name or dec triplet and returns a #RRGGBB color.
#   If any of the incoming values are greater than 255,
#   then 16 bit value are assumed, and #RRRRGGGGBBBB is
#   returned, unless $clip is set.
#
# Arguments:
#   r       red dec value, or list of {r g b} dec value or color name
#   g       green dec value, or the clip value, if $r is a list
#   b       blue dec value
#   clip    Whether to force clipping to 2 char hex
# Results:
#   Returns a #RRGGBB or #RRRRGGGGBBBB color
#
proc dec2rgb {r {g 0} {b UNSET} {clip 0}} {
    if {![string compare $b "UNSET"]} {
    set clip $g
    if {[regexp {^-?(0-9)+$} $r]} {
        lassign $r r g b
    } else {
        lassign [winfo rgb . $r] r g b
    }
    }
    set max 255
    set len 2
    if {($r > 255) || ($g > 255) || ($b > 255)} {
    if {$clip} {
        set r [expr {$r>>8}]; set g [expr {$g>>8}]; set b [expr {$b>>8}]
    } else {
        set max 65535
        set len 4
    }
    }
    return [format "#%.${len}X%.${len}X%.${len}X" \
        [expr {($r>$max)?$max:(($r<0)?0:$r)}] \
        [expr {($g>$max)?$max:(($g<0)?0:$g)}] \
        [expr {($b>$max)?$max:(($b<0)?0:$b)}]]
}


# the ubiquitous demo block
set pb1 [gnocl::pixBuf new {64 64 8} ]
set pb2 [gnocl::pixBuf new {64 64 8} ]
set pb3 [gnocl::pixBuf new {64 64 8} ]

set img1 [gnocl::image -image "%?$pb1"]
set img2 [gnocl::image -image "%?$pb2"]
set img3 [gnocl::image -image "%?$pb3"]

set box [gnocl::box -children [list $img1 $img2 $img3] ]

gnocl::window -child $box

# modify the buffer, and the image will be automatically updated
horizontal_gradient $pb1 {0 0 0 } {128 128 128}
vertical_gradient $pb2 {255 0 255} {0 255 0}
four_corners_gradient $pb3 {0 255 255} {255 0 0} {0 2550} {0 0 255}

gnocl::mainLoop