Skip to content

Commit b80bccd

Browse files
authored
Save Bool image with 1-bit depth (#172)
Fixes JuliaIO/FileIO.jl#232
1 parent 84c9ead commit b80bccd

File tree

3 files changed

+27
-13
lines changed

3 files changed

+27
-13
lines changed

src/ImageMagick.jl

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ function _metadata(wand)
8181
# use an even # of fractional bits for depth>8 (see issue 242#issuecomment-68845157)
8282
evendepth = ((depth+1)>>1)<<1
8383
if depth <= 8
84-
T = Normed{UInt8,8} # otherwise use 8 fractional bits
84+
if cs == "Gray"
85+
T = getimagechanneldepth(wand, GrayChannel) == 1 ? Bool : N0f8
86+
else
87+
T = Normed{UInt8,8} # otherwise use 8 fractional bits
88+
end
8589
elseif depth <= 16
8690
T = Normed{UInt16,evendepth}
8791
else
@@ -249,15 +253,15 @@ mapIM(c::RGBA{T}) where {T<:Normed} = c
249253
mapIM(x::UInt8) = reinterpret(N0f8, x)
250254
mapIM(x::UInt16) = reinterpret(N0f16, x)
251255
mapIM(x::UInt32) = reinterpret(N0f32, x)
252-
mapIM(x::Bool) = convert(N0f8, x)
256+
mapIM(x::Bool) = x
253257
mapIM(x::AbstractFloat) = convert(N0f8, x)
254258
mapIM(x::Normed) = x
255259

256260
# Make the data contiguous in memory, this is necessary for
257261
# imagemagick since it doesn't handle stride.
258262
to_contiguous(A::Array) = A
259263
to_contiguous(A::AbstractArray) = collect(A)
260-
to_contiguous(A::BitArray) = convert(Array{N0f8}, A)
264+
to_contiguous(A::BitArray) = convert(Array{Bool}, A)
261265
to_contiguous(A::ColorView) = to_contiguous(channelview(A))
262266

263267
to_explicit(A::Array{C}) where {C<:Colorant} = to_explicit(channelview(A))
@@ -266,6 +270,7 @@ function to_explicit(A::AbstractArray)
266270
As .= A
267271
to_explicit(As)
268272
end
273+
to_explicit(A::Array{Bool}) = A
269274
to_explicit(A::Array{T}) where {T<:Normed} = rawview(A)
270275
to_explicit(A::Array{T}) where {T<:AbstractFloat} = to_explicit(convert(Array{N0f8}, A))
271276

src/libmagickwand.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const FLOATPIXEL = 3
3535
const INTEGERPIXEL = 4
3636
const SHORTPIXEL = 7
3737
const IMStorageTypes = Union{UInt8,UInt16,UInt32,Float32,Float64}
38+
storagetype(::Type{Bool}) = CHARPIXEL
3839
storagetype(::Type{UInt8}) = CHARPIXEL
3940
storagetype(::Type{UInt16}) = SHORTPIXEL
4041
storagetype(::Type{UInt32}) = INTEGERPIXEL
@@ -208,7 +209,7 @@ bitdepth(buffer::AbstractArray{C}) where {C<:Colorant} = 8*sizeof(eltype(C))
208209
bitdepth(buffer::AbstractArray{T}) where {T} = 8*sizeof(T)
209210

210211
# colorspace is included for consistency with constituteimage, but it is not used
211-
function exportimagepixels!(@nospecialize(buffer::AbstractArray{<:Unsigned}), wand::MagickWand, colorspace::String, channelorder::String; x = 0, y = 0)
212+
function exportimagepixels!(@nospecialize(buffer::AbstractArray{<:Union{Unsigned,Bool}}), wand::MagickWand, colorspace::String, channelorder::String; x = 0, y = 0)
212213
T = eltype(buffer)
213214
cols, rows, nimages = getsize(buffer, channelorder)
214215
ncolors = colorsize(buffer, channelorder)
@@ -235,11 +236,11 @@ end
235236
# nothing
236237
# end
237238

238-
function constituteimage(buffer::AbstractArray{T}, wand::MagickWand, colorspace::String, channelorder::String; x = 0, y = 0) where T<:Unsigned
239+
function constituteimage(buffer::AbstractArray{T}, wand::MagickWand, colorspace::String, channelorder::String; x = 0, y = 0) where T<:Union{Unsigned,Bool}
239240
cols, rows, nimages = getsize(buffer, channelorder)
240241
ncolors = colorsize(buffer, channelorder)
241242
p = pointer(buffer)
242-
depth = bitdepth(buffer)
243+
depth = T == Bool ? 1 : bitdepth(buffer)
243244
for i = 1:nimages
244245
status = ccall((:MagickConstituteImage, libwand), Cint, (Ptr{Cvoid}, Cssize_t, Cssize_t, Ptr{UInt8}, Cint, Ptr{Cvoid}), wand, cols, rows, channelorder, storagetype(T), p)
245246
status == 0 && error(wand)

test/constructed_images.jl

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ using ImageMagick, ColorTypes, FixedPointNumbers, IndirectArrays, FileIO, Offset
22
using ImageShow # for show(io, ::MIME, img) & ImageMeta
33
using Test
44
using ImageCore
5+
using Random, Base.CoreLogging
56

67
mutable struct TestType end
78

@@ -27,24 +28,31 @@ mutable struct TestType end
2728
fn = joinpath(workdir, "5by5.png")
2829
ImageMagick.save(fn, a)
2930
b = ImageMagick.load(fn)
30-
a8 = convert(Array{Gray{N0f8}}, a) # IM won't read back as Bool
31-
@test b == a8
31+
ag = convert(Array{Gray{Bool}}, a) # IM won't read back as Bool
32+
@test b == ag
33+
@test eltype(b) (Bool, Gray{Bool})
3234
aim = colorview(Gray, a)
3335
ImageMagick.save(fn, aim)
3436
b = ImageMagick.load(fn)
35-
@test b == a8
37+
@test b == ag
3638
a = bitrand(5,5)
3739
fn = joinpath(workdir, "5by5.png")
3840
ImageMagick.save(fn, a)
3941
b = ImageMagick.load(fn)
40-
a8 = convert(Array{Gray{N0f8}}, a)
41-
@test b == a8
42+
ag = convert(Array{Gray{Bool}}, a)
43+
@test b == ag
4244
aim = colorview(Gray, a)
4345
ImageMagick.save(fn, aim)
4446
b = ImageMagick.load(fn)
45-
@test b == a8
47+
@test b == ag
4648

47-
@test ImageMagick.metadata(fn) == ((5,5), Gray{N0f8})
49+
@test ImageMagick.metadata(fn) == ((5,5), Gray{Bool})
50+
51+
# If we try to save as JPG, don't error
52+
fn = joinpath(workdir, "5by5.jpg")
53+
ImageMagick.save(fn, a)
54+
b = ImageMagick.load(fn)
55+
@test eltype(b) == Gray{N0f8}
4856
end
4957

5058
@testset "Gray png" begin

0 commit comments

Comments
 (0)