MM-49395: Increase compression for previews and emojis (#21985)
We also reduce the max size of uploaded emojis to 512KiB. https://mattermost.atlassian.net/browse/MM-49395 ```release-note Max size of uploaded emojis is now reduced to 512KiB to reduce image download bandwidth. ```
@@ -14,7 +14,6 @@ import (
|
|||||||
"image/draw"
|
"image/draw"
|
||||||
"image/gif"
|
"image/gif"
|
||||||
_ "image/jpeg"
|
_ "image/jpeg"
|
||||||
"image/png"
|
|
||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -31,7 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MaxEmojiFileSize = 1 << 20 // 1 MB
|
MaxEmojiFileSize = 1 << 19 // 512 KiB
|
||||||
MaxEmojiWidth = 128
|
MaxEmojiWidth = 128
|
||||||
MaxEmojiHeight = 128
|
MaxEmojiHeight = 128
|
||||||
MaxEmojiOriginalWidth = 1028
|
MaxEmojiOriginalWidth = 1028
|
||||||
@@ -155,8 +154,8 @@ func (a *App) UploadEmojiImage(c request.CTX, id string, imageData *multipart.Fi
|
|||||||
return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.decode_error", nil, "", http.StatusBadRequest).Wrap(err)
|
return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.decode_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resized_image := resizeEmoji(img, config.Width, config.Height)
|
resizedImg := resizeEmoji(img, config.Width, config.Height)
|
||||||
if err := png.Encode(newbuf, resized_image); err != nil {
|
if err := a.ch.imgEncoder.EncodePNG(newbuf, resizedImg); err != nil {
|
||||||
return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.encode_error", nil, "", http.StatusBadRequest).Wrap(err)
|
return model.NewAppError("uploadEmojiImage", "api.emoji.upload.large_image.encode_error", nil, "", http.StatusBadRequest).Wrap(err)
|
||||||
}
|
}
|
||||||
buf = newbuf
|
buf = newbuf
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ func NewEncoder(opts EncoderOptions) (*Encoder, error) {
|
|||||||
e.sem = make(chan struct{}, opts.ConcurrencyLevel)
|
e.sem = make(chan struct{}, opts.ConcurrencyLevel)
|
||||||
}
|
}
|
||||||
e.opts = opts
|
e.opts = opts
|
||||||
e.pngEncoder = &png.Encoder{}
|
e.pngEncoder = &png.Encoder{
|
||||||
|
CompressionLevel: png.BestCompression,
|
||||||
|
}
|
||||||
return &e, nil
|
return &e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ func TestAdjustProfileImage(t *testing.T) {
|
|||||||
assert.True(t, adjusted.Len() > 0)
|
assert.True(t, adjusted.Len() > 0)
|
||||||
assert.NotEqual(t, testjpg, adjusted)
|
assert.NotEqual(t, testjpg, adjusted)
|
||||||
|
|
||||||
// default image should require adjustment
|
// default image should not require adjustment
|
||||||
user := th.BasicUser
|
user := th.BasicUser
|
||||||
image, err := th.App.GetDefaultProfileImage(user)
|
image, err := th.App.GetDefaultProfileImage(user)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|||||||
@@ -160,7 +160,10 @@ func createProfileImage(username string, userID string, initialFont string) ([]b
|
|||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
if imgErr := png.Encode(buf, dstImg); imgErr != nil {
|
enc := png.Encoder{
|
||||||
|
CompressionLevel: png.BestCompression,
|
||||||
|
}
|
||||||
|
if imgErr := enc.Encode(buf, dstImg); imgErr != nil {
|
||||||
return nil, ImageEncodingError
|
return nil, ImageEncodingError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1719,7 +1719,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.emoji.create.too_large.app_error",
|
"id": "api.emoji.create.too_large.app_error",
|
||||||
"translation": "Unable to create emoji. Image must be less than 1 MB in size."
|
"translation": "Unable to create emoji. Image must be less than 512 KiB in size."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "api.emoji.disabled.app_error",
|
"id": "api.emoji.disabled.app_error",
|
||||||
|
|||||||
Двоичные данные
tests/10000x1_expected_preview.png
|
До Ширина: | Высота: | Размер: 92 B После Ширина: | Высота: | Размер: 92 B |
Двоичные данные
tests/10000x1_expected_thumb.png
|
До Ширина: | Высота: | Размер: 76 B После Ширина: | Высота: | Размер: 76 B |
Двоичные данные
tests/1x10000_expected_preview.png
|
До Ширина: | Высота: | Размер: 121 B После Ширина: | Высота: | Размер: 121 B |
Двоичные данные
tests/1x10000_expected_thumb.png
|
До Ширина: | Высота: | Размер: 79 B После Ширина: | Высота: | Размер: 79 B |
Двоичные данные
tests/fill_test_16bit_rgb_out.png
|
До Ширина: | Высота: | Размер: 1.2 KiB После Ширина: | Высота: | Размер: 1011 B |
Двоичные данные
tests/fill_test_16bit_rgba_out.png
|
До Ширина: | Высота: | Размер: 1.5 KiB После Ширина: | Высота: | Размер: 1.3 KiB |
Двоичные данные
tests/fill_test_8bit_palette_out.png
|
До Ширина: | Высота: | Размер: 140 B После Ширина: | Высота: | Размер: 140 B |
Двоичные данные
tests/fill_test_8bit_rgb_out.png
|
До Ширина: | Высота: | Размер: 896 B После Ширина: | Высота: | Размер: 700 B |
Двоичные данные
tests/fill_test_8bit_rgba_out.png
|
До Ширина: | Высота: | Размер: 1.1 KiB После Ширина: | Высота: | Размер: 897 B |