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
|
||||
layer Layer
|
||||
texture rl.RenderTexture2D
|
||||
capture *rl.Image
|
||||
config *LayerConfig
|
||||
}
|
||||
|
||||
@@ -241,21 +242,24 @@ func (s *Sketch) ResetCamera() {
|
||||
}
|
||||
|
||||
type SketchCapture struct {
|
||||
width, height uint32
|
||||
compositeImage *rl.Image
|
||||
layerImages []*rl.Image
|
||||
layerTools map[string]*LayerTools
|
||||
layerToolsOrdered []*LayerTools
|
||||
}
|
||||
|
||||
func (s *Sketch) Capture() *SketchCapture {
|
||||
composite := rl.LoadImageFromTexture(s.composite.Texture)
|
||||
rl.ImageFlipVertical(composite)
|
||||
layerImages := make([]*rl.Image, len(s.layerToolsOrdered))
|
||||
for i, layerTool := range s.layerToolsOrdered {
|
||||
layerImages[i] = rl.LoadImageFromTexture(layerTool.texture.Texture)
|
||||
rl.ImageFlipVertical(layerImages[i])
|
||||
for _, layerTool := range s.layerToolsOrdered {
|
||||
layerTool.capture = rl.LoadImageFromTexture(layerTool.texture.Texture)
|
||||
rl.ImageFlipVertical(layerTool.capture)
|
||||
}
|
||||
return &SketchCapture {
|
||||
width: uint32(s.sourceWidth), height: uint32(s.sourceHeight),
|
||||
compositeImage: composite,
|
||||
layerImages: layerImages,
|
||||
layerTools: s.layerTools,
|
||||
layerToolsOrdered: s.layerToolsOrdered,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
92
storage.go
92
storage.go
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/d2fn/sumi/internal/ids"
|
||||
"github.com/d2fn/sumi/internal/ora"
|
||||
"github.com/go-git/go-git/v6"
|
||||
"log"
|
||||
//"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) {
|
||||
|
||||
id, _ := s.gen.Next()
|
||||
kid := ids.Base62Encode(id)
|
||||
path := filepath.Join(s.snapshotsDir, kid)
|
||||
flakeId := ids.Base62Encode(id)
|
||||
path := filepath.Join(s.snapshotsDir, flakeId)
|
||||
os.MkdirAll(path, 0755)
|
||||
|
||||
// wysiwyg at screen res
|
||||
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)
|
||||
hash, branch, committed, err := s.SaveToGit(flakeId)
|
||||
|
||||
if err != nil {
|
||||
s.log.Printf("Error getting working tree in a known clean state: %v", err)
|
||||
} else {
|
||||
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 {
|
||||
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)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
id,
|
||||
kid,
|
||||
flakeId,
|
||||
time.Now().UnixMilli(),
|
||||
branch,
|
||||
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("Saved snapshot to %s\n", path)
|
||||
|
||||
return path, nil
|
||||
return err
|
||||
}
|
||||
|
||||
func HeadHash(repoPath string) (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user