Skip to content

Instantly share code, notes, and snippets.

@maintainer64
Created December 17, 2023 06:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maintainer64/be9671b05e08e9e4b565d421a345735f to your computer and use it in GitHub Desktop.
Save maintainer64/be9671b05e08e9e4b565d421a345735f to your computer and use it in GitHub Desktop.
// AVG BUFF
type AvgBuffColor struct {
_currentIndex int
_colors []color.RGBA
}
func NewAvgBuffColor(bufferSize uint) *AvgBuffColor {
return &AvgBuffColor{
_currentIndex: -1,
_colors: make([]color.RGBA, bufferSize),
}
}
func (c *AvgBuffColor) nextIndex() int {
c._currentIndex++
if c._currentIndex >= len(c._colors) {
c._currentIndex = 0
}
return c._currentIndex
}
func (c *AvgBuffColor) Add(value color.RGBA) {
c._colors[c.nextIndex()] = value
fmt.Println(c._colors)
}
func (c *AvgBuffColor) Get() color.RGBA {
total := [4]int64{0, 0, 0, 0}
var count int64 = 0
for _, pixel := range c._colors {
total[0] += int64(pixel.R)
total[1] += int64(pixel.G)
total[2] += int64(pixel.B)
total[3] += int64(pixel.A)
count++
}
return color.RGBA{
R: uint8(total[0] / count),
G: uint8(total[1] / count),
B: uint8(total[2] / count),
A: uint8(total[3] / count),
}
}
// Resizer
type ScreenUtils struct {
Width int
Height int
}
func (c *ScreenUtils) Resize(src image.Image) image.Image {
if c.Width <= 0 || c.Height <= 0 {
return src
}
dst := image.NewRGBA(image.Rect(0, 0, c.Width, c.Height))
draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
return dst
}
var SubImageIndexOutOfRange = errors.New("SubImage index is out of range [1..4]")
// SubImage use index [1] - TOP_RIGHT, [2] - BOTTOM_RIGHT, [3] - BOTTOM_LEFT, [4] - TOP_LEFT
func (c *ScreenUtils) SubImage(src *image.RGBA, index int) (*image.RGBA, error) {
var dstrect image.Rectangle
switch index {
case 1:
dstrect = image.Rect(c.Width/2, 0, c.Width, c.Height/2)
case 2:
dstrect = image.Rect(c.Width/2, c.Height/2, c.Width, c.Height)
case 3:
dstrect = image.Rect(0, 0, c.Width/2, c.Height/2)
case 4:
dstrect = image.Rect(0, c.Height/2, c.Width/2, c.Height)
default:
return nil, SubImageIndexOutOfRange
}
return src.SubImage(dstrect).(*image.RGBA), nil
}
func (c *ScreenUtils) AvgImage(src image.Image) color.RGBA {
total := [4]int64{0, 0, 0, 0}
var count int64 = 0
bounds := src.Bounds()
for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
for x := bounds.Min.X; x < bounds.Max.X; x++ {
r, g, b, a := src.At(x, y).RGBA()
total[0] += int64(r)
total[1] += int64(g)
total[2] += int64(b)
total[3] += int64(a)
count++
}
}
return color.RGBA{
R: uint8((total[0] / count) >> 8),
G: uint8((total[1] / count) >> 8),
B: uint8((total[2] / count) >> 8),
A: uint8((total[3] / count) >> 8),
}
}
// Screen class
type captureScreenCallback struct {
callback func(color.RGBA)
// Channels
events <-chan color.RGBA
quit <-chan bool
}
func (c *captureScreenCallback) run() {
for {
select {
case event := <-c.events:
c.callback(event)
continue
case _ = <-c.quit:
break
}
}
}
type CaptureScreen struct {
DelayDuration time.Duration
DisplayIndexes []int
utils *ScreenUtils
avgBuffColor *AvgBuffColor
// Channels
events chan<- color.RGBA
stopSignal chan<- bool
}
func NewCaptureScreen(
framerate uint,
displayIndexes []int,
resizeWidth int,
resizeHeight int,
callback func(color.RGBA),
) *CaptureScreen {
avgBuffColor := NewAvgBuffColor(framerate * 2)
channelSize := 100
events := make(chan color.RGBA, channelSize)
quit := make(chan bool, 1)
duration := time.Second / time.Duration(framerate)
utils := &ScreenUtils{
Width: resizeWidth,
Height: resizeHeight,
}
sm := captureScreenCallback{
callback: callback,
events: events,
quit: quit,
}
go sm.run()
return &CaptureScreen{
DelayDuration: duration,
DisplayIndexes: displayIndexes,
utils: utils,
avgBuffColor: avgBuffColor,
events: events,
stopSignal: quit,
}
}
func (c *CaptureScreen) GenerateScreenshots() {
for {
c.sendScreens()
time.Sleep(c.DelayDuration)
}
}
func (c *CaptureScreen) sendScreens() {
var displayIndex int
for _, displayIndex := range c.DisplayIndexes {
c.sendScreen(displayIndex)
}
if len(c.DisplayIndexes) != 0 {
return
}
// Если не переданы индексы мониторов
for displayIndex = 0; displayIndex < screenshot.NumActiveDisplays(); displayIndex++ {
c.sendScreen(displayIndex)
}
}
func (c *CaptureScreen) sendScreen(displayIndex int) {
img, err := screenshot.CaptureDisplay(displayIndex)
if err != nil {
log.Fatal(err)
}
newImg := c.utils.Resize(img)
for i := 0; i < 4; i++ {
subImg, err := c.utils.SubImage(newImg.(*image.RGBA), i+1)
if err != nil {
log.Fatal(err)
}
c.avgBuffColor.Add(c.utils.AvgImage(subImg))
}
c.events <- c.avgBuffColor.Get()
}
func (c *CaptureScreen) Shutdown() {
close(c.stopSignal)
}
// Usage
package main
import (
"fmt"
"image/color"
".../pkg/screener"
)
func callback(currentColor color.RGBA) {
fmt.Println(currentColor)
}
func main() {
displayIndexes := []int{0}
cs := screener.NewCaptureScreen(24, displayIndexes, 40, 40, callback)
cs.GenerateScreenshots()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment