From a01cd1f0eeeb1df7b69cc7fe5a055cc7ed983a5a Mon Sep 17 00:00:00 2001 From: David Christofas Date: Fri, 11 Mar 2022 18:10:18 +0100 Subject: [PATCH] improve gif thumbnail generation --- changelog/unreleased/gif-thumbnails.md | 5 ++++ thumbnails/pkg/thumbnail/generator.go | 39 ++++++++++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 changelog/unreleased/gif-thumbnails.md diff --git a/changelog/unreleased/gif-thumbnails.md b/changelog/unreleased/gif-thumbnails.md new file mode 100644 index 00000000000..188d8f5bdb9 --- /dev/null +++ b/changelog/unreleased/gif-thumbnails.md @@ -0,0 +1,5 @@ +Bugfix: Improve gif thumbnails + +Improved the gif thumbnail generation for gifs with different disposal strategies. + +https://github.com/owncloud/ocis/pull/3305 diff --git a/thumbnails/pkg/thumbnail/generator.go b/thumbnails/pkg/thumbnail/generator.go index 6bea9b87a3b..b22c15da458 100644 --- a/thumbnails/pkg/thumbnail/generator.go +++ b/thumbnails/pkg/thumbnail/generator.go @@ -3,6 +3,7 @@ package thumbnail import ( "errors" "image" + "image/color" "image/draw" "image/gif" "strings" @@ -35,22 +36,44 @@ func (g SimpleGenerator) GenerateThumbnail(size image.Rectangle, img interface{} type GifGenerator struct{} func (g GifGenerator) GenerateThumbnail(size image.Rectangle, img interface{}) (interface{}, error) { + // Code inspired by https://github.com/willnorris/gifresize/blob/db93a7e1dcb1c279f7eeb99cc6d90b9e2e23e871/gifresize.go + m, ok := img.(*gif.GIF) if !ok { return nil, ErrInvalidType2 } - var bounds image.Rectangle - for i := range m.Image { - img := imaging.Resize(m.Image[i], size.Dx(), size.Dy(), imaging.Lanczos) - bounds = image.Rect(0, 0, size.Dx(), size.Dy()) - m.Image[i] = image.NewPaletted(bounds, m.Image[i].Palette) - draw.Draw(m.Image[i], bounds, img, image.Pt(0, 0), draw.Src) + // Create a new RGBA image to hold the incremental frames. + srcX, srcY := m.Config.Width, m.Config.Height + b := image.Rect(0, 0, srcX, srcY) + tmp := image.NewRGBA(b) + + for i, frame := range m.Image { + bounds := frame.Bounds() + prev := tmp + draw.Draw(tmp, bounds, frame, bounds.Min, draw.Over) + scaled := imaging.Resize(tmp, size.Dx(), size.Dy(), imaging.Lanczos) + m.Image[i] = g.imageToPaletted(scaled, frame.Palette) + + switch m.Disposal[i] { + case gif.DisposalBackground: + tmp = image.NewRGBA(b) + case gif.DisposalPrevious: + tmp = prev + } } - m.Config.Height = bounds.Dy() - m.Config.Width = bounds.Dx() + m.Config.Width = size.Dx() + m.Config.Height = size.Dy() + return m, nil } +func (g GifGenerator) imageToPaletted(img image.Image, p color.Palette) *image.Paletted { + b := img.Bounds() + pm := image.NewPaletted(b, p) + draw.FloydSteinberg.Draw(pm, b, img, image.Point{}) + return pm +} + // GeneratorForType returns the generator for a given file type // or nil if the type is not supported. func GeneratorForType(fileType string) (Generator, error) {