26 Commits

Author SHA1 Message Date
twocookedfaggots 329b115e69 Update .gitignore 2026-06-21 03:10:41 +04:00
twocookedfaggots 445a6aab94 go fmt 2026-06-21 01:18:50 +03:00
twocookedfaggots afd438930e what the fuck are you doin to my files git 2026-06-20 15:23:14 +03:00
twocookedfaggots f27f44c89d fuck git 2026-06-20 15:18:52 +03:00
twocookedfaggots c8bfe0777b i forgor again 2026-06-20 11:43:14 +03:00
twocookedfaggots f5ff5d75bb i forgor 2026-06-20 11:41:03 +03:00
twocookedfaggots 559a4e9175 more new ideas 2026-06-20 11:37:41 +03:00
twocookedfaggots b75a8814a2 new simple downscaling ideas 2026-06-20 11:31:44 +03:00
twocookedfaggots 67ff655767 with the power of vibecoding... 2026-04-05 21:47:52 +03:00
twocookedfaggots 824a4ede47 random circles example 2026-04-04 16:57:10 +03:00
twocookedfaggots 3880b353c1 refactor 2026-03-29 17:03:48 +03:00
twocookedfaggots 7143e96ef3 cleanup 2026-03-05 01:01:57 +03:00
twocookedfaggots 3677407f05 experiments and stuff 2026-03-05 00:55:17 +03:00
twocookedfaggots 84d241e619 now palette project lays here 2026-03-04 22:13:30 +03:00
twocookedfaggots b9808c8150 dithering done :) 2026-03-04 21:44:20 +03:00
twocookedfaggots e5c0537f2b some redesign 2026-03-04 02:14:21 +03:00
twocookedfaggots c25b65a5f6 grayscale converter example 2026-03-02 22:22:02 +03:00
twocookedfaggots 1122be9007 update of test.go 2026-02-22 04:03:25 +03:00
twocookedfaggots dc5f2466d1 Delete pkg/.hex_test.go.swp 2026-02-22 02:05:56 +04:00
twocookedfaggots 0bbccec989 Delete util/.hex_test.go.swp 2026-02-22 02:05:25 +04:00
twocookedfaggots c5eefdec1f draft work as intended :) 2026-02-22 01:11:57 +03:00
twocookedfaggots 352631e071 merging all related stuff here 2026-02-22 01:06:02 +03:00
twocookedfaggots cc5c9c6d1a cleaning up, early http server draft 2026-01-24 09:39:27 +03:00
twocookedfaggots d52658ab5c cleaningup 2025-12-14 06:16:10 +03:00
twocookedfaggots 1db672d481 renamed 2025-12-14 06:07:13 +03:00
twocookedfaggots 02ece86303 cmd 2025-12-14 06:04:57 +03:00
83 changed files with 2147 additions and 32 deletions
+3
View File
@@ -2,3 +2,6 @@ child_porn
imageutils.test
cpu.out
profile.out
*.png
*.jpg
*.jpeg
-3
View File
@@ -1,3 +0,0 @@
(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.
+9
View File
@@ -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/
+24
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.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
}
+22
View File
@@ -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)
}
}
+18
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)
}
}
+39
View File
@@ -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)
}
}
+65
View File
@@ -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)
}
+24
View File
@@ -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]
}
+28
View File
@@ -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)
}
}
+19
View File
@@ -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
}
+1
View File
@@ -0,0 +1 @@
package main
+9
View File
@@ -0,0 +1,9 @@
package main
import (
iu "git.nkpl.cc/twocookedfaggots/imageutils"
)
func main() {
}
+80
View File
@@ -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)
}
+62
View File
@@ -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)
}
}
+22
View File
@@ -0,0 +1,22 @@
//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)
}
}
View File
+24
View File
@@ -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", 1024, "dx value of new bounds")
var y = flag.Int("y", 1024, "dy value of new bounds")
func main() {
flag.Parse()
if err := util.ProcessStdio(func(img image.Image) image.Image {
return downscale.GridDownscale{
Image: img,
NewBounds: image.Rect(0, 0, *x, *y),
}
}); err != nil {
panic(err)
}
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 MiB

+36
View File
@@ -0,0 +1,36 @@
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))
}
+12
View File
@@ -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)
}
+11 -3
View File
@@ -1,5 +1,13 @@
module git.niplace.ru/XoxJlopeZi4BB/imageutils
module git.nkpl.cc/twocookedfaggots/imageutils
go 1.22.6
go 1.25.5
require github.com/potassium5703/texture v0.0.0-20240820054037-fce43fc4b0f0 // indirect
require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
golang.org/x/image v0.43.0
)
require (
github.com/dbriemann/pixel v0.5.1 // indirect
github.com/go-gl/mathgl v1.2.0 // indirect
)
+10 -2
View File
@@ -1,2 +1,10 @@
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=
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=
+47
View File
@@ -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)
}
+43
View File
@@ -0,0 +1,43 @@
package main
import (
"log"
"net/http"
)
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)
}
}
+4
View File
@@ -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>
+49
View File
@@ -0,0 +1,49 @@
//go:build ignore
// works as i wanted
package main
import (
"git.nkpl.cc/twocookedfaggots/imageutils"
"git.nkpl.cc/twocookedfaggots/imageutils/http/blank"
"image"
"image/color"
"image/jpeg"
"image/png"
"os"
)
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)
}
}
+18
View File
@@ -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)
}
+8
View File
@@ -0,0 +1,8 @@
залил как есть.
любая кринжовая хуйня которая здесь найдется... просто зайбейте на это хуй, не обращайте внимания
запускается значится так:
заходите в cmd и...
> go run .
заходите в бровз и пишите адрес с портом
+82
View File
@@ -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)
}
}
+13
View File
@@ -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)
}
}
+22
View File
@@ -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)
}
}
+22
View File
@@ -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 {}
}
+5
View File
@@ -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
+4
View File
@@ -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=
+106
View File
@@ -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)
}
+11
View File
@@ -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
1 0 111101111
2 1 010010010
3 2 110010011
4 3 111011111
5 4 101111001
6 5 011010110
7 6 001111111
8 7 111001001
9 8 011111111
10 9 111111001
11 : 010000010
+35
View File
@@ -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)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

+20
View File
@@ -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)
}
}
+17
View File
@@ -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)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+66
View File
@@ -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)
}
+73
View File
@@ -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)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.
+58
View File
@@ -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))
}
+75
View File
@@ -0,0 +1,75 @@
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) {
b := img.Bounds()
c = img.At(b.Min.X, b.Min.Y)
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X + 1; x < b.Max.X; x++ {
c = blend(c, img.At(x, y))
}
}
return
}
func (grid GridDownscale) ColorModel() color.Model { return grid.Image.ColorModel() }
func (grid GridDownscale) Bounds() image.Rectangle { return grid.NewBounds }
func (grid GridDownscale) 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(),
},
),
)
println(r.Min.X, r.Min.Y)
return avgColor(sub{grid.Image, r})
}
+1
View File
@@ -0,0 +1 @@
package downscale
+41
View File
@@ -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)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 KiB

+44
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})
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 607 KiB

+152
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)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

+5
View File
@@ -0,0 +1,5 @@
experiments with palette and stuff
---
![alt text](converted.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

+41
View File
@@ -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) {
}
+93
View File
@@ -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)
}
}
+23
View File
@@ -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)
}
}
+37
View File
@@ -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
}
+67
View File
@@ -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)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+39
View File
@@ -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)
}
+70
View File
@@ -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)
}
}
+41
View File
@@ -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)
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 977 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

+74
View File
@@ -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)
}
Submodule
+1
Submodule pkg/texture added at fce43fc4b0
+18 -4
View File
@@ -8,7 +8,7 @@ import (
"io"
"testing"
"github.com/potassium5703/texture"
"git.nkpl.cc/twocookedfaggots/imageutils/pkg/texture"
)
func Render(img image.Image, rect image.Rectangle) image.Image {
@@ -21,11 +21,11 @@ func Render(img image.Image, rect image.Rectangle) image.Image {
func BenchmarkScale(b *testing.B) {
for i := 0; i < b.N; i++ {
err := png.Encode(io.Discard,
Scale(Render(
Scale(64, Render(
texture.New(color.White,
color.Black, 2),
image.Rect(0, 0, 64, 64),
), 64),
)),
)
if err != nil {
panic(err)
@@ -33,10 +33,24 @@ func BenchmarkScale(b *testing.B) {
}
}
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(instance, b.N),
Scale(b.N, instance),
)
if err != nil {
panic(err)
+5
View File
@@ -0,0 +1,5 @@
package imageutils
type Transformer interface {
Transform(image.Image) image.Image
}
-20
View File
@@ -1,20 +0,0 @@
package imageutils
import (
"image"
"image/color"
)
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)
}
+32
View File
@@ -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))
}
+47
View File
@@ -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)))
}
+20
View File
@@ -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
}