Merge remote-tracking branch 'sourceforge/master'
[emacs-jabber.git] / compat / hexrgb.el
blob57f2c2c3224522bdf13cec72a31e0f1881d5fa7a
1 ;;; hexrgb.el --- Functions to manipulate colors, including RGB hex strings.
2 ;;
3 ;; Filename: hexrgb.el
4 ;; Description: Functions to manipulate colors, including RGB hex strings.
5 ;; Author: Drew Adams
6 ;; Maintainer: Drew Adams
7 ;; Copyright (C) 2004-2009, Drew Adams, all rights reserved.
8 ;; Created: Mon Sep 20 22:58:45 2004
9 ;; Version: 21.0
10 ;; Last-Updated: Sat Nov 14 15:55:15 2009 (-0800)
11 ;; By: dradams
12 ;; Update #: 732
13 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/hexrgb.el
14 ;; Keywords: number, hex, rgb, color, background, frames, display
15 ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x
17 ;; Features that might be required by this library:
19 ;; None
21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
23 ;;; Commentary:
25 ;; Functions to manipulate colors, including RGB hex strings.
27 ;; This library provides functions for converting between RGB (red,
28 ;; green, blue) color components and HSV (hue, saturation, value)
29 ;; color components. It helps you convert among Emacs color values
30 ;; (whole numbers from 0 through 65535), RGB and HSV floating-point
31 ;; components (0.0 through 1.0), Emacs color-name strings (such as
32 ;; "blue"), and hex RGB color strings (such as "#FC43A7912").
34 ;; An RGB hex string, such as used as a frame `background-color'
35 ;; property, is a string of 1 + (3 * n) characters, the first of
36 ;; which is "#". The other characters are hexadecimal digits, in
37 ;; three groups representing (from the left): red, green, and blue
38 ;; hex codes.
40 ;; Constants defined here:
42 ;; `hexrgb-defined-colors', `hexrgb-defined-colors-alist',
43 ;; `hexrgb-defined-colors-no-dups',
44 ;; `hexrgb-defined-colors-no-dups-alist'.
46 ;; Options defined here:
48 ;; `hexrgb-canonicalize-defined-colors-flag'.
50 ;; Commands defined here:
52 ;; `hexrgb-blue', `hexrgb-complement', `hexrgb-green',
53 ;; `hexrgb-hue', `hexrgb-read-color', `hexrgb-red',
54 ;; `hexrgb-saturation', `hexrgb-value'.
56 ;; Non-interactive functions defined here:
58 ;; `hexrgb-approx-equal', `hexrgb-canonicalize-defined-colors',
59 ;; `hexrgb-color-name-to-hex', `hexrgb-color-values-to-hex',
60 ;; `hexrgb-color-value-to-float', `hexrgb-defined-colors',
61 ;; `hexrgb-defined-colors-alist',
62 ;; `hexrgb-delete-whitespace-from-string',
63 ;; `hexrgb-float-to-color-value', `hexrgb-hex-char-to-integer',
64 ;; `hexrgb-hex-to-color-values', `hexrgb-hex-to-hsv',
65 ;; `hexrgb-hex-to-rgb', `hexrgb-hsv-to-hex', `hexrgb-hex-to-int',
66 ;; `hexrgb-hsv-to-rgb', `hexrgb-increment-blue',
67 ;; `hexrgb-increment-equal-rgb', `hexrgb-increment-green',
68 ;; `hexrgb-increment-hex', `hexrgb-increment-red',
69 ;; `hexrgb-int-to-hex', `hexrgb-rgb-hex-string-p',
70 ;; `hexrgb-rgb-to-hex', `hexrgb-rgb-to-hsv'.
73 ;; Add this to your initialization file (~/.emacs or ~/_emacs):
75 ;; (require 'hexrgb)
77 ;; Do not try to use this library without a window manager.
78 ;; That is, do not use this with `emacs -nw'.
80 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
82 ;;; Change log:
84 ;; 2009/11/14 dadams
85 ;; hexrgb-rgb-to-hsv: Corrected hue when > 1.0. Use strict inequality for hue limit tests.
86 ;; hexrgb-approx-equal: Convert RFUZZ and AFUZZ to their absolute values.
87 ;; 2009/11/03 dadams
88 ;; Added: hexrgb-delete-whitespace-from-string, hexrgb-canonicalize-defined-colors,
89 ;; hexrgb-defined-colors(-no-dups)(-alist), hexrgb-canonicalize-defined-colors-flag.
90 ;; hexrgb-read-color: Use function hexrgb-defined-colors-alist, not the constant.
91 ;; 2008/12/25 dadams
92 ;; hexrgb-rgb-to-hsv:
93 ;; Replace (not (equal 0.0e+NaN saturation)) by standard test (= saturation saturation).
94 ;; Thx to Michael Heerdegen for the bug report.
95 ;; 2008-10-17 dadams
96 ;; hexrgb-defined-colors(-alist): Prevent load-time error if user tries to use emacs -nw.
97 ;; 2007/12/30 dadams
98 ;; Added: hexrgb-hex-to-color-values.
99 ;; 2007/10/20 dadams
100 ;; hexrgb-read-color: Treat pseudo colors too (e.g. *point foreground*).
101 ;; 2007/01/21 dadams
102 ;; hexrgb-read-color: Error if empty string (and not allow-empty-name-p).
103 ;; 2006/06/06 dadams
104 ;; Added: hexrgb-defined-colors(-alist). Use instead of (x-defined-colors).
105 ;; hexrgb-(red|green|blue): Added interactive specs.
106 ;; 2006/06/04 dadams
107 ;; hexrgb-read-color: Added optional arg allow-empty-name-p.
108 ;; 2006/06/02 dadams
109 ;; Added: hexrgb-rgb-hex-string-p. Used it.
110 ;; 2006/05/30 dadams
111 ;; Added: hexrgb-hex-to-(hsv|rgb), hexrgb-hsv-to-hex, hexrgb-color-name-to-hex,
112 ;; hexrgb-complement, hexrgb-read-color, hexrgb-hue, hexrgb-saturation,
113 ;; hexrgb-value, hexrgb-red, hexrgb-blue, hexrgb-green.
114 ;; approx-equal: Add optional fuzz factor arguments. Changed the algorithm.
115 ;; Renamed: approx-equal to hexrgb-approx-equal.
116 ;; hexrgb-rgb-to-hsv: Changed test from < to <=: (when (<= hue 0.0)...).
117 ;; hexrgb-hsv-to-rgb: Treat hue = 0.0 (int 0) the same as hue = 1.0 (int 6).
118 ;; hexrgb-rgb-to-hex, hexrgb-increment-hex: Corrected doc strings.
119 ;; 2006/05/22 dadams
120 ;; Added: hexrgb-hsv-to-hex, hexrgb-rgb-to-hex. Require cl.el when byte-compile.
121 ;; 2005/08/09 dadams
122 ;; hexrgb-rgb-to-hsv: Side-stepped Emacs-20 bug in comparing NaN.
123 ;; hexrgb-increment-*: Added optional arg wrap-p.
124 ;; hexrgb-increment-hex: Prevent wrap if not wrap-p.
125 ;; 2005/08/02 dadams
126 ;; hexrgb-rgb-to-hes: Bug fix: If delta is zero, then so are hue and saturation.
127 ;; 2005/06/24 dadams
128 ;; hexrgb-rgb-to-hsv: Bug fix: test for NaN (e.g. on divide by zero).
129 ;; 2005/02/08 dadams
130 ;; hexrgb-hsv-to-rgb: Bug fix (typo: p, q -> pp, qq; added ww).
131 ;; 2005/01/09 dadams
132 ;; hexrgb-int-to-hex: Fixed bug in hexrgb-int-to-hex: nb-digits not respected.
133 ;; Added: hexrgb-hsv-to-rgb, hexrgb-rgb-to-hsv, approx-equal.
134 ;; Renamed old hexrgb-increment-value to hexrgb-increment-equal-rgb.
135 ;; 2005/01/05 dadams
136 ;; hexrgb-int-to-hex: Used a suggestion from Juri Linkov.
138 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
140 ;; This program is free software; you can redistribute it and/or modify
141 ;; it under the terms of the GNU General Public License as published by
142 ;; the Free Software Foundation; either version 2, or (at your option)
143 ;; any later version.
145 ;; This program is distributed in the hope that it will be useful,
146 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
147 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
148 ;; GNU General Public License for more details.
150 ;; You should have received a copy of the GNU General Public License
151 ;; along with this program; see the file COPYING. If not, write to
152 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
153 ;; Floor, Boston, MA 02110-1301, USA.
155 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
157 ;;; Code:
159 (eval-when-compile (require 'cl)) ;; case; plus, for Emacs < 20: when, unless
161 ;; Unless you first load `hexrgb.el', then either `palette.el' or `eyedropper.el', you will get
162 ;; warnings about variables and functions with prefix `eyedrop-' when you byte-compile
163 ;; `hexrgb.el'. You can ignore these warnings.
165 (defvar eyedrop-picked-foreground)
166 (defvar eyedrop-picked-background)
168 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
170 ;;;###autoload
171 (eval-and-compile
172 (defun hexrgb-canonicalize-defined-colors (list)
173 "Copy of LIST with color names canonicalized.
174 LIST is a list of color names (strings).
175 Canonical names are lowercase, with no whitespace.
176 There are no duplicate names."
177 (let ((tail list)
178 this new)
179 (while tail
180 (setq this (car tail)
181 this (hexrgb-delete-whitespace-from-string (downcase this) 0 (length this)))
182 (unless (member this new) (push this new))
183 (pop tail))
184 (nreverse new)))
186 (defun hexrgb-delete-whitespace-from-string (string &optional from to)
187 "Remove whitespace from substring of STRING from FROM to TO.
188 If FROM is nil, then start at the beginning of STRING (FROM = 0).
189 If TO is nil, then end at the end of STRING (TO = length of STRING).
190 FROM and TO are zero-based indexes into STRING.
191 Character FROM is affected (possibly deleted). Character TO is not."
192 (setq from (or from 0)
193 to (or to (length string)))
194 (with-temp-buffer
195 (insert string)
196 (goto-char (+ from (point-min)))
197 (let ((count from)
198 char)
199 (while (and (not (eobp)) (< count to))
200 (setq char (char-after))
201 (if (memq char '(?\ ?\t ?\n)) (delete-char 1) (forward-char 1))
202 (setq count (1+ count)))
203 (buffer-string)))))
205 ;;;###autoload
206 (defconst hexrgb-defined-colors (eval-when-compile (and window-system (x-defined-colors)))
207 "List of all supported colors.")
209 ;;;###autoload
210 (defconst hexrgb-defined-colors-no-dups
211 (eval-when-compile
212 (and window-system (hexrgb-canonicalize-defined-colors (x-defined-colors))))
213 "List of all supported color names, with no duplicates.
214 Names are all lowercase, without any spaces.")
216 ;;;###autoload
217 (defconst hexrgb-defined-colors-alist
218 (eval-when-compile (and window-system (mapcar #'list (x-defined-colors))))
219 "Alist of all supported color names, for use in completion.
220 See also `hexrgb-defined-colors-no-dups-alist', which is the same
221 thing, but without any duplicates, such as \"light blue\" and
222 \"LightBlue\".")
224 ;;;###autoload
225 (defconst hexrgb-defined-colors-no-dups-alist
226 (eval-when-compile
227 (and window-system
228 (mapcar #'list (hexrgb-canonicalize-defined-colors (x-defined-colors)))))
229 "Alist of all supported color names, with no duplicates, for completion.
230 Names are all lowercase, without any spaces.")
232 ;;;###autoload
233 (defcustom hexrgb-canonicalize-defined-colors-flag t
234 "*Non-nil means remove duplicate color names.
235 Names are considered duplicates if they are the same when abstracting
236 from whitespace and letter case."
237 :type 'boolean
238 :group 'Icicles :group 'doremi-frame-commands :group 'faces :group 'convenience)
240 ;; You should use these two functions, not the constants, so users can change
241 ;; the behavior by customizing `hexrgb-canonicalize-defined-colors-flag'.
243 (defun hexrgb-defined-colors ()
244 "List of supported color names.
245 If `hexrgb-canonicalize-defined-colors-flag' is non-nil, then names
246 are lowercased, whitespace is removed, and there are no duplicates."
247 (if hexrgb-canonicalize-defined-colors-flag
248 hexrgb-defined-colors-no-dups
249 hexrgb-defined-colors))
251 (defun hexrgb-defined-colors-alist ()
252 "Alist of supported color names. Usable for completion.
253 If `hexrgb-canonicalize-defined-colors-flag' is non-nil, then names
254 are lowercased, whitespace is removed, and there are no duplicates."
255 (if hexrgb-canonicalize-defined-colors-flag
256 hexrgb-defined-colors-no-dups-alist
257 hexrgb-defined-colors-alist))
259 ;; RMS added this function to Emacs (23) as `read-color', with some feature loss.
260 ;;;###autoload
261 (defun hexrgb-read-color (&optional convert-to-RGB-p allow-empty-name-p prompt)
262 "Read a color name or RGB hex value: #RRRRGGGGBBBB.
263 Completion is available for color names, but not for RGB hex strings.
264 If you input an RGB hex string, it must have the form #XXXXXXXXXXXX or
265 XXXXXXXXXXXX, where each X is a hex digit. The number of Xs must be a
266 multiple of 3, with the same number of Xs for each of red, green, and
267 blue. The order is red, green, blue.
269 Color names that are normally considered equivalent are canonicalized:
270 They are lowercased, whitespace is removed, and duplicates are
271 eliminated. E.g. \"LightBlue\" and \"light blue\" are both replaced
272 by \"lightblue\". If you do not want this behavior, but want to
273 choose names that might contain whitespace or uppercase letters, then
274 customize option `hexrgb-canonicalize-defined-colors-flag' to nil.
276 In addition to standard color names and RGB hex values, the following
277 are available as color candidates. In each case, the corresponding
278 color is used.
280 * `*copied foreground*' - last copied foreground, if available
281 * `*copied background*' - last copied background, if available
282 * `*mouse-2 foreground*' - foreground where you click `mouse-2'
283 * `*mouse-2 background*' - background where you click `mouse-2'
284 * `*point foreground*' - foreground under the cursor
285 * `*point background*' - background under the cursor
287 \(You can copy a color using eyedropper commands such as
288 `eyedrop-pick-foreground-at-mouse'.)
290 Checks input to be sure it represents a valid color. If not, raises
291 an error (but see exception for empty input with non-nil
292 ALLOW-EMPTY-NAME-P).
294 Interactively, or with optional arg CONVERT-TO-RGB-P non-nil, converts
295 an input color name to an RGB hex string. Returns the RGB hex string.
297 Optional arg ALLOW-EMPTY-NAME-P controls what happens if you enter an
298 empty color name (that is, you just hit `RET'). If non-nil, then
299 `hexrgb-read-color' returns an empty color name, \"\". If nil, then
300 it raises an error. Programs must test for \"\" if ALLOW-EMPTY-NAME-P
301 is non-nil. They can then perform an appropriate action in case of
302 empty input.
304 Optional arg PROMPT is the prompt. Nil means use a default prompt."
305 (interactive "p") ; Always convert to RGB interactively.
306 (let* ((completion-ignore-case t)
307 ;; Free variables here: `eyedrop-picked-foreground', `eyedrop-picked-background'.
308 ;; They are defined in library `palette.el' or library `eyedropper.el'.
309 (colors (if (fboundp 'eyedrop-foreground-at-point)
310 (append (and eyedrop-picked-foreground
311 '(("*copied foreground*")))
312 (and eyedrop-picked-background
313 '(("*copied background*")))
314 '(("*mouse-2 foreground*")
315 ("*mouse-2 background*")
316 ("*point foreground*") ("*point background*"))
317 (hexrgb-defined-colors-alist))
318 (hexrgb-defined-colors-alist)))
319 (color (completing-read (or prompt "Color (name or #R+G+B+): ")
320 colors))
321 hex-string)
322 (when (fboundp 'eyedrop-foreground-at-point)
323 (cond ((string= "*copied foreground*" color) (setq color eyedrop-picked-foreground))
324 ((string= "*copied background*" color) (setq color eyedrop-picked-background))
325 ((string= "*point foreground*" color) (setq color (eyedrop-foreground-at-point)))
326 ((string= "*point background*" color) (setq color (eyedrop-background-at-point)))
327 ((string= "*mouse-2 foreground*" color)
328 (setq color (prog1 (eyedrop-foreground-at-mouse
329 (read-event "Click `mouse-2' to choose foreground color - "))
330 (read-event)))) ; Discard mouse up event.
331 ((string= "*mouse-2 background*" color)
332 (setq color (prog1 (eyedrop-background-at-mouse
333 (read-event "Click `mouse-2' to choose background color - "))
334 (read-event)))))) ; Discard mouse up event.
335 (setq hex-string (or (string-match "^#\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
336 (and (string-match "^\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
337 t)))
338 (if (and allow-empty-name-p (string= "" color))
340 (when (and hex-string (not (eq 0 hex-string)))
341 (setq color (concat "#" color))) ; No #; add it.
342 (unless hex-string
343 (when (or (string= "" color)
344 (not (if (fboundp 'test-completion) ; Not defined in Emacs 20.
345 (test-completion color colors)
346 (try-completion color colors))))
347 (error "No such color: %S" color))
348 (when convert-to-RGB-p (setq color (hexrgb-color-name-to-hex color))))
349 (when (interactive-p) (message "Color: `%s'" color))
350 color)))
352 ;;;###autoload
353 (defun hexrgb-rgb-hex-string-p (color &optional laxp)
354 "Non-nil if COLOR is an RGB string #XXXXXXXXXXXX.
355 Each X is a hex digit. The number of Xs must be a multiple of 3, with
356 the same number of Xs for each of red, green, and blue.
358 Non-nil optional arg LAXP means that the initial `#' is optional. In
359 that case, for a valid string of hex digits: when # is present 0 is
360 returned; otherwise, t is returned."
361 (or (string-match "^#\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
362 (and laxp (string-match "^\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color) t)))
364 ;;;###autoload
365 (defun hexrgb-complement (color)
366 "Return the color that is the complement of COLOR."
367 (interactive (list (hexrgb-read-color)))
368 (setq color (hexrgb-color-name-to-hex color))
369 (let ((red (hexrgb-red color))
370 (green (hexrgb-green color))
371 (blue (hexrgb-blue color)))
372 (setq color (hexrgb-rgb-to-hex (- 1.0 red) (- 1.0 green) (- 1.0 blue))))
373 (when (interactive-p) (message "Complement: `%s'" color))
374 color)
376 ;;;###autoload
377 (defun hexrgb-hue (color)
378 "Return the hue component of COLOR, in range 0 to 1 inclusive.
379 COLOR is a color name or hex RGB string that starts with \"#\"."
380 (interactive (list (hexrgb-read-color)))
381 (setq color (hexrgb-color-name-to-hex color))
382 (car (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
384 ;;;###autoload
385 (defun hexrgb-saturation (color)
386 "Return the saturation component of COLOR, in range 0 to 1 inclusive.
387 COLOR is a color name or hex RGB string that starts with \"#\"."
388 (interactive (list (hexrgb-read-color)))
389 (setq color (hexrgb-color-name-to-hex color))
390 (cadr (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
392 ;;;###autoload
393 (defun hexrgb-value (color)
394 "Return the value component of COLOR, in range 0 to 1 inclusive.
395 COLOR is a color name or hex RGB string that starts with \"#\"."
396 (interactive (list (hexrgb-read-color)))
397 (setq color (hexrgb-color-name-to-hex color))
398 (caddr (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
400 ;;;###autoload
401 (defun hexrgb-red (color)
402 "Return the red component of COLOR, in range 0 to 1 inclusive.
403 COLOR is a color name or hex RGB string that starts with \"#\"."
404 (interactive (list (hexrgb-read-color)))
405 (setq color (hexrgb-color-name-to-hex color))
406 (/ (hexrgb-hex-to-int (substring color 1 (1+ (/ (1- (length color)) 3))))
407 (expt 16.0 (/ (1- (length color)) 3.0))))
409 ;;;###autoload
410 (defun hexrgb-green (color)
411 "Return the green component of COLOR, in range 0 to 1 inclusive.
412 COLOR is a color name or hex RGB string that starts with \"#\"."
413 (interactive (list (hexrgb-read-color)))
414 (setq color (hexrgb-color-name-to-hex color))
415 (let* ((len (/ (1- (length color)) 3))
416 (start (1+ len)))
417 (/ (hexrgb-hex-to-int (substring color start (+ start len)))
418 (expt 16.0 (/ (1- (length color)) 3.0)))))
420 ;;;###autoload
421 (defun hexrgb-blue (color)
422 "Return the blue component of COLOR, in range 0 to 1 inclusive.
423 COLOR is a color name or hex RGB string that starts with \"#\"."
424 (interactive (list (hexrgb-read-color)))
425 (setq color (hexrgb-color-name-to-hex color))
426 (let* ((len (/ (1- (length color)) 3))
427 (start (+ 1 len len)))
428 (/ (hexrgb-hex-to-int (substring color start (+ start len)))
429 (expt 16.0 (/ (1- (length color)) 3.0)))))
431 ;;;###autoload
432 (defun hexrgb-rgb-to-hsv (red green blue)
433 "Convert RED, GREEN, BLUE components to HSV (hue, saturation, value).
434 Each input component is 0.0 to 1.0, inclusive.
435 Returns a list of HSV components of value 0.0 to 1.0, inclusive."
436 (let* ((min (min red green blue))
437 (max (max red green blue))
438 (value max)
439 (delta (- max min))
440 hue saturation)
441 (if (hexrgb-approx-equal 0.0 delta)
442 (setq hue 0.0
443 saturation 0.0) ; Gray scale - no color; only value.
444 (if (and (condition-case nil
445 (setq saturation (/ delta max))
446 (arith-error nil))
447 ;; Must be a number, not a NaN. The standard test for a NaN is (not (= N N)),
448 ;; but an Emacs 20 bug makes (= N N) return t for a NaN also.
449 (or (< emacs-major-version 21) (= saturation saturation)))
450 (if (hexrgb-approx-equal 0.0 saturation)
451 (setq hue 0.0
452 saturation 0.0) ; Again, no color; only value.
453 ;; Color
454 (setq hue (if (hexrgb-approx-equal red max)
455 (/ (- green blue) delta) ; Between yellow & magenta.
456 (if (hexrgb-approx-equal green max)
457 (+ 2.0 (/ (- blue red) delta)) ; Between cyan & yellow.
458 (+ 4.0 (/ (- red green) delta)))) ; Between magenta & cyan.
459 hue (/ hue 6.0))
460 ;; (when (<= hue 0.0) (setq hue (+ hue 1.0))) ; $$$$$$
461 ;; (when (>= hue 1.0) (setq hue (- hue 1.0)))) ; $$$$$$
462 (when (< hue 0.0) (setq hue (+ hue 1.0)))
463 (when (> hue 1.0) (setq hue (- hue 1.0))))
464 (setq hue 0.0 ; Div by zero (max=0): H:=0, S:=0. (Hue undefined.)
465 saturation 0.0)))
466 (list hue saturation value)))
468 ;;;###autoload
469 (defun hexrgb-hsv-to-rgb (hue saturation value)
470 "Convert HUE, SATURATION, VALUE components to RGB (red, green, blue).
471 Each input component is 0.0 to 1.0, inclusive.
472 Returns a list of RGB components of value 0.0 to 1.0, inclusive."
473 (let (red green blue int-hue fract pp qq tt ww)
474 (if (hexrgb-approx-equal 0.0 saturation)
475 (setq red value
476 green value
477 blue value) ; Gray
478 (setq hue (* hue 6.0) ; Sectors: 0 to 5
479 int-hue (floor hue)
480 fract (- hue int-hue)
481 pp (* value (- 1 saturation))
482 qq (* value (- 1 (* saturation fract)))
483 ww (* value (- 1 (* saturation (- 1 (- hue int-hue))))))
484 (case int-hue
485 ((0 6) (setq red value
486 green ww
487 blue pp))
488 (1 (setq red qq
489 green value
490 blue pp))
491 (2 (setq red pp
492 green value
493 blue ww))
494 (3 (setq red pp
495 green qq
496 blue value))
497 (4 (setq red ww
498 green pp
499 blue value))
500 (otherwise (setq red value
501 green pp
502 blue qq))))
503 (list red green blue)))
505 ;;;###autoload
506 (defun hexrgb-hsv-to-hex (hue saturation value)
507 "Return the hex RBG color string for inputs HUE, SATURATION, VALUE.
508 The inputs are each in the range 0 to 1.
509 The output string is of the form \"#RRRRGGGGBBBB\"."
510 (hexrgb-color-values-to-hex
511 (mapcar (lambda (x) (floor (* x 65535.0))) (hexrgb-hsv-to-rgb hue saturation value))))
513 ;;;###autoload
514 (defun hexrgb-rgb-to-hex (red green blue)
515 "Return the hex RBG color string for inputs RED, GREEN, BLUE.
516 The inputs are each in the range 0 to 1.
517 The output string is of the form \"#RRRRGGGGBBBB\"."
518 (hexrgb-color-values-to-hex
519 (mapcar (lambda (x) (floor (* x 65535.0))) (list red green blue))))
521 ;;;###autoload
522 (defun hexrgb-hex-to-hsv (color)
523 "Return a list of HSV (hue, saturation, value) color components.
524 Each component is a value from 0.0 to 1.0, inclusive.
525 COLOR is a color name or a hex RGB string that starts with \"#\" and
526 is followed by an equal number of hex digits for red, green, and blue
527 components."
528 (let ((rgb-components (hexrgb-hex-to-rgb color)))
529 (apply #'hexrgb-rgb-to-hsv rgb-components)))
531 ;;;###autoload
532 (defun hexrgb-hex-to-rgb (color)
533 "Return a list of RGB (red, green, blue) color components.
534 Each component is a value from 0.0 to 1.0, inclusive.
535 COLOR is a color name or a hex RGB string that starts with \"#\" and
536 is followed by an equal number of hex digits for red, green, and blue
537 components."
538 (unless (hexrgb-rgb-hex-string-p color) (setq color (hexrgb-color-name-to-hex color)))
539 (let ((len (/ (1- (length color)) 3)))
540 (list (/ (hexrgb-hex-to-int (substring color 1 (1+ len))) 65535.0)
541 (/ (hexrgb-hex-to-int (substring color (1+ len) (+ 1 len len))) 65535.0)
542 (/ (hexrgb-hex-to-int (substring color (+ 1 len len))) 65535.0))))
544 ;;;###autoload
545 (defun hexrgb-color-name-to-hex (color)
546 "Return the RGB hex string for the COLOR name, starting with \"#\".
547 If COLOR is already a string starting with \"#\", then just return it."
548 (let ((components (x-color-values color)))
549 (unless components (error "No such color: %S" color))
550 (unless (hexrgb-rgb-hex-string-p color)
551 (setq color (hexrgb-color-values-to-hex components))))
552 color)
554 ;; Just hard-code 4 as the number of hex digits, since `x-color-values'
555 ;; seems to produce appropriate integer values for this value.
557 ;; Color "components" would be better in the name than color "value"
558 ;; but this name follows the Emacs tradition (e.g. `x-color-values',
559 ;; 'ps-color-values', `ps-e-x-color-values').
560 ;;;###autoload
561 (defun hexrgb-color-values-to-hex (values)
562 "Convert list of rgb color VALUES to a hex string, #XXXXXXXXXXXX.
563 Each X in the string is a hexadecimal digit.
564 Input VALUES is as for the output of `x-color-values'."
565 (concat "#" (hexrgb-int-to-hex (nth 0 values) 4) ; red
566 (hexrgb-int-to-hex (nth 1 values) 4) ; green
567 (hexrgb-int-to-hex (nth 2 values) 4))) ; blue
569 ;;;###autoload
570 (defun hexrgb-hex-to-color-values (color)
571 "Convert hex COLOR to a list of rgb color values.
572 COLOR is a hex rgb color string, #XXXXXXXXXXXX
573 Each X in the string is a hexadecimal digit. There are 3N X's, N > 0.
574 The output list is as for `x-color-values'."
575 (let* ((hex-strgp (string-match
576 "^\\(#\\)?\\(\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+\\)$"
577 color))
578 (ndigits (/ (if (eq (match-beginning 1) (match-end 1))
579 (length color)
580 (1- (length color)))
582 red green blue)
583 (unless hex-strgp (error "Invalid RGB color string: %s" color))
584 (setq color (substring color (match-beginning 2) (match-end 2))
585 red (hexrgb-hex-to-int (substring color 0 ndigits))
586 green (hexrgb-hex-to-int (substring color ndigits (* 2 ndigits)))
587 blue (hexrgb-hex-to-int (substring color ndigits (* 3 ndigits))))
588 (list red green blue)))
590 ;;;###autoload
591 (defun hexrgb-increment-red (hex nb-digits increment &optional wrap-p)
592 "Increment red value of rgb string HEX by INCREMENT.
593 String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
594 If optional arg WRAP-P is non-nil, then the result wraps around zero.
595 For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
596 around to \"#000000000\"."
597 (concat "#"
598 (hexrgb-increment-hex (substring hex 1 (1+ nb-digits)) increment nb-digits wrap-p)
599 (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
600 (substring hex (1+ (* nb-digits 2)))))
602 ;;;###autoload
603 (defun hexrgb-increment-green (hex nb-digits increment &optional wrap-p)
604 "Increment green value of rgb string HEX by INCREMENT.
605 String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
606 For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
607 around to \"#000000000\"."
608 (concat
609 "#" (substring hex 1 (1+ nb-digits))
610 (hexrgb-increment-hex (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
611 increment
612 nb-digits
613 wrap-p)
614 (substring hex (1+ (* nb-digits 2)))))
616 ;;;###autoload
617 (defun hexrgb-increment-blue (hex nb-digits increment &optional wrap-p)
618 "Increment blue value of rgb string HEX by INCREMENT.
619 String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
620 For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
621 around to \"#000000000\"."
622 (concat "#" (substring hex 1 (1+ (* nb-digits 2)))
623 (hexrgb-increment-hex (substring hex (1+ (* nb-digits 2)))
624 increment
625 nb-digits
626 wrap-p)))
628 ;;;###autoload
629 (defun hexrgb-increment-equal-rgb (hex nb-digits increment &optional wrap-p)
630 "Increment each color value (r,g,b) of rgb string HEX by INCREMENT.
631 String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
632 For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
633 around to \"#000000000\"."
634 (concat
635 "#" (hexrgb-increment-hex (substring hex 1 (1+ nb-digits)) increment nb-digits wrap-p)
636 (hexrgb-increment-hex (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
637 increment
638 nb-digits
639 wrap-p)
640 (hexrgb-increment-hex (substring hex (1+ (* nb-digits 2))) increment nb-digits wrap-p)))
642 ;;;###autoload
643 (defun hexrgb-increment-hex (hex increment nb-digits &optional wrap-p)
644 "Increment HEX number (a string NB-DIGITS long) by INCREMENT.
645 For example, incrementing \"FFFFFFFFF\" by 1 will cause it to wrap
646 around to \"000000000\"."
647 (let* ((int (hexrgb-hex-to-int hex))
648 (new-int (+ increment int)))
649 (if (or wrap-p
650 (and (>= int 0) ; Not too large for the machine.
651 (>= new-int 0) ; For the case where increment < 0.
652 (<= (length (format (concat "%X") new-int)) nb-digits))) ; Not too long.
653 (hexrgb-int-to-hex new-int nb-digits) ; Use incremented number.
654 hex))) ; Don't increment.
656 ;;;###autoload
657 (defun hexrgb-hex-to-int (hex)
658 "Convert HEX string argument to an integer.
659 The characters of HEX must be hex characters."
660 (let* ((factor 1)
661 (len (length hex))
662 (indx (1- len))
663 (int 0))
664 (while (>= indx 0)
665 (setq int (+ int (* factor (hexrgb-hex-char-to-integer (aref hex indx))))
666 indx (1- indx)
667 factor (* 16 factor)))
668 int))
670 ;; From `hexl.el'. This is the same as `hexl-hex-char-to-integer' defined there.
671 ;;;###autoload
672 (defun hexrgb-hex-char-to-integer (character)
673 "Take a CHARACTER and return its value as if it were a hex digit."
674 (if (and (>= character ?0) (<= character ?9))
675 (- character ?0)
676 (let ((ch (logior character 32)))
677 (if (and (>= ch ?a) (<= ch ?f))
678 (- ch (- ?a 10))
679 (error "Invalid hex digit `%c'" ch)))))
681 ;; Originally, I used the code from `int-to-hex-string' in `float.el'.
682 ;; This version is thanks to Juri Linkov <juri@jurta.org>.
684 ;;;###autoload
685 (defun hexrgb-int-to-hex (int &optional nb-digits)
686 "Convert integer argument INT to a #XXXXXXXXXXXX format hex string.
687 Each X in the output string is a hexadecimal digit.
688 NB-DIGITS is the number of hex digits. If INT is too large to be
689 represented with NB-DIGITS, then the result is truncated from the
690 left. So, for example, INT=256 and NB-DIGITS=2 returns \"00\", since
691 the hex equivalent of 256 decimal is 100, which is more than 2 digits."
692 (setq nb-digits (or nb-digits 4))
693 (substring (format (concat "%0" (int-to-string nb-digits) "X") int) (- nb-digits)))
695 ;; Inspired by Elisp Info manual, node "Comparison of Numbers".
696 ;;;###autoload
697 (defun hexrgb-approx-equal (x y &optional rfuzz afuzz)
698 "Return non-nil if numbers X and Y are approximately equal.
699 RFUZZ is a relative fuzz factor. AFUZZ is an absolute fuzz factor.
700 RFUZZ defaults to 1.0e-8. AFUZZ defaults to (/ RFUZZ 10).
701 RFUZZ and AFUZZ are converted to their absolute values.
702 The algorithm is:
703 (< (abs (- X Y)) (+ AFUZZ (* RFUZZ (+ (abs X) (abs Y)))))."
704 (setq rfuzz (or rfuzz 1.0e-8)
705 rfuzz (abs rfuzz)
706 afuzz (or afuzz (/ rfuzz 10))
707 afuzz (abs afuzz))
708 (< (abs (- x y)) (+ afuzz (* rfuzz (+ (abs x) (abs y))))))
710 ;;;###autoload
711 (defun hexrgb-color-value-to-float (n)
712 "Return the floating-point equivalent of color value N.
713 N must be an integer between 0 and 65535, or else an error is raised."
714 (unless (and (wholenump n) (<= n 65535))
715 (error "Not a whole number less than 65536"))
716 (/ (float n) 65535.0))
718 ;;;###autoload
719 (defun hexrgb-float-to-color-value (x)
720 "Return the color value equivalent of floating-point number X.
721 X must be between 0.0 and 1.0, or else an error is raised."
722 (unless (and (numberp x) (<= 0.0 x) (<= x 1.0))
723 (error "Not a floating-point number between 0.0 and 1.0"))
724 (floor (* x 65535.0)))
726 ;;;;;;;;;;;;;;;;;;;;;;;;;;;
728 (provide 'hexrgb)
730 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
731 ;;; hexrgb.el ends here