Skip to content

Commit eca19cb

Browse files
committed
Use pure-Rust heic decoder
1 parent ec4a4c3 commit eca19cb

16 files changed

Lines changed: 83706 additions & 227516 deletions

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
Go decoder for [HEIC Image File Format](https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format) (HEVC in HEIF).
66

7-
Based on [libheif](https://github.com/strukturag/libheif) and [libde265](https://github.com/strukturag/libde265) compiled to [WASM](https://en.wikipedia.org/wiki/WebAssembly) and transpiled to pure Go with [wasm2go](https://github.com/ncruces/wasm2go) (CGo-free).
7+
Based on the pure-Rust [heic](https://crates.io/crates/heic) decoder compiled to [WASM](https://en.wikipedia.org/wiki/WebAssembly) and transpiled to pure Go with [wasm2go](https://github.com/ncruces/wasm2go) (CGo-free).
88

9-
The library will first try to use a dynamic/shared library (if installed) via [purego](https://github.com/ebitengine/purego) and will fall back to the transpiled Go.
9+
The library will first try to use a [libheif](https://github.com/strukturag/libheif) dynamic/shared library (if installed) via [purego](https://github.com/ebitengine/purego) and will fall back to the transpiled Go.
1010

1111
### Build tags
1212

‎decode.go‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package heic
2+
3+
import (
4+
"fmt"
5+
"image"
6+
"image/color"
7+
"io"
8+
"sync"
9+
)
10+
11+
var modPool = sync.Pool{New: func() any { return newModuleRaw() }}
12+
13+
func decode(r io.Reader, configOnly bool) (image.Image, image.Config, error) {
14+
var cfg image.Config
15+
16+
mod := modPool.Get().(*module)
17+
defer modPool.Put(mod)
18+
19+
var data []byte
20+
var err error
21+
if configOnly {
22+
data, err = io.ReadAll(io.LimitReader(r, heifMaxHeaderSize))
23+
} else {
24+
data, err = io.ReadAll(r)
25+
}
26+
if err != nil {
27+
return nil, cfg, fmt.Errorf("read: %w", err)
28+
}
29+
30+
inSize := len(data)
31+
32+
inPtr := mod.Xmalloc(int32(inSize))
33+
if inPtr == 0 {
34+
return nil, cfg, ErrMemWrite
35+
}
36+
defer mod.Xfree(inPtr)
37+
if !mod.write(inPtr, data) {
38+
return nil, cfg, ErrMemWrite
39+
}
40+
41+
info := mod.Xmalloc(2 * 4)
42+
if info == 0 {
43+
return nil, cfg, ErrMemWrite
44+
}
45+
defer mod.Xfree(info)
46+
47+
cfgOnly := int32(0)
48+
if configOnly {
49+
cfgOnly = 1
50+
}
51+
52+
out := mod.Xdecode(inPtr, int32(inSize), cfgOnly, info)
53+
54+
width := load32(mod.memory[info:])
55+
height := load32(mod.memory[info+4:])
56+
57+
cfg.Width = int(width)
58+
cfg.Height = int(height)
59+
cfg.ColorModel = color.NRGBAModel
60+
61+
if configOnly {
62+
if width == 0 {
63+
return nil, image.Config{}, ErrDecode
64+
}
65+
return nil, cfg, nil
66+
}
67+
68+
if out == 0 {
69+
return nil, cfg, ErrDecode
70+
}
71+
defer mod.Xfree(out)
72+
73+
size := int(width) * int(height) * 4
74+
pix, ok := mod.read(out, int32(size))
75+
if !ok {
76+
return nil, cfg, ErrMemRead
77+
}
78+
79+
img := image.NewNRGBA(image.Rect(0, 0, int(width), int(height)))
80+
copy(img.Pix, pix)
81+
82+
return img, cfg, nil
83+
}
84+
85+
func (m *module) write(ptr int32, data []byte) bool {
86+
if ptr < 0 || int(ptr)+len(data) > len(m.memory) {
87+
return false
88+
}
89+
copy(m.memory[ptr:], data)
90+
return true
91+
}
92+
93+
func (m *module) read(ptr, size int32) ([]byte, bool) {
94+
if ptr < 0 || size < 0 || int(ptr)+int(size) > len(m.memory) {
95+
return nil, false
96+
}
97+
return m.memory[ptr : ptr+size : ptr+size], true
98+
}

0 commit comments

Comments
 (0)