• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

doc/H03-May-2022-

.gitignoreH A D02-Nov-2019313

.travis.ymlH A D02-Nov-2019324

LICENSEH A D02-Nov-20191 KiB

README.mdH A D02-Nov-201922.7 KiB

colorgens.goH A D02-Nov-20191.3 KiB

colorgens_test.goH A D02-Nov-2019786

colors.goH A D02-Nov-201924.2 KiB

colors_test.goH A D02-Nov-201924.8 KiB

go.modH A D02-Nov-201951

go.sumH A D02-Nov-20190

happy_palettegen.goH A D02-Nov-2019746

hexcolor.goH A D02-Nov-2019771

hexcolor_test.goH A D02-Nov-2019775

soft_palettegen.goH A D02-Nov-20195.7 KiB

soft_palettegen_test.goH A D02-Nov-20191.8 KiB

warm_palettegen.goH A D02-Nov-2019757

README.md

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