;;
;; chicken-web-colors:
;; Parse and write HTML/CSS color strings in CHICKEN Scheme.
;;
;; Copyright © 2019 John Croisant.
;; All rights reserved.
;;
;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;;
;; - Redistributions of source code must retain the above copyright
;; notice, this list of conditions and the following disclaimer.
;;
;; - Redistributions in binary form must reproduce the above copyright
;; notice, this list of conditions and the following disclaimer in
;; the documentation and/or other materials provided with the
;; distribution.
;;
;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
;; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
;; COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
;; INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
;; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
;; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
;; STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
;; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
;; OF THE POSSIBILITY OF SUCH DAMAGE.
(export +web-color-types+
color-list?
normalize-web-color
rgb-color->bytes)
(define +web-color-types+ '(rgb rgb% hsl))
(: color-list? (any #!optional (list-of symbol) --> boolean))
(define (color-list? x #!optional (types +web-color-types+))
(and (list? x)
(= 5 (length x))
(memq (car x) types)
(real? (list-ref x 1))
(real? (list-ref x 2))
(real? (list-ref x 3))
(real? (list-ref x 4))))
(: normalize-web-color (color-list --> color-list))
(define (normalize-web-color color)
(assert (color-list? color)
"Invalid color list" color)
(case (car color)
((rgb)
(list 'rgb
(%byte (list-ref color 1))
(%byte (list-ref color 2))
(%byte (list-ref color 3))
(%clamp (list-ref color 4) 0 1)))
((rgb%)
(list 'rgb%
(%clamp (list-ref color 1) 0 1)
(%clamp (list-ref color 2) 0 1)
(%clamp (list-ref color 3) 0 1)
(%clamp (list-ref color 4) 0 1)))
((hsl)
(list 'hsl
(%wrap (inexact->exact (round (list-ref color 1))) 360)
(%clamp (list-ref color 2) 0 1)
(%clamp (list-ref color 3) 0 1)
(%clamp (list-ref color 4) 0 1)))))
(: rgb-color->bytes (color-list --> (list fixnum fixnum fixnum fixnum)))
(define (rgb-color->bytes color)
(assert (color-list? color '(rgb rgb%))
"Invalid rgb color list" color)
(let ((scale (if (eq? 'rgb% (car color)) 255 1)))
(list (%byte (* scale (list-ref color 1)))
(%byte (* scale (list-ref color 2)))
(%byte (* scale (list-ref color 3)))
(%byte (* 255 (list-ref color 4))))))
(: %byte (number --> number))
(define (%byte n)
(%clamp (inexact->exact (round n)) 0 255))
(: %clamp (number number number --> number))
(define (%clamp n lower upper)
(cond ((<= n lower) lower)
((<= upper n) upper)
(else n)))
(: %wrap (number number --> number))
(define (%wrap n upper)
(let loop ((n n))
(cond ((and (<= 0 n) (< n upper)) n)
((< n 0) (loop (+ n upper)))
((<= upper n) (loop (- n upper))))))