-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathmmap.jl
More file actions
127 lines (102 loc) 路 3.13 KB
/
Copy pathmmap.jl
File metadata and controls
127 lines (102 loc) 路 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""
$(TYPEDEF)
A type to represent memory-mapped TIFF data. Useful for opening and operating on
images too large to store in memory.
$(FIELDS)
"""
mutable struct DiskTaggedImage{T <: Colorant, O <: Unsigned, S <: Stream, AA <: AbstractArray} <: AbstractDenseTIFF{T, 3}
"""
Pointer to keep track of the backing file
"""
file::TiffFile{O, S}
"""
The associated tags for each slice in this array
"""
ifds::Vector{IFD{O}}
dims::NTuple{3, Int}
"""
An internal cache to fill reading from disk
"""
cache::AA
"""
The index of the currently loaded slice
"""
cache_index::Int
microcache::Vector{T}
"""
A flag tracking whether this file is editable
"""
readonly::Bool
end
function DiskTaggedImage(file::TiffFile{O, S}, ifds::Vector{IFD{O}}) where {O, S}
ifd = ifds[1]
dims = (nrows(ifd), ncols(ifd), length(ifds))
cache = getcache(ifd)
microcache = Vector{eltype(cache)}(undef, 1)
img = DiskTaggedImage{eltype(cache), O, S, typeof(cache)}(file, ifds, dims, cache, -1, microcache, true)
@async watch(img)
img
end
Base.size(A::DiskTaggedImage) = A.dims
function convert!(A::DiskTaggedImage; writeable=false)
if writeable
A.readonly = false
try
close(A.file.io)
catch
end
path = A.file.filepath
A.file.io = Stream(format"TIFF", open(path, append=true, read=true), path)
end
return nothing
end
function watch(A::DiskTaggedImage)
@info "Watching $(A.file.filepath) for changes"
while true
ev = watch_file(A.file.filepath)
if ev.renamed
error("Watched file $(A.file.filepath) has been moved.")
elseif ev.changed
A.cache_index = -1 #invalidate cache
convert!(A; writeable=!A.readonly)
end
end
end
function Base.getindex(A::DiskTaggedImage, i1::Int, i2::Int, i::Int)
# check the loaded cache is already the correct slice
if A.cache_index == i
return A.cache[i2, i1]
end
ifd = A.ifds[i]
# if the file isn't open, lets open a handle and update it
if !isopen(A.file.io)
path = A.file.filepath
A.file.io = Stream(format"TIFF", open(path), path)
end
read!(A.cache, A.file, ifd)
A.cache_index = i
return A.cache[i2, i1]
end
function Base.setindex!(A::DiskTaggedImage{T, O, S, AA}, value, i1::Int, i2::Int, i3::Int) where {T, O, S, AA}
if A.readonly
error("This is a read only memory-mapped file. Make sure to run convert!(img, writeable=true) first.")
end
linear = LinearIndices((1:size(A, 2), 1:size(A, 1)))
linidx = O(linear[i2, i1] * sizeof(T))::O
ifd = A.ifds[i3]
bytecounts = ifd[STRIPBYTECOUNTS].data::Vector{O}
stripidx = 1
counts = O(0)
for i in 1:length(bytecounts)
stripidx = i
counts = bytecounts[stripidx]::O
(linidx < counts) && break
linidx -= counts
end
A.cache_index = -1
offsets = ifd[STRIPOFFSETS].data::Vector{O}
offset = offsets[stripidx]::O
seek(A.file, Int(offset+linidx))
A.microcache[1] = value
write(A.file.io, A.microcache)
end