Skip to content

Commit efa9959

Browse files
authored
Optimize multiplication operations (#167)
This also fixes a broken scaling with `Normed`.
1 parent 06d70b8 commit efa9959

2 files changed

Lines changed: 36 additions & 17 deletions

File tree

src/ColorVectorSpace.jl

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,24 @@ channels(c::TransparentGray) = (gray(c), alpha(c))
140140
channels(c::AbstractRGB) = (red(c), green(c), blue(c))
141141
channels(c::TransparentRGB) = (red(c), green(c), blue(c), alpha(c))
142142

143+
if reinterpret(N0f16, 0xBADb)^2 === N0f16(float(reinterpret(N0f16, 0xBADb))^2)
144+
function _mul(x::N0f8, y::N0f8)
145+
m = widemul(reinterpret(x), reinterpret(y))
146+
z = (((m + 0x80) >> 0x8) + m + 0x80) >> 0x8
147+
reinterpret(N0f8, z % UInt8)
148+
end
149+
function _mul(x::N0f16, y::N0f16)
150+
m = widemul(reinterpret(x), reinterpret(y))
151+
z = (((m + 0x8000) >> 0x10) + m + 0x8000) >> 0x10
152+
reinterpret(N0f16, z % UInt16)
153+
end
154+
end
155+
_mul(x::T, y::T) where {T} = x * y
156+
_mul(x, y) = (T = multype(typeof(x), typeof(y)); _mul(T(x), T(y)))
157+
158+
_mapc(::Type{C}, f, c) where {C<:MathTypes} = C(f.(channels(c))...)
159+
_mapc(::Type{C}, f, a, b) where {C<:MathTypes} = C(f.(channels(a), channels(b))...)
160+
143161
## Generic algorithms
144162
Base.add_sum(c1::MathTypes,c2::MathTypes) = mapc(Base.add_sum, c1, c2)
145163
Base.reduce_first(::typeof(Base.add_sum), c::MathTypes) = mapc(x->Base.reduce_first(Base.add_sum, x), c)
@@ -182,6 +200,7 @@ complement(x::TransparentColor) = typeof(x)(complement(color(x)), alpha(x))
182200

183201
# Common
184202
copy(c::MathTypes) = c
203+
(*)(f::Real, c::MathTypes) = _mapc(rettype(*, f, c), v -> _mul(f, v), c)
185204
(*)(c::MathTypes, f::Real) = (*)(f, c)
186205
(+)(c::MathTypes) = mapc(+, c)
187206
(+)(c::MathTypes{Bool}) = c
@@ -191,7 +210,7 @@ copy(c::MathTypes) = c
191210
(/)(c::MathTypes, f::Integer) = (one(eltype(c))/f)*c
192211
abs(c::MathTypes) = mapc(abs, c)
193212
norm(c::MathTypes, p::Real=2) = (cc = channels(c); norm(cc, p)/(p == 0 ? length(cc) : length(cc)^(1/p)))
194-
()(a::C, b::C) where {C<:MathTypes} = mapc(*, a, b)
213+
()(a::C, b::C) where {C<:MathTypes} = _mapc(rettype(*, a, b), _mul, a, b)
195214
()(a::C, b::C) where {C<:MathTypes} = throw(MethodError(dot, (a, b)))
196215
()(a::C, b::C) where {C<:MathTypes} = throw(MethodError(tensor, (a, b)))
197216

@@ -204,16 +223,6 @@ norm(c::MathTypes, p::Real=2) = (cc = channels(c); norm(cc, p)/(p == 0 ? length(
204223

205224

206225
# Scalar RGB
207-
(*)(f::Real, c::AbstractRGB) = rettype(*, f, c)(f*red(c), f*green(c), f*blue(c))
208-
(*)(f::Real, c::TransparentRGB) = rettype(*, f, c)(f*red(c), f*green(c), f*blue(c), f*alpha(c))
209-
function (*)(f::Real, c::AbstractRGB{T}) where T<:Normed
210-
fs = f*(1/reinterpret(oneunit(T)))
211-
rettype(*, f, c)(fs*reinterpret(red(c)), fs*reinterpret(green(c)), fs*reinterpret(blue(c)))
212-
end
213-
function (*)(f::Normed, c::AbstractRGB{T}) where T<:Normed
214-
fs = reinterpret(f)*(1/widen(reinterpret(oneunit(T)))^2)
215-
rettype(*, f, c)(fs*reinterpret(red(c)), fs*reinterpret(green(c)), fs*reinterpret(blue(c)))
216-
end
217226
function (/)(c::AbstractRGB{T}, f::Real) where T<:Normed
218227
fs = (one(f)/reinterpret(oneunit(T)))/f
219228
rettype(/, c, f)(fs*reinterpret(red(c)), fs*reinterpret(green(c)), fs*reinterpret(blue(c)))
@@ -270,14 +279,12 @@ end
270279
middle(c::AbstractGray) = arith_colorant_type(c)(middle(gray(c)))
271280
middle(x::C, y::C) where {C<:AbstractGray} = arith_colorant_type(C)(middle(gray(x), gray(y)))
272281

273-
(*)(f::Real, c::AbstractGray) = rettype(*, f, c)(f*gray(c))
274-
(*)(f::Real, c::TransparentGray) = rettype(*, f, c)(f*gray(c), f*alpha(c))
275282
(/)(n::Number, c::AbstractGray) = base_color_type(c)(n/gray(c))
276283
(+)(a::AbstractGray, b::AbstractGray) = rettype(+, a, b)(gray(a)+gray(b))
277284
(+)(a::TransparentGray, b::TransparentGray) = rettype(+, a, b)(gray(a)+gray(b), alpha(a)+alpha(b))
278285
(-)(a::AbstractGray, b::AbstractGray) = rettype(-, a, b)(gray(a)-gray(b))
279286
(-)(a::TransparentGray, b::TransparentGray) = rettype(-, a, b)(gray(a)-gray(b), alpha(a)-alpha(b))
280-
(*)(a::AbstractGray, b::AbstractGray) = rettype(*, a, b)(gray(a)*gray(b))
287+
(*)(a::AbstractGray, b::AbstractGray) = a b
281288
(^)(a::AbstractGray, b::Integer) = rettype(^, a, b)(gray(a)^convert(Int,b))
282289
(^)(a::AbstractGray, b::Real) = rettype(^, a, b)(gray(a)^b)
283290
(/)(a::C, b::C) where C<:AbstractGray = base_color_type(C)(gray(a)/gray(b))
@@ -287,7 +294,7 @@ middle(x::C, y::C) where {C<:AbstractGray} = arith_colorant_type(C)(middle(gray(
287294
(-)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)-b)
288295
(-)(a::Number, b::AbstractGray) = base_color_type(b)(a-gray(b))
289296

290-
()(x::C, y::C) where {C<:AbstractGray} = gray(x)*gray(y)
297+
()(x::C, y::C) where {C<:AbstractGray} = _mul(gray(x), gray(y))
291298
()(x::C, y::C) where {C<:AbstractGray} = x y
292299

293300
max(a::T, b::T) where {T<:AbstractGray} = T(max(gray(a),gray(b)))
@@ -302,8 +309,7 @@ min(a::AbstractGray, b::Number) = min(promote(a,b)...)
302309
atan(x::AbstractGray, y::AbstractGray) = atan(gray(x), gray(y))
303310
hypot(x::AbstractGray, y::AbstractGray) = hypot(gray(x), gray(y))
304311

305-
dotc(x::C, y::C) where {C<:AbstractGray} = acc(gray(x))*acc(gray(y))
306-
dotc(x::AbstractGray, y::AbstractGray) = dotc(promote(x, y)...)
312+
dotc(x::AbstractGray, y::AbstractGray) = _mul(acc(gray(x)), acc(gray(y)))
307313

308314
typemin(::Type{C}) where {C<:AbstractGray} = C(typemin(eltype(C)))
309315
typemax(::Type{C}) where {C<:AbstractGray} = C(typemax(eltype(C)))

test/runtests.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ using ColorVectorSpace, Colors, FixedPointNumbers
33

44
using Test
55

6+
using ColorVectorSpace: _mul
7+
68
n8sum(x,y) = Float64(N0f8(x)) + Float64(N0f8(y))
79

810
macro test_colortype_approx_eq(a, b)
@@ -86,6 +88,15 @@ ColorTypes.comp2(c::RGBA32) = alpha(c)
8688
@test floattype(Gray{N0f8}) === Gray{float(N0f8)}
8789
end
8890

91+
@testset "_mul" begin
92+
n0f8p = ((x, y) for x = N0f8(0):eps(N0f8):N0f8(1), y = N0f8(0):eps(N0f8):N0f8(1))
93+
@test all(((x, y),) -> _mul(x, y) === N0f8(Float32(x) * Float32(y)), n0f8p)
94+
n0f16a = N0f16(0):eps(N0f16):N0f16(1)
95+
n0f16b = reinterpret(N0f16, 0xBADb)
96+
@test all(a -> _mul(a, n0f16b) === N0f16(Float64(a) * 0xBADb / 0xFFFF), n0f16a)
97+
@test _mul(0.6N0f8, 0.2N0f16) === _mul(0.2N0f16, 0.6N0f8) === 0.12N0f16
98+
end
99+
89100
@testset "Arithmetic with Gray" begin
90101
cf = Gray{Float32}(0.1)
91102
@test @inferred(+cf) === cf
@@ -397,6 +408,8 @@ ColorTypes.comp2(c::RGBA32) = alpha(c)
397408
@test RGB24(1,0,0)/2.0 === RGB(0.5,0,0)
398409
# issue #133
399410
@test RGB24(1, 0, 0) + RGB24(0, 0, 1) === RGB24(1, 0, 1)
411+
# issue #166
412+
@test 0.6N0f8 * RGB{N0f16}(0.2) === RGB{N0f16}(0.12, 0.12, 0.12)
400413

401414
# Multiplication
402415
@test_throws MethodError cf*cf

0 commit comments

Comments
 (0)