parent
cd7c27460f
commit
e1e6e3650a
145
main.go
145
main.go
@ -1,99 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/draw"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/oliamb/cutter"
|
||||
"go.oneofone.dev/resize"
|
||||
"log/slog"
|
||||
|
||||
"go.balki.me/collage-maker/collage"
|
||||
)
|
||||
|
||||
/**
|
||||
"431"
|
||||
"697"
|
||||
"2514"
|
||||
"2047"
|
||||
zoom: 0.392
|
||||
816
|
||||
528
|
||||
-------------
|
||||
"153"
|
||||
"9"
|
||||
"1331"
|
||||
"772"
|
||||
zoom: 0.6949
|
||||
816
|
||||
528
|
||||
*/
|
||||
var (
|
||||
imagesDir string
|
||||
collageDir string
|
||||
)
|
||||
|
||||
func main() {
|
||||
const width = 816 * 4
|
||||
const height = 528 * 4
|
||||
imgFile1, err := os.Open("img1.jpg")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
imgFile2, err := os.Open("img2.jpg")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
img1, _, err := image.Decode(imgFile1)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
img2, _, err := image.Decode(imgFile2)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
flag.StringVar(&imagesDir, "images-dir", "images", "Sets the images dir")
|
||||
flag.StringVar(&collageDir, "collages-dir", "collages", "Sets the collages dir")
|
||||
flag.Parse()
|
||||
nameGen := NewNameGen()
|
||||
imagesDirFs := os.DirFS(imagesDir)
|
||||
|
||||
img1, err = cutter.Crop(img1, cutter.Config{
|
||||
Width: 2514 - 431,
|
||||
Height: 2047 - 697,
|
||||
Anchor: image.Point{431, 697},
|
||||
Mode: cutter.TopLeft, // optional, default value
|
||||
http.HandleFunc("/make-collage", func(w http.ResponseWriter, r *http.Request) {
|
||||
collageReq := collage.Request{}
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
slog.Error("failed to read request body", "error", err)
|
||||
return
|
||||
}
|
||||
if err := json.Unmarshal(body, &collageReq); err != nil {
|
||||
slog.Error("failed to unmarshal json request", "error", err)
|
||||
return
|
||||
}
|
||||
collageFile := fmt.Sprintf("collage-%s.jpg", nameGen.Next())
|
||||
out, err := os.Create(path.Join(collageDir, collageFile))
|
||||
if err != nil {
|
||||
slog.Error("failed to create collage output file", "error", err)
|
||||
}
|
||||
if err := collage.Make(collageReq, imagesDirFs, out); err != nil {
|
||||
slog.Error("failed to make collage", "error", err)
|
||||
return
|
||||
}
|
||||
w.Write([]byte(collageFile))
|
||||
})
|
||||
if err := http.ListenAndServe(":8767", nil); err != nil {
|
||||
slog.Error("http ListenAndServe failed", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
type nameGen struct {
|
||||
prefix string
|
||||
counter atomic.Uint64
|
||||
}
|
||||
|
||||
func NewNameGen() *nameGen {
|
||||
currentTime := time.Now().Unix()
|
||||
randBytes := make([]byte, 8)
|
||||
_, err := rand.Read(randBytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
img1 = resize.Resize(width, height, img1, resize.Lanczos3)
|
||||
|
||||
img2, err = cutter.Crop(img2, cutter.Config{
|
||||
Width: 1331 - 153,
|
||||
Height: 772 - 9,
|
||||
Anchor: image.Point{153, 9},
|
||||
Mode: cutter.TopLeft, // optional, default value
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
alpha := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||
uniqRunID := ""
|
||||
for _, b := range randBytes {
|
||||
uniqRunID += string(alpha[int(b)%len(alpha)])
|
||||
}
|
||||
return &nameGen{
|
||||
prefix: fmt.Sprintf("%d-%s", currentTime, uniqRunID),
|
||||
counter: atomic.Uint64{},
|
||||
}
|
||||
}
|
||||
|
||||
img2 = resize.Resize(width, height, img2, resize.Lanczos3)
|
||||
|
||||
//starting position of the second image (bottom left)
|
||||
//sp2 := image.Point{img1.Bounds().Dx(), 0}
|
||||
//new rectangle for the second image
|
||||
//r2 := image.Rectangle{sp2, sp2.Add(img2.Bounds().Size())}
|
||||
//rectangle for the big image
|
||||
r := image.Rectangle{image.Point{0, 0}, image.Point{width, height + height}}
|
||||
r2 := image.Rectangle{image.Point{0, height + 1}, image.Point{width, height + height}}
|
||||
rgba := image.NewRGBA(r)
|
||||
|
||||
draw.Draw(rgba, img1.Bounds(), img1, image.Point{0, 0}, draw.Src)
|
||||
draw.Draw(rgba, r2, img2, image.Point{0, 0}, draw.Src)
|
||||
|
||||
out, err := os.Create("./output.jpg")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
func (n *nameGen) Next() string {
|
||||
return fmt.Sprintf("%s-%d", n.prefix, n.counter.Add(1))
|
||||
}
|
||||
|
||||
var opt jpeg.Options
|
||||
opt.Quality = 80
|
||||
|
||||
jpeg.Encode(out, rgba, &opt)
|
||||
}
|
||||
// curl -D - --json @req.json http://localhost:8767/make-collage
|
||||
|
10
main_test.go
Normal file
10
main_test.go
Normal file
@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNameGen(t *testing.T) {
|
||||
g := NewNameGen()
|
||||
t.Logf("next id: %s", g.Next())
|
||||
t.Logf("next id: %s", g.Next())
|
||||
t.Fail()
|
||||
}
|
29
req.json
Normal file
29
req.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"background_image": "",
|
||||
"aspect": { "width": 4224, "height": 3264 },
|
||||
"dimension": { "width": 1187, "height": 848 },
|
||||
"photos": [
|
||||
{
|
||||
"image": "img1.jpg",
|
||||
"crop": {
|
||||
"start": { "x": 419, "y": 667 },
|
||||
"end": { "x": 2707, "y": 3389 }
|
||||
},
|
||||
"frame": {
|
||||
"start": { "x": 0, "y": 0 },
|
||||
"end": { "x": 712, "y": 848 }
|
||||
}
|
||||
},
|
||||
{
|
||||
"image": "img2.jpg",
|
||||
"crop": {
|
||||
"start": { "x": 331, "y": 44 },
|
||||
"end": { "x": 1132, "y": 1468 }
|
||||
},
|
||||
"frame": {
|
||||
"start": { "x": 712, "y": 0 },
|
||||
"end": { "x": 1187, "y": 848 }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user