diff --git a/cmd/downscale2x/downscale.go b/cmd/downscale2x/downscale.go new file mode 100644 index 0000000..4861c83 --- /dev/null +++ b/cmd/downscale2x/downscale.go @@ -0,0 +1,58 @@ +package main + +import ( + "errors" + "image" + "image/color" +) + +var errOutOfBounds error = errors.New("downscale: out of bounds") + +type Downscaled struct { + image.Image + Factor int +} + +func isOutOfBounds(pt image.Point, img image.Image) bool { + x, y := img.Bounds().Dx(), img.Bounds().Dy() + if pt.X > x || pt.Y > y { + return true + } + return false +} + +func meanColor(palette color.Palette) color.Color { + var r, g, b int + for _, v := range palette { + c := color.RGBAModel.Convert(v).(color.RGBA) + r += int(c.R) + g += int(c.G) + b += int(c.B) + } + r, g, b = r/len(palette), g/len(palette), b/len(palette) + return color.RGBA{uint8(r), uint8(g), uint8(b), 0xff} +} + +func (d Downscaled) ColorModel() color.Model { return d.Image.ColorModel() } +func (d Downscaled) Bounds() image.Rectangle { + return image.Rect(0, 0, d.Image.Bounds().Dx()/d.Factor, d.Image.Bounds().Dy()/d.Factor) +} +func (d Downscaled) At(x, y int) color.Color { + palette := color.Palette{} + pt := image.Point{x * d.Factor, y * d.Factor} + if isOutOfBounds(pt, d.Image) { + panic(errOutOfBounds) + } + for i := 0; i < d.Factor; i++ { + for j := 0; j < d.Factor; j++ { + currentPt := image.Point{pt.X + i, pt.Y + j} + if i > 0 || j > 0 { + if isOutOfBounds(currentPt, d.Image) { + continue + } + } + palette = append(palette, d.Image.At(currentPt.X, currentPt.Y)) + } + } + return palette.Convert(meanColor(palette)) +} diff --git a/cmd/downscale2x/main.go b/cmd/downscale2x/main.go new file mode 100644 index 0000000..a72139b --- /dev/null +++ b/cmd/downscale2x/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "image/jpeg" + "image/png" + "os" +) + +func main() { + // open image + img, err := jpeg.Decode(os.Stdin) + if err != nil { + panic(err) + } + // no pre-processing needed so we just + // write image + err = png.Encode(os.Stdout, Downscaled{img, 3}) + if err != nil { + panic(err) + } +} diff --git a/cmd/mesh/8.clock.png b/cmd/mesh/8.clock.png new file mode 100644 index 0000000..9b047ac Binary files /dev/null and b/cmd/mesh/8.clock.png differ diff --git a/cmd/mesh/9p.go b/cmd/mesh/9p.go new file mode 100644 index 0000000..15c2b07 --- /dev/null +++ b/cmd/mesh/9p.go @@ -0,0 +1,41 @@ +//go:build ignore + +package main + +func ListenAndServe(addr string, filesystem fs.FS) error { + styx.HandlerFunc(func(s *styx.Session) { + for s.Next() { + switch msg := s.Request.(type) { + case styx.Twalk: + msg.Rwalk(fs.Stat(filesystem, msg.Path())) + case styx.Topen: + msg.Ropen(filesystem.Open(msg.Path())) + case styx.Tstat: + msg.Rstat(fs.Stat(filesystem, msg.Path())) + } + } + }, + ) +} + +type FS struct { + meshFile +} + +type meshFile struct { +} + +type meshFileInfo struct { + bufio.Reader + time.Time +} + +func (f meshFileInfo) Name() string { return "mesh" } +func (f meshFileInfo) Size() int64 { return f.Reader.Size() } +func (f meshFileInfo) Mode() fs.FileMode { return fs.ModePerm } +func (f meshFileInfo) ModTime() time.Time { return f.Time } +func (f meshFileInfo) IsDir() bool { return false } +func (f meshFileInfo) Sys() any { return nil } + +func (f meshFile) Stat() (fs.FileInfo, error) { +} diff --git a/cmd/mesh/mesh.go b/cmd/mesh/mesh.go new file mode 100644 index 0000000..b2a6fc5 --- /dev/null +++ b/cmd/mesh/mesh.go @@ -0,0 +1,91 @@ +package main + +import ( + "flag" + "image" + "image/color" + "image/draw" + "image/png" + "os" + "time" + + "github.com/golang/freetype/truetype" + "golang.org/x/image/font" + "golang.org/x/image/font/gofont/gomono" + "golang.org/x/image/math/fixed" + // "github.com/droyo/styx" + // "io/fs" +) + +type Mesh struct { + image.Image + X, Y int // divide +} + +// using source's color.Model +func (mesh Mesh) ColorModel() color.Model { return mesh.Image.ColorModel() } + +// using source's image.Rectangle +func (mesh Mesh) Bounds() image.Rectangle { return mesh.Image.Bounds() } +func (mesh Mesh) At(x, y int) color.Color { + dx, dy := mesh.Image.Bounds().Dx(), mesh.Image.Bounds().Dy() + if (x%(dx/mesh.X)) == 0 || (y%(dy/mesh.Y)) == 0 { + return color.Black + } + return mesh.Image.At(x, y) +} + +func render(img image.Image) draw.Image { + dst := image.NewRGBA(img.Bounds()) + draw.Draw(dst, dst.Bounds(), img, image.Point{}, draw.Src) + return dst +} + +func main() { +// x := flag.Int("x", 2, "use to grid by x axis") +// y := flag.Int("y", 2, "use to grid by y axis") + flag.Parse() // now flags is ready to use + + img, err := png.Decode(os.Stdin) + if err != nil { + panic(err) + } +// Mesh{ // our image modifyer +// Image: img, +// X: *x, +// Y: *y, +// }, + + dst := render( + img, + ) + + f, err := truetype.Parse(gomono.TTF) + if err != nil { + panic(err) + } + face := truetype.NewFace(f, &truetype.Options{ + Size: float64(img.Bounds().Dx() / 19), + DPI: 72, + Hinting: font.HintingNone, + }) + + d := &font.Drawer{ + Dst: dst, + Src: image.NewUniform(color.Black), + Face: face, + Dot: fixed.Point26_6{fixed.Int26_6(img.Bounds().Dx() / + 9*64, + ), + fixed.Int26_6(img.Bounds().Dy() / + 5*64, + ), + }, + } + d.DrawString(time.Now().Format(time.Kitchen)) + + err = png.Encode(os.Stdout, dst) + if err != nil { + panic(err) + } +} diff --git a/cmd/mkpalette/palette.go b/cmd/mkpalette/palette.go new file mode 100644 index 0000000..cd6365d --- /dev/null +++ b/cmd/mkpalette/palette.go @@ -0,0 +1,67 @@ +package main + +import ( + "encoding/csv" + "fmt" + "image/color" + "image/png" + "maps" + "os" + "slices" + + imgutil "git.niplace.ru/XoxJlopeZi4BB/imageutils/pkg" +) + +func check(err error) { + if err != nil { + panic(err) + } +} + +type Set[S comparable] map[S]struct{} + +func (s Set[S]) Add(v S) { + s[v] = struct{}{} +} + +func (s Set[S]) Contains(v S) bool { + _, ok := s[v] + return ok +} + +func Map[S1 ~[]T1, S2 ~[]T2, T1, T2 any](s S1, f func(T1) T2) S2 { + result := make(S2, len(s)) + for i := range s { + result[i] = f(s[i]) + } + return result +} + +type hex string + +func (h hex) String() string { + return string(fmt.Sprintf("hex value: %s\n", string(h))) +} + +func main() { + img, err := png.Decode(os.Stdin) + check(err) + p := Set[color.Color]{} + for y := 0; y < img.Bounds().Dy(); y++ { + for x := 0; x < img.Bounds().Dx(); x++ { + p.Add(img.At(x, y)) + } + } + w := csv.NewWriter(os.Stdout) + s := Map[color.Palette, []string]( + slices.Collect(maps.Keys(p)), imgutil.ColorToHex, + ) + /* + h := make([]hex, 0, len(s)) + for i := range s { + h = append(h, hex(s[i])) + } + */ + err = w.Write(s) + check(err) +} diff --git a/cmd/negate/8.absolutecinema.png b/cmd/negate/8.absolutecinema.png new file mode 100644 index 0000000..43b8023 Binary files /dev/null and b/cmd/negate/8.absolutecinema.png differ diff --git a/cmd/negate/minus19blue b/cmd/negate/minus19blue new file mode 100755 index 0000000..2f4c263 Binary files /dev/null and b/cmd/negate/minus19blue differ diff --git a/cmd/negate/minus19blue.go b/cmd/negate/minus19blue.go new file mode 100644 index 0000000..55d9eed --- /dev/null +++ b/cmd/negate/minus19blue.go @@ -0,0 +1,39 @@ +//go:build ignore + +package main + +import ( + "image" + "image/color" + "image/png" + "os" +) + +type Reshade struct{ image.Image } + +func (rs Reshade) ColorModel() color.Model { return rs.Image.ColorModel() } +func (rs Reshade) Bounds() image.Rectangle { return rs.Image.Bounds() } +func (rs Reshade) At(x, y int) color.Color { + c := rs.Image.At(x, y) + r, g, b, a := c.RGBA() + // minus 19 blue + return color.RGBA{ + uint8(r >> 8), + uint8(g >> 8), + uint8(b>>8) - 8*8, + uint8(a >> 8), + } +} + +func check(err error) { + if err != nil { + panic(err) + } +} + +func main() { + img, err := png.Decode(os.Stdin) + check(err) + err = png.Encode(os.Stdout, Reshade{img}) + check(err) +} diff --git a/cmd/negate/mixed.go b/cmd/negate/mixed.go new file mode 100644 index 0000000..8922940 --- /dev/null +++ b/cmd/negate/mixed.go @@ -0,0 +1,70 @@ +package main + +import ( + "image" + "image/color" + "image/png" + "math/rand" + "os" +) + +type MixedNegation struct { + src image.Image + counter int +} + +func toRGBA(c color.Color) color.RGBA { + r, g, b, a := c.RGBA() + return color.RGBA{ + R: uint8(r >> 8), + G: uint8(g >> 8), + B: uint8(b >> 8), + A: uint8(a >> 8), + } +} + +func negateRGBA(c color.RGBA) color.RGBA { + c.R = 255 - c.R + c.G = 255 - c.G + c.B = 255 - c.B + return c +} + +func Negate(c color.Color) color.Color { + return negateRGBA(toRGBA(c)) +} + +func randomBool() bool { + return rand.Intn(3) == 0 +} + +func (mn MixedNegation) ColorModel() color.Model { return mn.src.ColorModel() } +func (mn MixedNegation) Bounds() image.Rectangle { return mn.src.Bounds() } +func (mn *MixedNegation) At(x, y int) color.Color { + if (x%1 != 0) && (y%8 != 0) { + if randomBool() { + return Negate(mn.src.At(x, y)) + } + + } + return mn.src.At(x, y) +} + +func pixelCount(img image.Image) int { + return img.Bounds().Dx() * img.Bounds().Dy() +} + +func main() { + img, err := png.Decode(os.Stdin) + if err != nil { + panic(err) + } + + n := pixelCount(img) + println(n) + + err = png.Encode(os.Stdout, &MixedNegation{src: img, counter: n}) + if err != nil { + panic(err) + } +} diff --git a/cmd/negate/negate b/cmd/negate/negate new file mode 100755 index 0000000..f8f17d8 Binary files /dev/null and b/cmd/negate/negate differ diff --git a/cmd/negate/negate.go b/cmd/negate/negate.go new file mode 100644 index 0000000..3608fb0 --- /dev/null +++ b/cmd/negate/negate.go @@ -0,0 +1,44 @@ +package main + +import ( + "image" + "image/color" + "image/png" + "os" +) + +func NegateRGBA(c color.RGBA) color.RGBA { + c.R = 255 - c.R + c.G = 255 - c.G + c.B = 255 - c.B + return c +} + +type Negate struct{ image.Image } + +func (n Negate) ColorModel() color.Model { return n.Image.ColorModel() } +func (n Negate) Bounds() image.Rectangle { return n.Image.Bounds() } +func (n Negate) At(x, y int) color.Color { + c := n.Image.At(x, y) + r, g, b, a := c.RGBA() + RGBAColor := color.RGBA{ + R: uint8(r >> 8), + G: uint8(g >> 8), + B: uint8(b >> 8), + A: uint8(a >> 8), + } + return NegateRGBA(RGBAColor) +} + +func check(err error) { + if err != nil { + panic(err) + } +} + +func main() { + img, err := png.Decode(os.Stdin) + check(err) + err = png.Encode(os.Stdout, Negate{img}) + check(err) +} diff --git a/cmd/negate/negated5.png b/cmd/negate/negated5.png new file mode 100644 index 0000000..816ff41 Binary files /dev/null and b/cmd/negate/negated5.png differ diff --git a/cmd/negate/negated8.png b/cmd/negate/negated8.png new file mode 100644 index 0000000..a98d87f Binary files /dev/null and b/cmd/negate/negated8.png differ diff --git a/cmd/negate/reshaded,negated8.png b/cmd/negate/reshaded,negated8.png new file mode 100644 index 0000000..e3eb1fa Binary files /dev/null and b/cmd/negate/reshaded,negated8.png differ diff --git a/cmd/showpalette/.show.go.swp b/cmd/showpalette/.show.go.swp new file mode 100644 index 0000000..1e47865 Binary files /dev/null and b/cmd/showpalette/.show.go.swp differ diff --git a/cmd/showpalette/show.go b/cmd/showpalette/show.go new file mode 100644 index 0000000..f3936b9 --- /dev/null +++ b/cmd/showpalette/show.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/csv" + "image" + "image/color" + "image/png" + "math" + "os" + + imgutil "git.niplace.ru/XoxJlopeZi4BB/imageutils/pkg" +) + +func nearestHighSqrt(n int) int { + if math.Mod(math.Sqrt(float64(n)), 1) == 0 { + return n + } + return nearestHighSqrt(n + 1) +} + +type showColors struct { + color.Palette +} + +func (s showColors) ColorModel() color.Model { + return color.RGBAModel +} + +func (s showColors) Bounds() image.Rectangle { + n := nearestHighSqrt(len(s.Palette)) + return image.Rect(0, 0, n, n) +} + +func (s showColors) At(x, y int) color.Color { + n := nearestHighSqrt(len(s.Palette)) + return s.Palette[y*n+x] +} + +func check(err error) { + if err != nil { + panic(err) + } +} + +func Must[T any](v T, err error) T { + check(err) + return v +} + +func Map[S1 ~[]T1, S2 ~[]T2, T1, T2 any](s S1, f func(T1) T2) S2 { + result := make(S2, len(s)) + for i := range s { + result[i] = f(s[i]) + } + return result +} + +func main() { + r := csv.NewReader(os.Stdin) + r.TrailingComma = true + colors, err := r.Read() + check(err) + palette := color.Palette( + Map[[]string, color.Palette](colors, + func(v string) color.Color { + println(v + "cat-v") + return Must(imgutil.ParseHexColor(v)) + }, + ), + ) + + err = png.Encode(os.Stdout, showColors{palette}) + check(err) +} diff --git a/go.mod b/go.mod index 28057c4..22bfd08 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,13 @@ module git.niplace.ru/XoxJlopeZi4BB/imageutils -go 1.22.6 +go 1.24.0 -require github.com/potassium5703/texture v0.0.0-20240820054037-fce43fc4b0f0 // indirect +require ( + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + golang.org/x/image v0.33.0 +) + +require ( + 9fans.net/go v0.0.7 // indirect + github.com/potassium5703/texture v0.0.0-20240820054037-fce43fc4b0f0 // indirect +) diff --git a/go.sum b/go.sum index 2a7a0cd..1a19b10 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,40 @@ +9fans.net/go v0.0.7 h1:H5CsYJTf99C8EYAQr+uSoEJnLP/iZU8RmDuhyk30iSM= +9fans.net/go v0.0.7/go.mod h1:Rxvbbc1e+1TyGMjAvLthGTyO97t+6JMQ6ly+Lcs9Uf0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/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/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/potassium5703/texture v0.0.0-20240820054037-fce43fc4b0f0 h1:sowjIIVme5ovcdB0kjP3w+4xbVNrlOPdx7Up4LIGJz8= github.com/potassium5703/texture v0.0.0-20240820054037-fce43fc4b0f0/go.mod h1:KwM7hMpZhr3XySWuK/SZ7s1BXVQe8p1IyZftYg9KtWY= +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/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/exp v0.0.0-20210405174845-4513512abef3/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= +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.33.0 h1:LXRZRnv1+zGd5XBUVRFmYEphyyKJjQjCRiOuAP3sZfQ= +golang.org/x/image v0.33.0/go.mod h1:DD3OsTYT9chzuzTQt+zMcOlBHgfoKQb1gry8p76Y1sc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/.hex_test.go.swp b/pkg/.hex_test.go.swp new file mode 100644 index 0000000..9f578a3 Binary files /dev/null and b/pkg/.hex_test.go.swp differ diff --git a/pkg/hex.go b/pkg/hex.go new file mode 100644 index 0000000..65aa065 --- /dev/null +++ b/pkg/hex.go @@ -0,0 +1,32 @@ +package imageutils + +import ( + "fmt" + "image/color" + "errors" +) + +var ParseHexColorErr = errors.New("invalid length, must be 7 or 4") + +func ParseHexColor(s string) (c color.RGBA, err error) { + c.A = 0xff + switch len(s) { + case 7: + _, err = fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B) + case 4: + _, err = fmt.Sscanf(s, "#%1x%1x%1x", &c.R, &c.G, &c.B) + // Double the hex digits: + c.R *= 17 + c.G *= 17 + c.B *= 17 + default: + err = ParseHexColorErr + + } + return +} + +func ColorToHex(c color.Color) string { + r, g, b, _ := color.NRGBAModel.Convert(c).RGBA() + return fmt.Sprintf("#%02x%02x%02x", byte(r), byte(g), byte(b)) +} diff --git a/pkg/hex_test.go b/pkg/hex_test.go new file mode 100644 index 0000000..6a0123c --- /dev/null +++ b/pkg/hex_test.go @@ -0,0 +1,47 @@ +package imageutils + +import ( + "fmt" + "image/png" + "os" + "path/filepath" + "testing" +) + +// Returns path of first occured file +// with png extension in user's home directory. +func firstPNG(root string) (filename string, + err error) { + var fn filepath.WalkFunc = func(path string, + info os.FileInfo, + fileErr error) error { + if fileErr != nil { + return fileErr + } + if !info.IsDir() && + filepath.Ext(path) == ".png" { + filename = path + return nil + } + return nil + } + err = filepath.Walk(root, fn) + return +} + +func TestColorToHex(t *testing.T) { + root := os.Getenv("HOME") + path, err := firstPNG(root) + if err != nil { + t.Fatal(err) + } + f, err := os.Open(path) + if err != nil { + t.Fatal(err) + } + img, err := png.Decode(f) + if err != nil { + t.Fatal(err) + } + fmt.Println(ColorToHex(img.At(0, 0))) +} diff --git a/shakalizator.go b/shakalizator.go new file mode 100644 index 0000000..b94cb9b --- /dev/null +++ b/shakalizator.go @@ -0,0 +1 @@ +package imageutils