Files
aego/art/palette_test.go

214 lines
6.2 KiB
Go

package art
import (
"bytes"
"context"
"image"
"reflect"
"testing"
)
func TestPaletteJSONAndGPLImport(t *testing.T) {
p := []Swatch{{Name: "Transparent"}, {Color: red, Name: "Красный"}, {Color: Color{1, 2, 3, 96}, Name: "Alpha"}}
data, err := EncodePalette(p)
if err != nil {
t.Fatal(err)
}
got, err := DecodePalette(data, "json")
if err != nil || !reflect.DeepEqual(p, got) {
t.Fatal(got, err)
}
if _, err = DecodePalette(append(data, []byte(" {}")...), "json"); err == nil {
t.Fatal("trailing JSON accepted")
}
if _, err = DecodePalette(bytes.Replace(data, []byte(`"version": 1`), []byte(`"version": 99`), 1), "json"); err == nil {
t.Fatal("future version accepted")
}
got, err = DecodePalette([]byte("GIMP Palette\nName: Test\nColumns: 4\n#\n255 0 0 Red\n0 0 255 Blue\n"), "gpl")
if err != nil || len(got) != 3 || got[0].Color.A != 0 || got[1].Color != red {
t.Fatal(got, err)
}
if _, err = DecodePalette([]byte("GIMP Palette\n256 0 0 Bad"), "gpl"); err == nil {
t.Fatal("out of range GPL accepted")
}
}
func TestIndexedPaletteReplacementPreservesPixelsAndLinks(t *testing.T) {
d, l, f, c := doc(t, Indexed)
defer d.Close()
e := edit(t, d)
p := []Swatch{{}, {Color: red}, {Color: blue}}
if err := e.ReplacePalette(p, false); err != nil {
t.Fatal(err)
}
g, _ := e.Stroke(c, brush(red), nil)
_ = g.Point(image.Pt(1, 1))
f2, _ := e.AddFrame(f, 100)
c2, _ := e.CopyCel(c, l, f2, true)
done(t, e)
before := hash(t, d, f)
imageID := d.Info().Cels[0].Image
e = edit(t, d)
if err := e.ReplacePalette([]Swatch{{}, {Color: blue}, {Color: red}}, false); err != nil {
t.Fatal(err)
}
ch := done(t, e)
if hash(t, d, f) != before || d.Info().Cels[1].Image != imageID {
t.Fatal("palette reorder changed pixels or links")
}
pixel(t, d, c2, 1, 1, red)
if err := ch.Undo(); err != nil {
t.Fatal(err)
}
e = edit(t, d)
if err := e.ReplacePalette([]Swatch{{}, {Color: blue}}, false); err == nil {
t.Fatal("lossy palette replacement not explicit")
}
e.Cancel()
}
func TestAddPaletteEntryPreservesIndexedIdentityAndUndo(t *testing.T) {
d, layer, frame, cel := doc(t, Indexed)
defer d.Close()
e := edit(t, d)
duplicate := e.Info().Palette[1]
duplicate.Name = "second instance"
if err := e.SetPaletteEntry(2, duplicate); err != nil {
t.Fatal(err)
}
if err := e.SetPixel(cel, 2, 3, Paint{UseIndex: true, Index: 2}, nil, false); err != nil {
t.Fatal(err)
}
frame2, err := e.AddFrame(frame, 100)
if err != nil {
t.Fatal(err)
}
linked, err := e.CopyCel(cel, layer, frame2, true)
if err != nil {
t.Fatal(err)
}
normal, err := e.EnsureCel(layer, frame, Normal)
if err != nil {
t.Fatal(err)
}
normalColor := Color{128, 129, 255, 255}
if err := e.SetPixel(normal, 2, 3, Paint{Color: normalColor}, nil, false); err != nil {
t.Fatal(err)
}
done(t, e)
before := d.Info()
s, err := NewSession(d, NewJournal(DefaultLimits()))
if err != nil {
t.Fatal(err)
}
added := Swatch{Name: "extra", Color: Color{31, 91, 151, 190}}
if _, err := s.Execute(context.Background(), Operation{Kind: "palette-add", Swatch: &added}); err != nil {
t.Fatal(err)
}
check := func(wantCount int) {
t.Helper()
info := d.Info()
if len(info.Palette) != wantCount || info.Palette[0] != before.Palette[0] || !reflect.DeepEqual(info.Cels, before.Cels) {
t.Fatal("palette append changed transparent entry or cel links")
}
for _, id := range []ID{cel, linked} {
value, err := d.SamplePaint(id, Albedo, 2, 3)
if err != nil || !value.UseIndex || value.Index != 2 {
t.Fatalf("duplicate palette index was remapped: %+v, %v", value, err)
}
}
pixel(t, d, normal, 2, 3, normalColor)
}
check(len(before.Palette) + 1)
if !s.Info().Dirty || d.Info().Palette[len(before.Palette)] != added {
t.Fatal("palette append did not create a dirty swatch")
}
if err := s.Undo(); err != nil {
t.Fatal(err)
}
check(len(before.Palette))
if s.Info().Dirty {
t.Fatal("one Undo did not restore the saved state")
}
if err := s.Redo(); err != nil {
t.Fatal(err)
}
check(len(before.Palette) + 1)
}
func TestAddPaletteEntryRejectsLimitWithoutMutation(t *testing.T) {
d, _, _, _ := doc(t, Indexed)
defer d.Close()
e := edit(t, d)
for len(e.Info().Palette) < 256 {
if err := e.AddPaletteEntry(Swatch{Color: red}); err != nil {
t.Fatal(err)
}
}
before := e.Info().Palette
if err := e.AddPaletteEntry(Swatch{Color: blue}); err == nil {
t.Fatal("palette exceeded its 256-entry limit")
}
if !reflect.DeepEqual(before, e.Info().Palette) {
t.Fatal("rejected palette append changed the pending edit")
}
e.Cancel()
}
func TestPaletteExtractionQuantizationAndDataChannels(t *testing.T) {
d, l, f, c := doc(t, RGBA)
defer d.Close()
e := edit(t, d)
g, _ := e.Stroke(c, brush(red), nil)
_ = g.Point(image.Pt(1, 1))
_ = g.Point(image.Pt(3, 1))
g, _ = e.Stroke(c, brush(blue), nil)
_ = g.Point(image.Pt(5, 5))
normal, _ := e.EnsureCel(l, f, Normal)
g, _ = e.Stroke(normal, brush(Color{127, 128, 255, 255}), nil)
_ = g.Point(image.Pt(0, 0))
done(t, e)
e = edit(t, d)
if err := e.PaletteFromFrame(context.Background(), f, 2, true); err != nil {
t.Fatal(err)
}
ch := done(t, e)
if len(d.Info().Palette) != 2 || d.Info().Palette[1].Color != red {
t.Fatal(d.Info().Palette)
}
pixel(t, d, normal, 0, 0, Color{127, 128, 255, 255})
if err := ch.Undo(); err != nil {
t.Fatal(err)
}
pixel(t, d, c, 5, 5, blue)
ctx, cancel := context.WithCancel(context.Background())
cancel()
e = edit(t, d)
if err := e.PaletteFromFrame(ctx, f, 8, false); err != context.Canceled {
t.Fatal(err)
}
e.Cancel()
}
func TestRampShadeSelectionAndUndo(t *testing.T) {
d, _, _, c := doc(t, Indexed)
defer d.Close()
e := edit(t, d)
p := []Swatch{{}, {Color: Color{40, 40, 40, 255}}, {Color: Color{100, 100, 100, 255}}, {Color: Color{200, 200, 200, 255}}}
_ = e.ReplacePalette(p, false)
g, _ := e.Stroke(c, brush(p[1].Color), nil)
_ = g.Point(image.Pt(1, 1))
_ = g.Point(image.Pt(3, 1))
done(t, e)
selection, _ := RectSelection(16, 16, image.Rect(2, 1, 3, 2), false)
e = edit(t, d)
if err := e.ShadeRamp(context.Background(), c, selection, 1, 3, 1); err != nil {
t.Fatal(err)
}
ch := done(t, e)
pixel(t, d, c, 1, 1, p[1].Color)
pixel(t, d, c, 2, 1, p[2].Color)
if err := ch.Undo(); err != nil {
t.Fatal(err)
}
pixel(t, d, c, 2, 1, p[1].Color)
}