cardboard-sikuli/screencap.go

187 lines
4.1 KiB
Go

package main
import (
"bytes"
"fmt"
"image"
"image/draw"
"image/png"
"os"
"time"
"github.com/go-vgo/robotgo"
qt "github.com/mappu/miqt/qt6"
)
// doScreencap starts a new screen capture workflow.
func doScreencap(onGotNewScreenshot func(string, int64, int64)) {
mw.MainWindow.Hide()
time.Sleep(DefaultWait)
bmp := robotgo.CaptureScreen(0)
img := robotgo.ToImage(bmp)
robotgo.FreeBitmap(bmp)
mw.MainWindow.Show()
// load image into Qt
pngBuff := bytes.Buffer{}
fastPnger := png.Encoder{
CompressionLevel: png.NoCompression,
}
err := fastPnger.Encode(&pngBuff, img)
if err != nil {
qt.QMessageBox_Critical(mw.MainWindow.QWidget, "Screen capture error", err.Error())
return
}
pixm := qt.NewQPixmap()
ok := pixm.LoadFromData2(&pngBuff.Bytes()[0], uint(pngBuff.Len()), "png")
if !ok {
qt.QMessageBox_Critical(mw.MainWindow.QWidget, "Screen capture error", "PNG parse failure")
return
}
pngBuff.Reset()
//
scWnd := NewScreenCaptureDialogUi()
scWnd.ScreenCaptureDialog.SetModal(true)
currentZoom := 60
scWnd.gv.Scale(0.6, 0.6)
scene := qt.NewQGraphicsScene6(0, 0, float64(img.Bounds().Dx()), float64(img.Bounds().Dy()), nil)
scWnd.gv.SetScene(scene)
scene.AddPixmap(pixm)
rect := scene.AddRect2(0, 0, 200, 100)
crosshair1 := scene.AddLine2(-8, -8, 8, 8)
crosshair2 := scene.AddLine2(8, -8, -8, 8)
isMousedown := false
var nextStartPos *qt.QPointF
refreshCaption := func() {
curRect := rect.Rect()
curPos := crosshair1.Pos()
statusText := fmt.Sprintf("Image selection: (X=%.2f Y=%.2f W=%.2f H=%.2f) - Click offset: (X=%.2f Y=%.2f) - Zoom: %d%%",
curRect.X(), curRect.Y(), curRect.Width(), curRect.Height(),
curPos.X()-curRect.X(), curPos.Y()-curRect.Y(),
currentZoom,
)
scWnd.lbStatus.SetText(statusText)
}
refreshCaption()
scWnd.gv.OnMousePressEvent(func(super func(event *qt.QMouseEvent), event *qt.QMouseEvent) {
pos := scWnd.gv.MapToScene(event.Pos())
nextStartPos = pos // we'll use this pos if there is mouse movement - otherwise only move the crosshair
isMousedown = true
if rect.Rect().ContainsWithQPointF(pos) {
crosshair1.SetPos(pos)
crosshair2.SetPos(pos)
refreshCaption()
}
super(event)
})
scWnd.gv.OnMouseMoveEvent(func(super func(event *qt.QMouseEvent), event *qt.QMouseEvent) {
if isMousedown {
pos := scWnd.gv.MapToScene(event.Pos())
curRect := qt.NewQRectF4(nextStartPos.X(), nextStartPos.Y(), pos.X()-nextStartPos.X(), pos.Y()-nextStartPos.Y())
rect.SetRect(curRect)
crosshair1.SetPos(curRect.Center())
crosshair2.SetPos(curRect.Center())
refreshCaption()
}
super(event)
})
scWnd.gv.OnMouseReleaseEvent(func(super func(event *qt.QMouseEvent), event *qt.QMouseEvent) {
isMousedown = false
super(event)
})
refreshZoom := func() {
scWnd.gv.ResetTransform()
zScale := float64(currentZoom) / 100
scWnd.gv.Scale(zScale, zScale)
refreshCaption()
}
scWnd.btZoomIn.OnClicked(func() {
currentZoom += 10
refreshZoom()
})
scWnd.btZoomOut.OnClicked(func() {
if currentZoom < 15 {
return
}
currentZoom -= 10
refreshZoom()
})
scWnd.btZoomFit.OnClicked(func() {
currentZoom = 60
refreshZoom()
})
scWnd.btCapture.OnClicked(func() {
// Create a new image cropped to target selection
curRect := rect.Rect()
clickPos := crosshair1.Pos()
imRect := image.Rectangle{
Min: image.Point{
X: int(curRect.X()),
Y: int(curRect.Y()),
},
Max: image.Point{
X: int(curRect.X() + curRect.Width()),
Y: int(curRect.Y() + curRect.Height()),
},
}
result := image.NewRGBA(imRect)
draw.Draw(result, imRect, img, imRect.Min, draw.Src)
// Encode and save as a file in the current directory
filepath := fmt.Sprintf("%d.png", time.Now().UnixMicro())
fh, err := os.OpenFile(filepath, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
panic(err)
}
err = png.Encode(fh, result)
if err != nil {
panic(err)
}
fh.Close()
// Finished - close dialog and signal back to parent
onGotNewScreenshot(filepath, int64(clickPos.X()-curRect.X()), int64(clickPos.Y()-curRect.Y()))
scWnd.ScreenCaptureDialog.Close()
})
scWnd.ScreenCaptureDialog.ShowMaximized()
}