# # Nim's Runtime Library # (c) Copyright 2010 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements color handling for Nim. It is used by ## the ``graphics`` module. import strutils type Color* = distinct int ## a color stored as RGB {.deprecated: [TColor: Color].} proc `==` *(a, b: Color): bool {.borrow.} ## compares two colors. template extract(a: Color, r, g, b: expr) {.immediate.}= var r = a.int shr 16 and 0xff var g = a.int shr 8 and 0xff var b = a.int and 0xff template rawRGB(r, g, b: int): expr = Color(r shl 16 or g shl 8 or b) template colorOp(op: expr) {.immediate.} = extract(a, ar, ag, ab) extract(b, br, bg, bb) result = rawRGB(op(ar, br), op(ag, bg), op(ab, bb)) proc satPlus(a, b: int): int {.inline.} = result = a +% b if result > 255: result = 255 proc satMinus(a, b: int): int {.inline.} = result = a -% b if result < 0: result = 0 proc `+`*(a, b: Color): Color = ## adds two colors: This uses saturated artithmetic, so that each color ## component cannot overflow (255 is used as a maximum). colorOp(satPlus) proc `-`*(a, b: Color): Color = ## subtracts two colors: This uses saturated artithmetic, so that each color ## component cannot overflow (255 is used as a maximum). colorOp(satMinus) proc extractRGB*(a: Color): tuple[r, g, b: range[0..255]] = ## extracts the red/green/blue components of the color `a`. result.r = a.int shr 16 and 0xff result.g = a.int shr 8 and 0xff result.b = a.int and 0xff proc intensity*(a: Color, f: float): Color = ## returns `a` with intensity `f`. `f` should be a float from 0.0 (completely ## dark) to 1.0 (full color intensity). var r = toInt(toFloat(a.int shr 16 and 0xff) * f) var g = toInt(toFloat(a.int shr 8 and 0xff) * f) var b = toInt(toFloat(a.int and 0xff) * f) if r >% 255: r = 255 if g >% 255: g = 255 if b >% 255: b = 255 result = rawRGB(r, g, b) template mix*(a, b: Color, fn: expr): expr = ## uses `fn` to mix the colors `a` and `b`. `fn` is invoked for each component ## R, G, and B. This is a template because `fn` should be inlined and the ## compiler cannot inline proc pointers yet. If `fn`'s result is not in the ## range[0..255], it will be saturated to be so. template `><` (x: expr): expr = # keep it in the range 0..255 block: var y = x # eval only once if y >% 255: y = if y < 0: 0 else: 255 y (bind extract)(a, ar, ag, ab) (bind extract)(b, br, bg, bb) (bind rawRGB)(> 0: b = mid - 1 else: return mid result = - 1 proc parseColor*(name: string): Color = ## parses `name` to a color value. If no valid color could be ## parsed ``EInvalidValue`` is raised. if name[0] == '#': result = Color(parseHexInt(name)) else: var idx = binaryStrSearch(colorNames, name) if idx < 0: raise newException(ValueError, "unknown color: " & name) result = colorNames[idx][1] proc isColor*(name: string): bool = ## returns true if `name` is a known color name or a hexadecimal color ## prefixed with ``#``. if name[0] == '#': for i in 1 .. name.len-1: if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false result = true else: result = binaryStrSearch(colorNames, name) >= 0 proc rgb*(r, g, b: range[0..255]): Color = ## constructs a color from RGB values. result = rawRGB(r, g, b)