blob: 5d57480b0a0b0802c42e15b8e56f1e92cfe7c95c [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package iconvg
import (
"bytes"
"fmt"
"image"
"image/draw"
"image/png"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
)
// disassemble returns a disassembly of an encoded IconVG graphic. Users of
// this package aren't expected to want to do this, so it lives in a _test.go
// file, but it can be useful for debugging.
func disassemble(src []byte) (string, error) {
w := new(bytes.Buffer)
p := func(b []byte, format string, args ...interface{}) {
const hex = "0123456789abcdef"
var buf [14]byte
for i := range buf {
buf[i] = ' '
}
for i, x := range b {
buf[3*i+0] = hex[x>>4]
buf[3*i+1] = hex[x&0x0f]
}
w.Write(buf[:])
fmt.Fprintf(w, format, args...)
}
m := Metadata{}
if err := decode(nil, p, &m, false, buffer(src), nil); err != nil {
return "", err
}
return w.String(), nil
}
var (
_ Destination = (*Encoder)(nil)
_ Destination = (*Rasterizer)(nil)
)
// encodePNG is useful for manually debugging the tests.
func encodePNG(dstFilename string, src image.Image) error {
f, err := os.Create(dstFilename)
if err != nil {
return err
}
encErr := png.Encode(f, src)
closeErr := f.Close()
if encErr != nil {
return encErr
}
return closeErr
}
func decodePNG(srcFilename string) (image.Image, error) {
f, err := os.Open(srcFilename)
if err != nil {
return nil, err
}
defer f.Close()
return png.Decode(f)
}
func checkApproxEqual(m0, m1 image.Image) error {
diff := func(a, b uint32) uint32 {
if a < b {
return b - a
}
return a - b
}
bounds0 := m0.Bounds()
bounds1 := m1.Bounds()
if bounds0 != bounds1 {
return fmt.Errorf("bounds differ: got %v, want %v", bounds0, bounds1)
}
for y := bounds0.Min.Y; y < bounds0.Max.Y; y++ {
for x := bounds0.Min.X; x < bounds0.Max.X; x++ {
r0, g0, b0, a0 := m0.At(x, y).RGBA()
r1, g1, b1, a1 := m1.At(x, y).RGBA()
const D = 0xffff * 5 / 100 // Diff threshold of 5%.
if diff(r0, r1) > D || diff(g0, g1) > D || diff(b0, b1) > D || diff(a0, a1) > D {
return fmt.Errorf("at (%d, %d):\n"+
"got RGBA %#04x, %#04x, %#04x, %#04x\n"+
"want RGBA %#04x, %#04x, %#04x, %#04x",
x, y, r0, g0, b0, a0, r1, g1, b1, a1)
}
}
}
return nil
}
func diffLines(t *testing.T, got, want string) {
gotLines := strings.Split(got, "\n")
wantLines := strings.Split(want, "\n")
for i := 1; ; i++ {
if len(gotLines) == 0 {
t.Errorf("line %d:\ngot %q\nwant %q", i, "", wantLines[0])
return
}
if len(wantLines) == 0 {
t.Errorf("line %d:\ngot %q\nwant %q", i, gotLines[0], "")
return
}
g, w := gotLines[0], wantLines[0]
gotLines = gotLines[1:]
wantLines = wantLines[1:]
if g != w {
t.Errorf("line %d:\ngot %q\nwant %q", i, g, w)
return
}
}
}
func rasterizeASCIIArt(width int, encoded []byte) (string, error) {
dst := image.NewAlpha(image.Rect(0, 0, width, width))
var z Rasterizer
z.SetDstImage(dst, dst.Bounds(), draw.Src)
if err := Decode(&z, encoded, nil); err != nil {
return "", err
}
const asciiArt = ".++8"
buf := make([]byte, 0, width*(width+1))
for y := 0; y < width; y++ {
for x := 0; x < width; x++ {
a := dst.AlphaAt(x, y).A
buf = append(buf, asciiArt[a>>6])
}
buf = append(buf, '\n')
}
return string(buf), nil
}
func TestDisassembleActionInfo(t *testing.T) {
ivgData, err := ioutil.ReadFile(filepath.FromSlash("testdata/action-info.ivg"))
if err != nil {
t.Fatal(err)
}
got, err := disassemble(ivgData)
if err != nil {
t.Fatal(err)
}
want := strings.Join([]string{
"89 49 56 47 Magic identifier",
"02 Number of metadata chunks: 1",
"0a Metadata chunk length: 5",
"00 Metadata Identifier: 0 (viewBox)",
"50 -24",
"50 -24",
"b0 +24",
"b0 +24",
"c0 Start path, filled with CREG[CSEL-0]; M (absolute moveTo)",
"80 +0",
"58 -20",
"a0 C (absolute cubeTo), 1 reps",
"cf cc 30 c1 -11.049999",
"58 -20",
"58 -20",
"cf cc 30 c1 -11.049999",
"58 -20",
"80 +0",
"91 s (relative smooth cubeTo), 2 reps",
"37 33 0f 41 +8.950001",
"a8 +20",
"a8 +20",
"a8 +20",
" s (relative smooth cubeTo), implicit",
"a8 +20",
"37 33 0f c1 -8.950001",
"a8 +20",
"58 -20",
"80 S (absolute smooth cubeTo), 1 reps",
"cf cc 30 41 +11.049999",
"58 -20",
"80 +0",
"58 -20",
"e3 z (closePath); m (relative moveTo)",
"84 +2",
"bc +30",
"e7 h (relative horizontal lineTo)",
"78 -4",
"e8 V (absolute vertical lineTo)",
"7c -2",
"e7 h (relative horizontal lineTo)",
"88 +4",
"e9 v (relative vertical lineTo)",
"98 +12",
"e3 z (closePath); m (relative moveTo)",
"80 +0",
"60 -16",
"e7 h (relative horizontal lineTo)",
"78 -4",
"e9 v (relative vertical lineTo)",
"78 -4",
"e7 h (relative horizontal lineTo)",
"88 +4",
"e9 v (relative vertical lineTo)",
"88 +4",
"e1 z (closePath); end path",
}, "\n") + "\n"
if got != want {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
diffLines(t, got, want)
}
}
func TestDecodeActionInfo(t *testing.T) {
ivgData, err := ioutil.ReadFile(filepath.FromSlash("testdata/action-info.ivg"))
if err != nil {
t.Fatal(err)
}
got, err := rasterizeASCIIArt(24, ivgData)
if err != nil {
t.Fatal(err)
}
want := strings.Join([]string{
"........................",
"........................",
"........++8888++........",
"......+8888888888+......",
".....+888888888888+.....",
"....+88888888888888+....",
"...+8888888888888888+...",
"...88888888..88888888...",
"..+88888888..88888888+..",
"..+888888888888888888+..",
"..88888888888888888888..",
"..888888888..888888888..",
"..888888888..888888888..",
"..888888888..888888888..",
"..+88888888..88888888+..",
"..+88888888..88888888+..",
"...88888888..88888888...",
"...+8888888888888888+...",
"....+88888888888888+....",
".....+888888888888+.....",
"......+8888888888+......",
"........++8888++........",
"........................",
"........................",
}, "\n") + "\n"
if got != want {
t.Errorf("got:\n%s\nwant:\n%s", got, want)
diffLines(t, got, want)
}
}
func TestRasterizer(t *testing.T) {
testCases := []string{
"testdata/action-info",
"testdata/video-005.primitive",
}
for _, tc := range testCases {
ivgData, err := ioutil.ReadFile(filepath.FromSlash(tc) + ".ivg")
if err != nil {
t.Errorf("%s: ReadFile: %v", tc, err)
continue
}
md, err := DecodeMetadata(ivgData)
if err != nil {
t.Errorf("%s: DecodeMetadata: %v", tc, err)
continue
}
width, height := 256, 256
if dx, dy := md.ViewBox.AspectRatio(); dx < dy {
width = int(256 * dx / dy)
} else {
height = int(256 * dy / dx)
}
got := image.NewRGBA(image.Rect(0, 0, width, height))
var z Rasterizer
z.SetDstImage(got, got.Bounds(), draw.Src)
if err := Decode(&z, ivgData, nil); err != nil {
t.Errorf("%s: Decode: %v", tc, err)
continue
}
want, err := decodePNG(filepath.FromSlash(tc) + ".png")
if err != nil {
t.Errorf("%s: decodePNG: %v", tc, err)
continue
}
if err := checkApproxEqual(got, want); err != nil {
t.Errorf("%s: %v", tc, err)
continue
}
}
}