diff --git a/docs/colormapparams.jl b/docs/colormapparams.jl new file mode 100644 index 00000000..15023698 --- /dev/null +++ b/docs/colormapparams.jl @@ -0,0 +1,183 @@ +module ColormapParams + +using Colors +using Main.PNG16x16 + +struct ColormapParamSVG <: Main.SVG + buf::IOBuffer +end + +function ColormapParamSVG(target::Symbol) + io = IOBuffer() + write(io, + """ + + """) + write(io, + """ + + """) + if target in (:c, :s, :b) + write_chart_csb(io, target) + elseif target in (:w, :d) + write_chart_wd(io, target) + end + write(io, "") + ColormapParamSVG(io) +end + +function write_chart_csb(io, target::Symbol) + pp = (0.1, 0.5, 0.9) + pc = map(_ -> 0.88, pp) + ps = map(_ -> 0.60, pp) + pb = map(_ -> 0.75, pp) + markers = ("m -5,-5 v 10 h 10 v -10 z", + "m 0,-5 l -5,9 h 10 z", + "m 0,-4 l -4,4 4,4 4,-4 z") + if target === :c + col = Colors.JULIA_LOGO_COLORS.purple + label = "contrast" + pc = pp + elseif target === :s + col = Colors.JULIA_LOGO_COLORS.red + label = "saturation" + ps = pp + elseif target === :b + col = Colors.JULIA_LOGO_COLORS.green + label = "brightness" + pb = pp + end + h = round(Int, hue(convert(Luv, col))) + n = 16 + ls = (100/2/n):(100/n):100 + cs = (175/2/n):(175/n):175 + mc(l) = Colors.find_maximum_chroma(LCHuv(l, 0.0, h)) + plane = [LCHuv(100.0 - l, min(c, mc(l)), h) for l in ls, c in cs] + write(io, + """ + + \n") + mc_coords = join(string(round(Int, mc(100 - l)), ",", 2l, " ") for l = 0:2:100) + write(io, + """ + + + + C* + L* + """) + write(io, "") + for i = 1:3 + colors= sequential_palette(h, 7, c=pc[i], s=ps[i], b=pb[i], w=0, d=0) + write_colormap(io, 25 * i - 20, pp[i], colors, markers[i]) + end + tcolors = (sequential_palette(h, 25, c=pc[i], s=ps[i], b=pb[i], w=0, d=0) for i = 1:3) + write_stroke(io, tcolors, markers) + write(io, + """ + + $target + - $label [0,1] + + """) +end + +function write_chart_wd(io, target::Symbol) + pp = 0.0:0.2:1.0 + pw = map(_ -> 0.15, pp) + pd = map(_ -> 0.0, pp) + + if target === :w + col = Colors.JULIA_LOGO_COLORS.blue + cy = 40 + pw = pp + kcol = "#ff0" + elseif target === :d + col = Colors.JULIA_LOGO_COLORS.green + cy = 200 + pd = pp + kcol = "#00f" + end + h = round(Int, hue(convert(Luv, col))) + for i in eachindex(pp) + colors = sequential_palette(h, 9, w=pw[i], d=pd[i]) + write_colormap(io, 30 * i - 10, pp[i], colors) + end + write(io, + """ + + """) + write(io, + """ + + $target + - strength of $(target)color [0,1] + $(target)color + + """) +end + +function write_colormap(io, x, p, colors, marker="") + y = 0 + write(io, + """ + + """) + for col in colors + write(io, + """ + + """) + y += 20 + end + if !isempty(marker) + y += 10 + write(io, + """ + + """) + end + y += 20 + write(io, + """ + $p + """) + write(io, "") +end + +function write_stroke(io, tcolors, markers) + write(io, + """ + + """) + coord(c) = string(round(Int, c.c), ",", round(Int, 2 * (100 - c.l)), " ") + for (i, colors) in enumerate(tcolors) + coords = join(map(coord, LCHuv.(colors))) + op = (0.4, 0.6, 1.0)[i] + sw = (10, 4, 1)[i] + write(io, + """ + + """) + end + for (i, colors) in enumerate(tcolors) + mk = markers[i] + hx = hex(last(colors)) + write(io, + """ + + + """) + end + write(io, "") +end + +end # module diff --git a/docs/crosssectionalcharts.jl b/docs/crosssectionalcharts.jl index f5334a78..6059454a 100644 --- a/docs/crosssectionalcharts.jl +++ b/docs/crosssectionalcharts.jl @@ -2,7 +2,7 @@ module CrossSectionalCharts using Colors -using Base64 +using Main.PNG16x16 struct CrossSectionalChartSVG <: Main.SVG buf::IOBuffer @@ -119,10 +119,8 @@ function crosssection(::Type{C}, x::Axis, y::Axis, z::Axis) where C <: Color """ - \n") write(io, """ @@ -164,69 +162,6 @@ function crosssection(::Type{C}, x::Axis, y::Axis, z::Axis) where C <: Color CrossSectionalChartSVG(io) end - -function write_png(io::IO, cs::AbstractArray{T}) where T <: Color - buf = IOBuffer() # to calculate chunk CRCs - n = 16 # 16 x 16 - u8(x) = write(buf, UInt8(x & 0xFF)) - u16(x) = (u8((x & 0xFFFF)>>8); u8(x)) - u32(x) = (u16((x & 0xFFFFFFFF)>>16); u16(x)) - b(bstr) = write(buf, bstr) - function palette(c::Color) - rgb24 = convert(RGB24,c) - u8(rgb24.color>>16); u8(rgb24.color>>8); u8(rgb24.color) - end - crct(x) = (for i = 1:8; x = x & 1==1 ? 0xEDB88320 ⊻ (x>>1) : x>>1 end; x) - table = UInt32[crct(i) for i = 0x00:0xFF] - function crc32() - seekstart(buf) - crc = 0xFFFFFFFF - while !eof(buf) - crc = (crc>>8) ⊻ table[(crc&0xFF) ⊻ read(buf, UInt8) + 1] - end - u32(crc ⊻ 0xFFFFFFFF) - end - flush() = write(io, take!(seekstart(buf))) - - # The following is a pre-encoded 256-indexed-color PNG with size of 16x16. - # We only rewrite "pallets". - b(b"\x89PNG\x0D\x0A\x1A\x0A") - # Image header - u32(13); flush(); b(b"IHDR"); u32(n); u32(n); u8(8); u8(3); u8(0); u8(0); u8(0); crc32() - # Palette - u32(n * n * 3); flush(); - b(b"PLTE") - for y = 1:n, x = 1:n - palette(cs[y,x]) - end - crc32() - # Image data - u32(58); flush(); b(b"IDAT") - b(b"\x78\xDA\x63\x64\x60\x44\x03\x02\xE8\x02\x0A\xE8\x02\x06\xE8\x02") - b(b"\x0E\xE8\x02\x01\xE8\x02\x09\xE8\x02\x05\xE8\x02\x0D\xE8\x02\x13") - b(b"\xD0\x05\x16\xA0\x0B\x6C\x40\x17\x38\x80\x2E\x70\x01\x5D\xE0\x01") - b(b"\xBA\xC0\x07\x34\x3E\x00\x54\x4D\x08\x81"); crc32() - # Image trailer - u32(0); flush(); b(b"IEND"); crc32() - flush() -end - -""" -# Image data -using CodecZlib -raw = IOBuffer() -for y = 0:15 - write(raw, UInt8(1)) # filter: SUB - write(raw, UInt8(y*16)) # line head - write(raw, UInt8[1 for i=1:15]) # left + 1 -end -flush(raw) -cd = ZlibCompressorStream(raw,level=9) -flush(cd) -seekstart(cd) -@show read(cd) # UInt8[0x78, 0xda, 0x63, 0x64, ... -""" - crosssection(::Type{HSV}) = crosssection(HSV, x=(2, "S", 0:1), y=(3, "V", 0:1), z=(1, "H", 0:360)) diff --git a/docs/make.jl b/docs/make.jl index c5deb2e3..c307a475 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -7,10 +7,11 @@ function Base.show(io::IO, ::MIME"text/html", svg::SVG) write(io, "") flush(io) end - +include("png16x16.jl") include("crosssectionalcharts.jl") include("colordiffcharts.jl") include("colormaps.jl") +include("colormapparams.jl") include("namedcolorcharts.jl") include("sampleimages.jl") diff --git a/docs/png16x16.jl b/docs/png16x16.jl new file mode 100644 index 00000000..063327a5 --- /dev/null +++ b/docs/png16x16.jl @@ -0,0 +1,77 @@ +module PNG16x16 + +using Colors +using Base64 + +export write_png_as_data, write_png + +function write_png_as_data(io::IO, cs::AbstractMatrix{<:Color}) + write(io, "data:image/png;base64,") + b64enc = Base64EncodePipe(io) + write_png(b64enc, cs) + close(b64enc) +end + +function write_png(io::IO, cs::AbstractMatrix{<:Color}) + buf = IOBuffer() # to calculate chunk CRCs + n = 16 # 16 x 16 + u8(x) = write(buf, UInt8(x & 0xFF)) + u16(x) = (u8((x & 0xFFFF)>>8); u8(x)) + u32(x) = (u16((x & 0xFFFFFFFF)>>16); u16(x)) + b(bstr) = write(buf, bstr) + function palette(c::Color) + rgb24 = convert(RGB24,c) + u8(rgb24.color>>16); u8(rgb24.color>>8); u8(rgb24.color) + end + crct(x) = (for i = 1:8; x = x & 1==1 ? 0xEDB88320 ⊻ (x>>1) : x>>1 end; x) + table = UInt32[crct(i) for i = 0x00:0xFF] + function crc32() + seekstart(buf) + crc = 0xFFFFFFFF + while !eof(buf) + crc = (crc>>8) ⊻ table[(crc&0xFF) ⊻ read(buf, UInt8) + 1] + end + u32(crc ⊻ 0xFFFFFFFF) + end + flush() = write(io, take!(seekstart(buf))) + + # The following is a pre-encoded 256-indexed-color PNG with size of 16x16. + # We only rewrite "pallets". + b(b"\x89PNG\x0D\x0A\x1A\x0A") + # Image header + u32(13); flush(); b(b"IHDR"); u32(n); u32(n); u8(8); u8(3); u8(0); u8(0); u8(0); crc32() + # Palette + u32(n * n * 3); flush(); + b(b"PLTE") + for y = 1:n, x = 1:n + palette(cs[y,x]) + end + crc32() + # Image data + u32(58); flush(); b(b"IDAT") + b(b"\x78\xDA\x63\x64\x60\x44\x03\x02\xE8\x02\x0A\xE8\x02\x06\xE8\x02") + b(b"\x0E\xE8\x02\x01\xE8\x02\x09\xE8\x02\x05\xE8\x02\x0D\xE8\x02\x13") + b(b"\xD0\x05\x16\xA0\x0B\x6C\x40\x17\x38\x80\x2E\x70\x01\x5D\xE0\x01") + b(b"\xBA\xC0\x07\x34\x3E\x00\x54\x4D\x08\x81"); crc32() + # Image trailer + u32(0); flush(); b(b"IEND"); crc32() + flush() +end + +""" +# Image data +using CodecZlib +raw = IOBuffer() +for y = 0:15 + write(raw, UInt8(1)) # filter: SUB + write(raw, UInt8(y*16)) # line head + write(raw, UInt8[1 for i=1:15]) # left + 1 +end +flush(raw) +cd = ZlibCompressorStream(raw,level=9) +flush(cd) +seekstart(cd) +@show read(cd) # UInt8[0x78, 0xda, 0x63, 0x64, ... +""" + +end # module diff --git a/docs/src/colormapsandcolorscales.md b/docs/src/colormapsandcolorscales.md index 1e195eaa..92b78a40 100644 --- a/docs/src/colormapsandcolorscales.md +++ b/docs/src/colormapsandcolorscales.md @@ -94,34 +94,29 @@ weighted_color_mean This package provides some pre-defined colormaps (described below). There are also several other packages which provide colormaps: +- [ColorSchemes](https://github.com/JuliaGraphics/ColorSchemes.jl) - [PerceptualColourMaps](https://github.com/peterkovesi/PerceptualColourMaps.jl) - [ColorBrewer](https://github.com/timothyrenner/ColorBrewer.jl) -- [ColorSchemes.jl](https://github.com/JuliaGraphics/ColorSchemes.jl) - [NoveltyColors](https://github.com/randyzwitch/NoveltyColors.jl) ### Predefined sequential and diverging colormaps -The `colormap()` function returns a predefined sequential or diverging colormap computed using the algorithm by Wijffelaars, M., et al. (2008). +The [`colormap()`](@ref) function returns a predefined sequential or diverging +colormap computed using the algorithm by Wijffelaars, M., et al. (2008). -`colormap(cname::String [, N::Int=100; mid=0.5, logscale=false, kvs...])` - -The optional arguments are: - -- the number of colors `N` -- position of the middle point `mid` -- the use of logarithmic scaling with the `logscale` keyword - -Colormaps computed by this algorithm are guaranteed to have an increasing perceived depth or saturation making them ideal for data visualization. This also means that they are (in most cases) color-blind friendly and suitable for black-and-white printing. +```julia +colormap(cname::String [, N::Int=100; mid=0.5, logscale=false, ]) +``` -The currently supported colormap names are: +The `cname` specifies the name of colormap. The currently supported names are: #### Sequential - "Blues" ```@example colormap using Colors # hide -using Main: Colormaps # hide +using Main: Colormaps, ColormapParams # hide Colormaps.ColormapSVG(colormap("Blues", 32)) # hide ``` @@ -159,16 +154,29 @@ Colormaps.ColormapSVG(colormap("RdBu", 32)) # hide ### +The optional arguments of `colormap()` are: + +- the number of colors `N` +- position of the middle point `mid` for diverging colormaps +- the use of logarithmic scaling with the `logscale` keyword + +Colormaps computed by this algorithm are guaranteed to have an increasing +perceived depth or saturation making them ideal for data visualization. +This also means that they are (in most cases) color-blind friendly and suitable +for black-and-white printing. + ```@docs colormap ``` ### Sequential and diverging color palettes -You can create your own color palettes by using `sequential_palette()`: - -`sequential_palette(h, [N::Int=100; c=0.88, s=0.6, b=0.75, w=0.15, d=0.0, wcolor=RGB(1,1,0), dcolor=RGB(0,0,1), logscale=false])` +You can create your own color palettes by using [`sequential_palette()`](@ref): +```julia +sequential_palette(h, [N::Int=100; c=0.88, s=0.6, b=0.75, w=0.15, d=0.0, + wcolor=RGB(1,1,0), dcolor=RGB(0,0,1), logscale=false]) +``` which creates a sequential map for a hue `h` (defined in LCHuv space). Other possible parameters that you can fine tune are: @@ -181,30 +189,60 @@ Other possible parameters that you can fine tune are: * `d` - depth of the ending color [0,1] * `wcolor` - starting color (usually defined to be yellow) * `dcolor` - ending color (depth) -* `logscale` - true/false for toggling logspacing +* `logscale` - `true`/`false` for toggling logspacing + +```@example colormap +ColormapParams.ColormapParamSVG(:c) # hide +``` +```@example colormap +ColormapParams.ColormapParamSVG(:s) # hide +``` +```@example colormap +ColormapParams.ColormapParamSVG(:b) # hide +``` +```@example colormap +ColormapParams.ColormapParamSVG(:w) # hide +``` +```@example colormap +ColormapParams.ColormapParamSVG(:d) # hide +``` -Two sequential maps can also be combined into a diverging colormap by using: +Two sequential maps can also be combined into a diverging colormap by using +[`diverging_palette()`](@ref): -`diverging_palette(h1, h2 [, N::Int=100; mid=0.5,c=0.88, s=0.6, b=0.75, w=0.15, d1=0.0, d2=0.0, wcolor=RGB(1,1,0), dcolor1=RGB(1,0,0), dcolor2=RGB(0,0,1), logscale=false])` +```julia +diverging_palette(h1, h2 [, N::Int=100; mid=0.5, c=0.88, s=0.6, b=0.75, w=0.15, d1=0.0, d2=0.0, + wcolor=RGB(1,1,0), dcolor1=RGB(1,0,0), dcolor2=RGB(0,0,1), logscale=false]) +``` where the arguments are: -* `h1` - the main hue of the left side [0,360] -* `h2` - the main hue of the right side [0,360] +* `h1`, `h2` - the main hue of the first/latter part [0,360] and the optional arguments are: * `N` - number of colors -* `c` - the overall lightness contrast [0,1] -* `s` - saturation [0,1] -* `b` - brightness [0,1] +* `mid` - the position of the midpoint (0,1) +* `c`, `s`, `b` - contrast, saturation, brightness [0,1] * `w` - cold/warm parameter, i.e. the strength of the middle color [0,1] -* `d1` - depth of the end color in the left side [0,1] -* `d2` - depth of the end color in the right side [0,1] -* `wcolor` - starting color i.e. the middle color (warmness, usually defined to be yellow) -* `dcolor1` - end color of the left side (depth) -* `dcolor2` - end color of the right side (depth) -* `logscale` - true/false for toggling logspacing +* `d1`, `d2` - depth of the ending color in the first/latter part [0,1] +* `wcolor` - starting color i.e. the middle color +* `dcolor1`, `dcolor2` - ending color of the first/latter part (depth) +* `logscale` - `true`/`false` for toggling logspacing + +For examples: +```@example colormap +diverging_palette(0, 200, 32) +Colormaps.ColormapSVG(diverging_palette(0, 200, 32)) # hide +``` +```@example colormap +diverging_palette(0, 200, 32, mid=0.3) +Colormaps.ColormapSVG(diverging_palette(0, 200, 32, mid=0.3)) # hide +``` +```@example colormap +diverging_palette(0, 200, 32, mid=0.3, logscale=true) +Colormaps.ColormapSVG(diverging_palette(0, 200, 32, mid=0.3, logscale=true)) # hide +``` ```@docs sequential_palette @@ -213,17 +251,17 @@ diverging_palette ## Generating distinguishable colors -`distinguishable_colors()` generates `n` maximally distinguishable colors in LCHab space. A seed color or array of seed colors can be provided, and the remaining colors will be chosen to be maximally distinguishable from the seed colors and each other. +[`distinguishable_colors()`](@ref) generates `n` maximally distinguishable colors in LCHab space. A seed color or array of seed colors can be provided, and the remaining colors will be chosen to be maximally distinguishable from the seed colors and each other. ```julia distinguishable_colors(n::Integer, seed::Color) -distinguishable_colors{T<:Color}(n::Integer,seed::AbstractVector{T}) +distinguishable_colors(n::Integer, seed::AbstractVector{<:Color}) ``` -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. +By default, `distinguishable_colors` chooses maximally distinguishable colors from the outer product of lightness, chroma, and hue values specified by `lchoices`, `cchoices`, and `hchoices`. 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}; +distinguishable_colors(n::Integer, seed::AbstractVector{<:Color}; dropseed = false, transform::Function = identity, lchoices::AbstractVector = range(0, stop=100, length=15), diff --git a/docs/src/references.md b/docs/src/references.md index c59a9110..ae97426c 100644 --- a/docs/src/references.md +++ b/docs/src/references.md @@ -8,7 +8,7 @@ What perceptually uniform colorspaces are and why you should be using them: Functions in this library were mostly implemented according to: -* Schanda, J., ed. [Colorimetry: Understanding the CIE system](http://books.google.pt/books?id=uZadszSGe9MC). Wiley-Interscience, 2007. +* Schanda, J., ed. [Colorimetry: Understanding the CIE system](http://books.google.com/books?id=uZadszSGe9MC). Wiley-Interscience, 2007. * Sharma, G., Wu, W., and Dalal, E. N. (2005). [The CIEDE2000 color‐difference formula](http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf): Implementation notes, supplementary test data, and mathematical observations. Color Research & Application, 30(1), 21–30. doi:10.1002/col @@ -17,6 +17,6 @@ Functions in this library were mostly implemented according to: * Lindbloom, B. (2013). [Useful Color Equations](http://www.brucelindbloom.com/index.html?ColorCalculator.html) -* Wijffelaars, M., Vliegen, R., van Wijk, J., van der Linden, E-J. (2008). [Generating Color Palettes using Intuitive Parameters](http://magnaview.nl/documents/MagnaView-M_Wijffelaars-Generating_color_palettes_using_intuitive_parameters.pdf) +* Wijffelaars, M., Vliegen, R., van Wijk, J., van der Linden, E-J. (2008). [Generating Color Palettes using Intuitive Parameters](https://dl.acm.org/doi/10.1111/j.1467-8659.2008.01203.x) -* Georg A. Klein [Industrial Color Physics](http://http://books.google.de/books?id=WsKOAVCrLnwC). Springer Series in Optical Sciences, 2010. ISSN 0342-4111, ISBN 978-1-4419-1197-1. +* Georg A. Klein [Industrial Color Physics](http://books.google.com/books?id=WsKOAVCrLnwC). Springer Series in Optical Sciences, 2010. ISSN 0342-4111, ISBN 978-1-4419-1197-1.