automated snapshot
This commit is contained in:
98
internal/ora/ora_support.go
Normal file
98
internal/ora/ora_support.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package ora
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"encoding/xml"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ORALayer struct {
|
||||||
|
Name string
|
||||||
|
Filename string // relative to data/
|
||||||
|
Visible bool
|
||||||
|
Opacity float32 // 0..1
|
||||||
|
Blend string // svg:src-over, svg:multiply, etc
|
||||||
|
}
|
||||||
|
|
||||||
|
type imageXML struct {
|
||||||
|
XMLName xml.Name `xml:"image"`
|
||||||
|
W int `xml:"w,attr"`
|
||||||
|
H int `xml:"h,attr"`
|
||||||
|
Stack stackXML `xml:"stack"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type stackXML struct {
|
||||||
|
Layers []layerXML `xml:"layer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type layerXML struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Src string `xml:"src,attr"`
|
||||||
|
Opacity float32 `xml:"opacity,attr"`
|
||||||
|
Visible bool `xml:"visible,attr"`
|
||||||
|
Composite string `xml:"composite-op,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteORA(
|
||||||
|
outPath string,
|
||||||
|
width, height int,
|
||||||
|
layers []ORALayer,
|
||||||
|
pngLoader func(name string) ([]byte, error),
|
||||||
|
) error {
|
||||||
|
|
||||||
|
f, err := os.Create(outPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
zw := zip.NewWriter(f)
|
||||||
|
defer zw.Close()
|
||||||
|
|
||||||
|
// 1. mimetype (must be first, uncompressed)
|
||||||
|
h := &zip.FileHeader{
|
||||||
|
Name: "mimetype",
|
||||||
|
Method: zip.Store,
|
||||||
|
}
|
||||||
|
w, _ := zw.CreateHeader(h)
|
||||||
|
w.Write([]byte("image/openraster"))
|
||||||
|
|
||||||
|
// 2. stack.xml
|
||||||
|
var stack []layerXML
|
||||||
|
for _, l := range layers {
|
||||||
|
stack = append(stack, layerXML{
|
||||||
|
Name: l.Name,
|
||||||
|
Src: "data/" + l.Filename,
|
||||||
|
Opacity: l.Opacity,
|
||||||
|
Visible: l.Visible,
|
||||||
|
Composite: l.Blend,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
img := imageXML{
|
||||||
|
W: width,
|
||||||
|
H: height,
|
||||||
|
Stack: stackXML{
|
||||||
|
Layers: stack,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlBytes, _ := xml.MarshalIndent(img, "", " ")
|
||||||
|
xmlBuf := append([]byte(xml.Header), xmlBytes...)
|
||||||
|
|
||||||
|
w, _ = zw.Create("stack.xml")
|
||||||
|
w.Write(xmlBuf)
|
||||||
|
|
||||||
|
// 3. layer PNGs
|
||||||
|
for _, l := range layers {
|
||||||
|
data, err := pngLoader(l.Filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w, _ = zw.Create("data/" + l.Filename)
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
16
sketch.go
16
sketch.go
@@ -32,6 +32,7 @@ type LayerTools struct {
|
|||||||
name string
|
name string
|
||||||
layer Layer
|
layer Layer
|
||||||
texture rl.RenderTexture2D
|
texture rl.RenderTexture2D
|
||||||
|
capture *rl.Image
|
||||||
config *LayerConfig
|
config *LayerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,21 +242,24 @@ func (s *Sketch) ResetCamera() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SketchCapture struct {
|
type SketchCapture struct {
|
||||||
|
width, height uint32
|
||||||
compositeImage *rl.Image
|
compositeImage *rl.Image
|
||||||
layerImages []*rl.Image
|
layerTools map[string]*LayerTools
|
||||||
|
layerToolsOrdered []*LayerTools
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sketch) Capture() *SketchCapture {
|
func (s *Sketch) Capture() *SketchCapture {
|
||||||
composite := rl.LoadImageFromTexture(s.composite.Texture)
|
composite := rl.LoadImageFromTexture(s.composite.Texture)
|
||||||
rl.ImageFlipVertical(composite)
|
rl.ImageFlipVertical(composite)
|
||||||
layerImages := make([]*rl.Image, len(s.layerToolsOrdered))
|
for _, layerTool := range s.layerToolsOrdered {
|
||||||
for i, layerTool := range s.layerToolsOrdered {
|
layerTool.capture = rl.LoadImageFromTexture(layerTool.texture.Texture)
|
||||||
layerImages[i] = rl.LoadImageFromTexture(layerTool.texture.Texture)
|
rl.ImageFlipVertical(layerTool.capture)
|
||||||
rl.ImageFlipVertical(layerImages[i])
|
|
||||||
}
|
}
|
||||||
return &SketchCapture {
|
return &SketchCapture {
|
||||||
|
width: uint32(s.sourceWidth), height: uint32(s.sourceHeight),
|
||||||
compositeImage: composite,
|
compositeImage: composite,
|
||||||
layerImages: layerImages,
|
layerTools: s.layerTools,
|
||||||
|
layerToolsOrdered: s.layerToolsOrdered,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
92
storage.go
92
storage.go
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/d2fn/sumi/internal/ids"
|
"github.com/d2fn/sumi/internal/ids"
|
||||||
|
"github.com/d2fn/sumi/internal/ora"
|
||||||
"github.com/go-git/go-git/v6"
|
"github.com/go-git/go-git/v6"
|
||||||
"log"
|
"log"
|
||||||
//"github.com/go-git/go-git/v5/plumbing"
|
//"github.com/go-git/go-git/v5/plumbing"
|
||||||
@@ -86,48 +87,83 @@ func initSchema(db *sql.DB) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) Save(capture *SketchCapture) (string, error) {
|
func (s *Storage) Save(capture *SketchCapture) (string, error) {
|
||||||
|
|
||||||
id, _ := s.gen.Next()
|
id, _ := s.gen.Next()
|
||||||
kid := ids.Base62Encode(id)
|
flakeId := ids.Base62Encode(id)
|
||||||
path := filepath.Join(s.snapshotsDir, kid)
|
path := filepath.Join(s.snapshotsDir, flakeId)
|
||||||
os.MkdirAll(path, 0755)
|
os.MkdirAll(path, 0755)
|
||||||
|
|
||||||
// wysiwyg at screen res
|
hash, branch, committed, err := s.SaveToGit(flakeId)
|
||||||
img := rl.LoadImageFromScreen()
|
|
||||||
defer rl.UnloadImage(img)
|
|
||||||
|
|
||||||
snapshotPng := filepath.Join(path, fmt.Sprintf("%s.png", kid))
|
|
||||||
rl.ExportImage(*img, snapshotPng)
|
|
||||||
|
|
||||||
// capture full res compsite
|
|
||||||
compositePng := filepath.Join(path, fmt.Sprintf("%s-composite.png", kid))
|
|
||||||
rl.ExportImage(*capture.compositeImage, compositePng)
|
|
||||||
|
|
||||||
// capture full res layer
|
|
||||||
for i, layerImage := range capture.layerImages {
|
|
||||||
layerPng := filepath.Join(path, fmt.Sprintf("%s-%03d.png", kid, i))
|
|
||||||
rl.ExportImage(*layerImage, layerPng)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.log.Printf("Checking git status...\n")
|
|
||||||
|
|
||||||
hash, branch, committed, err := CommitAllIfDirty(s.repoRoot, "automated snapshot", s.log)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Printf("Error getting working tree in a known clean state: %v", err)
|
s.log.Printf("Error getting working tree in a known clean state: %v", err)
|
||||||
} else {
|
} else {
|
||||||
if committed {
|
if committed {
|
||||||
s.log.Printf("Created commit %s on %s for snapshot %s", hash, branch, kid)
|
s.log.Printf("Created commit %s on %s for snapshot %s", hash, branch, flakeId)
|
||||||
} else {
|
} else {
|
||||||
s.log.Printf("Referencing commit %s on %s for snapshot %s", hash, branch, kid)
|
s.log.Printf("Referencing commit %s on %s for snapshot %s", hash, branch, flakeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.db.Exec(`
|
err = s.SaveToDb(id, flakeId, branch, hash, path, committed)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Printf("Error writing to db: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wysiwyg at screen res
|
||||||
|
img := rl.LoadImageFromScreen()
|
||||||
|
defer rl.UnloadImage(img)
|
||||||
|
|
||||||
|
snapshotPng := filepath.Join(path, fmt.Sprintf("%s-screen.png", flakeId))
|
||||||
|
rl.ExportImage(*img, snapshotPng)
|
||||||
|
|
||||||
|
// capture full res compsite
|
||||||
|
compositePng := filepath.Join(path, fmt.Sprintf("%s-final.png", flakeId))
|
||||||
|
rl.ExportImage(*capture.compositeImage, compositePng)
|
||||||
|
|
||||||
|
// capture full res layer
|
||||||
|
oraLayers := make([]ora.ORALayer, len(capture.layerToolsOrdered))
|
||||||
|
for i, layerTools := range capture.layerToolsOrdered {
|
||||||
|
filename := fmt.Sprintf("%s-%03d.png", flakeId, i)
|
||||||
|
layerPng := filepath.Join(path, "data", filename)
|
||||||
|
rl.ExportImage(*layerTools.capture, layerPng)
|
||||||
|
opacity := float32(layerTools.config.a) / 255.0
|
||||||
|
oraLayers[i] =
|
||||||
|
ora.ORALayer{
|
||||||
|
Name: layerTools.name,
|
||||||
|
Filename: filename,
|
||||||
|
Visible: layerTools.config.visible,
|
||||||
|
Opacity: opacity,
|
||||||
|
Blend: "svg:src-over",
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
oraPath := filepath.Join(path, fmt.Sprintf("%s-layers.ora", flakeId))
|
||||||
|
|
||||||
|
ora.WriteORA(oraPath, int(capture.width), int(capture.height), oraLayers,
|
||||||
|
func(name string) ([]byte, error) {
|
||||||
|
return os.ReadFile(filepath.Join(path, "data", name))
|
||||||
|
})
|
||||||
|
|
||||||
|
s.log.Printf("Saved snapshot to %s\n", path)
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Storage) SaveToGit(flakeId string) (string, string, bool, error) {
|
||||||
|
s.log.Printf("Checking git status...\n")
|
||||||
|
return CommitAllIfDirty(s.repoRoot, "automated snapshot", s.log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Storage) SaveToDb(id uint64, flakeId string, branch string, hash string, path string, committed bool) error {
|
||||||
|
|
||||||
|
_, err := s.db.Exec(`
|
||||||
INSERT INTO snapshots (id, sid, created_at, branch, git_hash, committed, path)
|
INSERT INTO snapshots (id, sid, created_at, branch, git_hash, committed, path)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
`,
|
`,
|
||||||
id,
|
id,
|
||||||
kid,
|
flakeId,
|
||||||
time.Now().UnixMilli(),
|
time.Now().UnixMilli(),
|
||||||
branch,
|
branch,
|
||||||
hash,
|
hash,
|
||||||
@@ -139,9 +175,7 @@ func (s *Storage) Save(capture *SketchCapture) (string, error) {
|
|||||||
s.log.Printf("Error inserting snapshot row into db: %v\n", err)
|
s.log.Printf("Error inserting snapshot row into db: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.log.Printf("Saved snapshot to %s\n", path)
|
return err
|
||||||
|
|
||||||
return path, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func HeadHash(repoPath string) (string, error) {
|
func HeadHash(repoPath string) (string, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user