151 lines
3.1 KiB
Go
151 lines
3.1 KiB
Go
package assetops
|
|
|
|
import (
|
|
"image"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"aego/asset"
|
|
"aego/asset/pipeline"
|
|
"aego/core/errs"
|
|
"aego/core/ids"
|
|
"aego/editor/core/context"
|
|
"aego/platform/clip"
|
|
)
|
|
|
|
const PastedName = "Pasted"
|
|
|
|
func Report(ctx *context.Editor, err error) {
|
|
if err != nil {
|
|
ctx.Notify(context.LevelError, err.Error())
|
|
}
|
|
}
|
|
|
|
func NextNumbered(ctx *context.Editor, dir, base, sep, ext string) string {
|
|
for i := 1; ; i++ {
|
|
if rel := path.Join(dir, base+sep+strconv.Itoa(i)+ext); !Exists(ctx, rel) {
|
|
return rel
|
|
}
|
|
}
|
|
}
|
|
|
|
func EnsureDir(ctx *context.Editor, dir string) error {
|
|
if dir == "" || dir == "." || Exists(ctx, dir) {
|
|
return nil
|
|
}
|
|
if err := EnsureDir(ctx, path.Dir(dir)); err != nil {
|
|
return err
|
|
}
|
|
return ctx.Pipeline.Mkdir(dir)
|
|
}
|
|
|
|
func CreatePNG(ctx *context.Editor, rel string, img image.Image) (ids.AssetID, error) {
|
|
data := clip.EncodePNG(img)
|
|
if len(data) == 0 {
|
|
return ids.AssetID{}, errs.New(errs.EditorDocument, "cannot encode the image as PNG")
|
|
}
|
|
return CreateFile(ctx, rel, data)
|
|
}
|
|
|
|
func RelIn(pl *pipeline.Pipeline, abs string) (string, bool) {
|
|
if pl == nil {
|
|
return "", false
|
|
}
|
|
rel, err := filepath.Rel(pl.Abs("."), filepath.Clean(abs))
|
|
if err != nil {
|
|
return "", false
|
|
}
|
|
rel = filepath.ToSlash(rel)
|
|
if rel == "." || rel == ".." || strings.HasPrefix(rel, "../") {
|
|
return "", false
|
|
}
|
|
return rel, true
|
|
}
|
|
|
|
func ProjectRel(ctx *context.Editor, abs string) (string, bool) {
|
|
return RelIn(ctx.Pipeline, abs)
|
|
}
|
|
|
|
func sourcePNG(ctx *context.Editor, id ids.AssetID) ([]byte, bool) {
|
|
e, ok := ctx.Pipeline.Entry(id)
|
|
if !ok || e.Kind != asset.ArtTexture {
|
|
return nil, false
|
|
}
|
|
rel, ok := ctx.Pipeline.PathOf(id)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
data, err := os.ReadFile(ctx.Pipeline.Abs(rel))
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
if strings.EqualFold(path.Ext(rel), ".png") {
|
|
return data, true
|
|
}
|
|
img, err := clip.DecodeImage(data)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
out := clip.EncodePNG(img)
|
|
return out, len(out) > 0
|
|
}
|
|
|
|
func AbsPaths(ctx *context.Editor, list []ids.AssetID) []string {
|
|
var out []string
|
|
for _, id := range list {
|
|
if rel, ok := ctx.Pipeline.PathOf(id); ok {
|
|
out = append(out, filepath.Clean(ctx.Pipeline.Abs(rel)))
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func CopyAssets(ctx *context.Editor, list []ids.AssetID) []string {
|
|
if ctx.Clipboard == nil || ctx.Pipeline == nil {
|
|
return nil
|
|
}
|
|
paths := AbsPaths(ctx, list)
|
|
if len(paths) == 0 {
|
|
return nil
|
|
}
|
|
if len(list) == 1 {
|
|
if data, ok := sourcePNG(ctx, list[0]); ok && clip.SetImagePNG(ctx.Clipboard, data, paths) {
|
|
return paths
|
|
}
|
|
}
|
|
if !clip.SetFiles(ctx.Clipboard, paths) {
|
|
return nil
|
|
}
|
|
return paths
|
|
}
|
|
|
|
func CanPaste(ctx *context.Editor) bool {
|
|
if ctx.Clipboard == nil || ctx.Pipeline == nil {
|
|
return false
|
|
}
|
|
if clip.HasFiles(ctx.Clipboard) || clip.HasImage(ctx.Clipboard) {
|
|
return true
|
|
}
|
|
_, ok := clip.Files(ctx.Clipboard)
|
|
return ok
|
|
}
|
|
|
|
func SamePaths(a, b []string) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
norm := func(in []string) []string {
|
|
out := make([]string, len(in))
|
|
for i, s := range in {
|
|
out[i] = filepath.Clean(s)
|
|
}
|
|
slices.Sort(out)
|
|
return out
|
|
}
|
|
return slices.Equal(norm(a), norm(b))
|
|
}
|