Created
December 17, 2023 06:48
-
-
Save maintainer64/be9671b05e08e9e4b565d421a345735f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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