Added SaveProfileImage endpoint

Also added EncodeToBestSize func
This commit is contained in:
Samuel Hawksby-Robinson 2020-10-22 16:27:58 +01:00 committed by Andrea Maria Piana
parent 9bc240fc71
commit 3b7fbf94d3
6 changed files with 122 additions and 15 deletions

View File

@ -1,19 +1,57 @@
package images package images
import ( import (
"bytes"
"fmt"
"image" "image"
"image/jpeg" "image/jpeg"
"io" "io"
) )
func Encode(w io.Writer, img image.Image, imgDetail *Details) error { type EncodeConfig struct {
// Currently a wrapper for renderJpeg, but this function is useful if multiple render formats are needed Quality int
return renderJpeg(w, img, imgDetail)
} }
func renderJpeg(w io.Writer, m image.Image, imgDetail *Details) error { func Encode(w io.Writer, img image.Image, config EncodeConfig) error {
// Currently a wrapper for renderJpeg, but this function is useful if multiple render formats are needed
return renderJpeg(w, img, config)
}
func renderJpeg(w io.Writer, m image.Image, config EncodeConfig) error {
o := new(jpeg.Options) o := new(jpeg.Options)
o.Quality = imgDetail.Quality o.Quality = config.Quality
return jpeg.Encode(w, m, o) return jpeg.Encode(w, m, o)
} }
func EncodeToBestSize(bb *bytes.Buffer, img image.Image, size uint) error {
// TODO test
q := MaxJpegQuality
for q > MinJpegQuality-1 {
err := Encode(bb, img, EncodeConfig{Quality: q})
if err != nil {
return err
}
if DimensionSizeLimit[size].Ideal > bb.Len() {
return nil
}
if q == MinJpegQuality {
if DimensionSizeLimit[size].Max > bb.Len(){
return nil
} else {
return fmt.Errorf(
"image size after processing exceeds max, expect < '%d', received < '%d'",
DimensionSizeLimit[size].Max,
bb.Len(),
)
}
}
q -= 2
}
return nil
}

View File

@ -29,7 +29,7 @@ func TestEncode(t *testing.T) {
5834, 5834,
}, },
} }
options := Details{ options := EncodeConfig{
Quality: 70, Quality: 70,
} }
@ -38,7 +38,7 @@ func TestEncode(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
bb := bytes.NewBuffer([]byte{}) bb := bytes.NewBuffer([]byte{})
err = Encode(bb, img, &options) err = Encode(bb, img, options)
require.NoError(t, err) require.NoError(t, err)
require.Exactly(t, c.RenderSize, bb.Len()) require.Exactly(t, c.RenderSize, bb.Len())

View File

@ -1 +1,36 @@
package images package images
import (
"bytes"
"image"
)
func GenerateProfileImages(filepath string, aX, aY, bX, bY int) ([][]byte, error) {
img, err := Decode(filepath)
if err != nil {
return nil, err
}
cropRect := image.Rectangle{
Min: image.Point{X: aX, Y: aY},
Max: image.Point{X: bX, Y: bY},
}
cImg, err := Crop(img, cropRect)
if err != nil {
return nil, err
}
imgs := make([][]byte, len(ResizeDimensions))
for _, s := range ResizeDimensions {
rImg := Resize(s, cImg)
bb := bytes.NewBuffer([]byte{})
err = EncodeToBestSize(bb, rImg, s)
if err != nil {
return nil, err
}
imgs = append(imgs, bb.Bytes())
}
return imgs, nil
}

View File

@ -118,7 +118,7 @@ func TestCrop(t *testing.T) {
Max: image.Point{X: 1000000, Y: 1000000}, Max: image.Point{X: 1000000, Y: 1000000},
} }
rect := image.Rectangle{} rect := image.Rectangle{}
options := &Details{ options := EncodeConfig{
Quality: 70, Quality: 70,
} }

View File

@ -10,13 +10,31 @@ const (
WEBP WEBP
) )
type Details struct { const (
SizePixel uint MaxJpegQuality = 80
SizeFile int64 MinJpegQuality = 50
Quality int )
FileName string
Properties string var (
ResizeDimensions = []uint{80, 240}
// DimensionSizeLimit the size limits imposed on each resize dimension
// Figures are based on the following sample data https://github.com/status-im/status-react/issues/11047#issuecomment-694970473
DimensionSizeLimit = map[uint]DimensionSize{
80: {
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)
},
240: {
Ideal: 16384, // Base on the largest sample image at quality 60% (16,143 bytes ∴ 1024 * 16)
Max: 38400, // Base on the largest sample image at quality 80% + 50% margin (24,290 bytes * 1.5 ≈ 37500 ∴ 1024 * 37.5)
},
}
)
type DimensionSize struct {
Ideal int
Max int
} }
type FileType uint type FileType uint

View File

@ -1,10 +1,13 @@
package statusgo package statusgo
import ( import (
"bytes"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/status-im/status-go/images"
"image"
"os" "os"
"unsafe" "unsafe"
@ -628,3 +631,16 @@ func MultiformatDeserializePublicKey(key, outBase string) string {
return pk return pk
} }
// SaveProfileImage takes the filepath of an image, crops it as per the rect coords and finally resizes the image.
// The resulting image(s) will be stored in the DB along with other user account information.
// aX and aY represent the pixel coordinates of the upper left corner of the image's cropping area
// bX and bY represent the pixel coordinates of the lower right corner of the image's cropping area
func SaveProfileImage(filepath string, aX, aY, bX, bY int) string {
imgs, err := images.GenerateProfileImages(filepath, aX, aY, bX, bY)
if err != nil {
return makeJSONResponse(err)
}
}