Skip to content

Commit

Permalink
add PaddedViews.filltype specifications on Colorants
Browse files Browse the repository at this point in the history
This ensure that `fillvalue` is of dominant role of eltype of PaddedView.

One consequence of this is transparent color is correctly filled even if
`img` isn't of eltype `ARGB`:

`PaddedView(ARGB(0, 0, 0, 0), img, indices)`

This is a rework of JuliaArrays/PaddedViews.jl#25
and co-operates JuliaArrays/PaddedViews.jl#26
  • Loading branch information
johnnychen94 committed Mar 20, 2020
1 parent f0384ab commit a61af8e
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ FixedPointNumbers = "0.6.1, 0.7, 0.8"
Graphics = "0.4, 1.0"
MappedArrays = "0.2"
OffsetArrays = "0.8, 0.9, 0.10, 0.11, 1.0.1"
PaddedViews = "0.4.1, 0.5"
PaddedViews = "0.5.4"
Reexport = "0.2"
Requires = "0.5, 1"
julia = "1"
Expand Down
20 changes: 20 additions & 0 deletions src/ImageCore.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,26 @@ much faster to create, but generally slower to use.
"""
permuteddimsview(A, perm) = Base.PermutedDimsArrays.PermutedDimsArray(A, perm)

# PaddedViews support
# This make sure Colorants as `fillvalue` are correctly filled, for example, let
# `PaddedView(ARGB(0, 0, 0, 0), img)` correctly filled with transparent color even when
# `img` is of eltype `RGB`
function PaddedViews.filltype(::Type{FC}, ::Type{C}) where {FC<:Colorant, C<:Colorant}
# rand(RGB, 4, 4) has eltype RGB{Any} but it isn't a concrete type
# although the consensus[1] is to not make a concrete eltype, this op is needed to make a
# type-stable colorant construction in _filltype without error; there's no RGB{Any} thing
# [1]: https://github.com/JuliaLang/julia/pull/34948
T = eltype(C) === Any ? eltype(FC) : eltype(C)
_filltype(FC, base_colorant_type(C){T})
end
_filltype(::Type{<:Colorant}, ::Type{C}) where {C<:Colorant} = C
_filltype(::Type{FC}, ::Type{C}) where {FC<:Color3, C<:AbstractGray} =
base_colorant_type(FC){promote_type(eltype(FC), eltype(C))}
_filltype(::Type{FC}, ::Type{C}) where {FC<:TransparentColor, C<:AbstractGray} =
alphacolor(FC){promote_type(eltype(FC), eltype(C))}
_filltype(::Type{FC}, ::Type{C}) where {FC<:TransparentColor, C<:Color3} =
alphacolor(C){promote_type(eltype(FC), eltype(C))}

# Support transpose
Base.transpose(a::AbstractMatrix{C}) where {C<:Colorant} = permutedims(a, (2,1))
function Base.transpose(a::AbstractVector{C}) where C<:Colorant
Expand Down
85 changes: 85 additions & 0 deletions test/views.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# some views are in colorchannels.jl
using Colors, FixedPointNumbers, ImageCore, OffsetArrays, Test
using OffsetArrays: IdentityUnitRange
using PaddedViews: filltype

@testset "rawview" begin
a = map(N0f8, rand(3,5))
Expand Down Expand Up @@ -190,4 +191,88 @@ end
@test axes(A) == (Base.OneTo(2),Base.OneTo(2))
end

@testset "PaddedViews" begin
# don't promote to Colorant if it's a numerical array
@test @inferred(filltype(Gray{N0f8}, Float32)) === Float32

# cases that don't promote array eltype:
# * (Number, Colorant)
# * (Gray, Gray)
# * (Gray, Color3)
# * (Color3, Color3)
# * (Color3, TransparentColor)
# * (TransparentColor, TransparentColor)
@test @inferred(filltype(Float32, Gray{N0f8})) == Gray{N0f8}
@test @inferred(filltype(Float32, RGB{N0f8})) == RGB{N0f8}
@test @inferred(filltype(Gray{Float32}, Gray{N0f8})) === Gray{N0f8}
@test @inferred(filltype(Gray{N0f8}, Gray{Float32})) === Gray{Float32}
@test @inferred(filltype(Gray{Float32}, RGB{N0f8})) === RGB{N0f8}
@test @inferred(filltype(Gray{N0f8}, RGB{Float32})) === RGB{Float32}
@test @inferred(filltype(RGB{Float32}, RGB{N0f8})) === RGB{N0f8}
@test @inferred(filltype(RGB{N0f8}, RGB{Float32})) === RGB{Float32}
@test @inferred(filltype(Lab{Float32}, RGB{N0f8})) === RGB{N0f8}
@test @inferred(filltype(RGB{N0f8}, Lab{Float32})) === Lab{Float32}

@test @inferred(filltype(Gray{N0f8}, AGray{Float32})) === AGray{Float32}
@test @inferred(filltype(Gray{Float32}, AGray{N0f8})) === AGray{N0f8}
@test @inferred(filltype(Gray{N0f8}, ARGB{Float32})) === ARGB{Float32}
@test @inferred(filltype(Gray{Float32}, ARGB{N0f8})) === ARGB{N0f8}
@test @inferred(filltype(BGR{N0f8}, ARGB{Float32})) === ARGB{Float32}
@test @inferred(filltype(BGR{Float32}, ARGB{N0f8})) === ARGB{N0f8}
@test @inferred(filltype(AGray{N0f8}, ARGB{Float32})) === ARGB{Float32}
@test @inferred(filltype(AGray{Float32}, ARGB{N0f8})) === ARGB{N0f8}

# cases that promote both colorant type and storage type
# * (Color3, Gray)
# * (TransparentColor, Colorant)
@test @inferred(filltype(RGB{N0f8}, Gray{Float32})) === RGB{Float32}
@test @inferred(filltype(RGB{Float32}, Gray{N0f8})) === RGB{Float32}
@test @inferred(filltype(Lab{Float32}, Gray{N0f8})) === Lab{Float32}
@test @inferred(filltype(Lab{Float32}, Gray{Float64})) === Lab{Float64}

@test @inferred(filltype(AGray{N0f8}, Gray{Float32})) === AGray{Float32}
@test @inferred(filltype(AGray{Float32}, Gray{N0f8})) === AGray{Float32}
@test @inferred(filltype(AGray{N0f8}, RGB{Float32})) === ARGB{Float32}
@test @inferred(filltype(AGray{Float32}, RGB{N0f8})) === ARGB{Float32}
@test @inferred(filltype(AGray{N0f8}, Lab{Float32})) === ALab{Float32}
@test @inferred(filltype(AGray{Float64}, Lab{Float32})) === ALab{Float64}

@test @inferred(filltype(ARGB{N0f8}, Gray{Float32})) === ARGB{Float32}
@test @inferred(filltype(ARGB{Float32}, Gray{N0f8})) === ARGB{Float32}
@test @inferred(filltype(ARGB{N0f8}, BGR{Float32})) === ABGR{Float32}
@test @inferred(filltype(ARGB{Float32}, BGR{N0f8})) === ABGR{Float32}
@test @inferred(filltype(ARGB{N0f8}, Lab{Float32})) === ALab{Float32}
@test @inferred(filltype(ARGB{Float64}, Lab{Float32})) === ALab{Float64}


A = Gray{N0f8}[Gray(0.) Gray(0.3); Gray(0.6) Gray(1.)]
fv = Gray{N0f8}(0)
Ap = PaddedView(fv, A, (-1:4, -1:4))
@test eltype(Ap) == Gray{N0f8}
@test @inferred(getindex(Ap, -1, -1)) === fv
@test @inferred(getindex(Ap, 2, 2)) === Gray{N0f8}(1.)
@test Ap[axes(A)...] == A

fv = RGB{Float32}(1., 0., 0.)
Ap = PaddedView(fv, A, (-1:4, -1:4))
@test eltype(Ap) == RGB{Float32}
@test @inferred(getindex(Ap, -1, -1)) === fv
@test @inferred(getindex(Ap, 2, 2)) === RGB{Float32}(1., 1., 1.)
@test Ap[axes(A)...] == RGB{Float32}.(A)

fv = AGray{Float32}(0.5, 0)
Ap = PaddedView(fv, A, (-1:4, -1:4))
@test eltype(Ap) == AGray{Float32}
@test @inferred(getindex(Ap, -1, -1)) === fv
@test @inferred(getindex(Ap, 2, 2)) === AGray{Float32}(1., 1.)
@test Ap[axes(A)...] == AGray{Float32}.(A)

fv = ARGB{Float32}(0.5, 0., 0., 0.)
Ap = PaddedView(fv, A, (-1:4, -1:4))
@test eltype(Ap) == ARGB{Float32}
@test @inferred(getindex(Ap, -1, -1)) === fv
@test @inferred(getindex(Ap, 2, 2)) === ARGB{Float32}(1., 1., 1., 1.)
@test Ap[axes(A)...] == ARGB{Float32}.(A)
end

nothing

0 comments on commit a61af8e

Please sign in to comment.