From 37997e0504c33222d9c7957d9a6d311a65f21a69 Mon Sep 17 00:00:00 2001 From: kimikage Date: Fri, 10 Jan 2020 10:11:35 +0900 Subject: [PATCH] Move promotion rules and some typealiases to ColorTypes.jl v0.10 Since gray-->rgb conversions were supported and some rgb-->rgb conversions were fixed in ColorTypes.jl, this commit delegates the conversions to ColorType.jl. --- .travis.yml | 2 +- Project.toml | 6 +-- appveyor.yml | 2 +- src/Colors.jl | 4 -- src/conversions.jl | 132 ++++++++++++++++----------------------------- src/promotions.jl | 6 --- test/conversion.jl | 12 ----- 7 files changed, 52 insertions(+), 112 deletions(-) delete mode 100644 src/promotions.jl diff --git a/.travis.yml b/.travis.yml index 4268879e..737527ab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: - linux julia: - 1.0 - - 1.2 + - 1 - nightly notifications: email: false diff --git a/Project.toml b/Project.toml index 0d153d61..a080e274 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Colors" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.11.2" +version = "0.12.0" [deps] ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" @@ -9,8 +9,8 @@ InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" [compat] -ColorTypes = "0.9" -FixedPointNumbers = "0.6, 0.7" +ColorTypes = "0.10" +FixedPointNumbers = "0.6, 0.7, 0.8" Reexport = "0.2" julia = "1" diff --git a/appveyor.yml b/appveyor.yml index d92d1eb5..81bb802c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ environment: matrix: - julia_version: 1.0 - - julia_version: 1.2 + - julia_version: 1 - julia_version: latest platform: diff --git a/src/Colors.jl b/src/Colors.jl index 729a1ec1..4a82b3d1 100644 --- a/src/Colors.jl +++ b/src/Colors.jl @@ -7,9 +7,6 @@ using Reexport 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} -AbstractGrayA{C<:AbstractGray,T} = ColorAlpha{C,T,2} import Base: ==, +, -, *, / import Base: convert, eltype, isless, range, show, typemin, typemax @@ -29,7 +26,6 @@ include("utilities.jl") # Include other module components include("conversions.jl") -include("promotions.jl") include("algorithms.jl") include("parse.jl") include("differences.jl") diff --git a/src/conversions.jl b/src/conversions.jl index 62aaf7ea..e5b46d1d 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -1,50 +1,52 @@ # Conversions # ----------- -# convert(C, c) might be called as convert(RGB, c) or convert(RGB{Float32}, c) -# This is handled in ColorTypes, which calls functions -# _convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, c) -# _convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, c, alpha) -# Here are the argument types: -# - Cdest may be any concrete Color{T,N} type. For parametric Color types -# it _always_ has the desired element type (e.g., Float32), so it's -# safe to dispatch on Cdest{T}. -# - Odest and Osrc are Color subtypes, i.e., things like RGB -# or HSV. They have no element type. -# - c is the Colorant object you wish to convert. -# - alpha, if present, is a user-supplied alpha value (to be used in -# place of any default alpha or alpha present in c). -# -# The motivation for this arrangement is that Julia doesn't (yet) support -# "triangular dispatch", e.g., -# convert{T,C}(::Type{C{T}}, c) -# The various arguments of _convert therefore "peel off" element types -# (or guarantee them) so that comparisons may be made via -# dispatch. The alternative design is -# for C in parametric_colors -# @eval convert{T}(::Type{$C{T}}, c) = ... -# @eval convert( ::Type{$C}, c) = convert($C{eltype(c)}, c) -# ... -# end -# but this requires a fair amount of codegen (especially for all -# the various alpha variants) and can break if not all C support -# the same eltypes. -# -# Note that ColorTypes handles the cases where Odest == Osrc, or they -# are both subtypes of AbstractRGB. Therefore, here we only have to -# deal with conversions between different spaces. - - -function ColorTypes._convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, p, alpha) where {Cdest<:TransparentColor,Odest,Osrc} +#= +`convert(C, c)` might be called as `convert(RGB, c)` or +`convert(RGB{Float32}, c)`. +This is handled in ColorTypes, which calls functions +``` + _convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, c) + _convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, c, alpha) +``` +Here are the argument types: +- `Cdest` may be any concrete `Colorant{T,N}` type. For parametric Color types + it _always_ has the desired element type (e.g., `Float32`), so it's safe to + dispatch on `Cdest <: Colorant{T}`. +- `Odest` and `Osrc` are opaque `Color` subtypes, i.e., things like `RGB` or + `HSV`. They have no element type. +- `c` is the `Colorant` object you wish to convert. +- `alpha`, if present, is a user-supplied alpha value (to be used in place of + any default alpha or alpha present in `c`). + +The original motivation for this arrangement was that Julia "did not" support +"triangular dispatch", e.g., +``` + convert(::Type{C{T}}, c) where {C, T} +``` +On Julia v0.6 or later, parameter constraints can refer to previous parameters. +Threfore, we can use: +``` + convert(::Type{C}, c) where {T, C <: Colorant{T}} +``` +However, the example above does not match `convert(RGB, c)`. Also, we should +catch all the various alpha variants (e.g. `ARGB`/`RGBA` with/without element +type). +The various arguments of `_convert` "peel off" element types (or guarantee them) +so that comparisons may be made via dispatch. Therefore, this arrangement is +still helpful. + +Note that ColorTypes handles the cases where `Odest == Osrc`, or they are both +subtypes of `AbstractRGB` or `AbstractGray`. Therefore, here we only have to +deal with conversions between different spaces. +=# + +function ColorTypes._convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, p, alpha=alpha(p)) where {Cdest<:TransparentColor,Odest,Osrc} # Convert the base color c = cnvt(color_type(Cdest), color(p)) # Append the alpha ColorTypes._convert(Cdest, Odest, Odest, c, alpha) end -function ColorTypes._convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, p) where {Cdest<:TransparentColor,Odest,Osrc} - c = cnvt(color_type(Cdest), color(p)) - ColorTypes._convert(Cdest, Odest, Odest, c, alpha(p)) -end ColorTypes._convert(::Type{Cdest}, ::Type{Odest}, ::Type{Osrc}, c) where {Cdest<:Color,Odest,Osrc} = cnvt(Cdest, c) @@ -188,10 +190,7 @@ 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)) -function cnvt(::Type{CV}, c::AbstractGray) where CV<:AbstractRGB - g = convert(eltype(CV), gray(c)) - CV(g, g, g) -end +# AbstractRGB --> AbstractGray conversions are implemented in ColorTypes.jl # Everything to HSV @@ -735,51 +734,15 @@ end cnvt(::Type{YCbCr{T}}, c::Color3) where {T} = cnvt(YCbCr{T}, convert(RGB{T}, c)) -# Everything to RGB24 -# ------------------- - -convert(::Type{RGB24}, c::RGB24) = c -convert(::Type{RGB24}, c::AbstractRGB{N0f8}) = RGB24(red(c), green(c), blue(c)) -function convert(::Type{RGB24}, c::AbstractRGB) - u = (reinterpret(N0f8(red(c))) % UInt32)<<16 + - (reinterpret(N0f8(green(c))) % UInt32)<<8 + - reinterpret(N0f8(blue(c))) % UInt32 - reinterpret(RGB24, u) -end - -convert(::Type{RGB24}, c::Color) = convert(RGB24, convert(RGB{N0f8}, c)) - -# To ARGB32 -# ---------------- - -convert(::Type{ARGB32}, c::ARGB32) = c -convert(::Type{ARGB32}, c::TransparentColor{CV}) where {CV<:AbstractRGB{N0f8}} = - ARGB32(red(c), green(c), blue(c), alpha(c)) -function convert(::Type{ARGB32}, c::TransparentColor) - u = reinterpret(UInt32, convert(RGB24, c)) | (reinterpret(N0f8(alpha(c)))%UInt32)<<24 - reinterpret(ARGB32, u) -end -function convert(::Type{ARGB32}, c::Color) - u = reinterpret(UInt32, convert(RGB24, c)) | 0xff000000 - reinterpret(ARGB32, u) -end -function convert(::Type{ARGB32}, c::Color, alpha) - u = reinterpret(UInt32, convert(RGB24, c)) | (reinterpret(N0f8(alpha))%UInt32)<<24 - reinterpret(ARGB32, u) -end # To Gray # ------- +# AbstractGray --> AbstractRGB conversions are implemented in ColorTypes.jl, but +# AbstractRGB --> AbstractGray conversions should be implemented here. # Rec 601 luma conversion -const unsafe_trunc = Base.unsafe_trunc - -convert(::Type{Gray{T}}, x::Gray{T}) where {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} +function cnvt(::Type{G}, x::AbstractRGB{T}) where {G<:AbstractGray,T<:Normed} TU, Tf = FixedPointNumbers.rawtype(T), floattype(T) if sizeof(TU) < sizeof(UInt) val = Tf(0.001)*(299*reinterpret(red(x)) + 587*reinterpret(green(x)) + 114*reinterpret(blue(x))) @@ -788,8 +751,7 @@ function convert(::Type{G}, x::AbstractRGB{T}) where {G<:AbstractGray,T<:Normed} end return G(reinterpret(T, round(TU, val))) end -convert(::Type{G}, x::AbstractRGB) where {G<:AbstractGray} = +cnvt(::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)) +cnvt(::Type{G}, x::Color) where {G<:AbstractGray} = convert(G, convert(RGB, x)) diff --git a/src/promotions.jl b/src/promotions.jl deleted file mode 100644 index 066813bc..00000000 --- a/src/promotions.jl +++ /dev/null @@ -1,6 +0,0 @@ -Base.promote_rule(::Type{C3}, ::Type{Cgray}) where {C3<:Color3,Cgray<:AbstractGray} = base_colorant_type(C3){promote_type(eltype(C3), eltype(Cgray))} -Base.promote_rule(::Type{C3}, ::Type{Cagray}) where {C3<:Color3,Cagray<:AbstractAGray} = alphacolor(base_colorant_type(C3)){promote_type(eltype(C3), eltype(Cagray))} -Base.promote_rule(::Type{C3}, ::Type{Cgraya}) where {C3<:Color3,Cgraya<:AbstractGrayA} = coloralpha(base_colorant_type(C3)){promote_type(eltype(C3), eltype(Cgraya))} - -Base.promote_rule(::Type{C3}, ::Type{Cgray}) where {C3<:Transparent3,Cgray<:AbstractGray} = base_colorant_type(C3){promote_type(eltype(C3), eltype(Cgray))} -Base.promote_rule(::Type{C3}, ::Type{Cgray}) where {C3<:Transparent3,Cgray<:TransparentGray} = base_colorant_type(C3){promote_type(eltype(C3), eltype(Cgray))} diff --git a/test/conversion.jl b/test/conversion.jl index 6383a9b5..06e1114d 100644 --- a/test/conversion.jl +++ b/test/conversion.jl @@ -5,18 +5,6 @@ using ColorTypes: eltype_default, parametric3 @testset "Conversion" begin r8(x) = reinterpret(N0f8, x) - # Promotions - a, b = promote(RGB(1,0,0), Gray(0.8)) - @test isa(a, RGB{Float64}) && isa(b, RGB{Float64}) - a, b = promote(RGBA(1,0,0), Gray(0.8)) - @test isa(a, RGBA{Float64}) && isa(b, RGBA{Float64}) - a, b = promote(RGBA(1,0,0), GrayA(0.8)) - @test isa(a, RGBA{Float64}) && isa(b, RGBA{Float64}) - a, b = promote(RGB(1,0,0), GrayA(0.8)) - @test isa(a, RGBA{Float64}) && isa(b, RGBA{Float64}) - a, b = promote(RGB(1,0,0), AGray(0.8)) - @test isa(a, ARGB{Float64}) && isa(b, ARGB{Float64}) - # 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()