1 // ansi_colours – true-colour ↔ ANSI terminal palette converter
2 // Copyright 2018 by Michał Nazarewicz <mina86@mina86.com>
3 //
4 // ansi_colours is free software: you can redistribute it and/or modify it
5 // under the terms of the GNU Lesser General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or (at
7 // your option) any later version.
8 //
9 // ansi_colours is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU Lesser General Public License
15 // along with ansi_colours.  If not, see <http://www.gnu.org/licenses/>.
16 
17 //! `ansi_colours` is a library which converts between 24-bit sRGB colours and
18 //! 8-bit colour palette used by ANSI terminals such as xterm on rxvt-unicode in
19 //! 256-colour mode.
20 //!
21 //! The most common use case is when using 24-bit colours in a terminal emulator
22 //! which only support 8-bit colour palette.  This package allows true-colours
23 //! to be approximated by values supported by the terminal.
24 //!
25 //! When mapping true-colour into available 256-colour palette (of which only
26 //! 240 are actually usable), this package tries to balance accuracy and
27 //! performance.  It doesn’t implement the fastest algorithm nor is it the most
28 //! accurate, instead it uses a formula which should be fast enough and accurate
29 //! enough for most use-cases.
30 //!
31 //! ## Usage
32 //!
33 //! Using this library with Cargo projects is as simple as adding a single
34 //! dependency:
35 //!
36 //! ```toml
37 //! [dependencies]
38 //! ansi_colours = "^1.0"
39 //! ```
40 //!
41 //! and then using one of the two functions that the library provides:
42 //!
43 //! ```rust
44 //! extern crate ansi_colours;
45 //!
46 //! use ansi_colours::*;
47 //!
48 //! fn main() {
49 //!     // Colour at given index:
50 //!     println!("{:-3}: {:?}", 50, rgb_from_ansi256(50));
51 //!
52 //!     // Approximate true-colour by colour in the palette:
53 //!     let rgb = (100, 200, 150);
54 //!     let index = ansi256_from_rgb(rgb);
55 //!     println!("{:?} ~ {:-3} {:?}", rgb, index, rgb_from_ansi256(index));
56 //! }
57 //! ```
58 
59 mod externs;
60 
61 /// Returns sRGB colour corresponding to the index in the 256-colour ANSI
62 /// palette.
63 ///
64 /// The first 16 colours (so-called system colours) are not standardised and
65 /// terminal emulators often allow them to be customised.  Because of this,
66 /// their value should not be relied upon.  For system colours, this function
67 /// returns default colours used by XTerm.
68 ///
69 /// Remaining 240 colours consist of a 6×6×6 colour cube and a 24-step greyscale
70 /// ramp.  Those are standardised and thus should be the same on every terminal
71 /// which supports 256-colour colour palette.
72 ///
73 /// # Examples
74 ///
75 ///
76 /// ```
77 /// assert_eq!((  0,   0,   0), ansi_colours::rgb_from_ansi256( 16));
78 /// assert_eq!(( 95, 135, 175), ansi_colours::rgb_from_ansi256( 67));
79 /// assert_eq!((255, 255, 255), ansi_colours::rgb_from_ansi256(231));
80 /// assert_eq!((238, 238, 238), ansi_colours::rgb_from_ansi256(255));
81 /// ```
82 #[inline]
rgb_from_ansi256(idx: u8) -> (u8, u8, u8)83 pub fn rgb_from_ansi256(idx: u8) -> (u8, u8, u8) {
84     let rgb = unsafe { externs::rgb_from_ansi256(idx) };
85     ((rgb >> 16) as u8, (rgb >>  8) as u8, rgb as u8)
86 }
87 
88 /// Returns index of a colour in 256-colour ANSI palette approximating given
89 /// sRGB colour.
90 ///
91 /// Because the first 16 colours of the palette are not standardised and usually
92 /// user-configurable, the function essentially ignores them.
93 ///
94 /// Th first argument uses `AsRGB` trait so that the function can be called in
95 /// multiple ways using different representations of RGB colours such as
96 /// `0xRRGGBB` integer, `(r, g, b)` tuple or `[r, g, b]` array.
97 ///
98 /// # Examples
99 ///
100 ///
101 /// ```
102 /// assert_eq!( 16, ansi_colours::ansi256_from_rgb(0x000000));
103 /// assert_eq!( 16, ansi_colours::ansi256_from_rgb( (  1,   1,   1)));
104 /// assert_eq!( 16, ansi_colours::ansi256_from_rgb( [  0,   1,   2]));
105 /// assert_eq!( 67, ansi_colours::ansi256_from_rgb(&( 95, 135, 175)));
106 /// assert_eq!(231, ansi_colours::ansi256_from_rgb(&[255, 255, 255]));
107 /// ```
108 #[inline]
ansi256_from_rgb<C: AsRGB>(rgb: C) -> u8109 pub fn ansi256_from_rgb<C: AsRGB>(rgb: C) -> u8 {
110     unsafe { externs::ansi256_from_rgb(rgb.as_u32()) }
111 }
112 
113 /// A trait for types which (can) represent an sRGB colour.  Used to provide
114 /// overloaded versions of `ansi256_from_rgb` function.
115 pub trait AsRGB {
116     /// Returns representation of the sRGB colour as a 24-bit `0xRRGGBB`
117     /// integer.
as_u32(&self) -> u32118     fn as_u32(&self) -> u32;
119 }
120 
121 /// Representation of an RGB colour as 24-bit `0xRRGGBB` integer.
122 impl AsRGB for u32 {
as_u32(&self) -> u32123     fn as_u32(&self) -> u32 { *self }
124 }
125 
126 #[inline]
to_u32(r: u8, g: u8, b: u8) -> u32127 fn to_u32(r: u8, g: u8, b: u8) -> u32 {
128     ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
129 }
130 
131 impl AsRGB for (u8, u8, u8) {
as_u32(&self) -> u32132     fn as_u32(&self) -> u32 { to_u32(self.0, self.1, self.2) }
133 }
134 
135 impl AsRGB for [u8; 3] {
as_u32(&self) -> u32136     fn as_u32(&self) -> u32 { to_u32(self[0], self[1], self[2]) }
137 }
138 
139 impl<'a, T: AsRGB + ?Sized> AsRGB for &'a T {
as_u32(&self) -> u32140     fn as_u32(&self) -> u32 { (*self).as_u32() }
141 }
142