-
-
Notifications
You must be signed in to change notification settings - Fork 5.9k
Improve avatar uploading / resizing / compressing, remove Fomantic card module #24653
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
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
7b679b3
fix
wxiaoguang 8f3a71c
support webp animation
wxiaoguang d40c7e6
improve webp detection
wxiaoguang 4702918
fix
wxiaoguang 34868ce
Update modules/avatar/avatar.go
wxiaoguang 90f6e2e
remove unnecessary setting access
wxiaoguang ef79c9c
refactor
wxiaoguang ceeee5a
fine tune
wxiaoguang ab70def
improve comment
wxiaoguang d6e10c5
fix comment typo
wxiaoguang 0bfed61
Apply suggestions from code review
wxiaoguang 865f18e
Update docs/content/doc/administration/config-cheat-sheet.en-us.md
wxiaoguang 9c2eb7a
fix typo in comment
wxiaoguang b79d492
double default AVATAR_MAX_FILE_SIZE to 2MiB
silverwind 85e3fb1
double AVATAR_MAX_ORIGIN_SIZE to 256KiB
silverwind ceb44e5
reduce AVATAR_RENDERED_SIZE_FACTOR to 2
silverwind 60410f9
increase AVATAR_MAX_HEIGHT to 4096
silverwind 1ac0d77
take into account setting.Avatar.RenderedSizeFactor when scaling, red…
silverwind c3ac10f
revert AVATAR_MAX_FILE_SIZE to 1MiB
silverwind c32812f
update comment
silverwind b916c2a
make fmt
silverwind eb3037d
replace number in test
silverwind 2b8133c
Update templates/user/profile.tmpl
wxiaoguang f80f89e
fix failed tests
wxiaoguang 52fb4db
remove fomantic card module and extract all needed styles, fix avatar…
silverwind 68047ce
add comment
silverwind 5288391
avatar page fixes
silverwind 7941519
more fixes, migrate is now pixel-identical
silverwind 04c5fde
move card styles to own file
silverwind File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,13 +5,14 @@ package avatar | |
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"image" | ||
"image/color" | ||
"image/png" | ||
|
||
_ "image/gif" // for processing gif images | ||
_ "image/jpeg" // for processing jpeg images | ||
_ "image/png" // for processing png images | ||
|
||
"code.gitea.io/gitea/modules/avatar/identicon" | ||
"code.gitea.io/gitea/modules/setting" | ||
|
@@ -22,8 +23,11 @@ import ( | |
_ "golang.org/x/image/webp" // for processing webp images | ||
) | ||
|
||
// AvatarSize returns avatar's size | ||
const AvatarSize = 290 | ||
// DefaultAvatarSize is the target CSS pixel size for avatar generation. It is | ||
// multiplied by setting.Avatar.RenderedSizeFactor and the resulting size is the | ||
// usual size of avatar image saved on server, unless the original file is smaller | ||
// than the size after resizing. | ||
const DefaultAvatarSize = 256 | ||
silverwind marked this conversation as resolved.
Show resolved
Hide resolved
silverwind marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// RandomImageSize generates and returns a random avatar image unique to input data | ||
// in custom size (height and width). | ||
|
@@ -39,28 +43,44 @@ func RandomImageSize(size int, data []byte) (image.Image, error) { | |
// RandomImage generates and returns a random avatar image unique to input data | ||
// in default size (height and width). | ||
func RandomImage(data []byte) (image.Image, error) { | ||
return RandomImageSize(AvatarSize, data) | ||
return RandomImageSize(DefaultAvatarSize*setting.Avatar.RenderedSizeFactor, data) | ||
} | ||
|
||
// Prepare accepts a byte slice as input, validates it contains an image of an | ||
// acceptable format, and crops and resizes it appropriately. | ||
func Prepare(data []byte) (*image.Image, error) { | ||
imgCfg, _, err := image.DecodeConfig(bytes.NewReader(data)) | ||
// processAvatarImage process the avatar image data, crop and resize it if necessary. | ||
// the returned data could be the original image if no processing is needed. | ||
func processAvatarImage(data []byte, maxOriginSize int64) ([]byte, error) { | ||
imgCfg, imgType, err := image.DecodeConfig(bytes.NewReader(data)) | ||
if err != nil { | ||
return nil, fmt.Errorf("DecodeConfig: %w", err) | ||
return nil, fmt.Errorf("image.DecodeConfig: %w", err) | ||
} | ||
|
||
// for safety, only accept known types explicitly | ||
if imgType != "png" && imgType != "jpeg" && imgType != "gif" && imgType != "webp" { | ||
return nil, errors.New("unsupported avatar image type") | ||
} | ||
|
||
// do not process image which is too large, it would consume too much memory | ||
if imgCfg.Width > setting.Avatar.MaxWidth { | ||
return nil, fmt.Errorf("Image width is too large: %d > %d", imgCfg.Width, setting.Avatar.MaxWidth) | ||
return nil, fmt.Errorf("image width is too large: %d > %d", imgCfg.Width, setting.Avatar.MaxWidth) | ||
} | ||
if imgCfg.Height > setting.Avatar.MaxHeight { | ||
return nil, fmt.Errorf("Image height is too large: %d > %d", imgCfg.Height, setting.Avatar.MaxHeight) | ||
return nil, fmt.Errorf("image height is too large: %d > %d", imgCfg.Height, setting.Avatar.MaxHeight) | ||
Comment on lines
+59
to
+67
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
// If the origin is small enough, just use it, then APNG could be supported, | ||
// otherwise, if the image is processed later, APNG loses animation. | ||
// And one more thing, webp is not fully supported, for animated webp, image.DecodeConfig works but Decode fails. | ||
// So for animated webp, if the uploaded file is smaller than maxOriginSize, it will be used, if it's larger, there will be an error. | ||
if len(data) < int(maxOriginSize) { | ||
return data, nil | ||
} | ||
|
||
img, _, err := image.Decode(bytes.NewReader(data)) | ||
if err != nil { | ||
return nil, fmt.Errorf("Decode: %w", err) | ||
return nil, fmt.Errorf("image.Decode: %w", err) | ||
} | ||
|
||
// try to crop and resize the origin image if necessary | ||
if imgCfg.Width != imgCfg.Height { | ||
var newSize, ax, ay int | ||
if imgCfg.Width > imgCfg.Height { | ||
|
@@ -74,13 +94,33 @@ func Prepare(data []byte) (*image.Image, error) { | |
img, err = cutter.Crop(img, cutter.Config{ | ||
Width: newSize, | ||
Height: newSize, | ||
Anchor: image.Point{ax, ay}, | ||
Anchor: image.Point{X: ax, Y: ay}, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
img = resize.Resize(AvatarSize, AvatarSize, img, resize.Bilinear) | ||
return &img, nil | ||
targetSize := uint(DefaultAvatarSize * setting.Avatar.RenderedSizeFactor) | ||
img = resize.Resize(targetSize, targetSize, img, resize.Bilinear) | ||
|
||
// try to encode the cropped/resized image to png | ||
bs := bytes.Buffer{} | ||
if err = png.Encode(&bs, img); err != nil { | ||
return nil, err | ||
} | ||
resized := bs.Bytes() | ||
|
||
// usually the png compression is not good enough, use the original image (no cropping/resizing) if the origin is smaller | ||
if len(data) <= len(resized) { | ||
return data, nil | ||
} | ||
|
||
return resized, nil | ||
} | ||
|
||
// ProcessAvatarImage process the avatar image data, crop and resize it if necessary. | ||
// the returned data could be the original image if no processing is needed. | ||
func ProcessAvatarImage(data []byte) ([]byte, error) { | ||
return processAvatarImage(data, setting.Avatar.MaxOriginSize) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.