From 68524c5b9c0a0b0b640ce3e49df5c880d77e109b Mon Sep 17 00:00:00 2001 From: kimikage Date: Thu, 20 Feb 2020 00:05:27 +0900 Subject: [PATCH] Add cross-sectional charts and some examples to docs --- docs/Project.toml | 1 + docs/crosssectionalcharts.jl | 259 ++++++++++++++++++++++++++ docs/make.jl | 1 + docs/src/advancedfunctions.md | 2 +- docs/src/colordifferences.md | 4 +- docs/src/constructionandconversion.md | 136 +++++++++++--- docs/src/index.md | 56 ++++-- docs/src/namedcolors.md | 42 ----- 8 files changed, 417 insertions(+), 84 deletions(-) create mode 100644 docs/crosssectionalcharts.jl diff --git a/docs/Project.toml b/docs/Project.toml index fe5a86db..986a461d 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] +Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" FixedPointNumbers = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" diff --git a/docs/crosssectionalcharts.jl b/docs/crosssectionalcharts.jl new file mode 100644 index 00000000..f3521a43 --- /dev/null +++ b/docs/crosssectionalcharts.jl @@ -0,0 +1,259 @@ + +module CrossSectionalCharts + +using Colors +using Base64 + +struct CrossSectionalChartSVG <: Main.SVG + buf::IOBuffer +end + +struct Axis + index::Int # compornent index + label::String + range::AbstractRange +end +Base.first(a::Axis) = first(a.range) +Base.last(a::Axis) = last(a.range) + +function crosssection(::Type{C}; + x::Tuple=(1, "X", 0:1), + y::Tuple=(2, "Y", 0:1), + z::Tuple=(3, "Z", 0:1)) where C <: Color + crosssection(C, Axis(x...), Axis(y...), Axis(z...)) +end +function crosssection(::Type{C}, x::Axis, y::Axis, z::Axis) where C <: Color + io = IOBuffer() + id = String(nameof(C)) + + write(io, + """ + + + + + + + + + + + + """) + + xs = [xv for xv in range(first(x), stop=last(x), length=16)] + ys = [yv for yv in range(last(y), stop=first(y), length=16)] + zs = [zv for zv in range(first(z), stop=last(z), length=11)] + xmidf = (first(x) + last(x)) * 0.5 + ymidf = (first(y) + last(y)) * 0.5 + xmid = isinteger(xmidf) ? Int(xmidf) : xmidf + ymid = isinteger(ymidf) ? Int(ymidf) : ymidf + + vec = [0.0, 0.0, 0.0] + function col(xv, yv, zv) + vec[x.index] = xv + vec[y.index] = yv + vec[z.index] = zv + # TODO: Add more appropriate out-of-gamut color handling + xyz = convert(XYZ, C(vec...)) + rgb = convert(RGB, XYZ(max(xyz.x,0), max(xyz.y,0), max(xyz.z,0))) + end + + # add swatches of color bar and planes by layer + for i = 1:11 + zi = isodd(i) ? 6 - i÷2 : 6 + i÷2 # zigzag order + plane = [col(xs[xi], ys[yi], zs[zi]) for yi = 1:16, xi = 1:16] + ccolor = col(xmid, ymid, zs[zi]) # center color (for color bar) + barh = i == 1 ? 30 : 16.5 - 3*(i÷2) + op = i == 1 ? "style=\"opacity:1;\"" : "" + write(io, + """ + + + \n") + write(io, + """ + + + """) + end + # add labels + if first(x.range) * last(x.range) < 0 + write(io, + """ + + $(x.label) + $(last(x)) + $(y.label) + $(last(y)) + 0 + """) + else + write(io, + """ + $(x.label) + $(last(x)) + $xmid + $(y.label) + $(last(y)) + $ymid + 0 + """) + end + write(io, + """ + $(z.label) + $(last(z)) + $(first(z)) + $id + + """) + 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 pallet(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 + pallet(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)) +crosssection(::Type{HSL}) = crosssection(HSL, x=(2, "S", 0:1), + y=(3, "L", 0:1), + z=(1, "H", 0:360)) +crosssection(::Type{HSI}) = crosssection(HSI, x=(2, "S", 0:1), + y=(3, "I", 0:1), + z=(1, "H", 0:360)) + +crosssection(::Type{Lab}) = crosssection(Lab, x=(2, "a*", -100:100), + y=(3, "b*", -100:100), + z=(1, "L*", 0:100)) +crosssection(::Type{Luv}) = crosssection(Luv, x=(2, "u*", -100:100), + y=(3, "v*", -100:100), + z=(1, "L*", 0:100)) +crosssection(::Type{LCHab}) = crosssection(LCHab, x=(2, "C*", 0:100), + y=(1, "L*", 0:100), + z=(3, "H", 0:360)) +crosssection(::Type{LCHuv}) = crosssection(LCHuv, x=(2, "C*", 0:100), + y=(1, "L*", 0:100), + z=(3, "H", 0:360)) + +crosssection(::Type{YIQ}) = crosssection(YIQ, x=(2, "I", -1:1), + y=(3, "Q", -1:1), + z=(1, "Y", 0:1)) +crosssection(::Type{YCbCr}) = crosssection(YCbCr, x=(2, "Cb", 0:256), + y=(3, "Cr", 0:256), + z=(1, "Y", 0:256)) +end diff --git a/docs/make.jl b/docs/make.jl index 701b4c3c..556b14c8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,6 +6,7 @@ function Base.show(io::IO, mime::MIME"image/svg+xml", svg::SVG) flush(io) end +include("crosssectionalcharts.jl") include("colormaps.jl") include("namedcolorcharts.jl") diff --git a/docs/src/advancedfunctions.md b/docs/src/advancedfunctions.md index f4a0653f..c437bb6f 100644 --- a/docs/src/advancedfunctions.md +++ b/docs/src/advancedfunctions.md @@ -34,7 +34,7 @@ whitebalance ``` -## Simulation of color deficiency ("color blindness") +## [Simulation of color deficiency ("color blindness")](@id color_deficiency) Three functions are provided that map colors to a reduced gamut to simulate different types of *dichromacy*, the loss of one of the three types of human photopigments. diff --git a/docs/src/colordifferences.md b/docs/src/colordifferences.md index d4223983..cc23eed1 100644 --- a/docs/src/colordifferences.md +++ b/docs/src/colordifferences.md @@ -2,9 +2,7 @@ The `colordiff` function gives an approximate value for the difference between two colors. -```jldoctest example -julia> using Colors; #hide - +```jldoctest example; setup = :(using Colors) julia> colordiff(colorant"red", parse(Colorant, HSV(360, 0.75, 1))) 8.178248292426845 ``` diff --git a/docs/src/constructionandconversion.md b/docs/src/constructionandconversion.md index e29db134..10dfb8e6 100644 --- a/docs/src/constructionandconversion.md +++ b/docs/src/constructionandconversion.md @@ -8,58 +8,148 @@ The colorspaces used by Colors are defined in [ColorTypes](https://github.com/Ju - 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`) - +```@example cross +using Colors # hide +using Main: CrossSectionalCharts # hide +CrossSectionalCharts.crosssection(HSV) # hide +``` +```@example cross +CrossSectionalCharts.crosssection(HSL) # hide +``` +```@example cross +CrossSectionalCharts.crosssection(HSI) # hide +``` - `XYZ`, `xyY`, `LMS` and all 6 transparent variants - `Lab`, `Luv`, `LCHab`, `LCHuv` and all 8 transparent variants - +```@example cross +CrossSectionalCharts.crosssection(Lab) # hide +``` +```@example cross +CrossSectionalCharts.crosssection(Luv) # hide +``` +```@example cross +CrossSectionalCharts.crosssection(LCHab) # hide +``` +```@example cross +CrossSectionalCharts.crosssection(LCHuv) # hide +``` - `DIN99`, `DIN99d`, `DIN99o` and all 6 transparent variants - Storage formats `YIQ`, `YCbCr` and their transparent variants - +```@example cross +CrossSectionalCharts.crosssection(YIQ) # hide +``` +```@example cross +CrossSectionalCharts.crosssection(YCbCr) # hide +``` - `Gray`, `Gray24`, and the transparent variants `AGray`, `GrayA`, and `AGray32`. -## Converting colors -Colors.jl allows you to convert from one colorspace to another using the `convert` function. +## Color Parsing -For example: +You can parse any [CSS color specification](https://developer.mozilla.org/en-US/docs/CSS/color) +with the exception of `currentColor`. You can construct colors from strings +using the [`@colorant_str`](@ref) macro and the [`parse`](@ref) function. ```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. +julia> colorant"red" # named color +RGB{N0f8}(1.0,0.0,0.0) -## Color Parsing +julia> parse(Colorant, "DeepSkyBlue") # color names are case-insensitive +RGB{N0f8}(0.0,0.749,1.0) +julia> colorant"#FF0000" # 6-digit hex notation +RGB{N0f8}(1.0,0.0,0.0) -```jldoctest example -julia> c = colorant"red" +julia> colorant"#f00" # 3-digit hex notation RGB{N0f8}(1.0,0.0,0.0) -julia> parse(Colorant, "red") +julia> colorant"rgb(255,0,0)" # rgb() notation with integers in [0, 255] RGB{N0f8}(1.0,0.0,0.0) -julia> parse(Colorant, HSL(1, 1, 1)) -HSL{Float32}(1.0f0,1.0f0,1.0f0) +julia> colorant"rgba(255,0,0,0.6)" # with alpha in [0, 1] +RGBA{N0f8}(1.0,0.0,0.0,0.6) -julia> colorant"#FF0000" -RGB{N0f8}(1.0,0.0,0.0) +julia> colorant"rgba(100%,80%,0%,0.6)" # with "integer" percentages +RGBA{N0f8}(1.0,0.8,0.0,0.6) -julia> parse(Colorant, RGBA(1, 0.5, 1, 0.5)) -RGBA{Float64}(1.0,0.5,1.0,0.5) -``` +julia> parse(ARGB, "rgba(255,0,0,0.6)") # you can specify the return type +ARGB{N0f8}(1.0,0.0,0.0,0.6) + +julia> colorant"hsl(120, 100%, 25%)" # hsl() notation +HSL{Float32}(120.0f0,1.0f0,0.25f0) -You can parse any [CSS color specification](https://developer.mozilla.org/en-US/docs/CSS/color) with the exception of `currentColor`. +julia> colorant"hsla(120, 100%, 25%, 0.6)" # hsla() notation +HSLA{Float32}(120.0f0,1.0f0,0.25f0,0.6f0) + +julia> colorant"transparent" # transparent "black" +RGBA{N0f8}(0.0,0.0,0.0,0.0) +``` All CSS/SVG named colors are supported, in addition to X11 named colors, when their definitions do not clash with SVG. +You can find all names and their color swatches in [Named Colors](@ref) page. When writing functions the `colorant"red"` version is preferred, because the slow step runs when the code is parsed (i.e., during compilation rather than run-time). +The element types of the return types depend on the colorspaces, i.e. the `hsl()` +and `hsla()` notations return `HSL`/`HSLA` colors with `Float32` elements, and +other notations return `RGB`/`RGBA` colors with `N0f8` elements. The result +colors can be converted to `RGB{N0f16}` (for example) using: + +```jldoctest example +julia> using FixedPointNumbers + +julia> RGB{N0f16}(colorant"indianred") +RGB{N0f16}(0.80392,0.36078,0.36078) +``` +or +```jldoctest example +julia> parse(RGB{N0f16}, "indianred") +RGB{N0f16}(0.80392,0.36078,0.36078) +``` + +You can also pass a color (`Colorant` value) to the second argument of the +[`parse`](@ref) function, but in this case, `parse` does not apply any +conversion on the color. + +```jldoctest example +julia> parse(RGB{N0f8}, HSL{Float64}(120, 1, 0.5)) # use `convert` instead +HSL{Float64}(120.0,1.0,0.5) +``` + + +You can convert colors to hexadecimal strings using the [`hex`](@ref) function. +Note that the conversion result does not have the prefix `"#"`. + +```jldoctest example +julia> color = colorant"#C0FFEE" +RGB{N0f8}(0.753,1.0,0.933) + +julia> hex(color) +"C0FFEE" +``` + +## Color Conversions + +Colors.jl allows you to convert from one colorspace to another using the `convert` function. + +For example: + +```jldoctest example +julia> convert(RGB, HSL(270, 0.5, 0.5)) # without the element type +RGB{Float64}(0.5,0.25,0.75) + +julia> convert(RGB{N0f8}, HSL(270, 0.5, 0.5)) # with the element type +RGB{N0f8}(0.502,0.251,0.749) +``` + +Depending on the source and destination colorspace, this may not be perfectly lossless. + +--- + ```@docs @colorant_str parse diff --git a/docs/src/index.md b/docs/src/index.md index b51b7332..b1e7c295 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,22 +1,48 @@ # Introduction -This library provides a wide array of functions for dealing with color. +This package provides a wide array of functions for dealing with color. -Supported colorspaces include: +[Available colorspaces](@ref) include: -- 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 -- DIN99, DIN99d, DIN99o and all 6 transparent variants -- Storage formats YIQ, YCbCr and their transparent variants -- Gray, Gray24, and the transparent variants AGray, GrayA, and AGray32. +- `RGB`, `BGR`, `XRGB`, `RGBX`, `RGB24` +- `HSV`, `HSL`, `HSI` +- `XYZ`, `xyY`, `LMS` +- `Lab`, `Luv`, `LCHab`, `LCHuv` +- `DIN99`, `DIN99d`, `DIN99o` +- `YIQ`, `YCbCr` +- `Gray`, `Gray24` +- and their transparent variants: `ARGB`, `RGBA`, `ARGB32`, `AHSV`, `HSVA`, and so on -You can supply colors using names (eg `"red"`) or hex triplets (eg `#7aa457`) +## Package Features -Support is also provided for: +- [Color Parsing](@ref) +- [Color Conversions](@ref) +- [Color Differences](@ref) +- [Colormaps and Colorscales](@ref) +- [Simulation of color deficiency](@ref color_deficiency) -- color differences -- white balance -- color deficiency ("color blindness") -- colormaps and colorscales + +## Installation + +The package can be installed with the Julia package manager. From the Julia +REPL, type ] to enter the Pkg REPL mode and run: +```julia +pkg> add Colors +``` + +## Reexport + +Note that Colors is used within other packages (e.g. [Images](https://github.com/JuliaImages/Images.jl)) +and may have been [reexport](https://github.com/simonster/Reexport.jl)ed by them. +In addition, the color types used by Colors are defined in [ColorTypes](https://github.com/JuliaGraphics/ColorTypes.jl) +package. Colors reexports the types and functions exported by ColorTypes. +That means: +```julia +julia> using Images; # instead `using Colors` + +julia> RGB # We can use the types and functions (re-)exported by Colors +RGB + +julia> RGB === Images.RGB === Colors.RGB === ColorTypes.RGB +true +``` diff --git a/docs/src/namedcolors.md b/docs/src/namedcolors.md index e9d596e0..13d63c75 100644 --- a/docs/src/namedcolors.md +++ b/docs/src/namedcolors.md @@ -10,31 +10,6 @@ color_names = Dict( ... ``` -Named colors are available as `RGB{N0f8}` using: - -```jldoctest example -julia> using Colors - -julia> color = colorant"indianred" -RGB{N0f8}(0.804,0.361,0.361) -``` - -or - -```jldoctest example -julia> cname = "indianred" -"indianred" - -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) -``` ```@example chart using Main: NamedColorCharts # hide @@ -87,20 +62,3 @@ NamedColorCharts.ColorChartSVG("grays") # hide 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 -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) -```