github.com/status-im/status-go@v1.1.0/images/adjust.go (about)

     1  package images
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"image"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"os"
    12  	"strings"
    13  )
    14  
    15  const (
    16  	maxChatMessageImageSize = 400000
    17  	resizeTargetImageSize   = 350000
    18  	idealTargetImageSize    = 50000
    19  )
    20  
    21  var DefaultBounds = FileSizeLimits{Ideal: idealTargetImageSize, Max: resizeTargetImageSize}
    22  
    23  func FetchAndStoreRemoteImage(url string) (string, error) {
    24  	resp, err := http.Get(url) //nolint
    25  	if err != nil {
    26  		return "", fmt.Errorf("error fetching image from URL: %w", err)
    27  	}
    28  	defer resp.Body.Close()
    29  
    30  	if resp.StatusCode != http.StatusOK {
    31  		return "", fmt.Errorf("bad status code: %s", resp.Status)
    32  	}
    33  
    34  	tempFile, err := ioutil.TempFile("", "image-*")
    35  	if err != nil {
    36  		return "", fmt.Errorf("error creating a temporary file: %w", err)
    37  	}
    38  	defer tempFile.Close()
    39  
    40  	_, err = io.Copy(tempFile, resp.Body)
    41  	if err != nil {
    42  		os.Remove(tempFile.Name()) // Ensure temporary file is deleted on error
    43  		return "", fmt.Errorf("error writing image to temp file: %w", err)
    44  	}
    45  
    46  	return tempFile.Name(), nil
    47  }
    48  
    49  func OpenAndDecodeImage(imagePath string) (image.Image, error) {
    50  	file, err := os.Open(imagePath)
    51  	if err != nil {
    52  		return nil, fmt.Errorf("error opening image file: %w", err)
    53  	}
    54  	defer file.Close()
    55  
    56  	img, err := Decode(imagePath)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("error decoding image: %w", err)
    59  	}
    60  
    61  	return img, nil
    62  }
    63  
    64  func AdjustImage(img image.Image, crop bool, inputImage CroppedImage) ([]byte, error) {
    65  	if crop {
    66  		cropRect := image.Rectangle{
    67  			Min: image.Point{X: inputImage.X, Y: inputImage.Y},
    68  			Max: image.Point{X: inputImage.X + inputImage.Width, Y: inputImage.Y + inputImage.Height},
    69  		}
    70  		var err error
    71  		img, err = Crop(img, cropRect)
    72  		if err != nil {
    73  			return nil, fmt.Errorf("error cropping image: %w", err)
    74  		}
    75  	}
    76  
    77  	bb := bytes.NewBuffer([]byte{})
    78  	err := CompressToFileLimits(bb, img, DefaultBounds)
    79  	if err != nil {
    80  		return nil, fmt.Errorf("error compressing image: %w", err)
    81  	}
    82  
    83  	payload := bb.Bytes()
    84  	if len(payload) > maxChatMessageImageSize {
    85  		return nil, errors.New("image too large")
    86  	}
    87  
    88  	return payload, nil
    89  }
    90  func OpenAndAdjustImage(inputImage CroppedImage, crop bool) ([]byte, error) {
    91  	var imgPath string = inputImage.ImagePath
    92  	var err error
    93  
    94  	// Check if the image is from a remote source
    95  	if strings.HasPrefix(inputImage.ImagePath, "http://") || strings.HasPrefix(inputImage.ImagePath, "https://") {
    96  		imgPath, err = FetchAndStoreRemoteImage(inputImage.ImagePath)
    97  		if err != nil {
    98  			return nil, err
    99  		}
   100  		defer os.Remove(imgPath) // Clean up the temporary file
   101  	}
   102  
   103  	// Decode the image
   104  	img, err := OpenAndDecodeImage(imgPath)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	// Adjust (crop and compress) the image
   110  	return AdjustImage(img, crop, inputImage)
   111  }