Add support for creating an animated WebP

This initial implementation doesn't fully expose the `WebPAnimEncoderOptions`.
It also still needs some unit tests and examples.
master
Rohan Singh 6 years ago
parent 68562c9c99
commit 1c4bc9fe39

@ -0,0 +1,121 @@
package webp
/*
#include <stdlib.h>
#include <string.h>
#include <webp/encode.h>
#include <webp/mux.h>
int writeWebP(uint8_t*, size_t, struct WebPPicture*);
static WebPPicture *calloc_WebPPicture(void) {
return calloc(sizeof(WebPPicture), 1);
}
static void free_WebPPicture(WebPPicture* webpPicture) {
free(webpPicture);
}
*/
import "C"
import (
"errors"
"fmt"
"time"
"image"
"unsafe"
)
// AnimationEncoder encodes multiple images into an animated WebP.
type AnimationEncoder struct {
opts C.WebPAnimEncoderOptions
c *C.WebPAnimEncoder
duration time.Duration
}
// NewAnimationEncoder initializes a new encoder.
func NewAnimationEncoder(width, height, kmin, kmax int) (*AnimationEncoder, error) {
ae := &AnimationEncoder{}
if C.WebPAnimEncoderOptionsInit(&ae.opts) == 0 {
return nil, errors.New("failed to initialize animation encoder config")
}
ae.opts.kmin = C.int(kmin)
ae.opts.kmax = C.int(kmax)
ae.c = C.WebPAnimEncoderNew(C.int(width), C.int(height), &ae.opts)
if ae.c == nil {
return nil, errors.New("failed to initialize animation encoder")
}
return ae, nil
}
// AddFrame adds a frame to the encoder.
func (ae *AnimationEncoder) AddFrame(img image.Image, duration time.Duration) error {
pic := C.calloc_WebPPicture()
if pic == nil {
return errors.New("Could not allocate webp picture")
}
defer C.free_WebPPicture(pic)
if C.WebPPictureInit(pic) == 0 {
return errors.New("Could not initialize webp picture")
}
defer C.WebPPictureFree(pic)
pic.use_argb = 1
pic.width = C.int(img.Bounds().Dx())
pic.height = C.int(img.Bounds().Dy())
pic.writer = C.WebPWriterFunction(C.writeWebP)
switch p := img.(type) {
case *RGBImage:
C.WebPPictureImportRGB(pic, (*C.uint8_t)(&p.Pix[0]), C.int(p.Stride))
case *image.RGBA:
C.WebPPictureImportRGBA(pic, (*C.uint8_t)(&p.Pix[0]), C.int(p.Stride))
case *image.NRGBA:
C.WebPPictureImportRGBA(pic, (*C.uint8_t)(&p.Pix[0]), C.int(p.Stride))
default:
return errors.New("unsupported image type")
}
timestamp := C.int((duration + ae.duration) / time.Millisecond)
if C.WebPAnimEncoderAdd(ae.c, pic, timestamp, nil) == 0 {
return fmt.Errorf(
"Encoding error: %d - %s",
int(pic.error_code),
C.GoString(C.WebPAnimEncoderGetError(ae.c)),
)
}
ae.duration += duration
return nil
}
// Assemble assembles all frames into animated WebP.
func (ae *AnimationEncoder) Assemble() ([]byte, error) {
// add final empty frame
if C.WebPAnimEncoderAdd(ae.c, nil, C.int(ae.duration / time.Millisecond), nil) == 0 {
return nil, errors.New("Couldn't add final empty frame")
}
data := &C.WebPData{}
C.WebPDataInit(data)
if C.WebPAnimEncoderAssemble(ae.c, data) == 0 {
return nil, errors.New("Error assembling animation")
}
return C.GoBytes(
unsafe.Pointer(data.bytes),
C.int(int(data.size)),
), nil
}
// Close deletes the encoder and frees resources.
func (ae *AnimationEncoder) Close() {
C.WebPAnimEncoderDelete(ae.c)
}
Loading…
Cancel
Save