summaryrefslogtreecommitdiff
path: root/ipl/gprocs/color.icn
diff options
context:
space:
mode:
Diffstat (limited to 'ipl/gprocs/color.icn')
-rw-r--r--ipl/gprocs/color.icn526
1 files changed, 526 insertions, 0 deletions
diff --git a/ipl/gprocs/color.icn b/ipl/gprocs/color.icn
new file mode 100644
index 0000000..615ca05
--- /dev/null
+++ b/ipl/gprocs/color.icn
@@ -0,0 +1,526 @@
+############################################################################
+#
+# File: color.icn
+#
+# Subject: Procedures dealing with colors
+#
+# Author: Gregg M. Townsend
+#
+# Date: April 1, 1997
+#
+############################################################################
+#
+# This file is in the public domain.
+#
+############################################################################
+#
+# These procedures deal with colors in various ways.
+#
+# ScaleGamma(v, g) scales a number with gamma correction.
+#
+# Blend(k1, k2, ...) generates a sequence of colors.
+#
+# Contrast(win, k) returns "white" or "black" contrasting with k.
+#
+# Shade(win, k) sets Fg(), with dithering on a bilevel screen.
+#
+# RandomColor(W, p) returns a randomly chosen color from a palette.
+#
+# PaletteGrays(W, p) returns the gray entries of a palette.
+#
+# RGBKey(W, p, r, g, b) returns the palette key closest to (r,g,b).
+#
+# HSVKey(W, p, h, s, v) returns the palette key closest to (h/s/v).
+#
+# HSV(k) returns the h/s/v interpretation of a color.
+#
+# HSVValue(hsv) returns the ColorValue() of an h/s/v string.
+#
+# HLS(k) returns the h:l:s interpretation of a color.
+#
+# HLSValue(hls) returns the ColorValue() of an h:l:s string.
+#
+############################################################################
+#
+# ScaleGamma(v, g) nonlinearly scales the number v (between 0.0 and 1.0)
+# to an integer between 0 and 65535 using a gamma correction factor g.
+# the default value of g is 2.5.
+#
+# Blend(color1, color2, color3,...) generates ColorValue(color1), then
+# some intermediate shades, then ColorValue(color2), then some more
+# intermediate shades, and so on, finally generating the color value of
+# the last argument. An integer argument can be interpolated at any
+# point to set the number of steps (the default is four) from one color
+# to the next.
+#
+# Contrast(win, colr) returns either "white" or "black", depending
+# on which provides the greater contrast with the specified color.
+#
+# Shade(win, colr) sets the foreground for an area filling operation.
+# On a color screen, Shade() sets the foreground color and returns the
+# window. On a bilevel monochrome screen, Shade() sets the foreground
+# to a magic-square dithering pattern approximating the luminance of the
+# color specified. If the environment variable XSHADE is set to "gray"
+# (or "grey") then Shade simulates a multilevel grayscale monitor.
+# If it is set to any other value, Shade simulates a bilevel monitor.
+#
+# RandomColor(win, palette) returns a randomly chosen color from the
+# given image palette, excluding the "extra" grays of the palette, if
+# any. (Colors are selected from a small finite palette, rather than
+# from the entire color space, to avoid running out of colors if a
+# large number of random choices are desired.) The default palette
+# for this procedure is "c6".
+#
+# PaletteGrays([win,] palette) is like PaletteChars but it returns only
+# the characters corresponding to shades of gray. The characters are
+# ordered from black to white, and in all palettes the shades of gray
+# are equally spaced.
+#
+# RGBKey([win,] palette, r, g, b) returns a palette key given the
+# three color components as real number from 0.0 to 1.0.
+# HSVKey([win,] palette, h, s, v) returns a palette key given a
+# hue, saturation, and value as real numbers from 0.0 to 1.0.
+#
+# HSV() and HSVValue() convert between Icon color strings and strings
+# containing slash-separated HSV values with maxima of "360/100/100".
+# HSV(k) returns the h/s/v interpretation of an Icon color specification;
+# HSVValue(hsv) translates an h/s/v value into an Icon r,g,b value.
+#
+# HLS() and HLSValue() convert between Icon color strings and strings
+# containing colon-separated HLS values with maxima of "360:100:100".
+# HLS(k) returns the h:l:s interpretation of an Icon color specification;
+# HLSValue(hls) translates an h:l:s value into an Icon r,g,b value.
+#
+############################################################################
+#
+# Requires: Version 9 graphics
+#
+############################################################################
+
+
+# ScaleGamma(v, g) -- scale fraction to int with gamma correction.
+
+procedure ScaleGamma(v, g) #: scale with gamma correction
+ /g := 2.5
+ return integer(65535 * v ^ (1.0 / g))
+end
+
+
+# Blend(color1, color2, ...) -- generate sequence of colors
+
+procedure Blend(args[]) #: generate sequence of colors
+ local win, n, s, a, i, f1, f2, r1, g1, b1, r2, g2, b2, r3, g3, b3
+ static type
+
+ initial type := proc("type", 0) # protect attractive name
+
+ n := 4
+ if type(args[1]) == "window" then
+ win := get(args)
+ else
+ win := &window
+
+ while a := get(args) do
+ if integer(a) >= 0 then
+ n := integer(a)
+ else {
+ s := ColorValue(win, a) | fail
+ s ? {
+ r2 := tab(many(&digits)); move(1)
+ g2 := tab(many(&digits)); move(1)
+ b2 := tab(many(&digits))
+ }
+ if /r1 then
+ suspend s
+ else
+ every i := 1 to n do {
+ f2 := real(i) / real(n)
+ f1 := 1.0 - f2
+ r3 := integer(f1 * r1 + f2 * r2)
+ g3 := integer(f1 * g1 + f2 * g2)
+ b3 := integer(f1 * b1 + f2 * b2)
+ suspend r3 || "," || g3 || "," || b3
+ }
+ r1 := r2
+ g1 := g2
+ b1 := b2
+ }
+end
+
+
+# Contrast(win, color) -- return "white" or "black" to maximize contrast
+
+procedure Contrast(win, color) #: choose contrasting color
+ static l, type
+ initial {
+ l := ["white", "black"]
+ type := proc("type", 0) # protect attractive name
+ }
+
+ if type(win) == "window" then
+ return l[1 + PaletteKey(win, "g2", color)]
+ else
+ return l[1 + PaletteKey("g2", win)]
+end
+
+
+# Shade(win, color) -- approximate a shade with a pattern if bilevel screen
+
+procedure Shade(win, color) #: dither shade using pattern
+ local r, g, b
+ static dmat, env, type
+
+ initial {
+ env := ("" ~== map(getenv("XSHADE")))
+ type := proc("type", 0) # protect attractive name
+ }
+
+ if type(win) ~== "window" then {
+ color := win
+ win := &window
+ }
+ if WAttrib(win, "depth") ~== "1" & /env then {
+ Fg(win, color) | fail
+ return win
+ }
+ (ColorValue(win, color) | fail) ? {
+ r := tab(many(&digits)); move(1)
+ g := tab(many(&digits)); move(1)
+ b := tab(many(&digits))
+ }
+ g := integer(0.30 * r + 0.59 * g + 0.11 * b)
+
+ if \env == ("gray" | "grey") then {
+ Fg(win, g || "," || g || "," || g)
+ return win
+ }
+
+ /dmat := [
+ "4,15,15,15,15",
+ "4,15,15,13,15",
+ "4,11,15,13,15",
+ "4,10,15,13,15",
+ "4,10,15,5,15",
+ "4,10,7,5,15",
+ "4,10,7,5,14",
+ "4,10,7,5,10",
+ "4,10,5,5,10",
+ "4,10,5,5,2",
+ "4,10,4,5,2",
+ "4,10,0,5,2",
+ "4,10,0,5,0",
+ "4,8,0,5,0",
+ "4,8,0,1,0",
+ "4,8,0,0,0",
+ "4,0,0,0,0",
+ ]
+ WAttrib(win, "fillstyle=textured")
+ g := g / 3856 + 1
+ Pattern(win, dmat[g])
+ return win
+end
+
+
+# RandomColor(win, palette) -- choose random color
+
+procedure RandomColor(win, palette) #: choose random color
+ local s, n
+ static type
+
+ initial type := proc("type", 0) # protect attractive name
+
+ if type(win) ~== "window" then
+ palette:= win # window allowed but ignored
+ /palette := "c6"
+
+ s := PaletteChars(palette)
+ palette ?
+ if ="c" & any('23456') then {
+ n := integer(move(1))
+ s := s[1 +: n * n * n]
+ }
+ return PaletteColor(palette, ?s)
+
+end
+
+
+# PaletteGrays(win, palette) -- return grayscale entries from palette.
+
+procedure PaletteGrays(win, palette) #: grayscale entries from palette
+ static type
+
+ initial type := proc("type", 0) # protect attractive name
+ if (type(win) ~== "window") then
+ palette := win # window not needed
+
+ palette := string(palette) | runerr(103, palette)
+
+ if palette ? ="g" then
+ return PaletteChars(palette)
+
+ return case palette of {
+ "c1": "0123456"
+ "c2": "kxw"
+ "c3": "@abMcdZ"
+ "c4": "0$%&L*+-g/?@}"
+ "c5": "\0}~\177\200\37\201\202\203\204>\205\206\207\210]_
+ \211\212\213\214|"
+ "c6": "\0\330\331\332\333\334+\335\336\337\340\341V\342\343\344\345_
+ \346\201\347\350\351\352\353\254\354\355\356\357\360\327"
+ default: fail
+ }
+end
+
+
+# RGBKey(win, palette, r, g, b) -- find key given real-valued color
+
+procedure RGBKey(win, palette, r, g, b) #: return palette key for color
+ static type
+
+ initial type := proc("type", 0) # protect attractive name
+ if type(win) ~== "window" then # allow unused window argument
+ win :=: palette :=: r :=: g :=: b
+ r := integer(r * 65535.99)
+ g := integer(g * 65535.99)
+ b := integer(b * 65535.99)
+ return PaletteKey(palette, r || "," || g || "," || b)
+end
+
+
+# HSVKey(win, palette, h, s, v) -- find nearest key from h,s,v in [0.0,1.0]
+#
+# HSV conversion based on Foley et al, 2/e, p.593
+
+procedure HSVKey(win, palette, h, s, v) #: nearest key from HSV specification
+ local i, f, p, q, t, r, g, b
+ static type
+
+ initial type := proc("type", 0) # protect attractive name
+ if type(win) ~== "window" then # allow unused window argument
+ win :=: palette :=: h :=: s :=: v
+
+ if s = 0.0 then # achromatic case
+ return RGBKey(palette, v, v, v)
+
+ h *:= 6.0 # hue [0.0 - 6.0)
+ if h >= 6.0 then
+ h := 0.0
+
+ i := integer(h)
+ f := h - i
+ p := v * (1.0 - s)
+ q := v * (1.0 - f * s)
+ t := v * (1.0 - (1.0 - f) * s)
+
+ case i of {
+ 0: { r := v; g := t; b := p } # red - yellow
+ 1: { r := q; g := v; b := p } # yellow - green
+ 2: { r := p; g := v; b := t } # green - cyan
+ 3: { r := p; g := q; b := v } # cyan - blue
+ 4: { r := t; g := p; b := v } # blue - magenta
+ 5: { r := v; g := p; b := q } # magenta - red
+ }
+
+ return RGBKey(palette, r, g, b)
+end
+
+
+# HSV(k) -- return h/s/v interpretation of color spec.
+#
+# h is hue (0 <= h < 360)
+# s is saturation (0 <= s <= 100)
+# v is value (0 <= v <= 100)
+#
+# based on Foley et al, 2/e, p.592
+
+procedure HSV(k) #: HSV interpretation of color
+ local r, g, b, h, s, v, min, max, d
+
+ (ColorValue(k) | fail) ? {
+ r := tab(many(&digits)) / 65535.0
+ move(1)
+ g := tab(many(&digits)) / 65535.0
+ move(1)
+ b := tab(many(&digits)) / 65535.0
+ }
+
+ min := r; min >:= g; min >:= b # minimum
+ max := r; max <:= g; max <:= b # maximum
+ d := max - min # difference
+
+ v := max # value is max of all values
+ if max > 0 then
+ s := d / max # saturation is (max-min)/max
+ else
+ s := 0.0
+
+ if s = 0 then
+ h := 0.0 # use hue 0 if unsaturated
+ else if g = max then
+ h := 2 + (b - r) / d # yellow through cyan
+ else if b = max then
+ h := 4 + (r - g) / d # cyan through magenta
+ else if g < b then
+ h := 6 + (g - b) / d # magenta through red
+ else
+ h := (g - b) / d # red through yellow
+
+ return integer(60 * h + 0.5) || "/" ||
+ integer(100 * s + 0.5) || "/" || integer(100 * v + 0.5)
+end
+
+
+# HSVValue(hsv) -- return ColorValue of h/s/v string
+#
+# h is hue (0 <= h <= 360)
+# s is saturation (0 <= s <= 100)
+# v is value (0 <= v <= 100)
+#
+# based on Foley et al, 2/e, p.593
+
+procedure HSVValue(hsv) #: color value of HSV specification
+ local h, s, v, r, g, b, i, f, p, q, t
+
+ hsv ? {
+ h := tab(many(&digits)) / 360.0 | fail
+ ="/" | fail
+ s := tab(many(&digits)) / 100.0 | fail
+ ="/" | fail
+ v := tab(many(&digits)) / 100.0 | fail
+ pos(0) | fail
+ }
+ if (h | s | v) > 1 then fail
+
+ if s = 0.0 then { # achromatic case
+ v := integer(65535 * v + 0.499999)
+ return v || "," || v || "," || v
+ }
+
+ h *:= 6.0 # hue [0.0 - 6.0)
+ if h >= 6.0 then
+ h := 0.0
+
+ i := integer(h)
+ f := h - i
+ p := v * (1.0 - s)
+ q := v * (1.0 - f * s)
+ t := v * (1.0 - (1.0 - f) * s)
+
+ case i of {
+ 0: { r := v; g := t; b := p } # red - yellow
+ 1: { r := q; g := v; b := p } # yellow - green
+ 2: { r := p; g := v; b := t } # green - cyan
+ 3: { r := p; g := q; b := v } # cyan - blue
+ 4: { r := t; g := p; b := v } # blue - magenta
+ 5: { r := v; g := p; b := q } # magenta - red
+ }
+
+ return integer(65535 * r + 0.499999) || "," ||
+ integer(65535 * g + 0.499999) || "," || integer(65535 * b + 0.499999)
+end
+
+
+# HLS(k) -- return h:l:s interpretation of color spec.
+#
+# h is hue (0 <= h < 360)
+# l is lightness (0 <= l <= 100)
+# s is saturation (0 <= s <= 100)
+#
+# based on Foley et al, 2/e, p.595
+
+procedure HLS(k) #: HLS interpretation of color
+ local r, g, b, h, l, s, min, max, delta
+
+ (ColorValue(k) | fail) ? {
+ r := tab(many(&digits)) / 65535.0
+ move(1)
+ g := tab(many(&digits)) / 65535.0
+ move(1)
+ b := tab(many(&digits)) / 65535.0
+ }
+
+ min := r; min >:= g; min >:= b # minimum
+ max := r; max <:= g; max <:= b # maximum
+ delta := max - min # difference
+
+ l := (max + min) / 2 # lightness
+
+ if max = min then
+ h := s := 0 # achromatic
+
+ else {
+
+ if l <= 0.5 then
+ s := delta / (max + min) # saturation
+ else
+ s := delta / (2 - max - min)
+
+ if r = max then
+ h := (g - b) / delta # yellow through magenta
+ else if g = max then
+ h := 2 + (b - r) / delta # cyan through yellow
+ else # b = max
+ h := 4 + (r - g) / delta # magenta through cyan
+ if h < 0 then
+ h +:= 6 # ensure positive value
+ }
+
+ return integer(60 * h + 0.5) || ":" ||
+ integer(100 * l + 0.5) || ":" || integer(100 * s + 0.5)
+end
+
+
+# HLSValue(hls) -- return ColorValue of h:l:s string
+#
+# h is hue (0 <= h <= 360)
+# l is lightness (0 <= l <= 100)
+# s is saturation (0 <= s <= 100)
+#
+# based on Foley & Van Dam, 1/e, p.619
+
+procedure HLSValue(hls) #: color value of HLS specification
+ local h, l, s, r, g, b, m1, m2
+
+ hls ? {
+ h := tab(many(&digits)) / 360.0 | fail
+ =":" | fail
+ l := tab(many(&digits)) / 100.0 | fail
+ =":" | fail
+ s := tab(many(&digits)) / 100.0 | fail
+ pos(0) | fail
+ }
+ if (h | l | s) > 1 then fail
+
+ if l <= 0.5 then
+ m2 := l * (1 + s)
+ else
+ m2 := l + s - (l * s)
+ m1 := 2 * l - m2
+
+ if s = 0.0 then
+ r := g := b := l # achromatic
+ else {
+ r := hls_rgb_val(m1, m2, h + 0.3333333)
+ g := hls_rgb_val(m1, m2, h)
+ b := hls_rgb_val(m1, m2, h - 0.3333333)
+ }
+
+ return integer(65535 * r + 0.499999) || "," ||
+ integer(65535 * g + 0.499999) || "," || integer(65535 * b + 0.499999)
+end
+
+procedure hls_rgb_val(n1, n2, hue) # helper function for HLSValue
+ hue *:= 6
+ if hue >= 6 then
+ hue -:= 6
+ else if hue < 0 then
+ hue +:= 6
+ if (hue < 1) then
+ return n1 + (n2 - n1) * hue
+ else if (hue < 3) then
+ return n2
+ else if (hue < 4) then
+ return n1 + (n2 - n1) * (4 - hue)
+ else
+ return n1
+end