Skip to content

Commit

Permalink
Move promotion rules and some typealiases to ColorTypes.jl v0.10 (#403)
Browse files Browse the repository at this point in the history
Since gray-->rgb conversions were supported and some rgb-->rgb conversions were fixed in ColorTypes.jl,
this commit delegates the conversions to ColorType.jl.
  • Loading branch information
kimikage authored Mar 13, 2020
1 parent 7cfe475 commit 7cb350b
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 112 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ os:
- linux
julia:
- 1.0
- 1.2
- 1

This comment has been minimized.

Copy link
@SimonDanisch

SimonDanisch Mar 13, 2020

Member

Testing two times on julia 1.0?

This comment has been minimized.

Copy link
@kimikage

kimikage Mar 13, 2020

Author Collaborator

The "1" means the latest ”1.x”, i.e. "1.3.1" for now.
https://travis-ci.org/github/JuliaGraphics/Colors.jl/jobs/661999933

This comment has been minimized.

Copy link
@SimonDanisch

SimonDanisch Mar 13, 2020

Member

0h crazy :D thats confusing^^

- nightly
notifications:
email: false
Expand Down
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
environment:
matrix:
- julia_version: 1.0
- julia_version: 1.2
- julia_version: 1
- julia_version: latest

platform:
Expand Down
4 changes: 0 additions & 4 deletions src/Colors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")
Expand Down
132 changes: 47 additions & 85 deletions src/conversions.jl
Original file line number Diff line number Diff line change
@@ -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)

Expand Down Expand Up @@ -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
# AbstractGray --> AbstractRGB conversions are implemented in ColorTypes.jl


# Everything to HSV
Expand Down Expand Up @@ -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)))
Expand All @@ -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))
6 changes: 0 additions & 6 deletions src/promotions.jl

This file was deleted.

12 changes: 0 additions & 12 deletions test/conversion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

2 comments on commit 7cb350b

@kimikage
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/10916

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if Julia TagBot is installed, or can be done manually through the github interface, or via:

git tag -a v0.12.0 -m "<description of version>" 7cb350b5bbf6f9a8d03a58c16f8fa524c335a39b
git push origin v0.12.0

Please sign in to comment.