A simple way to edit images #7
|
|
@ -1,6 +1,10 @@
|
||||||
import Handlebars from "handlebars";
|
import Handlebars from "handlebars";
|
||||||
import {Controller} from "@hotwired/stimulus";
|
import {Controller} from "@hotwired/stimulus";
|
||||||
|
|
||||||
|
Handlebars.registerHelper("submit_on", function (id, event) {
|
||||||
|
return `data-action="${event}->edit-upload#updateProcessor" data-edit-upload-id-param="${id}"`
|
||||||
|
});
|
||||||
|
|
||||||
const processorFrame = Handlebars.compile(`
|
const processorFrame = Handlebars.compile(`
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header d-flex justify-content-between">
|
<div class="card-header d-flex justify-content-between">
|
||||||
|
|
@ -11,7 +15,7 @@ const processorFrame = Handlebars.compile(`
|
||||||
>X</a>
|
>X</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
{{{props}}}
|
<form data-role="processor-params" data-params-id="{{id}}">{{{props}}}</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
|
@ -19,7 +23,12 @@ const processorFrame = Handlebars.compile(`
|
||||||
const processorUIs = {
|
const processorUIs = {
|
||||||
"shadow": {
|
"shadow": {
|
||||||
label: "Shadow",
|
label: "Shadow",
|
||||||
template: Handlebars.compile(`This processor has no properties.`),
|
template: Handlebars.compile(`
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{id}}_width" class="form-label">Colour</label>
|
||||||
|
<input name="width" class="form-control" id="{{id}}_{{props.color}}" type="color" value="{{color}}" {{{submit_on id 'change'}}}>
|
||||||
|
</div>
|
||||||
|
`),
|
||||||
},
|
},
|
||||||
"resize": {
|
"resize": {
|
||||||
label: "Resize",
|
label: "Resize",
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,34 @@ import (
|
||||||
"lmika.dev/lmika/weiro/models"
|
"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) {
|
func (s *Service) reprocess(ctx context.Context, session *models.ImageEditSession) (imageSource, error) {
|
||||||
var img imageSource
|
var img imageSource
|
||||||
|
|
||||||
|
|
@ -72,14 +100,24 @@ func (s *Service) processImage(ctx context.Context, srcImg image.Image, processo
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
return imaging.Decode(f)
|
return imaging.Decode(f)
|
||||||
case "shadow":
|
//case "shadow":
|
||||||
shadow := makeBoxShadow(srcImg, color.Black, 4, 10, 0)
|
// shadow := makeBoxShadow(srcImg, color.Black, 4, 10, 0)
|
||||||
composit := imaging.OverlayCenter(shadow, srcImg, 1.0)
|
// composit := imaging.OverlayCenter(shadow, srcImg, 1.0)
|
||||||
return composit, nil
|
// return composit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc, ok := processors[processor.Type]
|
||||||
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown processor type: %v", processor.Type)
|
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 {
|
type imageSource interface {
|
||||||
image() (image.Image, error)
|
image() (image.Image, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package imgedit
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -97,10 +98,21 @@ func (s *Service) AddProcessor(ctx context.Context, sessionID string, req AddPro
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: verify processor, etc.
|
proc, ok := processors[req.Type]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown processor type: %v", req.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
paramType := proc.newParams()
|
||||||
|
paramBytes, err := json.Marshal(paramType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
session.Processors = append(session.Processors, models.ImageEditProcessor{
|
session.Processors = append(session.Processors, models.ImageEditProcessor{
|
||||||
ID: models.NewNanoID(),
|
ID: models.NewNanoID(),
|
||||||
Type: req.Type,
|
Type: req.Type,
|
||||||
|
Props: paramBytes,
|
||||||
})
|
})
|
||||||
|
|
||||||
session.RecalcVersionIDs()
|
session.RecalcVersionIDs()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue