Skip to content

Commit

Permalink
Merge pull request #2154 from traPtitech/gif-correct-disposal
Browse files Browse the repository at this point in the history
GIFのリサイズのDisposalの扱いを、仕様と実情に則したものに
  • Loading branch information
logica0419 committed Dec 15, 2023
2 parents 87833d1 + 4c894e8 commit d9006fd
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 21 deletions.
44 changes: 24 additions & 20 deletions service/imaging/processor_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ func (p *defaultProcessor) FitAnimationGIF(src io.Reader, width, height int) (*b

var (
gifBound = image.Rect(0, 0, srcWidth, srcHeight)

// フレームを重ねるためのキャンバス
// 差分最適化されたGIFに対応するための処置
// 差分最適化されたGIFでは、1フレーム目以外、周りが透明ピクセルのフレームを
Expand All @@ -128,8 +127,10 @@ func (p *defaultProcessor) FitAnimationGIF(src io.Reader, width, height int) (*b
// 混ざった色が透明色ではなくなってフレームの縁に黒っぽいノイズが入ってしまう
// ため、キャンバスでフレームを重ねてから縮小する
tempCanvas = image.NewNRGBA(gifBound)
// DisposalPreviousに対応するため、Disposeされていないフレームを保持
unDisposedFrame = image.NewNRGBA(gifBound)
// DisposalBackgroundに対応するための、背景色の画像
bgColorUniform = image.NewUniform(srcImage.Config.ColorModel.(color.Palette)[srcImage.BackgroundIndex])
// DisposalPreviousに対応するため、直前のフレームを保持するためのキャンバス
backupCanvas = image.NewNRGBA(gifBound)

// destImage.ImageのためのMutex
destImageMutex = &sync.Mutex{}
Expand All @@ -148,28 +149,18 @@ func (p *defaultProcessor) FitAnimationGIF(src io.Reader, width, height int) (*b
int(math.Round(float64(srcBounds.Max.Y)*ratio)),
)

switch srcImage.Disposal[i] {
case gif.DisposalBackground:
// Disposalが2に設定されていたらキャンバスを初期化
tempCanvas = image.NewNRGBA(gifBound)

case gif.DisposalPrevious:
// Disposalが3に設定されていたら、Disposeされていないフレームまでキャンバスを戻す
tempCanvas = unDisposedFrame
}

// キャンバスに読んだフレームを重ねる
draw.Draw(tempCanvas, srcBounds, srcFrame, srcBounds.Min, draw.Over)

// Disposalが1に設定されていたら、Disposeされていないフレームを更新
if srcImage.Disposal[i] == gif.DisposalNone {
unDisposedFrame = &image.NRGBA{
// DisposalがPreviousなら、今のキャンバスをDeep Copyしてバックアップ
if srcImage.Disposal[i] == gif.DisposalPrevious {
backupCanvas = &image.NRGBA{
Pix: append([]uint8{}, tempCanvas.Pix...),
Stride: tempCanvas.Stride,
Rect: tempCanvas.Rect,
} // tempCanvasはポインタを使い回しているので、Deep Copyする
}
}

// キャンバスに読んだフレームを重ねる
draw.Draw(tempCanvas, srcBounds, srcFrame, srcBounds.Min, draw.Over)

// 拡縮用GoRoutineを起動
eg.Go(resizeRoutine(frameData{
index: i,
Expand All @@ -184,6 +175,19 @@ func (p *defaultProcessor) FitAnimationGIF(src io.Reader, width, height int) (*b
destBounds: destBounds,
srcPalette: srcImage.Image[i].Palette,
}, destImage, destImageMutex))

switch srcImage.Disposal[i] {
case gif.DisposalBackground: // DisposalがBackgroundなら、このフレームの範囲を背景色で塗りつぶす
// フレームのカラーパレットに透明色が含まれていたら、背景色を透明色とみなす
r, g, b, a := srcFrame.Palette[srcFrame.Palette.Index(color.Transparent)].RGBA()
if r == 0 && g == 0 && b == 0 && a == 0 {
draw.Draw(tempCanvas, srcBounds, image.Transparent, image.Point{}, draw.Src)
} else {
draw.Draw(tempCanvas, srcBounds, bgColorUniform, image.Point{}, draw.Src)
}
case gif.DisposalPrevious: // DisposalがPreviousなら、直前のフレームを復元
tempCanvas = backupCanvas
}
}

err = eg.Wait()
Expand Down
14 changes: 13 additions & 1 deletion service/imaging/processor_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@ func TestProcessorDefault_FitAnimationGIF(t *testing.T) {
err: ErrInvalidImageSrc,
},
{
name: "success (tooth 正方形、Disposal設定アリ)",
name: "success (mushroom 正方形、小サイズ)",
file: "mushroom.gif",
want: lo.Must(io.ReadAll(testutils.MustOpenGif("mushroom_resized.gif"))),
err: nil,
},
{
name: "success (tooth 正方形、DisposalBackground)",
file: "tooth.gif",
want: lo.Must(io.ReadAll(testutils.MustOpenGif("tooth_resized.gif"))),
err: nil,
Expand All @@ -109,6 +115,12 @@ func TestProcessorDefault_FitAnimationGIF(t *testing.T) {
want: lo.Must(io.ReadAll(testutils.MustOpenGif("miku_resized.gif"))),
err: nil,
},
{
name: "success (frog 縦長、DisposalBackground + 背景色不整合)",
file: "frog.gif",
want: lo.Must(io.ReadAll(testutils.MustOpenGif("frog_resized.gif"))),
err: nil,
},
}

for _, tt := range test {
Expand Down
5 changes: 5 additions & 0 deletions testdata/gif/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ var FS embed.FS

// GIF画像 出典

// frog.gif https://sozai-good.com/illust/gifanimation/29065

// miku.gif https://piapro.jp/t/FB3J
// marucaさんの作品

// mushroom.gif http://www.ugokue.com
// 【動け!!動く絵】様より

// new_year.gif https://freesozaixtrain.web.fc2.com/freesozai-nenga-train2.html

// tooth.gif https://patirabi.com/2021/10/10/061gif/
Binary file added testdata/gif/frog.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added testdata/gif/frog_resized.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added testdata/gif/mushroom.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added testdata/gif/mushroom_resized.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d9006fd

Please sign in to comment.