summaryrefslogtreecommitdiff
path: root/ipl/gprocs/palettes.icn
diff options
context:
space:
mode:
Diffstat (limited to 'ipl/gprocs/palettes.icn')
-rw-r--r--ipl/gprocs/palettes.icn405
1 files changed, 405 insertions, 0 deletions
diff --git a/ipl/gprocs/palettes.icn b/ipl/gprocs/palettes.icn
new file mode 100644
index 0000000..a0f596e
--- /dev/null
+++ b/ipl/gprocs/palettes.icn
@@ -0,0 +1,405 @@
+############################################################################
+#
+# File: palettes.icn
+#
+# Subject: Procedures for programmer-defined palettes
+#
+# Author: Ralph E. Griswold
+#
+# Date: January 23, 2001
+#
+############################################################################
+#
+# This file is in the public domain.
+#
+############################################################################
+#
+# These procedures implement programmer-defined palettes. They overload
+# and build on top of the built-in palette mechanism.
+#
+############################################################################
+#
+# Data structures:
+#
+# Palette_() is a record that holds the information for a
+# programmer-defined palette. Its fields are:
+#
+# name: the name the palette is known by
+# keys: the string of the palette characters
+# table: a table keyed by the palette characters
+# whose corresponding values are the colors
+#
+# Color_() is a record that holds the components of an RGB
+# color in separate r, g, and b fields.
+#
+# PDB_ is a table whose keys are the names of programmer-
+# defined palettes and whose corresponding values are the
+# palettes. PDB_ is a global variable and provides the
+# way for programmer-defined palette procedures to access
+# a particular database. If it is null, a new database is
+# created.
+#
+# Procedures:
+#
+# BuiltinPalette(name)
+# succeeds if name is the name of a built-in palette but
+# fails otherwise.
+#
+# CreatePalette(name, keys, colors)
+# creates a new palette with the given colors and
+# corresponding keys. The colors used are the given ones.
+#
+# InitializePalettes()
+# initializes the built-in palette mechanism; it is called
+# by the first palette procedure that is called.
+#
+# Measure(color1, color2) returns the a measure of the distance
+# between color1 and color2 in RGB space.
+#
+# NearColor(name, color)
+# returns a color close to color in the palette name.
+#
+# PaletteChars(win, palette)
+# returns the palette characters of palette. It extends
+# the standard version.
+#
+# PaletteColor(win, palette, key)
+# returns color in palette for the given key. It extends
+# the standard version.
+#
+# PaletteKey(win, palette, color)
+# returns the key in palette closest to the given color.
+#
+# RGB(color)
+# parses RGB color and returns a corresponding record.
+#
+# makepalette(name, clist)
+# makes a palette from the list of colors, choosing
+# keys automatically.
+#
+# palette_colors(palette)
+#
+# returns the list of colors in palette.
+#
+# Procedures fail in case of errors. This leaves control and error
+# reporting to programs that use this module. This module is intended
+# to be used by programs that manage the necessary data and supply
+# the table through PDB_. The problem with this is that there is
+# no way to differentiate errors. A solution would be to post error
+# messages in a global variable.
+#
+# Limitations and problems:
+#
+# The names of built-in palettes may not be used for programmer-
+# defined ones.
+#
+# PaletteGrays() is not implemented for programmer-defined
+# palettes. The library version should work for built-in
+# palettes with this module linked.
+#
+# Transparency is not yet implemented for DrawImage().
+#
+# ReadImage() does not yet support programmer defined palettes.
+#
+# Not tested: Capture(), which may work.
+#
+# There is some library code that checks for the names of
+# built-in palettes in an ad-hoc fashion. It therefore is
+# not advisable to use names for programmer-defined palettes
+# that begin with "c" or "g" followed by a digit.
+#
+############################################################################
+#
+# Requires: Version 9 graphics
+#
+############################################################################
+#
+# Links: imrutils, lists, sort
+#
+############################################################################
+
+link imrutils
+link lists
+link sort
+
+global PDB_
+
+record Palette_(name, keys, table)
+record Color_(r, g, b)
+
+# Check for built-in palette
+
+procedure BuiltinPalette(name) #: check for built-in palette
+
+ BuiltinPalette := proc("PaletteChars", 0)
+
+ return BuiltinPalette(name)
+
+end
+
+procedure CreatePalette(name, keys, colors) #: create palette
+ local i, k, t
+
+ initial InitializePalettes()
+
+ if BuiltinPalette(name) then fail
+
+ if *keys ~= *cset(keys) then fail # duplicate keys
+
+ if *keys ~= *colors then fail # mismatch
+
+ t := table()
+
+ every i := 1 to *colors do
+ t[keys[i]] := ColorValue(colors[i]) | fail
+
+ PDB_[name] := Palette_(name, keys, t)
+
+ return PDB_[name]
+
+end
+
+# Extended version of DrawImage()
+
+procedure DrawImage(args[]) #: draw image
+ local palette_pixels, palette_lookup, keys, c, i, row, imr
+ static draw_image
+
+ initial draw_image := proc("DrawImage", 0)
+
+ if type(args[1]) ~== "window" then push(args, &window)
+
+ imr := imstoimr(args[4]) | return draw_image ! args
+
+ if BuiltinPalette(imr.palette) then return draw_image ! args
+
+ palette_lookup := (\PDB_[imr.palette]).table | fail
+ palette_pixels := copy(palette_lookup)
+
+ keys := cset(imr.pixels)
+
+ every !palette_pixels := [] # empty lists for coordinates
+
+ every c := !keys do {
+ i := 0
+ imr.pixels ? {
+ while row := move(imr.width) do {
+ row ? {
+ every put(palette_pixels[c], upto(c) - 1, i)
+ }
+ i +:= 1
+ }
+ }
+ }
+
+ every c := !keys do {
+ Fg(palette_lookup[c]) | fail # fails for invalid character
+ DrawPoint ! palette_pixels[c]
+ }
+
+ return
+
+end
+
+# Initialize defined palette mechanism
+
+procedure InitializePalettes() #: initialize palettes
+
+ /PDB_ := table()
+
+ if type(PDB_) ~== "table" then runerr(777)
+
+ InitializePalettes := 1 # make this procedure a no-op
+
+ return
+
+end
+
+procedure Measure(s1, s2) #: measure of RGB distance
+ local color1, color2
+
+ color1 := RGB(s1)
+ color2 := RGB(s2)
+
+ return (color1.r - color2.r) ^ 2 + (color1.g - color2.g) ^ 2 +
+ (color1.b - color2.b) ^ 2
+
+end
+
+# Get color close to specified key
+
+procedure NearColor(name, s) #: close color in palette
+ local palette_lookup, k, measure, close_key, color
+
+ measure := 3 * (2 ^ 16 - 1) ^ 2 # maximum
+
+ color := ColorValue(s) | fail
+
+ palette_lookup := (\PDB_[name]).table | fail
+
+ every k := key(palette_lookup) do
+ if measure >:= Measure(palette_lookup[k], color) then {
+ close_key := k
+ if measure = 0 then break
+ }
+
+ return \close_key
+
+end
+
+# Extended version of PaletteChars()
+
+procedure PaletteChars(args[]) #: characters in palette
+ local name
+ static palette_chars
+
+ initial {
+ InitializePalettes()
+ palette_chars := proc("PaletteChars", 0)
+ }
+
+ if type(args[1]) == "window" then get(args)
+
+ name := args[1]
+
+ if BuiltinPalette(name) then return palette_chars(name)
+ else return (\PDB_[name]).keys
+
+end
+
+# Extended version of PaletteColor()
+
+procedure PaletteColor(args[]) #: color for key in palette
+ local palette_lookup, name, s
+ static palette_color
+
+ initial {
+ InitializePalettes()
+ palette_color := proc("PaletteColor", 0)
+ }
+
+ if type(args[1]) == "window" then get(args)
+
+ name := args[1]
+ s := args[2]
+
+ if BuiltinPalette(name) then return palette_color(name, s)
+
+ palette_lookup := (\PDB_[name]).table | fail
+
+ return \palette_lookup[s]
+
+end
+
+# Extended version of PaletteKey()
+
+procedure PaletteKey(args[]) #: key for color in palette
+ local name, s
+ static palette_key
+
+ initial {
+ InitializePalettes()
+ palette_key := proc("PaletteKey", 0)
+ }
+
+ if type(args[1]) == "window" then get(args)
+
+ name := args[1]
+ s := args[2]
+
+ if BuiltinPalette(name) then return palette_key(name, s)
+ else return NearColor(name, s)
+
+end
+
+procedure RGB(s) #: convert RGB color to record
+ local color
+
+ color := Color_()
+
+ ColorValue(s) ? {
+ color.r := tab(upto(',')) &
+ move(1) &
+ color.g := tab(upto(',')) &
+ move(1) &
+ color.b := tab(0)
+ } | fail
+
+ return color
+
+end
+
+procedure makepalette(name, clist) #: make palette automatically
+ local keys
+ static alphan
+
+ initial alphan := &digits || &letters
+
+ if *clist = 0 then fail
+
+ keys :=
+ if *clist < *alphan then alphan
+ else &cset
+
+ CreatePalette(name, keys[1+:*clist], clist) | fail
+
+ return
+
+end
+
+procedure palette_colors(p) #: list of palette colors
+ local clist
+
+ clist := []
+
+ every put(clist, PaletteColor(p, !PaletteChars(p)))
+
+ return clist
+
+end
+
+procedure keyseq(palette, colors[]) #: sequence of palette keys
+ local chars
+
+ chars := PaletteChars(palette)
+
+ suspend upto(PaletteKey(palette, !colors), chars)
+
+end
+
+procedure color_range(color, range) #: adjust RGB range
+ local r, g, b
+
+ range := 2 ^ 16 / range
+
+ color ? {
+ r := tab(upto(','))
+ move(1)
+ g := tab(upto(','))
+ move(1)
+ b := tab(0)
+ return (r * range) || "," || (g * range) || "," || (b * range)
+ }
+
+end
+
+procedure colorseq(palette) #: sequence of palette colors
+
+ suspend PaletteColor(palette, !PaletteChars(palette))
+
+end
+
+procedure sort_colors(colors)
+
+ return isort(colors, value)
+
+end
+
+procedure value(s) #: RGB magnitude
+ local color
+
+ color := RGB(s)
+
+ return color.r ^ 2 + color.g ^ 2 + color.b ^ 2
+
+end