2024-04-11 15:51:56 +02:00
|
|
|
|
|
|
|
// imagery provides functions to read, edit and export images
|
|
|
|
|
|
|
|
package imagery
|
|
|
|
|
|
|
|
import "seekia/internal/encoding"
|
|
|
|
import "seekia/internal/helpers"
|
|
|
|
import "seekia/internal/appValues"
|
|
|
|
import "seekia/internal/localFilesystem"
|
|
|
|
|
|
|
|
import "github.com/disintegration/gift"
|
|
|
|
import "github.com/srwiley/oksvg"
|
|
|
|
import "github.com/srwiley/rasterx"
|
|
|
|
|
|
|
|
import chaiWebp "github.com/chai2010/webp"
|
|
|
|
|
|
|
|
import goJpeg "image/jpeg"
|
|
|
|
import goWebp "golang.org/x/image/webp"
|
|
|
|
import goPng "image/png"
|
|
|
|
|
|
|
|
import goFilepath "path/filepath"
|
|
|
|
import "image"
|
|
|
|
import "image/color"
|
|
|
|
import "image/draw"
|
|
|
|
import "strings"
|
|
|
|
import "bytes"
|
|
|
|
import "errors"
|
|
|
|
|
|
|
|
|
|
|
|
// Image can be either jpeg, jpg, webp or png
|
|
|
|
//Outputs:
|
|
|
|
// -bool: File found
|
|
|
|
// -bool: Able to read image
|
|
|
|
// -image.Image: golang image
|
|
|
|
// -error
|
|
|
|
func ReadImageFile(filepath string) (bool, bool, image.Image, error){
|
|
|
|
|
|
|
|
exists, fileBytes, err := localFilesystem.GetFileContents(filepath)
|
|
|
|
if (err != nil) { return false, false, nil, err }
|
|
|
|
if (exists == false){
|
|
|
|
return false, false, nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
filename := goFilepath.Base(filepath)
|
|
|
|
filenameLowercase := strings.ToLower(filename)
|
|
|
|
|
|
|
|
isJPEG := strings.HasSuffix(filenameLowercase, "jpeg")
|
|
|
|
isJPG := strings.HasSuffix(filenameLowercase, "jpg")
|
|
|
|
if (isJPEG == true || isJPG == true){
|
|
|
|
imageObject, err := ConvertJPEGImageFileBytesToGolangImage(fileBytes)
|
|
|
|
if (err == nil) {
|
|
|
|
return true, true, imageObject, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isPNG := strings.HasSuffix(filenameLowercase, "png")
|
|
|
|
if (isPNG == true){
|
|
|
|
|
|
|
|
imageObject, err := ConvertPNGImageFileBytesToGolangImage(fileBytes)
|
|
|
|
if (err == nil) {
|
|
|
|
return true, true, imageObject, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isWEBP := strings.HasSuffix(filenameLowercase, "webp")
|
|
|
|
if (isWEBP == true){
|
|
|
|
|
|
|
|
imageObject, err := ConvertWEBPImageFileBytesToGolangImage(fileBytes)
|
|
|
|
if (err == nil){
|
|
|
|
return true, true, imageObject, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// File extention is unknown, or it does not correspond to the image file's true format
|
|
|
|
// We will try every method we haven't already tried.
|
|
|
|
|
|
|
|
if (isJPEG == false && isJPG == false){
|
|
|
|
|
|
|
|
imageObject, err := ConvertJPEGImageFileBytesToGolangImage(fileBytes)
|
|
|
|
if (err == nil) {
|
|
|
|
return true, true, imageObject, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isPNG == false){
|
|
|
|
|
|
|
|
imageObject, err := ConvertPNGImageFileBytesToGolangImage(fileBytes)
|
|
|
|
if (err == nil) {
|
|
|
|
return true, true, imageObject, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isWEBP == false){
|
|
|
|
imageObject, err := ConvertWEBPImageFileBytesToGolangImage(fileBytes)
|
|
|
|
if (err == nil){
|
|
|
|
return true, true, imageObject, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, false, nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertJPEGImageFileBytesToGolangImage(input []byte)(image.Image, error){
|
|
|
|
|
|
|
|
bytesBuffer := bytes.NewBuffer(input)
|
|
|
|
|
|
|
|
imageObject, err := goJpeg.Decode(bytesBuffer)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
return imageObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertPNGImageFileBytesToGolangImage(input []byte)(image.Image, error){
|
|
|
|
|
|
|
|
bytesBuffer := bytes.NewBuffer(input)
|
|
|
|
|
|
|
|
imageObject, err := goPng.Decode(bytesBuffer)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
return imageObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertWEBPImageFileBytesToGolangImage(input []byte)(image.Image, error){
|
|
|
|
|
|
|
|
bytesBuffer := bytes.NewBuffer(input)
|
|
|
|
|
|
|
|
imageObject, err := goWebp.Decode(bytesBuffer)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
return imageObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function only works for square images
|
|
|
|
func ConvertSVGImageFileBytesToGolangImage(inputBytes []byte)(image.Image, error){
|
|
|
|
|
|
|
|
//TODO: Use a different svg package, because oksvg cannot render many complex openmoji icons
|
|
|
|
|
|
|
|
fileReader := bytes.NewReader(inputBytes)
|
|
|
|
|
|
|
|
svgIconObject, err := oksvg.ReadIconStream(fileReader, oksvg.StrictErrorMode)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
svgWidth := int(svgIconObject.ViewBox.W)
|
|
|
|
svgHeight := int(svgIconObject.ViewBox.H)
|
|
|
|
|
|
|
|
svgIconObject.SetTarget(0, 0, 400, 400)
|
|
|
|
|
|
|
|
imageObject := image.NewRGBA(image.Rect(0, 0, 400, 400))
|
|
|
|
|
|
|
|
scannerGV := rasterx.NewScannerGV(svgWidth, svgHeight, imageObject, imageObject.Bounds())
|
|
|
|
|
|
|
|
raster := rasterx.NewDasher(400, 400, scannerGV)
|
|
|
|
|
|
|
|
svgIconObject.Draw(raster, 1)
|
|
|
|
|
|
|
|
return imageObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function creates a 1 pixel image of a provided color
|
|
|
|
func GetColorSquare(colorCode string)(image.Image, error){
|
|
|
|
|
|
|
|
colorObject, err := GetColorObjectFromColorCode(colorCode)
|
|
|
|
if (err != nil){
|
|
|
|
return nil, errors.New("GetColorSquare called with invalid color code: " + colorCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
imageRectangle := image.Rect(0, 0, 1, 1)
|
|
|
|
imageObject := image.NewRGBA(imageRectangle)
|
|
|
|
imageObject.Set(0, 0, colorObject)
|
|
|
|
|
|
|
|
return imageObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Example color codes:
|
|
|
|
// -Black: "000000"
|
|
|
|
// -White: "ffffff"
|
|
|
|
// -Blue: "0000ff"
|
|
|
|
// -Red: "ff0000"
|
|
|
|
func GetColorObjectFromColorCode(colorCode string)(color.Color, error){
|
|
|
|
|
|
|
|
colorCodeBytes, err := encoding.DecodeHexStringToBytes(colorCode)
|
|
|
|
if (err != nil) {
|
|
|
|
return nil, errors.New("GetColorObjectFromColorCode called with invalid color code: " + colorCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len(colorCodeBytes) != 3){
|
|
|
|
return nil, errors.New("GetColorObjectFromColorCode called with invalid color code: " + colorCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
colorRed := colorCodeBytes[0]
|
|
|
|
colorGreen := colorCodeBytes[1]
|
|
|
|
colorBlue := colorCodeBytes[2]
|
|
|
|
|
|
|
|
// Color Alpha (opacity)
|
|
|
|
// It is always ff, which represents 100% opacity
|
|
|
|
colorAlpha := uint8(0xff)
|
|
|
|
|
|
|
|
colorObject := color.RGBA{colorRed, colorGreen, colorBlue, colorAlpha}
|
|
|
|
|
|
|
|
return colorObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This converts a profile/message webp base64 image string to a viewable, cropped image
|
|
|
|
// The cropping adds transparent bars so it will conform to a reasonable ratio
|
|
|
|
// The images are encoded into profiles/messages without the bars to save space
|
|
|
|
func ConvertWEBPBase64StringToCroppedDownsizedImageObject(base64Input string)(image.Image, error){
|
|
|
|
|
|
|
|
imageObject, err := ConvertWebpBase64StringToImageObject(base64Input)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
croppedImage, err := cropGolangImageToMaximumRatio(imageObject)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
maximumSideLength := appValues.GetStandardImageMaximumSideLength()
|
|
|
|
|
|
|
|
downsizedImage, err := DownsizeGolangImage(croppedImage, maximumSideLength)
|
|
|
|
if (err != nil){ return nil, err }
|
|
|
|
|
|
|
|
return downsizedImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConvertWebpBase64StringToImageObject(base64Input string)(image.Image, error){
|
|
|
|
|
|
|
|
imageBytes, err := encoding.DecodeBase64StringToBytes(base64Input)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
imageObject, err := ConvertWEBPImageFileBytesToGolangImage(imageBytes)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
return imageObject, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetImageWidthAndHeightPixels(inputImage image.Image)(int, int, error){
|
|
|
|
|
|
|
|
if (inputImage == nil) {
|
|
|
|
return 0, 0, errors.New("GetImageWidthAndHeightPixels called with nil image.")
|
|
|
|
}
|
|
|
|
|
|
|
|
minX := inputImage.Bounds().Min.X
|
|
|
|
maxX := inputImage.Bounds().Max.X
|
|
|
|
minY := inputImage.Bounds().Min.Y
|
|
|
|
maxY := inputImage.Bounds().Max.Y
|
|
|
|
|
|
|
|
width := maxX-minX
|
|
|
|
height := maxY-minY
|
|
|
|
|
|
|
|
if (width <= 0 || height <= 0) {
|
|
|
|
return 0, 0, errors.New("Failed to derive image width and height.")
|
|
|
|
}
|
|
|
|
|
|
|
|
return width, height, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This will downscale an image to the maximum side length
|
|
|
|
// If the image is already small enough, it will do nothing
|
|
|
|
// It preserves aspect ratio
|
|
|
|
func DownsizeGolangImage(inputImage image.Image, maximumSideLength int)(image.Image, error){
|
|
|
|
|
|
|
|
inputImageWidth, inputImageHeight, err := GetImageWidthAndHeightPixels(inputImage)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
if (inputImageWidth <= maximumSideLength && inputImageHeight <= maximumSideLength) {
|
|
|
|
return inputImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
resizedImage, err := ResizeGolangImage(inputImage, maximumSideLength)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
return resizedImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//This function can upscale or downscale an image. It preserves aspect ratio.
|
|
|
|
func ResizeGolangImage(inputImage image.Image, maximumSideLength int) (image.Image, error){
|
|
|
|
|
|
|
|
if (inputImage == nil) {
|
|
|
|
return nil, errors.New("ResizeGolangImage called with nil image.")
|
|
|
|
}
|
|
|
|
|
|
|
|
imageWidth, imageHeight, err := GetImageWidthAndHeightPixels(inputImage)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
if (imageWidth == 0 || imageHeight == 0){
|
|
|
|
return nil, errors.New("ResizeGolangImage called with input image of zero width and length.")
|
|
|
|
}
|
|
|
|
if (maximumSideLength <= 0){
|
|
|
|
return nil, errors.New("ResizeGolangImage called with invalid maximumSideLength")
|
|
|
|
}
|
|
|
|
|
|
|
|
//Outputs:
|
|
|
|
// -int: New width
|
|
|
|
// -int: New height
|
|
|
|
// -error
|
|
|
|
getResizedImageWidthAndHeight := func()(int, int, error){
|
|
|
|
|
|
|
|
if (imageWidth == imageHeight){
|
|
|
|
return maximumSideLength, maximumSideLength, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// oldWidth/oldHeight = newWidth/newHeight
|
|
|
|
// We set either newWidth or newHeight to maximumSideLength
|
|
|
|
// Then we solve for either newHeight or newWidth
|
|
|
|
|
|
|
|
if (imageWidth > imageHeight) {
|
|
|
|
|
|
|
|
newHeight := float64(maximumSideLength) * (float64(imageHeight)/float64(imageWidth))
|
|
|
|
|
|
|
|
newHeightInt, err := helpers.FloorFloat64ToInt(newHeight)
|
|
|
|
if (err != nil) { return 0, 0, err }
|
|
|
|
|
|
|
|
return maximumSideLength, newHeightInt, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
newWidth := float64(maximumSideLength) * (float64(imageWidth)/float64(imageHeight))
|
|
|
|
|
|
|
|
newWidthInt, err := helpers.FloorFloat64ToInt(newWidth)
|
|
|
|
if (err != nil) { return 0, 0, err }
|
|
|
|
|
|
|
|
return newWidthInt, maximumSideLength, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
newWidth, newHeight, err := getResizedImageWidthAndHeight()
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
rectangle := image.Rect(0, 0, newWidth, newHeight)
|
|
|
|
resizedImage := image.NewRGBA(rectangle)
|
|
|
|
|
|
|
|
giftResizeFilterList := gift.New(gift.Resize(newWidth, newHeight, gift.LanczosResampling))
|
|
|
|
giftResizeFilterList.Draw(resizedImage, inputImage)
|
|
|
|
|
|
|
|
return resizedImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This function crops image to maximum allowed aspect ratio.
|
|
|
|
// It adds transparent space to top/bottom or left/right of image.
|
|
|
|
// (Smaller Side)/(Larger side) must be > 0.7
|
|
|
|
func cropGolangImageToMaximumRatio(inputImage image.Image)(image.Image, error){
|
|
|
|
|
|
|
|
if (inputImage == nil) {
|
|
|
|
return nil, errors.New("cropGolangImageToMaximumRatio called with nil image.")
|
|
|
|
}
|
|
|
|
|
|
|
|
widthPixels, heightPixels, err := GetImageWidthAndHeightPixels(inputImage)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
shorterSide := min(widthPixels, heightPixels)
|
|
|
|
longerSide := max(widthPixels, heightPixels)
|
|
|
|
|
|
|
|
currentImageRatio := float64(shorterSide)/float64(longerSide)
|
|
|
|
|
|
|
|
if (currentImageRatio > 0.7) {
|
|
|
|
|
|
|
|
//Image does not exceed maximum allowed ratio, image does not need cropping.
|
|
|
|
return inputImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// We must increase the length of the smaller side so that the image has a ratio of .7
|
|
|
|
// smaller/larger = .7
|
|
|
|
// smaller = (.7)*(larger)
|
|
|
|
|
|
|
|
getNewWidthAndHeight := func()(int, int, error){
|
|
|
|
|
|
|
|
minimumRatio := float64(0.7)
|
|
|
|
|
|
|
|
if (widthPixels > heightPixels){
|
|
|
|
newHeightFloat := float64(widthPixels) * minimumRatio
|
|
|
|
|
|
|
|
newHeight, err := helpers.CeilFloat64ToInt(newHeightFloat)
|
|
|
|
if (err != nil) { return 0, 0, err }
|
|
|
|
|
|
|
|
return widthPixels, newHeight, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
newWidthFloat := float64(heightPixels) * minimumRatio
|
|
|
|
|
|
|
|
newWidth, err := helpers.CeilFloat64ToInt(newWidthFloat)
|
|
|
|
if (err != nil) { return 0, 0, err }
|
|
|
|
|
|
|
|
return newWidth, heightPixels, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
newWidthPixels, newHeightPixels, err := getNewWidthAndHeight()
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
newLongerSideLength := max(newWidthPixels, newHeightPixels)
|
|
|
|
|
|
|
|
resizedImage, err := ResizeGolangImage(inputImage, newLongerSideLength)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
resizedImageWidth, resizedImageHeight, err := GetImageWidthAndHeightPixels(resizedImage)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
croppedImageRectangle := image.Rect(0, 0, newWidthPixels, newHeightPixels)
|
|
|
|
croppedImage := image.NewRGBA(croppedImageRectangle)
|
|
|
|
|
|
|
|
// We find the coordinates of the top left corner of the image we are placing within our new image
|
|
|
|
|
|
|
|
xAxisPlacementPoint := -((newWidthPixels - resizedImageWidth)/2)
|
|
|
|
yAxisPlacementPoint := -((newHeightPixels - resizedImageHeight)/2)
|
|
|
|
|
|
|
|
croppedImagePlacementPoint := image.Point{
|
|
|
|
X: xAxisPlacementPoint,
|
|
|
|
Y: yAxisPlacementPoint,
|
|
|
|
}
|
|
|
|
|
|
|
|
draw.Draw(croppedImage, croppedImageRectangle, resizedImage, croppedImagePlacementPoint, draw.Over)
|
|
|
|
|
|
|
|
return croppedImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Performs downsizing and compression to maximum standard size
|
|
|
|
func ConvertImageObjectToStandardWebpBase64String(inputImage image.Image)(string, error){
|
|
|
|
|
|
|
|
if (inputImage == nil) {
|
|
|
|
return "", errors.New("ConvertImageObjectToStandardWebpBase64String called with nil image.")
|
|
|
|
}
|
|
|
|
|
|
|
|
maximumSideLength := appValues.GetStandardImageMaximumSideLength()
|
|
|
|
|
|
|
|
sideLength := maximumSideLength
|
|
|
|
|
|
|
|
for sideLength > 1 {
|
|
|
|
|
|
|
|
resizedImage, err := ResizeGolangImage(inputImage, sideLength)
|
|
|
|
if (err != nil) { return "", err }
|
|
|
|
|
|
|
|
imageMaximumBytes := appValues.GetStandardImageMaximumBytes()
|
|
|
|
|
|
|
|
imageQuality := float32(100)
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
|
|
|
bytesBuffer := new(bytes.Buffer)
|
|
|
|
|
|
|
|
webpOptions := &chaiWebp.Options{
|
|
|
|
Lossless: false,
|
|
|
|
Quality: imageQuality,
|
|
|
|
}
|
|
|
|
|
|
|
|
err := chaiWebp.Encode(bytesBuffer, resizedImage, webpOptions)
|
|
|
|
if (err != nil) { return "", err }
|
|
|
|
|
|
|
|
if (&bytesBuffer == nil) {
|
|
|
|
return "", errors.New("Nil image buffer after chaiWebp.Encode()")
|
|
|
|
}
|
|
|
|
|
|
|
|
imageBytes := bytesBuffer.Bytes()
|
|
|
|
|
|
|
|
if (len(imageBytes) == 0){
|
|
|
|
return "", errors.New("Empty image buffer after chaiWebp.Encode()")
|
|
|
|
}
|
|
|
|
|
|
|
|
imageSize := len(imageBytes)
|
|
|
|
|
|
|
|
if (imageSize < imageMaximumBytes){
|
|
|
|
|
|
|
|
imageBase64String := encoding.EncodeBytesToBase64String(imageBytes)
|
|
|
|
|
|
|
|
return imageBase64String, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if (imageQuality > 10){
|
|
|
|
|
|
|
|
imageQuality -= 10
|
|
|
|
|
|
|
|
} else if (imageQuality > 4){
|
|
|
|
|
|
|
|
imageQuality -= 1
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// imageQuality is <= 4
|
|
|
|
// We cannot compress the image while retaining good quality at these dimensions
|
|
|
|
// We will downsize the image by 100 pixels and try again
|
|
|
|
sideLength -= 100
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.New("Failed to create standard webp image.")
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func PixelateGolangImage(inputImage image.Image, amount0to100 int)(image.Image, error){
|
|
|
|
|
|
|
|
if (inputImage == nil) {
|
|
|
|
return nil, errors.New("PixelateGolangImage called with nil image.")
|
|
|
|
}
|
|
|
|
if (amount0to100 < 0 || amount0to100 > 100) {
|
|
|
|
return nil, errors.New("PixelateGolangImage called with input not between 0 and 100.")
|
|
|
|
}
|
|
|
|
|
|
|
|
if (amount0to100 == 0) {
|
|
|
|
return inputImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
widthPixels, heightPixels, err := GetImageWidthAndHeightPixels(inputImage)
|
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
longerSideLength := max(widthPixels, heightPixels)
|
|
|
|
|
2024-08-07 09:45:31 +02:00
|
|
|
pixelationAmountInt, err := helpers.ScaleIntProportionally(true, amount0to100, 0, 100, 0, longerSideLength/5)
|
2024-04-11 15:51:56 +02:00
|
|
|
if (err != nil) { return nil, err }
|
|
|
|
|
|
|
|
rectangle := image.Rect(0, 0, widthPixels, heightPixels)
|
|
|
|
pixelatedImage := image.NewRGBA(rectangle)
|
|
|
|
|
|
|
|
giftPixelateFilterList := gift.New(gift.Pixelate(pixelationAmountInt))
|
|
|
|
giftPixelateFilterList.Draw(pixelatedImage, inputImage)
|
|
|
|
|
|
|
|
return pixelatedImage, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// This will verify an image conforms to Seekia's image size and ratio requirements
|
|
|
|
// All images in profiles and messages must conform to these requirements
|
|
|
|
//Outputs:
|
|
|
|
// -bool: Image is valid
|
|
|
|
// -error
|
|
|
|
func VerifyStandardImageBytes(inputBytes []byte)(bool, error){
|
|
|
|
|
|
|
|
imageMaximumBytes := appValues.GetStandardImageMaximumBytes()
|
|
|
|
|
|
|
|
if (len(inputBytes) > imageMaximumBytes){
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
imageObject, err := ConvertWEBPImageFileBytesToGolangImage(inputBytes)
|
|
|
|
if (err != nil) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
imageWidth, imageHeight, err := GetImageWidthAndHeightPixels(imageObject)
|
|
|
|
if (err != nil) {
|
|
|
|
return false, errors.New("ConvertWEBPImageFileBytesToGolangImage returning image with invalid width and height: " + err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
maximumAllowedSideLength := appValues.GetStandardImageMaximumSideLength()
|
|
|
|
|
|
|
|
if (imageWidth > maximumAllowedSideLength || imageHeight > maximumAllowedSideLength){
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|