gemini-browser

A text-based gemini browser
git clone git://git.laack.co/gemini-browser.git
Log | Files | Refs | README

README.md (22334B)


      1 go-colorful
      2 ===========
      3 
      4 [![go reportcard](https://goreportcard.com/badge/github.com/lucasb-eyer/go-colorful)](https://goreportcard.com/report/github.com/lucasb-eyer/go-colorful)
      5 
      6 A library for playing with colors in Go. Supports Go 1.13 onwards.
      7 
      8 Why?
      9 ====
     10 I love games. I make games. I love detail and I get lost in detail.
     11 One such detail popped up during the development of [Memory Which Does Not Suck](https://github.com/lucasb-eyer/mwdns/),
     12 when we wanted the server to assign the players random colors. Sometimes
     13 two players got very similar colors, which bugged me. The very same evening,
     14 [I want hue](http://tools.medialab.sciences-po.fr/iwanthue/) was the top post
     15 on HackerNews' frontpage and showed me how to Do It Right™. Last but not
     16 least, there was no library for handling color spaces available in go. Colorful
     17 does just that and implements Go's `color.Color` interface.
     18 
     19 What?
     20 =====
     21 Go-Colorful stores colors in RGB and provides methods from converting these to various color-spaces. Currently supported colorspaces are:
     22 
     23 - **RGB:** All three of Red, Green and Blue in [0..1].
     24 - **HSL:** Hue in [0..360], Saturation and Luminance in [0..1]. For legacy reasons; please forget that it exists.
     25 - **HSV:** Hue in [0..360], Saturation and Value in [0..1]. You're better off using HCL, see below.
     26 - **Hex RGB:** The "internet" color format, as in #FF00FF.
     27 - **Linear RGB:** See [gamma correct rendering](http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/).
     28 - **CIE-XYZ:** CIE's standard color space, almost in [0..1].
     29 - **CIE-xyY:** encodes chromacity in x and y and luminance in Y, all in [0..1]
     30 - **CIE-L\*a\*b\*:** A *perceptually uniform* color space, i.e. distances are meaningful. L\* in [0..1] and a\*, b\* almost in [-1..1].
     31 - **CIE-L\*u\*v\*:** Very similar to CIE-L\*a\*b\*, there is [no consensus](http://en.wikipedia.org/wiki/CIELUV#Historical_background) on which one is "better".
     32 - **CIE-L\*C\*h° (HCL):** This is generally the [most useful](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/) one; CIE-L\*a\*b\* space in polar coordinates, i.e. a *better* HSV. H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*a\*b\*.
     33 - **CIE LCh(uv):** Called `LuvLCh` in code, this is a cylindrical transformation of the CIE-L\*u\*v\* color space. Like HCL above: H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*u\*v\*.
     34 - **HSLuv:** The better alternative to HSL, see [here](https://www.hsluv.org/) and [here](https://www.kuon.ch/post/2020-03-08-hsluv/). Hue in [0..360], Saturation and Luminance in [0..1].
     35 - **HPLuv:** A variant of HSLuv. The color space is smoother, but only pastel colors can be included. Because the valid colors are limited, it's easy to get invalid Saturation values way above 1.0, indicating the color can't be represented in HPLuv beccause it's not pastel.
     36 
     37 For the colorspaces where it makes sense (XYZ, Lab, Luv, HCl), the
     38 [D65](http://en.wikipedia.org/wiki/Illuminant_D65) is used as reference white
     39 by default but methods for using your own reference white are provided.
     40 
     41 A coordinate being *almost in* a range means that generally it is, but for very
     42 bright colors and depending on the reference white, it might overflow this
     43 range slightly. For example, C\* of #0000ff is 1.338.
     44 
     45 Unit-tests are provided.
     46 
     47 Nice, but what's it useful for?
     48 -------------------------------
     49 
     50 - Converting color spaces. Some people like to do that.
     51 - Blending (interpolating) between colors in a "natural" look by using the right colorspace.
     52 - Generating random colors under some constraints (e.g. colors of the same shade, or shades of one color.)
     53 - Generating gorgeous random palettes with distinct colors of a same temperature.
     54 
     55 What not (yet)?
     56 ===============
     57 There are a few features which are currently missing and might be useful.
     58 I just haven't implemented them yet because I didn't have the need for it.
     59 Pull requests welcome.
     60 
     61 - Sorting colors (potentially using above mentioned distances)
     62 
     63 So which colorspace should I use?
     64 =================================
     65 It depends on what you want to do. I think the folks from *I want hue* are
     66 on-spot when they say that RGB fits to how *screens produce* color, CIE L\*a\*b\*
     67 fits how *humans perceive* color and HCL fits how *humans think* colors.
     68 
     69 Whenever you'd use HSV, rather go for CIE-L\*C\*h°. for fixed lightness L\* and
     70 chroma C\* values, the hue angle h° rotates through colors of the same
     71 perceived brightness and intensity.
     72 
     73 How?
     74 ====
     75 
     76 ### Installing
     77 Installing the library is as easy as
     78 
     79 ```bash
     80 $ go get github.com/lucasb-eyer/go-colorful
     81 ```
     82 
     83 The package can then be used through an
     84 
     85 ```go
     86 import "github.com/lucasb-eyer/go-colorful"
     87 ```
     88 
     89 ### Basic usage
     90 
     91 Create a beautiful blue color using different source space:
     92 
     93 ```go
     94 // Any of the following should be the same
     95 c := colorful.Color{0.313725, 0.478431, 0.721569}
     96 c, err := colorful.Hex("#517AB8")
     97 if err != nil {
     98     log.Fatal(err)
     99 }
    100 c = colorful.Hsv(216.0, 0.56, 0.722)
    101 c = colorful.Xyz(0.189165, 0.190837, 0.480248)
    102 c = colorful.Xyy(0.219895, 0.221839, 0.190837)
    103 c = colorful.Lab(0.507850, 0.040585,-0.370945)
    104 c = colorful.Luv(0.507849,-0.194172,-0.567924)
    105 c = colorful.Hcl(276.2440, 0.373160, 0.507849)
    106 fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B)
    107 ```
    108 
    109 And then converting this color back into various color spaces:
    110 
    111 ```go
    112 hex := c.Hex()
    113 h, s, v := c.Hsv()
    114 x, y, z := c.Xyz()
    115 x, y, Y := c.Xyy()
    116 l, a, b := c.Lab()
    117 l, u, v := c.Luv()
    118 h, c, l := c.Hcl()
    119 ```
    120 
    121 Note that, because of Go's unfortunate choice of requiring an initial uppercase,
    122 the name of the functions relating to the xyY space are just off. If you have
    123 any good suggestion, please open an issue. (I don't consider XyY good.)
    124 
    125 ### The `color.Color` interface
    126 Because a `colorful.Color` implements Go's `color.Color` interface (found in the
    127 `image/color` package), it can be used anywhere that expects a `color.Color`.
    128 
    129 Furthermore, you can convert anything that implements the `color.Color` interface
    130 into a `colorful.Color` using the `MakeColor` function:
    131 
    132 ```go
    133 c, ok := colorful.MakeColor(color.Gray16{12345})
    134 ```
    135 
    136 **Caveat:** Be aware that this latter conversion (using `MakeColor`) hits a
    137 corner-case when alpha is exactly zero. Because `color.Color` uses pre-multiplied
    138 alpha colors, this means the RGB values are lost (set to 0) and it's impossible
    139 to recover them. In such a case `MakeColor` will return `false` as its second value.
    140 
    141 ### Comparing colors
    142 In the RGB color space, the Euclidian distance between colors *doesn't* correspond
    143 to visual/perceptual distance. This means that two pairs of colors which have the
    144 same distance in RGB space can look much further apart. This is fixed by the
    145 CIE-L\*a\*b\*, CIE-L\*u\*v\* and CIE-L\*C\*h° color spaces.
    146 Thus you should only compare colors in any of these space.
    147 (Note that the distance in CIE-L\*a\*b\* and CIE-L\*C\*h° are the same, since it's the same space but in cylindrical coordinates)
    148 
    149 ![Color distance comparison](doc/colordist/colordist.png)
    150 
    151 The two colors shown on the top look much more different than the two shown on
    152 the bottom. Still, in RGB space, their distance is the same.
    153 Here is a little example program which shows the distances between the top two
    154 and bottom two colors in RGB, CIE-L\*a\*b\* and CIE-L\*u\*v\* space. You can find it in `doc/colordist/colordist.go`.
    155 
    156 ```go
    157 package main
    158 
    159 import "fmt"
    160 import "github.com/lucasb-eyer/go-colorful"
    161 
    162 func main() {
    163 	c1a := colorful.Color{150.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
    164 	c1b := colorful.Color{53.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
    165 	c2a := colorful.Color{10.0 / 255.0, 150.0 / 255.0, 50.0 / 255.0}
    166 	c2b := colorful.Color{99.9 / 255.0, 150.0 / 255.0, 10.0 / 255.0}
    167 
    168 	fmt.Printf("DistanceRgb:       c1: %v\tand c2: %v\n", c1a.DistanceRgb(c1b), c2a.DistanceRgb(c2b))
    169 	fmt.Printf("DistanceLab:       c1: %v\tand c2: %v\n", c1a.DistanceLab(c1b), c2a.DistanceLab(c2b))
    170 	fmt.Printf("DistanceLuv:       c1: %v\tand c2: %v\n", c1a.DistanceLuv(c1b), c2a.DistanceLuv(c2b))
    171 	fmt.Printf("DistanceCIE76:     c1: %v\tand c2: %v\n", c1a.DistanceCIE76(c1b), c2a.DistanceCIE76(c2b))
    172 	fmt.Printf("DistanceCIE94:     c1: %v\tand c2: %v\n", c1a.DistanceCIE94(c1b), c2a.DistanceCIE94(c2b))
    173 	fmt.Printf("DistanceCIEDE2000: c1: %v\tand c2: %v\n", c1a.DistanceCIEDE2000(c1b), c2a.DistanceCIEDE2000(c2b))
    174 }
    175 ```
    176 
    177 Running the above program shows that you should always prefer any of the CIE distances:
    178 
    179 ```bash
    180 $ go run colordist.go
    181 DistanceRgb:       c1: 0.3803921568627451	and c2: 0.3858713931171159
    182 DistanceLab:       c1: 0.32048458312798056	and c2: 0.24397151758565272
    183 DistanceLuv:       c1: 0.5134369614199698	and c2: 0.2568692839860636
    184 DistanceCIE76:     c1: 0.32048458312798056	and c2: 0.24397151758565272
    185 DistanceCIE94:     c1: 0.19799168128511324	and c2: 0.12207136371167401
    186 DistanceCIEDE2000: c1: 0.17274551120971166	and c2: 0.10665210031428465
    187 ```
    188 
    189 It also shows that `DistanceLab` is more formally known as `DistanceCIE76` and
    190 has been superseded by the slightly more accurate, but much more expensive
    191 `DistanceCIE94` and `DistanceCIEDE2000`.
    192 
    193 Note that `AlmostEqualRgb` is provided mainly for (unit-)testing purposes. Use
    194 it only if you really know what you're doing. It will eat your cat.
    195 
    196 ### Blending colors
    197 Blending is highly connected to distance, since it basically "walks through" the
    198 colorspace thus, if the colorspace maps distances well, the walk is "smooth".
    199 
    200 Colorful comes with blending functions in RGB, HSV and any of the LAB spaces.
    201 Of course, you'd rather want to use the blending functions of the LAB spaces since
    202 these spaces map distances well but, just in case, here is an example showing
    203 you how the blendings (`#fdffcc` to `#242a42`) are done in the various spaces:
    204 
    205 ![Blending colors in different spaces.](doc/colorblend/colorblend.png)
    206 
    207 What you see is that HSV is really bad: it adds some green, which is not present
    208 in the original colors at all! RGB is much better, but it stays light a little
    209 too long. LUV and LAB both hit the right lightness but LAB has a little more
    210 color. HCL works in the same vein as HSV (both cylindrical interpolations) but
    211 it does it right in that there is no green appearing and the lighthness changes
    212 in a linear manner.
    213 
    214 While this seems all good, you need to know one thing: When interpolating in any
    215 of the CIE color spaces, you might get invalid RGB colors! This is important if
    216 the starting and ending colors are user-input or random. An example of where this
    217 happens is when blending between `#eeef61` and `#1e3140`:
    218 
    219 ![Invalid RGB colors may crop up when blending in CIE spaces.](doc/colorblend/invalid.png)
    220 
    221 You can test whether a color is a valid RGB color by calling the `IsValid` method
    222 and indeed, calling IsValid will return false for the redish colors on the bottom.
    223 One way to "fix" this is to get a valid color close to the invalid one by calling
    224 `Clamped`, which always returns a nearby valid color. Doing this, we get the
    225 following result, which is satisfactory:
    226 
    227 ![Fixing invalid RGB colors by clamping them to the valid range.](doc/colorblend/clamped.png)
    228 
    229 The following is the code creating the above three images; it can be found in `doc/colorblend/colorblend.go`
    230 
    231 ```go
    232 package main
    233 
    234 import "fmt"
    235 import "github.com/lucasb-eyer/go-colorful"
    236 import "image"
    237 import "image/draw"
    238 import "image/png"
    239 import "os"
    240 
    241 func main() {
    242     blocks := 10
    243     blockw := 40
    244     img := image.NewRGBA(image.Rect(0,0,blocks*blockw,200))
    245 
    246     c1, _ := colorful.Hex("#fdffcc")
    247     c2, _ := colorful.Hex("#242a42")
    248 
    249     // Use these colors to get invalid RGB in the gradient.
    250     //c1, _ := colorful.Hex("#EEEF61")
    251     //c2, _ := colorful.Hex("#1E3140")
    252 
    253     for i := 0 ; i < blocks ; i++ {
    254         draw.Draw(img, image.Rect(i*blockw,  0,(i+1)*blockw, 40), &image.Uniform{c1.BlendHsv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
    255         draw.Draw(img, image.Rect(i*blockw, 40,(i+1)*blockw, 80), &image.Uniform{c1.BlendLuv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
    256         draw.Draw(img, image.Rect(i*blockw, 80,(i+1)*blockw,120), &image.Uniform{c1.BlendRgb(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
    257         draw.Draw(img, image.Rect(i*blockw,120,(i+1)*blockw,160), &image.Uniform{c1.BlendLab(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
    258         draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
    259 
    260         // This can be used to "fix" invalid colors in the gradient.
    261         //draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1)).Clamped()}, image.Point{}, draw.Src)
    262     }
    263 
    264     toimg, err := os.Create("colorblend.png")
    265     if err != nil {
    266         fmt.Printf("Error: %v", err)
    267         return
    268     }
    269     defer toimg.Close()
    270 
    271     png.Encode(toimg, img)
    272 }
    273 ```
    274 
    275 #### Generating color gradients
    276 A very common reason to blend colors is creating gradients. There is an example
    277 program in [doc/gradientgen.go](doc/gradientgen/gradientgen.go); it doesn't use any API
    278 which hasn't been used in the previous example code, so I won't bother pasting
    279 the code in here. Just look at that gorgeous gradient it generated in HCL space:
    280 
    281 !["Spectral" colorbrewer gradient in HCL space.](doc/gradientgen/gradientgen.png)
    282 
    283 ### Getting random colors
    284 It is sometimes necessary to generate random colors. You could simply do this
    285 on your own by generating colors with random values. By restricting the random
    286 values to a range smaller than [0..1] and using a space such as CIE-H\*C\*l° or
    287 HSV, you can generate both random shades of a color or random colors of a
    288 lightness:
    289 
    290 ```go
    291 random_blue := colorful.Hcl(180.0+rand.Float64()*50.0, 0.2+rand.Float64()*0.8, 0.3+rand.Float64()*0.7)
    292 random_dark := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), rand.Float64()*0.4)
    293 random_light := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), 0.6+rand.Float64()*0.4)
    294 ```
    295 
    296 Since getting random "warm" and "happy" colors is quite a common task, there
    297 are some helper functions:
    298 
    299 ```go
    300 colorful.WarmColor()
    301 colorful.HappyColor()
    302 colorful.FastWarmColor()
    303 colorful.FastHappyColor()
    304 ```
    305 
    306 The ones prefixed by `Fast` are faster but less coherent since they use the HSV
    307 space as opposed to the regular ones which use CIE-L\*C\*h° space. The
    308 following picture shows the warm colors in the top two rows and happy colors
    309 in the bottom two rows. Within these, the first is the regular one and the
    310 second is the fast one.
    311 
    312 ![Warm, fast warm, happy and fast happy random colors, respectively.](doc/colorgens/colorgens.png)
    313 
    314 Don't forget to initialize the random seed! You can see the code used for
    315 generating this picture in `doc/colorgens/colorgens.go`.
    316 
    317 ### Getting random palettes
    318 As soon as you need to generate more than one random color, you probably want
    319 them to be distinguishible. Playing against an opponent which has almost the
    320 same blue as I do is not fun. This is where random palettes can help.
    321 
    322 These palettes are generated using an algorithm which ensures that all colors
    323 on the palette are as distinguishible as possible. Again, there is a `Fast`
    324 method which works in HSV and is less perceptually uniform and a non-`Fast`
    325 method which works in CIE spaces. For more theory on `SoftPalette`, check out
    326 [I want hue](http://tools.medialab.sciences-po.fr/iwanthue/theory.php). Yet
    327 again, there is a `Happy` and a `Warm` version, which do what you expect, but
    328 now there is an additional `Soft` version, which is more configurable: you can
    329 give a constraint on the color space in order to get colors within a certain *feel*.
    330 
    331 Let's start with the simple methods first, all they take is the amount of
    332 colors to generate, which could, for example, be the player count. They return
    333 an array of `colorful.Color` objects:
    334 
    335 ```go
    336 pal1, err1 := colorful.WarmPalette(10)
    337 pal2 := colorful.FastWarmPalette(10)
    338 pal3, err3 := colorful.HappyPalette(10)
    339 pal4 := colorful.FastHappyPalette(10)
    340 pal5, err5 := colorful.SoftPalette(10)
    341 ```
    342 
    343 Note that the non-fast methods *may* fail if you ask for way too many colors.
    344 Let's move on to the advanced one, namely `SoftPaletteEx`. Besides the color
    345 count, this function takes a `SoftPaletteSettings` object as argument. The
    346 interesting part here is its `CheckColor` member, which is a boolean function
    347 taking three floating points as arguments: `l`, `a` and `b`. This function
    348 should return `true` for colors which lie within the region you want and `false`
    349 otherwise. The other members are `Iteration`, which should be within [5..100]
    350 where higher means slower but more exact palette, and `ManySamples` which you
    351 should set to `true` in case your `CheckColor` constraint rejects a large part
    352 of the color space.
    353 
    354 For example, to create a palette of 10 brownish colors, you'd call it like this:
    355 
    356 ```go
    357 func isbrowny(l, a, b float64) bool {
    358     h, c, L := colorful.LabToHcl(l, a, b)
    359     return 10.0 < h && h < 50.0 && 0.1 < c && c < 0.5 && L < 0.5
    360 }
    361 // Since the above function is pretty restrictive, we set ManySamples to true.
    362 brownies := colorful.SoftPaletteEx(10, colorful.SoftPaletteSettings{isbrowny, 50, true})
    363 ```
    364 
    365 The following picture shows the palettes generated by all of these methods
    366 (sourcecode in `doc/palettegens/palettegens.go`), in the order they were presented, i.e.
    367 from top to bottom: `Warm`, `FastWarm`, `Happy`, `FastHappy`, `Soft`,
    368 `SoftEx(isbrowny)`. All of them contain some randomness, so YMMV.
    369 
    370 ![All example palettes](doc/palettegens/palettegens.png)
    371 
    372 Again, the code used for generating the above image is available as [doc/palettegens/palettegens.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/palettegens/palettegens.go).
    373 
    374 ### Sorting colors
    375 TODO: Sort using dist fn.
    376 
    377 ### Using linear RGB for computations
    378 There are two methods for transforming RGB<->Linear RGB: a fast and almost precise one,
    379 and a slow and precise one.
    380 
    381 ```go
    382 r, g, b := colorful.Hex("#FF0000").FastLinearRgb()
    383 ```
    384 
    385 TODO: describe some more.
    386 
    387 ### Want to use some other reference point?
    388 
    389 ```go
    390 c := colorful.LabWhiteRef(0.507850, 0.040585,-0.370945, colorful.D50)
    391 l, a, b := c.LabWhiteRef(colorful.D50)
    392 ```
    393 
    394 ### Reading and writing colors from databases
    395 
    396 The type `HexColor` makes it easy to store colors as strings in a database. It
    397 implements the [https://godoc.org/database/sql#Scanner](database/sql.Scanner)
    398 and [database/sql/driver.Value](https://godoc.org/database/sql/driver.Value)
    399 interfaces which provide automatic type conversion.
    400 
    401 Example:
    402 
    403 ```go
    404 var hc HexColor
    405 _, err := db.QueryRow("SELECT '#ff0000';").Scan(&hc)
    406 // hc == HexColor{R: 1, G: 0, B: 0}; err == nil
    407 ```
    408 
    409 FAQ
    410 ===
    411 
    412 ### Q: I get all f!@#ed up values! Your library sucks!
    413 A: You probably provided values in the wrong range. For example, RGB values are
    414 expected to reside between 0 and 1, *not* between 0 and 255. Normalize your colors.
    415 
    416 ### Q: Lab/Luv/HCl seem broken! Your library sucks!
    417 They look like this:
    418 
    419 <img height="150" src="https://user-images.githubusercontent.com/3779568/28646900-6548040c-7264-11e7-8f12-81097a97c260.png">
    420 
    421 A: You're likely trying to generate and display colors that can't be represented by RGB,
    422 and thus monitors. When you're trying to convert, say, `HCL(190.0, 1.0, 1.0).RGB255()`,
    423 you're asking for RGB values of `(-2105.254  300.680  286.185)`, which clearly don't exist,
    424 and the `RGB255` function just casts these numbers to `uint8`, creating wrap-around and
    425 what looks like a completely broken gradient. What you want to do, is either use more
    426 reasonable values of colors which actually exist in RGB, or just `Clamp()` the resulting
    427 color to its nearest existing one, living with the consequences:
    428 `HCL(190.0, 1.0, 1.0).Clamp().RGB255()`. It will look something like this:
    429 
    430 <img height="150" src="https://user-images.githubusercontent.com/1476029/29596343-9a8c62c6-8771-11e7-9026-b8eb8852cc4a.png">
    431 
    432 [Here's an issue going in-depth about this](https://github.com/lucasb-eyer/go-colorful/issues/14),
    433 as well as [my answer](https://github.com/lucasb-eyer/go-colorful/issues/14#issuecomment-324205385),
    434 both with code and pretty pictures. Also note that this was somewhat covered above in the
    435 ["Blending colors" section](https://github.com/lucasb-eyer/go-colorful#blending-colors).
    436 
    437 ### Q: In a tight loop, conversion to Lab/Luv/HCl/... are slooooow!
    438 A: Yes, they are.
    439 This library aims for correctness, readability, and modularity; it wasn't written with speed in mind.
    440 A large part of the slowness comes from these conversions going through `LinearRgb` which uses powers.
    441 I implemented a fast approximation to `LinearRgb` called `FastLinearRgb` by using Taylor approximations.
    442 The approximation is roughly 5x faster and precise up to roughly 0.5%,
    443 the major caveat being that if the input values are outside the range 0-1, accuracy drops dramatically.
    444 You can use these in your conversions as follows:
    445 
    446 ```go
    447 col := // Get your color somehow
    448 l, a, b := XyzToLab(LinearRgbToXyz(col.LinearRgb()))
    449 ```
    450 
    451 If you need faster versions of `Distance*` and `Blend*` that make use of this fast approximation,
    452 feel free to implement them and open a pull-request, I'll happily accept.
    453 
    454 The derivation of these functions can be followed in [this Jupyter notebook](doc/LinearRGB Approximations.ipynb).
    455 Here's the main figure showing the approximation quality:
    456 
    457 ![approximation quality](doc/approx-quality.png)
    458 
    459 More speed could be gained by using SIMD instructions in many places.
    460 You can also get more speed for specific conversions by approximating the full conversion function,
    461 but that is outside the scope of this library.
    462 Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation,
    463 see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details.
    464 
    465 ### Q: Why would `MakeColor` ever fail!?
    466 A: `MakeColor` fails when the alpha channel is zero. In that case, the
    467 conversion is undefined. See [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21)
    468 as well as the short caveat note in the ["The `color.Color` interface"](README.md#the-colorcolor-interface)
    469 section above.
    470 
    471 Who?
    472 ====
    473 
    474 This library was developed by Lucas Beyer with contributions from
    475 Bastien Dejean (@baskerville), Phil Kulak (@pkulak) and Christian Muehlhaeuser (@muesli).
    476 
    477 It is now maintained by makeworld (@makeworld-the-better-one).
    478 
    479 
    480 ## License
    481 
    482 This repo is under the MIT license, see [LICENSE](LICENSE) for details.