-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
607847e
commit 86fdae9
Showing
9 changed files
with
260 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
struct LibSixelDecoder <: AbstractSixelDecoder | ||
# (Experimental) internal fields | ||
|
||
# We need `allocator` to constructor libsixel objects, however, users of this | ||
# Julia package is not expected to use this field as it really should just live | ||
# in the C world. | ||
allocator::SixelAllocator | ||
|
||
function LibSixelDecoder(allocator=SixelAllocator()) | ||
new(allocator) | ||
end | ||
end | ||
|
||
function (dec::LibSixelDecoder)(bytes::Vector{UInt8}; transpose=false) | ||
pixels = Ref{Ptr{Cuchar}}() | ||
palette = Ref{Ptr{Cuchar}}() | ||
pwidth = Ref(Cint(0)) | ||
pheight = Ref(Cint(0)) | ||
ncolors = Ref(Cint(0)) | ||
|
||
status = C.sixel_decode_raw( | ||
bytes, length(bytes), | ||
pixels, pwidth, pheight, | ||
palette, ncolors, | ||
dec.allocator.ptr | ||
) | ||
check_status(status) | ||
|
||
index = unsafe_wrap(Matrix{Cuchar}, pixels[], (pwidth[], pheight[]); own=false) | ||
|
||
# libsixel declares it to be ARGB but it's actually RGB{N0f8} | ||
PT = RGB{N0f8} | ||
pvalues = convert(Ptr{PT}, palette[]) | ||
values = unsafe_wrap(Vector{PT}, pvalues, (ncolors[], ); own=false) | ||
values = OffsetArray(values, OffsetArrays.Origin(0)) # again, libsixel assumes 0-based indexing | ||
|
||
return index, values | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
""" | ||
sixel_decode([T=RGB{N0f8}], src, [decoder]; kwargs...) -> img::IndirectArray | ||
Decode the sixel format sequence provided by `src` and output as an indexed image. | ||
# Arguments | ||
- `T`: output eltype. By default it is `RGB{N0f8}`. | ||
- `src`: the input sixel data source. It can be either an `IO`, `AbstractVector{UInt8}`, or `AbstractString`. | ||
- `decoder::AbstractSixelDecoder`: the sixel decoder. Currently only `LibSixelDecoder` is available. | ||
# Parameters | ||
- `transpose::Bool`: whether we need to permute the image's width and height dimension before encoding. | ||
The default value is `false`. | ||
# References | ||
- [1] VT330/VT340 Programmer Reference Manual, Volume 1: Text Programming | ||
- [2] VT330/VT340 Programmer Reference Manual, Volume 2: Graphics Programming | ||
- [3] https://github.com/saitoha/libsixel | ||
""" | ||
function sixel_decode end | ||
|
||
sixel_decode(src, dec=default_decoder(); kwargs...) = sixel_decode(RGB{N0f8}, src, dec; kwargs...) | ||
|
||
sixel_decode(::Type{T}, data::AbstractString, dec=default_decoder(); kwargs...) where T = | ||
sixel_decode(T, collect(UInt8, convert(String, data)), dec; kwargs...) | ||
|
||
function sixel_decode(::Type{T}, bytes::AbstractArray, dec=default_decoder(); transpose=false) where {T} | ||
bytes = convert(Vector{UInt8}, bytes) | ||
|
||
expected_size = read_sixel_size(bytes) | ||
index, values = dec(bytes) | ||
values = eltype(values) == T ? values : map(T, values) | ||
|
||
if dec isa LibSixelDecoder | ||
# Julia uses column-major order while libsixel uses row-major order, | ||
# thus transpose=true means no permutation. | ||
index = transpose ? index : PermutedDimsArray(index, (2, 1)) | ||
expected_size = transpose ? (expected_size[2], expected_size[1]) : expected_size | ||
else | ||
throw(ArgumentError("Unsupported decoder type $(typeof(enc)). Please open an issue for this.")) | ||
end | ||
|
||
actual_size = size(index) | ||
if expected_size != actual_size | ||
@warn "Output size mismatch during decoding sixel sequences" actual_size expected_size | ||
end | ||
# We use IndirectArray to mark it as an indexed image so as to avoid unnecessary memory allocation | ||
# Users that expect a dense Array can always call `collect(rst)` or `convert(Array, rst)` on this. | ||
return IndirectArray(index, values) | ||
end | ||
|
||
function sixel_decode(::Type{T}, io::IO, dec=default_decoder(); kwargs...) where T | ||
# TODO: This is actually a duplicated check since the bytes method also checks it | ||
# but this is a quite fast operation... | ||
expected_size = read_sixel_size(io) | ||
|
||
bytes = read(io) | ||
img = sixel_decode(T, bytes, dec; kwargs...) | ||
|
||
actual_size = size(img) | ||
if expected_size != actual_size | ||
@warn "Output size mismatch during decoding sixel sequences" actual_size expected_size | ||
end | ||
return img | ||
end | ||
|
||
|
||
""" | ||
read_sixel_size(io::IO) | ||
read_sixel_size(bytes::Vector{UInt8}) | ||
Read the header from a sixel sequence `io`/`bytes` and return the size of the sixel image. | ||
""" | ||
function read_sixel_size(bytes::Vector{UInt8}) | ||
# There's absolutely something wrong if the header content exceeds 50 bytes | ||
max_header_length = 50 | ||
|
||
p_end = findfirst(isequal(UInt8('#')), bytes) | ||
p_end = isnothing(p_end) ? length(bytes) : p_end - 1 | ||
buffer = view(bytes, 1:min(p_end, max_header_length)) | ||
seps = findall(isequal(UInt8(';')), buffer) | ||
length(seps) == 3 || throw(ArgumentError("The input data is not recognizable as sixel sequence.")) | ||
w = parse(Int, String(buffer[seps[2]+1:seps[3]-1])) | ||
h = parse(Int, String(buffer[seps[3]+1:end])) | ||
# (h, w) in column-major order convention | ||
return h, w | ||
end | ||
|
||
function read_sixel_size(io::IO) | ||
# Sixel sequence format always start with this | ||
# \ePq"1;1;w;h# | ||
# where `#` indicates the first palette value | ||
buffer = UInt8[] | ||
p = position(io) | ||
try | ||
# There's absolutely something wrong if the header content exceeds 50 bytes | ||
i, max_header_length = 1, 50 | ||
ch = read(io, Cuchar) | ||
# Cuchar('#') == 0x23 | ||
while ch != 0x23 && i < max_header_length | ||
push!(buffer, ch) | ||
ch = read(io, Cuchar) | ||
i += 1 | ||
end | ||
catch e | ||
e isa EOFError && throw(ArgumentError("The input data is not recognizable as sixel sequence.")) | ||
rethrow(e) | ||
finally | ||
seek(io, p) | ||
end | ||
return read_sixel_size(buffer) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters