giffer

Create .gif images from sites like youtube.com
Log | Files | Refs | LICENSE

commit 0b643149224c757626a22de2d4972749e1f6b1ca
parent c3bcba529a9b504d988b9de2a5b63e5ad4caee13
Author: Jack Mordaunt <jackmordaunt@gmail.com>
Date:   Sun,  2 May 2021 13:20:42 +0800

fix: use image stack to reconstitute each frame

Gif format discards colors that don't change between frames.

Using a stack of the frames up to that point, we can recreate the full frame.

Diffstat:
Acmd/gio/image.go | 38++++++++++++++++++++++++++++++++++++++
Mcmd/gio/main.go | 56++++++++++++++++++++------------------------------------
2 files changed, 58 insertions(+), 36 deletions(-)

diff --git a/cmd/gio/image.go b/cmd/gio/image.go @@ -0,0 +1,38 @@ +package main + +import ( + "image" + "image/color" +) + +var _ image.Image = ImageStack{} + +// ImageStack implements image.Image as a stack of images that returns the first non-zero color +// encountered. +type ImageStack struct { + Stack []image.Image + Config image.Config +} + +func (stack ImageStack) At(x, y int) color.Color { + for _, img := range stack.Stack { + c := img.At(x, y) + if r, g, b, a := c.RGBA(); r > 0 || g > 0 || b > 0 || a > 0 { + return c + } + } + return color.NRGBA{} +} + +func (stack ImageStack) Bounds() image.Rectangle { + return image.Rectangle{ + Max: image.Point{ + X: stack.Config.Width, + Y: stack.Config.Height, + }, + } +} + +func (stack ImageStack) ColorModel() color.Model { + return stack.Config.ColorModel +} diff --git a/cmd/gio/main.go b/cmd/gio/main.go @@ -194,6 +194,9 @@ func (ui *UI) Layout(gtx C) D { l.Rigid(func(gtx C) D { return ui.Form.LayoutFields(gtx, ui.Th) }), + l.Flexed(1, func(gtx C) D { + return D{Size: gtx.Constraints.Max} + }), l.Rigid(func(gtx C) D { return l.Flex{ Axis: l.Horizontal, @@ -218,34 +221,14 @@ func (ui *UI) Layout(gtx C) D { ) }) }), + l.Rigid(func(gtx C) D { + return D{Size: image.Point{X: gtx.Px(unit.Dp(10))}} + }), l.Flexed(1, func(gtx C) D { - return l.UniformInset(unit.Dp(10)).Layout(gtx, func(gtx C) D { - return layout.Center.Layout(gtx, func(gtx C) D { - var ( - cs = &gtx.Constraints - width = gtx.Px(unit.Dp(350)) - height = gtx.Px(unit.Dp(250)) - ) - if ui.GifPlayer.Dimensions.X > 0 && ui.GifPlayer.Dimensions.Y > 0 { - cs.Min.Y = ui.GifPlayer.Dimensions.Y - cs.Min.X = ui.GifPlayer.Dimensions.X - } else { - cs.Max.X /= 2 - cs.Max.Y /= 2 - if cs.Max.X > width { - cs.Max.X = width - } - if cs.Max.Y > height { - cs.Max.Y = height - } - } - return ui.GifPlayer.Layout(gtx) - }) - }) + return ui.GifPlayer.Layout(gtx) }), ) }) - }), layout.Expanded(func(gtx C) D { if !ui.Processing { @@ -330,37 +313,37 @@ func (f *Form) LayoutActions(gtx C, th *m.Theme) D { // GifPlayer animates through a series of frames. // -// TODO(jfm): fix! Displays garbled images. +// TODO(jfm): fix! // - playing way too fast -// - stretching image too much type GifPlayer struct { - Frames []paint.ImageOp - Delays []time.Duration - Cursor int - Dimensions image.Point - since time.Time - img widget.Image + Frames []paint.ImageOp + Delays []time.Duration + Cursor int + since time.Time + img widget.Image } // Load a Gif image to render. func (g *GifPlayer) Load(src *gif.GIF) { g.Frames = make([]paint.ImageOp, len(src.Image)) for ii := range src.Image { - g.Frames[ii] = paint.NewImageOp(src.Image[ii]) + s := ImageStack{Config: src.Config} + for jj := ii; jj >= 0; jj-- { + s.Stack = append(s.Stack, src.Image[jj]) + } + g.Frames[ii] = paint.NewImageOp(s) } g.Delays = make([]time.Duration, len(src.Delay)) for ii := range g.Delays { // Gif delays are in 100th of a second. g.Delays[ii] = time.Duration(src.Delay[ii]) / 10 * time.Millisecond } - g.Dimensions.X = src.Config.Width - g.Dimensions.Y = src.Config.Height } // Ready if the next frame is ready to be displayed. func (g *GifPlayer) Ready(gtx C) bool { if len(g.Delays) == 0 { - return false + return true } var ( now = gtx.Now @@ -401,5 +384,6 @@ func (g *GifPlayer) Layout(gtx C) D { if g.Ready(gtx) { g.Next(gtx) } + g.img.Fit = widget.Fill return g.img.Layout(gtx) }