diff options
Diffstat (limited to 'jabber-fallback-lib/hexrgb.el')
-rw-r--r-- | jabber-fallback-lib/hexrgb.el | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/jabber-fallback-lib/hexrgb.el b/jabber-fallback-lib/hexrgb.el new file mode 100644 index 0000000..57f2c2c --- /dev/null +++ b/jabber-fallback-lib/hexrgb.el @@ -0,0 +1,731 @@ +;;; 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 |