test image manipulation

This commit is contained in:
tsmethurst
2021-03-30 13:41:44 +02:00
parent 39aca2025f
commit 572149fa0e
12 changed files with 159 additions and 41 deletions

View File

@ -66,6 +66,7 @@ type HeaderInfo struct {
/*
INTERFACE FUNCTIONS
*/
func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(f io.Reader, accountID string, headerOrAvi string) (*model.MediaAttachment, error) {
l := mh.log.WithField("func", "SetHeaderForAccountID")
@ -104,7 +105,15 @@ func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(f io.Reader, accountID str
*/
func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, headerOrAvi string, accountID string) (*model.MediaAttachment, error) {
if headerOrAvi != "header" && headerOrAvi != "avatar" {
var isHeader bool
var isAvatar bool
switch headerOrAvi {
case "header":
isHeader = true
case "avatar":
isAvatar = true
default:
return nil, errors.New("header or avatar not selected")
}
@ -149,7 +158,7 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
ID: newMediaID,
StatusID: "",
RemoteURL: "",
Type: "",
Type: model.FileTypeImage,
FileMeta: model.ImageFileMeta{
Original: model.ImageOriginal{
Width: original.width,
@ -167,7 +176,7 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
AccountID: accountID,
Description: "",
ScheduledStatusID: "",
Blurhash: "",
Blurhash: original.blurhash,
Processing: 2,
File: model.File{
Path: originalPath,
@ -180,6 +189,8 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
FileSize: len(small.image),
RemoteURL: "",
},
Avatar: isAvatar,
Header: isHeader,
}
return ma, nil

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -28,9 +28,10 @@ import (
"image/png"
"io"
"github.com/buckket/go-blurhash"
"github.com/h2non/filetype"
"github.com/nfnt/resize"
exifremove "github.com/scottleedavis/go-exif-remove"
"github.com/superseriousbusiness/exifremove/pkg/exifremove"
)
// parseContentType parses the MIME content type from a file, returning it as a string in the form (eg., "image/jpeg").
@ -73,7 +74,18 @@ func supportedImageType(mimeType string) bool {
// purgeExif is a little wrapper for the action of removing exif data from an image.
// Only pass pngs or jpegs to this function.
func purgeExif(b []byte) ([]byte, error) {
return exifremove.Remove(b)
if b == nil || len(b) == 0 {
return nil, errors.New("passed image was not valid")
}
clean, err := exifremove.Remove(b)
if err != nil {
return nil, fmt.Errorf("could not purge exif from image: %s", err)
}
if clean == nil || len(clean) == 0 {
return nil, errors.New("purged image was not valid")
}
return clean, nil
}
func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
@ -104,21 +116,26 @@ func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
height := i.Bounds().Size().Y
size := width * height
aspect := float64(width) / float64(height)
bh, err := blurhash.Encode(4, 3, i)
if err != nil {
return nil, fmt.Errorf("error generating blurhash: %s", err)
}
out := &bytes.Buffer{}
if err := jpeg.Encode(out, i, nil); err != nil {
return nil, err
}
return &imageAndMeta{
image: out.Bytes(),
width: width,
height: height,
size: size,
aspect: aspect,
image: out.Bytes(),
width: width,
height: height,
size: size,
aspect: aspect,
blurhash: bh,
}, nil
}
// deriveThumbnailFromImage returns a byte slice of an 80-pixel-width thumbnail
// deriveThumbnailFromImage returns a byte slice and metadata for a 256-pixel-width thumbnail
// of a given jpeg, png, or gif, or an error if something goes wrong.
//
// Note that the aspect ratio of the image will be retained,
@ -147,7 +164,7 @@ func deriveThumbnail(b []byte, extension string) (*imageAndMeta, error) {
return nil, fmt.Errorf("extension %s not recognised", extension)
}
thumb := resize.Thumbnail(80, 0, i, resize.NearestNeighbor)
thumb := resize.Thumbnail(256, 256, i, resize.NearestNeighbor)
width := thumb.Bounds().Size().X
height := thumb.Bounds().Size().Y
size := width * height
@ -167,9 +184,10 @@ func deriveThumbnail(b []byte, extension string) (*imageAndMeta, error) {
}
type imageAndMeta struct {
image []byte
width int
height int
size int
aspect float64
image []byte
width int
height int
size int
aspect float64
blurhash string
}

View File

@ -19,10 +19,12 @@
package media
import (
"io/ioutil"
"os"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
@ -61,13 +63,76 @@ func (suite *MediaUtilTestSuite) TearDownTest() {
ACTUAL TESTS
*/
func (suite *MediaUtilTestSuite) TestParseContentType() {
func (suite *MediaUtilTestSuite) TestParseContentTypeOK() {
f, err := os.Open("./test/test-jpeg.jpg")
if err != nil {
suite.FailNow(err.Error())
}
assert.Nil(suite.T(), err)
ct, err := parseContentType(f)
suite.log.Debug(ct)
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), "image/jpeg", ct)
}
func (suite *MediaUtilTestSuite) TestParseContentTypeNotOK() {
f, err := os.Open("./test/test-corrupted.jpg")
assert.Nil(suite.T(), err)
ct, err := parseContentType(f)
assert.NotNil(suite.T(), err)
assert.Equal(suite.T(), "", ct)
assert.Equal(suite.T(), "filetype unknown", err.Error())
}
func (suite *MediaUtilTestSuite) TestRemoveEXIF() {
// load and validate image
b, err := ioutil.ReadFile("./test/test-with-exif.jpg")
assert.Nil(suite.T(), err)
// clean it up and validate the clean version
clean, err := purgeExif(b)
assert.Nil(suite.T(), err)
// compare it to our stored sample
sampleBytes, err := ioutil.ReadFile("./test/test-without-exif.jpg")
assert.Nil(suite.T(), err)
assert.EqualValues(suite.T(), sampleBytes, clean)
}
func (suite *MediaUtilTestSuite) TestDeriveImageFromJPEG() {
// load image
b, err := ioutil.ReadFile("./test/test-jpeg.jpg")
assert.Nil(suite.T(), err)
// clean it up and validate the clean version
imageAndMeta, err := deriveImage(b, "image/jpeg")
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), 1920, imageAndMeta.width)
assert.Equal(suite.T(), 1080, imageAndMeta.height)
assert.Equal(suite.T(), 1.7777777777777777, imageAndMeta.aspect)
assert.Equal(suite.T(), 2073600, imageAndMeta.size)
assert.Equal(suite.T(), "LjCZnlvyRkRn_NvzRjWF?urqV@f9", imageAndMeta.blurhash)
// assert that the final image is what we would expect
sampleBytes, err := ioutil.ReadFile("./test/test-jpeg-processed.jpg")
assert.Nil(suite.T(), err)
assert.EqualValues(suite.T(), sampleBytes, imageAndMeta.image)
}
func (suite *MediaUtilTestSuite) TestDeriveThumbnailFromJPEG() {
// load image
b, err := ioutil.ReadFile("./test/test-jpeg.jpg")
assert.Nil(suite.T(), err)
// clean it up and validate the clean version
imageAndMeta, err := deriveThumbnail(b, "image/jpeg")
assert.Nil(suite.T(), err)
assert.Equal(suite.T(), 256, imageAndMeta.width)
assert.Equal(suite.T(), 144, imageAndMeta.height)
assert.Equal(suite.T(), 1.7777777777777777, imageAndMeta.aspect)
assert.Equal(suite.T(), 36864, imageAndMeta.size)
sampleBytes, err := ioutil.ReadFile("./test/test-jpeg-thumbnail.jpg")
assert.Nil(suite.T(), err)
assert.EqualValues(suite.T(), sampleBytes, imageAndMeta.image)
}
func TestMediaUtilTestSuite(t *testing.T) {

View File

@ -26,8 +26,8 @@ type Storage interface {
}
type FileInfo struct {
Data []byte
StorePath string
CreatedAt time.Time
UpdatedAt time.Time
}
Data []byte
StorePath string
CreatedAt time.Time
UpdatedAt time.Time
}