Implemented more comprehensive file compression to handle large files
This commit is contained in:
parent
2f15730003
commit
45b287370a
Binary file not shown.
After Width: | Height: | Size: 4.6 MiB |
|
@ -9,6 +9,8 @@ import (
|
|||
"image/jpeg"
|
||||
"io"
|
||||
"regexp"
|
||||
|
||||
"github.com/nfnt/resize"
|
||||
)
|
||||
|
||||
type EncodeConfig struct {
|
||||
|
@ -27,7 +29,7 @@ func renderJpeg(w io.Writer, m image.Image, config EncodeConfig) error {
|
|||
return jpeg.Encode(w, m, o)
|
||||
}
|
||||
|
||||
func EncodeToLimits(bb *bytes.Buffer, img image.Image, bounds DimensionLimits) error {
|
||||
func EncodeToLimits(bb *bytes.Buffer, img image.Image, bounds FileSizeLimits) error {
|
||||
q := MaxJpegQuality
|
||||
for q > MinJpegQuality-1 {
|
||||
|
||||
|
@ -58,6 +60,40 @@ func EncodeToLimits(bb *bytes.Buffer, img image.Image, bounds DimensionLimits) e
|
|||
return nil
|
||||
}
|
||||
|
||||
// CompressToFileLimits takes an image.Image and analyses the pixel dimensions, if the longest side is greater
|
||||
// than the `longSideMax` image.Image will be resized, before compression begins.
|
||||
// Next the image.Image is repeatedly encoded and resized until the data fits within
|
||||
// the given FileSizeLimits. There is no limit on the number of times the cycle is performed, the image.Image
|
||||
// is reduced to 95% of its size at the end of every round the file size exceeds the given limits.
|
||||
func CompressToFileLimits(bb *bytes.Buffer, img image.Image, bounds FileSizeLimits) error {
|
||||
longSideMax := 2000
|
||||
|
||||
// Do we need to do a pre-compression resize?
|
||||
if img.Bounds().Max.X > img.Bounds().Max.Y {
|
||||
// X is longer
|
||||
if img.Bounds().Max.X > longSideMax {
|
||||
img = resize.Resize(uint(longSideMax), 0, img, resize.Bilinear)
|
||||
}
|
||||
} else {
|
||||
// Y is longer or equal
|
||||
if img.Bounds().Max.Y > longSideMax {
|
||||
img = resize.Resize(0, uint(longSideMax), img, resize.Bilinear)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
err := EncodeToLimits(bb, img, bounds)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if err.Error()[:50] != "image size after processing exceeds max, expect < " {
|
||||
return err
|
||||
}
|
||||
|
||||
img = ResizeTo(95, img)
|
||||
}
|
||||
}
|
||||
|
||||
func EncodeToBestSize(bb *bytes.Buffer, img image.Image, size ResizeDimension) error {
|
||||
return EncodeToLimits(bb, img, DimensionSizeLimit[size])
|
||||
}
|
||||
|
|
|
@ -90,3 +90,13 @@ func TestEncodeToBestSize(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressToFileLimits(t *testing.T) {
|
||||
img, err := Decode(path + "IMG_1205.HEIC.jpg")
|
||||
require.NoError(t, err)
|
||||
|
||||
bb := bytes.NewBuffer([]byte{})
|
||||
err = CompressToFileLimits(bb, img, FileSizeLimits{50000, 350000})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 291645, bb.Len())
|
||||
}
|
||||
|
|
|
@ -80,8 +80,7 @@ func GenerateBannerImage(filepath string, aX, aY, bX, bY int) (*IdentityImage, e
|
|||
return nil, err
|
||||
}
|
||||
|
||||
dimension := BannerDim
|
||||
resizedImg := ShrinkOnly(dimension, croppedImg)
|
||||
resizedImg := ShrinkOnly(BannerDim, croppedImg)
|
||||
|
||||
sizeLimits := GetBannerDimensionLimits()
|
||||
|
||||
|
@ -97,7 +96,7 @@ func GenerateBannerImage(filepath string, aX, aY, bX, bY int) (*IdentityImage, e
|
|||
Width: resizedImg.Bounds().Dx(),
|
||||
Height: resizedImg.Bounds().Dy(),
|
||||
FileSize: bb.Len(),
|
||||
ResizeTarget: int(dimension),
|
||||
ResizeTarget: int(BannerDim),
|
||||
}
|
||||
|
||||
return ii, nil
|
||||
|
|
|
@ -28,6 +28,13 @@ func Resize(size ResizeDimension, img image.Image) image.Image {
|
|||
return resize.Resize(width, height, img, resize.Bilinear)
|
||||
}
|
||||
|
||||
func ResizeTo(percent int, img image.Image) image.Image {
|
||||
width := uint(img.Bounds().Max.X * percent / 100)
|
||||
height := uint(img.Bounds().Max.Y * percent / 100)
|
||||
|
||||
return resize.Resize(width, height, img, resize.Bilinear)
|
||||
}
|
||||
|
||||
func ShrinkOnly(size ResizeDimension, img image.Image) image.Image {
|
||||
finalSize := int(math.Min(float64(size), math.Min(float64(img.Bounds().Dx()), float64(img.Bounds().Dy()))))
|
||||
return Resize(ResizeDimension(finalSize), img)
|
||||
|
@ -50,9 +57,9 @@ func Crop(img image.Image, rect image.Rectangle) (image.Image, error) {
|
|||
})
|
||||
}
|
||||
|
||||
// CropImage takes an image, usually downloaded from a URL
|
||||
// CropCenter takes an image, usually downloaded from a URL
|
||||
// If the image is square, the full image is returned
|
||||
// It the image is rectangular, the largest central square is returned
|
||||
// If the image is rectangular, the largest central square is returned
|
||||
// calculations at _docs/image-center-crop-calculations.png
|
||||
func CropCenter(img image.Image) (image.Image, error) {
|
||||
var cropRect image.Rectangle
|
||||
|
|
|
@ -31,7 +31,7 @@ var (
|
|||
|
||||
// DimensionSizeLimit the size limits imposed on each resize dimension
|
||||
// Figures are based on the following sample data https://github.com/status-im/status-mobile/issues/11047#issuecomment-694970473
|
||||
DimensionSizeLimit = map[ResizeDimension]DimensionLimits{
|
||||
DimensionSizeLimit = map[ResizeDimension]FileSizeLimits{
|
||||
SmallDim: {
|
||||
Ideal: 2560, // Base on the largest sample image at quality 60% (2,554 bytes ∴ 1024 * 2.5)
|
||||
Max: 5632, // Base on the largest sample image at quality 80% + 50% margin (3,683 bytes * 1.5 ≈ 5500 ∴ 1024 * 5.5)
|
||||
|
@ -55,7 +55,7 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
type DimensionLimits struct {
|
||||
type FileSizeLimits struct {
|
||||
Ideal int
|
||||
Max int
|
||||
}
|
||||
|
@ -63,8 +63,8 @@ type DimensionLimits struct {
|
|||
type ImageType uint
|
||||
type ResizeDimension uint
|
||||
|
||||
func GetBannerDimensionLimits() DimensionLimits {
|
||||
return DimensionLimits{
|
||||
func GetBannerDimensionLimits() FileSizeLimits {
|
||||
return FileSizeLimits{
|
||||
Ideal: 307200, // We want to save space and traffic but keep to maximum compression
|
||||
Max: 460800, // Can't go bigger than 450 KB
|
||||
}
|
||||
|
|
|
@ -2631,7 +2631,7 @@ func (m *Messenger) OpenAndAdjustImage(inputImage userimage.CroppedImage, crop b
|
|||
}
|
||||
|
||||
bb := bytes.NewBuffer([]byte{})
|
||||
err = userimage.EncodeToLimits(bb, img, userimage.DimensionLimits{Ideal: idealTargetImageSize, Max: resizeTargetImageSize})
|
||||
err = userimage.CompressToFileLimits(bb, img, userimage.FileSizeLimits{Ideal: idealTargetImageSize, Max: resizeTargetImageSize})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in New Issue