Compare commits
31 Commits
1.0.0
...
c8bfe0777b
| Author | SHA1 | Date | |
|---|---|---|---|
| c8bfe0777b | |||
| f5ff5d75bb | |||
| 559a4e9175 | |||
| 67ff655767 | |||
| 824a4ede47 | |||
| 3880b353c1 | |||
| 7143e96ef3 | |||
| 3677407f05 | |||
| 84d241e619 | |||
| b9808c8150 | |||
| e5c0537f2b | |||
| c25b65a5f6 | |||
| 1122be9007 | |||
| dc5f2466d1 | |||
| 0bbccec989 | |||
| c5eefdec1f | |||
| 352631e071 | |||
| cc5c9c6d1a | |||
| d52658ab5c | |||
| 1db672d481 | |||
| 02ece86303 | |||
| 2709034fc3 | |||
| aadc89bca0 | |||
| 11aa6e044b | |||
| c577804946 | |||
| 7c1911bbda | |||
| a49ce499e2 | |||
| a78768aeb8 | |||
| e6889deab6 | |||
| 73d114ef5b | |||
| d4aec6bcaf |
@@ -0,0 +1,4 @@
|
|||||||
|
child_porn
|
||||||
|
imageutils.test
|
||||||
|
cpu.out
|
||||||
|
profile.out
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# imageutils
|
||||||
|
## that is exactly image utilities, what i made for myself
|
||||||
|
|
||||||
|
||(with a dummy as fuck image rescaling)||
|
||||||
|
utilities work in theory, but doing everything very slow.
|
||||||
|
i really love Go's image.Image interface type, but its really hard to do something with it well.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- create main binary that give access to all implemented things in cmd/
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package imageutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go.dev/blog/image-draw#drawing-through-a-mask
|
||||||
|
type Circle struct {
|
||||||
|
image.Point
|
||||||
|
R int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Circle) ColorModel() color.Model { return color.Alpha16Model }
|
||||||
|
func (c Circle) Bounds() image.Rectangle {
|
||||||
|
return image.Rect(c.Point.X-c.R, c.Point.Y-c.R, c.Point.X+c.R, c.Point.Y+c.R)
|
||||||
|
}
|
||||||
|
func (c Circle) At(x, y int) color.Color {
|
||||||
|
xx, yy, rr := float64(x-c.Point.X)+0.5, float64(y-c.Point.Y)+0.5, float64(c.R)
|
||||||
|
if xx*xx+yy*yy < rr*rr {
|
||||||
|
return color.Opaque
|
||||||
|
}
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/pkg/dithering"
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var factor = flag.Float64("n", 0.75, "dithering effect 'magic' number")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
println(*factor)
|
||||||
|
if err := util.ProcessStdio(func(img image.Image) image.Image {
|
||||||
|
return dithering.Dithering{img, *factor}
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
img, err := jpeg.Decode(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = png.Encode(os.Stdout, img)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 main() {
|
||||||
|
if err := util.ProcessStdio(func(img image.Image) image.Image {
|
||||||
|
return Negate{img}
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"image/png"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
iu "git.nkpl.cc/twocookedfaggots/imageutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func square(side int) image.Rectangle { return image.Rect(0, 0, side, side) }
|
||||||
|
|
||||||
|
func Circles(b image.Rectangle, radius int) chan iu.Circle {
|
||||||
|
ch := make(chan iu.Circle)
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
for {
|
||||||
|
x, y := rand.Int()%b.Dx(), rand.Int()%b.Dy()
|
||||||
|
dxdy := b.Dx() * b.Dy()
|
||||||
|
r := rand.Int() % (dxdy / (dxdy / radius))
|
||||||
|
ch <- iu.Circle{image.Point{x, y}, r}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
var exchange = color.ModelFunc(
|
||||||
|
func(c color.Color) color.Color {
|
||||||
|
if color.Alpha16Model.Convert(c) == color.Transparent{
|
||||||
|
return color.Opaque
|
||||||
|
}
|
||||||
|
return color.Transparent
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
type Exchange struct { image.Image }
|
||||||
|
|
||||||
|
func (e Exchange) ColorModel() color.Model { return color.Alpha16Model }
|
||||||
|
func (e Exchange) Bounds() image.Rectangle { return e.Image.Bounds() }
|
||||||
|
func (e Exchange) At(x, y int) color.Color {
|
||||||
|
c := e.ColorModel().Convert(e.Image.At(x, y))
|
||||||
|
return exchange.Convert(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
var blackAndWhite = color.ModelFunc(
|
||||||
|
func(c color.Color) color.Color {
|
||||||
|
if color.Alpha16Model.Convert(c) == color.Transparent{
|
||||||
|
return color.Black
|
||||||
|
}
|
||||||
|
return color.White
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Black and white's alpha image.
|
||||||
|
type BlackAndWhite struct { image.Image }
|
||||||
|
|
||||||
|
func (b BlackAndWhite) ColorModel() color.Model { return color.GrayModel }
|
||||||
|
func (b BlackAndWhite) Bounds() image.Rectangle { return b.Image.Bounds() }
|
||||||
|
func (b BlackAndWhite) At(x, y int) color.Color {
|
||||||
|
c := b.Image.At(x, y)
|
||||||
|
return blackAndWhite.Convert(c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//go:build colored
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
const (
|
||||||
|
red = iota
|
||||||
|
green
|
||||||
|
blue
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
canvas := image.NewRGBA(image.Rect(0, 0, 3840, 2160))
|
||||||
|
circles := Circles(canvas.Bounds(), 2160/6)
|
||||||
|
|
||||||
|
palette := []color.Color{
|
||||||
|
color.RGBA{R:255, A:255},
|
||||||
|
color.RGBA{G:255, A:255},
|
||||||
|
color.RGBA{B:255, A:255},
|
||||||
|
}
|
||||||
|
c := palette[rand.Int()%2]
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
canvas := image.NewRGBA(image.Rect(0, 0, 3840, 2160))
|
||||||
|
circles := Circles(canvas.Bounds(), 2160/6)
|
||||||
|
|
||||||
|
// draw n circles
|
||||||
|
for i := 0; i < 24; i++ {
|
||||||
|
c := <-circles
|
||||||
|
|
||||||
|
draw.Draw(canvas, c.Bounds(), xor{c, canvas}, c.Bounds().Min, draw.Src)
|
||||||
|
/*
|
||||||
|
draw.DrawMask(
|
||||||
|
canvas, canvas.Bounds(), &image.Uniform{color.White},
|
||||||
|
image.Point{}, xor{c, canvas}, image.Point{}, draw.Src,
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
newCanvas := image.NewRGBA(canvas.Bounds())
|
||||||
|
draw.Draw(newCanvas, newCanvas.Bounds(), &image.Uniform{color.RGBA{255, 255, 0, 255}}, image.ZP, draw.Over)
|
||||||
|
draw.Draw(newCanvas, newCanvas.Bounds(), canvas, image.ZP, draw.Over)
|
||||||
|
*/
|
||||||
|
err := png.Encode(os.Stdout, BlackAndWhite{canvas})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
var Opaque, Transparent = color.Alpha{0xff}, color.Alpha{0}
|
||||||
|
|
||||||
|
type xor struct{ dst, src image.Image }
|
||||||
|
|
||||||
|
func (t xor) ColorModel() color.Model { return t.dst.ColorModel() }
|
||||||
|
func (t xor) Bounds() image.Rectangle { return t.src.Bounds() }
|
||||||
|
func (t xor) At(x, y int) color.Color {
|
||||||
|
dstColor := t.ColorModel().Convert(t.dst.At(x, y))
|
||||||
|
srcColor := t.ColorModel().Convert(t.src.At(x, y))
|
||||||
|
|
||||||
|
// xor operation (a || b) && !(a && b)
|
||||||
|
if ((dstColor == color.Opaque) || (srcColor == color.Opaque)) &&
|
||||||
|
!((dstColor == color.Opaque) && (srcColor == color.Opaque)) {
|
||||||
|
return color.Opaque
|
||||||
|
}
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
iu "git.nkpl.cc/twocookedfaggots/imageutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
iu "git.nkpl.cc/twocookedfaggots/imageutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func square(side int) image.Rectangle { return image.Rect(0, 0, side, side) }
|
||||||
|
|
||||||
|
func Circles(b image.Rectangle, radius int) chan iu.Circle {
|
||||||
|
ch := make(chan iu.Circle)
|
||||||
|
go func() {
|
||||||
|
defer close(ch)
|
||||||
|
for {
|
||||||
|
x, y := rand.Int()%b.Dx(), rand.Int()%b.Dy()
|
||||||
|
dxdy := b.Dx() * b.Dy()
|
||||||
|
r := rand.Int() % (dxdy / (dxdy / radius))
|
||||||
|
ch <- iu.Circle{image.Point{x, y}, r}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
var Opaque, Transparent = color.Alpha{0xff}, color.Alpha{0}
|
||||||
|
|
||||||
|
type xor struct{ dst, src image.Image }
|
||||||
|
|
||||||
|
func (t xor) ColorModel() color.Model { return t.dst.ColorModel() }
|
||||||
|
func (t xor) Bounds() image.Rectangle { return t.src.Bounds() }
|
||||||
|
func (t xor) At(x, y int) color.Color {
|
||||||
|
dstColor := t.ColorModel().Convert(t.dst.At(x, y))
|
||||||
|
srcColor := t.ColorModel().Convert(t.src.At(x, y))
|
||||||
|
|
||||||
|
// xor operation (a || b) && !(a && b)
|
||||||
|
if ((dstColor == color.Opaque) || (srcColor == color.Opaque)) &&
|
||||||
|
!((dstColor == color.Opaque) && (srcColor == color.Opaque)) {
|
||||||
|
return color.Opaque
|
||||||
|
}
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
var exchange = color.ModelFunc(
|
||||||
|
func(c color.Color) color.Color {
|
||||||
|
if color.Alpha16Model.Convert(c) == color.Transparent {
|
||||||
|
return color.Opaque
|
||||||
|
}
|
||||||
|
return color.Transparent
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
type Exchange struct{ image.Image }
|
||||||
|
|
||||||
|
func (e Exchange) ColorModel() color.Model { return color.Alpha16Model }
|
||||||
|
func (e Exchange) Bounds() image.Rectangle { return e.Image.Bounds() }
|
||||||
|
func (e Exchange) At(x, y int) color.Color {
|
||||||
|
c := e.ColorModel().Convert(e.Image.At(x, y))
|
||||||
|
return exchange.Convert(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
var blackAndWhite = color.ModelFunc(
|
||||||
|
func(c color.Color) color.Color {
|
||||||
|
if color.Alpha16Model.Convert(c) == color.Transparent {
|
||||||
|
return color.Black
|
||||||
|
}
|
||||||
|
return color.White
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Black and white's alpha image.
|
||||||
|
type BlackAndWhite struct{ image.Image }
|
||||||
|
|
||||||
|
func (b BlackAndWhite) ColorModel() color.Model { return color.Gray16Model }
|
||||||
|
func (b BlackAndWhite) Bounds() image.Rectangle { return b.Image.Bounds() }
|
||||||
|
func (b BlackAndWhite) At(x, y int) color.Color {
|
||||||
|
c := b.Image.At(x, y)
|
||||||
|
return blackAndWhite.Convert(c)
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XORMask struct {
|
||||||
|
dst, src image.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m XORMask) ColorModel() color.Model { return color.Gray16Model }
|
||||||
|
func (m XORMask) Bounds() image.Rectangle { return m.dst.Bounds() }
|
||||||
|
func (m XORMask) At(x, y int) color.Color {
|
||||||
|
srcColor := m.src.At(x, y)
|
||||||
|
dstColor := m.dst.At(x, y)
|
||||||
|
|
||||||
|
if ((dstColor == color.Black) || (srcColor == color.Black)) &&
|
||||||
|
!((dstColor == color.Black) && (srcColor == color.Black)) {
|
||||||
|
return color.Black
|
||||||
|
}
|
||||||
|
return color.White
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
img, err := png.Decode(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
grayImg := image.NewGray16(img.Bounds())
|
||||||
|
for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
|
||||||
|
for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
|
||||||
|
c := img.At(x, y)
|
||||||
|
grayImg.Set(x, y, color.Gray16Model.Convert(c))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas := image.NewRGBA(img.Bounds())
|
||||||
|
circle := Circles(canvas.Bounds(), canvas.Bounds().Dy()/6)
|
||||||
|
for i := 0; i < 24; i++ {
|
||||||
|
c := <-circle
|
||||||
|
draw.Draw(canvas, c.Bounds(), xor{c, canvas}, c.Bounds().Min, draw.Src)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = png.Encode(
|
||||||
|
os.Stdout, XORMask{
|
||||||
|
grayImg,
|
||||||
|
BlackAndWhite{
|
||||||
|
Exchange{
|
||||||
|
canvas,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//go:build upsampling
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils"
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var scale = flag.Int("n", 2, "rescale by factor")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
if err := util.ProcessStdio(func(img image.Image) image.Image {
|
||||||
|
return imageutils.Scale(img, *scale)
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/pkg/downscale"
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var x = flag.Int("x", 256, "dx value of new bounds")
|
||||||
|
var y = flag.Int("y", 256, "dy value of new bounds")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
if err := util.ProcessStdio(func(img image.Image) image.Image {
|
||||||
|
return downscale.GridDownscale{
|
||||||
|
Image: img,
|
||||||
|
Rectangle: image.Rect(0,0,x,y),
|
||||||
|
}
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
var (
|
||||||
|
x = flag.Int("x", 0, "")
|
||||||
|
y = flag.Int("y", 0, "")
|
||||||
|
size = flag.Int("size", 24, "size of font")
|
||||||
|
// img = flag.String("path", "", "path to image")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
f, err := truetype.Parse(gomono.TTF)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
face := truetype.NewFace(f, &truetype.Options{
|
||||||
|
Size: float64(img.Bounds().Dx() / size),
|
||||||
|
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() /
|
||||||
|
x,
|
||||||
|
),
|
||||||
|
fixed.Int26_6(img.Bounds().Dy() /
|
||||||
|
y,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
d.DrawString(time.Now().Format(time.Kitchen))
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "image/color"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c1, c2 := color.Black,
|
||||||
|
color.Gray16Model.Convert(color.RGBA{})
|
||||||
|
|
||||||
|
println(c1.RGBA())
|
||||||
|
println(c2.RGBA())
|
||||||
|
println(c1 == c2)
|
||||||
|
}
|
||||||
@@ -21,9 +21,9 @@ type pair struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Takes color.Model of the first Image.
|
// Takes color.Model of the first Image.
|
||||||
func (p pair) ColorModel() color.Model { return p.first.ColorModel() }
|
func (p *pair) ColorModel() color.Model { return p.first.ColorModel() }
|
||||||
|
|
||||||
func (p pair) Bounds() image.Rectangle {
|
func (p *pair) Bounds() image.Rectangle {
|
||||||
var (
|
var (
|
||||||
b1 = p.first.Bounds()
|
b1 = p.first.Bounds()
|
||||||
b2 = p.second.Bounds()
|
b2 = p.second.Bounds()
|
||||||
@@ -52,7 +52,7 @@ func (p pair) Bounds() image.Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p pair) At(x, y int) color.Color {
|
func (p *pair) At(x, y int) color.Color {
|
||||||
img := image.NewRGBA(p.Bounds())
|
img := image.NewRGBA(p.Bounds())
|
||||||
|
|
||||||
point := image.Point{}
|
point := image.Point{}
|
||||||
@@ -86,7 +86,7 @@ func (p pair) At(x, y int) color.Color {
|
|||||||
return img.At(x, y)
|
return img.At(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func render(p pair) image.Image {
|
func render(p *pair) image.Image {
|
||||||
img := image.NewRGBA(p.Bounds())
|
img := image.NewRGBA(p.Bounds())
|
||||||
|
|
||||||
point := image.Point{}
|
point := image.Point{}
|
||||||
@@ -122,5 +122,5 @@ func render(p pair) image.Image {
|
|||||||
|
|
||||||
// Concat concatenates second image on a given side.
|
// Concat concatenates second image on a given side.
|
||||||
func Concat(i1, i2 image.Image, s Side) image.Image {
|
func Concat(i1, i2 image.Image, s Side) image.Image {
|
||||||
return render(pair{i1, i2, s})
|
return render(&pair{i1, i2, s})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package imageutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkConcat(b *testing.B) {
|
||||||
|
var instance image.Image = image.NewRGBA(image.Rect(0, 0, 0, 0))
|
||||||
|
pixel := SinglePixel{}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
instance = Concat(instance, pixel, Right)
|
||||||
|
}
|
||||||
|
err := png.Encode(
|
||||||
|
io.Discard,
|
||||||
|
instance,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
go test -cpuprofile=profile.out -bench=$1 &&
|
||||||
|
go tool pprof -text profile.out
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
module github.com/potassium5703/imageutils
|
module git.nkpl.cc/twocookedfaggots/imageutils
|
||||||
|
|
||||||
go 1.21.4
|
go 1.25.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
|
||||||
|
golang.org/x/image v0.43.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dbriemann/pixel v0.5.1 // indirect
|
||||||
|
github.com/go-gl/mathgl v1.2.0 // indirect
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
github.com/dbriemann/pixel v0.5.1 h1:9iPqkfolOW2VRu8IzEbUBii9/S5ubMwPTQP7xBGkNX8=
|
||||||
|
github.com/dbriemann/pixel v0.5.1/go.mod h1:iWjzS4T3euA4FW04ULyz3SCkZB5LyK7pwTR1vyZFv0E=
|
||||||
|
github.com/go-gl/mathgl v1.2.0 h1:v2eOj/y1B2afDxF6URV1qCYmo1KW08lAMtTbOn3KXCY=
|
||||||
|
github.com/go-gl/mathgl v1.2.0/go.mod h1:pf9+b5J3LFP7iZ4XXaVzZrCle0Q/vNpB/vDe5+3ulRE=
|
||||||
|
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=
|
||||||
|
golang.org/x/image v0.36.0 h1:Iknbfm1afbgtwPTmHnS2gTM/6PPZfH+z2EFuOkSbqwc=
|
||||||
|
golang.org/x/image v0.36.0/go.mod h1:YsWD2TyyGKiIX1kZlu9QfKIsQ4nAAK9bdgdrIsE7xy4=
|
||||||
|
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
|
||||||
|
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package blank
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type blank struct {
|
||||||
|
image.Image
|
||||||
|
color.Color
|
||||||
|
imageutils.Side
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b blank) ColorModel() color.Model { return b.Image.ColorModel() }
|
||||||
|
|
||||||
|
func (b blank) Bounds() image.Rectangle {
|
||||||
|
rect := image.Rectangle{}
|
||||||
|
switch b.Side {
|
||||||
|
case imageutils.Left:
|
||||||
|
fallthrough
|
||||||
|
case imageutils.Right:
|
||||||
|
rect = image.Rectangle{
|
||||||
|
image.ZP,
|
||||||
|
image.Point{
|
||||||
|
1, b.Image.Bounds().Dy(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case imageutils.Up:
|
||||||
|
fallthrough
|
||||||
|
case imageutils.Down:
|
||||||
|
rect = image.Rectangle{
|
||||||
|
image.ZP,
|
||||||
|
image.Point{
|
||||||
|
b.Image.Bounds().Dx(), 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rect
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b blank) At(x, y int) color.Color { return b.Color }
|
||||||
|
|
||||||
|
func Blank(img image.Image, clr color.Color, side imageutils.Side) image.Image {
|
||||||
|
return imageutils.Concat(img, blank{img, clr, side}, side)
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type imageTransform struct {
|
||||||
|
image.Image
|
||||||
|
TransformFunc func(image.Image) image.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlankFill struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t imageTransform) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexHTML(w http.ResponseWriter, r *http.Request) {
|
||||||
|
f, err := os.Open("")
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
log.Fatalln("error opening file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err := io.Copy(w, f)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
log.Println("copy error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", indexHTML)
|
||||||
|
http.Handle("POST /upload", imageTransform)
|
||||||
|
|
||||||
|
err := http.ListenAndServe(":8080", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<form action="/upload" method="POST" enctype="multipart/form-data">
|
||||||
|
<input type="file" name="image" accept="image/*" required>
|
||||||
|
<button type="submit">Upload Image</button>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
//go:build ignore
|
||||||
|
|
||||||
|
// works as i wanted
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"image/jpeg"
|
||||||
|
"os"
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils"
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/http/blank"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BlankFillWhite(img image.Image) image.Image {
|
||||||
|
dy := img.Bounds().Dy()
|
||||||
|
up, down := dy*2/3, dy*1/3
|
||||||
|
for i := 0; i < up; i++ {
|
||||||
|
img = blank.Blank(img, color.White, imageutils.Up)
|
||||||
|
}
|
||||||
|
for i := 0; i < down; i++ {
|
||||||
|
img = blank.Blank(img, color.White, imageutils.Down)
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessImageStdio(f func(image.Image) image.Image) error {
|
||||||
|
img, err := jpeg.Decode(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
img = f(img)
|
||||||
|
err = png.Encode(os.Stdout, img)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := ProcessImageStdio(
|
||||||
|
BlankFillWhite,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package center
|
||||||
|
|
||||||
|
type Into struct {
|
||||||
|
Src, Dst image.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Into) Bounds() image.Rectangle { return i.Dst.Bounds() }
|
||||||
|
func (i Into) ColorModel() color.Model { return i.Dst.ColorModel }
|
||||||
|
func (i Into) At(x, y int) color.Color {
|
||||||
|
x1, y1 := (i.Dst.Bounds().Dx()/2)-(i.Src.Bounds().Dx()/2),
|
||||||
|
(i.Dst.Bounds().Dy()/2)-(i.Src.Bounds().Dy()/2)
|
||||||
|
r := image.Rect(x1, y1,
|
||||||
|
x1+i.Src.Bounds().Dx(), y1+i.Src.Bounds().Dy())
|
||||||
|
if (image.Point{x, y}).In(r) {
|
||||||
|
return i.Src.At(x-x1,y-y2)
|
||||||
|
}
|
||||||
|
return i.Dst.At(x, y)
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
залил как есть.
|
||||||
|
любая кринжовая хуйня которая здесь найдется... просто зайбейте на это хуй, не обращайте внимания
|
||||||
|
|
||||||
|
запускается значится так:
|
||||||
|
заходите в cmd и...
|
||||||
|
> go run .
|
||||||
|
|
||||||
|
заходите в бровз и пишите адрес с портом
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
//go:build draft
|
||||||
|
package clock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.niplace.ru/XoxJlopeZi4BB/clock/letter"
|
||||||
|
"git.niplace.ru/XoxJlopeZi4BB/imageutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var img1, img2 = letter.Render(letter.Nums[6]), letter.Render(letter.Nums[9])
|
||||||
|
|
||||||
|
func DownRightBlank(img image.Image) image.Image {
|
||||||
|
clr := color.White
|
||||||
|
img = letter.Blank(img, clr, imageutils.Down)
|
||||||
|
return letter.Blank(img, clr, imageutils.Right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LeftUpBlank(img image.Image) image.Image {
|
||||||
|
clr := color.White
|
||||||
|
img = letter.Blank(img, clr, imageutils.Left)
|
||||||
|
return letter.Blank(img, clr, imageutils.Up)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderNum(n int) image.Image {
|
||||||
|
var img image.Image = image.NewRGBA(image.Rect(0, 0, 0, 0))
|
||||||
|
s := strconv.Itoa(n)
|
||||||
|
for i := range s {
|
||||||
|
n, err := strconv.Atoi(string(s[i]))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
num := letter.Render(letter.Nums[n])
|
||||||
|
num = DownRightBlank(num)
|
||||||
|
img = imageutils.Concat(img, num, imageutils.Right)
|
||||||
|
}
|
||||||
|
img = LeftUpBlank(img)
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderTime(t time.Time) image.Image {
|
||||||
|
hour := RenderNum(t.Hour())
|
||||||
|
if t.Hour() < 10 {
|
||||||
|
hour = imageutils.Concat(
|
||||||
|
DownRightBlank(letter.Empty), hour, imageutils.Right)
|
||||||
|
}
|
||||||
|
minute := RenderNum(t.Minute())
|
||||||
|
if t.Minute() < 10 {
|
||||||
|
minute = imageutils.Concat(
|
||||||
|
DownRightBlank(letter.Empty), minute, imageutils.Right)
|
||||||
|
}
|
||||||
|
|
||||||
|
img := imageutils.Concat(hour, minute, imageutils.Down)
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func Margin(img image.Image, length int) image.Image {
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
img = LeftUpBlank(DownRightBlank(img))
|
||||||
|
}
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func Time(w io.Writer) {
|
||||||
|
t := time.Now()
|
||||||
|
img := RenderTime(t)
|
||||||
|
img = Margin(img, 3)
|
||||||
|
img = imageutils.Scale(img, 1<<6)
|
||||||
|
|
||||||
|
err := png.Encode(w, img)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build ignore
|
||||||
|
package clock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkNumber(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Numbers(io.Discard)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.niplace.ru/XoxJlopeZi4BB/clock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
addrwithport := ":8080"
|
||||||
|
http.HandleFunc("/",
|
||||||
|
func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
clock.Time(w)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
println("this thingy running on", addrwithport)
|
||||||
|
err := http.ListenAndServe(addrwithport, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
d, err := time.ParseDuration("1s")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
t := time.Tick(d)
|
||||||
|
go func() {
|
||||||
|
fmt.Printf("\033[2J\033[H")
|
||||||
|
for {
|
||||||
|
<-t
|
||||||
|
fmt.Printf("\r%s", time.Now().Format(time.TimeOnly))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
module git.niplace.ru/XoxJlopeZi4BB/clock
|
||||||
|
|
||||||
|
go 1.25.3
|
||||||
|
|
||||||
|
require git.niplace.ru/XoxJlopeZi4BB/imageutils v0.0.0-20241224134015-2709034fc322
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
git.niplace.ru/XoxJlopeZi4BB/imageutils v0.0.0-20241224134015-2709034fc322 h1:jefEdqKZ/JKnmky9LipaS/UH0phAhz/56ORHIjmHREk=
|
||||||
|
git.niplace.ru/XoxJlopeZi4BB/imageutils v0.0.0-20241224134015-2709034fc322/go.mod h1:QieEgSGk9ZYISDv5XyMMktQXi/2qlTmdHuxTlt1Xfq8=
|
||||||
|
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=
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package letter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"encoding/csv"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"git.niplace.ru/XoxJlopeZi4BB/imageutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Letter string
|
||||||
|
|
||||||
|
//go:embed map.csv
|
||||||
|
var fs embed.FS
|
||||||
|
var mapFile = func() io.Reader {
|
||||||
|
f, err := fs.Open("map.csv")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}()
|
||||||
|
|
||||||
|
var Letters = func(r io.Reader) (m map[string]string) {
|
||||||
|
records, err := csv.NewReader(r).ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, v := range records {
|
||||||
|
if len(v) < 2 {
|
||||||
|
panic("csv file isn's key-value pair")
|
||||||
|
}
|
||||||
|
m[v[0]] = v[1]
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}(mapFile)
|
||||||
|
|
||||||
|
// implements image.Image interface.
|
||||||
|
func (l Letter) ColorModel() color.Model {
|
||||||
|
return color.Alpha16Model
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements image.Image interface.
|
||||||
|
func (l Letter) Bounds() image.Rectangle {
|
||||||
|
return image.Rect(0, 0, 3, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements image.Image interface.
|
||||||
|
func (l Letter) At(x, y int) color.Color {
|
||||||
|
if len(l) != 9 {
|
||||||
|
panic("len(Letter) != 9")
|
||||||
|
}
|
||||||
|
target := l[x+y*3]
|
||||||
|
if target != '1' {
|
||||||
|
return color.Opaque
|
||||||
|
}
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
func Render(l Letter) image.Image {
|
||||||
|
img := image.NewRGBA(l.Bounds())
|
||||||
|
draw.Draw(img, l.Bounds(), image.White, image.ZP, draw.Src)
|
||||||
|
draw.Draw(img, l.Bounds(), l, image.ZP, draw.Over)
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
type blank struct {
|
||||||
|
image.Image
|
||||||
|
color.Color
|
||||||
|
imageutils.Side
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b blank) ColorModel() color.Model { return b.Image.ColorModel() }
|
||||||
|
|
||||||
|
func (b blank) Bounds() image.Rectangle {
|
||||||
|
rect := image.Rectangle{}
|
||||||
|
switch b.Side {
|
||||||
|
case imageutils.Left:
|
||||||
|
fallthrough
|
||||||
|
case imageutils.Right:
|
||||||
|
rect = image.Rectangle{
|
||||||
|
image.ZP,
|
||||||
|
image.Point{
|
||||||
|
1, b.Image.Bounds().Dy(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case imageutils.Up:
|
||||||
|
fallthrough
|
||||||
|
case imageutils.Down:
|
||||||
|
rect = image.Rectangle{
|
||||||
|
image.ZP,
|
||||||
|
image.Point{
|
||||||
|
b.Image.Bounds().Dx(), 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rect
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b blank) At(x, y int) color.Color { return b.Color }
|
||||||
|
|
||||||
|
func Blank(img image.Image, clr color.Color, side imageutils.Side) image.Image {
|
||||||
|
return imageutils.Concat(img, blank{img, clr, side}, side)
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
0, 111101111
|
||||||
|
1, 010010010
|
||||||
|
2, 110010011
|
||||||
|
3, 111011111
|
||||||
|
4, 101111001
|
||||||
|
5, 011010110
|
||||||
|
6, 001111111
|
||||||
|
7, 111001001
|
||||||
|
8, 011111111
|
||||||
|
9, 111111001
|
||||||
|
:, 010000010
|
||||||
|
@@ -0,0 +1,35 @@
|
|||||||
|
package clock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/clock/letter"
|
||||||
|
)
|
||||||
|
|
||||||
|
var blank = Letter("000000000")
|
||||||
|
|
||||||
|
type String struct {
|
||||||
|
S string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s String) Bounds() image.Rectangle {
|
||||||
|
splitted := strings.Split(s.S, '\n')
|
||||||
|
max := 0
|
||||||
|
for _, v := range splitted {
|
||||||
|
max = max(len(v), max)
|
||||||
|
}
|
||||||
|
x, y := max*4+1, len(splitted)*4+1
|
||||||
|
return image.Rect(0,0,x,y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s String) ColorModel() color.Model { return color.Alpha16Model }
|
||||||
|
func (s String) At(x, y int) color.Color {
|
||||||
|
splitted := string.Split(s.S, '\n')
|
||||||
|
if y/4 > len(splitted) {
|
||||||
|
return blank.At(0, 0)
|
||||||
|
}
|
||||||
|
if x/4 > len(splitted[y/4]) {
|
||||||
|
return blank.At(0, 0)
|
||||||
|
}
|
||||||
|
c := splitted[y/4][x/4]
|
||||||
|
return Letter(c).At(x%4, y%4)
|
||||||
|
}
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 424 KiB |
|
After Width: | Height: | Size: 5.0 MiB |
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
img, err := jpeg.Decode(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = png.Encode(os.Stdout, img)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/pkg/dithering"
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := util.ProcessStdio(func(img image.Image) image.Image {
|
||||||
|
return dithering.Dithering{img}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 145 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,66 @@
|
|||||||
|
package dithering
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
const side = 16
|
||||||
|
|
||||||
|
// tile
|
||||||
|
type grid struct {
|
||||||
|
color.Gray
|
||||||
|
Factor float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ grid) ColorModel() color.Model {
|
||||||
|
return color.RGBAModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ grid) Bounds() image.Rectangle {
|
||||||
|
return image.Rect(0, 0, side, side)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g grid) At(x, y int) color.Color {
|
||||||
|
n := uint8(float64(g.Gray.Y) * g.Factor)
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
return color.Black
|
||||||
|
}
|
||||||
|
step := int((side*side-1) / n)
|
||||||
|
if (y*side+x)%step == 0 {
|
||||||
|
return color.White
|
||||||
|
}
|
||||||
|
return color.Black
|
||||||
|
}
|
||||||
|
|
||||||
|
type uniformGrid struct {
|
||||||
|
image.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g uniformGrid) ColorModel() color.Model {
|
||||||
|
return g.Image.ColorModel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g uniformGrid) Bounds() image.Rectangle {
|
||||||
|
// just need to return something
|
||||||
|
return g.Image.Bounds()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g uniformGrid) At(x, y int) color.Color {
|
||||||
|
dx, dy := g.Image.Bounds().Dx(), g.Image.Bounds().Dy()
|
||||||
|
return g.Image.At(x%dx, y%dy)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Dithering struct {
|
||||||
|
image.Image
|
||||||
|
Factor float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Dithering) ColorModel() color.Model { return color.GrayModel }
|
||||||
|
func (d Dithering) Bounds() image.Rectangle { return d.Image.Bounds() }
|
||||||
|
func (d Dithering) At(x, y int) color.Color {
|
||||||
|
c := color.GrayModel.Convert(d.Image.At(x, y))
|
||||||
|
return uniformGrid{grid{c.(color.Gray), d.Factor}}.
|
||||||
|
At(x, y)
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package dithering
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
func callImage(img image.Image) {
|
||||||
|
for y := 0; y < img.Bounds().Dy(); y++ {
|
||||||
|
for x := 0; x < img.Bounds().Dx(); x++ {
|
||||||
|
_ = img.At(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGrid(t *testing.T) {
|
||||||
|
command := exec.Command("imv", "-")
|
||||||
|
w, err := command.StdinPipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = command.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := uint8(rand.Int() % 256)
|
||||||
|
t.Log(n)
|
||||||
|
img := imageutils.Scale(1<<6, grid{color.Gray{n}})
|
||||||
|
err = png.Encode(w, img)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
callImage(grid{color.Gray{4}})
|
||||||
|
callImage(grid{color.Gray{0}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDithering(t *testing.T) {
|
||||||
|
r, err := os.Open("/home/potassium/documents/wallpaper/8.png")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
w, err := os.Create("sample.png")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
img, err := png.Decode(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = png.Encode(w, Dithering{img})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 249 KiB |
@@ -0,0 +1,58 @@
|
|||||||
|
package downscale
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package downscale
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sub struct {
|
||||||
|
image.Image
|
||||||
|
image.Rectangle
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sub sub) ColorModel() color.Model { return sub.Image.ColorModel() }
|
||||||
|
func (sub sub) Bounds() image.Rectangle { return sub.Rectangle }
|
||||||
|
func (sub sub) At(x, y int) color.Color {
|
||||||
|
return sub.Image.At(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GridDownscale struct {
|
||||||
|
image.Image
|
||||||
|
newBounds image.Rectangle
|
||||||
|
}
|
||||||
|
|
||||||
|
func blend(c1, c2 color.Color) color.RGBA {
|
||||||
|
r1, g1, b1, a1 := c1.RGBA()
|
||||||
|
r2, g2, b2, a2 := c2.RGBA()
|
||||||
|
|
||||||
|
r := r1/2 + r2/2
|
||||||
|
g := g1/2 + g2/2
|
||||||
|
b := b1/2 + b2/2
|
||||||
|
a := a1/2 + a2/2
|
||||||
|
|
||||||
|
return color.RGBA{
|
||||||
|
R: uint8(r >> 8),
|
||||||
|
G: uint8(g >> 8),
|
||||||
|
B: uint8(b >> 8),
|
||||||
|
A: uint8(a >> 8),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func avgColor(img image.Image) (c color.Color) {
|
||||||
|
dx, dy := img.Bounds().Dx(), img.Bounds().Dy()
|
||||||
|
for y := 0; y < dy; y++ {
|
||||||
|
for x := 0; x < dx; x++ {
|
||||||
|
c = blend(c, img.At(x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (grid grid) ColorModel() color.Model { return grid.Image.ColorModel }
|
||||||
|
func (grid grid) Bounds() image.Rectangle { return grid.NewBounds }
|
||||||
|
func (grid grid) At(x, y int) color.Color {
|
||||||
|
b := grid.Image.Bounds()
|
||||||
|
dx, dy := b.Dx(), b.Dy()
|
||||||
|
cellBounds := image.Rect(
|
||||||
|
0, 0,
|
||||||
|
dx/grid.newBounds.Dx(),
|
||||||
|
dy/grid.newBounds.Dy(),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := grid.Image.Bounds().Intersect(
|
||||||
|
// move cell
|
||||||
|
cellBounds.Add(
|
||||||
|
image.Point{
|
||||||
|
x * cellBounds.Dx(),
|
||||||
|
y * cellBounds.Dy(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return avgColor(sub{grid.Image, r})
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package downscale
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// processImage takes input png image from stdin, processes it with f and outputs it to stdout.
|
||||||
|
func processImage(f func(image.Image) image.Image) error {
|
||||||
|
img, err := png.Decode(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = png.Encode(os.Stdout, f(img))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type convert struct {
|
||||||
|
image.Image
|
||||||
|
color.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c convert) ColorModel() color.Model { return c.Model }
|
||||||
|
func (c convert) Bounds() image.Rectangle { return c.Image.Bounds() }
|
||||||
|
func (c convert) At(x, y int) color.Color { return c.Model.Convert(c.Image.At(x, y)) }
|
||||||
|
|
||||||
|
func grayscale(img image.Image) image.Image {
|
||||||
|
return convert{img, color.GrayModel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := processImage(grayscale)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 424 KiB |
|
After Width: | Height: | Size: 541 KiB |
|
After Width: | Height: | Size: 615 KiB |
@@ -0,0 +1,44 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
_ "image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
_ "image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Merge struct {
|
||||||
|
first, second image.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Merge) ColorModel() color.Model { return m.first.ColorModel() }
|
||||||
|
func (m Merge) Bounds() image.Rectangle { return m.first.Bounds() }
|
||||||
|
|
||||||
|
func (m Merge) At(x, y int) color.Color {
|
||||||
|
if (x%2 != 0) && (y%2 != 0) {
|
||||||
|
return m.second.At(x, y)
|
||||||
|
}
|
||||||
|
return m.first.At(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f1, err := os.Open(os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
f2, err := os.Open(os.Args[2])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
img1, _, err := image.Decode(f1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
img2, _, err := image.Decode(f2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
png.Encode(os.Stdout, Merge{img1, img2})
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 607 KiB |
@@ -0,0 +1,152 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
_ "image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
_ "image/png"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
func paletteFromImage(img image.Image) (p []color.Color) {
|
||||||
|
for i := 0; i < img.Bounds().Dx(); i++ {
|
||||||
|
p = append(p, img.At(i, 0))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = fmt.Errorf("invalid length, must be 7 or 4")
|
||||||
|
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mean(c1, c2 color.Color) color.Color {
|
||||||
|
r1, g1, b1, _ := c1.RGBA()
|
||||||
|
r2, g2, b2, _ := c2.RGBA()
|
||||||
|
return color.RGBA{
|
||||||
|
uint8((r1 + r2) / 2),
|
||||||
|
uint8((g1 + g2) / 2),
|
||||||
|
uint8((b1 + b2) / 2),
|
||||||
|
0xFF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Convert struct {
|
||||||
|
image.Image
|
||||||
|
color.Palette
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Convert) ColorModel() color.Model { return c.Image.ColorModel() }
|
||||||
|
func (c Convert) Bounds() image.Rectangle { return c.Image.Bounds() }
|
||||||
|
func (c Convert) At(x, y int) color.Color {
|
||||||
|
if isMeanCloser(c.Palette, c.Image.At(x, y)) {
|
||||||
|
first, second := twoClosest(c.Palette, c.Image.At(x, y))
|
||||||
|
// chessboard texture
|
||||||
|
if (x%2 != 0) && (y%2 != 0) || (x%2 == 0) && (y%2 == 0) {
|
||||||
|
return first
|
||||||
|
}
|
||||||
|
return second
|
||||||
|
}
|
||||||
|
return c.Palette.Convert(c.Image.At(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func twoClosest(p color.Palette, c color.Color) (first, second color.Color) {
|
||||||
|
first = p.Convert(c)
|
||||||
|
i := p.Index(c)
|
||||||
|
p2 := slices.Delete(slices.Clone(p), i, i+1)
|
||||||
|
second = p2.Convert(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMeanCloser(p color.Palette, c color.Color) bool {
|
||||||
|
first, second := twoClosest(p, c)
|
||||||
|
mp := color.Palette{first, mean(first, second), second}
|
||||||
|
i := mp.Index(c)
|
||||||
|
return i == mp.Index(mean(first, second))
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
paletteFile := flag.String("p", "", "provide palette file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var palette []color.Color
|
||||||
|
|
||||||
|
var colors []string
|
||||||
|
_ = []string{
|
||||||
|
"#ffffec", // soft-white
|
||||||
|
"#ff0000", // red
|
||||||
|
"#00ff00", // green
|
||||||
|
"#0000ff", // blue
|
||||||
|
"#000000", // black
|
||||||
|
}
|
||||||
|
funniPalette := []string{
|
||||||
|
"#ffffec",
|
||||||
|
"#000000",
|
||||||
|
"#4a3544",
|
||||||
|
"#757575",
|
||||||
|
"#8e8e8e",
|
||||||
|
"#666666",
|
||||||
|
"#4c4c4c",
|
||||||
|
"#3b3b3b",
|
||||||
|
"#949494",
|
||||||
|
"#ffffff",
|
||||||
|
"#665361",
|
||||||
|
"#3f3f3f",
|
||||||
|
"#525252",
|
||||||
|
"#838383",
|
||||||
|
"#111111",
|
||||||
|
"#070707",
|
||||||
|
"#b9b9b9",
|
||||||
|
"#dfdfdf",
|
||||||
|
}
|
||||||
|
colors = funniPalette
|
||||||
|
if *paletteFile != "" {
|
||||||
|
f, err := os.Open(*paletteFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
img, _, err := image.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
palette = paletteFromImage(img)
|
||||||
|
} else {
|
||||||
|
palette = func() (p []color.Color) {
|
||||||
|
for _, v := range colors {
|
||||||
|
c, err := parseHexColor(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
p = append(p, c)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
img, _, err := image.Decode(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
palette = append(palette, color.RGBA{88, 88, 88, 0xFF})
|
||||||
|
err = png.Encode(os.Stdout, Convert{img, palette})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 510 B |
@@ -0,0 +1,5 @@
|
|||||||
|
experiments with palette and stuff
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
After Width: | Height: | Size: 1.8 MiB |
@@ -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) {
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
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"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/pkg/mesh/wallpaper"
|
||||||
|
// "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 = wallpaper.Wallpaper(dst)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f, err := os.Open("/home/potassium/documents/wallpaper/8.png")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(
|
||||||
|
"swaybg",
|
||||||
|
"--image", "/dev/stdin",
|
||||||
|
)
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Stdin = f
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package wallpaper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
func Wallpaper(img image.Image) error {
|
||||||
|
/*
|
||||||
|
cmd := exec.Command(
|
||||||
|
"swaybg",
|
||||||
|
"--image", "/dev/stdin",
|
||||||
|
"--mode", "fill",
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
cmd := exec.Command("imv", "/dev/stdin")
|
||||||
|
|
||||||
|
var buf = new(bytes.Buffer)
|
||||||
|
err := png.Encode(buf, img)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Stderr = os.Stdout
|
||||||
|
cmd.Stdin = buf
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"maps"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
imgutil "git.nkpl.cc/twocookedfaggots/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)
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 1.1 MiB |
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 main() {
|
||||||
|
if err := util.ProcessStdio(func(img image.Image) image.Image {
|
||||||
|
return Negate{img}
|
||||||
|
}); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 977 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,74 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
imgutil "git.nkpl.cc/twocookedfaggots/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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package imageutils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/draw"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.nkpl.cc/twocookedfaggots/imageutils/pkg/texture"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Render(img image.Image, rect image.Rectangle) image.Image {
|
||||||
|
newimg := image.NewRGBA(rect)
|
||||||
|
draw.Draw(newimg, rect, image.White, image.ZP, draw.Src)
|
||||||
|
draw.Draw(newimg, rect, img, image.ZP, draw.Over)
|
||||||
|
return newimg
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkScale(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := png.Encode(io.Discard,
|
||||||
|
Scale(64, Render(
|
||||||
|
texture.New(color.White,
|
||||||
|
color.Black, 2),
|
||||||
|
image.Rect(0, 0, 64, 64),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type SinglePixel struct{}
|
||||||
|
|
||||||
|
func (s SinglePixel) At(x, y int) color.Color {
|
||||||
|
return color.White
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SinglePixel) ColorModel() color.Model {
|
||||||
|
return color.RGBAModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SinglePixel) Bounds() image.Rectangle {
|
||||||
|
return image.Rect(0, 0, 1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSinglePixel(b *testing.B) {
|
||||||
|
instance := SinglePixel{}
|
||||||
|
err := png.Encode(io.Discard,
|
||||||
|
Scale(b.N, instance),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package imageutils
|
||||||
|
|
||||||
|
type Transformer interface {
|
||||||
|
Transform(image.Image) image.Image
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProcessImage takes input png image from stdin, processes it with f and outputs it to stdout.
|
||||||
|
func ProcessStdio(f func(image.Image) image.Image) error {
|
||||||
|
img, err := png.Decode(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = png.Encode(os.Stdout, f(img))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||