diff --git a/src/ColorVectorSpace.jl b/src/ColorVectorSpace.jl index 0e5ea8d..06fb795 100644 --- a/src/ColorVectorSpace.jl +++ b/src/ColorVectorSpace.jl @@ -90,6 +90,7 @@ end multype(::Type{A}, ::Type{B}) where {A,B} = coltype(typeof(zero(A)*zero(B))) sumtype(::Type{A}, ::Type{B}) where {A,B} = coltype(typeof(zero(A)+zero(B))) divtype(::Type{A}, ::Type{B}) where {A,B} = coltype(typeof(zero(A)/oneunit(B))) +divtype(::Type{A}, ::Type{B}) where {A,B<:FixedPoint} = coltype(typeof(zero(A)/typemax(B))) powtype(::Type{A}, ::Type{B}) where {A,B} = coltype(typeof(zero(A)^zero(B))) sumtype(a::Colorant, b::Colorant) = coltype(sumtype(eltype(a),eltype(b))) @@ -131,6 +132,7 @@ rettype(::typeof(-), a, b) = parametric(color_rettype(a, b), sumtype(a, b)) rettype(::typeof(*), a, b) = parametric(color_rettype(a, b), multype(eltype(a), eltype(b))) # gray * gray rettype(::typeof(*), a::Real, b) = arith_colorant_type(b){multype(typeof(a), eltype(b))} rettype(::typeof(/), a, b::Real) = arith_colorant_type(a){divtype(eltype(a), typeof(b))} +rettype(::typeof(/), a, b) = arith_colorant_type(a){divtype(eltype(a), eltype(b))} rettype(::typeof(^), a, b) = arith_colorant_type(a){powtype(eltype(a), typeof(b))} rettype(::typeof(^), a, b::Integer) = arith_colorant_type(a){powtype(eltype(a), Int)} @@ -155,8 +157,21 @@ end _mul(x::T, y::T) where {T} = x * y _mul(x, y) = (T = multype(typeof(x), typeof(y)); _mul(T(x), T(y))) -_mapc(::Type{C}, f, c) where {C<:MathTypes} = C(f.(channels(c))...) -_mapc(::Type{C}, f, a, b) where {C<:MathTypes} = C(f.(channels(a), channels(b))...) +function _div(x::T, y::T) where {T <: FixedPoint} + F = floattype(T) + # range check should be done in the color constructor + F(reinterpret(x)) / F(reinterpret(y)) +end +function _div(x::AbstractFloat, y::Normed) + F = divtype(typeof(x), typeof(y)) + F(x) * F(reinterpret(oneunit(y))) / F(reinterpret(y)) +end +_div(x::AbstractFloat, y::Integer) = _mul(x, oneunit(x) / y) +_div(x::T, y::T) where {T} = x / y +_div(x, y) = (T = divtype(typeof(x), typeof(y)); _div(T(x), T(y))) + +@inline _mapc(::Type{C}, f, c) where {C<:MathTypes} = C(f.(channels(c))...) +@inline _mapc(::Type{C}, f, a, b) where {C<:MathTypes} = C(f.(channels(a), channels(b))...) ## Generic algorithms Base.add_sum(c1::MathTypes,c2::MathTypes) = mapc(Base.add_sum, c1, c2) @@ -202,12 +217,11 @@ complement(x::TransparentColor) = typeof(x)(complement(color(x)), alpha(x)) copy(c::MathTypes) = c (*)(f::Real, c::MathTypes) = _mapc(rettype(*, f, c), v -> _mul(f, v), c) (*)(c::MathTypes, f::Real) = (*)(f, c) +(/)(c::MathTypes, f::Real) = _mapc(rettype(/, c, f), v -> _div(v, f), c) (+)(c::MathTypes) = mapc(+, c) (+)(c::MathTypes{Bool}) = c (-)(c::MathTypes) = mapc(-, c) (-)(c::MathTypes{Bool}) = c -(/)(c::MathTypes, f::Real) = (one(f)/f)*c -(/)(c::MathTypes, f::Integer) = (one(eltype(c))/f)*c abs(c::MathTypes) = mapc(abs, c) norm(c::MathTypes, p::Real=2) = (cc = channels(c); norm(cc, p)/(p == 0 ? length(cc) : length(cc)^(1/p))) (⊙)(a::C, b::C) where {C<:MathTypes} = _mapc(rettype(*, a, b), _mul, a, b) @@ -223,13 +237,9 @@ norm(c::MathTypes, p::Real=2) = (cc = channels(c); norm(cc, p)/(p == 0 ? length( # Scalar RGB -function (/)(c::AbstractRGB{T}, f::Real) where T<:Normed - fs = (one(f)/reinterpret(oneunit(T)))/f - rettype(/, c, f)(fs*reinterpret(red(c)), fs*reinterpret(green(c)), fs*reinterpret(blue(c))) -end -function (/)(c::AbstractRGB{T}, f::Integer) where T<:Normed - fs = (1/reinterpret(oneunit(T)))/f - rettype(/, c, f)(fs*reinterpret(red(c)), fs*reinterpret(green(c)), fs*reinterpret(blue(c))) +function (/)(c::C, f::AbstractFloat) where {C<:Union{AbstractRGB, TransparentRGB}} + r = oneunit(divtype(eltype(c), typeof(f))) / f + _mapc(rettype(/, c, f), v -> v * r, c) end (+)(a::AbstractRGB, b::AbstractRGB) = rettype(+, a, b)(red(a)+red(b), green(a)+green(b), blue(a)+blue(b)) (-)(a::AbstractRGB, b::AbstractRGB) = rettype(-, a, b)(red(a)-red(b), green(a)-green(b), blue(a)-blue(b)) @@ -279,7 +289,7 @@ end middle(c::AbstractGray) = arith_colorant_type(c)(middle(gray(c))) middle(x::C, y::C) where {C<:AbstractGray} = arith_colorant_type(C)(middle(gray(x), gray(y))) -(/)(n::Number, c::AbstractGray) = base_color_type(c)(n/gray(c)) +(/)(n::Number, c::AbstractGray) = base_color_type(c)(_div(real(n), gray(c))) (+)(a::AbstractGray, b::AbstractGray) = rettype(+, a, b)(gray(a)+gray(b)) (+)(a::TransparentGray, b::TransparentGray) = rettype(+, a, b)(gray(a)+gray(b), alpha(a)+alpha(b)) (-)(a::AbstractGray, b::AbstractGray) = rettype(-, a, b)(gray(a)-gray(b)) @@ -287,8 +297,7 @@ middle(x::C, y::C) where {C<:AbstractGray} = arith_colorant_type(C)(middle(gray( (*)(a::AbstractGray, b::AbstractGray) = a ⊙ b (^)(a::AbstractGray, b::Integer) = rettype(^, a, b)(gray(a)^convert(Int,b)) (^)(a::AbstractGray, b::Real) = rettype(^, a, b)(gray(a)^b) -(/)(a::C, b::C) where C<:AbstractGray = base_color_type(C)(gray(a)/gray(b)) -(/)(a::AbstractGray, b::AbstractGray) = /(promote(a, b)...) +(/)(a::AbstractGray, b::AbstractGray) = rettype(/, a, b)(_div(gray(a), gray(b))) (+)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)+b) (+)(a::Number, b::AbstractGray) = b+a (-)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)-b) diff --git a/test/runtests.jl b/test/runtests.jl index 2c917e2..2ee7464 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -129,6 +129,10 @@ ColorTypes.comp2(c::RGBA32) = alpha(c) @test @inferred(cf/2.0f0) === Gray{Float32}(0.05) @test @inferred(cu/2) === Gray(cu.val/2) @test @inferred(cu/0.5f0) === Gray(cu.val/0.5f0) + @test @inferred(cu/0.1N0f8) === Gray{N0f8}(1) # issue #154 + @test_colortype_approx_eq @inferred(cf/0.6N0f8) Gray{Float32}(1/6) # issue #154 + @test @inferred(Gray{Q0f7}(0.25) / 0.5Q0f7) === Gray{Q0f7}(0.5) # issue #154 + @test @inferred(1+0im / Gray(1)) === @inferred(1 / Gray(1)) === Gray{Float32}(1) @test @inferred(cf+cf) === ccmp @test isfinite(cf) @test isfinite(Gray(true)) @@ -300,6 +304,12 @@ ColorTypes.comp2(c::RGBA32) = alpha(c) @test_colortype_approx_eq ([p1]/2)[1] GrayA{Float32}(Gray(0.4),0.1) @test_colortype_approx_eq (0.4f0*[p1]+0.6f0*[p2])[1] GrayA{Float32}(Gray(0.68),0.26) + cf = AGray{Float32}(0.8, 0.2) + cu = AGray{N0f8}(0.8, 0.2) + @test @inferred(cu / 0.8N0f8) === AGray{N0f8}(1, 0.25) # issue #154 + @test_colortype_approx_eq @inferred(cf / 0.8N0f8) AGray{Float32}(1, 0.25) # issue #154 + @test @inferred(AGray{Q0f7}(0.25, 0.125) / 0.5Q0f7) === AGray{Q0f7}(0.5, 0.25) # issue #154 + a = GrayA{N0f8}[GrayA(0.8,0.7), GrayA(0.5,0.2)] @test sum(a) == GrayA(n8sum(0.8,0.5), n8sum(0.7,0.2)) @test isapprox(a, a) @@ -328,7 +338,6 @@ ColorTypes.comp2(c::RGBA32) = alpha(c) @test @inferred(GrayA32(1, 0.4) - GrayA32(0.2, 0.2)) === GrayA32(0.8, 0.2) # Multiplication - cf = AGray{Float32}(0.8, 0.2) @test_throws MethodError cf * cf @test_throws MethodError cf ⋅ cf @test_throws MethodError cf ⊗ cf @@ -357,6 +366,10 @@ ColorTypes.comp2(c::RGBA32) = alpha(c) @test cf/2.0f0 == RGB{Float32}(0.05,0.1,0.15) @test cu/2 ≈ RGB(cu.r/2,cu.g/2,cu.b/2) @test cu/0.5f0 ≈ RGB(cu.r/0.5f0, cu.g/0.5f0, cu.b/0.5f0) + @test @inferred(cu/0.4N0f8) === RGB{N0f8}(26/102, 51/102, 76/102) + @test_colortype_approx_eq @inferred(cf / 0.4N0f8) RGB{Float32}(0.25, 0.5, 0.75) + cq0f7 = RGB{Q0f7}(0.125, 0.25, 0.375) + @test @inferred(cq0f7 / 0.5Q0f7) === RGB{Q0f7}(0.25, 0.5, 0.75) # issue #154 @test cf+cf == ccmp @test cu * 1//2 == mapc(x->Float64(Rational(x)/2), cu) @test_colortype_approx_eq (cf.*[0.8f0])[1] RGB{Float32}(0.8*0.1,0.8*0.2,0.8*0.3) @@ -472,6 +485,10 @@ ColorTypes.comp2(c::RGBA32) = alpha(c) @test cf/2.0f0 == RGBA{Float32}(0.05,0.1,0.15,0.2) @test cu/2 == RGBA(cu.r/2,cu.g/2,cu.b/2,cu.alpha/2) @test cu/0.5f0 == RGBA(cu.r/0.5f0, cu.g/0.5f0, cu.b/0.5f0, cu.alpha/0.5f0) + @test @inferred(cu/0.4N0f8) === RGBA{N0f8}(26/102, 51/102, 76/102, 102/102) + @test_colortype_approx_eq @inferred(cf / 0.4N0f8) RGBA{Float32}(0.25, 0.5, 0.75, 1) + cq0f7 = RGBA{Q0f7}(0.125, 0.25, 0.375, 0.4375) + @test @inferred(cq0f7 / 0.5Q0f7) === RGBA{Q0f7}(0.25, 0.5, 0.75, 0.875) # issue #154 @test cf+cf == ccmp @test_colortype_approx_eq (cf.*[0.8f0])[1] RGBA{Float32}(0.8*0.1,0.8*0.2,0.8*0.3,0.8*0.4) @test_colortype_approx_eq ([0.8f0].*cf)[1] RGBA{Float32}(0.8*0.1,0.8*0.2,0.8*0.3,0.8*0.4)