flamenco/internal/manager/last_rendered/image_processing.go
Sybren A. Stüvel e6af6a708c Manager: always close file when saving to JPEG
Always close the output file; previously this was not done when the
JPEG encoding would fail.
2022-06-26 13:24:37 +02:00

84 lines
2.0 KiB
Go

package last_rendered
// SPDX-License-Identifier: GPL-3.0-or-later
import (
"bytes"
"errors"
"fmt"
"image"
"image/jpeg"
"os"
"path/filepath"
// Import for side-effect of registering decoder.
_ "image/png"
"github.com/disintegration/imaging"
"github.com/rs/zerolog/log"
)
var (
supportedMimeTypes = map[string]bool{
"image/jpeg": true,
"image/png": true,
}
ErrMimeTypeUnsupported = errors.New("mime type unsupported")
)
// decodeImage checks the payload mime type, and if okay, decodes the image and returns it.
// Returns `ErrMimeTypeUnsupported` if the mime type is unsupported.
func decodeImage(payload Payload) (image.Image, error) {
if !supportedMimeTypes[payload.MimeType] {
return nil, ErrMimeTypeUnsupported
}
reader := bytes.NewReader(payload.Image)
img, _, err := image.Decode(reader)
if err != nil {
return nil, fmt.Errorf("decoding image: %w", err)
}
return img, nil
}
// saveJPEG writes the given image to a JPEG file.
func saveJPEG(targetpath string, img image.Image) error {
// Ensure the directory exists.
targetdir := filepath.Dir(targetpath)
err := os.MkdirAll(targetdir, os.ModePerm)
if err != nil {
return fmt.Errorf("creating directory %s: %w", targetdir, err)
}
file, err := os.Create(targetpath)
if err != nil {
return fmt.Errorf("creating file: %w", err)
}
defer func() {
err = file.Close()
if err != nil {
log.Warn().Err(err).Str("filename", targetpath).Msg("last-rendered: error closing file")
}
}()
options := jpeg.Options{
Quality: thumbnailJPEGQuality,
}
err = jpeg.Encode(file, img, &options)
if err != nil {
return fmt.Errorf("encoding as JPEG: %w", err)
}
return nil
}
func downscaleImage(spec thumbspec, img image.Image) image.Image {
// Fill out the entire frame, cropping the image if necessary:
// return imaging.Fill(img, spec.maxWidth, spec.maxHeight, imaging.Center, imaging.Lanczos)
// Fit the image to the frame, potentially resulting in either a narrower or lower image:
return imaging.Fit(img, spec.maxWidth, spec.maxHeight, imaging.Lanczos)
}