############################################################################ # # File: hsvpick.icn # # Subject: Program to pick RGB or HSV colors # # Author: Gregg M. Townsend # # Date: November 14, 1994 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # hsvpick is a simple HSV color picker. The three sliders on the # left control red, green, blue; the sliders on the right control # hue, saturation, value. The equivalent hexadecimal specification # is displayed in the center. # ############################################################################ # # Requires: Version 9 graphics # ############################################################################ # # Links: button, slider, evmux, graphics # ############################################################################ link button link slider link evmux link graphics $define BevelWidth 2 $define WindowMargin 10 record valrec(r, g, b, h, s, v) global sl # the six sliders global val # the six values [0.0 - 1.0] global w, h, m, l # geometry options global sw # slider width global colr # selected color procedure main(args) local cwin, x, y, ww, hh # create window Window("size=420,300", args) m := WindowMargin # size of outside margins w := w := WAttrib("width") - 2 * m # usable width h := WAttrib("height") - 2 * m # usable height l := WAttrib("leading") # leading sw := 20 # set slider width # get mutable color to display the selected color # use a new binding to avoid disturbing fg/bg of &window. colr := NewColor(&window) | stop("can't allocate mutable color") cwin := Clone(&window) Bg(cwin, colr) # draw the area showing the color itself x := 4 * m + 3 * sw y := m ww := w - 6 * sw - 6 * m hh := h - m - 3 * l BevelRectangle(x, y, ww, hh, -BevelWidth) EraseArea(cwin, x+BevelWidth, y+BevelWidth, ww-2*BevelWidth, hh-2*BevelWidth) # set up sliders to control the colors val := valrec(0.75, 0.625, 0.50, 0.0, 0.0, 0.0) # initial values sl := valrec( slider(&window, setval, 1, m, m, sw, h, 0.0, val.r, 1.0), slider(&window, setval, 2, sw + 2 * m, m, sw, h, 0.0, val.g, 1.0), slider(&window, setval, 3, 2 * sw + 3 * m, m, sw, h, 0.0, val.b, 1.0), slider(&window, setval, 4, w - m - 3 * sw, m, sw, h, 0.0, val.h, 1.0), slider(&window, setval, 5, w - 2 * sw, m, sw, h, 0.0, val.s, 1.0), slider(&window, setval, 6, w + m - sw, m, sw, h, 0.0, val.v, 1.0)) sethsv() # set hsv from rgb setcolor() # download the colors # set up sensors for quitting quitsensor(&window) # q or Q button(&window, "QUIT", argless, exit, m + w / 2 - 30, m + h - 20, 60, 20) # enter event loop evmux(&window) end procedure setval(win, i, v) # set color component i to value v val[i] := v if i < 4 then sethsv() # rgb slider moved; set hsv values else setrgb() # hsv slider moved; set rgv values setcolor() # set color, update display return end procedure sethsv() # set hsv from rgb values # based on Foley et al, 2/e, p.592 local min, max, d min := val.r; min >:= val.g; min >:= val.b # minimum max := val.r; max <:= val.g; max <:= val.b # maximum d := max - min # difference val.v := max # v is max of all values if max > 0 then val.s := d / max else val.s := 0 # sat is (max-min)/max if val.s > 0 then { if val.g = max then val.h := 2 + (val.b - val.r) / d # yellow through cyan else if val.b = max then val.h := 4 + (val.r - val.g) / d # cyan through magenta else if val.g < val.b then val.h := 6 + (val.g - val.b) / d # magenta through red else val.h := (val.g - val.b) / d # red through yellow } val.h /:= 6 # scale to 0.0 - 1.0 # set sliders to reflect calculated values slidervalue(sl.h, val.h) slidervalue(sl.s, val.s) slidervalue(sl.v, val.v) return end procedure setrgb() # set rgb from hsv values # based on Foley et al, 2/e, p.593 local h, f, i, p, q, t, v if val.s = 0.0 then val.r := val.g := val.b := val.v # achromatic else { h := val.h * 6.0 # hue [0.0 - 6.0) if h >= 6.0 then h := 0.0 i := integer(h) f := h - i v := val.v p := val.v * (1.0 - val.s) q := val.v * (1.0 - f * val.s) t := val.v * (1.0 - (1.0 - f) * val.s) case i of { 0: { val.r := v; val.g := t; val.b := p } # red - yellow 1: { val.r := q; val.g := v; val.b := p } # yellow - green 2: { val.r := p; val.g := v; val.b := t } # green - cyan 3: { val.r := p; val.g := q; val.b := v } # cyan - blue 4: { val.r := t; val.g := p; val.b := v } # blue - magenta 5: { val.r := v; val.g := p; val.b := q } # magenta - red } } # set sliders to reflect calculated values slidervalue(sl.r, val.r) slidervalue(sl.g, val.g) slidervalue(sl.b, val.b) return end procedure setcolor() # set the color in the color map local s, x # build and display hex color spec, and set color s := "#" || hexv(val.r) || hexv(val.g) || hexv(val.b) Color(colr, s) GotoXY(m + w / 2 - TextWidth(s) / 2, m + h - 2 * l) WWrites(s) # display r, g, b values x := 4 * m + 3 * sw GotoXY(x, m + h - 2 * l) WWrites("r: ", right(integer(65535 * val.r), 5)) GotoXY(x, m + h - l) WWrites("g: ", right(integer(65535 * val.g), 5)) GotoXY(x, m + h) WWrites("b: ", right(integer(65535 * val.b), 5)) # display h, s, v values x := w - 2 * m - 3 * sw - TextWidth("h: 000") GotoXY(x, m + h - 2 * l) WWrites("h: ", right(integer(360 * val.h), 3)) GotoXY(x, m + h - l) WWrites("s: ", right(integer(100 * val.s), 3)) GotoXY(x, m + h) WWrites("v: ", right(integer(100 * val.v), 3)) return end procedure hexv(v) # two-hex-digit specification of v static hextab initial { every put((hextab := []), !"0123456789ABCDEF" || !"0123456789ABCDEF") } return hextab [integer(255 * v + 1.5)] end