Skip to content

Commit

Permalink
Merge pull request #3 from mikegleasonjr/safe-encoding
Browse files Browse the repository at this point in the history
Can now specify a safe encoding which prevents segfaults in containers. Fixes #1
  • Loading branch information
jdeng authored Mar 29, 2019
2 parents 0715d98 + c912ff6 commit 78ce073
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

.vscode
9 changes: 8 additions & 1 deletion goheif.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import (
"github.com/jdeng/goheif/libde265"
)

// SafeEncoding uses more memory but seems to make
// the library safer to use in containers.
var SafeEncoding bool

type gridBox struct {
columns, rows int
width, height int
Expand Down Expand Up @@ -100,7 +104,10 @@ func Decode(r io.Reader) (image.Image, error) {
return nil, fmt.Errorf("No item info")
}

dec := libde265.NewDecoder()
dec, err := libde265.NewDecoder(libde265.WithSafeEncoding(SafeEncoding))
if err != nil {
return nil, err
}
defer dec.Free()
if it.Info.ItemType == "hvc1" {
return decodeHevcItem(dec, hf, it)
Expand Down
32 changes: 32 additions & 0 deletions goheif_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package goheif
import (
"bytes"
"image"
"io"
"io/ioutil"
"testing"
)
Expand All @@ -26,3 +27,34 @@ func TestFormatRegistered(t *testing.T) {
t.Errorf("unexpected decoded image size: got %dx%d, want 1596x1064", w, h)
}
}

func BenchmarkSafeEncoding(b *testing.B) {
benchEncoding(b, true)
}

func BenchmarkRegularEncoding(b *testing.B) {
benchEncoding(b, false)
}

func benchEncoding(b *testing.B, safe bool) {
b.Helper()

currentSetting := SafeEncoding
defer func() {
SafeEncoding = currentSetting
}()
SafeEncoding = safe

f, err := ioutil.ReadFile("testdata/camel.heic")
if err != nil {
b.Fatal(err)
}
r := bytes.NewReader(f)

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
Decode(r)
r.Seek(0, io.SeekStart)
}
}
44 changes: 31 additions & 13 deletions libde265/libde265.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import (
)

type Decoder struct {
ctx unsafe.Pointer
hasImage bool
ctx unsafe.Pointer
hasImage bool
safeEncode bool
}

func Init() {
Expand All @@ -26,13 +27,27 @@ func Fini() {
C.de265_free()
}

func NewDecoder() *Decoder {
if p := C.de265_new_decoder(); p != nil {
return &Decoder{ctx: p, hasImage: false}
func NewDecoder(opts ...Option) (*Decoder, error) {
p := C.de265_new_decoder()
if p == nil {
return nil, fmt.Errorf("Unable to create decoder")
}
return nil

dec := &Decoder{ctx: p, hasImage: false}
for _, opt := range opts {
opt(dec)
}

return dec, nil
}

type Option func(*Decoder)

func WithSafeEncoding(b bool) Option {
return func(dec *Decoder) {
dec.safeEncode = b
}
}

func (dec *Decoder) Free() {
dec.Reset()
Expand Down Expand Up @@ -113,7 +128,7 @@ func (dec *Decoder) DecodeImage(data []byte) (image.Image, error) {
// crh := C.de265_get_image_height(img, 2)

// sanity check
if int(height) * int(ystride) >= int(1 << 30) {
if int(height)*int(ystride) >= int(1<<30) {
return nil, fmt.Errorf("image too big")
}

Expand All @@ -127,17 +142,20 @@ func (dec *Decoder) DecodeImage(data []byte) (image.Image, error) {
r = image.YCbCrSubsampleRatio444
}
ycc := &image.YCbCr{
Y: (*[1 << 30]byte)(unsafe.Pointer(y))[:int(height)*int(ystride)],
Cb: (*[1 << 30]byte)(unsafe.Pointer(cb))[:int(cheight)*int(cstride)],
Cr: (*[1 << 30]byte)(unsafe.Pointer(cr))[:int(cheight)*int(cstride)],
//Y: C.GoBytes(unsafe.Pointer(y), C.int(height*ystride)),
//Cb: C.GoBytes(unsafe.Pointer(cb), C.int(cheight*cstride)),
//Cr: C.GoBytes(unsafe.Pointer(cr), C.int(cheight*cstride)),
YStride: int(ystride),
CStride: int(cstride),
SubsampleRatio: r,
Rect: image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{int(width), int(height)}},
}
if dec.safeEncode {
ycc.Y = C.GoBytes(unsafe.Pointer(y), C.int(height*ystride))
ycc.Cb = C.GoBytes(unsafe.Pointer(cb), C.int(cheight*cstride))
ycc.Cr = C.GoBytes(unsafe.Pointer(cr), C.int(cheight*cstride))
} else {
ycc.Y = (*[1 << 30]byte)(unsafe.Pointer(y))[:int(height)*int(ystride)]
ycc.Cb = (*[1 << 30]byte)(unsafe.Pointer(cb))[:int(cheight)*int(cstride)]
ycc.Cr = (*[1 << 30]byte)(unsafe.Pointer(cr))[:int(cheight)*int(cstride)]
}

//C.de265_release_next_picture(dec.ctx)

Expand Down

0 comments on commit 78ce073

Please sign in to comment.