- Created debug tool (cmd/debug/main.go) to analyze real D2R screenshots and calibrate HSV ranges - Fixed HSV color ranges for health/mana orbs based on real screenshot analysis (99.5% and 82% detection rates) - Replaced ReadBarPercentage with ReadOrbPercentage for circular orbs (not horizontal bars) - Added SetSource() method to capture Manager for hot-swapping capture sources - Fixed dashboard JavaScript null reference errors with proper array checks - Improved dashboard refresh rate from 100ms to 1000ms for better performance - Added proper error handling for empty/null API responses - Successfully detecting game state, health (99.5%), and mana (82%) from real D2R screenshot
129 lines
2.9 KiB
Go
129 lines
2.9 KiB
Go
// Package capture provides screen capture from various sources.
|
|
//
|
|
// Supports capturing from:
|
|
// - Local window (by title or handle)
|
|
// - VM display (VNC, Spice, or VM window on host)
|
|
// - Full screen / monitor region
|
|
//
|
|
// The capture interface is source-agnostic — the engine doesn't care
|
|
// where the frames come from.
|
|
package capture
|
|
|
|
import (
|
|
"image"
|
|
"time"
|
|
)
|
|
|
|
// Region defines a rectangular area to capture.
|
|
type Region struct {
|
|
X, Y, Width, Height int
|
|
}
|
|
|
|
// Source represents a capture source (window, VM, screen, etc.)
|
|
type Source interface {
|
|
// Name returns a human-readable description of the source.
|
|
Name() string
|
|
|
|
// Capture grabs a single frame.
|
|
Capture() (image.Image, error)
|
|
|
|
// CaptureRegion grabs a sub-region of the source.
|
|
CaptureRegion(r Region) (image.Image, error)
|
|
|
|
// Size returns the source dimensions.
|
|
Size() (width, height int)
|
|
|
|
// Close releases resources.
|
|
Close() error
|
|
}
|
|
|
|
// Stats tracks capture performance metrics.
|
|
type Stats struct {
|
|
FrameCount uint64
|
|
AvgCaptureMs float64
|
|
FPS float64
|
|
LastCapture time.Time
|
|
}
|
|
|
|
// Manager handles screen capture with performance tracking.
|
|
type Manager struct {
|
|
source Source
|
|
stats Stats
|
|
}
|
|
|
|
// NewManager creates a capture manager with the given source.
|
|
func NewManager(source Source) *Manager {
|
|
return &Manager{source: source}
|
|
}
|
|
|
|
// Capture grabs a frame and updates performance stats.
|
|
func (m *Manager) Capture() (image.Image, error) {
|
|
start := time.Now()
|
|
frame, err := m.source.Capture()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
elapsed := time.Since(start)
|
|
m.stats.FrameCount++
|
|
m.stats.LastCapture = start
|
|
|
|
// Rolling average
|
|
alpha := 0.1
|
|
ms := float64(elapsed.Microseconds()) / 1000.0
|
|
if m.stats.FrameCount == 1 {
|
|
m.stats.AvgCaptureMs = ms
|
|
} else {
|
|
m.stats.AvgCaptureMs = m.stats.AvgCaptureMs*(1-alpha) + ms*alpha
|
|
}
|
|
if m.stats.AvgCaptureMs > 0 {
|
|
m.stats.FPS = 1000.0 / m.stats.AvgCaptureMs
|
|
}
|
|
|
|
return frame, nil
|
|
}
|
|
|
|
// CaptureRegion grabs a sub-region.
|
|
func (m *Manager) CaptureRegion(r Region) (image.Image, error) {
|
|
return m.source.CaptureRegion(r)
|
|
}
|
|
|
|
// Stats returns current capture performance stats.
|
|
func (m *Manager) Stats() Stats {
|
|
return m.stats
|
|
}
|
|
|
|
// Close releases the capture source.
|
|
func (m *Manager) Close() error {
|
|
return m.source.Close()
|
|
}
|
|
|
|
// Source returns the underlying capture source.
|
|
func (m *Manager) Source() Source {
|
|
return m.source
|
|
}
|
|
|
|
// Size returns the source dimensions.
|
|
func (m *Manager) Size() (width, height int) {
|
|
return m.source.Size()
|
|
}
|
|
|
|
// SetSource swaps the capture source.
|
|
// This is useful for development when uploading new screenshots.
|
|
func (m *Manager) SetSource(newSource Source) error {
|
|
// Close the old source
|
|
if m.source != nil {
|
|
if err := m.source.Close(); err != nil {
|
|
// Log but don't fail - we still want to switch sources
|
|
// log.Printf("Warning: failed to close old capture source: %v", err)
|
|
}
|
|
}
|
|
|
|
// Switch to new source
|
|
m.source = newSource
|
|
|
|
// Reset stats for the new source
|
|
m.stats = Stats{}
|
|
|
|
return nil
|
|
}
|