diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2023-05-14 02:59:11 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-13 20:59:11 +0200 |
commit | 82224c54e0488738dbd3b7eccf56ab08b6790627 (patch) | |
tree | ba45f57d31a49186952afefb8448842656921026 /modules/avatar/avatar_test.go | |
parent | 9f1d377b87771ce2ef76caeef0aa649e768a24d7 (diff) | |
download | gitea-82224c54e0488738dbd3b7eccf56ab08b6790627.tar.gz gitea-82224c54e0488738dbd3b7eccf56ab08b6790627.zip |
Improve avatar uploading / resizing / compressing, remove Fomantic card module (#24653)
Fixes: #8972
Fixes: #24263
And I think it also (partially) fix #24263 (no need to convert) ,
because users could upload any supported image format if it isn't larger
than AVATAR_MAX_ORIGIN_SIZE
The main idea:
* if the uploaded file size is not larger than AVATAR_MAX_ORIGIN_SIZE,
use the origin
* if the resized size is larger than the origin, use the origin
Screenshots:
JPG:
<details>

</details>
APNG:
<details>


</details>
WebP (animated)
<details>

</details>
The only exception: if a WebP image is larger than MaxOriginSize and it
is animated, then current `webp` package can't decode it, so only in
this case it isn't supported. IMO no need to support such case: why a
user would upload a 1MB animated webp as avatar? crazy .....
---------
Co-authored-by: silverwind <me@silverwind.io>
Diffstat (limited to 'modules/avatar/avatar_test.go')
-rw-r--r-- | modules/avatar/avatar_test.go | 95 |
1 files changed, 79 insertions, 16 deletions
diff --git a/modules/avatar/avatar_test.go b/modules/avatar/avatar_test.go index 5ef4ed379b..a721c77868 100644 --- a/modules/avatar/avatar_test.go +++ b/modules/avatar/avatar_test.go @@ -4,6 +4,9 @@ package avatar import ( + "bytes" + "image" + "image/png" "os" "testing" @@ -25,49 +28,109 @@ func Test_RandomImage(t *testing.T) { assert.NoError(t, err) } -func Test_PrepareWithPNG(t *testing.T) { +func Test_ProcessAvatarPNG(t *testing.T) { setting.Avatar.MaxWidth = 4096 setting.Avatar.MaxHeight = 4096 data, err := os.ReadFile("testdata/avatar.png") assert.NoError(t, err) - imgPtr, err := Prepare(data) + _, err = processAvatarImage(data, 262144) assert.NoError(t, err) - - assert.Equal(t, 290, (*imgPtr).Bounds().Max.X) - assert.Equal(t, 290, (*imgPtr).Bounds().Max.Y) } -func Test_PrepareWithJPEG(t *testing.T) { +func Test_ProcessAvatarJPEG(t *testing.T) { setting.Avatar.MaxWidth = 4096 setting.Avatar.MaxHeight = 4096 data, err := os.ReadFile("testdata/avatar.jpeg") assert.NoError(t, err) - imgPtr, err := Prepare(data) + _, err = processAvatarImage(data, 262144) assert.NoError(t, err) - - assert.Equal(t, 290, (*imgPtr).Bounds().Max.X) - assert.Equal(t, 290, (*imgPtr).Bounds().Max.Y) } -func Test_PrepareWithInvalidImage(t *testing.T) { +func Test_ProcessAvatarInvalidData(t *testing.T) { setting.Avatar.MaxWidth = 5 setting.Avatar.MaxHeight = 5 - _, err := Prepare([]byte{}) - assert.EqualError(t, err, "DecodeConfig: image: unknown format") + _, err := processAvatarImage([]byte{}, 12800) + assert.EqualError(t, err, "image.DecodeConfig: image: unknown format") } -func Test_PrepareWithInvalidImageSize(t *testing.T) { +func Test_ProcessAvatarInvalidImageSize(t *testing.T) { setting.Avatar.MaxWidth = 5 setting.Avatar.MaxHeight = 5 data, err := os.ReadFile("testdata/avatar.png") assert.NoError(t, err) - _, err = Prepare(data) - assert.EqualError(t, err, "Image width is too large: 10 > 5") + _, err = processAvatarImage(data, 12800) + assert.EqualError(t, err, "image width is too large: 10 > 5") +} + +func Test_ProcessAvatarImage(t *testing.T) { + setting.Avatar.MaxWidth = 4096 + setting.Avatar.MaxHeight = 4096 + scaledSize := DefaultAvatarSize * setting.Avatar.RenderedSizeFactor + + newImgData := func(size int, optHeight ...int) []byte { + width := size + height := size + if len(optHeight) == 1 { + height = optHeight[0] + } + img := image.NewRGBA(image.Rect(0, 0, width, height)) + bs := bytes.Buffer{} + err := png.Encode(&bs, img) + assert.NoError(t, err) + return bs.Bytes() + } + + // if origin image canvas is too large, crop and resize it + origin := newImgData(500, 600) + result, err := processAvatarImage(origin, 0) + assert.NoError(t, err) + assert.NotEqual(t, origin, result) + decoded, err := png.Decode(bytes.NewReader(result)) + assert.NoError(t, err) + assert.EqualValues(t, scaledSize, decoded.Bounds().Max.X) + assert.EqualValues(t, scaledSize, decoded.Bounds().Max.Y) + + // if origin image is smaller than the default size, use the origin image + origin = newImgData(1) + result, err = processAvatarImage(origin, 0) + assert.NoError(t, err) + assert.Equal(t, origin, result) + + // use the origin image if the origin is smaller + origin = newImgData(scaledSize + 100) + result, err = processAvatarImage(origin, 0) + assert.NoError(t, err) + assert.Less(t, len(result), len(origin)) + + // still use the origin image if the origin doesn't exceed the max-origin-size + origin = newImgData(scaledSize + 100) + result, err = processAvatarImage(origin, 262144) + assert.NoError(t, err) + assert.Equal(t, origin, result) + + // allow to use known image format (eg: webp) if it is small enough + origin, err = os.ReadFile("testdata/animated.webp") + assert.NoError(t, err) + result, err = processAvatarImage(origin, 262144) + assert.NoError(t, err) + assert.Equal(t, origin, result) + + // do not support unknown image formats, eg: SVG may contain embedded JS + origin = []byte("<svg></svg>") + _, err = processAvatarImage(origin, 262144) + assert.ErrorContains(t, err, "image: unknown format") + + // make sure the canvas size limit works + setting.Avatar.MaxWidth = 5 + setting.Avatar.MaxHeight = 5 + origin = newImgData(10) + _, err = processAvatarImage(origin, 262144) + assert.ErrorContains(t, err, "image width is too large: 10 > 5") } |