~mendelmaleh/dummy

3d3140690e0544a213e1a14eb3e763f3ff5187c1 — Mendel E 1 year, 5 months ago
Initial commit
3 files changed, 224 insertions(+), 0 deletions(-)

A .gitignore
A main.go
A main_test.go
A  => .gitignore +2 -0
@@ 1,2 @@
dummy
config.toml

A  => main.go +190 -0
@@ 1,190 @@
package main

import (
	"errors"
	"fmt"
	"image"
	"image/color"
	"image/png"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"

	"github.com/pelletier/go-toml"
)

type Config struct {
	Dummy struct {
		Addr    string
		Pattern string
	}
}

type Options struct {
	Width, Height int
	Colors        []color.RGBA
}

func Hex(s string) (c color.RGBA, err error) {
	if len(s) < 1 {
		return c, errors.New("can't convert empty string")
	}

	if s[0] == '#' {
		s = s[1:]
	}

	htb := func(b byte) byte {
		switch {
		case '0' <= b && b <= '9':
			return b - '0'
		case 'a' <= b && b <= 'z':
			return b - 'a' + 10
		case 'A' <= b && b <= 'Z':
			return b - 'A' + 10
		}
		err = fmt.Errorf("htb: invalid byte %d\n", b)
		return 0
	}

	c.A = ^uint8(0)
	l := len(s)

	switch {
	case l < 3:
		return color.RGBA{}, errors.New("string needs to have minimum len of 3")
	case l <= 4:
		c.R = htb(s[0]) * 17
		c.G = htb(s[1]) * 17
		c.B = htb(s[2]) * 17

		if l == 4 {
			c.A = htb(s[3]) * 17
		}
	case l == 6 || l == 8:
		c.R = htb(s[0])<<4 + htb(s[1])
		c.G = htb(s[2])<<4 + htb(s[3])
		c.B = htb(s[4])<<4 + htb(s[5])

		if l == 8 {
			c.A = htb(s[6])<<4 + htb(s[7])
		}
	default:
		return color.RGBA{}, fmt.Errorf("invalid length string %q", s)
	}

	return c, err
}

func main() {
	doc, err := ioutil.ReadFile("config.toml")
	if err != nil {
		log.Fatal(err)
	}

	config := Config{}
	err = toml.Unmarshal(doc, &config)
	if err != nil {
		log.Fatal(err)
	}

	dopt := Options{
		Width:  100,
		Height: 100,
	}

	dopt.Colors = make([]color.RGBA, 2)

	c, err := Hex("#242933")
	if err != nil {
		log.Panic(err)
		return
	}
	dopt.Colors[0] = c

	c, err = Hex("#eceff4")
	if err != nil {
		log.Panic(err)
		return
	}
	dopt.Colors[1] = c

	http.HandleFunc(config.Dummy.Pattern,
		func(w http.ResponseWriter, r *http.Request) {
			Dummy(w, r, dopt)
		},
	)

	log.Panic(http.ListenAndServe(config.Dummy.Addr, nil))
}

func Dummy(w http.ResponseWriter, r *http.Request, opt Options) {
	err := r.ParseForm()
	if err != nil {
		fmt.Fprintln(w, err)
		return
	}

	if colors, ok := r.Form["color"]; ok {
		for i, h := range colors {
			c, err := Hex(h)
			if err != nil {
				// error
				continue
			}

			opt.Colors[i] = c
		}
	}

	if w, ok := r.Form["width"]; ok {
		i, err := strconv.Atoi(w[0])
		if err != nil {
			// error
		} else {
			opt.Width = i
		}
	}

	if h, ok := r.Form["height"]; ok {
		i, err := strconv.Atoi(h[0])
		if err != nil {
			// error
		} else {
			opt.Height = i
		}
	}

	// limit width to 600 for now
	if opt.Width > 600 {
		opt.Width = 600
	}

	// TODO: fix rectangles, for now only squares
	opt.Height = opt.Width

	img := Image(opt)
	png.Encode(w, img)

	// fmt.Fprintf(w, "%+v\n", dopt)
}

func Image(opt Options) image.Image {
	img := image.NewRGBA(image.Rectangle{image.Point{}, image.Point{opt.Width, opt.Height}})

	for x := 0; x < opt.Width; x++ {
		for y := 0; y < opt.Height; y++ {
			// if y < opt.Height-((opt.Height/opt.Width)*x) {
			if y <= opt.Height-x {
				img.Set(x, y, opt.Colors[0])
			} else {
				img.Set(x, y, opt.Colors[1])
			}
		}
	}

	// TODO: anti aliasing

	return img
}

A  => main_test.go +32 -0
@@ 1,32 @@
package main

import (
	"image/color"
	"testing"
)

const u8 = ^uint8(0)

func TestHex(t *testing.T) {
	cases := []struct {
		name, hex string
		rgba      color.RGBA
	}{
		{"3 digits", "222", color.RGBA{34, 34, 34, u8}},
		{"3 letters", "fff", color.RGBA{u8, u8, u8, u8}},
		{"# and 3 letters", "#fff", color.RGBA{u8, u8, u8, u8}},

		{"6 digits", "#242933", color.RGBA{36, 41, 51, u8}},
	}

	for _, test := range cases {
		cl, err := Hex(test.hex)
		if err != nil {
			t.Error(err)
		}

		if cl != test.rgba {
			t.Errorf("test %s: expected %v, got %v\n", test.name, test.rgba, cl)
		}
	}
}