Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imagemagickを抹殺 #2143

Merged
merged 41 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
955bc1b
reimplement gif resize func with pure go
logica0419 Dec 7, 2023
841930d
delete imagemagick utils
logica0419 Dec 7, 2023
8ab4114
using ErrInvalidImageSrc
logica0419 Dec 7, 2023
6e61063
remove redundant error
logica0419 Dec 7, 2023
cbcab23
delete imagemagick path from every config
logica0419 Dec 7, 2023
4b6339b
return error in convert util
logica0419 Dec 8, 2023
afc37e2
make variable names understandable
logica0419 Dec 9, 2023
eba7e4f
fix render gif
logica0419 Dec 9, 2023
b6fae9b
fix bound calculation
logica0419 Dec 9, 2023
fd2f92f
rename vars
logica0419 Dec 9, 2023
250cf6e
using imaging package
logica0419 Dec 9, 2023
0daa012
round coordinates
logica0419 Dec 10, 2023
e9ddab2
resize composite frame
logica0419 Dec 10, 2023
74ab087
write comments
logica0419 Dec 10, 2023
c925749
initialize Image slice
logica0419 Dec 10, 2023
719a37e
delete imagemagick from Dockerfile
logica0419 Dec 10, 2023
e907dbf
parallelize animated gif resize
logica0419 Dec 10, 2023
2185fc8
power up concurrency
logica0419 Dec 10, 2023
8b1431d
put out type definition
logica0419 Dec 10, 2023
0c8d8b0
rename package alias
logica0419 Dec 10, 2023
78f085a
prepare IoReaderToBytes util
logica0419 Dec 10, 2023
33ddae6
add gif testdata
logica0419 Dec 10, 2023
708fc5f
write test for FitAnimationGIF
logica0419 Dec 10, 2023
d13c886
add testcases
logica0419 Dec 10, 2023
3471bbf
changed implementation
logica0419 Dec 10, 2023
113444a
moved MustOpenGif to testutils
logica0419 Dec 10, 2023
9e684a5
write test for GifToBytesReader
logica0419 Dec 10, 2023
cebe6bd
parallelize test
logica0419 Dec 10, 2023
4b7a666
fix test naming
logica0419 Dec 10, 2023
662751b
Revert "power up concurrency"
logica0419 Dec 10, 2023
4302fa8
fix concurrency limit setting
logica0419 Dec 10, 2023
257fa13
not testing concurrency pattern
logica0419 Dec 11, 2023
6cb6c02
support disposal method
logica0419 Dec 11, 2023
3f0350d
using only free images in testing
logica0419 Dec 11, 2023
3f2c8d9
replace buggy image
logica0419 Dec 11, 2023
80932b2
replace names
logica0419 Dec 11, 2023
5e061b4
fix test file names & files
logica0419 Dec 11, 2023
fa38f02
refactor resize goroutine starting
logica0419 Dec 11, 2023
53cc3d9
use deep copy to avoid
logica0419 Dec 11, 2023
80ec7b5
moved type definition readability
logica0419 Dec 11, 2023
95c6d03
use io.ReadAll & lo.Must
logica0419 Dec 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ type Config struct {
Enabled bool `mapstructure:"enabled" yaml:"enabled"`
} `mapstructure:"accessLog" yaml:"accessLog"`

// ImageMagick ImageMagick実行ファイルパス
ImageMagick string `mapstructure:"imagemagick" yaml:"imagemagick"`

// Imaging 画像処理設定
Imaging struct {
// MaxPixels 処理可能な最大画素数 (default: 2560*1600)
Expand Down Expand Up @@ -263,7 +260,6 @@ func init() {
viper.SetDefault("gzip", true)
viper.SetDefault("allowSignUp", false)
viper.SetDefault("accessLog.enabled", true)
viper.SetDefault("imagemagick", "")
viper.SetDefault("imaging.maxPixels", 2560*1600)
viper.SetDefault("imaging.concurrency", 1)
viper.SetDefault("mariadb.host", "127.0.0.1")
Expand Down Expand Up @@ -470,7 +466,6 @@ func provideImageProcessorConfig(c *Config) imaging.Config {
MaxPixels: c.Imaging.MaxPixels,
Concurrency: c.Imaging.Concurrency,
ThumbnailMaxSize: image.Pt(360, 480),
ImageMagickPath: c.ImageMagick,
}
}

Expand Down
8 changes: 2 additions & 6 deletions router/utils/process_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/traPtitech/traQ/router/extension/herror"
"github.com/traPtitech/traQ/service/file"
imaging2 "github.com/traPtitech/traQ/service/imaging"
"github.com/traPtitech/traQ/utils/imaging"
)

const (
Expand Down Expand Up @@ -71,7 +70,7 @@ func saveUploadImage(p imaging2.Processor, c echo.Context, m file.Manager, name
}

// PNGに変換
var b = bytes.Buffer{}
b := bytes.Buffer{}
if err := png.Encode(&b, img); err != nil {
return uuid.Nil, herror.InternalServerError(err)
}
Expand All @@ -86,10 +85,7 @@ func saveUploadImage(p imaging2.Processor, c echo.Context, m file.Manager, name
b, err := p.FitAnimationGIF(src, maxImageSize, maxImageSize)
if err != nil {
switch err {
case imaging.ErrImageMagickUnavailable:
// gifは一時的にサポートされていない
return uuid.Nil, herror.BadRequest("gif file is temporarily unsupported")
case imaging2.ErrInvalidImageSrc, imaging2.ErrTimeout:
case imaging2.ErrInvalidImageSrc:
// 不正なgifである
return uuid.Nil, herror.BadRequest(badImage)
default:
Expand Down
1 change: 0 additions & 1 deletion router/v1/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ func TestMain(m *testing.M) {
MaxPixels: 1000 * 1000,
Concurrency: 1,
ThumbnailMaxSize: image.Pt(360, 480),
ImageMagickPath: "",
})
env.FileManager, _ = file.InitFileManager(env.Repository, storage.NewInMemoryFileStorage(), env.ImageProcessor, zap.NewNop())

Expand Down
1 change: 0 additions & 1 deletion router/v3/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ func TestMain(m *testing.M) {
MaxPixels: 1000 * 1000,
Concurrency: 1,
ThumbnailMaxSize: image.Pt(360, 480),
ImageMagickPath: "",
})
env.FM, _ = file.InitFileManager(repo, storage.NewInMemoryFileStorage(), env.IP, l.Named("FM"))

Expand Down
3 changes: 0 additions & 3 deletions service/imaging/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
var (
ErrPixelLimitExceeded = errors.New("the image exceeds max pixels limit")
ErrInvalidImageSrc = errors.New("invalid image src")
ErrTimeout = errors.New("processing timeout")
)

type Config struct {
Expand All @@ -19,6 +18,4 @@ type Config struct {
Concurrency int
// ThumbnailMaxSize サムネイル画像サイズ
ThumbnailMaxSize image.Point
// ImageMagickPath imagemagickの実行パス
ImageMagickPath string
}
47 changes: 29 additions & 18 deletions service/imaging/mks2013_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,34 @@ import (
"math"

"github.com/disintegration/imaging"
"golang.org/x/image/draw"
)

// Magic Kernel Sharp 2013
// http://johncostella.com/magic/
var mks2013Filter = imaging.ResampleFilter{
Support: 2.5,
Kernel: func(x float64) float64 {
x = math.Abs(x)
if x >= 2.5 {
return 0.0
}
if x >= 1.5 {
return -0.125 * (x - 2.5) * (x - 2.5)
}
if x >= 0.5 {
return 0.25 * (4*x*x - 11*x + 7)
}
return 1.0625 - 1.75*x*x
},
}
var (
// Magic Kernel Sharp 2013
// http://johncostella.com/magic/
mks2013Filter = imaging.ResampleFilter{
Support: 2.5,
Kernel: func(x float64) float64 {
x = math.Abs(x)
if x >= 2.5 {
return 0.0
}
if x >= 1.5 {
return -0.125 * (x - 2.5) * (x - 2.5)
}
if x >= 0.5 {
return 0.25 * (4*x*x - 11*x + 7)
}
return 1.0625 - 1.75*x*x
},
}

// mks2013Filterのdraw.Kernelへの型キャスト
// Magic Kernel Sharp 2013
// http://johncostella.com/magic/
mks2013FilterKernel = draw.Kernel{
Support: mks2013Filter.Support,
At: mks2013Filter.Kernel,
}
)
60 changes: 45 additions & 15 deletions service/imaging/processor_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import (
"context"
"fmt"
"image"
"image/gif"
_ "image/jpeg" // image.Decode用
_ "image/png" // image.Decode用
"io"
"time"
"math"

_ "golang.org/x/image/webp" // image.Decode用

"github.com/disintegration/imaging"
"github.com/go-audio/wav"
"github.com/hajimehoshi/go-mp3"
"github.com/motoki317/go-waveform"
"golang.org/x/image/draw"
"golang.org/x/sync/semaphore"

imaging2 "github.com/traPtitech/traQ/utils/imaging"
Expand Down Expand Up @@ -72,22 +74,50 @@ func (p *defaultProcessor) Fit(src io.ReadSeeker, width, height int) (image.Imag
return orig, nil
}

func (p *defaultProcessor) FitAnimationGIF(src io.Reader, width, height int) (*bytes.Reader, error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // 10秒以内に終わらないファイルは無効
defer cancel()

b, err := imaging2.ResizeAnimationGIF(ctx, p.c.ImageMagickPath, src, width, height, false)
func (p *defaultProcessor) FitAnimationGIF(srcFile io.Reader, width, height int) (*bytes.Reader, error) {
logica0419 marked this conversation as resolved.
Show resolved Hide resolved
src, err := gif.DecodeAll(srcFile)
if err != nil {
switch err {
case context.DeadlineExceeded:
return nil, ErrTimeout
case imaging2.ErrInvalidImageSrc:
return nil, ErrInvalidImageSrc
default:
return nil, err
}
return nil, ErrInvalidImageSrc
}

srcWidth, srcHeight := src.Config.Width, src.Config.Height
// 画素数チェック
if srcWidth*srcHeight > p.c.MaxPixels {
return nil, ErrPixelLimitExceeded
}
return b, nil
// 画像が十分小さければスキップ
if srcWidth <= width && srcHeight <= height {
return imaging2.GifToBytesReader(src)
}

// 元の比率を保つよう調整
floatSrcWidth, floatSrcHeight, floatWidth, floatHeight := float64(srcWidth), float64(srcHeight), float64(width), float64(height)
if floatSrcWidth/floatSrcHeight > floatWidth/floatHeight {
height = int(math.Round(floatSrcHeight * floatWidth / floatSrcWidth))
} else if floatSrcWidth/floatSrcHeight < floatWidth/floatHeight {
width = int(math.Round(floatSrcWidth * floatHeight / floatSrcHeight))
}

dst := &gif.GIF{
Delay: src.Delay,
LoopCount: src.LoopCount,
Disposal: src.Disposal,
Config: image.Config{
ColorModel: src.Config.ColorModel,
Width: width,
Height: height,
},
BackgroundIndex: src.BackgroundIndex,
}

targetBounds := image.Rect(0, 0, width, height)
for _, frame := range src.Image {
destFrame := image.NewPaletted(targetBounds, frame.Palette)
mks2013FilterKernel.Scale(destFrame, targetBounds, frame, frame.Bounds(), draw.Src, nil)
dst.Image = append(dst.Image, destFrame)
}

return imaging2.GifToBytesReader(dst)
}

func (p *defaultProcessor) WaveformMp3(src io.ReadSeeker, width, height int) (r io.Reader, err error) {
Expand Down
1 change: 0 additions & 1 deletion service/imaging/processor_default_test.go
logica0419 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func setup() (Processor, *os.File) {
MaxPixels: 500 * 500,
Concurrency: 1,
ThumbnailMaxSize: image.Point{50, 50},
ImageMagickPath: "",
})
return processor, mustOpen("test.png")
}
Expand Down
15 changes: 15 additions & 0 deletions utils/imaging/gif.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package imaging

import (
"bytes"
"image/gif"
)

// GifToBytesReader GIF画像を*bytes.Readerに書き出します
func GifToBytesReader(src *gif.GIF) (*bytes.Reader, error) {
buf := new(bytes.Buffer)
if err := gif.EncodeAll(buf, src); err != nil {
return nil, err
}
return bytes.NewReader(buf.Bytes()), nil
}
99 changes: 0 additions & 99 deletions utils/imaging/imagemagick.go

This file was deleted.

Loading
Loading