Added SaveProfileImage endpoint
Also added EncodeToBestSize func
This commit is contained in:
parent
9bc240fc71
commit
3b7fbf94d3
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue