Initial commit
This commit is contained in:
commit
c240d1ed40
|
@ -0,0 +1,7 @@
|
||||||
|
module git.redxen.eu/caskd/v6canvas
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require golang.org/x/net v0.8.0
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.6.0 // indirect
|
|
@ -0,0 +1,4 @@
|
||||||
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
@ -0,0 +1,154 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
_ "image/png"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/net/icmp"
|
||||||
|
"golang.org/x/net/ipv6"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
dt uint
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
flag.UintVar(&dt, "timeout", 5000, "Frame display time (ms)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var (
|
||||||
|
up = make(chan image.Image)
|
||||||
|
lo = make(chan image.Image)
|
||||||
|
dx = make(chan pixdelta)
|
||||||
|
)
|
||||||
|
|
||||||
|
c, err := icmp.ListenPacket("udp6", "::")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to listen for ICMP packets:", err)
|
||||||
|
}
|
||||||
|
go sendDelta(dx, c)
|
||||||
|
go syncColors(up, lo, dx)
|
||||||
|
|
||||||
|
var imgs [2]image.Image
|
||||||
|
for i := range imgs {
|
||||||
|
imgs[i] = image.NewRGBA(image.Rect(0, 0, 511, 511))
|
||||||
|
}
|
||||||
|
for _, v := range flag.Args() {
|
||||||
|
f, err := os.Open(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to open image:", err)
|
||||||
|
}
|
||||||
|
imgs[0] = imgs[1]
|
||||||
|
imgs[1], _, err = image.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to decode image:", err)
|
||||||
|
}
|
||||||
|
up <- imgs[0]
|
||||||
|
lo <- imgs[1]
|
||||||
|
time.Sleep(time.Duration(dt) * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendDelta(d chan pixdelta, c *icmp.PacketConn) {
|
||||||
|
pm := icmp.Message{
|
||||||
|
Type: ipv6.ICMPTypeEchoRequest, Code: 0,
|
||||||
|
Body: &icmp.Echo{Seq: 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
bb, err := pm.Marshal(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to encode ICMP packet")
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case delta := <-d:
|
||||||
|
dest := &net.UDPAddr{
|
||||||
|
IP: convV6(delta.p, delta.c),
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := c.WriteTo(bb, dest); err != nil {
|
||||||
|
log.Println("Failed to send packet to:", dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func syncColors(upstream, local chan image.Image, dx chan pixdelta) {
|
||||||
|
var (
|
||||||
|
oimg, nimg image.Image
|
||||||
|
)
|
||||||
|
|
||||||
|
oimg = nil
|
||||||
|
nimg = nil
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case nimg = <-local:
|
||||||
|
break
|
||||||
|
case oimg = <-upstream:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if oimg == nil || nimg == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs := uint64(0)
|
||||||
|
|
||||||
|
wg := new(sync.WaitGroup)
|
||||||
|
b := nimg.Bounds()
|
||||||
|
for x := 0; x < b.Dx(); x++ {
|
||||||
|
for y := 0; y < b.Dy(); y++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go comparePixel(oimg, nimg, image.Point{X: x, Y: y}, dx, wg)
|
||||||
|
jobs++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Framebuffer
|
||||||
|
|
||||||
|
type pixdelta struct {
|
||||||
|
p image.Point
|
||||||
|
c color.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePixel(a, b image.Image, loc image.Point, r chan pixdelta, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
car, cab, cag, _ := a.At(loc.X, loc.Y).RGBA()
|
||||||
|
cbr, cbb, cbg, cba := b.At(loc.X, loc.Y).RGBA()
|
||||||
|
if (cba == 0xFFFF) && !(car == cbr && cag == cbg && cab == cbb) {
|
||||||
|
r <- pixdelta{c: b.At(loc.X, loc.Y), p: loc}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network
|
||||||
|
|
||||||
|
// fdcf:8538:9ad5:3333:XXXX:YYYY:11RR:GGBB
|
||||||
|
func convV6(p image.Point, c color.Color) (addr net.IP) {
|
||||||
|
addr = net.IP{
|
||||||
|
0xfd, 0xcf, 0x85, 0x38, 0x9a, 0xd5, 0x33, 0x33,
|
||||||
|
}
|
||||||
|
addr = append(addr, split2Bytes(uint16(p.X))...)
|
||||||
|
addr = append(addr, split2Bytes(uint16(p.Y))...)
|
||||||
|
r, g, b, _ := c.RGBA()
|
||||||
|
addr = append(addr, 0x11, uint8(r), uint8(g), uint8(b))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func split2Bytes(s uint16) []uint8 {
|
||||||
|
return []uint8{
|
||||||
|
uint8((s & (0xFF << 8)) >> 8),
|
||||||
|
uint8(s & 0xFF),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue