From 559a4e9175433b24e247aa5950220f04230a81e6 Mon Sep 17 00:00:00 2001 From: potassium Date: Sat, 20 Jun 2026 11:31:44 +0300 Subject: [PATCH] more new ideas --- circle.go | 2 +- cmd/playground/circles/circles.go | 56 +++++-------------- cmd/playground/circles/colored.go | 24 ++++++++ cmd/playground/circles/main.go | 28 ++++++++++ cmd/playground/circles/xor.go | 19 +++++++ cmd/playground/circles/xor_colored.go | 3 + cmd/playground/mask/circles.go | 80 +++++++++++++++++++++++++++ cmd/playground/mask/mask.go | 62 +++++++++++++++++++++ cmd/scale/{main.go => downscale.go} | 1 + cmd/scale/griddownscale.go | 24 ++++++++ cmd/textonimage/main.go | 38 +++++++++++++ color/test.go | 12 ++++ go.mod | 7 ++- go.sum | 6 ++ pkg/dithering/grid.go | 1 + pkg/downscale/downscale.go | 2 +- pkg/downscale/grid.go | 68 +++++++++++++++++++++++ pkg/downscale/grid_test.go | 1 + pkg/downscale/main.go | 21 ------- transform.go | 5 ++ 20 files changed, 395 insertions(+), 65 deletions(-) create mode 100644 cmd/playground/circles/colored.go create mode 100644 cmd/playground/circles/main.go create mode 100644 cmd/playground/circles/xor.go create mode 100644 cmd/playground/circles/xor_colored.go create mode 100644 cmd/playground/mask/circles.go create mode 100644 cmd/playground/mask/mask.go rename cmd/scale/{main.go => downscale.go} (94%) create mode 100644 cmd/scale/griddownscale.go create mode 100644 cmd/textonimage/main.go create mode 100644 color/test.go create mode 100644 pkg/downscale/grid.go create mode 100644 pkg/downscale/grid_test.go delete mode 100644 pkg/downscale/main.go create mode 100644 transform.go diff --git a/circle.go b/circle.go index 0e8c8db..1ffed0a 100644 --- a/circle.go +++ b/circle.go @@ -11,7 +11,7 @@ type Circle struct { R int } -func (c Circle) ColorModel() color.Model { return color.AlphaModel } +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) } diff --git a/cmd/playground/circles/circles.go b/cmd/playground/circles/circles.go index 026810c..dc79df9 100644 --- a/cmd/playground/circles/circles.go +++ b/cmd/playground/circles/circles.go @@ -27,24 +27,6 @@ func Circles(b image.Rectangle, radius int) chan iu.Circle { 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)) - - - 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{ @@ -63,29 +45,21 @@ func (e Exchange) At(x, y int) color.Color { return exchange.Convert(c) } -func main() { - canvas := image.NewAlpha16(square(1024)) - circles := Circles(canvas.Bounds(), 1024/7) +var blackAndWhite = color.ModelFunc( + func(c color.Color) color.Color { + if color.Alpha16Model.Convert(c) == color.Transparent{ + return color.Black + } + return color.White + }, +) - // draw n circles - for i := 0; i < 500; i++ { - c := <-circles +// Black and white's alpha image. +type BlackAndWhite struct { image.Image } - 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, canvas) - if err != nil { - panic(err) - } +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) } diff --git a/cmd/playground/circles/colored.go b/cmd/playground/circles/colored.go new file mode 100644 index 0000000..427cb7b --- /dev/null +++ b/cmd/playground/circles/colored.go @@ -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] + +} diff --git a/cmd/playground/circles/main.go b/cmd/playground/circles/main.go new file mode 100644 index 0000000..653d77e --- /dev/null +++ b/cmd/playground/circles/main.go @@ -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) + } +} diff --git a/cmd/playground/circles/xor.go b/cmd/playground/circles/xor.go new file mode 100644 index 0000000..6dd9ca2 --- /dev/null +++ b/cmd/playground/circles/xor.go @@ -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 +} diff --git a/cmd/playground/circles/xor_colored.go b/cmd/playground/circles/xor_colored.go new file mode 100644 index 0000000..1110061 --- /dev/null +++ b/cmd/playground/circles/xor_colored.go @@ -0,0 +1,3 @@ +package main + + diff --git a/cmd/playground/mask/circles.go b/cmd/playground/mask/circles.go new file mode 100644 index 0000000..5a10241 --- /dev/null +++ b/cmd/playground/mask/circles.go @@ -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) +} diff --git a/cmd/playground/mask/mask.go b/cmd/playground/mask/mask.go new file mode 100644 index 0000000..40fbee9 --- /dev/null +++ b/cmd/playground/mask/mask.go @@ -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) + } + +} diff --git a/cmd/scale/main.go b/cmd/scale/downscale.go similarity index 94% rename from cmd/scale/main.go rename to cmd/scale/downscale.go index 2e1e316..7df49df 100644 --- a/cmd/scale/main.go +++ b/cmd/scale/downscale.go @@ -1,3 +1,4 @@ +//go:build upsampling package main import ( diff --git a/cmd/scale/griddownscale.go b/cmd/scale/griddownscale.go new file mode 100644 index 0000000..36dd7f7 --- /dev/null +++ b/cmd/scale/griddownscale.go @@ -0,0 +1,24 @@ +package main + +import ( + "flag" + "image" + + "git.nkpl.cc/twocookedfaggots/imageutils/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) + } +} diff --git a/cmd/textonimage/main.go b/cmd/textonimage/main.go new file mode 100644 index 0000000..6031678 --- /dev/null +++ b/cmd/textonimage/main.go @@ -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)) +} diff --git a/color/test.go b/color/test.go new file mode 100644 index 0000000..fe9f6c3 --- /dev/null +++ b/color/test.go @@ -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) +} diff --git a/go.mod b/go.mod index 47e5e39..fbc267f 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,10 @@ go 1.25.5 require ( github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 - golang.org/x/image v0.36.0 + 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 ) diff --git a/go.sum b/go.sum index 3b477c3..3eeabf0 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +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= diff --git a/pkg/dithering/grid.go b/pkg/dithering/grid.go index 8f11335..ca04034 100644 --- a/pkg/dithering/grid.go +++ b/pkg/dithering/grid.go @@ -7,6 +7,7 @@ import ( const side = 16 +// tile type grid struct { color.Gray Factor float64 diff --git a/pkg/downscale/downscale.go b/pkg/downscale/downscale.go index 4861c83..f6306a7 100644 --- a/pkg/downscale/downscale.go +++ b/pkg/downscale/downscale.go @@ -1,4 +1,4 @@ -package main +package downscale import ( "errors" diff --git a/pkg/downscale/grid.go b/pkg/downscale/grid.go new file mode 100644 index 0000000..57b7bf7 --- /dev/null +++ b/pkg/downscale/grid.go @@ -0,0 +1,68 @@ +package downscale + +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}) +} diff --git a/pkg/downscale/grid_test.go b/pkg/downscale/grid_test.go new file mode 100644 index 0000000..27ad4f6 --- /dev/null +++ b/pkg/downscale/grid_test.go @@ -0,0 +1 @@ +package downscale diff --git a/pkg/downscale/main.go b/pkg/downscale/main.go deleted file mode 100644 index a72139b..0000000 --- a/pkg/downscale/main.go +++ /dev/null @@ -1,21 +0,0 @@ -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/transform.go b/transform.go new file mode 100644 index 0000000..14ae62b --- /dev/null +++ b/transform.go @@ -0,0 +1,5 @@ +package imageutils + +type Transformer interface { + Transform(image.Image) image.Image +}