random circles example

This commit is contained in:
2026-04-04 16:57:10 +03:00
parent 3880b353c1
commit 824a4ede47
13 changed files with 308 additions and 1 deletions

24
circle.go Normal file
View File

@@ -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.AlphaModel }
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
}

18
cmd/jpg2png/jpg2png.go Normal file
View File

@@ -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)
}
}

View File

@@ -0,0 +1,56 @@
package main
import (
"math/rand"
"image"
"image/color"
"image/draw"
"image/png"
"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
}
// Converts color.Opaque and color.Transparent to color.Black and color.White respectively.
var BlackAndWhite = color.ModelFunc(
func(c color.Color) color.Color {
if color.AlphaModel.Convert(c) == color.Opaque {
return color.Black
}
return color.White
},
)
func main() {
canvas := image.NewRGBA(square(1024))
circles := Circles(canvas.Bounds(), 1024/16)
// draw n circles
for i := 0; i < 100; i++ {
c := <-circles
draw.DrawMask(
canvas, canvas.Bounds(), &image.Uniform{color.White},
image.Point{}, c, image.Point{}, draw.Over,
)
}
err := png.Encode(os.Stdout, canvas)
if err != nil {
panic(err)
}
}

9
cmd/playground/main.go Normal file
View File

@@ -0,0 +1,9 @@
package main
import (
iu "git.nkpl.cc/twocookedfaggots/imageutils"
)
func main() {
}

BIN
pkg/merge/IMG_0732.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

BIN
pkg/merge/converted.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB

44
pkg/merge/merge.go Normal file
View File

@@ -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})
}

BIN
pkg/merge/merged.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 KiB

152
pkg/merge/palette.go Normal file
View File

@@ -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)
}
}

BIN
pkg/merge/palette.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

5
pkg/merge/readme.md Normal file
View File

@@ -0,0 +1,5 @@
experiments with palette and stuff
---
![alt text](converted.png)

Submodule pkg/palette deleted from e161720f1c