Skip to content

Commit

Permalink
promote more functionality to library
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrew Pennebaker committed May 10, 2023
1 parent 8b04e98 commit 75ceab1
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 132 deletions.
139 changes: 137 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package buttery

import (
"github.com/andybons/gogif"
"github.com/disintegration/imaging"

"errors"
"image"
"image/color"
"image/draw"
"image/gif"
"math"
"os"
)

// Config models a set of animation editing instructions.
// Config models a set of animation editing manipulations.
type Config struct {
// Reverse plays the incoming sequence backwards (Default false).
Reverse bool
Expand All @@ -29,7 +38,7 @@ type Config struct {
// Stitch denotes a loop continuity transition (Default Mirror).
Stitch Stitch

//
// Speed applies a factor to each frame delay (Default 1.0)
Speed float64
}

Expand Down Expand Up @@ -65,3 +74,129 @@ func (o Config) Validate() error {

return nil
}

// Edit applies the configured GIF manipulations.
func (o Config) Edit(destPth string, sourceGif *gif.GIF) error {
sourcePaletteds := sourceGif.Image
sourcePalettedsLen := len(sourcePaletteds)

if o.TrimStart+o.TrimEnd >= sourcePalettedsLen {
return errors.New("minimum 1 output frame")
}

if o.Window > sourcePalettedsLen-o.TrimStart-o.TrimEnd {
return errors.New("window longer than subsequence")
}

sourceDelays := sourceGif.Delay
sourceWidth, sourceHeight := GetDimensions(sourcePaletteds)
canvasImage := image.NewRGBA(image.Rect(0, 0, sourceWidth, sourceHeight))
canvasBounds := canvasImage.Bounds()
paletteSize := GetPaletteSize(sourcePaletteds)
clonePaletteds := make([]*image.Paletted, sourcePalettedsLen)
quantizer := gogif.MedianCutQuantizer{NumColor: paletteSize}
draw.DrawMask(canvasImage, canvasBounds, &image.Uniform{sourcePaletteds[0].Palette.Convert(color.Black)}, image.Point{}, nil, image.Pt(0, 0), draw.Src)

for i, sourcePaletted := range sourcePaletteds {
draw.Draw(canvasImage, canvasBounds, sourcePaletted, image.Point{}, draw.Over)
clonePaletted := image.NewPaletted(canvasBounds, nil)
quantizer.Quantize(clonePaletted, canvasBounds, canvasImage, image.Point{})
clonePaletteds[i] = clonePaletted
}

if o.Reverse {
ReverseSlice(clonePaletteds)
ReverseSlice(sourceDelays)
}

clonePaletteds = clonePaletteds[o.TrimStart:]
clonePaletteds = clonePaletteds[:len(clonePaletteds)-o.TrimEnd]
sourceDelays = sourceDelays[o.TrimStart:]
sourceDelays = sourceDelays[:len(sourceDelays)-o.TrimEnd]

if o.Window != 0 {
clonePaletteds = clonePaletteds[:o.Window]
sourceDelays = sourceDelays[:o.Window]
}

clonePalettedsLen := len(clonePaletteds)
var butteryPalettedsLen int

switch o.Stitch {
case Mirror:
butteryPalettedsLen = 2*clonePalettedsLen - 1
case FlipH:
butteryPalettedsLen = 2 * clonePalettedsLen
case FlipV:
butteryPalettedsLen = 2 * clonePalettedsLen
default:
butteryPalettedsLen = clonePalettedsLen
}

butteryPaletteds := make([]*image.Paletted, butteryPalettedsLen)
butteryDelays := make([]int, butteryPalettedsLen)
butteryDelaysLen := butteryPalettedsLen
var r int

for i := 0; i < butteryPalettedsLen; i++ {
paletted := clonePaletteds[r]

if (o.Stitch == FlipH || o.Stitch == FlipV) && i > clonePalettedsLen-1 {
flipPaletted := image.NewPaletted(canvasBounds, nil)

var flippedNRGBA *image.NRGBA

if o.Stitch == FlipH {
flippedNRGBA = imaging.FlipH(paletted)
} else {
flippedNRGBA = imaging.FlipV(paletted)
}

quantizer.Quantize(flipPaletted, canvasBounds, flippedNRGBA, image.Point{})
paletted = flipPaletted
}

butteryPaletteds[i] = paletted
sourceDelay := sourceDelays[r]
butteryDelays[i] = int(math.Max(2.0, float64(sourceDelay)/o.Speed))

if o.Stitch == Mirror && i >= clonePalettedsLen-1 {
r--
} else if (o.Stitch == FlipH || o.Stitch == FlipV) && i == clonePalettedsLen-1 {
r = 0
} else {
r++
}
}

var shiftedPaletteds = make([]*image.Paletted, butteryPalettedsLen)
var shiftedDelays = make([]int, butteryDelaysLen)

for i := range butteryPaletteds {
r = (i + o.Shift) % butteryPalettedsLen

if r < 0 {
r += butteryPalettedsLen
}

shiftedPaletteds[i] = butteryPaletteds[r]
shiftedDelays[i] = butteryDelays[r]
}

butteryGif := gif.GIF{
LoopCount: 0,
BackgroundIndex: sourceGif.BackgroundIndex,
Config: sourceGif.Config,
Image: shiftedPaletteds,
Delay: shiftedDelays,
Disposal: nil,
}

butteryFile, err := os.Create(destPth)

if err != nil {
return err
}

return gif.EncodeAll(butteryFile, &butteryGif)
}
133 changes: 3 additions & 130 deletions src/cmd/buttery/main.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
package main

import (
"github.com/andybons/gogif"
"github.com/disintegration/imaging"
"github.com/mcandre/buttery"

"flag"
"fmt"
"image"
"image/color"
"image/draw"
"image/gif"
"math"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -119,138 +113,17 @@ func main() {
}

sourcePaletteds := sourceGif.Image
sourcePalettedsLen := len(sourcePaletteds)

if getFrames {
fmt.Println(sourcePalettedsLen)
fmt.Println(len(sourcePaletteds))
os.Exit(0)
}

if config.TrimStart+config.TrimEnd >= sourcePalettedsLen {
fmt.Fprintln(os.Stderr, "minimum 1 output frame")
os.Exit(1)
}

if config.Window > sourcePalettedsLen-config.TrimStart-config.TrimEnd {
fmt.Fprintln(os.Stderr, "window longer than subsequence")
os.Exit(1)
}

sourceDelays := sourceGif.Delay
sourceWidth, sourceHeight := buttery.GetDimensions(sourcePaletteds)
canvasImage := image.NewRGBA(image.Rect(0, 0, sourceWidth, sourceHeight))
canvasBounds := canvasImage.Bounds()
paletteSize := buttery.GetPaletteSize(sourcePaletteds)
clonePaletteds := make([]*image.Paletted, sourcePalettedsLen)
quantizer := gogif.MedianCutQuantizer{NumColor: paletteSize}
draw.DrawMask(canvasImage, canvasBounds, &image.Uniform{sourcePaletteds[0].Palette.Convert(color.Black)}, image.ZP, nil, image.Pt(0, 0), draw.Src)

for i, sourcePaletted := range sourcePaletteds {
draw.Draw(canvasImage, canvasBounds, sourcePaletted, image.ZP, draw.Over)
clonePaletted := image.NewPaletted(canvasBounds, nil)
quantizer.Quantize(clonePaletted, canvasBounds, canvasImage, image.ZP)
clonePaletteds[i] = clonePaletted
}

if config.Reverse {
buttery.ReverseSlice(clonePaletteds)
buttery.ReverseSlice(sourceDelays)
}

clonePaletteds = clonePaletteds[config.TrimStart:]
clonePaletteds = clonePaletteds[:len(clonePaletteds)-config.TrimEnd]
sourceDelays = sourceDelays[config.TrimStart:]
sourceDelays = sourceDelays[:len(sourceDelays)-config.TrimEnd]

if config.Window != 0 {
clonePaletteds = clonePaletteds[:config.Window]
sourceDelays = sourceDelays[:config.Window]
}

clonePalettedsLen := len(clonePaletteds)
var butteryPalettedsLen int

switch config.Stitch {
case buttery.Mirror:
butteryPalettedsLen = 2*clonePalettedsLen - 1
case buttery.FlipH:
butteryPalettedsLen = 2 * clonePalettedsLen
case buttery.FlipV:
butteryPalettedsLen = 2 * clonePalettedsLen
default:
butteryPalettedsLen = clonePalettedsLen
}

butteryPaletteds := make([]*image.Paletted, butteryPalettedsLen)
butteryDelays := make([]int, butteryPalettedsLen)
butteryDelaysLen := butteryPalettedsLen
var r int

for i := 0; i < butteryPalettedsLen; i++ {
paletted := clonePaletteds[r]

if (config.Stitch == buttery.FlipH || config.Stitch == buttery.FlipV) && i > clonePalettedsLen-1 {
flipPaletted := image.NewPaletted(canvasBounds, nil)

var flippedNRGBA *image.NRGBA

if config.Stitch == buttery.FlipH {
flippedNRGBA = imaging.FlipH(paletted)
} else {
flippedNRGBA = imaging.FlipV(paletted)
}

quantizer.Quantize(flipPaletted, canvasBounds, flippedNRGBA, image.ZP)
paletted = flipPaletted
}

butteryPaletteds[i] = paletted
sourceDelay := sourceDelays[r]
butteryDelays[i] = int(math.Max(2.0, float64(sourceDelay)/config.Speed))

if config.Stitch == buttery.Mirror && i >= clonePalettedsLen-1 {
r--
} else if (config.Stitch == buttery.FlipH || config.Stitch == buttery.FlipV) && i == clonePalettedsLen-1 {
r = 0
} else {
r++
}
}

var shiftedPaletteds = make([]*image.Paletted, butteryPalettedsLen)
var shiftedDelays = make([]int, butteryDelaysLen)

for i := range butteryPaletteds {
r = (i + config.Shift) % butteryPalettedsLen

if r < 0 {
r += butteryPalettedsLen
}

shiftedPaletteds[i] = butteryPaletteds[r]
shiftedDelays[i] = butteryDelays[r]
}

butteryGif := gif.GIF{
LoopCount: 0,
BackgroundIndex: sourceGif.BackgroundIndex,
Config: sourceGif.Config,
Image: shiftedPaletteds,
Delay: shiftedDelays,
Disposal: nil,
}

sourceBasename := strings.TrimSuffix(sourcePth, filepath.Ext(sourcePth))
butteryPth := fmt.Sprintf("%v.buttery.gif", sourceBasename)
butteryFile, err := os.Create(butteryPth)
destPth := fmt.Sprintf("%v.buttery.gif", sourceBasename)

if err != nil {
if err := config.Edit(destPth, sourceGif); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

if err2 := gif.EncodeAll(butteryFile, &butteryGif); err2 != nil {
fmt.Fprintln(os.Stderr, err2)
os.Exit(1)
}
}

0 comments on commit 75ceab1

Please sign in to comment.