138 lines
3.1 KiB
Go
138 lines
3.1 KiB
Go
package imgedit
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/disintegration/imaging"
|
|
"lmika.dev/lmika/weiro/models"
|
|
)
|
|
|
|
type imageProcessor struct {
|
|
newParams func() any
|
|
processImage func(ctx context.Context, srcImg image.Image, params any) (image.Image, error)
|
|
}
|
|
|
|
type shadowProcessorArgs struct {
|
|
Color string `json:"color"`
|
|
OffsetY int `json:"offset_y"`
|
|
}
|
|
|
|
var processors = map[string]imageProcessor{
|
|
"shadow": {
|
|
newParams: func() any {
|
|
return &shadowProcessorArgs{
|
|
Color: "#000000",
|
|
OffsetY: 0,
|
|
}
|
|
},
|
|
processImage: func(ctx context.Context, srcImg image.Image, params any) (image.Image, error) {
|
|
p := params.(*shadowProcessorArgs)
|
|
|
|
shadow := makeBoxShadow(srcImg, color.Black, 4, 10, p.OffsetY)
|
|
composit := imaging.OverlayCenter(shadow, srcImg, 1.0)
|
|
return composit, nil
|
|
},
|
|
},
|
|
}
|
|
|
|
func (s *Service) reprocess(ctx context.Context, session *models.ImageEditSession) (imageSource, error) {
|
|
var img imageSource
|
|
|
|
for _, p := range session.Processors {
|
|
// Check if there's currently a cached image of this processor
|
|
cachedImageFile := filepath.Join(s.scratchDir, session.GUID, fmt.Sprintf("%v.%v", p.VersionID, session.ImageExt))
|
|
if s, err := os.Stat(cachedImageFile); err == nil && !s.IsDir() {
|
|
img = fileImageSource(cachedImageFile)
|
|
continue
|
|
}
|
|
|
|
// Need to process the image
|
|
var srcImg image.Image
|
|
if img != nil {
|
|
var err error
|
|
srcImg, err = img.image()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
resImg, err := s.processImage(ctx, srcImg, p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Cache the processed image
|
|
if err := imaging.Save(resImg, cachedImageFile); err != nil {
|
|
return nil, err
|
|
}
|
|
img = imageImageSource{resImg}
|
|
}
|
|
|
|
log.Printf("result of processed image: %T", img)
|
|
|
|
return img, nil
|
|
}
|
|
|
|
func (s *Service) processImage(ctx context.Context, srcImg image.Image, processor models.ImageEditProcessor) (image.Image, error) {
|
|
switch processor.Type {
|
|
case "copy-upload":
|
|
var p models.CopyUploadProps
|
|
if err := json.Unmarshal(processor.Props, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, rc, err := s.uploadService.OpenUpload(ctx, p.UploadID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
f, err := rc()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
return imaging.Decode(f)
|
|
//case "shadow":
|
|
// shadow := makeBoxShadow(srcImg, color.Black, 4, 10, 0)
|
|
// composit := imaging.OverlayCenter(shadow, srcImg, 1.0)
|
|
// return composit, nil
|
|
}
|
|
|
|
proc, ok := processors[processor.Type]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown processor type: %v", processor.Type)
|
|
}
|
|
|
|
paramType := proc.newParams()
|
|
if err := json.Unmarshal(processor.Props, paramType); err != nil {
|
|
return nil, err
|
|
}
|
|
return proc.processImage(ctx, srcImg, paramType)
|
|
}
|
|
|
|
type imageSource interface {
|
|
image() (image.Image, error)
|
|
}
|
|
|
|
type fileImageSource string
|
|
|
|
func (f fileImageSource) image() (image.Image, error) {
|
|
return imaging.Open(string(f))
|
|
}
|
|
|
|
type imageImageSource struct {
|
|
img image.Image
|
|
}
|
|
|
|
func (i imageImageSource) image() (image.Image, error) {
|
|
return i.img, nil
|
|
}
|