From 4d09dbaa61be8665e995221a9056dbd9b9644682 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Tue, 1 Oct 2019 17:50:32 +0200 Subject: [PATCH] initial import Signed-off-by: Elias Naur --- README.md | 21 ++ data.go | 54 +++++ data_test.go | 10 + gen.go | 3 + genicons.go | 524 ++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 8 + go.sum | 23 +++ ic_gio_48px.svg | 1 + 8 files changed, 644 insertions(+) create mode 100644 README.md create mode 100644 data.go create mode 100644 data_test.go create mode 100644 gen.go create mode 100644 genicons.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 ic_gio_48px.svg diff --git a/README.md b/README.md new file mode 100644 index 0000000..2be7854 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +[Egon Elbre's](https://egonelbre.com/) logo for +[Gio](https://gioui.org) in SVG and +[IconVG](https://godoc.org/golang.org/x/exp/shiny/iconvg) format. + +# Use + + import "eliasnaur.com/giologo" + import "golang.org/x/exp/shiny/iconvg" + + sz := 100 // pixels + logo := giologo.Gio + m, _ := iconvg.DecodeMetadata(logo) + dx, dy := m.ViewBox.AspectRatio() + img := image.NewRGBA(image.Rectangle{Max: image.Point{X: sz, Y: int(float32(sz) * dy / dx)}}) + var ico iconvg.Rasterizer + ico.SetDstImage(img, img.Bounds(), draw.Src) + // Use white for icons. + m.Palette[0] = color.RGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff} + iconvg.Decode(&ico, logo, &iconvg.DecodeOptions{ + Palette: &m.Palette, + }) diff --git a/data.go b/data.go new file mode 100644 index 0000000..a4ffb6f --- /dev/null +++ b/data.go @@ -0,0 +1,54 @@ +// generated by go run gen.go; DO NOT EDIT + +package giologo + +var Gio = []byte{ + 0x89, 0x49, 0x56, 0x47, 0x02, 0x0a, 0x00, 0x50, 0x50, 0xb0, 0xb0, 0x80, 0x80, 0xc0, 0x19, 0x88, + 0xe5, 0x69, 0xbf, 0x21, 0x80, 0x3d, 0x80, 0x31, 0x80, 0x91, 0x80, 0x29, 0x80, 0xc5, 0x80, 0xe1, + 0x7f, 0xb9, 0x80, 0x71, 0x7f, 0x05, 0x81, 0xd9, 0x7e, 0x35, 0x81, 0xc9, 0x7c, 0x0d, 0x81, 0xad, + 0x79, 0x71, 0x82, 0xc5, 0x76, 0x25, 0x84, 0x95, 0x7e, 0xd5, 0x80, 0x35, 0x7d, 0xc1, 0x81, 0xe9, + 0x7b, 0xc1, 0x82, 0xb1, 0x7c, 0x8d, 0x82, 0xcd, 0x79, 0xa5, 0x85, 0x15, 0x78, 0x6d, 0x89, 0x45, + 0x7f, 0xa1, 0x81, 0xc5, 0x7e, 0x5d, 0x83, 0x8d, 0x7e, 0x21, 0x85, 0xd9, 0x7f, 0x51, 0x81, 0xd1, + 0x7f, 0xa5, 0x82, 0xf9, 0x7f, 0xf9, 0x83, 0x2d, 0x80, 0x69, 0x81, 0x99, 0x80, 0xcd, 0x82, 0x51, + 0x81, 0x09, 0x84, 0x8d, 0x80, 0xf1, 0x80, 0x39, 0x81, 0xcd, 0x81, 0x0d, 0x82, 0x85, 0x82, 0x86, + 0x95, 0x82, 0x31, 0x87, 0xc9, 0x83, 0xdd, 0x8a, 0x39, 0x84, 0xd9, 0x82, 0x59, 0x80, 0x89, 0x85, + 0x35, 0x80, 0x1d, 0x88, 0xb1, 0x7f, 0x21, 0x83, 0x65, 0x7f, 0x99, 0x86, 0xd1, 0x7e, 0xa9, 0x88, + 0x69, 0x7c, 0xb1, 0x80, 0x35, 0x7f, 0x19, 0x81, 0x25, 0x7e, 0x82, 0x2d, 0x7d, 0xd9, 0x7f, 0x65, + 0x7e, 0xd1, 0x7e, 0x21, 0x7d, 0x95, 0x7d, 0x3d, 0x7c, 0x85, 0x7f, 0xa9, 0x7f, 0x7e, 0x5d, 0x7f, + 0x71, 0x7e, 0x31, 0x7f, 0xe5, 0x7f, 0xf9, 0x7f, 0xc9, 0x7f, 0xf1, 0x7f, 0xad, 0x7f, 0xe5, 0x7f, + 0xbf, 0x9d, 0x7f, 0xd5, 0x7f, 0x55, 0x7f, 0x71, 0x7f, 0x41, 0x7f, 0x09, 0x7f, 0xf1, 0x7f, 0xa1, + 0x7f, 0x05, 0x80, 0x45, 0x7f, 0x3d, 0x80, 0x05, 0x7f, 0x81, 0x80, 0x65, 0x7f, 0x21, 0x81, 0x69, + 0x7f, 0xc5, 0x81, 0x9d, 0x7f, 0xb9, 0x80, 0x3d, 0x80, 0x69, 0x81, 0x95, 0x80, 0x11, 0x82, 0xf9, + 0x80, 0x05, 0x81, 0x9d, 0x80, 0xfd, 0x81, 0x55, 0x81, 0xb9, 0x82, 0x41, 0x82, 0xd9, 0x80, 0x0d, + 0x81, 0x85, 0x81, 0x3d, 0x82, 0xd5, 0x81, 0x59, 0x83, 0x81, 0x80, 0xbd, 0x81, 0x49, 0x80, 0xa1, + 0x83, 0x81, 0x7f, 0x41, 0x85, 0x2d, 0x7f, 0xb1, 0x81, 0xb1, 0x7d, 0x45, 0x83, 0x61, 0x7c, 0x41, + 0x84, 0x6d, 0x7e, 0x31, 0x81, 0x95, 0x7c, 0x41, 0x82, 0xf9, 0x7a, 0xd5, 0x82, 0xf9, 0x7c, 0x19, + 0x81, 0xcd, 0x79, 0xb1, 0x81, 0x0d, 0x77, 0xa5, 0x81, 0xa9, 0x7b, 0xed, 0x7f, 0xb9, 0x76, 0x71, + 0x7e, 0x31, 0x73, 0x8d, 0x7c, 0xa9, 0x7d, 0xc1, 0x7e, 0x99, 0x7b, 0x15, 0x7d, 0x05, 0x7a, 0x09, + 0x7b, 0xcd, 0x7d, 0x2d, 0x7d, 0xcd, 0x7c, 0x29, 0x79, 0xe5, 0x7c, 0xfd, 0x75, 0x21, 0x80, 0x89, + 0x7b, 0x11, 0x82, 0xb1, 0x76, 0x61, 0x84, 0x79, 0x73, 0xf1, 0x81, 0x51, 0x7d, 0x69, 0x84, 0x76, + 0x15, 0x87, 0x09, 0x79, 0x51, 0x83, 0x91, 0x7d, 0x8e, 0x9d, 0x7b, 0xd5, 0x8a, 0x09, 0x7a, 0xb1, + 0x71, 0x81, 0x69, 0x7f, 0xe9, 0x82, 0xe1, 0x7e, 0x69, 0x84, 0x65, 0x7e, 0x9d, 0x80, 0xd1, 0x7f, + 0x49, 0x81, 0x31, 0x80, 0x8d, 0x81, 0x9d, 0x80, 0xe1, 0x80, 0x80, 0xc0, 0x45, 0x7b, 0x59, 0x82, + 0xbb, 0x75, 0x80, 0x25, 0x80, 0xb9, 0x80, 0x95, 0x80, 0xd5, 0x80, 0x05, 0x81, 0x5d, 0x80, 0x65, + 0x81, 0xed, 0x80, 0xb5, 0x82, 0x59, 0x81, 0x11, 0x84, 0x1d, 0x80, 0x59, 0x80, 0x39, 0x80, 0xb1, + 0x80, 0x3d, 0x80, 0x0d, 0x81, 0xe9, 0x7f, 0x91, 0x80, 0x7d, 0x7f, 0x82, 0x35, 0x7f, 0x7d, 0x81, + 0xc9, 0x7f, 0x61, 0x80, 0x91, 0x7f, 0xcd, 0x80, 0x35, 0x7f, 0x11, 0x81, 0xe1, 0x7f, 0x19, 0x80, + 0xbd, 0x7f, 0xe9, 0x7f, 0xa1, 0x7f, 0xd5, 0x7f, 0x75, 0x7f, 0x9d, 0x7f, 0x7e, 0x1d, 0x7f, 0x81, + 0x7e, 0xad, 0x7e, 0xc1, 0x7f, 0xc9, 0x7f, 0x8d, 0x7f, 0x7d, 0x7f, 0x95, 0x7f, 0x29, 0x7f, 0xf5, + 0x7f, 0x11, 0x7f, 0x19, 0x80, 0x21, 0x7e, 0x29, 0x80, 0x35, 0x7d, 0x0d, 0x80, 0x4d, 0x7f, 0x1d, + 0x80, 0x99, 0x7e, 0x1d, 0x80, 0xe5, 0x7d, 0x80, 0x99, 0x7f, 0x11, 0x80, 0x29, 0x7f, 0x5d, 0x80, + 0xdd, 0x7e, 0x3d, 0x80, 0xc5, 0x7f, 0x95, 0x80, 0xa9, 0x7f, 0xe5, 0x80, 0xc5, 0x7f, 0xe1, 0x80, + 0x80, 0xc0, 0x69, 0x84, 0x59, 0x84, 0xb3, 0x29, 0x7f, 0x75, 0x80, 0xc9, 0x7e, 0x89, 0x81, 0xf5, + 0x7e, 0x71, 0x82, 0x19, 0x80, 0x7d, 0x80, 0xad, 0x80, 0x91, 0x80, 0x19, 0x81, 0x95, 0x80, 0x5d, + 0x80, 0x05, 0x80, 0xc5, 0x80, 0xe5, 0x7f, 0xdd, 0x80, 0x81, 0x7f, 0x3d, 0x80, 0xf5, 0x7e, 0xd9, + 0x7f, 0x7c, 0x1d, 0x7f, 0x7d, 0x7d, 0xe3, 0xf9, 0x7f, 0xf5, 0x7d, 0xb7, 0x21, 0x81, 0xd9, 0x7f, + 0x51, 0x82, 0x05, 0x80, 0x49, 0x83, 0xa1, 0x80, 0xd9, 0x80, 0x89, 0x80, 0x79, 0x81, 0x69, 0x81, + 0x9d, 0x81, 0x65, 0x82, 0x31, 0x80, 0x81, 0x81, 0xad, 0x7f, 0x1d, 0x83, 0x95, 0x7e, 0x2d, 0x84, + 0x15, 0x7f, 0xe9, 0x80, 0xbd, 0x7d, 0x45, 0x81, 0x75, 0x7c, 0x11, 0x81, 0xbd, 0x7e, 0xcd, 0x7f, + 0x79, 0x7d, 0x29, 0x7f, 0xd9, 0x7c, 0x7c, 0x59, 0x7f, 0xcd, 0x7e, 0x85, 0x7f, 0x4d, 0x7d, 0x19, + 0x80, 0x1d, 0x7c, 0x8d, 0x80, 0xe5, 0x7e, 0x8d, 0x81, 0xe9, 0x7d, 0xcd, 0x82, 0xad, 0x7d, 0x21, + 0x80, 0xfd, 0x7f, 0x3d, 0x80, 0xf9, 0x7f, 0x5d, 0x80, 0xf9, 0x7f, 0xe1, +} + +// In total, 2912 SVG bytes in 1 files converted to 732 IconVG bytes. diff --git a/data_test.go b/data_test.go new file mode 100644 index 0000000..5a376c2 --- /dev/null +++ b/data_test.go @@ -0,0 +1,10 @@ +// generated by go run genicons.go; DO NOT EDIT + +package giologo + +var list = []struct { + name string + data []byte +}{ + {"Gio", Gio}, +} diff --git a/gen.go b/gen.go new file mode 100644 index 0000000..752e899 --- /dev/null +++ b/gen.go @@ -0,0 +1,3 @@ +package giologo + +//go:generate go run genicons.go -pkg giologo . diff --git a/genicons.go b/genicons.go new file mode 100644 index 0000000..5144404 --- /dev/null +++ b/genicons.go @@ -0,0 +1,524 @@ +// 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. + +// +build ignore + +package main + +import ( + "bytes" + "encoding/xml" + "flag" + "fmt" + "go/format" + "image/color" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + + "golang.org/x/exp/shiny/iconvg" + "golang.org/x/image/math/f32" +) + +var outDir = flag.String("o", "", "output directory") +var pkgName = flag.String("pkg", "icons", "package name") + +var ( + out = new(bytes.Buffer) + failures = []string{} + varNames = []string{} + + totalFiles int + totalIVGBytes int + totalSVGBytes int +) + +func upperCase(s string) string { + if c := s[0]; 'a' <= c && c <= 'z' { + return string(c-0x20) + s[1:] + } + return s +} + +func main() { + flag.Parse() + args := flag.Args() + if len(args) < 1 { + fmt.Fprintf(os.Stderr, "please provide a directory to convert\n") + os.Exit(2) + } + iconsDir := args[0] + + out.WriteString("// generated by go run gen.go; DO NOT EDIT\n\npackage ") + out.WriteString(*pkgName) + out.WriteString("\n\n") + + if err := genDir(iconsDir); err != nil { + log.Fatal(err) + } + + fmt.Fprintf(out, + "// In total, %d SVG bytes in %d files converted to %d IconVG bytes.\n", + totalSVGBytes, totalFiles, totalIVGBytes) + + if len(failures) != 0 { + out.WriteString("\n/*\nFAILURES:\n\n") + for _, failure := range failures { + out.WriteString(failure) + out.WriteByte('\n') + } + out.WriteString("\n*/") + } + + if *outDir != "" { + if err := os.MkdirAll(*outDir, 0775); err != nil && !os.IsExist(err) { + log.Fatal(err) + } + } + raw := out.Bytes() + formatted, err := format.Source(raw) + if err != nil { + log.Fatalf("gofmt failed: %v\n\nGenerated code:\n%s", err, raw) + } + if err := ioutil.WriteFile(filepath.Join(*outDir, "data.go"), formatted, 0644); err != nil { + log.Fatalf("WriteFile failed: %s\n", err) + } + + { + b := new(bytes.Buffer) + b.WriteString("// generated by go run genicons.go; DO NOT EDIT\n\npackage ") + b.WriteString(*pkgName) + b.WriteString("\n\n") + b.WriteString("var list = []struct{ name string; data []byte } {\n") + for _, v := range varNames { + fmt.Fprintf(b, "{%q, %s},\n", v, v) + } + b.WriteString("}\n\n") + raw := b.Bytes() + formatted, err := format.Source(raw) + if err != nil { + log.Fatalf("gofmt failed: %v\n\nGenerated code:\n%s", err, raw) + } + if err := ioutil.WriteFile(filepath.Join(*outDir, "data_test.go"), formatted, 0644); err != nil { + log.Fatalf("WriteFile failed: %s\n", err) + } + } +} + +func genDir(dirName string) error { + fqSVGDirName := filepath.FromSlash(dirName) + f, err := os.Open(fqSVGDirName) + if err != nil { + return err + } + defer f.Close() + + infos, err := f.Readdir(-1) + if err != nil { + log.Fatal(err) + } + baseNames, fileNames, sizes := []string{}, map[string]string{}, map[string]int{} + for _, info := range infos { + name := info.Name() + + nameParts := strings.Split(name, "_") + if len(nameParts) != 3 || nameParts[0] != "ic" { + continue + } + baseName := nameParts[1] + var size int + if n, err := fmt.Sscanf(nameParts[2], "%dpx.svg", &size); err != nil || n != 1 { + continue + } + if prevSize, ok := sizes[baseName]; ok { + if size > prevSize { + fileNames[baseName] = name + sizes[baseName] = size + } + } else { + fileNames[baseName] = name + sizes[baseName] = size + baseNames = append(baseNames, baseName) + } + } + + sort.Strings(baseNames) + for _, baseName := range baseNames { + fileName := fileNames[baseName] + path := filepath.Join(dirName, fileName) + f, err := ioutil.ReadFile(path) + if err != nil { + failures = append(failures, fmt.Sprintf("%s: %v", path, err)) + continue + } + if err = genFile(f, baseName, float32(sizes[baseName])); err != nil { + failures = append(failures, fmt.Sprintf("%s: %v", path, err)) + continue + } + } + return nil +} + +type SVG struct { + Width string `xml:"width,attr"` + Height string `xml:"height,attr"` + Fill string `xml:"fill,attr"` + ViewBox string `xml:"viewBox,attr"` + Paths []*Path `xml:"path"` + // Some of the SVG files contain elements, not just + // elements. IconVG doesn't have circles per se. Instead, we convert such + // circles to paired arcTo commands, tacked on to the first path. + // + // In general, this isn't correct if the circles and the path overlap, but + // that doesn't happen in the specific case of the Material Design icons. + Circles []Circle `xml:"circle"` +} + +type Path struct { + D string `xml:"d,attr"` + Fill string `xml:"fill,attr"` + FillOpacity *float32 `xml:"fill-opacity,attr"` + Opacity *float32 `xml:"opacity,attr"` + + creg uint8 +} + +type Circle struct { + Cx float32 `xml:"cx,attr"` + Cy float32 `xml:"cy,attr"` + R float32 `xml:"r,attr"` +} + +func genFile(svgData []byte, baseName string, outSize float32) error { + var varName string + for _, s := range strings.Split(baseName, "_") { + varName += upperCase(s) + } + fmt.Fprintf(out, "var %s = []byte{", varName) + defer fmt.Fprintf(out, "\n}\n\n") + varNames = append(varNames, varName) + + g := &SVG{} + if err := xml.Unmarshal(svgData, g); err != nil { + return err + } + + var vbx, vby, vbx2, vby2 float32 + for i, v := range strings.Split(g.ViewBox, " ") { + f, err := strconv.ParseFloat(v, 32) + if err != nil { + return fmt.Errorf("genFile: failed to parse ViewBox (%q):", g.ViewBox, err) + } + switch i { + case 0: + vbx = float32(f) + case 1: + vby = float32(f) + case 2: + vbx2 = float32(f) + case 3: + vby2 = float32(f) + } + } + dx, dy := outSize, outSize + var size float32 + if aspect := (vbx2 - vbx) / (vby2 - vby); aspect >= 1 { + dy /= aspect + size = vbx2 - vbx + } else { + dx /= aspect + size = vby2 - vby + } + palette := iconvg.DefaultPalette + pmap := make(map[color.RGBA]uint8) + for _, p := range g.Paths { + if p.Fill == "" { + p.Fill = g.Fill + } + c, err := parseColor(p.Fill) + if err != nil { + return err + } + var ok bool + if p.creg, ok = pmap[c]; !ok { + if len(pmap) == 64 { + panic("too many colors") + } + p.creg = uint8(len(pmap)) + palette[p.creg] = c + pmap[c] = p.creg + } + } + var enc iconvg.Encoder + enc.Reset(iconvg.Metadata{ + ViewBox: iconvg.Rectangle{ + Min: f32.Vec2{-dx * .5, -dy * .5}, + Max: f32.Vec2{+dx * .5, +dy * .5}, + }, + Palette: palette, + }) + + offset := f32.Vec2{ + vbx * outSize / size, + vby * outSize / size, + } + + // adjs maps from opacity to a cReg adj value. + adjs := map[float32]uint8{} + + for _, p := range g.Paths { + if err := genPath(&enc, p, adjs, outSize, size, offset, g.Circles); err != nil { + return err + } + g.Circles = nil + } + + if len(g.Circles) != 0 { + if err := genPath(&enc, &Path{}, adjs, outSize, size, offset, g.Circles); err != nil { + return err + } + g.Circles = nil + } + + ivgData, err := enc.Bytes() + if err != nil { + return fmt.Errorf("iconvg encoding failed: %v", err) + } + for i, x := range ivgData { + if i&0x0f == 0x00 { + out.WriteByte('\n') + } + fmt.Fprintf(out, "%#02x, ", x) + } + + totalFiles++ + totalSVGBytes += len(svgData) + totalIVGBytes += len(ivgData) + return nil +} + +func parseColor(col string) (color.RGBA, error) { + if col == "none" { + return color.RGBA{}, nil + } + if len(col) == 0 { + return color.RGBA{A: 0xff}, nil + } + if len(col) == 0 || col[0] != '#' { + return color.RGBA{}, fmt.Errorf("invalid color: %q", col) + } + col = col[1:] + if len(col) != 6 { + return color.RGBA{}, fmt.Errorf("invalid color length: %q", col) + } + elems := make([]byte, len(col)/2) + for i := range elems { + e, err := strconv.ParseUint(col[i*2:i*2+2], 16, 8) + if err != nil { + return color.RGBA{}, err + } + elems[i] = byte(e) + } + return color.RGBA{R: elems[0], G: elems[1], B: elems[2], A: 255}, nil +} + +func genPath(enc *iconvg.Encoder, p *Path, adjs map[float32]uint8, outSize, size float32, offset f32.Vec2, circles []Circle) error { + adj := uint8(0) + opacity := float32(1) + if p.Opacity != nil { + opacity = *p.Opacity + } else if p.FillOpacity != nil { + opacity = *p.FillOpacity + } + if opacity != 1 { + var ok bool + if adj, ok = adjs[opacity]; !ok { + adj = uint8(len(adjs) + 1) + adjs[opacity] = adj + // Set CREG[0-adj] to be a blend of transparent (0x7f) and the + // first custom palette color (0x80). + enc.SetCReg(adj, false, iconvg.BlendColor(uint8(opacity*0xff), 0x7f, 0x80+p.creg)) + } + } else { + enc.SetCReg(adj, false, iconvg.PaletteIndexColor(p.creg)) + } + + needStartPath := true + if p.D != "" { + needStartPath = false + if err := genPathData(enc, adj, p.D, outSize, size, offset); err != nil { + return err + } + } + + for _, c := range circles { + // Normalize. + cx := c.Cx * outSize / size + cx -= outSize/2 + offset[0] + cy := c.Cy * outSize / size + cy -= outSize/2 + offset[1] + r := c.R * outSize / size + + if needStartPath { + needStartPath = false + enc.StartPath(adj, cx-r, cy) + } else { + enc.ClosePathAbsMoveTo(cx-r, cy) + } + + // Convert a circle to two relative arcTo ops, each of 180 degrees. + // We can't use one 360 degree arcTo as the start and end point + // would be coincident and the computation is degenerate. + enc.RelArcTo(r, r, 0, false, true, +2*r, 0) + enc.RelArcTo(r, r, 0, false, true, -2*r, 0) + } + + enc.ClosePathEndPath() + return nil +} + +func genPathData(enc *iconvg.Encoder, adj uint8, pathData string, outSize, size float32, offset f32.Vec2) error { + if strings.HasSuffix(pathData, "z") { + pathData = pathData[:len(pathData)-1] + } + r := strings.NewReader(pathData) + + var args [7]float32 + op, relative, started := byte(0), false, false + var count int + for { + b, err := r.ReadByte() + if err == io.EOF { + break + } + if err != nil { + return err + } + count++ + + switch { + case b == ' ' || b == '\n' || b == '\t': + continue + case 'A' <= b && b <= 'Z': + op, relative = b, false + case 'a' <= b && b <= 'z': + op, relative = b, true + default: + r.UnreadByte() + } + + n := 0 + switch op { + case 'A', 'a': + n = 7 + case 'L', 'l', 'T', 't': + n = 2 + case 'Q', 'q', 'S', 's': + n = 4 + case 'C', 'c': + n = 6 + case 'H', 'h', 'V', 'v': + n = 1 + case 'M', 'm': + n = 2 + case 'Z', 'z': + default: + return fmt.Errorf("unknown opcode %c\n", b) + } + + scan(&args, r, n) + normalize(&args, n, op, outSize, size, offset, relative) + + switch op { + case 'A': + enc.AbsArcTo(args[0], args[1], args[2], args[3] != 0, args[4] != 0, args[5], args[6]) + case 'a': + enc.RelArcTo(args[0], args[1], args[2], args[3] != 0, args[4] != 0, args[5], args[6]) + case 'L': + enc.AbsLineTo(args[0], args[1]) + case 'l': + enc.RelLineTo(args[0], args[1]) + case 'T': + enc.AbsSmoothQuadTo(args[0], args[1]) + case 't': + enc.RelSmoothQuadTo(args[0], args[1]) + case 'Q': + enc.AbsQuadTo(args[0], args[1], args[2], args[3]) + case 'q': + enc.RelQuadTo(args[0], args[1], args[2], args[3]) + case 'S': + enc.AbsSmoothCubeTo(args[0], args[1], args[2], args[3]) + case 's': + enc.RelSmoothCubeTo(args[0], args[1], args[2], args[3]) + case 'C': + enc.AbsCubeTo(args[0], args[1], args[2], args[3], args[4], args[5]) + case 'c': + enc.RelCubeTo(args[0], args[1], args[2], args[3], args[4], args[5]) + case 'H': + enc.AbsHLineTo(args[0]) + case 'h': + enc.RelHLineTo(args[0]) + case 'V': + enc.AbsVLineTo(args[0]) + case 'v': + enc.RelVLineTo(args[0]) + case 'M': + if !started { + started = true + enc.StartPath(adj, args[0], args[1]) + } else { + enc.ClosePathAbsMoveTo(args[0], args[1]) + } + case 'm': + enc.ClosePathRelMoveTo(args[0], args[1]) + } + } + return nil +} + +func scan(args *[7]float32, r *strings.Reader, n int) { + for i := 0; i < n; i++ { + for { + if b, _ := r.ReadByte(); b != ' ' && b != ',' && b != '\n' && b != '\t' { + r.UnreadByte() + break + } + } + fmt.Fscanf(r, "%f", &args[i]) + } +} + +func normalize(args *[7]float32, n int, op byte, outSize, size float32, offset f32.Vec2, relative bool) { + for i := 0; i < n; i++ { + if (op == 'A' || op == 'a') && (i == 3 || i == 4) { + continue + } + args[i] *= outSize / size + if relative { + continue + } + if (op == 'A' || op == 'a') && i < 5 { + // For arcs, skip everything other than x, y. + continue + } + args[i] -= outSize / 2 + switch { + case op == 'A' && i == 5: // Arc x. + args[i] -= offset[0] + case op == 'A' && i == 6: // Arc y. + args[i] -= offset[1] + case n != 1: + args[i] -= offset[i&0x01] + case op == 'H': + args[i] -= offset[0] + case op == 'V': + args[i] -= offset[1] + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..37d7b6f --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module eliasnaur.com/giologo + +go 1.13 + +require ( + golang.org/x/exp v0.0.0-20190927203820-447a159532ef // indirect + golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ee52ca2 --- /dev/null +++ b/go.sum @@ -0,0 +1,23 @@ +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190927203820-447a159532ef h1:0MEfU0Kh8iitbYr+L8WhnyAxLCVa5p0hV8tnPmdGDp0= +golang.org/x/exp v0.0.0-20190927203820-447a159532ef/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ic_gio_48px.svg b/ic_gio_48px.svg new file mode 100644 index 0000000..66f2658 --- /dev/null +++ b/ic_gio_48px.svg @@ -0,0 +1 @@ + \ No newline at end of file -- 2.45.2