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
import (
"bytes"
"fmt"
"image"
"image/jpeg"
"io"
)
func Encode(w io.Writer, img image.Image, imgDetail *Details) error {
// Currently a wrapper for renderJpeg, but this function is useful if multiple render formats are needed
return renderJpeg(w, img, imgDetail)
type EncodeConfig struct {
Quality int
}
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.Quality = imgDetail.Quality
o.Quality = config.Quality
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,
},
}
options := Details{
options := EncodeConfig{
Quality: 70,
}
@ -38,7 +38,7 @@ func TestEncode(t *testing.T) {
require.NoError(t, err)
bb := bytes.NewBuffer([]byte{})
err = Encode(bb, img, &options)
err = Encode(bb, img, options)
require.NoError(t, err)
require.Exactly(t, c.RenderSize, bb.Len())

View File

@ -1 +1,36 @@
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},
}
rect := image.Rectangle{}
options := &Details{
options := EncodeConfig{
Quality: 70,
}

View File

@ -10,13 +10,31 @@ const (
WEBP
)
type Details struct {
SizePixel uint
SizeFile int64
Quality int
FileName string
Properties string
const (
MaxJpegQuality = 80
MinJpegQuality = 50
)
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

View File

@ -1,10 +1,13 @@
package statusgo
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/status-im/status-go/images"
"image"
"os"
"unsafe"
@ -628,3 +631,16 @@ func MultiformatDeserializePublicKey(key, outBase string) string {
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)
}
}