123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732 |
- ;;; hexrgb.el --- Functions to manipulate colors, including RGB hex strings.
- ;;
- ;; Filename: hexrgb.el
- ;; Description: Functions to manipulate colors, including RGB hex strings.
- ;; Author: Drew Adams
- ;; Maintainer: Drew Adams
- ;; Copyright (C) 2004-2009, Drew Adams, all rights reserved.
- ;; Created: Mon Sep 20 22:58:45 2004
- ;; Version: 21.0
- ;; Last-Updated: Sat Nov 14 15:55:15 2009 (-0800)
- ;; By: dradams
- ;; Update #: 732
- ;; URL: http://www.emacswiki.org/cgi-bin/wiki/hexrgb.el
- ;; Keywords: number, hex, rgb, color, background, frames, display
- ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x
- ;;
- ;; Features that might be required by this library:
- ;;
- ;; None
- ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;;; Commentary:
- ;;
- ;; Functions to manipulate colors, including RGB hex strings.
- ;;
- ;; This library provides functions for converting between RGB (red,
- ;; green, blue) color components and HSV (hue, saturation, value)
- ;; color components. It helps you convert among Emacs color values
- ;; (whole numbers from 0 through 65535), RGB and HSV floating-point
- ;; components (0.0 through 1.0), Emacs color-name strings (such as
- ;; "blue"), and hex RGB color strings (such as "#FC43A7912").
- ;;
- ;; An RGB hex string, such as used as a frame `background-color'
- ;; property, is a string of 1 + (3 * n) characters, the first of
- ;; which is "#". The other characters are hexadecimal digits, in
- ;; three groups representing (from the left): red, green, and blue
- ;; hex codes.
- ;;
- ;; Constants defined here:
- ;;
- ;; `hexrgb-defined-colors', `hexrgb-defined-colors-alist',
- ;; `hexrgb-defined-colors-no-dups',
- ;; `hexrgb-defined-colors-no-dups-alist'.
- ;;
- ;; Options defined here:
- ;;
- ;; `hexrgb-canonicalize-defined-colors-flag'.
- ;;
- ;; Commands defined here:
- ;;
- ;; `hexrgb-blue', `hexrgb-complement', `hexrgb-green',
- ;; `hexrgb-hue', `hexrgb-read-color', `hexrgb-red',
- ;; `hexrgb-saturation', `hexrgb-value'.
- ;;
- ;; Non-interactive functions defined here:
- ;;
- ;; `hexrgb-approx-equal', `hexrgb-canonicalize-defined-colors',
- ;; `hexrgb-color-name-to-hex', `hexrgb-color-values-to-hex',
- ;; `hexrgb-color-value-to-float', `hexrgb-defined-colors',
- ;; `hexrgb-defined-colors-alist',
- ;; `hexrgb-delete-whitespace-from-string',
- ;; `hexrgb-float-to-color-value', `hexrgb-hex-char-to-integer',
- ;; `hexrgb-hex-to-color-values', `hexrgb-hex-to-hsv',
- ;; `hexrgb-hex-to-rgb', `hexrgb-hsv-to-hex', `hexrgb-hex-to-int',
- ;; `hexrgb-hsv-to-rgb', `hexrgb-increment-blue',
- ;; `hexrgb-increment-equal-rgb', `hexrgb-increment-green',
- ;; `hexrgb-increment-hex', `hexrgb-increment-red',
- ;; `hexrgb-int-to-hex', `hexrgb-rgb-hex-string-p',
- ;; `hexrgb-rgb-to-hex', `hexrgb-rgb-to-hsv'.
- ;;
- ;;
- ;; Add this to your initialization file (~/.emacs or ~/_emacs):
- ;;
- ;; (require 'hexrgb)
- ;;
- ;; Do not try to use this library without a window manager.
- ;; That is, do not use this with `emacs -nw'.
- ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;;; Change log:
- ;;
- ;; 2009/11/14 dadams
- ;; hexrgb-rgb-to-hsv: Corrected hue when > 1.0. Use strict inequality for hue limit tests.
- ;; hexrgb-approx-equal: Convert RFUZZ and AFUZZ to their absolute values.
- ;; 2009/11/03 dadams
- ;; Added: hexrgb-delete-whitespace-from-string, hexrgb-canonicalize-defined-colors,
- ;; hexrgb-defined-colors(-no-dups)(-alist), hexrgb-canonicalize-defined-colors-flag.
- ;; hexrgb-read-color: Use function hexrgb-defined-colors-alist, not the constant.
- ;; 2008/12/25 dadams
- ;; hexrgb-rgb-to-hsv:
- ;; Replace (not (equal 0.0e+NaN saturation)) by standard test (= saturation saturation).
- ;; Thx to Michael Heerdegen for the bug report.
- ;; 2008-10-17 dadams
- ;; hexrgb-defined-colors(-alist): Prevent load-time error if user tries to use emacs -nw.
- ;; 2007/12/30 dadams
- ;; Added: hexrgb-hex-to-color-values.
- ;; 2007/10/20 dadams
- ;; hexrgb-read-color: Treat pseudo colors too (e.g. *point foreground*).
- ;; 2007/01/21 dadams
- ;; hexrgb-read-color: Error if empty string (and not allow-empty-name-p).
- ;; 2006/06/06 dadams
- ;; Added: hexrgb-defined-colors(-alist). Use instead of (x-defined-colors).
- ;; hexrgb-(red|green|blue): Added interactive specs.
- ;; 2006/06/04 dadams
- ;; hexrgb-read-color: Added optional arg allow-empty-name-p.
- ;; 2006/06/02 dadams
- ;; Added: hexrgb-rgb-hex-string-p. Used it.
- ;; 2006/05/30 dadams
- ;; Added: hexrgb-hex-to-(hsv|rgb), hexrgb-hsv-to-hex, hexrgb-color-name-to-hex,
- ;; hexrgb-complement, hexrgb-read-color, hexrgb-hue, hexrgb-saturation,
- ;; hexrgb-value, hexrgb-red, hexrgb-blue, hexrgb-green.
- ;; approx-equal: Add optional fuzz factor arguments. Changed the algorithm.
- ;; Renamed: approx-equal to hexrgb-approx-equal.
- ;; hexrgb-rgb-to-hsv: Changed test from < to <=: (when (<= hue 0.0)...).
- ;; hexrgb-hsv-to-rgb: Treat hue = 0.0 (int 0) the same as hue = 1.0 (int 6).
- ;; hexrgb-rgb-to-hex, hexrgb-increment-hex: Corrected doc strings.
- ;; 2006/05/22 dadams
- ;; Added: hexrgb-hsv-to-hex, hexrgb-rgb-to-hex. Require cl.el when byte-compile.
- ;; 2005/08/09 dadams
- ;; hexrgb-rgb-to-hsv: Side-stepped Emacs-20 bug in comparing NaN.
- ;; hexrgb-increment-*: Added optional arg wrap-p.
- ;; hexrgb-increment-hex: Prevent wrap if not wrap-p.
- ;; 2005/08/02 dadams
- ;; hexrgb-rgb-to-hes: Bug fix: If delta is zero, then so are hue and saturation.
- ;; 2005/06/24 dadams
- ;; hexrgb-rgb-to-hsv: Bug fix: test for NaN (e.g. on divide by zero).
- ;; 2005/02/08 dadams
- ;; hexrgb-hsv-to-rgb: Bug fix (typo: p, q -> pp, qq; added ww).
- ;; 2005/01/09 dadams
- ;; hexrgb-int-to-hex: Fixed bug in hexrgb-int-to-hex: nb-digits not respected.
- ;; Added: hexrgb-hsv-to-rgb, hexrgb-rgb-to-hsv, approx-equal.
- ;; Renamed old hexrgb-increment-value to hexrgb-increment-equal-rgb.
- ;; 2005/01/05 dadams
- ;; hexrgb-int-to-hex: Used a suggestion from Juri Linkov.
- ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; This program is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation; either version 2, or (at your option)
- ;; any later version.
- ;; This program is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with this program; see the file COPYING. If not, write to
- ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
- ;; Floor, Boston, MA 02110-1301, USA.
- ;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;;; Code:
- (eval-when-compile (require 'cl)) ;; case; plus, for Emacs < 20: when, unless
- ;; Unless you first load `hexrgb.el', then either `palette.el' or `eyedropper.el', you will get
- ;; warnings about variables and functions with prefix `eyedrop-' when you byte-compile
- ;; `hexrgb.el'. You can ignore these warnings.
- (defvar eyedrop-picked-foreground)
- (defvar eyedrop-picked-background)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;###autoload
- (eval-and-compile
- (defun hexrgb-canonicalize-defined-colors (list)
- "Copy of LIST with color names canonicalized.
- LIST is a list of color names (strings).
- Canonical names are lowercase, with no whitespace.
- There are no duplicate names."
- (let ((tail list)
- this new)
- (while tail
- (setq this (car tail)
- this (hexrgb-delete-whitespace-from-string (downcase this) 0 (length this)))
- (unless (member this new) (push this new))
- (pop tail))
- (nreverse new)))
- (defun hexrgb-delete-whitespace-from-string (string &optional from to)
- "Remove whitespace from substring of STRING from FROM to TO.
- If FROM is nil, then start at the beginning of STRING (FROM = 0).
- If TO is nil, then end at the end of STRING (TO = length of STRING).
- FROM and TO are zero-based indexes into STRING.
- Character FROM is affected (possibly deleted). Character TO is not."
- (setq from (or from 0)
- to (or to (length string)))
- (with-temp-buffer
- (insert string)
- (goto-char (+ from (point-min)))
- (let ((count from)
- char)
- (while (and (not (eobp)) (< count to))
- (setq char (char-after))
- (if (memq char '(?\ ?\t ?\n)) (delete-char 1) (forward-char 1))
- (setq count (1+ count)))
- (buffer-string)))))
- ;;;###autoload
- (defconst hexrgb-defined-colors (eval-when-compile (and window-system (x-defined-colors)))
- "List of all supported colors.")
- ;;;###autoload
- (defconst hexrgb-defined-colors-no-dups
- (eval-when-compile
- (and window-system (hexrgb-canonicalize-defined-colors (x-defined-colors))))
- "List of all supported color names, with no duplicates.
- Names are all lowercase, without any spaces.")
- ;;;###autoload
- (defconst hexrgb-defined-colors-alist
- (eval-when-compile (and window-system (mapcar #'list (x-defined-colors))))
- "Alist of all supported color names, for use in completion.
- See also `hexrgb-defined-colors-no-dups-alist', which is the same
- thing, but without any duplicates, such as \"light blue\" and
- \"LightBlue\".")
- ;;;###autoload
- (defconst hexrgb-defined-colors-no-dups-alist
- (eval-when-compile
- (and window-system
- (mapcar #'list (hexrgb-canonicalize-defined-colors (x-defined-colors)))))
- "Alist of all supported color names, with no duplicates, for completion.
- Names are all lowercase, without any spaces.")
- ;;;###autoload
- (defcustom hexrgb-canonicalize-defined-colors-flag t
- "*Non-nil means remove duplicate color names.
- Names are considered duplicates if they are the same when abstracting
- from whitespace and letter case."
- :type 'boolean
- :group 'Icicles :group 'doremi-frame-commands :group 'faces :group 'convenience)
- ;; You should use these two functions, not the constants, so users can change
- ;; the behavior by customizing `hexrgb-canonicalize-defined-colors-flag'.
- (defun hexrgb-defined-colors ()
- "List of supported color names.
- If `hexrgb-canonicalize-defined-colors-flag' is non-nil, then names
- are lowercased, whitespace is removed, and there are no duplicates."
- (if hexrgb-canonicalize-defined-colors-flag
- hexrgb-defined-colors-no-dups
- hexrgb-defined-colors))
- (defun hexrgb-defined-colors-alist ()
- "Alist of supported color names. Usable for completion.
- If `hexrgb-canonicalize-defined-colors-flag' is non-nil, then names
- are lowercased, whitespace is removed, and there are no duplicates."
- (if hexrgb-canonicalize-defined-colors-flag
- hexrgb-defined-colors-no-dups-alist
- hexrgb-defined-colors-alist))
- ;; RMS added this function to Emacs (23) as `read-color', with some feature loss.
- ;;;###autoload
- (defun hexrgb-read-color (&optional convert-to-RGB-p allow-empty-name-p prompt)
- "Read a color name or RGB hex value: #RRRRGGGGBBBB.
- Completion is available for color names, but not for RGB hex strings.
- If you input an RGB hex string, it must have the form #XXXXXXXXXXXX or
- XXXXXXXXXXXX, where each X is a hex digit. The number of Xs must be a
- multiple of 3, with the same number of Xs for each of red, green, and
- blue. The order is red, green, blue.
- Color names that are normally considered equivalent are canonicalized:
- They are lowercased, whitespace is removed, and duplicates are
- eliminated. E.g. \"LightBlue\" and \"light blue\" are both replaced
- by \"lightblue\". If you do not want this behavior, but want to
- choose names that might contain whitespace or uppercase letters, then
- customize option `hexrgb-canonicalize-defined-colors-flag' to nil.
- In addition to standard color names and RGB hex values, the following
- are available as color candidates. In each case, the corresponding
- color is used.
- * `*copied foreground*' - last copied foreground, if available
- * `*copied background*' - last copied background, if available
- * `*mouse-2 foreground*' - foreground where you click `mouse-2'
- * `*mouse-2 background*' - background where you click `mouse-2'
- * `*point foreground*' - foreground under the cursor
- * `*point background*' - background under the cursor
- \(You can copy a color using eyedropper commands such as
- `eyedrop-pick-foreground-at-mouse'.)
- Checks input to be sure it represents a valid color. If not, raises
- an error (but see exception for empty input with non-nil
- ALLOW-EMPTY-NAME-P).
- Interactively, or with optional arg CONVERT-TO-RGB-P non-nil, converts
- an input color name to an RGB hex string. Returns the RGB hex string.
- Optional arg ALLOW-EMPTY-NAME-P controls what happens if you enter an
- empty color name (that is, you just hit `RET'). If non-nil, then
- `hexrgb-read-color' returns an empty color name, \"\". If nil, then
- it raises an error. Programs must test for \"\" if ALLOW-EMPTY-NAME-P
- is non-nil. They can then perform an appropriate action in case of
- empty input.
- Optional arg PROMPT is the prompt. Nil means use a default prompt."
- (interactive "p") ; Always convert to RGB interactively.
- (let* ((completion-ignore-case t)
- ;; Free variables here: `eyedrop-picked-foreground', `eyedrop-picked-background'.
- ;; They are defined in library `palette.el' or library `eyedropper.el'.
- (colors (if (fboundp 'eyedrop-foreground-at-point)
- (append (and eyedrop-picked-foreground
- '(("*copied foreground*")))
- (and eyedrop-picked-background
- '(("*copied background*")))
- '(("*mouse-2 foreground*")
- ("*mouse-2 background*")
- ("*point foreground*") ("*point background*"))
- (hexrgb-defined-colors-alist))
- (hexrgb-defined-colors-alist)))
- (color (completing-read (or prompt "Color (name or #R+G+B+): ")
- colors))
- hex-string)
- (when (fboundp 'eyedrop-foreground-at-point)
- (cond ((string= "*copied foreground*" color) (setq color eyedrop-picked-foreground))
- ((string= "*copied background*" color) (setq color eyedrop-picked-background))
- ((string= "*point foreground*" color) (setq color (eyedrop-foreground-at-point)))
- ((string= "*point background*" color) (setq color (eyedrop-background-at-point)))
- ((string= "*mouse-2 foreground*" color)
- (setq color (prog1 (eyedrop-foreground-at-mouse
- (read-event "Click `mouse-2' to choose foreground color - "))
- (read-event)))) ; Discard mouse up event.
- ((string= "*mouse-2 background*" color)
- (setq color (prog1 (eyedrop-background-at-mouse
- (read-event "Click `mouse-2' to choose background color - "))
- (read-event)))))) ; Discard mouse up event.
- (setq hex-string (or (string-match "^#\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
- (and (string-match "^\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
- t)))
- (if (and allow-empty-name-p (string= "" color))
- ""
- (when (and hex-string (not (eq 0 hex-string)))
- (setq color (concat "#" color))) ; No #; add it.
- (unless hex-string
- (when (or (string= "" color)
- (not (if (fboundp 'test-completion) ; Not defined in Emacs 20.
- (test-completion color colors)
- (try-completion color colors))))
- (error "No such color: %S" color))
- (when convert-to-RGB-p (setq color (hexrgb-color-name-to-hex color))))
- (when (interactive-p) (message "Color: `%s'" color))
- color)))
- ;;;###autoload
- (defun hexrgb-rgb-hex-string-p (color &optional laxp)
- "Non-nil if COLOR is an RGB string #XXXXXXXXXXXX.
- Each X is a hex digit. The number of Xs must be a multiple of 3, with
- the same number of Xs for each of red, green, and blue.
- Non-nil optional arg LAXP means that the initial `#' is optional. In
- that case, for a valid string of hex digits: when # is present 0 is
- returned; otherwise, t is returned."
- (or (string-match "^#\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color)
- (and laxp (string-match "^\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+$" color) t)))
- ;;;###autoload
- (defun hexrgb-complement (color)
- "Return the color that is the complement of COLOR."
- (interactive (list (hexrgb-read-color)))
- (setq color (hexrgb-color-name-to-hex color))
- (let ((red (hexrgb-red color))
- (green (hexrgb-green color))
- (blue (hexrgb-blue color)))
- (setq color (hexrgb-rgb-to-hex (- 1.0 red) (- 1.0 green) (- 1.0 blue))))
- (when (interactive-p) (message "Complement: `%s'" color))
- color)
- ;;;###autoload
- (defun hexrgb-hue (color)
- "Return the hue component of COLOR, in range 0 to 1 inclusive.
- COLOR is a color name or hex RGB string that starts with \"#\"."
- (interactive (list (hexrgb-read-color)))
- (setq color (hexrgb-color-name-to-hex color))
- (car (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
- ;;;###autoload
- (defun hexrgb-saturation (color)
- "Return the saturation component of COLOR, in range 0 to 1 inclusive.
- COLOR is a color name or hex RGB string that starts with \"#\"."
- (interactive (list (hexrgb-read-color)))
- (setq color (hexrgb-color-name-to-hex color))
- (cadr (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
- ;;;###autoload
- (defun hexrgb-value (color)
- "Return the value component of COLOR, in range 0 to 1 inclusive.
- COLOR is a color name or hex RGB string that starts with \"#\"."
- (interactive (list (hexrgb-read-color)))
- (setq color (hexrgb-color-name-to-hex color))
- (caddr (hexrgb-rgb-to-hsv (hexrgb-red color) (hexrgb-green color) (hexrgb-blue color))))
- ;;;###autoload
- (defun hexrgb-red (color)
- "Return the red component of COLOR, in range 0 to 1 inclusive.
- COLOR is a color name or hex RGB string that starts with \"#\"."
- (interactive (list (hexrgb-read-color)))
- (setq color (hexrgb-color-name-to-hex color))
- (/ (hexrgb-hex-to-int (substring color 1 (1+ (/ (1- (length color)) 3))))
- (expt 16.0 (/ (1- (length color)) 3.0))))
- ;;;###autoload
- (defun hexrgb-green (color)
- "Return the green component of COLOR, in range 0 to 1 inclusive.
- COLOR is a color name or hex RGB string that starts with \"#\"."
- (interactive (list (hexrgb-read-color)))
- (setq color (hexrgb-color-name-to-hex color))
- (let* ((len (/ (1- (length color)) 3))
- (start (1+ len)))
- (/ (hexrgb-hex-to-int (substring color start (+ start len)))
- (expt 16.0 (/ (1- (length color)) 3.0)))))
- ;;;###autoload
- (defun hexrgb-blue (color)
- "Return the blue component of COLOR, in range 0 to 1 inclusive.
- COLOR is a color name or hex RGB string that starts with \"#\"."
- (interactive (list (hexrgb-read-color)))
- (setq color (hexrgb-color-name-to-hex color))
- (let* ((len (/ (1- (length color)) 3))
- (start (+ 1 len len)))
- (/ (hexrgb-hex-to-int (substring color start (+ start len)))
- (expt 16.0 (/ (1- (length color)) 3.0)))))
- ;;;###autoload
- (defun hexrgb-rgb-to-hsv (red green blue)
- "Convert RED, GREEN, BLUE components to HSV (hue, saturation, value).
- Each input component is 0.0 to 1.0, inclusive.
- Returns a list of HSV components of value 0.0 to 1.0, inclusive."
- (let* ((min (min red green blue))
- (max (max red green blue))
- (value max)
- (delta (- max min))
- hue saturation)
- (if (hexrgb-approx-equal 0.0 delta)
- (setq hue 0.0
- saturation 0.0) ; Gray scale - no color; only value.
- (if (and (condition-case nil
- (setq saturation (/ delta max))
- (arith-error nil))
- ;; Must be a number, not a NaN. The standard test for a NaN is (not (= N N)),
- ;; but an Emacs 20 bug makes (= N N) return t for a NaN also.
- (or (< emacs-major-version 21) (= saturation saturation)))
- (if (hexrgb-approx-equal 0.0 saturation)
- (setq hue 0.0
- saturation 0.0) ; Again, no color; only value.
- ;; Color
- (setq hue (if (hexrgb-approx-equal red max)
- (/ (- green blue) delta) ; Between yellow & magenta.
- (if (hexrgb-approx-equal green max)
- (+ 2.0 (/ (- blue red) delta)) ; Between cyan & yellow.
- (+ 4.0 (/ (- red green) delta)))) ; Between magenta & cyan.
- hue (/ hue 6.0))
- ;; (when (<= hue 0.0) (setq hue (+ hue 1.0))) ; $$$$$$
- ;; (when (>= hue 1.0) (setq hue (- hue 1.0)))) ; $$$$$$
- (when (< hue 0.0) (setq hue (+ hue 1.0)))
- (when (> hue 1.0) (setq hue (- hue 1.0))))
- (setq hue 0.0 ; Div by zero (max=0): H:=0, S:=0. (Hue undefined.)
- saturation 0.0)))
- (list hue saturation value)))
- ;;;###autoload
- (defun hexrgb-hsv-to-rgb (hue saturation value)
- "Convert HUE, SATURATION, VALUE components to RGB (red, green, blue).
- Each input component is 0.0 to 1.0, inclusive.
- Returns a list of RGB components of value 0.0 to 1.0, inclusive."
- (let (red green blue int-hue fract pp qq tt ww)
- (if (hexrgb-approx-equal 0.0 saturation)
- (setq red value
- green value
- blue value) ; Gray
- (setq hue (* hue 6.0) ; Sectors: 0 to 5
- int-hue (floor hue)
- fract (- hue int-hue)
- pp (* value (- 1 saturation))
- qq (* value (- 1 (* saturation fract)))
- ww (* value (- 1 (* saturation (- 1 (- hue int-hue))))))
- (case int-hue
- ((0 6) (setq red value
- green ww
- blue pp))
- (1 (setq red qq
- green value
- blue pp))
- (2 (setq red pp
- green value
- blue ww))
- (3 (setq red pp
- green qq
- blue value))
- (4 (setq red ww
- green pp
- blue value))
- (otherwise (setq red value
- green pp
- blue qq))))
- (list red green blue)))
- ;;;###autoload
- (defun hexrgb-hsv-to-hex (hue saturation value)
- "Return the hex RBG color string for inputs HUE, SATURATION, VALUE.
- The inputs are each in the range 0 to 1.
- The output string is of the form \"#RRRRGGGGBBBB\"."
- (hexrgb-color-values-to-hex
- (mapcar (lambda (x) (floor (* x 65535.0))) (hexrgb-hsv-to-rgb hue saturation value))))
- ;;;###autoload
- (defun hexrgb-rgb-to-hex (red green blue)
- "Return the hex RBG color string for inputs RED, GREEN, BLUE.
- The inputs are each in the range 0 to 1.
- The output string is of the form \"#RRRRGGGGBBBB\"."
- (hexrgb-color-values-to-hex
- (mapcar (lambda (x) (floor (* x 65535.0))) (list red green blue))))
- ;;;###autoload
- (defun hexrgb-hex-to-hsv (color)
- "Return a list of HSV (hue, saturation, value) color components.
- Each component is a value from 0.0 to 1.0, inclusive.
- COLOR is a color name or a hex RGB string that starts with \"#\" and
- is followed by an equal number of hex digits for red, green, and blue
- components."
- (let ((rgb-components (hexrgb-hex-to-rgb color)))
- (apply #'hexrgb-rgb-to-hsv rgb-components)))
- ;;;###autoload
- (defun hexrgb-hex-to-rgb (color)
- "Return a list of RGB (red, green, blue) color components.
- Each component is a value from 0.0 to 1.0, inclusive.
- COLOR is a color name or a hex RGB string that starts with \"#\" and
- is followed by an equal number of hex digits for red, green, and blue
- components."
- (unless (hexrgb-rgb-hex-string-p color) (setq color (hexrgb-color-name-to-hex color)))
- (let ((len (/ (1- (length color)) 3)))
- (list (/ (hexrgb-hex-to-int (substring color 1 (1+ len))) 65535.0)
- (/ (hexrgb-hex-to-int (substring color (1+ len) (+ 1 len len))) 65535.0)
- (/ (hexrgb-hex-to-int (substring color (+ 1 len len))) 65535.0))))
- ;;;###autoload
- (defun hexrgb-color-name-to-hex (color)
- "Return the RGB hex string for the COLOR name, starting with \"#\".
- If COLOR is already a string starting with \"#\", then just return it."
- (let ((components (x-color-values color)))
- (unless components (error "No such color: %S" color))
- (unless (hexrgb-rgb-hex-string-p color)
- (setq color (hexrgb-color-values-to-hex components))))
- color)
- ;; Just hard-code 4 as the number of hex digits, since `x-color-values'
- ;; seems to produce appropriate integer values for this value.
- ;;
- ;; Color "components" would be better in the name than color "value"
- ;; but this name follows the Emacs tradition (e.g. `x-color-values',
- ;; 'ps-color-values', `ps-e-x-color-values').
- ;;;###autoload
- (defun hexrgb-color-values-to-hex (values)
- "Convert list of rgb color VALUES to a hex string, #XXXXXXXXXXXX.
- Each X in the string is a hexadecimal digit.
- Input VALUES is as for the output of `x-color-values'."
- (concat "#" (hexrgb-int-to-hex (nth 0 values) 4) ; red
- (hexrgb-int-to-hex (nth 1 values) 4) ; green
- (hexrgb-int-to-hex (nth 2 values) 4))) ; blue
- ;;;###autoload
- (defun hexrgb-hex-to-color-values (color)
- "Convert hex COLOR to a list of rgb color values.
- COLOR is a hex rgb color string, #XXXXXXXXXXXX
- Each X in the string is a hexadecimal digit. There are 3N X's, N > 0.
- The output list is as for `x-color-values'."
- (let* ((hex-strgp (string-match
- "^\\(#\\)?\\(\\([a-fA-F0-9][a-fA-F0-9][a-fA-F0-9]\\)+\\)$"
- color))
- (ndigits (/ (if (eq (match-beginning 1) (match-end 1))
- (length color)
- (1- (length color)))
- 3))
- red green blue)
- (unless hex-strgp (error "Invalid RGB color string: %s" color))
- (setq color (substring color (match-beginning 2) (match-end 2))
- red (hexrgb-hex-to-int (substring color 0 ndigits))
- green (hexrgb-hex-to-int (substring color ndigits (* 2 ndigits)))
- blue (hexrgb-hex-to-int (substring color ndigits (* 3 ndigits))))
- (list red green blue)))
-
- ;;;###autoload
- (defun hexrgb-increment-red (hex nb-digits increment &optional wrap-p)
- "Increment red value of rgb string HEX by INCREMENT.
- String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
- If optional arg WRAP-P is non-nil, then the result wraps around zero.
- For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
- around to \"#000000000\"."
- (concat "#"
- (hexrgb-increment-hex (substring hex 1 (1+ nb-digits)) increment nb-digits wrap-p)
- (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
- (substring hex (1+ (* nb-digits 2)))))
- ;;;###autoload
- (defun hexrgb-increment-green (hex nb-digits increment &optional wrap-p)
- "Increment green value of rgb string HEX by INCREMENT.
- String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
- For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
- around to \"#000000000\"."
- (concat
- "#" (substring hex 1 (1+ nb-digits))
- (hexrgb-increment-hex (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
- increment
- nb-digits
- wrap-p)
- (substring hex (1+ (* nb-digits 2)))))
- ;;;###autoload
- (defun hexrgb-increment-blue (hex nb-digits increment &optional wrap-p)
- "Increment blue value of rgb string HEX by INCREMENT.
- String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
- For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
- around to \"#000000000\"."
- (concat "#" (substring hex 1 (1+ (* nb-digits 2)))
- (hexrgb-increment-hex (substring hex (1+ (* nb-digits 2)))
- increment
- nb-digits
- wrap-p)))
- ;;;###autoload
- (defun hexrgb-increment-equal-rgb (hex nb-digits increment &optional wrap-p)
- "Increment each color value (r,g,b) of rgb string HEX by INCREMENT.
- String HEX starts with \"#\". Each color is NB-DIGITS hex digits long.
- For example, incrementing \"#FFFFFFFFF\" by 1 will cause it to wrap
- around to \"#000000000\"."
- (concat
- "#" (hexrgb-increment-hex (substring hex 1 (1+ nb-digits)) increment nb-digits wrap-p)
- (hexrgb-increment-hex (substring hex (1+ nb-digits) (1+ (* nb-digits 2)))
- increment
- nb-digits
- wrap-p)
- (hexrgb-increment-hex (substring hex (1+ (* nb-digits 2))) increment nb-digits wrap-p)))
- ;;;###autoload
- (defun hexrgb-increment-hex (hex increment nb-digits &optional wrap-p)
- "Increment HEX number (a string NB-DIGITS long) by INCREMENT.
- For example, incrementing \"FFFFFFFFF\" by 1 will cause it to wrap
- around to \"000000000\"."
- (let* ((int (hexrgb-hex-to-int hex))
- (new-int (+ increment int)))
- (if (or wrap-p
- (and (>= int 0) ; Not too large for the machine.
- (>= new-int 0) ; For the case where increment < 0.
- (<= (length (format (concat "%X") new-int)) nb-digits))) ; Not too long.
- (hexrgb-int-to-hex new-int nb-digits) ; Use incremented number.
- hex))) ; Don't increment.
- ;;;###autoload
- (defun hexrgb-hex-to-int (hex)
- "Convert HEX string argument to an integer.
- The characters of HEX must be hex characters."
- (let* ((factor 1)
- (len (length hex))
- (indx (1- len))
- (int 0))
- (while (>= indx 0)
- (setq int (+ int (* factor (hexrgb-hex-char-to-integer (aref hex indx))))
- indx (1- indx)
- factor (* 16 factor)))
- int))
- ;; From `hexl.el'. This is the same as `hexl-hex-char-to-integer' defined there.
- ;;;###autoload
- (defun hexrgb-hex-char-to-integer (character)
- "Take a CHARACTER and return its value as if it were a hex digit."
- (if (and (>= character ?0) (<= character ?9))
- (- character ?0)
- (let ((ch (logior character 32)))
- (if (and (>= ch ?a) (<= ch ?f))
- (- ch (- ?a 10))
- (error "Invalid hex digit `%c'" ch)))))
- ;; Originally, I used the code from `int-to-hex-string' in `float.el'.
- ;; This version is thanks to Juri Linkov <juri@jurta.org>.
- ;;
- ;;;###autoload
- (defun hexrgb-int-to-hex (int &optional nb-digits)
- "Convert integer argument INT to a #XXXXXXXXXXXX format hex string.
- Each X in the output string is a hexadecimal digit.
- NB-DIGITS is the number of hex digits. If INT is too large to be
- represented with NB-DIGITS, then the result is truncated from the
- left. So, for example, INT=256 and NB-DIGITS=2 returns \"00\", since
- the hex equivalent of 256 decimal is 100, which is more than 2 digits."
- (setq nb-digits (or nb-digits 4))
- (substring (format (concat "%0" (int-to-string nb-digits) "X") int) (- nb-digits)))
- ;; Inspired by Elisp Info manual, node "Comparison of Numbers".
- ;;;###autoload
- (defun hexrgb-approx-equal (x y &optional rfuzz afuzz)
- "Return non-nil if numbers X and Y are approximately equal.
- RFUZZ is a relative fuzz factor. AFUZZ is an absolute fuzz factor.
- RFUZZ defaults to 1.0e-8. AFUZZ defaults to (/ RFUZZ 10).
- RFUZZ and AFUZZ are converted to their absolute values.
- The algorithm is:
- (< (abs (- X Y)) (+ AFUZZ (* RFUZZ (+ (abs X) (abs Y)))))."
- (setq rfuzz (or rfuzz 1.0e-8)
- rfuzz (abs rfuzz)
- afuzz (or afuzz (/ rfuzz 10))
- afuzz (abs afuzz))
- (< (abs (- x y)) (+ afuzz (* rfuzz (+ (abs x) (abs y))))))
- ;;;###autoload
- (defun hexrgb-color-value-to-float (n)
- "Return the floating-point equivalent of color value N.
- N must be an integer between 0 and 65535, or else an error is raised."
- (unless (and (wholenump n) (<= n 65535))
- (error "Not a whole number less than 65536"))
- (/ (float n) 65535.0))
- ;;;###autoload
- (defun hexrgb-float-to-color-value (x)
- "Return the color value equivalent of floating-point number X.
- X must be between 0.0 and 1.0, or else an error is raised."
- (unless (and (numberp x) (<= 0.0 x) (<= x 1.0))
- (error "Not a floating-point number between 0.0 and 1.0"))
- (floor (* x 65535.0)))
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;
- (provide 'hexrgb)
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;; hexrgb.el ends here
|