Compare commits
4 Commits
e5c0537f2b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7143e96ef3 | |||
| 3677407f05 | |||
| 84d241e619 | |||
| b9808c8150 |
@@ -1,43 +0,0 @@
|
|||||||
package dithering
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
)
|
|
||||||
|
|
||||||
type grid struct {
|
|
||||||
color.Gray
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_ grid) ColorModel() color.Model {
|
|
||||||
return color.RGBAModel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_ grid) Bounds() image.Rectangle {
|
|
||||||
return image.Rect(0, 0, 8, 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g grid) At(x, y int) color.Color {
|
|
||||||
step := int(64 / (g.Gray.Y / 4))
|
|
||||||
if (y*8+x)%step == 0 {
|
|
||||||
return color.Black
|
|
||||||
}
|
|
||||||
return color.White
|
|
||||||
}
|
|
||||||
|
|
||||||
type uniformGrid struct {
|
|
||||||
image.Image
|
|
||||||
image.Rectangle
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g uniformGrid) ColorModel() color.Model {
|
|
||||||
return g.Image.ColorModel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g uniformGrid) Bounds() image.Rectangle {
|
|
||||||
return g.Rectangle
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g uniformGrid) At(x, y int) color.Color {
|
|
||||||
return color.Color(nil)
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package dithering
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image/color"
|
|
||||||
"image/png"
|
|
||||||
"math/rand"
|
|
||||||
"os/exec"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.nkpl.cc/twocookedfaggots/imageutils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGrid(t *testing.T) {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
|
|
||||||
command := exec.Command("imv", "-")
|
|
||||||
w, err := command.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
img := imageutils.Scale(1<<6, grid{color.Gray{uint8(rand.Int() % 64)}})
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
err = png.Encode(w, img)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
err = command.Start()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
pkg/dithering/cmd/IMG_0732.jpg
Normal file
|
After Width: | Height: | Size: 424 KiB |
BIN
pkg/dithering/cmd/IMG_0732.png
Normal file
|
After Width: | Height: | Size: 5.0 MiB |
20
pkg/dithering/cmd/jpg2png.go
Normal 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
pkg/dithering/cmd/main.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
pkg/dithering/cmd/nz9ipc5vywca1.jpg
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
pkg/dithering/cmd/schizophrenia.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
BIN
pkg/dithering/cmd/something.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
pkg/dithering/cmd/touhouthemedbj.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
63
pkg/dithering/grid.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package dithering
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
const side = 16
|
||||||
|
|
||||||
|
type grid struct {
|
||||||
|
color.Gray
|
||||||
|
}
|
||||||
|
|
||||||
|
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(g.Gray.Y * 10)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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)}}.
|
||||||
|
At(x, y)
|
||||||
|
}
|
||||||
73
pkg/dithering/grid_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
pkg/dithering/sample.png
Normal file
|
After Width: | Height: | Size: 249 KiB |
32
pkg/hex.go
@@ -1,32 +0,0 @@
|
|||||||
package imageutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"image/color"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ParseHexColorErr = errors.New("invalid length, must be 7 or 4")
|
|
||||||
|
|
||||||
func ParseHexColor(s string) (c color.RGBA, err error) {
|
|
||||||
c.A = 0xff
|
|
||||||
switch len(s) {
|
|
||||||
case 7:
|
|
||||||
_, err = fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B)
|
|
||||||
case 4:
|
|
||||||
_, err = fmt.Sscanf(s, "#%1x%1x%1x", &c.R, &c.G, &c.B)
|
|
||||||
// Double the hex digits:
|
|
||||||
c.R *= 17
|
|
||||||
c.G *= 17
|
|
||||||
c.B *= 17
|
|
||||||
default:
|
|
||||||
err = ParseHexColorErr
|
|
||||||
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ColorToHex(c color.Color) string {
|
|
||||||
r, g, b, _ := color.NRGBAModel.Convert(c).RGBA()
|
|
||||||
return fmt.Sprintf("#%02x%02x%02x", byte(r), byte(g), byte(b))
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package imageutils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"image/png"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Returns path of first occured file
|
|
||||||
// with png extension in user's home directory.
|
|
||||||
func firstPNG(root string) (filename string,
|
|
||||||
err error) {
|
|
||||||
var fn filepath.WalkFunc = func(path string,
|
|
||||||
info os.FileInfo,
|
|
||||||
fileErr error) error {
|
|
||||||
if fileErr != nil {
|
|
||||||
return fileErr
|
|
||||||
}
|
|
||||||
if !info.IsDir() &&
|
|
||||||
filepath.Ext(path) == ".png" {
|
|
||||||
filename = path
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err = filepath.Walk(root, fn)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestColorToHex(t *testing.T) {
|
|
||||||
root := os.Getenv("HOME")
|
|
||||||
path, err := firstPNG(root)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
img, err := png.Decode(f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
fmt.Println(ColorToHex(img.At(0, 0)))
|
|
||||||
}
|
|
||||||
1
pkg/palette
Submodule
@@ -1,9 +1,9 @@
|
|||||||
package imageutils
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/color"
|
"image/color"
|
||||||
"errors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ParseHexColorErr = errors.New("invalid length, must be 7 or 4")
|
var ParseHexColorErr = errors.New("invalid length, must be 7 or 4")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package imageutils
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|||||||
20
util/stdio.go
Normal 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
|
||||||
|
}
|
||||||