99 lines
1.7 KiB
Go
99 lines
1.7 KiB
Go
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
|
|
}
|
|
|