Can now save and load from json

Much improved speed over loading everything manually.
This commit is contained in:
Unknown
2018-03-22 20:11:51 -04:00
parent daac5bf3d2
commit 5b36b9193a
8 changed files with 264 additions and 90 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
data/

57
colors.go Normal file
View File

@@ -0,0 +1,57 @@
package ps
import (
"encoding/hex"
)
var Colors map[string]Color = map[string]Color{
"Gray": &RGB{128, 128, 128},
"White": &RGB{255, 255, 255},
}
// Color represents a color.
type Color interface {
RGB() [3]int
}
func Compare(a, b Color) Color {
A := a.RGB()
B := b.RGB()
Aavg := (A[0] + A[1] + A[2]) / 3
Bavg := (B[0] + B[1] + B[2]) / 3
if Aavg > Bavg {
return a
}
return b
}
// Color is a color in RGB format.
type RGB struct {
Red int
Green int
Blue int
}
// RGB returns the color in RGB format.
func (r RGB) RGB() [3]int {
return [3]int{r.Red, r.Green, r.Blue}
}
// Hex is a color in hexidecimal format.
type Hex []uint8
func (h Hex) RGB() [3]int {
src := []byte(h)
dst := make([]byte, hex.DecodedLen(len(src)))
_, err := hex.Decode(dst, src)
if err != nil {
panic(err)
}
return [3]int{int(dst[0]), int(dst[1]), int(dst[2])}
}
// Stroke represents a layer stroke effect.
type Stroke struct {
Size float32
Color
}

1
ps.go
View File

@@ -184,7 +184,6 @@ func ApplyDataset(name string) ([]byte, error) {
func JSLayer(path string) string { func JSLayer(path string) string {
path = strings.TrimLeft(path, "/") path = strings.TrimLeft(path, "/")
pth := strings.Split(path, "/") pth := strings.Split(path, "/")
// fmt.Println(path)
js := "app.activeDocument" js := "app.activeDocument"
last := len(pth) - 1 last := len(pth) - 1
if last > 0 { if last > 0 {

View File

@@ -1,7 +1,9 @@
package ps package ps
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@@ -112,9 +114,8 @@ func TestSaveAs(t *testing.T) {
os.Remove("F:\\TEMP\\test.png") os.Remove("F:\\TEMP\\test.png")
} }
/*
func TestLayerSet(t *testing.T) { func TestLayerSet(t *testing.T) {
_, err := NewLayerSet("Areas/TitleBackground/") _, err := NewLayerSet("Areas/TitleBackground/", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -132,9 +133,9 @@ func TestMove(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
lyr.Position(100, 50, "top") lyr.SetPos(100, 50, "TL")
} }
*/
func TestActiveDocument(t *testing.T) { func TestActiveDocument(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("Skipping \"TestDocument\"") t.Skip("Skipping \"TestDocument\"")
@@ -166,10 +167,8 @@ func TestActiveDocument(t *testing.T) {
} }
s := Stroke{Size: 4, Color: &RGB{0, 0, 0}} s := Stroke{Size: 4, Color: &RGB{0, 0, 0}}
lyr.SetStroke(s, &RGB{128, 128, 128}) lyr.SetStroke(s, &RGB{128, 128, 128})
} }
/*
func TestColor(t *testing.T) { func TestColor(t *testing.T) {
byt, err := run("colorLayer.vbs", "255", "255", "255") byt, err := run("colorLayer.vbs", "255", "255", "255")
fmt.Println(string(byt)) fmt.Println(string(byt))
@@ -179,9 +178,7 @@ func TestColor(t *testing.T) {
t.Fatal() t.Fatal()
} }
} }
*/
/*
func TestApplyDataset(t *testing.T) { func TestApplyDataset(t *testing.T) {
out := []byte("done!\r\n") out := []byte("done!\r\n")
ret, err := ApplyDataset(" Anger") ret, err := ApplyDataset(" Anger")
@@ -215,20 +212,40 @@ func TestDocumentLayerSet(t *testing.T) {
fmt.Println(lyr.name) fmt.Println(lyr.name)
} }
} }
*/
/* func TestLoadedDoc(t *testing.T) {
var d *Document
byt, err := ioutil.ReadFile("Document.txt")
if err != nil {
t.Fatal(err)
}
err = json.Unmarshal(byt, &d)
if err != nil {
t.Fatal(err)
}
if d != d.ArtLayers()[0].Parent() {
t.Fatal("Loaded document's ArtLayers do not point to doc")
}
if d != d.LayerSets()[0].Parent() {
t.Fatal("Loaded document's LayerSets do not point to doc")
}
if d.LayerSets()[0] != d.layerSets[0].artLayers[0].Parent() {
t.Fatal("Loaded document's LayerSet's ArtLayers do not point to layerSets")
}
}
func TestDoJs_HideLayer(t *testing.T) { func TestDoJs_HideLayer(t *testing.T) {
err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd") err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
lyr, err := NewLayerSet("Areas/TitleBackground") lyr, err := NewLayerSet("Areas/TitleBackground", nil)
lyr.SetVisible(false) lyr.SetVisible(false)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
*/
func BenchmarkDoc_Go(b *testing.B) { func BenchmarkDoc_Go(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := ActiveDocument() _, err := ActiveDocument()

View File

@@ -0,0 +1,2 @@
#include lib.js
var stdout = newFile(arguments[0]);stdout.writeln(app.activeDocument.name);stdout.close();

View File

@@ -2,7 +2,7 @@
var stdout = newFile(arguments[0]); var stdout = newFile(arguments[0]);
var lyr = eval(arguments[1]); var lyr = eval(arguments[1]);
stdout.writeln(('{"Name":"' + lyr.name + '", "Bounds": [[' + lyr.bounds[0] + ',' + stdout.writeln(('{"Name":"' + lyr.name + '","Bounds":[[' + lyr.bounds[0] + ',' +
lyr.bounds[1] + '],[' + lyr.bounds[2] + ',' + lyr.bounds[1] + '],[' + lyr.bounds[2] + ',' +
lyr.bounds[3] + ']], "Visible": ' + lyr.visible + '}').replace(/ px/g, "")); lyr.bounds[3] + ']],"Visible":' + lyr.visible + '}').replace(/ px/g, ""));
stdout.close(); stdout.close();

2
scripts/isVisible.jsx Normal file
View File

@@ -0,0 +1,2 @@
#include lib.js
var stdout = newFile(arguments[0]);stdout.writeln(eval(arguments[1]).visible);stdout.close();

View File

@@ -1,65 +1,31 @@
package ps package ps
import ( import (
"encoding/hex"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"log" "log"
"os"
"path/filepath"
"runtime"
"strings" "strings"
) )
var Colors map[string]Color = map[string]Color{ type ModeEnum int
"Gray": &RGB{128, 128, 128},
"White": &RGB{255, 255, 255},
}
// Color represents a color. // Mode determines how aggressively ps will attempt to sync with Photoshop.
type Color interface { var Mode ModeEnum
RGB() [3]int
}
func Compare(a, b Color) Color { // Normal Mode Always checks to see if layers are updated
A := a.RGB() // before returning them.
B := b.RGB() const Normal ModeEnum = 0
Aavg := (A[0] + A[1] + A[2]) / 3
Bavg := (B[0] + B[1] + B[2]) / 3
if Aavg > Bavg {
return a
}
return b
}
// Color is a color in RGB format. // Safe Mode Always loads the document from scratch. (Slow)
type RGB struct { const Safe ModeEnum = 1
Red int
Green int
Blue int
}
// RGB returns the color in RGB format. // Fast mode never checks layers before returning.
func (r *RGB) RGB() [3]int { const Fast ModeEnum = 2
return [3]int{r.Red, r.Green, r.Blue}
}
// Hex is a color in hexidecimal format.
type Hex []uint8
func (h Hex) RGB() [3]int {
src := []byte(h)
dst := make([]byte, hex.DecodedLen(len(src)))
_, err := hex.Decode(dst, src)
if err != nil {
panic(err)
}
return [3]int{int(dst[0]), int(dst[1]), int(dst[2])}
}
// Stroke represents a layer stroke effect.
type Stroke struct {
Size float32
Color
}
// Group represents a Document or LayerSet. // Group represents a Document or LayerSet.
type Group interface { type Group interface {
@@ -69,6 +35,8 @@ type Group interface {
Path() string Path() string
ArtLayers() []*ArtLayer ArtLayers() []*ArtLayer
LayerSets() []*LayerSet LayerSets() []*LayerSet
MarshalJSON() ([]byte, error)
UnmarshalJSON(b []byte) error
} }
// Document represents a Photoshop document (PSD file). // Document represents a Photoshop document (PSD file).
@@ -88,6 +56,11 @@ type DocumentJSON struct {
LayerSets []*LayerSet LayerSets []*LayerSet
} }
func (d *Document) MarshalJSON() ([]byte, error) {
return json.Marshal(&DocumentJSON{Name: d.name, Height: d.height,
Width: d.width, ArtLayers: d.artLayers, LayerSets: d.layerSets})
}
func (d *Document) UnmarshalJSON(b []byte) error { func (d *Document) UnmarshalJSON(b []byte) error {
tmp := &DocumentJSON{} tmp := &DocumentJSON{}
if err := json.Unmarshal(b, &tmp); err != nil { if err := json.Unmarshal(b, &tmp); err != nil {
@@ -97,7 +70,13 @@ func (d *Document) UnmarshalJSON(b []byte) error {
d.height = tmp.Height d.height = tmp.Height
d.width = tmp.Width d.width = tmp.Width
d.artLayers = tmp.ArtLayers d.artLayers = tmp.ArtLayers
for _, lyr := range d.artLayers {
lyr.SetParent(d)
}
d.layerSets = tmp.LayerSets d.layerSets = tmp.LayerSets
for _, set := range d.layerSets {
set.SetParent(d)
}
return nil return nil
} }
@@ -141,10 +120,37 @@ func (d *Document) Path() string {
return "" return ""
} }
// Filename returns the path to the json file for this document.
func (d *Document) Filename() string {
_, dir, _, ok := runtime.Caller(0)
if !ok {
log.Panic("No caller information")
}
return filepath.Join(filepath.Dir(dir), "data",
strings.TrimRight(string(d.name), "\r\n")+".txt")
}
func ActiveDocument() (*Document, error) { func ActiveDocument() (*Document, error) {
log.Println("Loading ActiveDoucment/") log.Println("Loading ActiveDoucment")
byt, err := DoJs("getActiveDoc.jsx") d := &Document{}
var d *Document
byt, err := DoJs("activeDocName.jsx")
if err != nil {
return nil, err
}
d.name = string(byt)
if Mode != 1 {
byt, err = ioutil.ReadFile(d.Filename())
if err == nil {
log.Println("Previous version found, loading")
err = json.Unmarshal(byt, &d)
if err == nil {
return d, err
}
}
}
log.Println("Loading manually (This could take awhile)")
byt, err = DoJs("getActiveDoc.jsx")
err = json.Unmarshal(byt, &d) err = json.Unmarshal(byt, &d)
for _, lyr := range d.artLayers { for _, lyr := range d.artLayers {
lyr.SetParent(d) lyr.SetParent(d)
@@ -155,27 +161,55 @@ func ActiveDocument() (*Document, error) {
log.Fatal(err) log.Fatal(err)
} }
d.layerSets[i] = s d.layerSets[i] = s
// s.SetParent(d) s.SetParent(d)
} }
d.Dump()
return d, err return d, err
} }
func (d *Document) Dump() {
f, err := os.Create(d.Filename())
if err != nil {
log.Fatal(err)
}
defer f.Close()
byt, err := json.MarshalIndent(d, "", "\t")
if err != nil {
log.Fatal(err)
}
f.Write(byt)
}
// ArtLayer represents an Art Layer in a photoshop document. // ArtLayer represents an Art Layer in a photoshop document.
type ArtLayer struct { type ArtLayer struct {
name string name string // The layer's name.
// TextItem string // TextItem string
bounds [2][2]int bounds [2][2]int // The layers' corners.
parent Group parent Group // The LayerSet/Document this layer is in.
visible bool visible bool // Whether or not the layer is visible.
Color current bool // Whether we've checked this layer since we loaded from disk.
*Stroke Color // The layer's color overlay.
*Stroke // The layer's stroke.
} }
type ArtLayerJSON struct { type ArtLayerJSON struct {
Name string Name string
Bounds [2][2]int Bounds [2][2]int
Parent Group Visible bool
Visible bool Color [3]int
Stroke [3]int
StrokeAmt float32
}
func (a *ArtLayer) MarshalJSON() ([]byte, error) {
return json.Marshal(&ArtLayerJSON{
Name: a.name,
Bounds: a.bounds,
Visible: a.visible,
Color: a.Color.RGB(),
Stroke: a.Stroke.RGB(),
StrokeAmt: a.Stroke.Size,
})
} }
func (a *ArtLayer) UnmarshalJSON(b []byte) error { func (a *ArtLayer) UnmarshalJSON(b []byte) error {
@@ -185,8 +219,10 @@ func (a *ArtLayer) UnmarshalJSON(b []byte) error {
} }
a.name = tmp.Name a.name = tmp.Name
a.bounds = tmp.Bounds a.bounds = tmp.Bounds
a.parent = tmp.Parent a.Color = RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]}
a.Stroke = &Stroke{tmp.StrokeAmt, RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}}
a.visible = tmp.Visible a.visible = tmp.Visible
a.current = false
return nil return nil
} }
@@ -231,20 +267,25 @@ func (a *ArtLayer) SetActive() ([]byte, error) {
// SetColor creates a color overlay for the layer // SetColor creates a color overlay for the layer
func (a *ArtLayer) SetColor(c Color) { func (a *ArtLayer) SetColor(c Color) {
if Mode == 2 && a.Color.RGB() == c.RGB() {
return
}
if a.Stroke.Size != 0 {
a.SetStroke(*a.Stroke, c)
return
}
a.Color = c a.Color = c
cols := c.RGB() cols := a.Color.RGB()
log.Printf(`Setting layer "%s" to color %v`, a.name, cols)
r := cols[0] r := cols[0]
g := cols[1] g := cols[1]
b := cols[2] b := cols[2]
if a.Stroke != nil {
a.SetStroke(Stroke{a.Stroke.Size, a.Stroke.Color}, a.Color)
return
}
byt, err := a.SetActive() byt, err := a.SetActive()
if len(byt) != 0 { if len(byt) != 0 {
log.Println(string(byt), "err") log.Println(string(byt), "err")
} }
if err != nil { if err != nil {
log.Println(a.Path())
log.Panic(err) log.Panic(err)
} }
byt, err = run("colorLayer", fmt.Sprint(r), fmt.Sprint(g), fmt.Sprint(b)) byt, err = run("colorLayer", fmt.Sprint(r), fmt.Sprint(g), fmt.Sprint(b))
@@ -257,6 +298,13 @@ func (a *ArtLayer) SetColor(c Color) {
} }
func (a *ArtLayer) SetStroke(stk Stroke, fill Color) { func (a *ArtLayer) SetStroke(stk Stroke, fill Color) {
if Mode == 2 {
if stk.Size == a.Stroke.Size && stk.Color.RGB() == a.Color.RGB() {
if a.Color.RGB() == fill.RGB() {
return
}
}
}
byt, err := a.SetActive() byt, err := a.SetActive()
if len(byt) != 0 { if len(byt) != 0 {
log.Println(string(byt)) log.Println(string(byt))
@@ -264,8 +312,12 @@ func (a *ArtLayer) SetStroke(stk Stroke, fill Color) {
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
a.Stroke = &stk
a.Color = fill
stkCol := stk.Color.RGB() stkCol := stk.Color.RGB()
col := fill.RGB() col := fill.RGB()
log.Printf("Setting layer %s stroke to %.2fpt %v and color to %v\n", a.name, a.Stroke.Size,
a.Stroke.Color.RGB(), a.Color.RGB())
byt, err = run("colorStroke", fmt.Sprint(col[0]), fmt.Sprint(col[1]), fmt.Sprint(col[2]), byt, err = run("colorStroke", fmt.Sprint(col[0]), fmt.Sprint(col[1]), fmt.Sprint(col[2]),
fmt.Sprintf("%.2f", stk.Size), fmt.Sprint(stkCol[0]), fmt.Sprint(stkCol[1]), fmt.Sprint(stkCol[2])) fmt.Sprintf("%.2f", stk.Size), fmt.Sprint(stkCol[0]), fmt.Sprint(stkCol[1]), fmt.Sprint(stkCol[2]))
if len(byt) != 0 { if len(byt) != 0 {
@@ -274,7 +326,6 @@ func (a *ArtLayer) SetStroke(stk Stroke, fill Color) {
if err != nil { if err != nil {
log.Panic(err) log.Panic(err)
} }
} }
func (a *ArtLayer) Parent() Group { func (a *ArtLayer) Parent() Group {
@@ -299,9 +350,16 @@ func Layer(path string) (ArtLayer, error) {
// SetVisible makes the layer visible. // SetVisible makes the layer visible.
func (a *ArtLayer) SetVisible(b bool) { func (a *ArtLayer) SetVisible(b bool) {
if a.Visible() == b {
return
}
if b {
log.Printf("Showing %s", a.name)
} else {
log.Printf("Hiding %s", a.name)
}
js := fmt.Sprintf("%s.visible=%v;", js := fmt.Sprintf("%s.visible=%v;",
strings.TrimRight(JSLayer(a.Path()), ";"), b) strings.TrimRight(JSLayer(a.Path()), ";"), b)
log.Printf("Setting %s.Visible to %v\n", a.name, b)
DoJs("compilejs.jsx", js) DoJs("compilejs.jsx", js)
} }
@@ -314,6 +372,9 @@ func (a *ArtLayer) Visible() bool {
// Valid options for bound are: TL, TR, BL, BR // Valid options for bound are: TL, TR, BL, BR
// TODO: Improve // TODO: Improve
func (a *ArtLayer) SetPos(x, y int, bound string) { func (a *ArtLayer) SetPos(x, y int, bound string) {
if x == 0 && y == 0 {
return
}
if !a.visible { if !a.visible {
return return
} }
@@ -326,35 +387,51 @@ func (a *ArtLayer) SetPos(x, y int, bound string) {
lyrY = a.Y1() lyrY = a.Y1()
} }
byt, err := DoJs("moveLayer.jsx", JSLayer(a.Path()), fmt.Sprint(x-lyrX), fmt.Sprint(y-lyrY)) byt, err := DoJs("moveLayer.jsx", JSLayer(a.Path()), fmt.Sprint(x-lyrX), fmt.Sprint(y-lyrY))
var bounds [2][2]int
if err != nil { if err != nil {
panic(err) panic(err)
} }
json.Unmarshal(byt, &a) json.Unmarshal(byt, bounds)
a.bounds = bounds
} }
type LayerSet struct { type LayerSet struct {
name string name string
parent Group parent Group
current bool // Whether we've checked this layer since we loaded from disk.
artLayers []*ArtLayer artLayers []*ArtLayer
layerSets []*LayerSet layerSets []*LayerSet
} }
type LayerSetJSON struct { type LayerSetJSON struct {
Name string Name string
Parent Group
ArtLayers []*ArtLayer ArtLayers []*ArtLayer
LayerSets []*LayerSet LayerSets []*LayerSet
} }
func (l *LayerSet) MarshalJSON() ([]byte, error) {
return json.Marshal(&LayerSetJSON{
Name: l.name,
ArtLayers: l.artLayers,
LayerSets: l.layerSets,
})
}
func (l *LayerSet) UnmarshalJSON(b []byte) error { func (l *LayerSet) UnmarshalJSON(b []byte) error {
tmp := &LayerSetJSON{} tmp := &LayerSetJSON{}
if err := json.Unmarshal(b, &tmp); err != nil { if err := json.Unmarshal(b, &tmp); err != nil {
return err return err
} }
l.name = tmp.Name l.name = tmp.Name
l.parent = tmp.Parent
l.artLayers = tmp.ArtLayers l.artLayers = tmp.ArtLayers
for _, lyr := range l.artLayers {
lyr.SetParent(l)
}
l.layerSets = tmp.LayerSets l.layerSets = tmp.LayerSets
for _, set := range l.layerSets {
set.SetParent(l)
}
l.current = false
return nil return nil
} }
@@ -363,6 +440,11 @@ func (l *LayerSet) Name() string {
} }
func (l *LayerSet) ArtLayers() []*ArtLayer { func (l *LayerSet) ArtLayers() []*ArtLayer {
for i := 0; i < len(l.artLayers); i++ {
if l.artLayers[i] != nil && !l.artLayers[i].current {
l.artLayers[i] = l.ArtLayer(l.artLayers[i].name)
}
}
return l.artLayers return l.artLayers
} }
@@ -371,6 +453,18 @@ func (l *LayerSet) ArtLayers() []*ArtLayer {
func (l *LayerSet) ArtLayer(name string) *ArtLayer { func (l *LayerSet) ArtLayer(name string) *ArtLayer {
for _, lyr := range l.artLayers { for _, lyr := range l.artLayers {
if lyr.name == name { if lyr.name == name {
if Mode == 0 && !lyr.current {
byt, err := DoJs("getLayer.jsx", JSLayer(lyr.Path()))
if err != nil {
log.Panic(err)
}
var lyr2 *ArtLayer
err = json.Unmarshal(byt, &lyr2)
lyr.name = lyr2.name
lyr.bounds = lyr2.bounds
lyr.visible = lyr2.visible
lyr.current = true
}
return lyr return lyr
} }
} }
@@ -408,11 +502,13 @@ func (l *LayerSet) Path() string {
} }
func NewLayerSet(path string, g Group) (*LayerSet, error) { func NewLayerSet(path string, g Group) (*LayerSet, error) {
path = strings.Replace(path, "//", "/", -1)
byt, err := DoJs("getLayerSet.jsx", JSLayer(path)) byt, err := DoJs("getLayerSet.jsx", JSLayer(path))
var out *LayerSet var out *LayerSet
err = json.Unmarshal(byt, &out) err = json.Unmarshal(byt, &out)
if flag.Lookup("test.v") != nil { if flag.Lookup("test.v") != nil {
// log.Println(string(byt)) // log.Println(path)
// log.Println(out)
} }
out.SetParent(g) out.SetParent(g)
log.Printf("Loading ActiveDocument/%s\n", out.Path()) log.Printf("Loading ActiveDocument/%s\n", out.Path())
@@ -423,7 +519,6 @@ func NewLayerSet(path string, g Group) (*LayerSet, error) {
lyr.SetParent(out) lyr.SetParent(out)
} }
for i, set := range out.layerSets { for i, set := range out.layerSets {
// log.Println("\t", set.name)
s, err := NewLayerSet(fmt.Sprintf("%s%s/", path, set.Name()), out) s, err := NewLayerSet(fmt.Sprintf("%s%s/", path, set.Name()), out)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)