############################################################################ # # File: colrmodl.icn # # Subject: Procedures to convert between color models # # Author: Ralph E. Griswold # # Date: December 5, 1995 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # These procedures convert between various color models. A color # value is represented by a record (see the declarations below). # # Color values are normalized to a maximum of 1.0. # ############################################################################ # # Acknowledgement: Some of the procedures here are based on information # given in Computer Graphics; Principles and Practice, second edition; # James D. Foley, Andries van Dam, Steven K. Feiner, and John F. Hughes; # Addison-Wesley Publishing Company; 1990. # ############################################################################ # # Note: These procedures have not been extensively tested. Those related # to the YIQ model are particularly in question. # ############################################################################ # # Links: matrix, numbers # ############################################################################ link matrix link numbers record rgb(r, g, b) record cmy(c, m, y) record cmyk(c, m, y, k) record yiq(y, i, q) record hsv(h, s, v) record hls(h, l, s) procedure rgb2cmy(color) return cmy(1.0 - color.r, 1.0 - color.g, 1.0 - color.b) end procedure cmy2rgb(color) return rgb(1.0 - color.c, 1.0 - color.m, 1.0 - color.y) end # Note: The following procedure illustrates the principle of # undercolor removal, but for pragmatic reasons, it does not # produce acceptable results in process printing. procedure cmy2cmyk(color) local k k := min(color.c, color.m, color.y) return cmyk(color.c - k, color.m - k, color.y - k, k) end procedure cmyk2cmy(color) local kdelta kdelta := color.k / 3 return cmy(color.c + kdelta, color.m + kdelta, color.y + kdelta) end # # Note: The RGB specification is assumed to be based on the standard # NTSC phosphors. See the reference cited above. procedure rgb2yiq(color) static M, R, Y initial { M := create_matrix(3, 3) M[1, 1] := 0.299 M[1, 2] := 0.587 M[1, 3] := 0.114 M[2, 1] := 0.596 M[2, 2] := -0.275 M[2, 3] := -0.321 M[3, 1] := 0.212 M[3, 2] := -0.528 M[3, 3] := 0.311 } R := create_matrix(3, 1) R[1][1] := color.r R[2][1] := color.g R[3][1] := color.b Y := mult_matrix(M, R) return yiq(Y[1][1], Y[2][1], Y[3][1]) end procedure yiq2rgb(color) static M, R, Y initial { M := create_matrix(3, 3) M[1, 1] := 1.0031 M[1, 2] := 0.9548 M[1, 3] := 0.6179 M[2, 1] := 0.9968 M[2, 2] := -0.2707 M[2, 3] := -0.6448 M[3, 1] := 1.0084 M[3, 2] := -1.1005 M[3, 3] := 1.6996 } Y := create_matrix(3, 1) Y[1][1] := color.y Y[2][1] := color.i Y[3][1] := color.q R := mult_matrix(M, Y) return rgb(R[1][1], R[2][1], R[3][1]) end procedure rgb2hsv(color) local maximum, minimum, delta, h, s, v maximum := max(color.r, color.g, color.b) minimum := min(color.r, color.g, color.b) delta := maximum - minimum v := maximum if maximum ~= 0 then s := delta / maximum else s := 0 if s = 0 then h := -1.0 # undefined else { if color.r = maximum then { h := (color.g - color.b) / delta } else if color.g = maximum then { h := 2 + (color.b - color.r) / delta } else if color.b = maximum then { h := 4 + (color.r - color.g) / delta } h := h * 60 if h < 0 then h +:= 360.0 # make sure hue is nonnegative } return hsv(h, s, v) end procedure hsv2rgb(color) local h, i, f, p, q, t, s, v if color.s = 0 then { if color.h = -1 then { return rgb(color.v, color.v, color.v) } else stop("*** error in HSV to RGB conversion") } else { h := color.h v := color.v s := color.s if h = 360.0 then h := 0.0 h /:= 60 i := floor(h) f := h - i p := v * (1.0 - s) q := v * (1.0 - s * f) t := v * (1.0 - (s * (1.0 - f))) return case i of { 0: rgb(v, t, p) 1: rgb(q, v, p) 2: rgb(p, v, t) 3: rgb(p, q, v) 4: rgb(t, p, v) 5: rgb(v, p, q) default: stop("*** error in HSV to RGB conversion") } } end procedure rgb2hls(color) local maximum, minimum, delta, sum, h, s, l maximum := max(color.r, color.b, color.g) minimum := min(color.r, color.b, color.g) delta := maximum - minimum sum := maximum + minimum l := sum / 2 # lightness if maximum = minimum then { # achromatic case s := 0.0 h := -1.0 } else { if l <= 0.5 then s := delta / sum else s := delta / (2 - sum) if color.r = maximum then h := (color.g - color.r) / delta else if color.g = maximum then h := 2 + (color.b - color.r) / delta else if color.b = maximum then h := 4 + (color.r - color.g) / delta h *:= 60 # convert to degrees if h < 0.0 then h +:= 360.0 # make positive return hls(h, l, s) } end procedure hls2rgb(color) local h, l, s, m1, m2 h := color.h l := color.l s := color.s if l <= 0.5 then m2 := l * (1 + s) else m2 := l + s - l * s m1 := 2 * l - m2 if s = 0 then { # achromatic case if h = -1.0 then return rgb(l, l, l) else stop("*** error in HLS specification") } else { return rgb( color_value(m1, m2, h + 120.0), color_value(m1, m2, h), color_value(m1, m2, h - 120.0) ) } end procedure color_value(m1, m2, h) if h > 360.0 then h -:= 360.0 else if h < 0.0 then h +:= 360.0 if h < 60.0 then return m1 + (m2 - m1) * h / 60.0 else if h < 180.0 then return m2 else if h < 240.0 then return m1 + (m2 - m1) * (240.0 - h) / 60.0 else return m1 end