From 8f316447fffab755c012bed88c52476ee5d8306f Mon Sep 17 00:00:00 2001 From: kimikage Date: Wed, 4 Sep 2019 18:48:34 +0900 Subject: [PATCH 01/33] Fix negative size swatches in output of show() (#341) (#342) --- src/display.jl | 66 +++++++++++++----------- test/colormaps.jl | 6 +-- test/display.jl | 127 ++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 4 files changed, 165 insertions(+), 35 deletions(-) create mode 100644 test/display.jl diff --git a/src/display.jl b/src/display.jl index c30b966b..b7432fe9 100644 --- a/src/display.jl +++ b/src/display.jl @@ -3,62 +3,68 @@ const max_width = 180 const max_height = 150 -const max_pixel_size = 25 +const max_swatch_size = 25 -function Base.show(io::IO, ::MIME"image/svg+xml", c::Color) +function Base.show(io::IO, mime::MIME"image/svg+xml", c::Color) + write_declaration(io, mime) write(io, """ - - - + """) + flush(io) # return nothing end -function Base.show(io::IO, ::MIME"image/svg+xml", +function Base.show(io::IO, mime::MIME"image/svg+xml", cs::AbstractVector{T}) where T <: Color - show(io, MIME"image/svg+xml"(), Base.ReshapedArray(cs, (1, length(cs)), ())) + show(io, mime, Base.ReshapedArray(cs, (1, length(cs)), ())) end -function Base.show(io::IO, ::MIME"image/svg+xml", +function Base.show(io::IO, mime::MIME"image/svg+xml", cs::AbstractMatrix{T}) where T <: Color m, n = size(cs) - apsect_ratio = clamp(n / m, - max_pixel_size / max_height, - max_width / max_pixel_size) - pixel_aspect = apsect_ratio / (n / m) - scale_factor = min(max_width / (n * max_pixel_size * pixel_aspect), - max_height / (m * max_pixel_size), - 1) - ysize = max_pixel_size * scale_factor - xsize = ysize * pixel_aspect + w = min(n * max_swatch_size, max_width) + h = min(m * max_swatch_size, max_height) + if max_width * m > max_height * n + w = max(h * n / m, max_swatch_size) + else + h = max(w * m / n, max_swatch_size) + end - xpad = n > 50 ? 0 : 1 - ypad = m > 28 ? 0 : 1 + simplify(x) = replace(string(round(x, digits=2)), r"(^0(?=\.))|(.0+$)"=>"") + sw = simplify(1 - (w / n < 3.6 ? 0 : n / w)) + sh = simplify(1 - (h / m < 3.6 ? 0 : m / h)) + comp(x) = round(x)==round(x, digits=2) ? Int(round(x)) : round(x, digits=2) + write_declaration(io, mime) write(io, """ - - + width="$(comp(w))mm" height="$(comp(h))mm" + viewBox="0 0 $n $m" preserveAspectRatio="none" + shape-rendering="crispEdges" stroke="none"> """) for i in 1:m, j in 1:n - c = ndims(cs) == 2 ? cs[i, j] : cs[j] + c = hex(cs[i, j]) write(io, """ - + """) end write(io, "") + flush(io) # return nothing +end + +function write_declaration(io::IO, ::MIME"image/svg+xml") + write(io, + """ + + + """) + flush(io) # return nothing end diff --git a/test/colormaps.jl b/test/colormaps.jl index 6f7f28c5..f2468466 100644 --- a/test/colormaps.jl +++ b/test/colormaps.jl @@ -1,9 +1,5 @@ @testset "Colormaps" begin @test length(colormap("RdBu", 100)) == 100 - # test ability to add to previously shown array of colors - issue #328 - a = [colorant"white", colorant"red"] - show(stdout, MIME"image/svg+xml"(), a) - push!(a, colorant"blue") - @test length(a) == 3 + # TODO: add more tests end diff --git a/test/display.jl b/test/display.jl new file mode 100644 index 00000000..5fa71c8e --- /dev/null +++ b/test/display.jl @@ -0,0 +1,127 @@ +@testset "Display" begin + function count_filled_rect(svg::AbstractString) + n = 0 + i = firstindex(svg) + while true + r = findnext(r"]*\sfill=\"#[0-9A-F]{6}\"[^>]+>", svg, i) + r === nothing && return n + n += 1 + i = last(r) + end + end + + # test ability to add to previously shown array of colors - issue #328 + a = [colorant"white", colorant"red"] + buf = IOBuffer() + show(buf, MIME"image/svg+xml"(), a) + take!(buf) + push!(a, colorant"blue") + @test length(a) == 3 + + + # the following tests depend on the constants in "src/display.jl" + + # single color + + show(buf, MIME"image/svg+xml"(), colorant"hsl(120, 100%, 50%)") + single = String(take!(buf)) + @test occursin(r"]+\swidth=\"25mm\"", single) + @test occursin(r"]+\sheight=\"25mm\"", single) + @test occursin(r"\sfill=\"#00FF00\"", single) + @test occursin(r"$", single) + + + # vectors + + # square swatches + v3 = [colorant"red", colorant"green", colorant"blue"] + show(buf, MIME"image/svg+xml"(), v3) + vector3 = String(take!(buf)) + @test occursin(r"]*\swidth=\"75mm\"", vector3) + @test occursin(r"]*\sheight=\"25mm\"", vector3) + @test occursin(r"]*\swidth=\".96\"", vector3) # 0.96 = 24mm/25mm + @test occursin(r"]*\sheight=\".96\"", vector3) # height = width + @test occursin(r"]*\sfill=\"#FF0000\"", vector3) + @test occursin(r"]*\sfill=\"#008000\"", vector3) + @test occursin(r"]*\sfill=\"#0000FF\"", vector3) + @test occursin(r"$", vector3) + + # rectangle swatches + show(buf, MIME"image/svg+xml"(), colormap("RdBu", 100)) + vec100 = String(take!(buf)) + @test occursin(r"]*\swidth=\"180mm\"", vec100) # max_width + @test occursin(r"]*\sheight=\"25mm\"", vec100) + @test occursin(r"]*\swidth=\"1\"", vec100) # no padding + @test occursin(r"]*\sheight=\".96\"", vec100) # 0.96 = 24mm/25mm + @test count_filled_rect(vec100) == 100 + + + # m * n matrices + + # n * max_swatch_size <= max_width && + # m * max_swatch_size <= max_height + # square swatches + m2x2 = [colorant"white" colorant"blue" + colorant"black" colorant"cyan"] + show(buf, MIME"image/svg+xml"(), m2x2) + mat2x2 = String(take!(buf)) + @test occursin(r"]*\swidth=\"50mm\"", mat2x2) + @test occursin(r"]*\sheight=\"50mm\"", mat2x2) + @test occursin(r"]*\swidth=\".96\"", mat2x2) # 0.96 = 24mm/25mm + @test occursin(r"]*\sheight=\".96\"", mat2x2) # height = width + @test occursin(r"]*\sfill=\"#FFFFFF\"", mat2x2) + @test occursin(r"]*\sfill=\"#0000FF\"", mat2x2) + @test occursin(r"]*\sfill=\"#000000\"", mat2x2) + @test occursin(r"]*\sfill=\"#00FFFF\"", mat2x2) + @test occursin(r"$", vector3) + + # n * max_swatch_size > max_width && + # m * max_swatch_size <= max_height + # square swatches + show(buf, MIME"image/svg+xml"(), rand(RGB, 2, 9)) + mat2x9 = String(take!(buf)) + @test occursin(r"]*\swidth=\"180mm\"", mat2x9) # max_width + @test occursin(r"]*\sheight=\"40mm\"", mat2x9) # 40mm = 180mm/9 * 2 + @test occursin(r"]*\swidth=\".95\"", mat2x9) # 0.95 = 19mm/20mm + @test occursin(r"]*\sheight=\".95\"", mat2x9) # height = width + @test count_filled_rect(mat2x9) == 18 + + # n * max_swatch_size <= max_width && + # m * max_swatch_size > max_height + # square swatches + show(buf, MIME"image/svg+xml"(), rand(RGB, 10, 2)) + mat10x2 = String(take!(buf)) + @test occursin(r"]*\swidth=\"30mm\"", mat10x2) # 30mm = 150mm/10 * 2 + @test occursin(r"]*\sheight=\"150mm\"", mat10x2) # max_height + @test occursin(r"]*\swidth=\".93\"", mat10x2) # 0.93 = 14mm/15mm + @test occursin(r"]*\sheight=\".93\"", mat10x2) # height = width + @test count_filled_rect(mat10x2) == 20 + + # issue #341 + # n * max_swatch_size > max_width && + # m * max_swatch_size > max_height && + # max_width / n * m >= max_swatch_size + # square swatches + show(buf, MIME"image/svg+xml"(), rand(RGB, 28, 181)) + mat28x181 = String(take!(buf)) + @test occursin(r"]*\swidth=\"180mm\"", mat28x181) # max_width + @test occursin(r"]*\sheight=\"27.85mm\"", mat28x181) # 180mm/181 * 28 + @test occursin(r"]*\swidth=\"1\"", mat28x181) # no padding + @test occursin(r"]*\sheight=\"1\"", mat28x181) # no padding + @test count_filled_rect(mat28x181) == 28*181 + + # issue #341 + # n * max_swatch_size > max_width && + # m * max_swatch_size > max_height && + # max_height / m * n < max_swatch_size + # rectangle swatches + # Keeping the aspect ratio 1:1 (i.e. square) seems to be more important + # than keeping the width/height >= 25mm, for large matrices. + show(buf, MIME"image/svg+xml"(), rand(RGB, 200, 28)) + mat200x28 = String(take!(buf)) + @test occursin(r"]*\swidth=\"25mm\"", mat200x28) # max_swatch_size + @test occursin(r"]*\sheight=\"150mm\"", mat200x28) # max_height + @test occursin(r"]*\swidth=\"1\"", mat200x28) # no padding + @test occursin(r"]*\sheight=\"1\"", mat200x28) # no padding + @test count_filled_rect(mat200x28) == 200*28 +end diff --git a/test/runtests.jl b/test/runtests.jl index 67e478a0..109e3653 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,4 +6,5 @@ include("conversion.jl") include("colormaps.jl") include("colordiff.jl") include("din99.jl") +include("display.jl") include("utilities.jl") From 8f1fabbde4cc0234ce00084becbcb44f85020186 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 4 Sep 2019 05:21:26 -0500 Subject: [PATCH 02/33] Document that `mid` affects only diverging colormaps (fixes #300) --- src/colormaps.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/colormaps.jl b/src/colormaps.jl index 3c13c0fa..b31afe03 100644 --- a/src/colormaps.jl +++ b/src/colormaps.jl @@ -242,7 +242,7 @@ end # Main function to handle different predefined colormaps # """ - colormap(cname, [N; mid, logscale, kvs...]) + colormap(cname, N=100; mid=0.5, logscale=false, kvs...]) Returns a predefined sequential or diverging colormap computed using the algorithm by Wijffelaars, M., et al. (2008). @@ -259,8 +259,12 @@ Sequential colormaps `cname` choices are: Diverging colormap choices are `RdBu`. Optionally, you can specify the number of colors `N` (default 100). -Keyword arguments include the position of the middle point `mid` (default 0.5) and the -possibility to switch to log scaling with `logscale` (default false). + +Extra control is provided by keyword arguments. +- `mid` sets the position of the midpoint for diverging colormaps. +- `logscale=true` uses logarithmically-spaced steps in the colormap. +You can also use keyword argument names that match the argument names in +[`sequential_palette`](@ref) or [`diverging_palette`](@ref). """ function colormap(cname::AbstractString, N::Int=100; mid=0.5, logscale=false, kvs...) From b39b9186672515f0c390271d57864f95b213ee25 Mon Sep 17 00:00:00 2001 From: cormullion Date: Wed, 4 Sep 2019 18:38:59 +0100 Subject: [PATCH 03/33] Update README, remove Pkgeval badges (#345) Farewell Pkg.evaluator badges, you were nice. --- README.md | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 062805c3..41934096 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,22 @@ # Colors -| **Documentation** | [**Package Evaluator**][pkgeval-link] | **Build Status** | **Code Coverage** | -|:---------------------------------------:|:-------------------------------------:|:-----------------------------------------:|:-------------------------------:| -| [![][docs-stable-img]][docs-stable-url] | [![][pkg-0.4-img]][pkg-0.4-url] | [![Build Status][travis-img]][travis-url] | [![][codecov-img]][codecov-url] | -| [![][docs-latest-img]][docs-latest-url] | [![][pkg-0.5-img]][pkg-0.5-url] | [![Build Status][appvey-img]][appvey-url] | | -| | [![][pkg-0.6-img]][pkg-0.6-url] | | | -| | [![][pkg-0.7-img]][pkg-0.7-url] | | | +| **Documentation** | **Build Status** | **Code Coverage** | +|:---------------------------------------:|:-----------------------------------------:|:-------------------------------:| +| [![][docs-stable-img]][docs-stable-url] | [![Build Status][travis-img]][travis-url] | [![][codecov-img]][codecov-url] | +| [![][docs-latest-img]][docs-latest-url] | [![Build Status][appvey-img]][appvey-url] | | +| | | | +| | | | This library provides a wide array of functions for dealing with color. This includes conversion between colorspaces, measuring distance between colors, simulating color blindness, parsing colors, and generating color scales for graphics. - - - - [docs-latest-img]: https://img.shields.io/badge/docs-latest-blue.svg [docs-latest-url]: http://juliagraphics.github.io/Colors.jl/latest/ [docs-stable-img]: https://img.shields.io/badge/docs-stable-blue.svg [docs-stable-url]: http://juliagraphics.github.io/Colors.jl/stable/ -[pkgeval-link]: http://pkg.julialang.org/?pkg=Colors - -[pkg-0.4-img]: http://pkg.julialang.org/badges/Colors_0.4.svg -[pkg-0.4-url]: http://pkg.julialang.org/detail/Colors.html - -[pkg-0.5-img]: http://pkg.julialang.org/badges/Colors_0.5.svg -[pkg-0.5-url]: http://pkg.julialang.org/detail/Colors.html - -[pkg-0.6-img]: http://pkg.julialang.org/badges/Colors_0.6.svg -[pkg-0.6-url]: http://pkg.julialang.org/detail/Colors.html - -[pkg-0.7-img]: http://pkg.julialang.org/badges/Colors_0.7.svg -[pkg-0.7-url]: http://pkg.julialang.org/?pkg=Colors&ver=0.7 - [travis-img]: https://travis-ci.org/JuliaGraphics/Colors.jl.svg?branch=master [travis-url]: https://travis-ci.org/JuliaGraphics/Colors.jl From 11d692ce3b8fdef37b5850c6d586e35ee15515d6 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 4 Sep 2019 05:46:23 -0500 Subject: [PATCH 04/33] Fix DIN conversions Fixes #256, fixes #259 --- src/conversions.jl | 80 +++++++++++++--------------------------------- test/din99.jl | 10 ++++++ 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/src/conversions.jl b/src/conversions.jl index e8393ff7..fad33ec1 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -354,8 +354,7 @@ cnvt(::Type{XYZ{T}}, c::LCHuv) where {T} = cnvt(XYZ{T}, convert(Luv{T}, c)) function cnvt(::Type{XYZ{T}}, c::DIN99d) where T # Go back to C-h space - # FIXME: Clean this up (why is there no atand?) - h = rad2deg(atan(c.b,c.a)) + 50 + h = atand(c.b, c.a) - 50 while h > 360; h -= 360; end while h < 0; h += 360; end @@ -363,14 +362,14 @@ function cnvt(::Type{XYZ{T}}, c::DIN99d) where T # Intermediate terms G = (exp(C/22.5)-1)/0.06 - f = G*sind(h - 50) - ee = G*cosd(h - 50) + f = G*sind(h) + ee = G*cosd(h) - l = (exp(c.l/325.22)-1)/0.0036 + l = (exp(c.l/325.221)-1)/0.0036 # a = ee*cosd(50) - f/1.14*sind(50) - a = ee*0.6427876096865393 - f/1.14*0.766044443118978 + a = ee*0.6427876096865394 - f/1.14*0.766044443118978 # b = ee*sind(50) - f/1.14*cosd(50) - b = ee*0.766044443118978 - f/1.14*0.6427876096865393 + b = ee*0.766044443118978 + f/1.14*0.6427876096865394 adj = convert(XYZ, Lab(l, a, b)) @@ -443,30 +442,16 @@ function cnvt(::Type{Lab{T}}, c::DIN99) where T # Calculate Chroma (C99) in the DIN99 space cc = sqrt(c.a^2 + c.b^2) - # NOTE: This is calculated in degrees, against the standard, to save - # computation steps later. - if (c.a > 0 && c.b >= 0) - h = atand(c.b/c.a) - elseif (c.a == 0 && c.b > 0) - h = 90 - elseif (c.a < 0) - h = 180+atand(c.b/c.a) - elseif (c.a == 0 && c.b < 0) - h = 270 - elseif (c.a > 0 && c.b <= 0) - h = 360 + atand(c.b/c.a) - else - h = 0 - end + h = atan(c.b, c.a) # Temporary variable for chroma g = (exp(0.045*cc*kch*ke)-1)/0.045 # Temporary redness - ee = g*cosd(h) + ee = g*cos(h) # Temporary yellowness - f = g*sind(h) + f = g*sin(h) # CIELAB a*b* # ciea = ee*cosd(16) - (f/0.7)*sind(16) @@ -492,19 +477,16 @@ function cnvt(::Type{Lab{T}}, c::DIN99o) where T co = sqrt(c.a^2 + c.b^2) # hue angle h99o - h = atan(c.b, c.a) - - # revert rotation by 26° - ho= rad2deg(h)-26 + h = atan(c.b, c.a) - 26*π/180 # revert logarithmic chroma compression g = (exp(co*kch*ke/23.0)-1)/0.075 # Temporary redness - eo = g*cosd(ho) + eo = g*cos(h) # Temporary yellowness - fo = g*sind(ho) + fo = g*sin(h) # CIELAB a*b* (revert b* axis compression) # ciea = eo*cosd(26) - (fo/0.83)*sind(26) @@ -606,26 +588,13 @@ function cnvt(::Type{DIN99{T}}, c::Lab) where T g = sqrt(ee^2 + f^2) # Hue angle - # Calculated in degrees, against the specification. - if (ee > 0 && f >= 0) - h = atand(f/ee) - elseif (ee == 0 && f > 0) - h = 90 - elseif (ee < 0) - h = 180+atand(f/ee) - elseif (ee == 0 && f < 0) - h = 270 - elseif (ee > 0 && f <= 0) - h = 360 + atand(f/ee) - else - h = 0 - end + h = atan(f, ee) # DIN99 chroma cc = log(1+0.045*g)/(0.045*kch*ke) # DIN99 chromaticities - a99, b99 = cc*cosd(h), cc*sind(h) + a99, b99 = cc*cos(h), cc*sin(h) DIN99{T}(l99, a99, b99) @@ -645,23 +614,22 @@ function cnvt(::Type{DIN99d{T}}, c::XYZ{T}) where T # Apply L*a*b*-space correction lab = convert(Lab, adj_c) - adj_L = 325.22*log(1+0.0036*lab.l) + adj_L = 325.221*log(1+0.0036*lab.l) # Calculate intermediate parameters # ee = lab.a*cosd(50) + lab.b*sind(50) - ee = lab.a*0.6427876096865393 + lab.b*0.766044443118978 + ee = lab.a*0.6427876096865394 + lab.b*0.766044443118978 # f = 1.14*(lab.b*cosd(50) - lab.a*sind(50)) - f = 1.14*(lab.b*0.6427876096865393 - lab.a*0.766044443118978) + f = 1.14*(lab.b*0.6427876096865394 - lab.a*0.766044443118978) G = sqrt(ee^2+f^2) # Calculate hue/chroma C = 22.5*log(1+0.06*G) - # FIXME: Clean this up (why is there no atand?) - h = rad2deg(atan(f,ee)) + 50 - while h > 360; h -= 360; end - while h < 0; h += 360; end + h = atan(f, ee) + 50*π/180 + while h > 2π; h -= 2π; end + while h < 0; h += 2π; end - DIN99d{T}(adj_L, C*cosd(h), C*sind(h)) + DIN99d{T}(adj_L, C*cos(h), C*sin(h)) end @@ -692,15 +660,13 @@ function cnvt(::Type{DIN99o{T}}, c::Lab) where T # Temporary value for chroma go = sqrt(eo^2 + fo^2) - ho = atan(fo,eo) - # rotation of the colorspace by 26° - h = rad2deg(ho) + 26 + h = atan(fo, eo) + 26*π/180 # DIN99o chroma (logarithmic compression) cc = 23.0*log(1+0.075*go)/(kch*ke) # DIN99o chromaticities - a99, b99 = cc*cosd(h), cc*sind(h) + a99, b99 = cc*cos(h), cc*sin(h) DIN99o{T}(l99, a99, b99) diff --git a/test/din99.jl b/test/din99.jl index 4a28b3fb..9db63347 100644 --- a/test/din99.jl +++ b/test/din99.jl @@ -42,4 +42,14 @@ using Colors @test (abs(colordiff(convert(DIN99, Lab(a...)), DIN99(b...); metric=metric)) < diffeps) end end + + # From #256 + for c in (RGB(1, 0.5, 0),) + @test colordiff(convert(RGB,convert(DIN99d, c)), c) < 1e-3 + end + for c in (RGBA(1,0.5,0,0.4),) + cc = convert(RGBA,convert(DIN99dA, c)) + @test colordiff(color(cc), color(c)) < 1e-3 + @test cc.alpha ≈ c.alpha atol=1e-3 + end end # @testset From 286292146187d7c5316e16e54dcf366c8958fc00 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 4 Sep 2019 06:07:59 -0500 Subject: [PATCH 05/33] Add a `dropseed` argument to `distinguishable_colors` (fixes #298) --- src/colormaps.jl | 40 +++++++++++++++++++++++++++------------- test/algorithms.jl | 3 +++ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/colormaps.jl b/src/colormaps.jl index b31afe03..914567a4 100644 --- a/src/colormaps.jl +++ b/src/colormaps.jl @@ -5,7 +5,12 @@ include("maps_data.jl") # ---------------------- """ - distinguishable_colors(n, [seed]; [transform, lchoices, cchoices, hchoices]) + colors = distinguishable_colors(n, seed=RGB{N0f8}[]; + dropseed=false, + transform=identity, + lchoices=range(0, stop=100, length=15), + cchoices=range(0, stop=100, length=15), + hchoices=range(0, stop=342, length=20)) Generate n maximally distinguishable colors. @@ -18,24 +23,30 @@ in the palette. # Arguments - `n`: Number of colors to generate. -- `seed`: Initial color(s) included in the palette. Default is `Vector{RGB{N0f8}}()`. +- `seed`: Initial color(s) included in the palette. # Keyword arguments -- `transform`: Transform applied to colors before measuring distance. Default is the identity; other choices include `deuteranopic` to simulate color-blindness. -- `lchoices`: Possible lightness values (default `range(0,stop=100,start=15)`) -- `cchoices`: Possible chroma values (default `range(0,stop=100,start=15)`) -- `hchoices`: Possible hue values (default `range(0,stop=340,start=20)`) +- `dropseed`: if true, the `seed` values will be dropped. This provides an easy + mechanism to ensure that the chosen colors are distinguishable from the seed value(s). + When true, `n` does not include the seed color(s). +- `transform`: Transform applied to colors before measuring distance. Default is `identity`; + other choices include `deuteranopic` to simulate color-blindness. +- `lchoices`: Possible lightness values +- `cchoices`: Possible chroma values +- `hchoices`: Possible hue values Returns a `Vector` of colors of length `n`, of the type specified in `seed`. """ function distinguishable_colors(n::Integer, - seed::AbstractVector{T}; - transform::Function = identity, - lchoices::AbstractVector = range(0, stop=100, length=15), - cchoices::AbstractVector = range(0, stop=100, length=15), - hchoices::AbstractVector = range(0, stop=340, length=20)) where T<:Color - if n <= length(seed) + seed::AbstractVector{T}; + dropseed = false, + transform::Function = identity, + lchoices::AbstractVector = range(0, stop=100, length=15), + cchoices::AbstractVector = range(0, stop=100, length=15), + hchoices::AbstractVector = range(0, stop=342, length=20)) where T<:Color + + if n <= length(seed) && !dropseed return seed[1:n] end @@ -55,6 +66,7 @@ function distinguishable_colors(n::Integer, end # Start with the seed colors + n += dropseed ? length(seed) : 0 colors = Vector{T}(undef, n) copyto!(colors, seed) @@ -77,7 +89,9 @@ function distinguishable_colors(n::Integer, end end - colors + dropseed && deleteat!(colors, 1:length(seed)) + + return colors end diff --git a/test/algorithms.jl b/test/algorithms.jl index 815fc39f..8e11283f 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -15,4 +15,7 @@ using Test, Colors end end @test mindiff > 8 + + cols = distinguishable_colors(1) + @test colordiff(distinguishable_colors(1, cols; dropseed=true)[1], cols[1]) > 50 end From 500459b003fe2313f914c26706ae160f411379f5 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Thu, 5 Sep 2019 03:45:40 -0500 Subject: [PATCH 06/33] Small conversion improvements --- src/conversions.jl | 6 ++---- test/conversion.jl | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/conversions.jl b/src/conversions.jl index fad33ec1..a73d32db 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -541,8 +541,7 @@ cnvt(::Type{Luv{T}}, c::Color3) where {T} = cnvt(Luv{T}, convert(XYZ{T}, c)) # ------------------- function cnvt(::Type{LCHuv{T}}, c::Luv) where T - h = rad2deg(atan(c.v, c.u)) - while h > 360; h -= 360; end + h = atand(c.v, c.u) while h < 0; h += 360; end LCHuv{T}(c.l, sqrt(c.u^2 + c.v^2), h) end @@ -555,8 +554,7 @@ cnvt(::Type{LCHuv{T}}, c::Color3) where {T} = cnvt(LCHuv{T}, convert(Luv{T}, c)) # ------------------- function cnvt(::Type{LCHab{T}}, c::Lab) where T - h = rad2deg(atan(c.b, c.a)) - while h > 360; h -= 360; end + h = atand(c.b, c.a) while h < 0; h += 360; end LCHab{T}(c.l, sqrt(c.a^2 + c.b^2), h) end diff --git a/test/conversion.jl b/test/conversion.jl index 96641f12..b12d4c73 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -225,7 +225,7 @@ using ColorTypes: eltype_default end errmax = max(errmax, diff/mag) end - errmax > eps && warn("Error on conversions from ", eltype(from), " to ", eltype(to), ", relative error = ", errmax) + errmax > eps && @warn("Error on conversions from $(eltype(from)) to $(eltype(to)), relative error = $errmax") errmax <= eps end From 0625f97cc0525dff63bc56b7281ccaa6de346018 Mon Sep 17 00:00:00 2001 From: kimikage Date: Sun, 1 Sep 2019 22:31:20 +0900 Subject: [PATCH 07/33] Use stroke instead of fill (rect) with decimation for large matrix This reduces the size of SVG document and the rendering time. --- src/display.jl | 38 ++++++++++++++++++++++++++++++++++++++ test/display.jl | 31 +++++++++++++++++++------------ 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/display.jl b/src/display.jl index b7432fe9..fd018cc4 100644 --- a/src/display.jl +++ b/src/display.jl @@ -4,6 +4,7 @@ const max_width = 180 const max_height = 150 const max_swatch_size = 25 +const max_num_of_swatches = 128 * 128 function Base.show(io::IO, mime::MIME"image/svg+xml", c::Color) write_declaration(io, mime) @@ -36,6 +37,9 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", simplify(x) = replace(string(round(x, digits=2)), r"(^0(?=\.))|(.0+$)"=>"") sw = simplify(1 - (w / n < 3.6 ? 0 : n / w)) sh = simplify(1 - (h / m < 3.6 ? 0 : m / h)) + if sw == "1" && sh == "1" + return show_strokes(io, mime, cs) + end comp(x) = round(x)==round(x, digits=2) ? Int(round(x)) : round(x, digits=2) write_declaration(io, mime) @@ -59,6 +63,40 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", flush(io) # return nothing end +function show_strokes(io::IO, mime::MIME"image/svg+xml", + cs::AbstractMatrix{T}) where T <: Color + m, n = size(cs) + w = max_width + h = max_height + if max_width * m > max_height * n + w = n / m * h + else + h = m / n * w + end + d = Int(ceil(sqrt(m * n / max_num_of_swatches))) # decimation factor + + comp(x) = round(x)==round(x, digits=2) ? Int(round(x)) : round(x, digits=2) + write_declaration(io, mime) + write(io, + """ + + """) + + for i in 1:d:m, j in 1:d:n + c = hex(cs[i, j]) + write(io, + """ + + """) + end + + write(io, "") + flush(io) # return nothing +end + function write_declaration(io::IO, ::MIME"image/svg+xml") write(io, """ diff --git a/test/display.jl b/test/display.jl index 5fa71c8e..e15a16e8 100644 --- a/test/display.jl +++ b/test/display.jl @@ -10,6 +10,17 @@ end end + function count_colored_stroke(svg::AbstractString) + n = 0 + i = firstindex(svg) + while true + r = findnext(r"]*\sstroke=\"#[0-9A-F]{6}\"[^>]+>", svg, i) + r === nothing && return n + n += 1 + i = last(r) + end + end + # test ability to add to previously shown array of colors - issue #328 a = [colorant"white", colorant"red"] buf = IOBuffer() @@ -63,7 +74,7 @@ # square swatches m2x2 = [colorant"white" colorant"blue" colorant"black" colorant"cyan"] - show(buf, MIME"image/svg+xml"(), m2x2) + show(buf, MIME"image/svg+xml"(), m2x2) mat2x2 = String(take!(buf)) @test occursin(r"]*\swidth=\"50mm\"", mat2x2) @test occursin(r"]*\sheight=\"50mm\"", mat2x2) @@ -101,27 +112,23 @@ # n * max_swatch_size > max_width && # m * max_swatch_size > max_height && # max_width / n * m >= max_swatch_size - # square swatches + # square swatches (strokes) show(buf, MIME"image/svg+xml"(), rand(RGB, 28, 181)) mat28x181 = String(take!(buf)) @test occursin(r"]*\swidth=\"180mm\"", mat28x181) # max_width @test occursin(r"]*\sheight=\"27.85mm\"", mat28x181) # 180mm/181 * 28 - @test occursin(r"]*\swidth=\"1\"", mat28x181) # no padding - @test occursin(r"]*\sheight=\"1\"", mat28x181) # no padding - @test count_filled_rect(mat28x181) == 28*181 + @test occursin(r"]*\sd=\"[^\"]+h1\"", mat28x181) # no padding + @test count_colored_stroke(mat28x181) == 28*181 # issue #341 # n * max_swatch_size > max_width && # m * max_swatch_size > max_height && # max_height / m * n < max_swatch_size - # rectangle swatches - # Keeping the aspect ratio 1:1 (i.e. square) seems to be more important - # than keeping the width/height >= 25mm, for large matrices. + # square swatches (strokes) show(buf, MIME"image/svg+xml"(), rand(RGB, 200, 28)) mat200x28 = String(take!(buf)) - @test occursin(r"]*\swidth=\"25mm\"", mat200x28) # max_swatch_size + @test occursin(r"]*\swidth=\"21mm\"", mat200x28) # 150mm/200*28 @test occursin(r"]*\sheight=\"150mm\"", mat200x28) # max_height - @test occursin(r"]*\swidth=\"1\"", mat200x28) # no padding - @test occursin(r"]*\sheight=\"1\"", mat200x28) # no padding - @test count_filled_rect(mat200x28) == 200*28 + @test occursin(r"]*\sd=\"[^\"]+h1\"", mat200x28) # no padding + @test count_colored_stroke(mat200x28) == 200*28 end From a031d489f9af463d730d4e75d3b86b09bd60b5c4 Mon Sep 17 00:00:00 2001 From: kimikage Date: Sat, 7 Sep 2019 14:14:10 +0900 Subject: [PATCH 08/33] Change swatch reduction method to area-average - Change the swatch reduction method from simple decimation to area-average method - Add keyword argument `max_swatches` - Add size warning for large image --- src/display.jl | 55 +++++++++++++++++++++++--------- test/display.jl | 84 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 112 insertions(+), 27 deletions(-) diff --git a/src/display.jl b/src/display.jl index fd018cc4..376819c5 100644 --- a/src/display.jl +++ b/src/display.jl @@ -4,7 +4,7 @@ const max_width = 180 const max_height = 150 const max_swatch_size = 25 -const max_num_of_swatches = 128 * 128 +const default_max_swatches = 128 * 128 function Base.show(io::IO, mime::MIME"image/svg+xml", c::Color) write_declaration(io, mime) @@ -18,14 +18,19 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", c::Color) flush(io) # return nothing end -function Base.show(io::IO, mime::MIME"image/svg+xml", - cs::AbstractVector{T}) where T <: Color - show(io, mime, Base.ReshapedArray(cs, (1, length(cs)), ())) +function Base.show(io::IO, mime::MIME"image/svg+xml", cs::AbstractVector{T}; + max_swatches::Integer=0) where T <: Color + mat = Base.ReshapedArray(cs, (1, length(cs)), ()) + show(io, mime, mat, max_swatches=max_swatches) end -function Base.show(io::IO, mime::MIME"image/svg+xml", - cs::AbstractMatrix{T}) where T <: Color +function Base.show(io::IO, mime::MIME"image/svg+xml", cs::AbstractMatrix{T}; + max_swatches::Integer=0) where T <: Color m, n = size(cs) + actual_max_swatches = max_swatches > 0 ? max_swatches : default_max_swatches + if m * n > actual_max_swatches + return show_strokes(io, mime, cs, max_swatches=max_swatches) + end w = min(n * max_swatch_size, max_width) h = min(m * max_swatch_size, max_height) if max_width * m > max_height * n @@ -38,7 +43,7 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", sw = simplify(1 - (w / n < 3.6 ? 0 : n / w)) sh = simplify(1 - (h / m < 3.6 ? 0 : m / h)) if sw == "1" && sh == "1" - return show_strokes(io, mime, cs) + return show_strokes(io, mime, cs, max_swatches=max_swatches) end comp(x) = round(x)==round(x, digits=2) ? Int(round(x)) : round(x, digits=2) @@ -63,9 +68,17 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", flush(io) # return nothing end -function show_strokes(io::IO, mime::MIME"image/svg+xml", - cs::AbstractMatrix{T}) where T <: Color +function show_strokes(io::IO, mime::MIME"image/svg+xml", cs::AbstractMatrix{T}; + max_swatches::Integer=0) where T <: Color m, n = size(cs) + actual_max_swatches = max_swatches > 0 ? max_swatches : default_max_swatches + # When `max_swatches` is specified, the following warning is suppressed. + if max_swatches == 0 && m * n > actual_max_swatches + @warn """ + Output swatches are reduced due to the large size ($m×$n). + Load the ImageShow package for large images.""" + yield() + end w = max_width h = max_height if max_width * m > max_height * n @@ -73,23 +86,35 @@ function show_strokes(io::IO, mime::MIME"image/svg+xml", else h = m / n * w end - d = Int(ceil(sqrt(m * n / max_num_of_swatches))) # decimation factor + + # decimation factor `d` is rounded to integer to simplify the SVG document + d = Int(ceil(sqrt(m * n / actual_max_swatches))) comp(x) = round(x)==round(x, digits=2) ? Int(round(x)) : round(x, digits=2) write_declaration(io, mime) write(io, """ + width="$(comp(w))mm" height="$(comp(h))mm" + viewBox="0 -$(comp(d/2)) $n $m" stroke-width="$d" + stroke-linecap="butt" shape-rendering="crispEdges"> """) for i in 1:d:m, j in 1:d:n - c = hex(cs[i, j]) + # since there is no universal way, + # calculate the mean color in "RGB space" + csum = Float32[0, 0, 0] + u = min(n-j+1, d) # cell width + v = min(m-i+1, d) # cell height + for y in i:i+v-1, x in j:j+u-1 + rgb = convert(RGB{Float32}, cs[y, x]) + csum += [red(rgb), green(rgb), blue(rgb)] + end + csum /= (u * v) + c = hex(RGB{Float32}(csum[1], csum[2], csum[3])) write(io, """ - + """) end diff --git a/test/display.jl b/test/display.jl index e15a16e8..33982764 100644 --- a/test/display.jl +++ b/test/display.jl @@ -24,7 +24,7 @@ # test ability to add to previously shown array of colors - issue #328 a = [colorant"white", colorant"red"] buf = IOBuffer() - show(buf, MIME"image/svg+xml"(), a) + show(buf, "image/svg+xml", a) take!(buf) push!(a, colorant"blue") @test length(a) == 3 @@ -33,8 +33,8 @@ # the following tests depend on the constants in "src/display.jl" # single color - - show(buf, MIME"image/svg+xml"(), colorant"hsl(120, 100%, 50%)") + # ------------ + show(buf, "image/svg+xml", colorant"hsl(120, 100%, 50%)") single = String(take!(buf)) @test occursin(r"]+\swidth=\"25mm\"", single) @test occursin(r"]+\sheight=\"25mm\"", single) @@ -43,10 +43,10 @@ # vectors - + # ------- # square swatches v3 = [colorant"red", colorant"green", colorant"blue"] - show(buf, MIME"image/svg+xml"(), v3) + show(buf, "image/svg+xml", v3) vector3 = String(take!(buf)) @test occursin(r"]*\swidth=\"75mm\"", vector3) @test occursin(r"]*\sheight=\"25mm\"", vector3) @@ -58,7 +58,7 @@ @test occursin(r"$", vector3) # rectangle swatches - show(buf, MIME"image/svg+xml"(), colormap("RdBu", 100)) + show(buf, "image/svg+xml", colormap("RdBu", 100)) vec100 = String(take!(buf)) @test occursin(r"]*\swidth=\"180mm\"", vec100) # max_width @test occursin(r"]*\sheight=\"25mm\"", vec100) @@ -66,15 +66,38 @@ @test occursin(r"]*\sheight=\".96\"", vec100) # 0.96 = 24mm/25mm @test count_filled_rect(vec100) == 100 + # issue #317 + # limits max swatches + # the size warning should be suppressed + show(buf, MIME"image/svg+xml"(), v3, max_swatches=2) # not string but MIME + lim_vector3 = String(take!(buf)) + # since `max_swatches`(=2) is less practical, + # the width/height is greater than `max_swatch_size` + @test occursin(r"]*\swidth=\"180mm\"", lim_vector3) # max_width + @test occursin(r"]*\sheight=\"60mm\"", lim_vector3) # 180mm/3 * 1 + # decimation factor `d` should be 2 + # viewBox is based on the original size (=3x1) + @test occursin(r"]*\sviewBox=\"0 -1 3 1\"", lim_vector3) + # therefore, each swatch is contrarily enlarged due to the swatch reduction. + # stroke width is equivalent to swatch height which is equal to `d`(=2) + @test occursin(r"]*\sstroke-width=\"2\"", lim_vector3) + # swatch width is equal to `d`(=2) + @test occursin(r"]*\sd=\"[^\"]+h2\"", lim_vector3) # no padding + # the mean RGB of first 2 elements (red and green) + @test occursin(r"stroke=\"#804000\"[^/]*/>\s*\s*", lim_vector3) + @test count_colored_stroke(lim_vector3) == 2 # <= max_swatches - # m * n matrices + # m * n matrices + # -------------- # n * max_swatch_size <= max_width && # m * max_swatch_size <= max_height # square swatches m2x2 = [colorant"white" colorant"blue" colorant"black" colorant"cyan"] - show(buf, MIME"image/svg+xml"(), m2x2) + show(buf, "image/svg+xml", m2x2) mat2x2 = String(take!(buf)) @test occursin(r"]*\swidth=\"50mm\"", mat2x2) @test occursin(r"]*\sheight=\"50mm\"", mat2x2) @@ -89,7 +112,7 @@ # n * max_swatch_size > max_width && # m * max_swatch_size <= max_height # square swatches - show(buf, MIME"image/svg+xml"(), rand(RGB, 2, 9)) + show(buf, "image/svg+xml", rand(RGB, 2, 9)) mat2x9 = String(take!(buf)) @test occursin(r"]*\swidth=\"180mm\"", mat2x9) # max_width @test occursin(r"]*\sheight=\"40mm\"", mat2x9) # 40mm = 180mm/9 * 2 @@ -100,7 +123,7 @@ # n * max_swatch_size <= max_width && # m * max_swatch_size > max_height # square swatches - show(buf, MIME"image/svg+xml"(), rand(RGB, 10, 2)) + show(buf, "image/svg+xml", rand(RGB, 10, 2)) mat10x2 = String(take!(buf)) @test occursin(r"]*\swidth=\"30mm\"", mat10x2) # 30mm = 150mm/10 * 2 @test occursin(r"]*\sheight=\"150mm\"", mat10x2) # max_height @@ -113,7 +136,7 @@ # m * max_swatch_size > max_height && # max_width / n * m >= max_swatch_size # square swatches (strokes) - show(buf, MIME"image/svg+xml"(), rand(RGB, 28, 181)) + show(buf, "image/svg+xml", rand(RGB, 28, 181)) mat28x181 = String(take!(buf)) @test occursin(r"]*\swidth=\"180mm\"", mat28x181) # max_width @test occursin(r"]*\sheight=\"27.85mm\"", mat28x181) # 180mm/181 * 28 @@ -125,10 +148,47 @@ # m * max_swatch_size > max_height && # max_height / m * n < max_swatch_size # square swatches (strokes) - show(buf, MIME"image/svg+xml"(), rand(RGB, 200, 28)) + show(buf, "image/svg+xml", rand(RGB, 200, 28)) mat200x28 = String(take!(buf)) @test occursin(r"]*\swidth=\"21mm\"", mat200x28) # 150mm/200*28 @test occursin(r"]*\sheight=\"150mm\"", mat200x28) # max_height @test occursin(r"]*\sd=\"[^\"]+h1\"", mat200x28) # no padding @test count_colored_stroke(mat200x28) == 200*28 + + # issue #317 + # implicitly limits max swatches (but explicitly warns) + # m * n > default_max_swatches + # square swatches (strokes) with reduction + m184x360 = [HSV(h, s / 183, 0.8) for s = 0:183, h = 0:359] + # warning of size + warning = r"the large size \(184×360\)" + @test_logs (:warn, warning) show(buf, "image/svg+xml", m184x360) + mat184x360 = String(take!(buf)) + @test occursin(r"]*\swidth=\"180mm\"", mat184x360) # max_width + @test occursin(r"]*\sheight=\"92mm\"", mat184x360) # 180mm/360 * 184 + # decimation factor should be 3 + @test occursin(r"]*\sviewBox=\"0 -1.5[0]* 360 184\"", mat184x360) + @test occursin(r"]*\sstroke-width=\"3\"", mat184x360) # `d`(=3) + @test occursin(r"]*\sd=\"[^\"]+h3\"", mat184x360) # `d`(=3) + # the mean RGB of 3 (not 9) elements in the bottom-right corner + # "CC0007" == hex(RGB(0.8, 0.0, (0.12/3 + 0.08/3 + 0.04/3)/3)) + @test occursin(r"stroke=\"#CC0007\"[^/]*/>\s*", mat184x360) + @test count_colored_stroke(mat184x360) == ceil(184/3)*ceil(360/3) + + # issue #317 + # limits max swatches + # the size warning should be suppressed + show(buf, MIME"image/svg+xml"(), m2x2, max_swatches=3) # not string but MIME + lim_mat2x2= String(take!(buf)) + # since `max_swatches`(=3) is less practical, + # the width/height is greater than `max_swatch_size` + @test occursin(r"]*\swidth=\"150mm\"", lim_mat2x2) # 180mm/2 * 2 + @test occursin(r"]*\sheight=\"150mm\"", lim_mat2x2) # max_height + # decimation factor `d` should be 2 + @test occursin(r"]*\sviewBox=\"0 -1 2 2\"", lim_mat2x2) + @test occursin(r"]*\sstroke-width=\"2\"", lim_mat2x2) # `d`(=2) + @test occursin(r"]*\sd=\"[^\"]+h2\"", lim_mat2x2) # `d`(=2) + # the mean RGB of 2x2 elements + @test occursin(r"stroke=\"#4080BF\"[^/]*/>\s*", lim_mat2x2) + @test count_colored_stroke(lim_mat2x2) == 1 # <= max_swatches end From da286f02882f0dcbcc16ebedd0f72f593fb19cb5 Mon Sep 17 00:00:00 2001 From: kimikage Date: Sat, 14 Sep 2019 09:16:21 +0900 Subject: [PATCH 09/33] Move colormap-related tests to `test/colormaps.jl` and Add regression tests --- test/algorithms.jl | 15 --------------- test/colormaps.jl | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/test/algorithms.jl b/test/algorithms.jl index 8e11283f..cf341827 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -1,21 +1,6 @@ using Test, Colors @testset "Algorithms" begin - @test isconcretetype(eltype(colormap("Grays"))) - @test_throws ArgumentError colormap("Grays", N=10) - col = distinguishable_colors(10) - @test isconcretetype(eltype(col)) - local mindiff - mindiff = Inf - for i = 1:10 - for j = i+1:10 - mindiff = min(mindiff, colordiff(col[i], col[j])) - end - end - @test mindiff > 8 - - cols = distinguishable_colors(1) - @test colordiff(distinguishable_colors(1, cols; dropseed=true)[1], cols[1]) > 50 end diff --git a/test/colormaps.jl b/test/colormaps.jl index f2468466..af4221c3 100644 --- a/test/colormaps.jl +++ b/test/colormaps.jl @@ -1,5 +1,43 @@ @testset "Colormaps" begin + col = distinguishable_colors(10) + @test isconcretetype(eltype(col)) + local mindiff + mindiff = Inf + for i = 1:10 + for j = i+1:10 + mindiff = min(mindiff, colordiff(col[i], col[j])) + end + end + @test mindiff > 8 + + cols = distinguishable_colors(1) + @test colordiff(distinguishable_colors(1, cols; dropseed=true)[1], cols[1]) > 50 + + @test length(colormap("RdBu", 100)) == 100 + @test isconcretetype(eltype(colormap("Grays"))) + + @test_throws ArgumentError colormap("Grays", N=10) # optional arguments, not keyword + + # The following were generated by `colormap()` in Colors.jl v0.9.6. + blues_old = ["F4FDFF", "B3E3F4", "65B9E7", "2978BE", "0B2857"] + greens_old = ["FAFFF7", "B6EEA0", "75C769", "308B40", "00391A"] + grays_old = ["FFFFFF", "DCDCDC", "A9A9A9", "626262", "000000"] + oranges_old = ["FFFBF6", "FFD6B4", "FF9D5F", "E2500D", "732108"] + purples_old = ["FBFBFB", "DBD7F6", "AFA7E6", "7666BF", "3C0468"] + reds_old = ["FFF1EE", "FFC4B9", "FF8576", "E72823", "6D0B0C"] + rdbu_old = ["610102", "FF8D7B", "F9F8F9", "76B4E8", "092C58"] + + to_rgb(s) = parse(RGB, "#"*s) + max_colordiff(a1, a2) = reduce(max, colordiff.(a1, a2)) + @test max_colordiff(colormap("Blues", 5), to_rgb.(blues_old)) < 1 + @test max_colordiff(colormap("Greens", 5), to_rgb.(greens_old)) < 1 + @test max_colordiff(colormap("Grays", 5), to_rgb.(grays_old)) < 1 + @test max_colordiff(colormap("Oranges", 5), to_rgb.(oranges_old)) < 1 + @test max_colordiff(colormap("Purples", 5), to_rgb.(purples_old)) < 1 + @test max_colordiff(colormap("Reds", 5), to_rgb.(reds_old)) < 1 + @test max_colordiff(colormap("RdBu", 5), to_rgb.(rdbu_old)) < 1 + # TODO: add more tests end From 337b689ce3cf96377ec9994837c2616ba2a8abbc Mon Sep 17 00:00:00 2001 From: kimikage Date: Sat, 14 Sep 2019 11:03:13 +0900 Subject: [PATCH 10/33] Fix erroneous `MSC(h)` and improve accuracy of `MSC(h, l)` --- src/algorithms.jl | 154 +++++++++++++++++++++++++++------------------ test/algorithms.jl | 40 ++++++++++++ test/colormaps.jl | 2 + 3 files changed, 135 insertions(+), 61 deletions(-) diff --git a/src/algorithms.jl b/src/algorithms.jl index 71901082..c67f5d34 100644 --- a/src/algorithms.jl +++ b/src/algorithms.jl @@ -152,54 +152,50 @@ tritanopic(c::Color) = tritanopic(c, 1.0) """ MSC(h) - MSC(h, l) + MSC(h, l; linear=false) -Calculate the most saturated color for any given hue `h` by -finding the corresponding corner in LCHuv space. Optionally, -the lightness `l` may also be specified. -""" -function MSC(h) +Calculate the most saturated color in sRGB gamut for any given hue `h` by +finding the corresponding corner in LCHuv space. Optionally, the lightness `l` +may also be specified. - #Corners of RGB cube - h0 = 12.173988685914473 #convert(LCHuv,RGB(1,0,0)).h - h1 = 85.872748860776770 #convert(LCHuv,RGB(1,1,0)).h - h2 = 127.72355046632740 #convert(LCHuv,RGB(0,1,0)).h - h3 = 192.17397321802082 #convert(LCHuv,RGB(0,1,1)).h - h4 = 265.87273498040290 #convert(LCHuv,RGB(0,0,1)).h - h5 = 307.72354567594960 #convert(LCHuv,RGB(1,0,1)).h +# Arguments - #Wrap h to [0, 360] range] - h = mod(h, 360) +- `h`: Hue [0,360] in LCHuv space +- `l`: Lightness [0,100] in LCHuv space - p=0 #variable - o=0 #min - t=0 #max +# Keyword arguments - #Selecting edge of RGB cube; R=1 G=2 B=3 - if h0 <= h < h1 - p=2; o=3; t=1 - elseif h1 <= h < h2 - p=1; o=3; t=2 - elseif h2 <= h < h3 - p=3; o=1; t=2 - elseif h3 <= h < h4 - p=2; o=1; t=3 - elseif h4 <= h < h5 - p=1; o=2; t=3 - elseif h5 <= h || h < h0 - p=3; o=2; t=1 - end +- `linear` : If true, the saturation is linearly interpolated between black/ + white and `MSC(h)` as the gamut is approximately triangular in L-C section. - col=zeros(3) +!!! note + `MSC(h)` returns an `LCHuv` color, but `MSC(h, l)` returns a saturation + value. This behavior might change in a future release. - #check if we are directly on the edge of the RGB cube (within some tolerance) - for edge in [h0, h1, h2, h3, h4, h5] - if edge - 200eps() < h < edge + 200eps() - col[p] = edge in [h0, h2, h4] ? 0.0 : 1.0 - col[o] = 0.0 - col[t] = 1.0 - return convert(LCHuv, RGB(col[1],col[2],col[3])) - end +""" +function MSC(h) + + #Wrap h to [0, 360] range + h = mod(h, 360) + + #Selecting edge of RGB cube; R=1 G=2 B=3 + # p #variable + # o #min + # t #max + if h < 12.173988685914473 #convert(LCHuv,RGB(1,0,0)).h + p=3; t=1 + elseif h < 85.872748860776770 #convert(LCHuv,RGB(1,1,0)).h + p=2; t=1 + elseif h < 127.72355046632740 #convert(LCHuv,RGB(0,1,0)).h + p=1; t=2 + elseif h < 192.17397321802082 #convert(LCHuv,RGB(0,1,1)).h + p=3; t=2 + elseif h < 265.87273498040290 #convert(LCHuv,RGB(0,0,1)).h + p=2; t=3 + elseif h < 307.72354567594960 #convert(LCHuv,RGB(1,0,1)).h + p=1; t=3 + else + p=3; t=1 end alpha=-sind(h) @@ -214,7 +210,6 @@ function MSC(h) M = [0.4124564 0.3575761 0.1804375; 0.2126729 0.7151522 0.0721750; 0.0193339 0.1191920 0.9503041]' - g = 2.4 m_tx=M[t,1] m_ty=M[t,2] @@ -223,20 +218,17 @@ function MSC(h) m_py=M[p,2] m_pz=M[p,3] - f1=(4alpha*m_px+9beta*m_py) - a1=(4alpha*m_tx+9beta*m_ty) - f2=(m_px+15m_py+3m_pz) - a2=(m_tx+15m_ty+3m_tz) + f1 = 4alpha*m_px+9beta*m_py + a1 = 4alpha*m_tx+9beta*m_ty + f2 = m_px+15m_py+3m_pz + a2 = m_tx+15m_ty+3m_tz cp=((alpha*un+beta*vn)*a2-a1)/(f1-(alpha*un+beta*vn)*f2) - #gamma inversion - cp = cp <= 0.003 ? 12.92cp : 1.055cp^(1.0/g)-0.05 - #cp = 1.055cp^(1.0/g)-0.05 - - col[p]=clamp01(cp) - col[o]=0.0 - col[t]=1.0 + col = zeros(3) + col[p] = clamp01(srgb_compand(cp)) + # col[o] = 0.0 + col[t] = 1.0 return convert(LCHuv, RGB(col[1],col[2],col[3])) end @@ -247,15 +239,55 @@ end # Maximally saturated color for a specific hue and lightness # is found by looking for the edge of LCHuv space. -function MSC(h,l) - pmid=MSC(h) +function MSC(h, l; linear::Bool=false) + if linear + pmid = MSC(h) + pend_l = l > pmid.l ? 100 : 0 + return (pend_l-l)/(pend_l-pmid.l) * pmid.c + end + return find_maximum_chroma(LCHuv(l, 0, h)) +end - if l <= pmid.l - pend=LCHuv(0,0,0) - elseif l > pmid.l - pend=LCHuv(100,0,0) +# This function finds the maximum chroma for the lightness `c.l` and hue `c.h` +# by means of the binary search. Even though this requires more than 20 +# iterations, somehow, this is fast. +function find_maximum_chroma(c::T, + low::Real=0, + high::Real=180) where {T<:Union{LCHab, LCHuv}} + err = 1e-6 + high-low < err && return low + + mid = (low + high) / 2 + lchm = T(c.l, mid, c.h) + rgbm = convert(RGB, lchm) + clamped = max(red(rgbm), green(rgbm), blue(rgbm)) > 1-err || + min(red(rgbm), green(rgbm), blue(rgbm)) < err + if clamped + return find_maximum_chroma(c, low, mid) + else + return find_maximum_chroma(c, mid, high) end +end - a=(pend.l-l)/(pend.l-pmid.l) - a*(pmid.c-pend.c)+pend.c +function find_maximum_chroma(c::LCHab) + maxc = find_maximum_chroma(c, 0, 135) + + # The sRGB gamut in LCHab space has a *hollow* around the yellow corner. + # Since the following boundary is based on the D65 white point, the values + # should be modified on other conditions. + if 97 < c.h < 108 && c.l > 92 + err = 1e-6 + h_yellow = 102.85124420310268 # convert(LCHab,RGB{Float64}(1,1,0)).h + for chroma in range(maxc, stop=100, length=10000) + rgb = convert(RGB, LCHab(c.l, chroma, c.h)) + blue(rgb) < err && continue + y = c.h < h_yellow ? red(rgb) : green(rgb) + if y < 1-err + maxc = chroma + end + end + end + return maxc end +find_maximum_chroma(c::Lab) = find_maximum_chroma(convert(LCHab, c)) +find_maximum_chroma(c::Luv) = find_maximum_chroma(convert(LCHuv, c)) diff --git a/test/algorithms.jl b/test/algorithms.jl index cf341827..353a49a2 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -2,5 +2,45 @@ using Test, Colors @testset "Algorithms" begin + # issue #349 + msc_h_diff = 0 + for hsv_h in 0:0.1:360 + hsv = HSV(hsv_h, 1.0, 1.0) # most saturated + lch = convert(LCHuv, hsv) + msc = MSC(lch.h) + msc_h_diff = max(msc_h_diff, colordiff(msc, lch)) + end + @test msc_h_diff < 1 + @test MSC(123.45, 100) ≈ 0 + @test MSC(123.45, 0) ≈ 0 + + msc_h_l_sat = 1 + for h = 0:0.1:359.9, l = 1:1:99 + c = MSC(h, l) + hsv = convert(HSV, LCHuv(l, c, h)) + # When the color is saturated (except black/white), `s` or `v` is 1. + msc_h_l_sat = min(msc_h_l_sat, max(hsv.s, hsv.v)) + end + @test msc_h_l_sat > 1 - 1e-4 + + # the linear interpolation introduces some approximation errors.(issue #349) + @test MSC(0, 90, linear=true) > MSC(0, 90) + @test MSC(280, 50, linear=true) < MSC(280, 50) + + @testset "find_maximum_chroma hsv_h=$hsv_h" for hsv_h in 0:60:300 + hsv = HSV(hsv_h, 1.0, 1.0) # corner + lchab = convert(LCHab, hsv) + lchuv = convert(LCHuv, hsv) + lab = convert(Lab, hsv) + luv = convert(Luv, hsv) + @test Colors.find_maximum_chroma(lchab) ≈ lchab.c atol=0.01 + @test Colors.find_maximum_chroma(lchuv) ≈ lchuv.c atol=0.01 + @test Colors.find_maximum_chroma(lab) ≈ lchab.c atol=0.01 + @test Colors.find_maximum_chroma(luv) ≈ lchuv.c atol=0.01 + end + + # yellow in LCHab + @test Colors.find_maximum_chroma(LCHab(94.2, 0, 100)) ≈ 93.749 atol=0.01 + @test Colors.find_maximum_chroma(LCHab(97.6, 0, 105)) ≈ 68.828 atol=0.01 end diff --git a/test/colormaps.jl b/test/colormaps.jl index af4221c3..54cb7388 100644 --- a/test/colormaps.jl +++ b/test/colormaps.jl @@ -20,6 +20,8 @@ @test_throws ArgumentError colormap("Grays", N=10) # optional arguments, not keyword + # The outputs of `colormap()` were slightly affected by the bug fix of + # `MSC(h)` (issue #349). # The following were generated by `colormap()` in Colors.jl v0.9.6. blues_old = ["F4FDFF", "B3E3F4", "65B9E7", "2978BE", "0B2857"] greens_old = ["FAFFF7", "B6EEA0", "75C769", "308B40", "00391A"] From 184d3ab2df99554ac5d49bb46386d5886df0ec71 Mon Sep 17 00:00:00 2001 From: kimikage Date: Sat, 21 Sep 2019 13:04:31 +0900 Subject: [PATCH 11/33] Speed up RGB <--> XYZ conversions --- src/conversions.jl | 27 +++++++++++++++++---------- test/conversion.jl | 12 ++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/conversions.jl b/src/conversions.jl index a73d32db..d3e797f6 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -73,8 +73,10 @@ end correct_gamut(c::CV) where {CV<:AbstractRGB} = CV(clamp01(red(c)), clamp01(green(c)), clamp01(blue(c))) clamp01(v::T) where {T<:Fractional} = ifelse(v < zero(T), zero(T), ifelse(v > one(T), one(T), v)) -function srgb_compand(v) - v <= 0.0031308 ? 12.92v : 1.055v^(1/2.4) - 0.055 +function srgb_compand(v::Fractional) + # the following is an optimization technique for `1.055v^(1/2.4) - 0.055`. + # x^y ≈ exp(y*log(x)) ≈ exp2(y*log2(y)); the middle form is faster + v <= 0.0031308 ? 12.92v : 1.055 * exp(1/2.4 * log(v)) - 0.055 end cnvt(::Type{CV}, c::AbstractRGB) where {CV<:AbstractRGB} = CV(red(c), green(c), blue(c)) @@ -167,9 +169,6 @@ cnvt(::Type{CV}, c::LCHab) where {CV<:AbstractRGB} = cnvt(CV, convert(Lab{eltyp cnvt(::Type{CV}, c::LCHuv) where {CV<:AbstractRGB} = cnvt(CV, convert(Luv{eltype(c)}, c)) cnvt(::Type{CV}, c::Color3) where {CV<:AbstractRGB} = cnvt(CV, convert(XYZ{eltype(c)}, c)) -cnvt(::Type{CV}, c::RGB24) where {CV<:AbstractRGB{N0f8}} = CV(N0f8((c.color&0x00ff0000)>>>16,0), N0f8((c.color&0x0000ff00)>>>8,0), N0f8(c.color&0x000000ff,0)) -cnvt(::Type{CV}, c::RGB24) where {CV<:AbstractRGB} = CV(((c.color&0x00ff0000)>>>16)/255, (((c.color&0x0000ff00))>>>8)/255, (c.color&0x000000ff)/255) - function cnvt(::Type{CV}, c::AbstractGray) where CV<:AbstractRGB g = convert(eltype(CV), gray(c)) CV(g, g, g) @@ -271,12 +270,22 @@ cnvt(::Type{HSI{T}}, c::Color3) where {T} = cnvt(HSI{T}, convert(RGB{T}, c)) # Everything to XYZ # ----------------- -function invert_rgb_compand(v) - v <= 0.04045 ? v/12.92 : ((v+0.055) /1.055)^2.4 +function invert_srgb_compand(v::Fractional) + v <= 0.04045 && return v/12.92 + # the following is an optimization technique for `((v+0.055) /1.055)^2.4`. + # see also: srgb_compand(v::Fractional) + x = (v + 0.055) / 1.055 + return x^2 * exp(0.4 * log(x)) # 2.4 == 2 + 0.4 +end + +const invert_srgb_compand_n0f8 = [invert_srgb_compand(v/255) for v = 0:255] # LUT + +function invert_srgb_compand(v::N0f8) + invert_srgb_compand_n0f8[reinterpret(UInt8, v) + 1] end function cnvt(::Type{XYZ{T}}, c::AbstractRGB) where T - r, g, b = invert_rgb_compand(red(c)), invert_rgb_compand(green(c)), invert_rgb_compand(blue(c)) + r, g, b = invert_srgb_compand(red(c)), invert_srgb_compand(green(c)), invert_srgb_compand(blue(c)) XYZ{T}(0.4124564*r + 0.3575761*g + 0.1804375*b, 0.2126729*r + 0.7151522*g + 0.0721750*b, 0.0193339*r + 0.1191920*g + 0.9503041*b) @@ -386,8 +395,6 @@ end cnvt(::Type{XYZ{T}}, c::YIQ) where {T} = cnvt(XYZ{T}, convert(RGB{T}, c)) cnvt(::Type{XYZ{T}}, c::YCbCr) where {T} = cnvt(XYZ{T}, convert(RGB{T}, c)) -cnvt(::Type{XYZ{T}}, c::RGB24) where {T} = cnvt(XYZ{T}, convert(RGB{T}, c)) - # Everything to xyY # ----------------- diff --git a/test/conversion.jl b/test/conversion.jl index b12d4c73..c517239f 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -43,6 +43,18 @@ using ColorTypes: eltype_default @test hex(RGB(1,0.5,0)) == "FF8000" @test hex(RGBA(1,0.5,0,0.25)) == "40FF8000" + # srgb_compand / invert_srgb_compand + @test Colors.srgb_compand(0.5) ≈ 0.7353569830524494 atol=eps() + @test Colors.invert_srgb_compand(0.7353569830524494) ≈ 0.5 atol=eps() + # issue #351 + l_pow_x_y() = for i=1:1000; (i/1000)^(1/2.4) end + l_exp_y_log_x() = for i=1:1000; exp(1/2.4*log(i/1000)) end + l_pow_x_y(); t_pow_x_y = @elapsed l_pow_x_y() + l_exp_y_log_x(); t_exp_y_log_x = @elapsed l_exp_y_log_x() + if t_exp_y_log_x > t_pow_x_y + @warn "Optimization in `[invert_]srgb_compand()` may have the opposite effect." + end + fractional_types = (RGB, BGR, RGB1, RGB4) # types that support Fractional red24 = reinterpret(RGB24, 0x00ff0000) From 43d93ffbe855b298fceda03cb2d730b1c223981d Mon Sep 17 00:00:00 2001 From: Lasse Peters Date: Fri, 27 Sep 2019 16:39:48 -0700 Subject: [PATCH 12/33] Fix colormap keyword argument handling (#358) --- src/colormaps.jl | 18 +++++++++++------- test/colormaps.jl | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/colormaps.jl b/src/colormaps.jl index 914567a4..51edf789 100644 --- a/src/colormaps.jl +++ b/src/colormaps.jl @@ -284,31 +284,35 @@ function colormap(cname::AbstractString, N::Int=100; mid=0.5, logscale=false, kv cname = lowercase(cname) if haskey(colormaps_sequential, cname) + allowedkeys = [:h, :w, :d, :c, :s, :b, :wcolor, :dcolor] p = copy(colormaps_sequential[cname][1:8]) - allowedkeys = [:h, :w, :d, :c, :s, :b, :wcolor, :dcolor] for (k,v) in kvs - k in allowedkeys || throw(ArgumentError("Unknown keyword argument $k")) - ind = findfirst(allowedkeys, k) + ind = findfirst(e->e==k, allowedkeys) + ind == nothing && throw(ArgumentError("Unknown keyword argument $k")) if ind > 0 p[ind] = v end end - return sequential_palette(p[1], N, w=p[2], d=p[3], c=p[4], s=p[5], b=p[6], wcolor=p[7], dcolor=p[8], logscale=logscale) + return sequential_palette(p[1], N, w=p[2], d=p[3], c=p[4], s=p[5], b=p[6], wcolor=p[7], + dcolor=p[8], logscale=logscale) elseif haskey(colormaps_diverging, cname) + allowedkeys = [:h1, :h2, :w, :d1, :d2, :c, :s, :b, :wcolor, :dcolor1, :dcolor2] p = copy(colormaps_diverging[cname][1:11]) for (k,v) in kvs - k in allowedkeys || throw(ArgumentError("Unknown keyword argument $k")) - ind = findfirst(allowedkeys, k) + ind = findfirst(e->e==k, allowedkeys) + ind == nothing && throw(ArgumentError("Unknown keyword argument $k")) if ind > 0 p[ind] = v end end - return diverging_palette(p[1], p[2], N, w=p[3], d1=p[4], d2=p[5], c=p[6], s=p[7], b=p[8], wcolor=p[9], dcolor1=p[10], dcolor2=p[11], mid=mid, logscale=logscale) + return diverging_palette(p[1], p[2], N, w=p[3], d1=p[4], d2=p[5], c=p[6], s=p[7], + b=p[8], wcolor=p[9], dcolor1=p[10], dcolor2=p[11], mid=mid, + logscale=logscale) else throw(ArgumentError(string("Unknown colormap: ", cname))) diff --git a/test/colormaps.jl b/test/colormaps.jl index 54cb7388..311f061f 100644 --- a/test/colormaps.jl +++ b/test/colormaps.jl @@ -13,13 +13,46 @@ cols = distinguishable_colors(1) @test colordiff(distinguishable_colors(1, cols; dropseed=true)[1], cols[1]) > 50 - + @test length(colormap("RdBu", 100)) == 100 @test isconcretetype(eltype(colormap("Grays"))) @test_throws ArgumentError colormap("Grays", N=10) # optional arguments, not keyword + # not return values to check here, just checking that keywords can be used + # Sequential + default_blues = colormap("Blues", 10) + @test colormap("Blues", 10; mid=0.9) == default_blues # `mid` is for diverging colormaps only (issue #300) + @test colormap("Blues", 10; logscale=true) != default_blues + @test colormap("Blues", 10; h=0.5) != default_blues + @test colormap("Blues", 10; w=0.5) != default_blues + @test colormap("Blues", 10; d=0.5) != default_blues + @test colormap("Blues", 10; c=0.5) != default_blues + @test colormap("Blues", 10; s=0.5) != default_blues + @test colormap("Blues", 10; b=0.5) != default_blues + @test colormap("Blues", 10; wcolor=colorant"white") != default_blues + @test colormap("Blues", 10; dcolor=colorant"black") != default_blues + @test_throws ArgumentError colormap("Blues", 10; h1=0.5) + + # Diverging + default_rdbu = colormap("RdBu", 10) + @test colormap("RdBu", 10; mid=0.9) != default_rdbu + @test colormap("RdBu", 10; logscale=true) != default_rdbu + @test colormap("RdBu", 10; h1=0.5) != default_rdbu + @test colormap("RdBu", 10; h2=0.5) != default_rdbu + @test colormap("RdBu", 10; w=0.5) != default_rdbu + @test colormap("RdBu", 10; d1=0.5) != default_rdbu + @test colormap("RdBu", 10; d2=0.5) != default_rdbu + @test colormap("RdBu", 10; c=0.5) != default_rdbu + @test colormap("RdBu", 10; s=0.5) != default_rdbu + @test colormap("RdBu", 10; b=0.5) != default_rdbu + @test colormap("RdBu", 10; wcolor=colorant"white") != default_rdbu + @test colormap("RdBu", 10; dcolor1=colorant"black") != default_rdbu + # `dcolor2` is disabled by the default `d2`(=0) + @test colormap("RdBu", 10; dcolor2=colorant"black", d2=1) != default_rdbu + @test_throws ArgumentError colormap("RdBu", 10; h=0.5) + # The outputs of `colormap()` were slightly affected by the bug fix of # `MSC(h)` (issue #349). # The following were generated by `colormap()` in Colors.jl v0.9.6. From 58194db79c469d5386d834462d8fc937642b24f4 Mon Sep 17 00:00:00 2001 From: kimikage Date: Sun, 8 Sep 2019 12:53:37 +0900 Subject: [PATCH 13/33] Improve accumulation slightly --- src/display.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/display.jl b/src/display.jl index 376819c5..6dbe8ea7 100644 --- a/src/display.jl +++ b/src/display.jl @@ -12,7 +12,7 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", c::Color) """ - + """) flush(io) # return nothing @@ -108,7 +108,9 @@ function show_strokes(io::IO, mime::MIME"image/svg+xml", cs::AbstractMatrix{T}; v = min(m-i+1, d) # cell height for y in i:i+v-1, x in j:j+u-1 rgb = convert(RGB{Float32}, cs[y, x]) - csum += [red(rgb), green(rgb), blue(rgb)] + csum[1] += red(rgb) # avoid array allocation + csum[2] += green(rgb) + csum[3] += blue(rgb) end csum /= (u * v) c = hex(RGB{Float32}(csum[1], csum[2], csum[3])) From 2afabe2c1311f41383bc1b90a33c10b2f8c3456e Mon Sep 17 00:00:00 2001 From: kimikage Date: Sun, 8 Sep 2019 20:49:58 +0900 Subject: [PATCH 14/33] Add support for displaying `TransparentColor` as SVG --- src/display.jl | 71 ++++++++++++++++++++++++++++++++++++++ test/display.jl | 91 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 143 insertions(+), 19 deletions(-) diff --git a/src/display.jl b/src/display.jl index 6dbe8ea7..6ee70637 100644 --- a/src/display.jl +++ b/src/display.jl @@ -18,12 +18,74 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", c::Color) flush(io) # return nothing end +function Base.show(io::IO, mime::MIME"image/svg+xml", c::TransparentColor) + show(io, mime, [c], max_swatches=default_max_swatches) +end + + function Base.show(io::IO, mime::MIME"image/svg+xml", cs::AbstractVector{T}; max_swatches::Integer=0) where T <: Color mat = Base.ReshapedArray(cs, (1, length(cs)), ()) show(io, mime, mat, max_swatches=max_swatches) end +function Base.show(io::IO, mime::MIME"image/svg+xml",cs::AbstractVector{T}; + max_swatches::Integer=0) where T <: TransparentColor + + # use random id to avoid id collision when the SVG is embedded inline + id = "pat_" * string(rand(UInt32), base=62) + n = length(cs) + actual_max_swatches = max_swatches > 0 ? max_swatches : default_max_swatches + # When `max_swatches` is specified, the following warning is suppressed. + if max_swatches == 0 && n > actual_max_swatches + trunc = n - actual_max_swatches + @warn """Last $trunc swatches (of $n-element Array) are truncated.""" + yield() + end + w = min(n * max_swatch_size, max_width) + + scale = n * max_swatch_size / w # 1 for square, >1 for portrait + rscale = round(scale, digits=3) + pat_scale = rscale == 1 ? "" : """patternTransform="scale($rscale,1)" """ + + # the following are with the assumption that scale >= 1 (i.e. not landscape) + if rscale == 1 + shape = "0v1h-1z" # triangle + else # rscale > 1 + simplify(x) = replace(string(round(x, digits=2)), r"^0"=>"") + d1 = simplify((1 - 1 / scale) / 2) + d2 = simplify((1 + 1 / scale) / 2) + shape = """$(d1)V1h-1V$(d2)z""" # trapezoid + end + write_declaration(io, mime) + write(io, + """ + + + + + + + + """) + for i in 1:min(n, actual_max_swatches) + hexc = hex(color(cs[i])) + opacity = string(round(alpha(cs[i]), digits=4)) + op = replace(opacity, r"(^0(?!\.0$))|(\.0$)"=>"") + write(io, + """ + + + """) + end + write(io, "") + flush(io) # return nothing +end + + function Base.show(io::IO, mime::MIME"image/svg+xml", cs::AbstractMatrix{T}; max_swatches::Integer=0) where T <: Color m, n = size(cs) @@ -68,6 +130,15 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", cs::AbstractMatrix{T}; flush(io) # return nothing end +# TODO: add `show` method for `AbstractMatrix{T} where T<:TransparentColor` +# Unlike the case of opaque `Color`, hasty generalization has some problems: +# - Large matrices (i.e. images) may cause performance (size or speed) issue +# - User may expect a raster image to be displayed instead of swatches +# - ImageShow.jl(v0.2.0) suppresses only the SVG matrices of opaque `Color` +# - Checker pattern is less useful when each swatch is smaller than the checker +# - Area-average as reduction method is perceptually meaningless for alpha + + function show_strokes(io::IO, mime::MIME"image/svg+xml", cs::AbstractMatrix{T}; max_swatches::Integer=0) where T <: Color m, n = size(cs) diff --git a/test/display.jl b/test/display.jl index 33982764..7a44ca49 100644 --- a/test/display.jl +++ b/test/display.jl @@ -1,25 +1,20 @@ @testset "Display" begin - function count_filled_rect(svg::AbstractString) + function count_element(pattern::Regex, svg::AbstractString) n = 0 i = firstindex(svg) while true - r = findnext(r"]*\sfill=\"#[0-9A-F]{6}\"[^>]+>", svg, i) - r === nothing && return n - n += 1 - i = last(r) - end - end - - function count_colored_stroke(svg::AbstractString) - n = 0 - i = firstindex(svg) - while true - r = findnext(r"]*\sstroke=\"#[0-9A-F]{6}\"[^>]+>", svg, i) + r = findnext(pattern, svg, i) r === nothing && return n n += 1 i = last(r) end end + count_filled_rect(svg) = + count_element(r"]*\sfill=\"#[0-9A-F]{6}\"[^>]+>", svg) + count_colored_stroke(svg) = + count_element(r"]*\sstroke=\"#[0-9A-F]{6}\"[^>]+>", svg) + count_transparently_filled(svg) = + count_element(r"<[^>]*\sfill-opacity=\"[\d.]+\"[^>]+>", svg) # test ability to add to previously shown array of colors - issue #328 a = [colorant"white", colorant"red"] @@ -32,8 +27,8 @@ # the following tests depend on the constants in "src/display.jl" - # single color - # ------------ + # single `Color` + # -------------- show(buf, "image/svg+xml", colorant"hsl(120, 100%, 50%)") single = String(take!(buf)) @test occursin(r"]+\swidth=\"25mm\"", single) @@ -42,8 +37,8 @@ @test occursin(r"$", single) - # vectors - # ------- + # vectors of `Color` + # ------------------ # square swatches v3 = [colorant"red", colorant"green", colorant"blue"] show(buf, "image/svg+xml", v3) @@ -90,8 +85,8 @@ @test count_colored_stroke(lim_vector3) == 2 # <= max_swatches - # m * n matrices - # -------------- + # m * n matrices of `Color` + # ------------------------- # n * max_swatch_size <= max_width && # m * max_swatch_size <= max_height # square swatches @@ -191,4 +186,62 @@ # the mean RGB of 2x2 elements @test occursin(r"stroke=\"#4080BF\"[^/]*/>\s*", lim_mat2x2) @test count_colored_stroke(lim_mat2x2) == 1 # <= max_swatches + + + # single `TransparentColor` + # ------------------------- + show(buf, "image/svg+xml", colorant"hsla(120, 100%, 50%, 0.2)") + tp_single = String(take!(buf)) + @test occursin(r"]+\swidth=\"25mm\"", tp_single) + @test occursin(r"]+\sheight=\"25mm\"", tp_single) + @test occursin(r"M1,0v1h-1z", tp_single) # triangle + @test occursin(r"\sfill=\"#00FF00\"", tp_single) + @test occursin(r"\sfill-opacity=\".2\"", tp_single) + @test occursin(r"$", tp_single) + @test count_transparently_filled(tp_single) == 1 + + # vectors of `TransparentColor` + # ----------------------------- + # square swatches + tp_v3 = RGBA[colorant"transparent", RGBA(0,0.5,0,0.5), colorant"blue"] + show(buf, "image/svg+xml", tp_v3) + tp_vector3 = String(take!(buf)) + @test occursin(r"]*\swidth=\"75mm\"", tp_vector3) + @test occursin(r"]*\sheight=\"25mm\"", tp_vector3) + @test occursin(r"M1,0v1h-1z", tp_vector3) # triangle + @test occursin(r"\sfill-opacity=\"0\"", tp_vector3) + @test occursin(r"\sfill-opacity=\".5\"", tp_vector3) + @test occursin(r"\sfill-opacity=\"1\"", tp_vector3) + + @test count_transparently_filled(tp_vector3) == 3 + + # rectangle swatches + show(buf, "image/svg+xml", LuvA.(colormap("Greens", 100), 0.8)) + tp_vec100 = String(take!(buf)) + @test occursin(r"]*\swidth=\"180mm\"", tp_vec100) # max_width + @test occursin(r"]*\sheight=\"25mm\"", tp_vec100) + # scale = 13.889 = 1 / 0.072 + @test occursin(r"M1,.46V1h-1V.54z", tp_vec100) # 0.5±(0.072/2), trapezoid + @test count_transparently_filled(tp_vec100) == 100 + + # implicitly limits max swatches (but explicitly warns) + # n > default_max_swatches + # warning of length + warning_tpv = r"Last 16 swatches \(of 16400-element Array\) are truncated\." + tp_vec16400 = [LCHabA(70, 40, i/100, i/16400) for i = 1:128*128+16] + @test_logs (:warn, warning_tpv) show(buf, "image/svg+xml", tp_vec16400) + tp_v16400 = String(take!(buf)) + @test occursin(r"]*\swidth=\"180mm\"", tp_v16400) # max_width + @test occursin(r"]*\sheight=\"25mm\"", tp_v16400) + @test count_transparently_filled(tp_v16400) == 128 * 128 + + # limits max swatches + # the length warning should be suppressed + show(buf, MIME"image/svg+xml"(), tp_v3, max_swatches=2) + lim_tp_vec3 = String(take!(buf)) + @test occursin(r"]*\swidth=\"75mm\"", lim_tp_vec3) + @test occursin(r"]*\sheight=\"25mm\"", lim_tp_vec3) + # viewBox is based on the original length (=3) + @test occursin(r"]*\sviewBox=\"0 0 3 1\"", lim_tp_vec3) + @test count_transparently_filled(lim_tp_vec3) == 2 # max_swatches end From e8d048a8897e1c76fa969c1e88c91039ae8fb8b4 Mon Sep 17 00:00:00 2001 From: kimikage Date: Sun, 22 Sep 2019 19:31:26 +0900 Subject: [PATCH 15/33] Move parser tests to a new file `test/parser.jl` --- test/algorithms.jl | 6 ++++++ test/conversion.jl | 34 +--------------------------------- test/parse.jl | 28 ++++++++++++++++++++++++++++ test/runtests.jl | 1 + test/utilities.jl | 4 ++++ 5 files changed, 40 insertions(+), 33 deletions(-) create mode 100644 test/parse.jl diff --git a/test/algorithms.jl b/test/algorithms.jl index 353a49a2..ea5d6336 100644 --- a/test/algorithms.jl +++ b/test/algorithms.jl @@ -1,7 +1,13 @@ using Test, Colors @testset "Algorithms" begin + # Test vector space operations + @test LMS{Float64}(0.125,0.5,0.0)+LMS{Float64}(0.2,0.7,0.4) ≈ LMS{Float64}(0.325,1.2,0.4) atol=91eps() + @test 3LMS{Float64}(0.125,0.5,0.03) ≈ LMS{Float64}(0.375,1.5,0.09) atol=91eps() + @test XYZ{Float64}(0.125,0.5,0.0)+XYZ{Float64}(0.2,0.7,0.4) ≈ XYZ{Float64}(0.325,1.2,0.4) atol=91eps() + @test 3XYZ{Float64}(0.125,0.5,0.03) ≈ XYZ{Float64}(0.375,1.5,0.09) atol=91eps() + # issue #349 msc_h_diff = 0 for hsv_h in 0:0.1:360 diff --git a/test/conversion.jl b/test/conversion.jl index c517239f..718bc1ba 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -17,32 +17,6 @@ using ColorTypes: eltype_default a, b = promote(RGB(1,0,0), AGray(0.8)) @test isa(a, ARGB{Float64}) && isa(b, ARGB{Float64}) - # Color parsing - redN0f8 = parse(Colorant, "red") - @test colorant"red" == redN0f8 - @test isa(redN0f8, RGB{N0f8}) - @test redN0f8 == RGB(1,0,0) - redF64 = convert(RGB{Float64}, redN0f8) - @test parse(RGB{Float64}, "red") === RGB{Float64}(1,0,0) - @test isa(parse(HSV, "blue"), HSV) - @test parse(Colorant, "rgb(55,217,127)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) - @test colorant"rgb(55,217,127)" === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) - @test parse(Colorant, "rgba(55,217,127,0.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) - @test parse(Colorant, "rgb(55,217,127)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) - @test parse(Colorant, "rgba(55,217,127,0.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) - @test parse(Colorant, "hsl(120, 100%, 50%)") === HSL{Float32}(120,1.0,.5) - @test colorant"hsl(120, 100%, 50%)" === HSL{Float32}(120,1.0,.5) - @test parse(RGB{N0f8}, "hsl(120, 100%, 50%)") === convert(RGB{N0f8}, HSL{Float32}(120,1.0,.5)) - @test_throws ErrorException parse(Colorant, "hsl(120, 100, 50)") - @test parse(Colorant, "#D0FF58") === RGB(r8(0xD0),r8(0xFF),r8(0x58)) - @test parse(Colorant, "#FB0") === RGB(r8(0xFF),r8(0xBB),r8(0x00)) - - @test parse(Colorant, :red) === colorant"red" - @test parse(Colorant, colorant"red") === colorant"red" - - @test hex(RGB(1,0.5,0)) == "FF8000" - @test hex(RGBA(1,0.5,0,0.25)) == "40FF8000" - # srgb_compand / invert_srgb_compand @test Colors.srgb_compand(0.5) ≈ 0.7353569830524494 atol=eps() @test Colors.invert_srgb_compand(0.7353569830524494) ≈ 0.5 atol=eps() @@ -57,6 +31,7 @@ using ColorTypes: eltype_default fractional_types = (RGB, BGR, RGB1, RGB4) # types that support Fractional + redF64 = RGB{Float64}(1, 0, 0) red24 = reinterpret(RGB24, 0x00ff0000) red32 = reinterpret(ARGB32, 0xffff0000) for T in (Float64, Float32, N0f8) @@ -173,13 +148,6 @@ using ColorTypes: eltype_default @test isa(convert(Luv, convert(XYZ, redF64), Colors.WP_DEFAULT), Luv{Float64}) @test isa(convert(Luv{Float32}, convert(XYZ, redF64), Colors.WP_DEFAULT), Luv{Float32}) - # Test vector space operations - @test LMS{Float64}(0.125,0.5,0.0)+LMS{Float64}(0.2,0.7,0.4) ≈ LMS{Float64}(0.325,1.2,0.4) atol=91eps() - @test 3LMS{Float64}(0.125,0.5,0.03) ≈ LMS{Float64}(0.375,1.5,0.09) atol=91eps() - - @test XYZ{Float64}(0.125,0.5,0.0)+XYZ{Float64}(0.2,0.7,0.4) ≈ XYZ{Float64}(0.325,1.2,0.4) atol=91eps() - @test 3XYZ{Float64}(0.125,0.5,0.03) ≈ XYZ{Float64}(0.375,1.5,0.09) atol=91eps() - #59 @test Colors.xyz_to_uv(XYZ{Float64}(0.0, 0.0, 0.0)) === (0.0, 0.0) @test Colors.xyz_to_uv(XYZ{Float64}(0.0, 1.0, 0.0)) === (0.0, 0.6) diff --git a/test/parse.jl b/test/parse.jl new file mode 100644 index 00000000..3e6e9f18 --- /dev/null +++ b/test/parse.jl @@ -0,0 +1,28 @@ +using FixedPointNumbers + +@testset "Parse" begin + r8(x) = reinterpret(N0f8, x) + + # Color parsing + redN0f8 = parse(Colorant, "red") + @test colorant"red" == redN0f8 + @test isa(redN0f8, RGB{N0f8}) + @test redN0f8 == RGB(1,0,0) + @test parse(RGB{Float64}, "red") === RGB{Float64}(1,0,0) + @test isa(parse(HSV, "blue"), HSV) + @test parse(Colorant, "rgb(55,217,127)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) + @test colorant"rgb(55,217,127)" === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) + @test parse(Colorant, "rgba(55,217,127,0.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) + @test parse(Colorant, "rgb(55,217,127)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) + @test parse(Colorant, "rgba(55,217,127,0.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) + @test parse(Colorant, "hsl(120, 100%, 50%)") === HSL{Float32}(120,1.0,.5) + @test colorant"hsl(120, 100%, 50%)" === HSL{Float32}(120,1.0,.5) + @test parse(RGB{N0f8}, "hsl(120, 100%, 50%)") === convert(RGB{N0f8}, HSL{Float32}(120,1.0,.5)) + @test_throws ErrorException parse(Colorant, "hsl(120, 100, 50)") + @test parse(Colorant, "#D0FF58") === RGB(r8(0xD0),r8(0xFF),r8(0x58)) + @test parse(Colorant, "#FB0") === RGB(r8(0xFF),r8(0xBB),r8(0x00)) + + @test parse(Colorant, :red) === colorant"red" + @test parse(Colorant, colorant"red") === colorant"red" + +end diff --git a/test/runtests.jl b/test/runtests.jl index 109e3653..597dd341 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,4 +7,5 @@ include("colormaps.jl") include("colordiff.jl") include("din99.jl") include("display.jl") +include("parse.jl") include("utilities.jl") diff --git a/test/utilities.jl b/test/utilities.jl index 9f39991d..6913186b 100644 --- a/test/utilities.jl +++ b/test/utilities.jl @@ -1,6 +1,10 @@ using Colors, FixedPointNumbers, Test, InteractiveUtils @testset "Utilities" begin + + @test hex(RGB(1,0.5,0)) == "FF8000" + @test hex(RGBA(1,0.5,0,0.25)) == "40FF8000" + # test utility function weighted_color_mean parametric2 = [GrayA,AGray32,AGray] parametric3 = ColorTypes.parametric3 From 9c1fe68e742bccb85e9a879af29a096be3fd6efa Mon Sep 17 00:00:00 2001 From: kimikage Date: Sun, 22 Sep 2019 23:39:47 +0900 Subject: [PATCH 16/33] Remove duplicate test cases and Add labels for test sets Unfortunately, there is almost no speedup effect. --- test/conversion.jl | 151 ++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 78 deletions(-) diff --git a/test/conversion.jl b/test/conversion.jl index 718bc1ba..34df4bc5 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -1,6 +1,6 @@ using Colors, FixedPointNumbers, JLD2 using Test -using ColorTypes: eltype_default +using ColorTypes: eltype_default, parametric3 @testset "Conversion" begin r8(x) = reinterpret(N0f8, x) @@ -31,79 +31,62 @@ using ColorTypes: eltype_default fractional_types = (RGB, BGR, RGB1, RGB4) # types that support Fractional - redF64 = RGB{Float64}(1, 0, 0) + redF64 = RGB{Float64}(1,0,0) + redF32 = RGB{Float32}(1,0,0) red24 = reinterpret(RGB24, 0x00ff0000) red32 = reinterpret(ARGB32, 0xffff0000) - for T in (Float64, Float32, N0f8) + @testset "type check: RGB, RGB{$T}" for T in (Float64, Float32, N0f8) c = RGB(one(T), zero(T), zero(T)) @test eltype(c) == T - c64 = convert(RGB{Float64}, c) - @test typeof(c64) == RGB{Float64} - @test c64 == redF64 - cr = convert(RGB{T}, redF64) - @test cr == c + @test convert(RGB{Float64}, c) === redF64 + @test convert(RGB{T}, redF64) == c end @test RGB(1,0,0) == redF64 - @test RGB(convert(UInt8, 1),0,0) == redF64 - @test RGB(convert(UInt8, 1),convert(UInt8, 0),convert(UInt8, 0)) == redF64 + @test RGB(UInt8(1), 0, 0) == redF64 + @test RGB(UInt8(1), UInt8(0), UInt8(0)) == redF64 # != colorant"#010000" @test convert(RGB, red24) == redF64 - @test convert(Gray{N0f8}, Gray{N0f8}(0.1)) == Gray{N0f8}(0.1) - @test convert(Gray{N0f8}, Gray(0.1)) == Gray{N0f8}(0.1) - @test convert(Gray{N0f8}, Gray24(0.1)) == Gray{N0f8}(0.1) - @test convert(Gray24, Gray{N0f8}(0.1)) == Gray24(0.1) - - @test convert(RGB{N0f8}, Gray{N0f8}(0.1)) == RGB{N0f8}(0.1,0.1,0.1) - @test convert(RGB{N0f8}, Gray24(0.1)) == RGB{N0f8}(0.1,0.1,0.1) - - for Cto in ColorTypes.parametric3 - for Cfrom in ColorTypes.parametric3 - for Tto in (Float32, Float64) - for Tfrom in (Float32, Float64) - c = convert(Cfrom{Tfrom}, redF64) - @test typeof(c) == Cfrom{Tfrom} - c1 = convert(Cto, c) - @test eltype(c1) == Tfrom - c2 = convert(Cto{Tto}, c) - @test typeof(c2) == Cto{Tto} - end - end - end - end - for Cto in ColorTypes.parametric3 + @testset "type check: RGB24-->$Cto" for Cto in parametric3 @test typeof(convert(Cto, red24)) == Cto{eltype_default(Cto)} @test typeof(convert(Cto{Float64}, red24)) == Cto{Float64} end + @testset "type check: C{Float}-->C{Float}" for Cfrom in parametric3, Tfrom in (Float32, Float64) + c = convert(Cfrom{Tfrom}, redF64) + @test typeof(c) == Cfrom{Tfrom} + @testset "$Cfrom{$Tfrom}-->$Cto" for Cto in parametric3 + c1 = convert(Cto, c) + @test eltype(c1) == Tfrom + end + @testset "$Cfrom{$Tfrom}-->$Cto{$Tto}" for Cto in parametric3, Tto in (Float32, Float64) + c2 = convert(Cto{Tto}, c) + @test typeof(c2) == Cto{Tto} + end + end + + normed_types = (N0f8, N6f10, N4f12, N2f14, N0f16) # Test conversion from Normed types - for Cto in ColorTypes.parametric3 - for Cfrom in fractional_types - for Tto in (Float32, Float64) - for Tfrom in (N0f8, N6f10, N4f12, N2f14, N0f16) - c = convert(Cfrom{Tfrom}, redF64) - @test typeof(c) == Cfrom{Tfrom} - if !(eltype_default(Cto) <: FixedPoint) - c1 = convert(Cto, c) - @test eltype(c1) == eltype_default(Cto) - end - c2 = convert(Cto{Tto}, c) - @test typeof(c2) == Cto{Tto} - end - end + @testset "type check: C{Normed}-->C{Float}" for Cfrom in fractional_types, Tfrom in normed_types + c = convert(Cfrom{Tfrom}, redF64) + @test typeof(c) == Cfrom{Tfrom} + @testset "$Cfrom{$Tfrom}-->$Cto" for Cto in parametric3 + eltype_default(Cto) <: FixedPoint && continue + c1 = convert(Cto, c) + @test eltype(c1) == eltype_default(Cto) + end + @testset "$Cfrom{$Tfrom}-->$Cto{$Tto}" for Cto in parametric3, Tto in (Float32, Float64) + c2 = convert(Cto{Tto}, c) + @test typeof(c2) == Cto{Tto} end end # Test conversion to Normed types - for Cto in fractional_types - for Cfrom in ColorTypes.parametric3 - for Tto in (N0f8, N6f10, N4f12, N2f14, N0f16) - for Tfrom in (Float32, Float64) - c = convert(Cfrom{Tfrom}, redF64) - @test typeof(c) == Cfrom{Tfrom} - c2 = convert(Cto{Tto}, c) - @test typeof(c2) == Cto{Tto} - end - end + @testset "type check: C{Float}-->C{Normed}" for Cfrom in parametric3, Tfrom in (Float32, Float64) + c = convert(Cfrom{Tfrom}, redF64) + @test typeof(c) == Cfrom{Tfrom} + @testset "$Cfrom{$Tfrom}-->$Cto{$Tto}" for Cto in fractional_types, Tto in normed_types + c2 = convert(Cto{Tto}, c) + @test typeof(c2) == Cto{Tto} end end @@ -169,16 +152,35 @@ using ColorTypes: eltype_default @test convert(RGB, YIQ(0.0,0.0,-1.0)) == RGB(0,-0.6474*v,0) # Gray - c = Gray{N0f16}(0.8) - @test convert(RGB, c) == RGB{N0f16}(0.8,0.8,0.8) - @test convert(RGB{Float32}, c) == RGB{Float32}(0.8,0.8,0.8) + @test convert(Gray{N0f8}, Gray{N0f8}(0.1)) == Gray{N0f8}(0.1) + @test convert(Gray{N0f8}, Gray(0.1)) == Gray{N0f8}(0.1) + @test convert(Gray{N0f8}, Gray24(0.1)) == Gray{N0f8}(0.1) + @test convert(Gray24, Gray{N0f8}(0.1)) == Gray24(0.1) + + @test convert(RGB{N0f8}, Gray{N0f8}(0.1)) == RGB{N0f8}(0.1,0.1,0.1) + @test convert(RGB{N0f8}, Gray24(0.1)) == RGB{N0f8}(0.1,0.1,0.1) - for C in ColorTypes.parametric3 + grayN0f16 = Gray{N0f16}(0.8) + @test convert(RGB, grayN0f16) == RGB{N0f16}(0.8,0.8,0.8) + @test convert(RGB{Float32}, grayN0f16) == RGB{Float32}(0.8,0.8,0.8) + + @testset "$C-->Gray" for C in parametric3 c = convert(C, RGB(1,1,1)) - @test gray(convert(Gray, c)) ≈ 1 atol=0.01 - @test gray(convert(Gray{Float64}, c)) ≈ 1 atol=0.01 + g1 = convert(Gray, c) + @test isa(g1, Gray) + @test gray(g1) ≈ 1 atol=0.01 + g2 = convert(Gray{Float64}, c) + @test typeof(g2) == Gray{Float64} + @test gray(g2) ≈ 1 atol=0.01 end + # Images issue #382 + @test convert(Gray, RGBA(1,1,1,1)) == Gray(N0f8(1)) + + # https://github.com/timholy/Images.jl/pull/445#issuecomment-189866806 + @test convert(Gray, RGB{N0f8}(0.145,0.145,0.145)) == Gray{N0f8}(0.145) + + # More AbstractRGB r4 = RGB4(1,0,0) @test convert(RGB, r4) == RGB(1,0,0) @@ -187,6 +189,11 @@ using ColorTypes: eltype_default @test convert(RGB4{Float32}, r4) == RGB4{Float32}(1,0,0) @test convert(BGR{Float32}, r4) == BGR{Float32}(1,0,0) + # Issue #257 + c = RGB{Float16}(0.9473,0.962,0.9766) + hsi = convert(HSI, c) + @test hsi.i > 0.96 && hsi.h ≈ 210 + # Test accuracy of conversion csconv = jldopen(joinpath(dirname(@__FILE__), "test_conversions.jld2")) do file read(file, "csconv") @@ -209,21 +216,9 @@ using ColorTypes: eltype_default errmax <= eps end - for i = 1:length(csconv) + base_t(i, from_to) = base_color_type(eltype(csconv[i][from_to])) + @testset "accuracy test: $(base_t(i,1))-->$(base_t(i,2)) (index $i)" for i = 1:length(csconv) f, t = csconv[i] - if !convcompare(f, t, 1e-3) - println(" index $i") - end + @test convcompare(f, t, 1e-3) end - - # Images issue #382 - @test convert(Gray, RGBA(1,1,1,1)) == Gray(N0f8(1)) - - # https://github.com/timholy/Images.jl/pull/445#issuecomment-189866806 - @test convert(Gray, RGB{N0f8}(0.145,0.145,0.145)) == Gray{N0f8}(0.145) - - # Issue #257 - c = RGB{Float16}(0.9473,0.962,0.9766) - hsi = convert(HSI, c) - @test hsi.i > 0.96 && hsi.h ≈ 210 end From 7f171bd6eeacc2a5016e7a0e5d2e6429ffe42278 Mon Sep 17 00:00:00 2001 From: kimikage Date: Tue, 24 Sep 2019 01:50:39 +0900 Subject: [PATCH 17/33] Modify error evaluation methods for conversion accuracy tests --- test/conversion.jl | 53 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/test/conversion.jl b/test/conversion.jl index 34df4bc5..7d56d14b 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -199,26 +199,61 @@ using ColorTypes: eltype_default, parametric3 read(file, "csconv") end - function convcompare(from, to, eps; showfailure::Bool=false) + # Since `colordiff`(e.g. `DE_2000`) involves a color space conversion, it is + # not suitable for evaluating the conversion itself. On the other hand, + # since the tolerance varies from component to component, a homogeneous + # error evaluation function (e.g. a simple sum of differences) is also not + # appropriate. Therefore, a series of `diffnorm`, which returns the + # normalized Euclidian distance, is defined as follows. They are just for + # testing purposes as the cyclicity of hue is ignored. + sqd(a, b, s=1.0) = ((float(a) - float(b))/s)^2 + function diffnorm(a::T, b::T) where {T<:Color3} # RGB,XYZ,xyY,LMS + sqrt(sqd(comp1(a), comp1(b)) + sqd(comp2(a), comp2(b)) + sqd(comp3(a),comp3(b)))/sqrt(3) + end + function diffnorm(a::T, b::T) where {T<:Union{HSV,HSL,HSI}} + sqrt(sqd(a.h, b.h, 360) + sqd(a.s, b.s) + sqd(comp3(a), comp3(b)))/sqrt(3) + end + function diffnorm(a::T, b::T) where {T<:Union{Lab,Luv}} + sqrt(sqd(a.l, b.l, 100) + sqd(comp2(a), comp2(b), 200) + sqd(comp3(a), comp3(b), 200))/sqrt(3) + end + function diffnorm(a::T, b::T) where {T<:Union{LCHab,LCHuv}} + sqrt(sqd(a.l, b.l, 100) + sqd(a.c, b.c, 100) + sqd(a.h, b.h, 360))/sqrt(3) + end + function diffnorm(a::T, b::T) where {T<:Union{DIN99,DIN99d,DIN99o}} # csconv has no DIN99 case + sqrt(sqd(a.l, b.l, 100) + sqd(a.a, b.a, 100) + sqd(a.b, b.b, 100))/sqrt(3) + end + function diffnorm(a::T, b::T) where {T<:YIQ} + sqrt(sqd(a.y, b.y) + sqd(a.i, b.i, 1.2) + sqd(a.q, b.q, 1.2))/sqrt(3) + end + function diffnorm(a::T, b::T) where {T<:YCbCr} + sqrt(sqd(a.y, b.y, 219) + sqd(a.cb, b.cb, 224) + sqd(a.cr, b.cr, 224))/sqrt(3) + end + + for C in ColorTypes.parametric3 + y = convert(C, RGB(1.0,1.0,0.0)) + b = convert(C, RGB(0.1,0.1,0.2)) + diffnorm(y, b) < 0.5 && @warn("`diffnorm` for $C may be broken") + end + + function convcompare(from, to, tol; showfailure::Bool=false) errmax = 0.0 for i = 1:length(from) t = to[i] f = convert(typeof(t), from[i]) - diff = abs(comp1(t)-comp1(f)) + abs(comp2(t)-comp2(f)) + abs(comp3(t)-comp3(f)) - mag = abs(comp1(t)+comp1(f)) + abs(comp2(t)+comp2(f)) + abs(comp3(t)+comp3(f)) - if showfailure && diff>eps*mag + diff = diffnorm(t, f) + if showfailure && diff>tol original = from[i] - @show original f t + @show original f t diff end - errmax = max(errmax, diff/mag) + errmax = max(errmax, diff) end - errmax > eps && @warn("Error on conversions from $(eltype(from)) to $(eltype(to)), relative error = $errmax") - errmax <= eps + errmax > tol && @warn("Error on conversions from $(eltype(from)) to $(eltype(to)), relative error = $errmax") + errmax <= tol end base_t(i, from_to) = base_color_type(eltype(csconv[i][from_to])) @testset "accuracy test: $(base_t(i,1))-->$(base_t(i,2)) (index $i)" for i = 1:length(csconv) f, t = csconv[i] - @test convcompare(f, t, 1e-3) + @test convcompare(f, t, 2e-3, showfailure=false) end end From c061b8b383a72159379b75c615b0ee69fd2dd2f1 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Fri, 22 Nov 2019 07:58:21 -0600 Subject: [PATCH 18/33] Create CompatHelper.yml --- .github/workflows/CompatHelper.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/CompatHelper.yml diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml new file mode 100644 index 00000000..0243c706 --- /dev/null +++ b/.github/workflows/CompatHelper.yml @@ -0,0 +1,26 @@ +name: CompatHelper + +on: + schedule: + - cron: '00 * * * *' + issues: + types: [opened, reopened] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + julia-version: [1.2.0] + julia-arch: [x86] + os: [ubuntu-latest] + steps: + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.julia-version }} + - name: Pkg.add("CompatHelper") + run: julia -e 'using Pkg; Pkg.add("CompatHelper")' + - name: CompatHelper.main() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: julia -e 'using CompatHelper; CompatHelper.main()' From 0ceb396d38c3a0577110a3b50623dd5de5916d09 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 23 Nov 2019 07:57:55 -0600 Subject: [PATCH 19/33] Add [compat] section to Project.toml and drop Julia 0.7 support (#362) --- .travis.yml | 2 +- Manifest.toml | 85 --------------------------------------------------- Project.toml | 6 ++++ appveyor.yml | 2 +- 4 files changed, 8 insertions(+), 87 deletions(-) delete mode 100644 Manifest.toml diff --git a/.travis.yml b/.travis.yml index c663b93c..4268879e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ os: - osx - linux julia: - - 0.7 - 1.0 + - 1.2 - nightly notifications: email: false diff --git a/Manifest.toml b/Manifest.toml deleted file mode 100644 index 4021bf97..00000000 --- a/Manifest.toml +++ /dev/null @@ -1,85 +0,0 @@ -[[Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[ColorTypes]] -deps = ["FixedPointNumbers", "Random", "Test"] -git-tree-sha1 = "0e3209ba7418aed732e5c3818076b4400ee36c08" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.7.4" - -[[Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[Distributed]] -deps = ["LinearAlgebra", "Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[FixedPointNumbers]] -deps = ["Pkg", "Test"] -git-tree-sha1 = "b8045033701c3b10bf2324d7203404be7aef88ba" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.5.3" - -[[InteractiveUtils]] -deps = ["LinearAlgebra", "Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[LibGit2]] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[LinearAlgebra]] -deps = ["Libdl"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[Pkg]] -deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" - -[[Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[Random]] -deps = ["Serialization"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[Reexport]] -deps = ["Pkg"] -git-tree-sha1 = "7b1d07f411bc8ddb7977ec7f377b97b158514fe0" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "0.2.0" - -[[SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" - -[[Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[Test]] -deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[UUIDs]] -deps = ["Random"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" diff --git a/Project.toml b/Project.toml index 214949a1..6263c246 100644 --- a/Project.toml +++ b/Project.toml @@ -9,6 +9,12 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" +[compat] +julia = "1" +ColorTypes = "0.7, 0.8" +FixedPointNumbers = "0.6" +Reexport = "0.2" + [extras] JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/appveyor.yml b/appveyor.yml index c3acffe7..d92d1eb5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ environment: matrix: - - julia_version: 0.7 - julia_version: 1.0 + - julia_version: 1.2 - julia_version: latest platform: From 1bc194e5e2122b5c9e69f9d55e12afc1ad0e9759 Mon Sep 17 00:00:00 2001 From: kimikage Date: Wed, 27 Nov 2019 12:54:17 +0900 Subject: [PATCH 20/33] Modify strictness of color parser (Fixes #360) - Modify white space handling - Add error message about 8-digit or 4-digit hex notation - Add support for some CSS4 notations - Add tests - Speed up parsing averagely --- src/parse.jl | 114 +++++++++++++++++++++++++++++--------------------- test/parse.jl | 41 ++++++++++++++---- 2 files changed, 98 insertions(+), 57 deletions(-) diff --git a/src/parse.jl b/src/parse.jl index f9468853..5b157905 100644 --- a/src/parse.jl +++ b/src/parse.jl @@ -5,19 +5,20 @@ include("names_data.jl") # Color Parsing # ------------- -const col_pat_hex1 = r"(#|0x)([[:xdigit:]])([[:xdigit:]])([[:xdigit:]])" -const col_pat_hex2 = r"(#|0x)([[:xdigit:]]{2})([[:xdigit:]]{2})([[:xdigit:]]{2})" -const col_pat_rgb = r"rgb\((\d+%?),(\d+%?),(\d+%?)\)" -const col_pat_hsl = r"hsl\((\d+%?),(\d+%?),(\d+%?)\)" -const col_pat_rgba = r"rgba\((\d+%?),(\d+%?),(\d+%?),(\d+(?:\.\d*)?%?)\)" -const col_pat_hsla = r"hsla\((\d+%?),(\d+%?),(\d+%?),(\d+(?:\.\d*)?%?)\)" +const col_pat_hex = r"^\s*(#|0x)([[:xdigit:]]{3,8})\s*$" +const col_pat_rgb = r"^\s*rgb\(\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*\)\s*$" +const col_pat_hsl = r"^\s*hsl\(\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*\)\s*$" +const col_pat_rgba = r"^\s*rgba?\(\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*[,/]\s*((?:\d+|(?=\.\d))(?:\.\d*)?%?)\s*\)\s*$" +const col_pat_hsla = r"^\s*hsla?\(\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*[,\s]\s*(\d+%?)\s*[,/]\s*((?:\d+|(?=\.\d))(?:\.\d*)?%?)\s*\)\s*$" + +chop1(x) = SubString(x, 1, lastindex(x) - 1) # `chop` is slightly slow # Parse a number used in the "rgb()" or "hsl()" color. function parse_rgb(num::AbstractString) if num[end] == '%' - return clamp(parse(Int, num[1:end-1], base=10) / 100, 0, 1) + return N0f8(clamp(parse(Int, chop1(num), base=10) / 100, 0, 1)) else - return clamp(parse(Int, num, base=10) / 255, 0, 1) + return reinterpret(N0f8, UInt8(clamp(parse(Int, num, base=10), 0, 255))) end end @@ -33,78 +34,95 @@ function parse_hsl_sl(num::AbstractString) if num[end] != '%' error("saturation and lightness must end in %") else - return parse(Int, num[1:end-1], base=10) / 100 + return parse(Int, chop1(num), base=10) / 100 end end # Parse a number used in the alpha field of "rgba()" and "hsla()". function parse_alpha_num(num::AbstractString) if num[end] == '%' - return parse(Int, num[1:end-1]) / 100 + return parse(Int, chop1(num), base=10) / 100f0 else + # `parse(Float32, num)` is somewhat slow on Windows(x86_64-w64-mingw32). + # However, the following has the opposite effect on Linux. + # m = match(r"0?\.(\d{1,9})", num) + # if m != nothing + # d = m.captures[1] + # return parse(Int, d, base=10) / Float32(exp10(length(d))) + # end return parse(Float32, num) end end function _parse_colorant(desc::AbstractString) - desc_ = replace(desc, " " => "") - mat = match(col_pat_hex2, desc_) + mat = match(col_pat_hex, desc) if mat != nothing - return RGB{N0f8}(parse(Int, mat.captures[2], base=16) / 255, - parse(Int, mat.captures[3], base=16) / 255, - parse(Int, mat.captures[4], base=16) / 255) + prefix = mat.captures[1] + len = length(mat.captures[2]) + digits = parse(UInt32, mat.captures[2], base=16) + if len == 6 + return convert(RGB{N0f8}, reinterpret(RGB24, digits)) + elseif len == 3 + return RGB{N0f8}(reinterpret(N0f8, UInt8(((digits&0xF00)>>8) * 17)), + reinterpret(N0f8, UInt8(((digits&0x0F0)>>4) * 17)), + reinterpret(N0f8, UInt8(((digits&0x00F)) * 17))) + elseif len == 8 || len == 4 + error("8-digit and 4-digit hex notations are not supported yet.") + end end - - mat = match(col_pat_hex1, desc_) - if mat != nothing - return RGB{N0f8}(parse(Int, mat.captures[2], base=16) / 15, - parse(Int, mat.captures[3], base=16) / 15, - parse(Int, mat.captures[4], base=16) / 15) - end - - mat = match(col_pat_rgb, desc_) + mat = match(col_pat_rgb, desc) if mat != nothing return RGB{N0f8}(parse_rgb(mat.captures[1]), - parse_rgb(mat.captures[2]), - parse_rgb(mat.captures[3])) + parse_rgb(mat.captures[2]), + parse_rgb(mat.captures[3])) end - mat = match(col_pat_hsl, desc_) + mat = match(col_pat_hsl, desc) if mat != nothing - return HSL{ColorTypes.eltype_default(HSL)}(parse_hsl_hue(mat.captures[1]), - parse_hsl_sl(mat.captures[2]), - parse_hsl_sl(mat.captures[3])) + T = ColorTypes.eltype_default(HSL) + return HSL{T}(parse_hsl_hue(mat.captures[1]), + parse_hsl_sl(mat.captures[2]), + parse_hsl_sl(mat.captures[3])) end - mat = match(col_pat_rgba, desc_) + mat = match(col_pat_rgba, desc) if mat != nothing return RGBA{N0f8}(parse_rgb(mat.captures[1]), - parse_rgb(mat.captures[2]), - parse_rgb(mat.captures[3]), - parse_alpha_num(mat.captures[4])) + parse_rgb(mat.captures[2]), + parse_rgb(mat.captures[3]), + parse_alpha_num(mat.captures[4])) end - mat = match(col_pat_hsla, desc_) + mat = match(col_pat_hsla, desc) if mat != nothing - return HSLA{ColorTypes.eltype_default(HSLA)}(parse_hsl_hue(mat.captures[1]), - parse_hsl_sl(mat.captures[2]), - parse_hsl_sl(mat.captures[3]), - parse_alpha_num(mat.captures[4])) + T = ColorTypes.eltype_default(HSLA) + return HSLA{T}(parse_hsl_hue(mat.captures[1]), + parse_hsl_sl(mat.captures[2]), + parse_hsl_sl(mat.captures[3]), + parse_alpha_num(mat.captures[4])) end - - desc_ = lowercase(desc_) - - if desc_ == "transparent" - return RGBA{N0f8}(0,0,0,0) + sdesc = strip(desc) + c = get(color_names, sdesc, nothing) + if c != nothing + return RGB{N0f8}(reinterpret(N0f8, UInt8(c[1])), + reinterpret(N0f8, UInt8(c[2])), + reinterpret(N0f8, UInt8(c[3]))) + end + # since `lowercase` is slightly slow, it is applied only when needed + ldesc = lowercase(sdesc) + c = get(color_names, ldesc, nothing) + if c != nothing + return RGB{N0f8}(reinterpret(N0f8, UInt8(c[1])), + reinterpret(N0f8, UInt8(c[2])), + reinterpret(N0f8, UInt8(c[3]))) end - if !haskey(color_names, desc_) - error("Unknown color: ", desc) + if ldesc == "transparent" + return RGBA{N0f8}(0,0,0,0) end - c = color_names[desc_] - return RGB{N0f8}(c[1] / 255, c[2] / 255, c[3] / 255) + error("Unknown color: ", desc) end # note: these exist to enable proper dispatch, since super(Colorant) == Any diff --git a/test/parse.jl b/test/parse.jl index 3e6e9f18..f4421023 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -4,23 +4,46 @@ using FixedPointNumbers r8(x) = reinterpret(N0f8, x) # Color parsing + # named-color redN0f8 = parse(Colorant, "red") @test colorant"red" == redN0f8 @test isa(redN0f8, RGB{N0f8}) @test redN0f8 == RGB(1,0,0) @test parse(RGB{Float64}, "red") === RGB{Float64}(1,0,0) @test isa(parse(HSV, "blue"), HSV) - @test parse(Colorant, "rgb(55,217,127)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) - @test colorant"rgb(55,217,127)" === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) - @test parse(Colorant, "rgba(55,217,127,0.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) - @test parse(Colorant, "rgb(55,217,127)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) - @test parse(Colorant, "rgba(55,217,127,0.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) - @test parse(Colorant, "hsl(120, 100%, 50%)") === HSL{Float32}(120,1.0,.5) - @test colorant"hsl(120, 100%, 50%)" === HSL{Float32}(120,1.0,.5) - @test parse(RGB{N0f8}, "hsl(120, 100%, 50%)") === convert(RGB{N0f8}, HSL{Float32}(120,1.0,.5)) - @test_throws ErrorException parse(Colorant, "hsl(120, 100, 50)") + @test_throws ErrorException parse(Colorant, "p ink") + @test parse(Colorant, "transparent") === RGBA{N0f8}(0,0,0,0) + @test parse(Colorant, "\nSeaGreen ") === RGB{N0f8}(r8(0x2E),r8(0x8B),r8(0x57)) + + # hex-color @test parse(Colorant, "#D0FF58") === RGB(r8(0xD0),r8(0xFF),r8(0x58)) + @test parse(Colorant, "0xd0ff58") === RGB(r8(0xD0),r8(0xFF),r8(0x58)) @test parse(Colorant, "#FB0") === RGB(r8(0xFF),r8(0xBB),r8(0x00)) + @test_throws ErrorException parse(Colorant, "#FB0A") + @test_throws ErrorException parse(Colorant, "#BAD05") + @test_throws ErrorException parse(Colorant, "#BAD0007") + @test_throws ErrorException parse(Colorant, "#FFBB00AA") # not supported yet + @test_throws ErrorException parse(Colorant, "0xFFBB00AA") # not supported yet + + # rgb() + @test parse(Colorant, "rgb(55,217,127)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) + @test colorant" rgb( 55, 217, 127 ) " === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x7f)) + @test parse(Colorant, "rgb(22%,85%,50%)") === RGB{N0f8}(r8(0x38),r8(0xd9),r8(0x80)) + @test parse(Colorant, "rgba(55,217,127,0.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) + @test parse(Colorant, "rgb( 55,217,127,50%)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) # CSS Color Module Level 4 + @test parse(Colorant, "rgb( 55 217 127 /.5)") === RGBA{N0f8}(r8(0x37),r8(0xd9),r8(0x7f),0.5) # CSS Color Module Level 4 + @test parse(Colorant, "rgb(55, 85%, 50%)") === RGB{N0f8}(r8(0x37),r8(0xd9),r8(0x80)) # this is invalid according to CSS spec. + @test_throws ErrorException parse(Colorant, "rgb(21.6%,85%,50%)") # this is valid but not supported + + # hsl() + @test parse(Colorant, "hsl(120, 100%, 50%)") === HSL{Float32}(120,1.0,.5) + @test colorant" hsl( 120, 100%, 50% ) " === HSL{Float32}(120,1.0,.5) + @test parse(RGB{N0f8},"hsl(120, 100%, 50%)") === convert(RGB{N0f8}, HSL{Float32}(120,1.0,.5)) + @test_throws ErrorException parse(Colorant, "hsl(120, 100, 50)") + @test_throws ErrorException parse(Colorant, "hsl(120%,100%,50%)") + @test parse(Colorant, "hsla(120,50%,7%, .6)") === HSLA{Float32}(120,.5,.07,.6) + @test parse(Colorant, "hsl( 120,50%,7%,60%)") === HSLA{Float32}(120,.5,.07,.6) # CSS Color Module Level 4 + @test parse(Colorant, "hsl( 120 50% 7% / 1)") === HSLA{Float32}(120,.5,.07, 1) # CSS Color Module Level 4 @test parse(Colorant, :red) === colorant"red" @test parse(Colorant, colorant"red") === colorant"red" From c96664185be834aa74a3325e41f3721f225e2e01 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 2 Dec 2019 19:11:01 -0600 Subject: [PATCH 21/33] Add some precompiles --- src/Colors.jl | 3 +++ src/precompile.jl | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/precompile.jl diff --git a/src/Colors.jl b/src/Colors.jl index fc7a9197..ad7178d6 100644 --- a/src/Colors.jl +++ b/src/Colors.jl @@ -35,4 +35,7 @@ include("colormaps.jl") include("display.jl") include("colormatch.jl") +include("precompile.jl") +_precompile_() + end # module diff --git a/src/precompile.jl b/src/precompile.jl new file mode 100644 index 00000000..35afe82b --- /dev/null +++ b/src/precompile.jl @@ -0,0 +1,29 @@ +function _precompile_() + ccall(:jl_generating_output, Cint, ()) == 1 || return nothing + eltypes = (N0f8, N0f16, Float32, Float64) # eltypes of parametric colors + feltypes = (Float32, Float64) # floating-point eltypes + pctypes = (Gray, RGB, AGray, GrayA, ARGB, RGBA) # parametric colors + cctypes = (Gray24, AGray32, RGB24, ARGB32) # non-parametric colors + # conversions + ## to RGB + for T in eltypes, F in feltypes, C in (HSV,LCHab,LCHuv,Lab,Luv,XYZ) + @assert precompile(Tuple{typeof(convert),Type{RGB{T}},C{F}}) + end + ## to XYZ + for T in feltypes, F in feltypes, C in (HSV,LCHab,LCHuv,Lab,Luv,XYZ,RGB) + @assert precompile(Tuple{typeof(convert),Type{XYZ{T}},C{F}}) + end + for T in feltypes, F in (N0f8, N0f16) + @assert precompile(Tuple{typeof(convert),Type{XYZ{T}},RGB{F}}) + end + # parse + @assert precompile(Tuple{typeof(parse),Type{ColorTypes.Colorant},String}) + @assert precompile(Tuple{typeof(parse),Type{RGB{N0f8}},String}) + @assert precompile(Tuple{typeof(parse),Type{RGBA{N0f8}},String}) + # colordiff + for T in eltypes + @assert precompile(Tuple{typeof(colordiff),RGB{T},RGB{T}}) + end + @assert precompile(Tuple{typeof(colormap),String}) + @assert precompile(Tuple{typeof(distinguishable_colors),Int}) +end From eb734ea30c3ddb53fc59bbb9797be15b2c72ab3b Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 3 Dec 2019 04:14:59 -0600 Subject: [PATCH 22/33] Use `literal_pow` rather than `pow` in `Bezier` --- src/utilities.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utilities.jl b/src/utilities.jl index 6e5ee144..855e561a 100644 --- a/src/utilities.jl +++ b/src/utilities.jl @@ -69,7 +69,7 @@ end #Double quadratic Bezier curve function Bezier(t::T, p0::T, p2::T, q0::T, q1::T, q2::T) where T<:Real - B(t,a,b,c)=a*(1.0-t)^2.0+2.0b*(1.0-t)*t+c*t^2.0 + B(t,a,b,c)=a*(1.0-t)^2 + 2.0b*(1.0-t)*t + c*t^2 if t <= 0.5 return B(2.0t, p0, q0, q1) else #t > 0.5 @@ -79,7 +79,7 @@ end #Inverse double quadratic Bezier curve function invBezier(t::T, p0::T, p2::T, q0::T, q1::T, q2::T) where T<:Real - invB(t,a,b,c)=(a-b+sqrt(b^2.0-a*c+(a-2.0b+c)*t))/(a-2.0b+c) + invB(t,a,b,c)=(a-b+sqrt(b^2-a*c+(a-2.0b+c)*t))/(a-2.0b+c) if t < q1 return 0.5*invB(t,p0,q0,q1) else #t >= q1 From 90c1a3fb2e9c24edd246592d74486a446f5772ee Mon Sep 17 00:00:00 2001 From: kimikage Date: Mon, 9 Dec 2019 23:29:00 +0900 Subject: [PATCH 23/33] Change the examples to doctests --- docs/Project.toml | 2 ++ docs/src/colorscales.md | 39 +++++++++++++++++++++++---------------- docs/src/colorspaces.md | 23 +++++++++++++---------- docs/src/namedcolors.md | 21 ++++++++++++++++++--- 4 files changed, 56 insertions(+), 29 deletions(-) diff --git a/docs/Project.toml b/docs/Project.toml index dfa65cd1..fe5a86db 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,2 +1,4 @@ [deps] +Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" diff --git a/docs/src/colorscales.md b/docs/src/colorscales.md index 783352ac..01688a3d 100644 --- a/docs/src/colorscales.md +++ b/docs/src/colorscales.md @@ -9,14 +9,15 @@ distinguishable_colors(n::Integer, seed::Color) distinguishable_colors{T<:Color}(n::Integer,seed::AbstractVector{T}) ``` -By default, `distinguishable_colors` chooses maximally distinguishable colors from the outer product of lightness, chroma, and hue values specified by `lchoices = range(0, stop=100, length=15)`, `cchoices = range(0, stop=100, length=15)`, and `hchoices = range(0, stop=340, length=20)`. The set of colors that `distinguishable_colors` chooses from can be specified by passing different choices as keyword arguments. +By default, `distinguishable_colors` chooses maximally distinguishable colors from the outer product of lightness, chroma, and hue values specified by `lchoices = range(0, stop=100, length=15)`, `cchoices = range(0, stop=100, length=15)`, and `hchoices = range(0, stop=342, length=20)`. The set of colors that `distinguishable_colors` chooses from can be specified by passing different choices as keyword arguments. ```julia distinguishable_colors{T<:Color}(n::Integer, seed::AbstractVector{T}; + dropseed = false, transform::Function = identity, lchoices::AbstractVector = range(0, stop=100, length=15), cchoices::AbstractVector = range(0, stop=100, length=15), - hchoices::AbstractVector = range(0, stop=340, length=20) + hchoices::AbstractVector = range(0, stop=342, length=20) ) ``` @@ -24,7 +25,7 @@ Distinguishability is maximized with respect to the CIEDE2000 color difference f Color arrays generated by `distinguishable_colors` are particularly useful for improving the readability of multiple trace plots. Here’s an example using `PyPlot`: -``` +```julia using PyPlot, Colors vars = 1:10 cols = distinguishable_colors(length(vars)+1, [RGB(1,1,1)])[2:end] @@ -51,26 +52,32 @@ The `range()` function has a method that accepts colors: This generates `n` colors in a linearly interpolated ramp from `start` to `stop`, inclusive, returning an `Array` of colors. -``` +```jldoctest example +julia> using Colors + julia> c1 = colorant"red" RGB{N0f8}(1.0,0.0,0.0) julia> c2 = colorant"green" RGB{N0f8}(0.0,0.502,0.0) -julia> range(c1, stop=c2, length=43) -43-element Array{RGB{N0f8},1} with eltype RGB{FixedPointNumbers.Normed{UInt8,8}}: - RGB{N0f8}(1.0,0.0,0.0) - RGB{N0f8}(0.976,0.012,0.0) - RGB{N0f8}(0.953,0.024,0.0) +julia> range(c1, stop=c2, length=15) +15-element Array{RGB{N0f8},1} with eltype RGB{FixedPointNumbers.Normed{UInt8,8}}: + RGB{N0f8}(1.0,0.0,0.0) RGB{N0f8}(0.929,0.035,0.0) - RGB{N0f8}(0.906,0.047,0.0) - ⋮ - RGB{N0f8}(0.094,0.455,0.0) + RGB{N0f8}(0.859,0.071,0.0) + RGB{N0f8}(0.784,0.11,0.0) + RGB{N0f8}(0.714,0.145,0.0) + RGB{N0f8}(0.643,0.18,0.0) + RGB{N0f8}(0.573,0.216,0.0) + RGB{N0f8}(0.502,0.251,0.0) + RGB{N0f8}(0.427,0.286,0.0) + RGB{N0f8}(0.357,0.322,0.0) + RGB{N0f8}(0.286,0.357,0.0) + RGB{N0f8}(0.216,0.392,0.0) + RGB{N0f8}(0.141,0.431,0.0) RGB{N0f8}(0.071,0.467,0.0) - RGB{N0f8}(0.047,0.478,0.0) - RGB{N0f8}(0.024,0.49,0.0) - RGB{N0f8}(0.0,0.502,0.0) + RGB{N0f8}(0.0,0.502,0.0) ``` ## Weighted color means @@ -79,7 +86,7 @@ The `weighted_color_mean()` function returns a color that is the weighted mean o For example: -``` +```jldoctest example julia> weighted_color_mean(0.5, colorant"red", colorant"green") RGB{N0f8}(0.502,0.251,0.0) ``` diff --git a/docs/src/colorspaces.md b/docs/src/colorspaces.md index 6310f854..8f2ff4b0 100644 --- a/docs/src/colorspaces.md +++ b/docs/src/colorspaces.md @@ -25,29 +25,32 @@ Colors.jl allows you to convert from one colorspace to another using the `conver For example: -```julia -convert(RGB, HSL(270, 0.5, 0.5)) +```jldoctest example +julia> using Colors + +julia> convert(RGB, HSL(270, 0.5, 0.5)) +RGB{Float64}(0.5,0.25,0.75) ``` Depending on the source and destination colorspace, this may not be perfectly lossless. ## Color Parsing -``` +```jldoctest example julia> c = colorant"red" -RGB{N0f8}(1.0, 0.0, 0.0) +RGB{N0f8}(1.0,0.0,0.0) julia> parse(Colorant, "red") -RGB{N0f8}(1.0, 0.0, 0.0) +RGB{N0f8}(1.0,0.0,0.0) julia> parse(Colorant, HSL(1, 1, 1)) -HSL{Float32}(1.0f0, 1.0f0, 1.0f0) +HSL{Float32}(1.0f0,1.0f0,1.0f0) julia> colorant"#FF0000" -RGB{N0f8}(1.0, 0.0, 0.0) +RGB{N0f8}(1.0,0.0,0.0) julia> parse(Colorant, RGBA(1, 0.5, 1, 0.5)) -RGBA{Float64}(1.0, 0.5, 1.0, 0.5) +RGBA{Float64}(1.0,0.5,1.0,0.5) ``` You can parse any [CSS color specification](https://developer.mozilla.org/en-US/docs/CSS/color) with the exception of `currentColor`. @@ -97,8 +100,8 @@ whitebalance The `colordiff` function gives an approximate value for the difference between two colors. -``` -julia> colordiff(colorant"red", parse(Colorant, HSB(360, 0.75, 1))) +```jldoctest example +julia> colordiff(colorant"red", parse(Colorant, HSV(360, 0.75, 1))) 8.178248292426845 ``` diff --git a/docs/src/namedcolors.md b/docs/src/namedcolors.md index de2cc4bb..0a97da80 100644 --- a/docs/src/namedcolors.md +++ b/docs/src/namedcolors.md @@ -12,7 +12,7 @@ color_names = Dict( Named colors are available as `RGB{N0f8}` using: -```julia +```jldoctest example julia> using Colors julia> color = colorant"indianred" @@ -21,7 +21,7 @@ RGB{N0f8}(0.804,0.361,0.361) or -```julia +```jldoctest example julia> cname = "indianred" "indianred" @@ -29,6 +29,13 @@ julia> color = parse(Colorant, cname) RGB{N0f8}(0.804,0.361,0.361) ``` +or + +```jldoctest example +julia> color = parse(RGB, cname) +RGB{N0f8}(0.804,0.361,0.361) +``` + ![Whites](assets/figures/namedcolorchart-whites.svg) ![Grays](assets/figures/namedcolorchart-grays.svg) @@ -53,8 +60,16 @@ RGB{N0f8}(0.804,0.361,0.361) These colors can be converted to `RGB{N0f32}` (for example) using: -```julia +```jldoctest example julia> using FixedPointNumbers + julia> RGB{N0f32}(color) RGB{N0f32}(0.803922,0.360784,0.360784) ``` + +or + +```jldoctest example +julia> parse(RGB{N0f32}, cname) +RGB{N0f32}(0.803922,0.360784,0.360784) +``` From e948aa4d17f04ea795a1fbc940a4cbf112f95a16 Mon Sep 17 00:00:00 2001 From: kimikage Date: Tue, 8 Oct 2019 08:03:36 +0900 Subject: [PATCH 24/33] Use inline SVG figures in the documents --- docs/colormaps.jl | 31 + docs/make.jl | 14 +- docs/namedcolorcharts.jl | 182 + docs/src/assets/figures/Blues.png | Bin 708 -> 0 bytes docs/src/assets/figures/Greens.png | Bin 710 -> 0 bytes docs/src/assets/figures/Oranges.png | Bin 682 -> 0 bytes docs/src/assets/figures/Purples.png | Bin 703 -> 0 bytes docs/src/assets/figures/RdBu.png | Bin 747 -> 0 bytes docs/src/assets/figures/Reds.png | Bin 686 -> 0 bytes .../figures/generate-named-color-charts.jl | 146 - .../assets/figures/namedcolorchart-blues.svg | 3031 ---------- .../assets/figures/namedcolorchart-browns.svg | 1368 ----- .../assets/figures/namedcolorchart-cyans.svg | 396 -- .../assets/figures/namedcolorchart-grays.svg | 5340 ----------------- .../assets/figures/namedcolorchart-greens.svg | 1817 ------ .../figures/namedcolorchart-oranges.svg | 518 -- .../assets/figures/namedcolorchart-pinks.svg | 646 -- .../figures/namedcolorchart-purples.svg | 2152 ------- .../assets/figures/namedcolorchart-reds.svg | 1283 ---- .../assets/figures/namedcolorchart-whites.svg | 1341 ----- .../figures/namedcolorchart-yellows.svg | 1489 ----- docs/src/assets/figures/wavelength_to_RGB.png | Bin 20761 -> 0 bytes docs/src/assets/resize_svg.js | 24 + docs/src/colormaps.md | 47 +- docs/src/colorscales.md | 6 + docs/src/colorspaces.md | 6 +- docs/src/namedcolors.md | 48 +- images/GenerateImages.jl | 39 - 28 files changed, 333 insertions(+), 19591 deletions(-) create mode 100644 docs/colormaps.jl create mode 100644 docs/namedcolorcharts.jl delete mode 100644 docs/src/assets/figures/Blues.png delete mode 100644 docs/src/assets/figures/Greens.png delete mode 100644 docs/src/assets/figures/Oranges.png delete mode 100644 docs/src/assets/figures/Purples.png delete mode 100644 docs/src/assets/figures/RdBu.png delete mode 100644 docs/src/assets/figures/Reds.png delete mode 100644 docs/src/assets/figures/generate-named-color-charts.jl delete mode 100644 docs/src/assets/figures/namedcolorchart-blues.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-browns.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-cyans.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-grays.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-greens.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-oranges.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-pinks.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-purples.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-reds.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-whites.svg delete mode 100644 docs/src/assets/figures/namedcolorchart-yellows.svg delete mode 100644 docs/src/assets/figures/wavelength_to_RGB.png create mode 100644 docs/src/assets/resize_svg.js delete mode 100644 images/GenerateImages.jl diff --git a/docs/colormaps.jl b/docs/colormaps.jl new file mode 100644 index 00000000..638dd083 --- /dev/null +++ b/docs/colormaps.jl @@ -0,0 +1,31 @@ +# This file is the successor to "images/GenerateImages.jl" (v0.5.1). +module Colormaps + +using Colors + +struct ColormapSVG <: Main.SVG + buf::IOBuffer +end + +function ColormapSVG(cm::AbstractVector{T}, w="96mm", h="4mm") where T<:Color + io = IOBuffer() + n = length(cm) + write(io, + """ + + """) + for i in 1:n + c = hex(cm[i]) + write(io, + """ + + """) + end + write(io, "") + ColormapSVG(io) +end + +end diff --git a/docs/make.jl b/docs/make.jl index b2aea07f..fa481bee 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,8 +1,20 @@ using Documenter, Colors +abstract type SVG end +function Base.show(io::IO, mime::MIME"image/svg+xml", svg::SVG) + write(io, take!(svg.buf)) + flush(io) +end + +include("colormaps.jl") +include("namedcolorcharts.jl") + + makedocs( + clean = false, modules = [Colors], - format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true"), + format = Documenter.HTML(prettyurls = get(ENV, "CI", nothing) == "true", + assets = ["assets/resize_svg.js"]), sitename = "Colors", pages = Any[ "Introduction" => "index.md", diff --git a/docs/namedcolorcharts.jl b/docs/namedcolorcharts.jl new file mode 100644 index 00000000..b28e3bcf --- /dev/null +++ b/docs/namedcolorcharts.jl @@ -0,0 +1,182 @@ +# This file was derived from "docs/src/assets/figures/generate-named-color-charts.jl" (v0.9.6). + +module NamedColorCharts + +using Colors + +struct ColorChartSVG <: Main.SVG + buf::IOBuffer +end + +function ColorChartSVG(colorcategory) + io = IOBuffer() + + colornames = colordictionary[colorcategory] + numbercells = length(colornames) + numbercols = 10 + colwidth = 14.8 #mm + rowheight = 15 # mm + swatchheight = 9 # mm + pagewidth = 150 # mm + margin = round(pagewidth - colwidth * numbercols) / 2 + numberrows = convert(Int, ceil(numbercells/numbercols)) + headerheight = 15 # mm + pageheight = headerheight + numberrows * rowheight + margin + write(io, + """ + + + + + """) + write(io, + """ + $(titlecase(colorcategory)) + + """) + + for n = 1:numbercells + name = colornames[n] + col = parse(RGB, name) + x = round(margin + colwidth * ((n - 1) % numbercols), digits=1) + y = round(headerheight + rowheight * ((n - 1) ÷ numbercols), digits=1) + cx = round(colwidth / 2, digits=1) + + write(io, """""") + write(io, """""") + write(io, "") + + len = length(name) + if len <= 12 + write(io, """$name""") + else + m = match(r"^(dark|pale|light|lemon|medium|blanched|(?:.+(?=white|blue|purple|blush)))(.+)$", name) + if m != nothing + write(io, """$(m.captures[1])""") + write(io, """$(m.captures[2])""") + else + write(io, """$(name[1:(len÷2)])""") + write(io, """$(name[(len÷2+1):end])""") + end + end + write(io, "") + + vec = string(round(red(col), digits=2), ", ", + round(green(col), digits=2), ", ", + round(blue(col), digits=2)) + veccol = convert(Lab, col).l > 60 ? "" : " class=\"w\"" + write(io, """$vec""") + write(io, "\n") + end + + write(io, "") + ColorChartSVG(io) +end + +function compare_colors(colorname_a, colorname_b) + # compare two colors, looking just at their LUV luminance values + luv1 = parse(Luv, colorname_a) + luv2 = parse(Luv, colorname_b) + luv1.l == luv2.l ? colorname_a < colorname_b : luv1.l > luv2.l +end + +# sort colors into categories +# these are very bikesheddable +function classifycolornames(colornames) + colorcategories = Dict{String, Array{String, 1}}( + "whites" => [], + "pinks" => [], + "reds" => [], + "oranges" => [], + "yellows" => [], + "greens" => [], + "cyans" => [], + "blues" => [], + "purples" => [], + "browns" => [], + "grays" => []) + for colorname in colornames + if occursin(r"(^grey)|(^gray)", colorname) + push!(colorcategories["grays"], colorname) + elseif occursin(r"turquoise|^aqua$|teal|cyan", colorname) + push!(colorcategories["cyans"], colorname) + elseif occursin(r"lemon|khaki|wheat", colorname) + push!(colorcategories["yellows"], colorname) + elseif occursin(r"orange", colorname) + push!(colorcategories["oranges"], colorname) + elseif occursin(r"chartreuse|olive|marine|lime|green", colorname) + push!(colorcategories["greens"], colorname) + elseif occursin(r"maroon|violetred|pink", colorname) + push!(colorcategories["pinks"], colorname) + elseif occursin(r"mistyrose|plum|thistle|lavender|violet|orchid|magenta|fuchsia|purple", colorname) + push!(colorcategories["purples"], colorname) + elseif occursin(r"azure|gains|slate|navy|indigo|blue", colorname) + push!(colorcategories["blues"], colorname) + elseif occursin(r"gold|yellow", colorname) + push!(colorcategories["yellows"], colorname) + elseif occursin(r"chocolate|sienna|wood|moccasin|bisque|peru|peach|papaya|almond|tan|brown", colorname) + push!(colorcategories["browns"], colorname) + elseif occursin(r"crimson|firebrick|salmon|coral|tomato|red", colorname) + push!(colorcategories["reds"], colorname) + elseif occursin(r"honey|mint|beige|linen|corn|sea|silver|lace|ivory|snow|white", colorname) + push!(colorcategories["whites"], colorname) + elseif occursin(r"grey|gray|black", colorname) + push!(colorcategories["grays"], colorname) + else + error(colorname) + end + end + # sort the RGB values in Luv.l format + # TODO perhaps do this conversion just once instead of 1000s of times :) + for k in keys(colorcategories) + vals = colorcategories[k] + sort!(vals, lt = compare_colors) + colorcategories[k] = vals + end + return colorcategories +end + +function makecolordictionary() + colornames = collect(keys(Colors.color_names)) + colordictionary = classifycolornames(colornames) + checkclassified = String[] + for category in keys(colordictionary) + for c in colordictionary[category] + push!(checkclassified, c) + end + end + stilltodo = setdiff(colornames, checkclassified) + length(stilltodo) > 0 && error("gotta catch them all!") + return colordictionary +end + +const colordictionary = makecolordictionary() + +end diff --git a/docs/src/assets/figures/Blues.png b/docs/src/assets/figures/Blues.png deleted file mode 100644 index 03fe5b405971e3587600b6517e7162cca2a41f92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 708 zcmeAS@N?(olHy`uVBq!ia0y~yV7viji*T?3Nxc@GM?i`#$=lt9;Xep2*t>i(0|Qf~ zr;B4q#hkZyPfwCEN; ztXb)k8Q!@!?WIT120oS}Id3LJY!=(CX1OUW`}I=gi3y&bT32RWnC#;7d=HONu^nGR z?8FNt`-F6|-_A2~mERh5Tw(L|ecAEBGh15(v$em~JH;C<49FHwPI-A}Tgl20?K!uj ztc-(Wx_vKJr@vh+vU`iFd)i9%b8m~XpLu#+ycH^vw!Ht`t}Q|SI&UXgR=wrjzbE|A zgh^#TS)RSu{C()wxusFn*3TupS6+MiDL81_ul*$o3|epz4Sgl^cq_|}T2HSYYcXaAc2N%j8c+;=ZKcXd=i1`8Yz%(S}S;Qqk<<-EMDT%T;DH7uCI zk9=9ftUhD?)CZ1_RtiafSfrr!-i3wTMcFI=k>-Qgg*)5de&gFe<(;RE`-Wh#G~0b? zbALv%`zhKTy|adWBeQIm?MIPIaZ@AvD_Fx4s;)IIejsCD`kG-s!;So4-|yjj{DH}m N!PC{xWt~$(69C(3Mp*y= diff --git a/docs/src/assets/figures/Greens.png b/docs/src/assets/figures/Greens.png deleted file mode 100644 index e5f95e16923d55fe381afdd879638592275884fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 710 zcmeAS@N?(olHy`uVBq!ia0y~yV7viji*T?3Nxc@GM?i`#$=lt9;Xep2*t>i(0|QgF zr;B4q#hkZyPv=P+^0Ypj-*)-Vn?3)FmuZ8JdT&J@pZl_zNh$J}(uI<}&U1C6 znJ$}dzi70%>{_YLq@rDMD%Wx~_we+{9g&g z@Y(&@@0;y5O)1aaTN2iA{Fb++vFq(!7iz-4Ow5U%mbQ28%$c9}vTU%aO1$LA>8(9) zYxlO)%xl-TK9~4h$gutQikfRDM82nn1TE6r(sL;?)AnZ8`%`PXlm1^9Vsct~^pEIo zUA6j#4%;u(|23HNU`b)|8qAk3Rh?N z-+a3L@5IgTvX+0cJ=zz!UR-APt&I_%&wmN3JveLT%$xr!L6qzP?rUexykUsjkd~Il z{-Md#*x0&3d<{^ZaZke8GiUf7ux6*Fl`-f?7#nY9t{`2@oW`%e&0kAx;JB5OaDe^D za<%^&c7AgN*H5xbFZQb9<~UfA;}X*l8uWCYU4y;iQ=b5~^iFfGuT1L&=XiZ*5qD9J z;HtD&$a6~8(t5PyiPOv63cfEYq3<;BHS$*YxE~5FaDNqCD99M2xrg^ff?T~cay2DS}eXX8bg=d#Wzp$Pzi(0|QgM zr;B4q#hkZycNa+;^0Ypbw=T*#J?Z{`OIhz*+~Vr3T}wP2-xS=LlkcY5#vrb6$8yVE z*Q5JXPpuTWEw-RwjnKDHhmzxI&3msV-HuvcT0P~^gax`zug-^CZ&6kJRh;KN>1fpT zNDc0vT-P4iKIMBoiDhe4^y;g-?zD34Wihz#mH5V5$GhoQ@x8Dc+ivar?40s&H&e~^ z?5mFzMW2Pf_Bpq?l$z0(poPy^U=LcU(9kfpV z8xNS)G#y&=dCu22bG{}BT}g|rV)k=ccrE>ZzQg}DhfN>M`*OC5p`L-^j$(cMhph>~ P#K++2>gTe~DWM4f=~PE< diff --git a/docs/src/assets/figures/Purples.png b/docs/src/assets/figures/Purples.png deleted file mode 100644 index b60a1602de2a448dd2c7728b9f27a5372c7deb19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 703 zcmeAS@N?(olHy`uVBq!ia0y~yV7viji*T?3Nxc@GM?i`#$=lt9;Xep2*t>i(0|Qfu zr;B4q#hkZyPxsw6;Awq0|Co>Ubd~r2XT06A^Two#T{FsVwq{6dwv?Zs>UdG@h=? zyQXMY>sr;R@26-UQwUwLwY;e3*4{kbrHAf%B;EB=c30anjq~%?ZF+9!gYK>6O%>B) z-(jD1cpm5NYu=mh-CpoLP-;)^Tg##;RgVhmLkPi@_(F{SK55#|I(KEA=Rp7K~K~E9bHwH_I73I z`S4rf(W^tR#67I?VrWV|zSr&LnOlVqY&Tk8{q#=i`mOJGv(K+_w+xF9dM$UgyjONJ z-^Wn>=$B`{)-~8}eHXDIEiLVveH@5l|IlP=Y;4^izGmjkn~ZxB&Yn5L_kcA!Ev<|} zKf>5}GjqkkSu;aE`9Wu{YsD!?cx`1<7nHuVc*1v~Bc@9YXBG=u z1zPBLy%($!TUuSqzeu%meqXPJ_Z@}bHBsj#Y1#gE`OP~9|vT6F6Q{`Rejt-jNSk?r+^xe}?$GG`u-MOlld-e!z40yTx0ZUHD%gFu< zt}ncfU0I{Qp}}wI?ul_s^RhO}y%*|DsJr%1X9wft&2oPj_bpCJUvjms6PO|yJYD@< J);T3K0RU^$PJ93W diff --git a/docs/src/assets/figures/RdBu.png b/docs/src/assets/figures/RdBu.png deleted file mode 100644 index e35a60a5bc52f15c7bbe724fcf8e00e56facbcb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 747 zcmeAS@N?(olHy`uVBq!ia0y~yV7viji*T?3Nxc@GM?i`#$=lt9;Xep2*t>i(0|V0n zPZ!6KiaBrZ?w$15ji>b@zly2yBq7^-b1(2epI&bD_@|(1V9#~1Z`gV1^`mpf5 ze7xrp?=8Ci`ua`3YHmMQ-&LsLPm>&=h*W_tzJuASb#HymV|wa+Jlr&e|Jr>Ggx|{Ml$2yVy#9<-h;)&Zq9YAH4P7?~i>U|2Hia zv9r6cAh=s6{n=%A$FJ{==ifV3G&gu_-QNGoQ{7knFP0Z~Sn@1w>(uAVlBT{~@_(k0 zERRb-*qoWXtVzBFde1k#iVZ*V?3+=;Cd>6F!d!N#UkiA0-~DD<%*Fq&pY(?om2G)< z;mhqKvu@;^i~3j_DL6ZBLt0wexBGh%&Yn4A_JB1zEo~d4euS~{X5NZ}vu4iBJ-~hK z%$Xc!urlcnO{T`iw;ROQ%$#|XjSMYT4}Sd)cP(()C9pAMzLu}a$H>3?zb(pMC|(mL z&)M9hAjnR55!d{O?QOs~MdwxY695h{9DpLcb32)_uF zyve}6;{5ank~bEf*=@vR{$Ngrti(0|QgC zr;B4q#hkZyPv_ls;Awq0zv;4C-`nr?rx*Asy*9InT~gbyP${L@?tpibGp}Wg)tBeK zAy1~I@GZSjs@tk~a2r?0yBj_?W;y)bczfCk(d^g-pITLKh%IzlyVmix(8c;^xxP_9 z<6P7JSE@!l3-U5bj1=bDx-RTyt=a3xCW>GAtG#-ze%^L$%hsU(_S-L=RE^UA;W5{W z-)WW|-|H4~e$V5$`W63A%U52%d!;&w{pg107na^waPPmp>8qz#zV1{kn_~TD*6v$> zGmbyrvN_Dy*!Xu|+=jHYH1-corpCtB4dQEN&b-OEC*kawGkgzNv(wVb81y5IjW;t_ z9Go?CrtAUkYiG{9VIV_`dcwc&uS;Eh4qTFzyc2Ne<-g)z7K(cn?(Qz0l&;jY=E$+u z1h9Tnq<)v(_Ra`G;2K|4{ z)55i}$@{>XL$f6cnC}IbOkKUc{0C3q%TDox6<4}S7{x-;U~22WQ%mvv4FO#m@`L;(N* diff --git a/docs/src/assets/figures/generate-named-color-charts.jl b/docs/src/assets/figures/generate-named-color-charts.jl deleted file mode 100644 index 0f7c77aa..00000000 --- a/docs/src/assets/figures/generate-named-color-charts.jl +++ /dev/null @@ -1,146 +0,0 @@ -using Colors, Luxor - -function compare_colors(color_a, color_b) - # compare two colors, looking just at their LUV luminance values - luv1 = convert(Luv, RGB(color_a[1]/255, color_a[2]/255, color_a[3]/255)) - luv2 = convert(Luv, RGB(color_b[1]/255, color_b[2]/255, color_b[3]/255)) - luv1.l > luv2.l -end - -# sort colors into categories -# these are very bikesheddable -function classifycolornames(colornames) - colorcategories = Dict{String, Array{String, 1}}( - "whites" => [], - "pinks" => [], - "reds" => [], - "oranges" => [], - "yellows" => [], - "greens" => ["lime"], - "cyans" => [], - "blues" => ["navy", "indigo", "teal"], - "purples" => ["fuchsia"], - "browns" => [], - "grays" => ["black", "dimgrey", "lightgrey", "darkgrey"]) - for colorname in colornames - if occursin(r"(^grey)|(^gray)", colorname) - push!(colorcategories["grays"], colorname) - elseif occursin(r"turquoise|azure|gains|blue|slate", colorname) - push!(colorcategories["blues"], colorname) - elseif occursin(r"lemon|khaki|wheat", colorname) - push!(colorcategories["yellows"], colorname) - elseif occursin(r"chartreuse|olive|aqua|green", colorname) - push!(colorcategories["greens"], colorname) - elseif occursin(r"mistyrose|plum|thistle|lavender|maroon|violet|orchid|magenta|maroon", colorname) - push!(colorcategories["purples"], colorname) - elseif occursin(r".*gold.*", colorname) - push!(colorcategories["yellows"], colorname) - elseif occursin(r"chocolate|sienna|wood|moccasin|bisque|peru|peach|papaya|almond|tan", colorname) - push!(colorcategories["browns"], colorname) - elseif occursin(r"crimson|firebrick|salmon|coral|tomato", colorname) - push!(colorcategories["reds"], colorname) - elseif occursin(r"honey|mint|beige|linen|corn|sea|silver|lace|ivory|snow|white", colorname) - push!(colorcategories["whites"], colorname) - else - # some colors obviously belong to categories - # ie "deeppink" is in "pinks" - for k in keys(colorcategories) - if occursin(replace(k, "s" => ""), colorname) - push!(colorcategories[k], colorname) - end - end - end - end - # sort the RGB values in Luv.l format - # TODO perhaps do this conversion just once instead of 1000s of times :) - for k in keys(colorcategories) - vals = colorcategories[k] - sort!(vals, lt = (color1, color2) -> - compare_colors(Colors.color_names[color1] ./ 255, - Colors.color_names[color2] ./ 255)) - colorcategories[k] = unique(vals) - end - return colorcategories -end - -""" -find a readable color for text placed on top of another color -""" -function inversecolor(foregroundcolor, backgroundcolor; - tolerance = 20) - foreground = foregroundcolor - if colordiff(parse(Colorant, foreground), parse(Colorant, backgroundcolor)) < tolerance - tempcolor = parse(Colorant, backgroundcolor) - clab = convert(Lab, parse(Colorant, tempcolor)) - if clab.l > 50 - labelbrightness = 0 - else - labelbrightness = 100 - end - foreground = convert(RGB, Lab(labelbrightness, clab.b, clab.a)) - end - return foreground -end - -function makeallcolorcharts() - colornames = collect(keys(Colors.color_names)) - colordictionary = classifycolornames(colornames) - categoryorder = ["whites", "yellows", "greens", "cyans", "blues", "purples", "pinks", "reds", "oranges", "browns", "grays"] - checkclassified = String[] - for category in categoryorder - for c in colordictionary[category] - push!(checkclassified, c) - end - end - stilltodo = setdiff(colornames, checkclassified) - length(stilltodo) > 0 && (@show stilltodo; @warn "gotta catch them all!") - for colorcategory in categoryorder - numbercells = length(keys(colordictionary[colorcategory])) - numbercols = 10 - colwidth = 150 - rowheight = 100 - pagewidth = 800 - margin = 15 - numberrows = convert(Int, ceil(numbercells/numbercols) + 1) - Drawing(pagewidth - margin, margin + (numberrows * rowheight), - "/tmp/namedcolorchart-$(colorcategory).svg") - origin() - background("white") - setcolor("black") - fontsize(24) - fontface("Helvetica-Bold") - text(titlecase(colorcategory), BoundingBox()[1] + (margin, 30), halign=:left) - setline(0.5) - line(Point(-pagewidth/2 + margin, 40 + -Luxor.current_height()/2), Point(pagewidth/2 - margin, 40 + -Luxor.current_height()/2), :stroke) - table = Table(numberrows, numbercols, - (pagewidth - 2margin)/numbercols, # cell width - rowheight, # row height - O + (0, 3margin) # initial center position - ) - pts = first.(collect(table)) - n = 1 - for col in colordictionary[colorcategory] - backgroundcolor = Colors.RGB(Colors.color_names[col] ./ 255 ...) - setcolor(backgroundcolor) - # draw swatch - box(pts[n], table.colwidths[1], table.rowheights[1]/2, :fill) - # if overlapping text on swatch, choose color wisely - # sethue(inversecolor("white", backgroundcolor, tolerance=60)) - sethue("black") - fontface("IBMPlexSansCond-Medium") - fontsize(12) - text(col, pts[n] + (0, -table.rowheights[1]/3), halign=:center) - fontsize(9) - fontface("InputMonoCompressed-Regular") - text(string(round(backgroundcolor.r, digits=2), " ", - round(backgroundcolor.g, digits=2), " ", - round(backgroundcolor.b, digits=2)), - pts[n] + (0, table.rowheights[1]/3), halign=:center) - n += 1 - end - finish() - end - return colordictionary -end - -makeallcolorcharts() diff --git a/docs/src/assets/figures/namedcolorchart-blues.svg b/docs/src/assets/figures/namedcolorchart-blues.svg deleted file mode 100644 index 0343a1e6..00000000 --- a/docs/src/assets/figures/namedcolorchart-blues.svg +++ /dev/null @@ -1,3031 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-browns.svg b/docs/src/assets/figures/namedcolorchart-browns.svg deleted file mode 100644 index f744dd9c..00000000 --- a/docs/src/assets/figures/namedcolorchart-browns.svg +++ /dev/null @@ -1,1368 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-cyans.svg b/docs/src/assets/figures/namedcolorchart-cyans.svg deleted file mode 100644 index 996586af..00000000 --- a/docs/src/assets/figures/namedcolorchart-cyans.svg +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-grays.svg b/docs/src/assets/figures/namedcolorchart-grays.svg deleted file mode 100644 index 83d22bff..00000000 --- a/docs/src/assets/figures/namedcolorchart-grays.svg +++ /dev/null @@ -1,5340 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-greens.svg b/docs/src/assets/figures/namedcolorchart-greens.svg deleted file mode 100644 index c092e688..00000000 --- a/docs/src/assets/figures/namedcolorchart-greens.svg +++ /dev/null @@ -1,1817 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-oranges.svg b/docs/src/assets/figures/namedcolorchart-oranges.svg deleted file mode 100644 index 3f41d329..00000000 --- a/docs/src/assets/figures/namedcolorchart-oranges.svg +++ /dev/null @@ -1,518 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-pinks.svg b/docs/src/assets/figures/namedcolorchart-pinks.svg deleted file mode 100644 index 8cfb6739..00000000 --- a/docs/src/assets/figures/namedcolorchart-pinks.svg +++ /dev/null @@ -1,646 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-purples.svg b/docs/src/assets/figures/namedcolorchart-purples.svg deleted file mode 100644 index 07a51e3c..00000000 --- a/docs/src/assets/figures/namedcolorchart-purples.svg +++ /dev/null @@ -1,2152 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-reds.svg b/docs/src/assets/figures/namedcolorchart-reds.svg deleted file mode 100644 index 17a0d2e9..00000000 --- a/docs/src/assets/figures/namedcolorchart-reds.svg +++ /dev/null @@ -1,1283 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-whites.svg b/docs/src/assets/figures/namedcolorchart-whites.svg deleted file mode 100644 index dca35ad2..00000000 --- a/docs/src/assets/figures/namedcolorchart-whites.svg +++ /dev/null @@ -1,1341 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/namedcolorchart-yellows.svg b/docs/src/assets/figures/namedcolorchart-yellows.svg deleted file mode 100644 index 89383427..00000000 --- a/docs/src/assets/figures/namedcolorchart-yellows.svg +++ /dev/null @@ -1,1489 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/assets/figures/wavelength_to_RGB.png b/docs/src/assets/figures/wavelength_to_RGB.png deleted file mode 100644 index 8bff532cd714ffa260a46296447b70d40d6e381b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20761 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 range(c1, stop=c2, length=15) RGB{N0f8}(0.071,0.467,0.0) RGB{N0f8}(0.0,0.502,0.0) ``` +If you use Julia through Juno or IJulia, you can get the following color swatches. +```@example +using Colors # hide +showable(::MIME"text/plain", ::AbstractVector{C}) where {C<:Colorant} = false # hide +range(colorant"red", stop=colorant"green", length=15) +``` ## Weighted color means diff --git a/docs/src/colorspaces.md b/docs/src/colorspaces.md index 8f2ff4b0..2aba8c39 100644 --- a/docs/src/colorspaces.md +++ b/docs/src/colorspaces.md @@ -36,6 +36,7 @@ Depending on the source and destination colorspace, this may not be perfectly lo ## Color Parsing + ```jldoctest example julia> c = colorant"red" RGB{N0f8}(1.0,0.0,0.0) @@ -75,10 +76,11 @@ The CIE defines a *standard observer*, defining a typical frequency response cur For instance, conversion from optical wavelength to RGB can be achieved with: -```julia +```@example +using Colors # hide +showable(::MIME"text/plain", ::AbstractVector{C}) where {C<:Colorant} = false # hide RGB.(colormatch.(350:10:750)) ``` -![wavelength to RGB](assets/figures/wavelength_to_RGB.png) ```@docs colormatch diff --git a/docs/src/namedcolors.md b/docs/src/namedcolors.md index 0a97da80..c9e653bf 100644 --- a/docs/src/namedcolors.md +++ b/docs/src/namedcolors.md @@ -27,6 +27,9 @@ julia> cname = "indianred" julia> color = parse(Colorant, cname) RGB{N0f8}(0.804,0.361,0.361) + +julia> color = parse(RGB, cname) +RGB{N0f8}(0.804,0.361,0.361) ``` or @@ -36,27 +39,50 @@ julia> color = parse(RGB, cname) RGB{N0f8}(0.804,0.361,0.361) ``` -![Whites](assets/figures/namedcolorchart-whites.svg) +```@example chart +using Main: NamedColorCharts # hide +NamedColorCharts.ColorChartSVG("whites") # hide +``` -![Grays](assets/figures/namedcolorchart-grays.svg) +```@example chart +NamedColorCharts.ColorChartSVG("reds") # hide +``` -![Reds](assets/figures/namedcolorchart-reds.svg) +```@example chart +NamedColorCharts.ColorChartSVG("oranges") # hide +``` -![Oranges](assets/figures/namedcolorchart-oranges.svg) +```@example chart +NamedColorCharts.ColorChartSVG("yellows") # hide +``` -![Yellows](assets/figures/namedcolorchart-yellows.svg) +```@example chart +NamedColorCharts.ColorChartSVG("greens") # hide +``` -![Greens](assets/figures/namedcolorchart-greens.svg) +```@example chart +NamedColorCharts.ColorChartSVG("cyans") # hide +``` -![Cyans](assets/figures/namedcolorchart-cyans.svg) +```@example chart +NamedColorCharts.ColorChartSVG("blues") # hide +``` -![Blues](assets/figures/namedcolorchart-blues.svg) +```@example chart +NamedColorCharts.ColorChartSVG("purples") # hide +``` -![Purples](assets/figures/namedcolorchart-purples.svg) +```@example chart +NamedColorCharts.ColorChartSVG("pinks") # hide +``` -![Browns](assets/figures/namedcolorchart-browns.svg) +```@example chart +NamedColorCharts.ColorChartSVG("browns") # hide +``` -![Pinks](assets/figures/namedcolorchart-pinks.svg) +```@example chart +NamedColorCharts.ColorChartSVG("grays") # hide +``` These colors can be converted to `RGB{N0f32}` (for example) using: diff --git a/images/GenerateImages.jl b/images/GenerateImages.jl deleted file mode 100644 index 3bd50b97..00000000 --- a/images/GenerateImages.jl +++ /dev/null @@ -1,39 +0,0 @@ -using Colors -using Colors, ColorBrewer - -function out_image(name, color_list) - total_width = 12. - elt_width = total_width/length(color_list) - elt_height = 0.5 - width, height = "$(elt_width)cm", "0.5cm" - path = "$git_path/images" - svg_filename = "$path/$name.svg" - png_filename = "$path/$name.png" - f = open(svg_filename, "w") - @printf f "%s\n" "" - for (x, color) in zip(0:length(color_list), color_list) - r = int(round(color.r*255)) - g = int(round(color.g*255)) - b = int(round(color.b*255)) - position = "x=\"$(x*elt_width)cm\" width=\"$width\" height=\"$height\"" - fill = "fill=\"rgb($r, $g, $b)\"" - stroke = "stroke=\"rgb($r, $g, $b)\"" - print(f, "\n") - end - @printf f "" - close(f) - run(`svg2png $svg_filename $png_filename`) - run(`rm $svg_filename`) -end - -function main() - for name in ["Blues", "Greens", "Grays", "Oranges", "Purples", "Reds", "RdBu"] - try - out_image(name, colormap(name, 32)) - catch ex - @printf "Colormap %s failed: %s\n" name ex - end - end -end - -nothing From 9f7f010243aa974c1a65260a0aa8a62435a9bf5f Mon Sep 17 00:00:00 2001 From: kimikage Date: Sat, 14 Dec 2019 16:52:11 +0900 Subject: [PATCH 25/33] Add explanation about the origin of named colors --- docs/src/namedcolors.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/src/namedcolors.md b/docs/src/namedcolors.md index c9e653bf..4d40fb0c 100644 --- a/docs/src/namedcolors.md +++ b/docs/src/namedcolors.md @@ -27,9 +27,6 @@ julia> cname = "indianred" julia> color = parse(Colorant, cname) RGB{N0f8}(0.804,0.361,0.361) - -julia> color = parse(RGB, cname) -RGB{N0f8}(0.804,0.361,0.361) ``` or @@ -84,6 +81,14 @@ NamedColorCharts.ColorChartSVG("browns") # hide NamedColorCharts.ColorChartSVG("grays") # hide ``` +!!! info + Colors.jl supports the CSS/SVG named colors and the X11 named colors. The + CSS/SVG named colors come from the 16 colors defined in HTML3.2 and the X11 + named colors. There are some unnatural definitions due to the different + origins. For example, "LightGray" is lighter than "Gray", but "DarkGray" is + also lighter than "Gray". + + These colors can be converted to `RGB{N0f32}` (for example) using: ```jldoctest example From 33ce6c0c4c3e654021d713e99d0d62c119b68044 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 16 Dec 2019 18:08:32 -0600 Subject: [PATCH 26/33] Use RGB accessor functions Gray conversion (fixes #377) (#380) This also provides methods for Gray24 conversion --- src/conversions.jl | 22 ++++++++++++++-------- test/conversion.jl | 3 +++ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/conversions.jl b/src/conversions.jl index d3e797f6..24d29660 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -779,11 +779,17 @@ end const unsafe_trunc = Base.unsafe_trunc convert(::Type{Gray{T}}, x::Gray{T}) where {T} = x -convert(::Type{Gray{T}}, x::Gray) where {T} = Gray{T}(gray(x)) -function convert(::Type{Gray{T}}, x::AbstractRGB{T}) where {T<:Normed} - TU = FixedPointNumbers.rawtype(T) - val = min(typemax(TU), 0.299f0*reinterpret(x.r) + 0.587f0*reinterpret(x.g) + 0.114f0*reinterpret(x.b)) - return Gray{T}(T(round(TU, val), 0)) -end -convert(::Type{Gray{T}}, x::AbstractRGB) where {T} = convert(Gray{T}, 0.299f0*x.r + 0.587f0*x.g + 0.114f0*x.b) -convert(::Type{Gray{T}}, x::Color) where {T} = convert(Gray{T}, convert(RGB{T}, x)) +convert(::Type{Gray24}, x::Gray24) = x + +convert(::Type{G}, x::AbstractGray) where {G<:AbstractGray} = G(gray(x)) + +function convert(::Type{G}, x::AbstractRGB{T}) where {G<:AbstractGray,T<:Normed} + TU, Tf = FixedPointNumbers.rawtype(T), floattype(T) + val = min(typemax(TU), Tf(0.299)*reinterpret(red(x)) + Tf(0.587)*reinterpret(green(x)) + Tf(0.114)*reinterpret(blue(x))) + return G(reinterpret(T, round(TU, val))) +end +convert(::Type{G}, x::AbstractRGB) where {G<:AbstractGray} = + G(0.299f0*red(x) + 0.587f0*green(x) + 0.114f0*blue(x)) + +convert(::Type{G}, x::Color) where {G<:AbstractGray} = + convert(G, convert(RGB, x)) diff --git a/test/conversion.jl b/test/conversion.jl index 7d56d14b..aef77b41 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -173,6 +173,9 @@ using ColorTypes: eltype_default, parametric3 @test typeof(g2) == Gray{Float64} @test gray(g2) ≈ 1 atol=0.01 end + # Issue #377 + @test convert(Gray, RGB24(1,0,0)) === convert(Gray, RGB(1,0,0)) === Gray{N0f8}(0.298) + @test convert(Gray24, RGB(1,0,0)) === Gray24(0.298) # Images issue #382 @test convert(Gray, RGBA(1,1,1,1)) == Gray(N0f8(1)) From 29f43f7489c356b418b6489903cc88db1eee72f9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2019 06:16:34 -0600 Subject: [PATCH 27/33] CompatHelper: bump compat for "FixedPointNumbers" to "0.7" (#382) --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 6263c246..c7ef03e4 100644 --- a/Project.toml +++ b/Project.toml @@ -10,10 +10,10 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] -julia = "1" ColorTypes = "0.7, 0.8" -FixedPointNumbers = "0.6" +FixedPointNumbers = "0.6, 0.7" Reexport = "0.2" +julia = "1" [extras] JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" From 36af6c5346535183e45f1b92ff762e734beec8a3 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Tue, 17 Dec 2019 06:34:58 -0600 Subject: [PATCH 28/33] Version 0.10.0 (#381) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c7ef03e4..c6ecd159 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Colors" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.9.6" +version = "0.10.0" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" From 9ba8b264232717f0495dd05becf0e404b699c38b Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 18 Dec 2019 12:11:25 -0600 Subject: [PATCH 29/33] Support ColorTypes 0.9 (#383) --- Project.toml | 4 ++-- docs/src/colorspaces.md | 2 +- docs/src/index.md | 2 +- src/Colors.jl | 2 ++ test/conversion.jl | 8 ++++---- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index c6ecd159..066a1c2f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Colors" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.10.0" +version = "0.11.0" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -10,7 +10,7 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] -ColorTypes = "0.7, 0.8" +ColorTypes = "0.9" FixedPointNumbers = "0.6, 0.7" Reexport = "0.2" julia = "1" diff --git a/docs/src/colorspaces.md b/docs/src/colorspaces.md index 2aba8c39..c84833c2 100644 --- a/docs/src/colorspaces.md +++ b/docs/src/colorspaces.md @@ -5,7 +5,7 @@ The colorspaces used by Colors are defined in [ColorTypes](https://github.com/JuliaGraphics/ColorTypes.jl). Briefly, the defined spaces are: -- Red-Green-Blue spaces: `RGB`, `BGR`, `RGB1`, `RGB4`, `RGB24`, plus transparent versions `ARGB`, `RGBA`, `ABGR`, `BGRA`, and `ARGB32`. +- Red-Green-Blue spaces: `RGB`, `BGR`, `XRGB`, `RGBX`, `RGB24`, plus transparent versions `ARGB`, `RGBA`, `ABGR`, `BGRA`, and `ARGB32`. - `HSV`, `HSL`, `HSI`, plus all 6 transparent variants (`AHSV`, `HSVA`, `AHSL`, `HSLA`, `AHSI`, `HSIA`) diff --git a/docs/src/index.md b/docs/src/index.md index 076b4d68..b51b7332 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -4,7 +4,7 @@ This library provides a wide array of functions for dealing with color. Supported colorspaces include: -- RGB, BGR, RGB1, RGB4, RGB24, plus transparent versions ARGB, RGBA, ABGR, BGRA, and ARGB32 +- RGB, BGR, XRGB, RGBX, RGB24, plus transparent versions ARGB, RGBA, ABGR, BGRA, and ARGB32 - HSV, HSL, HSI, plus all 6 transparent variants (AHSV, HSVA, AHSL, HSLA, AHSI, HSIA) - XYZ, xyY, LMS and all 6 transparent variants - Lab, Luv, LCHab, LCHuv and all 8 transparent variants diff --git a/src/Colors.jl b/src/Colors.jl index ad7178d6..3d1c9116 100644 --- a/src/Colors.jl +++ b/src/Colors.jl @@ -5,6 +5,8 @@ using Reexport using Printf @reexport using ColorTypes +Base.@deprecate_binding RGB1 XRGB +Base.@deprecate_binding RGB4 RGBX # TODO: why these types are defined here? Can they move to ColorTypes.jl? AbstractAGray{C<:AbstractGray,T} = AlphaColor{C,T,2} diff --git a/test/conversion.jl b/test/conversion.jl index aef77b41..7d416eab 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -29,7 +29,7 @@ using ColorTypes: eltype_default, parametric3 @warn "Optimization in `[invert_]srgb_compand()` may have the opposite effect." end - fractional_types = (RGB, BGR, RGB1, RGB4) # types that support Fractional + fractional_types = (RGB, BGR, XRGB, RGBX) # types that support Fractional redF64 = RGB{Float64}(1,0,0) redF32 = RGB{Float32}(1,0,0) @@ -185,11 +185,11 @@ using ColorTypes: eltype_default, parametric3 # More AbstractRGB - r4 = RGB4(1,0,0) + r4 = RGBX(1,0,0) @test convert(RGB, r4) == RGB(1,0,0) @test convert(RGB{N0f8}, r4) == RGB{N0f8}(1,0,0) - @test convert(RGB4{N0f8}, r4) == RGB4{N0f8}(1,0,0) - @test convert(RGB4{Float32}, r4) == RGB4{Float32}(1,0,0) + @test convert(RGBX{N0f8}, r4) == RGBX{N0f8}(1,0,0) + @test convert(RGBX{Float32}, r4) == RGBX{Float32}(1,0,0) @test convert(BGR{Float32}, r4) == BGR{Float32}(1,0,0) # Issue #257 From 58aa6bae107f5986d863f3a3958d5989ca04c2ff Mon Sep 17 00:00:00 2001 From: kimikage Date: Mon, 30 Dec 2019 23:54:21 +0900 Subject: [PATCH 30/33] Re-support parsing the X11 color names with spaces with a depwarn (Fixes #386) (#390) --- src/parse.jl | 18 ++++++++++++++++++ test/parse.jl | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/parse.jl b/src/parse.jl index 5b157905..317e2d19 100644 --- a/src/parse.jl +++ b/src/parse.jl @@ -122,6 +122,20 @@ function _parse_colorant(desc::AbstractString) return RGBA{N0f8}(0,0,0,0) end + wo_spaces = replace(ldesc, r"(?<=[^ ]{3}) (?=[^ ]{3})" => "") + c = get(color_names, wo_spaces, nothing) + if c != nothing + camel = replace(titlecase(ldesc), " " => "") + Base.depwarn( + """ + The X11 color names with spaces are not recommended because they are not allowed in the SVG/CSS. + Use "$camel" or "$wo_spaces" instead. + """, :parse) + return RGB{N0f8}(reinterpret(N0f8, UInt8(c[1])), + reinterpret(N0f8, UInt8(c[2])), + reinterpret(N0f8, UInt8(c[3]))) + end + error("Unknown color: ", desc) end @@ -159,6 +173,10 @@ A literal Colorant will parse according to the `desc` string (usually returning - an `HSLA` color if `hsla(h, s, l, a)` was used - a specific `Colorant` type as specified in the first argument + +!!! note + The X11 color names with spaces (e.g. "sea green") are not recommended + because they are not allowed in the SVG/CSS. """ Base.parse(::Type{C}, desc::AbstractString) where {C<:Colorant} = _parse_colorant(C, supertype(C), desc) Base.parse(::Type{C}, desc::Symbol) where {C<:Colorant} = parse(C, string(desc)) diff --git a/test/parse.jl b/test/parse.jl index f4421023..b068b76e 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -14,6 +14,8 @@ using FixedPointNumbers @test_throws ErrorException parse(Colorant, "p ink") @test parse(Colorant, "transparent") === RGBA{N0f8}(0,0,0,0) @test parse(Colorant, "\nSeaGreen ") === RGB{N0f8}(r8(0x2E),r8(0x8B),r8(0x57)) + seagreen = @test_logs (:warn, r"Use \"SeaGreen\" or \"seagreen\"") parse(Colorant, "sea GREEN") + @test seagreen == colorant"seagreen" # hex-color @test parse(Colorant, "#D0FF58") === RGB(r8(0xD0),r8(0xFF),r8(0x58)) From 5d077b35405730e76578a9bfe1fe4614fad8adcb Mon Sep 17 00:00:00 2001 From: kimikage Date: Tue, 31 Dec 2019 02:14:13 +0900 Subject: [PATCH 31/33] Version 0.11.1 (#392) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 066a1c2f..9137f1aa 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Colors" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.11.0" +version = "0.11.1" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" From 9a443dc080b9a358b0eafa229bf4f6a2c570fc74 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 30 Dec 2019 11:14:53 -0600 Subject: [PATCH 32/33] Speed up grayscale conversion (#391) --- src/conversions.jl | 6 +++++- test/conversion.jl | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/conversions.jl b/src/conversions.jl index 24d29660..b67b620e 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -785,7 +785,11 @@ convert(::Type{G}, x::AbstractGray) where {G<:AbstractGray} = G(gray(x)) function convert(::Type{G}, x::AbstractRGB{T}) where {G<:AbstractGray,T<:Normed} TU, Tf = FixedPointNumbers.rawtype(T), floattype(T) - val = min(typemax(TU), Tf(0.299)*reinterpret(red(x)) + Tf(0.587)*reinterpret(green(x)) + Tf(0.114)*reinterpret(blue(x))) + if sizeof(TU) < sizeof(UInt) + val = Tf(0.001)*(299*reinterpret(red(x)) + 587*reinterpret(green(x)) + 114*reinterpret(blue(x))) + else + val = Tf(0.299)*reinterpret(red(x)) + Tf(0.587)*reinterpret(green(x)) + Tf(0.114)*reinterpret(blue(x)) + end return G(reinterpret(T, round(TU, val))) end convert(::Type{G}, x::AbstractRGB) where {G<:AbstractGray} = diff --git a/test/conversion.jl b/test/conversion.jl index 7d416eab..6c57d6ba 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -176,6 +176,11 @@ using ColorTypes: eltype_default, parametric3 # Issue #377 @test convert(Gray, RGB24(1,0,0)) === convert(Gray, RGB(1,0,0)) === Gray{N0f8}(0.298) @test convert(Gray24, RGB(1,0,0)) === Gray24(0.298) + # Check for roundoff error + for N in (N0f8, N0f16, N0f32) + @test convert(Gray{N}, RGB{N}(1,1,1)) === Gray{N}(1) + end + @test gray(convert(Gray{N0f64}, RGB{N0f64}(1,1,1))) ≈ 1.0 # Images issue #382 @test convert(Gray, RGBA(1,1,1,1)) == Gray(N0f8(1)) From 0890b410940fa9645496a394e5c449ef7104769a Mon Sep 17 00:00:00 2001 From: kimikage Date: Tue, 31 Dec 2019 05:06:17 +0900 Subject: [PATCH 33/33] Remove JLD2 dependency (#389) --- Project.toml | 3 +- test/conversion.jl | 12 +- test/test_conversions.jl | 233 +++++++++++++++++++++++++++++++++++++ test/test_conversions.jld2 | Bin 151535 -> 0 bytes 4 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 test/test_conversions.jl delete mode 100644 test/test_conversions.jld2 diff --git a/Project.toml b/Project.toml index 9137f1aa..bc096b04 100644 --- a/Project.toml +++ b/Project.toml @@ -16,8 +16,7 @@ Reexport = "0.2" julia = "1" [extras] -JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "JLD2"] +test = ["Test"] diff --git a/test/conversion.jl b/test/conversion.jl index 6c57d6ba..c9300264 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -1,4 +1,4 @@ -using Colors, FixedPointNumbers, JLD2 +using Colors, FixedPointNumbers using Test using ColorTypes: eltype_default, parametric3 @@ -203,9 +203,7 @@ using ColorTypes: eltype_default, parametric3 @test hsi.i > 0.96 && hsi.h ≈ 210 # Test accuracy of conversion - csconv = jldopen(joinpath(dirname(@__FILE__), "test_conversions.jld2")) do file - read(file, "csconv") - end + include("test_conversions.jl") # Since `colordiff`(e.g. `DE_2000`) involves a color space conversion, it is # not suitable for evaluating the conversion itself. On the other hand, @@ -259,9 +257,9 @@ using ColorTypes: eltype_default, parametric3 errmax <= tol end - base_t(i, from_to) = base_color_type(eltype(csconv[i][from_to])) - @testset "accuracy test: $(base_t(i,1))-->$(base_t(i,2)) (index $i)" for i = 1:length(csconv) - f, t = csconv[i] + @testset "accuracy test: $Cfrom-->$Cto" for Cto in keys(csconv), Cfrom in keys(csconv) + f = csconv[Cfrom] + t = csconv[Cto] @test convcompare(f, t, 2e-3, showfailure=false) end end diff --git a/test/test_conversions.jl b/test/test_conversions.jl new file mode 100644 index 00000000..4c3933fc --- /dev/null +++ b/test/test_conversions.jl @@ -0,0 +1,233 @@ +csconv = Dict{Type, AbstractVector{Color}}( + Lab{Float64} => Lab{Float64}[ +Lab{Float64}(61.40554408859231,-61.26004899789989,57.97749162785381), +Lab{Float64}(55.54652888796126,30.809120864024266,53.44054155482845), +Lab{Float64}(44.15905874913364,-27.770886035217714,34.72162815967184), +Lab{Float64}(62.25658951557699,-14.029208420051065,52.59616361156139), +Lab{Float64}(52.41763760855498,78.65495176055043,28.797415938527948), +Lab{Float64}(37.36754271570359,12.494669958826199,4.788494682968725), +Lab{Float64}(75.82115093728481,-24.72761078970176,64.60074310217375), +Lab{Float64}(71.8426650650214,-7.604216749949655,57.77684960484335), +Lab{Float64}(37.754274173620324,70.3180945761324,-83.16037630169984), +Lab{Float64}(42.15029927306029,68.36754602428618,-47.55058470357214), +Lab{Float64}(19.332795440287164,42.919076249245904,-62.726071239338566), +Lab{Float64}(50.02974879505459,76.5885392906267,45.398569298282474), +Lab{Float64}(40.561202169048386,-31.962174166736716,24.242059437675913), +Lab{Float64}(60.757760020025074,-52.783977694143616,58.95267093429221), +Lab{Float64}(77.9139415958467,16.03655624480843,-21.775158356104264), +Lab{Float64}(57.14306499534186,6.570139697934629,-24.826155752006727), +Lab{Float64}(44.97666070400284,70.24363046832543,-20.532082159114996), +Lab{Float64}(60.664366637100485,-35.19163329720709,35.983642777064205), +Lab{Float64}(17.93658797983788,19.195613517492216,1.4523602503233035), +Lab{Float64}(22.70092488166712,42.09789731817473,-67.66437344882776)], + RGB{Float64} => RGB{Float64}[ +RGB{Float64}(0.11228396215602665,0.6732949141086532,0.09859409271099773), +RGB{Float64}(0.7844278907439127,0.42956445925185305,0.14202724843192838), +RGB{Float64}(0.2915703179069309,0.45173923490473555,0.16825129849152776), +RGB{Float64}(0.6035334387508868,0.609857169290216,0.19624892225695534), +RGB{Float64}(0.9644226672159013,0.059403296858277166,0.3174797751494354), +RGB{Float64}(0.432484993970361,0.31581143833886605,0.31642899914629097), +RGB{Float64}(0.6947521946179401,0.7727221308629347,0.21756330942282065), +RGB{Float64}(0.7580992752894541,0.6964329890060948,0.2510418460157361), +RGB{Float64}(0.4326423261471005,0.12533218110917999,0.892922405285977), +RGB{Float64}(0.6554980398035374,0.13015145038942355,0.703223224556291), +RGB{Float64}(0.10975505072305158,0.09235233871920157,0.5557379427193866), +RGB{Float64}(0.9337598483853324,0.00782029356933489,0.18443366775765324), +RGB{Float64}(0.1874608064216865,0.4231093851641665,0.21203084253232074), +RGB{Float64}(0.2661788389076388,0.6555731749379143,0.0773468081126768), +RGB{Float64}(0.7978302602115971,0.7229225246920243,0.9138004107795679), +RGB{Float64}(0.48760377543392364,0.5312092935824387,0.7067152176969306), +RGB{Float64}(0.7689582640588691,0.10881793827304542,0.5577889667548762), +RGB{Float64}(0.39600674521787516,0.6317663735284889,0.3134289899365913), +RGB{Float64}(0.2729387941236906,0.1264998653293029,0.16620356290215066), +RGB{Float64}(0.03723463407032801,0.13430330431357462,0.6224972592798952)], + HSI{Float64} => HSI{Float64}[ +HSI{Float64}(118.8039444580707,0.6654701189568601,0.2947243229918925), +HSI{Float64}(26.537330142953778,0.6857849652100698,0.4520065328092315), +HSI{Float64}(94.29190735403657,0.4462751501965313,0.30385361710106473), +HSI{Float64}(60.76444178490796,0.5823423264482519,0.4698798434326861), +HSI{Float64}(343.93235717256283,0.8671370103299607,0.44710191307453795), +HSI{Float64}(359.7366651138475,0.110160904373796,0.3549084771518394), +HSI{Float64}(67.45289969116865,0.6126555782798495,0.5616792116345651), +HSI{Float64}(53.60149020968084,0.5584328271084302,0.5685247034370949), +HSI{Float64}(263.43653587702016,0.740852337559448,0.4836323041807525), +HSI{Float64}(295.6962678920551,0.7377516913969178,0.4962909049164173), +HSI{Float64}(241.89845939343923,0.6344148279341627,0.2526151107205466), +HSI{Float64}(349.6518042121588,0.9791646598774854,0.3753379365707736), +HSI{Float64}(125.4417442482248,0.31633635755402123,0.2742003447060579), +HSI{Float64}(101.32416888416884,0.7677502773116757,0.3330329406527433), +HSI{Float64}(262.9200122451308,0.10917223828930589,0.8115177318943965), +HSI{Float64}(229.16502448773292,0.1522530592134964,0.5751760955710976), +HSI{Float64}(318.2512056299271,0.7725956147105435,0.47852172302893026), +HSI{Float64}(105.5262070776537,0.2989222401885988,0.4470673695609852), +HSI{Float64}(344.80359875018354,0.32908191611333415,0.18854740745171472), +HSI{Float64}(231.09848635556082,0.8593212208480958,0.26467839922126596)], + YIQ{Float64} => YIQ{Float64}[ +YIQ{Float64}(0.4400367458354851,-0.14957309026873586,-0.29743867192165413), +YIQ{Float64}(0.5028893832345075,0.30377289091436527,-0.0144248883188723), +YIQ{Float64}(0.37153110397128625,-0.004341001642347811,-0.1220716976086112), +YIQ{Float64}(0.5608150336971648,0.1291098788046794,-0.13002519270561796), +YIQ{Float64}(0.3594248071203988,0.4562240955867101,0.2716684010514613), +YIQ{Float64}(0.3507672334047295,0.0693059044288959,0.024863468161431534), +YIQ{Float64}(0.6861210142815083,0.13190404991219223,-0.18921655074739152), +YIQ{Float64}(0.6640966183039183,0.17982328817008927,-0.12553706704197126), +YIQ{Float64}(0.3047232000316731,-0.06352876782830047,0.3038067584283862), +YIQ{Float64}(0.35256026287926645,0.12885061147524787,0.28939037486655916), +YIQ{Float64}(0.15038170846437382,-0.13850157531382595,0.14785538777008367), +YIQ{Float64}(0.30481014511678645,0.49485766540490517,0.25074607668125404), +YIQ{Float64}(0.32858750626013455,-0.07256790289262055,-0.1155032282283292), +YIQ{Float64}(0.47322646264678486,-0.04620569899723531,-0.26224622936178826), +YIQ{Float64}(0.7670800166263565,-0.016698265745364704,0.0752284812098746), +YIQ{Float64}(0.5381789190050847,-0.08236006454815478,0.04538538725375002), +YIQ{Float64}(0.3573825929299354,0.2490183747926694,0.27928123367606156), +YIQ{Float64}(0.524983782934139,-0.03817575987380177,-0.1488986898079142), +YIQ{Float64}(0.17481132656212944,0.07448068391233167,0.04331860007147402), +YIQ{Float64}(0.16093388277700443,-0.21466401451697095,0.1313684734434902)], + LCHab{Float64} => LCHab{Float64}[ +LCHab{Float64}(61.40554408859231,84.34561718716007,136.57692947148365), +LCHab{Float64}(55.54652888796126,61.68543920640755,60.03602910879027), +LCHab{Float64}(44.15905874913364,44.46137169768345,128.65335148070065), +LCHab{Float64}(62.25658951557699,54.43505410622245,104.93504261794598), +LCHab{Float64}(52.41763760855498,83.76092526465487,20.108859052334054), +LCHab{Float64}(37.36754271570359,13.380824291082131,20.96898020598853), +LCHab{Float64}(75.82115093728481,69.17160360089987,110.94564405157224), +LCHab{Float64}(71.8426650650214,58.27511014696499,97.49780692927679), +LCHab{Float64}(37.754274173620324,108.90492464281962,310.2169286804031), +LCHab{Float64}(42.15029927306029,83.27772484304839,325.1808465885591), +LCHab{Float64}(19.332795440287164,76.00399410038371,304.38111497800685), +LCHab{Float64}(50.02974879505459,89.03277174727756,30.65773681622481), +LCHab{Float64}(40.561202169048386,40.11555836885015,142.82110494046856), +LCHab{Float64}(60.757760020025074,79.13005567736441,131.8400549984751), +LCHab{Float64}(77.9139415958467,27.04308890689674,306.3701994798936), +LCHab{Float64}(57.14306499534186,25.680824462490943,284.8232668592222), +LCHab{Float64}(44.97666070400284,73.18288064267016,343.7064570495635), +LCHab{Float64}(60.664366637100485,50.33163619069481,134.3624622173942), +LCHab{Float64}(17.93658797983788,19.25047865923466,4.326814913733189), +LCHab{Float64}(22.70092488166712,79.69128178686806,301.8881183495275)], + LMS{Float64} => LMS{Float64}[ +LMS{Float64}(0.2307455933908811,0.3966129226546093,0.06205539564220773), +LMS{Float64}(0.3107019890330763,0.1897841714619035,0.0498207460521362), +LMS{Float64}(0.1218228377056502,0.17064451545425868,0.04614786486182357), +LMS{Float64}(0.30782939233464807,0.3408975716081407,0.0796305359309192), +LMS{Float64}(0.36293517359571514,0.06999350791647939,0.09885173109323558), +LMS{Float64}(0.10665192537186992,0.08953048173500038,0.09046458812156158), +LMS{Float64}(0.47953245310048775,0.5692794259137218,0.11805172223345846), +LMS{Float64}(0.4529818748418708,0.46457163956456615,0.11708679460289854), +LMS{Float64}(0.07608222895850802,0.02604919712027899,0.7296345053537655), +LMS{Float64}(0.1636469929148561,0.04282906750024994,0.43464808179574577), +LMS{Float64}(0.011814257293131639,0.00973850643965921,0.253539933890513), +LMS{Float64}(0.3347188452051175,0.06123620787020029,0.04649844014616222), +LMS{Float64}(0.09407290872609109,0.1462548602213991,0.054453038775816975), +LMS{Float64}(0.23553355846222834,0.3771163500455532,0.05733013093653634), +LMS{Float64}(0.5062615568672968,0.5071983572055632,0.838514346078231), +LMS{Float64}(0.2174546897679178,0.2500580133905722,0.4643617224024501), +LMS{Float64}(0.22442774528787113,0.05052631522063127,0.2684043405961488), +LMS{Float64}(0.2477609356957181,0.35305569878781684,0.12364967459867356), +LMS{Float64}(0.031897534250467936,0.01841899562603634,0.025306259760502797), +LMS{Float64}(0.013112766066299388,0.016272875996157916,0.32555480144136284)], + HSL{Float64} => HSL{Float64}[ +HSL{Float64}(118.57074822913228,0.7445381606943033,0.38594450340982545), +HSL{Float64}(26.855877022639817,0.6933963827794761,0.46322756958792055), +HSL{Float64}(93.89962599981922,0.4572455886709776,0.30999526669813166), +HSL{Float64}(60.917350742112355,0.5130940596657895,0.4030530457735857), +HSL{Float64}(342.89032345092164,0.9271086272022048,0.5119129820370892), +HSL{Float64}(359.6824160518213,0.1559188987062825,0.3741482161546135), +HSL{Float64}(68.42677085192335,0.5606048507387105,0.4951427201428777), +HSL{Float64}(52.703041187462325,0.5117352633925326,0.5045705606525951), +HSL{Float64}(264.0214220055365,0.7818628063239984,0.5091272931975785), +HSL{Float64}(295.0032243529561,0.6876520146285906,0.4166873374728573), +HSL{Float64}(242.25333439627224,0.7150016244212027,0.3240451407192941), +HSL{Float64}(348.5556218047031,0.9833890006365249,0.47079007097733366), +HSL{Float64}(126.25593489468521,0.3859483839694542,0.3052850957929265), +HSL{Float64}(100.40573294866358,0.7889351910129655,0.36645999152529557), +HSL{Float64}(263.54627989285797,0.5254333530120703,0.8183614677357961), +HSL{Float64}(228.05935891850672,0.27195805833187997,0.5971594965654271), +HSL{Float64}(319.1931281022064,0.7520599488025355,0.4388881011659573), +HSL{Float64}(104.43580436274233,0.3367953291950933,0.4725976817325401), +HSL{Float64}(343.732318489466,0.3666118071669045,0.19971932972649675), +HSL{Float64}(230.04870640337006,0.8871219219636491,0.3298659466751116)], + HSV{Float64} => HSV{Float64}[ +HSV{Float64}(118.57074822913228,0.8535647742987599,0.6732949141086532), +HSV{Float64}(26.855877022639817,0.8189416132345362,0.7844278907439127), +HSV{Float64}(93.89962599981922,0.627547741061258,0.45173923490473555), +HSV{Float64}(60.917350742112355,0.678205107459243,0.609857169290216), +HSV{Float64}(342.89032345092164,0.9384053290350767,0.9644226672159013), +HSV{Float64}(359.6824160518213,0.26977480665951337,0.432484993970361), +HSV{Float64}(68.42677085192335,0.7184456084105452,0.7727221308629347), +HSV{Float64}(52.703041187462325,0.6688535997875945,0.7580992752894541), +HSV{Float64}(264.0214220055365,0.8596382167507156,0.892922405285977), +HSV{Float64}(295.0032243529561,0.8149215699303098,0.703223224556291), +HSV{Float64}(242.25333439627224,0.8338203465696532,0.5557379427193866), +HSV{Float64}(348.5556218047031,0.9916249412706513,0.9337598483853324), +HSV{Float64}(126.25593489468521,0.5569448161757232,0.4231093851641665), +HSV{Float64}(100.40573294866358,0.8820165146018949,0.6555731749379143), +HSV{Float64}(263.54627989285797,0.20888356345200657,0.9138004107795679), +HSV{Float64}(228.05935891850672,0.31004206047388555,0.7067152176969306), +HSV{Float64}(319.1931281022064,0.8584865481532628,0.7689582640588691), +HSV{Float64}(104.43580436274233,0.503884658839859,0.6317663735284889), +HSV{Float64}(343.732318489466,0.5365266204262058,0.2729387941236906), +HSV{Float64}(230.04870640337006,0.940185063443651,0.6224972592798952)], + Luv{Float64} => Luv{Float64}[ +Luv{Float64}(61.40554408859231,-55.412996764588925,72.15094916974675), +Luv{Float64}(55.54652888796126,73.81632939424085,47.28648729212383), +Luv{Float64}(44.15905874913364,-20.1916300181127,41.666371386767686), +Luv{Float64}(62.25658951557699,3.2697611242111053,60.31219107239103), +Luv{Float64}(52.41763760855498,152.259323372186,15.212166995935663), +Luv{Float64}(37.36754271570359,18.436507756805966,3.8578775297180368), +Luv{Float64}(75.82115093728481,-7.409794283217813,77.29226601764863), +Luv{Float64}(71.8426650650214,15.684580802243243,66.90904075772004), +Luv{Float64}(37.754274173620324,7.754363614644651,-117.7874863449133), +Luv{Float64}(42.15029927306029,48.20313623793672,-76.81447399397281), +Luv{Float64}(19.332795440287164,-4.309127046305885,-66.66113003627298), +Luv{Float64}(50.02974879505459,157.6413223995605,26.989718351905513), +Luv{Float64}(40.561202169048386,-27.00395794142001,32.04972428697958), +Luv{Float64}(60.757760020025074,-45.680120676009444,71.02883623327125), +Luv{Float64}(77.9139415958467,7.2620670193642765,-36.88967721685302), +Luv{Float64}(57.14306499534186,-7.94128077035882,-38.54634624531412), +Luv{Float64}(44.97666070400284,85.27844273261951,-40.087521394485464), +Luv{Float64}(60.664366637100485,-29.74711054177352,49.71477206711368), +Luv{Float64}(17.93658797983788,20.000790452137903,-1.0590760936318002), +Luv{Float64}(22.70092488166712,-7.783710802693105,-77.2290167510072)], + XYZ{Float64} => XYZ{Float64}[ +XYZ{Float64}(0.153663016662531,0.29712708024737594,0.05852499318777285), +XYZ{Float64}(0.29674743038157375,0.23463442881668406,0.04651156755042157), +XYZ{Float64}(0.09437874875578477,0.13948552143012788,0.044709910030614676), +XYZ{Float64}(0.25690546129316694,0.3070356352157358,0.07594481890187685), +XYZ{Float64}(0.39636754695382753,0.20517774641604547,0.09647367409102692), +XYZ{Float64}(0.10846840739164045,0.0973772826964292,0.0903140653393484), +XYZ{Float64}(0.3884459435137322,0.49596865344466834,0.11200041764904389), +XYZ{Float64}(0.38836664204882565,0.4342527151240371,0.11187294869947646), +XYZ{Float64}(0.2094684185109957,0.09950961331508182,0.7399357020105221), +XYZ{Float64}(0.24686350958413994,0.1259742829096898,0.4394897712217018), +XYZ{Float64}(0.0565673219895524,0.028259210828662944,0.25725636227097265), +XYZ{Float64}(0.358313785231492,0.18443569110836341,0.04363959059527557), +XYZ{Float64}(0.0722806398419704,0.11592628720860365,0.05354850452537528), +XYZ{Float64}(0.1634846895954648,0.2897296286168442,0.05379230620150586), +XYZ{Float64}(0.5667179078583392,0.5306598592674642,0.8436009947819997), +XYZ{Float64}(0.2534837034751407,0.25069489921914667,0.46795995593110057), +XYZ{Float64}(0.28095997402550754,0.1452502685062495,0.27006920583932), +XYZ{Float64}(0.1957167557114015,0.28867334765309877,0.12114761724980394), +XYZ{Float64}(0.03445175720203697,0.025039782925762675,0.02528204539465763), +XYZ{Float64}(0.06932878517091455,0.03713556742882635,0.3303251691771589)], + YCbCr{Float64} => YCbCr{Float64}[ +YCbCr{Float64}(112.36804733797123,84.838038954416,75.63437414224273), +YCbCr{Float64}(126.13277492835714,82.38305926806305,172.9819070849848), +YCbCr{Float64}(97.36531176971168,102.30325567748676,115.22453057007604), +YCbCr{Float64}(138.81849237967913,81.91489437546983,134.82520279105893), +YCbCr{Float64}(94.71403275936733,122.6975484272026,224.66156450445874), +YCbCr{Float64}(92.81802411563575,123.65925642822798,141.056189978181), +YCbCr{Float64}(166.2605021276503,68.76924167895929,129.37902991427086), +YCbCr{Float64}(161.4371594085581,75.7853913624277,143.01897834216263), +YCbCr{Float64}(82.7343808069364,202.35470355580298,148.43784790109092), +YCbCr{Float64}(93.21069757055935,172.3275136666039,176.40088871970542), +YCbCr{Float64}(48.93359415369787,179.2414173424112,121.50899835317183), +YCbCr{Float64}(82.75342178057623,112.7829605557114,228.4883941419257), +YCbCr{Float64}(87.96066387096947,113.26601255596279,105.45194375633868), +YCbCr{Float64}(119.63659531964588,77.95658463450972,94.91964940996402), +YCbCr{Float64}(183.99052364117208,146.5470355623716,132.91301656099364), +YCbCr{Float64}(133.86118326211357,149.3048212702825,119.91951706554495), +YCbCr{Float64}(94.26678785165586,153.33343129623827,193.75815817524418), +YCbCr{Float64}(130.97144846257646,101.25721970896373,107.39311873395408), +YCbCr{Float64}(54.28368051710635,126.91186193651748,143.67799687737957), +YCbCr{Float64}(51.24452032816397,186.3466274854119,108.23634423699981)], +) diff --git a/test/test_conversions.jld2 b/test/test_conversions.jld2 deleted file mode 100644 index ec51452fcaea7d7d64f4c92f2a129b8f30a67901..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151535 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 kc^5aA<;y$