summaryrefslogblamecommitdiffstats
path: root/vendor/github.com/Benau/tgsconverter/libtgsconverter/lib.go
blob: af6f8ab05ca92a0a353bf977a825d81ced7a5a37 (plain) (tree)






























































































































































                                                                                         
package libtgsconverter

import "bytes"
import "errors"
import "compress/gzip"
import "image"
import "io/ioutil"

import "github.com/Benau/go_rlottie"

type ConverterOptions interface {
	SetExtension(ext string)
	SetFPS(fps uint)
	SetScale(scale float32)
	SetWebpQuality(webp_quality float32)
	GetExtension() string
	GetFPS() uint
	GetScale() float32
	GetWebpQuality() float32
}

type converter_options struct {
	// apng, gif, png or webp
	extension string
	// Frame per second of output image (if you specify apng, gif or webp)
	fps uint
	// Scale of image result
	scale float32
	// Webp encoder quality (0 to 100)
	webpQuality float32
}

func(opt *converter_options) SetExtension(ext string) {
	opt.extension = ext
}

func(opt *converter_options) SetFPS(fps uint) {
	opt.fps = fps
}

func(opt *converter_options) SetScale(scale float32) {
	opt.scale = scale
}

func(opt *converter_options) SetWebpQuality(webp_quality float32) {
	opt.webpQuality = webp_quality
}

func(opt *converter_options) GetExtension() string {
	return opt.extension
}

func(opt *converter_options) GetFPS() uint {
	return opt.fps
}

func(opt *converter_options) GetScale() float32 {
	return opt.scale
}

func(opt *converter_options) GetWebpQuality() float32 {
	return opt.webpQuality
}

func NewConverterOptions() ConverterOptions {
	return &converter_options{"png", 30, 1.0, 75}
}

func imageFromBuffer(p []byte, w uint, h uint) *image.RGBA {
	// rlottie use ARGB32_Premultiplied
	for i := 0; i < len(p); i += 4 {
		p[i + 0], p[i + 2] = p[i + 2], p[i + 0]
	}
	m := image.NewRGBA(image.Rect(0, 0, int(w), int(h)))
	m.Pix = p
	m.Stride = int(w) * 4
	return m
}

var disabled_cache = false
func ImportFromData(data []byte, options ConverterOptions) ([]byte, error) {
	if !disabled_cache {
		disabled_cache = true
		go_rlottie.LottieConfigureModelCacheSize(0)
	}
	z, err := gzip.NewReader(bytes.NewReader(data))
	if err != nil {
		return nil, errors.New("Failed to create gzip reader:" + err.Error())
	}
	uncompressed, err := ioutil.ReadAll(z)
	if err != nil {
		return nil, errors.New("Failed to read gzip archive")
	}
	z.Close()

	animation := go_rlottie.LottieAnimationFromData(string(uncompressed[:]), "", "")
	if animation == nil {
		return nil, errors.New("Failed to import lottie animation data")
	}

	w, h := go_rlottie.LottieAnimationGetSize(animation)
	w = uint(float32(w) * options.GetScale())
	h = uint(float32(h) * options.GetScale())

	frame_rate := go_rlottie.LottieAnimationGetFramerate(animation)
	frame_count := go_rlottie.LottieAnimationGetTotalframe(animation)
	duration := float32(frame_count) / float32(frame_rate)
	var desired_framerate = float32(options.GetFPS())
	// Most (Gif) player doesn't support ~60fps (found in most tgs)
	if desired_framerate > 50. {
		desired_framerate = 50.
	}
	step := 1.0 / desired_framerate

	writer := newImageWriter(options.GetExtension(), w, h, options)
	if writer == nil {
		return nil, errors.New("Failed create imagewriter")
	}

	var i float32
	for i = 0.; i < duration; i += step {
		frame := go_rlottie.LottieAnimationGetFrameAtPos(animation, i / duration)
		buf := make([]byte, w * h * 4)
		go_rlottie.LottieAnimationRender(animation, frame, buf, w, h, w * 4)
		m := imageFromBuffer(buf, w, h)
		err := writer.AddFrame(m, uint(desired_framerate))
		if err != nil {
			return nil, errors.New("Failed to add frame:" + err.Error())
		}
		if !writer.SupportsAnimation() {
			break
		}
	}
	go_rlottie.LottieAnimationDestroy(animation)
	return writer.Result(), nil
}

func ImportFromFile(path string, options ConverterOptions) ([]byte, error) {
	tgs, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, errors.New("Error when opening file:" + err.Error())
	}
	return ImportFromData(tgs, options)
}

func SupportsExtension(extension string) (bool) {
	switch extension {
	case "apng":
		fallthrough
	case "gif":
		fallthrough
	case "png":
		fallthrough
	case "webp":
		return true
	default:
		return false
	}
	return false
}