[MM-62191] Remove disintegration/imaging dependency (#29657)
* Remove disintegration/imaging dependency * Simplify FillCenter logic --------- Co-authored-by: Mattermost Build <build@mattermost.com>
@@ -18,7 +18,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/mattermost/mattermost/server/v8/channels/app/imaging"
|
||||||
_ "golang.org/x/image/webp"
|
_ "golang.org/x/image/webp"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost/server/public/model"
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
@@ -337,7 +337,7 @@ func resizeEmoji(img image.Image, width int, height int) image.Image {
|
|||||||
if emojiHeight <= MaxEmojiHeight && emojiWidth <= MaxEmojiWidth {
|
if emojiHeight <= MaxEmojiHeight && emojiWidth <= MaxEmojiWidth {
|
||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
return imaging.Fit(img, MaxEmojiWidth, MaxEmojiHeight, imaging.Lanczos)
|
return imaging.Fit(img, MaxEmojiWidth, MaxEmojiHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageToPaletted(img image.Image) *image.Paletted {
|
func imageToPaletted(img image.Image) *image.Paletted {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/anthonynsimon/bild/transform"
|
||||||
"github.com/rwcarlsen/goexif/exif"
|
"github.com/rwcarlsen/goexif/exif"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,19 +37,19 @@ const (
|
|||||||
func MakeImageUpright(img image.Image, orientation int) image.Image {
|
func MakeImageUpright(img image.Image, orientation int) image.Image {
|
||||||
switch orientation {
|
switch orientation {
|
||||||
case UprightMirrored:
|
case UprightMirrored:
|
||||||
return imaging.FlipH(img)
|
return transform.FlipH(img)
|
||||||
case UpsideDown:
|
case UpsideDown:
|
||||||
return imaging.Rotate180(img)
|
return transform.Rotate(img, 180, &transform.RotationOptions{ResizeBounds: true})
|
||||||
case UpsideDownMirrored:
|
case UpsideDownMirrored:
|
||||||
return imaging.FlipV(img)
|
return transform.FlipV(img)
|
||||||
case RotatedCWMirrored:
|
case RotatedCWMirrored:
|
||||||
return imaging.Transpose(img)
|
return transform.Rotate(transform.FlipH(img), -90, &transform.RotationOptions{ResizeBounds: true})
|
||||||
case RotatedCCW:
|
case RotatedCCW:
|
||||||
return imaging.Rotate270(img)
|
return transform.Rotate(img, 90, &transform.RotationOptions{ResizeBounds: true})
|
||||||
case RotatedCCWMirrored:
|
case RotatedCCWMirrored:
|
||||||
return imaging.Transverse(img)
|
return transform.Rotate(transform.FlipV(img), -90, &transform.RotationOptions{ResizeBounds: true})
|
||||||
case RotatedCW:
|
case RotatedCW:
|
||||||
return imaging.Rotate90(img)
|
return transform.Rotate(img, 270, &transform.RotationOptions{ResizeBounds: true})
|
||||||
default:
|
default:
|
||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/anthonynsimon/bild/transform"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GeneratePreview generates the preview for the given image.
|
// GeneratePreview generates the preview for the given image.
|
||||||
@@ -18,7 +18,7 @@ func GeneratePreview(img image.Image, width int) image.Image {
|
|||||||
w := img.Bounds().Dx()
|
w := img.Bounds().Dx()
|
||||||
|
|
||||||
if w > width {
|
if w > width {
|
||||||
preview = imaging.Resize(img, width, 0, imaging.Lanczos)
|
preview = Resize(img, width, 0, transform.Lanczos)
|
||||||
}
|
}
|
||||||
|
|
||||||
return preview
|
return preview
|
||||||
@@ -31,16 +31,16 @@ func GenerateThumbnail(img image.Image, targetWidth, targetHeight int) image.Ima
|
|||||||
|
|
||||||
// We keep aspect ratio and ensure the output dimensions are never higher than the provided targets.
|
// We keep aspect ratio and ensure the output dimensions are never higher than the provided targets.
|
||||||
if width > height {
|
if width > height {
|
||||||
return imaging.Resize(img, targetWidth, 0, imaging.Lanczos)
|
return Resize(img, targetWidth, 0, transform.Lanczos)
|
||||||
}
|
}
|
||||||
|
|
||||||
return imaging.Resize(img, 0, targetHeight, imaging.Lanczos)
|
return Resize(img, 0, targetHeight, transform.Lanczos)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateMiniPreviewImage generates the mini preview for the given image.
|
// GenerateMiniPreviewImage generates the mini preview for the given image.
|
||||||
func GenerateMiniPreviewImage(img image.Image, w, h, q int) ([]byte, error) {
|
func GenerateMiniPreviewImage(img image.Image, w, h, q int) ([]byte, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
preview := imaging.Resize(img, w, h, imaging.Lanczos)
|
preview := Resize(img, w, h, transform.Lanczos)
|
||||||
if err := jpeg.Encode(&buf, preview, &jpeg.Options{Quality: q}); err != nil {
|
if err := jpeg.Encode(&buf, preview, &jpeg.Options{Quality: q}); err != nil {
|
||||||
return nil, fmt.Errorf("failed to encode image to JPEG format: %w", err)
|
return nil, fmt.Errorf("failed to encode image to JPEG format: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ package imaging
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
|
"image/color"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anthonynsimon/bild/transform"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,3 +77,96 @@ func TestGenerateThumbnail(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createTestImage(t *testing.T, width, height int) image.Image {
|
||||||
|
t.Helper()
|
||||||
|
img := image.NewNRGBA(image.Rect(0, 0, width, height))
|
||||||
|
for y := 0; y < height; y++ {
|
||||||
|
for x := 0; x < width; x++ {
|
||||||
|
img.Set(x, y, color.NRGBA{uint8(x % 256), uint8(y % 256), 0, 255})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResize(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
img image.Image
|
||||||
|
targetW int
|
||||||
|
targetH int
|
||||||
|
expectedW int
|
||||||
|
expectedH int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "zero target dimensions",
|
||||||
|
img: createTestImage(t, 100, 50),
|
||||||
|
targetW: 0,
|
||||||
|
targetH: 0,
|
||||||
|
expectedW: 0,
|
||||||
|
expectedH: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "negative target dimensions",
|
||||||
|
img: createTestImage(t, 100, 50),
|
||||||
|
targetW: -1,
|
||||||
|
targetH: 25,
|
||||||
|
expectedW: 0,
|
||||||
|
expectedH: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zero source dimensions",
|
||||||
|
img: createTestImage(t, 0, 0),
|
||||||
|
targetW: 50,
|
||||||
|
targetH: 25,
|
||||||
|
expectedW: 0,
|
||||||
|
expectedH: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserve aspect ratio with width",
|
||||||
|
img: createTestImage(t, 100, 50),
|
||||||
|
targetW: 50,
|
||||||
|
targetH: 0,
|
||||||
|
expectedW: 50,
|
||||||
|
expectedH: 25,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserve aspect ratio with width, height > width",
|
||||||
|
img: createTestImage(t, 50, 100),
|
||||||
|
targetW: 50,
|
||||||
|
targetH: 0,
|
||||||
|
expectedW: 50,
|
||||||
|
expectedH: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserve aspect ratio with height",
|
||||||
|
img: createTestImage(t, 100, 50),
|
||||||
|
targetW: 0,
|
||||||
|
targetH: 25,
|
||||||
|
expectedW: 50,
|
||||||
|
expectedH: 25,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "preserve aspect ratio with height, height > width",
|
||||||
|
img: createTestImage(t, 50, 100),
|
||||||
|
targetW: 0,
|
||||||
|
targetH: 25,
|
||||||
|
expectedW: 13,
|
||||||
|
expectedH: 25,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid target dimensions",
|
||||||
|
img: createTestImage(t, 100, 50),
|
||||||
|
targetW: 50,
|
||||||
|
targetH: 25,
|
||||||
|
expectedW: 50,
|
||||||
|
expectedH: 25,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
resizedImg := Resize(tc.img, tc.targetW, tc.targetH, transform.Lanczos)
|
||||||
|
require.Equal(t, tc.expectedW, resizedImg.Bounds().Dx())
|
||||||
|
require.Equal(t, tc.expectedH, resizedImg.Bounds().Dy())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ package imaging
|
|||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/anthonynsimon/bild/clone"
|
||||||
|
"github.com/anthonynsimon/bild/transform"
|
||||||
)
|
)
|
||||||
|
|
||||||
type rawImg interface {
|
type rawImg interface {
|
||||||
@@ -140,8 +142,126 @@ func FillImageTransparency(img image.Image, c color.Color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CropAnchor cuts out a rectangular region with the specified size
|
||||||
|
// from the image using the specified anchor point and returns the cropped image.
|
||||||
|
// Adapted from github.com/disintegration/imaging
|
||||||
|
func CropCenter(img image.Image, w, h int) image.Image {
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
anchorPoint := image.Pt(srcBounds.Min.X+(srcBounds.Dx()-w)/2, srcBounds.Min.Y+(srcBounds.Dy()-h)/2)
|
||||||
|
r := image.Rect(0, 0, w, h).Add(anchorPoint)
|
||||||
|
b := srcBounds.Intersect(r)
|
||||||
|
return transform.Crop(img, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resizeAndCrop resizes the image to the smallest possible size that will cover the specified dimensions,
|
||||||
|
// crops the resized image to the specified dimensions using a centered anchor point and returns
|
||||||
|
// the transformed image.
|
||||||
|
// Adapted from github.com/disintegration/imaging
|
||||||
|
func resizeAndCropCenter(img image.Image, width, height int) image.Image {
|
||||||
|
dstW, dstH := width, height
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||||
|
dstAspectRatio := float64(dstW) / float64(dstH)
|
||||||
|
|
||||||
|
var tmp image.Image
|
||||||
|
if srcAspectRatio < dstAspectRatio {
|
||||||
|
tmp = Resize(img, dstW, 0, transform.Lanczos)
|
||||||
|
} else {
|
||||||
|
tmp = Resize(img, 0, dstH, transform.Lanczos)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CropCenter(tmp, dstW, dstH)
|
||||||
|
}
|
||||||
|
|
||||||
// FillCenter creates an image with the specified dimensions and fills it with
|
// FillCenter creates an image with the specified dimensions and fills it with
|
||||||
// the centered and scaled source image.
|
// the centered and scaled source image.
|
||||||
func FillCenter(img image.Image, w, h int) *image.NRGBA {
|
// To achieve the correct aspect ratio without stretching, the source image will be cropped.
|
||||||
return imaging.Fill(img, w, h, imaging.Center, imaging.Lanczos)
|
// Adapted from github.com/disintegration/imaging
|
||||||
|
func FillCenter(img image.Image, dstW, dstH int) image.Image {
|
||||||
|
if dstW <= 0 || dstH <= 0 {
|
||||||
|
return &image.RGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.RGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcW == dstW && srcH == dstH {
|
||||||
|
return clone.AsShallowRGBA(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resizeAndCropCenter(img, dstW, dstH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fit scales down the image to fit the specified
|
||||||
|
// maximum width and height and returns the transformed image.
|
||||||
|
// Adapted from github.com/disintegration/imaging
|
||||||
|
func Fit(img image.Image, maxW, maxH int) image.Image {
|
||||||
|
if maxW <= 0 || maxH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcBounds := img.Bounds()
|
||||||
|
srcW := srcBounds.Dx()
|
||||||
|
srcH := srcBounds.Dy()
|
||||||
|
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.RGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcW <= maxW && srcH <= maxH {
|
||||||
|
return clone.AsShallowRGBA(img)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||||
|
maxAspectRatio := float64(maxW) / float64(maxH)
|
||||||
|
|
||||||
|
var newW, newH int
|
||||||
|
if srcAspectRatio > maxAspectRatio {
|
||||||
|
newW = maxW
|
||||||
|
newH = int(float64(newW) / srcAspectRatio)
|
||||||
|
} else {
|
||||||
|
newH = maxH
|
||||||
|
newW = int(float64(newH) * srcAspectRatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Resize(img, newW, newH, transform.Lanczos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize resizes the image to the specified width and height using the specified resampling filter and returns the transformed image.
|
||||||
|
// If one of width or height is 0, the image aspect ratio is preserved.
|
||||||
|
// Adapted from github.com/disintegration/imaging
|
||||||
|
func Resize(img image.Image, targetWidth, targetHeight int, filter transform.ResampleFilter) image.Image {
|
||||||
|
if targetWidth < 0 || targetHeight < 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetWidth == 0 && targetHeight == 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcW := img.Bounds().Dx()
|
||||||
|
srcH := img.Bounds().Dy()
|
||||||
|
if srcW <= 0 || srcH <= 0 {
|
||||||
|
return &image.NRGBA{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If new width or height is 0 then preserve aspect ratio, minimum 1px.
|
||||||
|
if targetWidth == 0 {
|
||||||
|
tmpW := float64(targetHeight) * float64(srcW) / float64(srcH)
|
||||||
|
targetWidth = int(math.Max(1.0, math.Floor(tmpW+0.5)))
|
||||||
|
}
|
||||||
|
if targetHeight == 0 {
|
||||||
|
tmpH := float64(targetWidth) * float64(srcH) / float64(srcW)
|
||||||
|
targetHeight = int(math.Max(1.0, math.Floor(tmpH+0.5)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return transform.Resize(img, targetWidth, targetHeight, filter)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package imaging
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -115,3 +116,234 @@ func TestFillImageTransparency(t *testing.T) {
|
|||||||
require.Equal(t, expectedImg, inputImg)
|
require.Equal(t, expectedImg, inputImg)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCropCenter(t *testing.T) {
|
||||||
|
imgDir, ok := fileutils.FindDir("tests")
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
d, err := NewDecoder(DecoderOptions{})
|
||||||
|
require.NotNil(t, d)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
inputName string
|
||||||
|
outputName string
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Crop to center 100x100",
|
||||||
|
"crop_test_input.png",
|
||||||
|
"crop_test_output_100x100.png",
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Crop to center 45x45",
|
||||||
|
"crop_test_input.png",
|
||||||
|
"crop_test_output_45x45.png",
|
||||||
|
45,
|
||||||
|
45,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Crop to center 100x45",
|
||||||
|
"crop_test_input.png",
|
||||||
|
"crop_test_output_100x45.png",
|
||||||
|
100,
|
||||||
|
45,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Crop to center 45x100",
|
||||||
|
"crop_test_input.png",
|
||||||
|
"crop_test_output_45x100.png",
|
||||||
|
45,
|
||||||
|
100,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
inputFile, err := os.Open(imgDir + "/" + tc.inputName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, inputFile)
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, inputFile.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
inputImg, format, err := d.Decode(inputFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, inputImg)
|
||||||
|
require.Equal(t, "png", format)
|
||||||
|
|
||||||
|
expectedFile, err := os.Open(imgDir + "/" + tc.outputName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, expectedFile)
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, expectedFile.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
expectedImg, format, err := d.Decode(expectedFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, expectedImg)
|
||||||
|
require.Equal(t, "png", format)
|
||||||
|
|
||||||
|
croppedImg := CropCenter(inputImg, tc.width, tc.height)
|
||||||
|
require.Equal(t, expectedImg.Bounds().Dx(), croppedImg.Bounds().Dx())
|
||||||
|
require.Equal(t, expectedImg.Bounds().Dy(), croppedImg.Bounds().Dy())
|
||||||
|
require.Equal(t, expectedImg.(*image.RGBA).Pix, croppedImg.(*image.RGBA).Pix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFit(t *testing.T) {
|
||||||
|
imgDir, ok := fileutils.FindDir("tests")
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
d, err := NewDecoder(DecoderOptions{})
|
||||||
|
require.NotNil(t, d)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
inputName string
|
||||||
|
outputName string
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Fit to 100x100",
|
||||||
|
"fit_test_input.png",
|
||||||
|
"fit_test_output_100x100.png",
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Fit to 45x45",
|
||||||
|
"fit_test_input.png",
|
||||||
|
"fit_test_output_45x45.png",
|
||||||
|
45,
|
||||||
|
45,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Fit to 100x45",
|
||||||
|
"fit_test_input.png",
|
||||||
|
"fit_test_output_100x45.png",
|
||||||
|
100,
|
||||||
|
45,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Fit to 45x100",
|
||||||
|
"fit_test_input.png",
|
||||||
|
"fit_test_output_45x100.png",
|
||||||
|
45,
|
||||||
|
100,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
inputFile, err := os.Open(imgDir + "/" + tc.inputName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, inputFile)
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, inputFile.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
inputImg, format, err := d.Decode(inputFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, inputImg)
|
||||||
|
require.Equal(t, "png", format)
|
||||||
|
|
||||||
|
expectedFile, err := os.Open(imgDir + "/" + tc.outputName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, expectedFile)
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, expectedFile.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
expectedImg, format, err := d.Decode(expectedFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, expectedImg)
|
||||||
|
require.Equal(t, "png", format)
|
||||||
|
|
||||||
|
fittedImg := Fit(inputImg, tc.width, tc.height)
|
||||||
|
require.Equal(t, expectedImg, fittedImg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFillCenter(t *testing.T) {
|
||||||
|
imgDir, ok := fileutils.FindDir("tests")
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
d, err := NewDecoder(DecoderOptions{})
|
||||||
|
require.NotNil(t, d)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tcs := []struct {
|
||||||
|
name string
|
||||||
|
inputName string
|
||||||
|
outputName string
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Fill center 100x100",
|
||||||
|
"fill_test_input.png",
|
||||||
|
"fill_test_output_100x100.png",
|
||||||
|
100,
|
||||||
|
100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Fill center 45x45",
|
||||||
|
"fill_test_input.png",
|
||||||
|
"fill_test_output_45x45.png",
|
||||||
|
45,
|
||||||
|
45,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Fill center 100x45",
|
||||||
|
"fill_test_input.png",
|
||||||
|
"fill_test_output_100x45.png",
|
||||||
|
100,
|
||||||
|
45,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Fill center 45x100",
|
||||||
|
"fill_test_input.png",
|
||||||
|
"fill_test_output_45x100.png",
|
||||||
|
45,
|
||||||
|
100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tcs {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
inputFile, err := os.Open(imgDir + "/" + tc.inputName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, inputFile)
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, inputFile.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
inputImg, format, err := d.Decode(inputFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, inputImg)
|
||||||
|
require.Equal(t, "png", format)
|
||||||
|
|
||||||
|
expectedFile, err := os.Open(imgDir + "/" + tc.outputName)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, expectedFile)
|
||||||
|
defer func() {
|
||||||
|
require.NoError(t, expectedFile.Close())
|
||||||
|
}()
|
||||||
|
|
||||||
|
expectedImg, format, err := d.Decode(expectedFile)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, expectedImg)
|
||||||
|
require.Equal(t, "png", format)
|
||||||
|
|
||||||
|
filledImg := FillCenter(inputImg, tc.width, tc.height)
|
||||||
|
require.Equal(t, expectedImg.Bounds().Dx(), filledImg.Bounds().Dx())
|
||||||
|
require.Equal(t, expectedImg.Bounds().Dy(), filledImg.Bounds().Dy())
|
||||||
|
require.Equal(t, expectedImg.(*image.RGBA).Pix, filledImg.(*image.RGBA).Pix)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ toolchain go1.22.6
|
|||||||
require (
|
require (
|
||||||
code.sajari.com/docconv/v2 v2.0.0-pre.4
|
code.sajari.com/docconv/v2 v2.0.0-pre.4
|
||||||
github.com/Masterminds/semver/v3 v3.2.1
|
github.com/Masterminds/semver/v3 v3.2.1
|
||||||
|
github.com/anthonynsimon/bild v0.14.0
|
||||||
github.com/avct/uasurfer v0.0.0-20240501094946-ca0c4d1e541b
|
github.com/avct/uasurfer v0.0.0-20240501094946-ca0c4d1e541b
|
||||||
github.com/aws/aws-sdk-go v1.55.5
|
github.com/aws/aws-sdk-go v1.55.5
|
||||||
github.com/blang/semver/v4 v4.0.0
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/blevesearch/bleve/v2 v2.4.1
|
github.com/blevesearch/bleve/v2 v2.4.1
|
||||||
github.com/cespare/xxhash/v2 v2.3.0
|
github.com/cespare/xxhash/v2 v2.3.0
|
||||||
github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3
|
github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3
|
||||||
github.com/disintegration/imaging v1.6.2
|
|
||||||
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a
|
github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a
|
||||||
github.com/elastic/go-elasticsearch/v8 v8.14.0
|
github.com/elastic/go-elasticsearch/v8 v8.14.0
|
||||||
github.com/fatih/color v1.17.0
|
github.com/fatih/color v1.17.0
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9Pq
|
|||||||
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
|
||||||
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
|
github.com/anthonynsimon/bild v0.14.0 h1:IFRkmKdNdqmexXHfEU7rPlAmdUZ8BDZEGtGHDnGWync=
|
||||||
|
github.com/anthonynsimon/bild v0.14.0/go.mod h1:hcvEAyBjTW69qkKJTfpcDQ83sSZHxwOunsseDfeQhUs=
|
||||||
github.com/araddon/dateparse v0.0.0-20180729174819-cfd92a431d0e/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
|
github.com/araddon/dateparse v0.0.0-20180729174819-cfd92a431d0e/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI=
|
||||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||||
@@ -125,8 +127,6 @@ github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3 h1:AqeKSZIG/NIC7
|
|||||||
github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3/go.mod h1:hEfFauPHz7+NnjR/yHJGhrKo1Za+zStgwUETx3yzqgY=
|
github.com/dgryski/dgoogauth v0.0.0-20190221195224-5a805980a5f3/go.mod h1:hEfFauPHz7+NnjR/yHJGhrKo1Za+zStgwUETx3yzqgY=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||||
@@ -670,7 +670,6 @@ golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/
|
|||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
|||||||
Двоичные данные
server/tests/crop_test_input.png
Обычный файл
|
После Ширина: | Высота: | Размер: 1.7 KiB |
Двоичные данные
server/tests/crop_test_output_100x100.png
Обычный файл
|
После Ширина: | Высота: | Размер: 808 B |
Двоичные данные
server/tests/crop_test_output_100x45.png
Обычный файл
|
После Ширина: | Высота: | Размер: 411 B |
Двоичные данные
server/tests/crop_test_output_45x100.png
Обычный файл
|
После Ширина: | Высота: | Размер: 469 B |
Двоичные данные
server/tests/crop_test_output_45x45.png
Обычный файл
|
После Ширина: | Высота: | Размер: 351 B |
Двоичные данные
server/tests/fill_test_input.png
Обычный файл
|
После Ширина: | Высота: | Размер: 1.3 KiB |
Двоичные данные
server/tests/fill_test_output_100x100.png
Обычный файл
|
После Ширина: | Высота: | Размер: 1021 B |
Двоичные данные
server/tests/fill_test_output_100x45.png
Обычный файл
|
После Ширина: | Высота: | Размер: 716 B |
Двоичные данные
server/tests/fill_test_output_45x100.png
Обычный файл
|
После Ширина: | Высота: | Размер: 702 B |
Двоичные данные
server/tests/fill_test_output_45x45.png
Обычный файл
|
После Ширина: | Высота: | Размер: 553 B |
Двоичные данные
server/tests/fit_test_input.png
Обычный файл
|
После Ширина: | Высота: | Размер: 4.5 KiB |
Двоичные данные
server/tests/fit_test_output_100x100.png
Обычный файл
|
После Ширина: | Высота: | Размер: 3.0 KiB |
Двоичные данные
server/tests/fit_test_output_100x45.png
Обычный файл
|
После Ширина: | Высота: | Размер: 1.9 KiB |
Двоичные данные
server/tests/fit_test_output_45x100.png
Обычный файл
|
После Ширина: | Высота: | Размер: 1.5 KiB |
Двоичные данные
server/tests/fit_test_output_45x45.png
Обычный файл
|
После Ширина: | Высота: | Размер: 1.5 KiB |
Двоичные данные
server/tests/orientation_test_2_expected_preview.jpeg
|
До Ширина: | Высота: | Размер: 300 KiB После Ширина: | Высота: | Размер: 300 KiB |
Двоичные данные
server/tests/orientation_test_3_expected_preview.jpeg
|
До Ширина: | Высота: | Размер: 302 KiB После Ширина: | Высота: | Размер: 302 KiB |
Двоичные данные
server/tests/orientation_test_6_expected_preview.jpeg
|
До Ширина: | Высота: | Размер: 301 KiB После Ширина: | Высота: | Размер: 301 KiB |
Двоичные данные
server/tests/orientation_test_7_expected_preview.jpeg
|
До Ширина: | Высота: | Размер: 302 KiB После Ширина: | Высота: | Размер: 302 KiB |
Двоичные данные
server/tests/orientation_test_8_expected_preview.jpeg
|
До Ширина: | Высота: | Размер: 301 KiB После Ширина: | Высота: | Размер: 301 KiB |
Двоичные данные
server/tests/testgif_expected_thumbnail.jpg
|
До Ширина: | Высота: | Размер: 5.2 KiB После Ширина: | Высота: | Размер: 5.2 KiB |