Gopher2600/gui/sdlimgui/win_dbgscr_paint.go
JetSetIlly c2626e07a1 magnification window improvements
area moveable with drag of magnification window (left or middle button);
or drag of main debug window (middle button); window can be opened with
double click of middle button in main debug window

area of magnification visible in main debug window
2023-03-22 18:49:14 +00:00

163 lines
4.6 KiB
Go

// This file is part of Gopher2600.
//
// Gopher2600 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gopher2600 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
package sdlimgui
import (
"github.com/inkyblackness/imgui-go/v4"
"github.com/jetsetilly/gopher2600/hardware/memory/cpubus"
"github.com/jetsetilly/gopher2600/hardware/television/coords"
"github.com/jetsetilly/gopher2600/hardware/television/signal"
"github.com/jetsetilly/gopher2600/hardware/television/specification"
"github.com/jetsetilly/gopher2600/hardware/tia/video"
"github.com/jetsetilly/gopher2600/logger"
"github.com/jetsetilly/gopher2600/reflection"
)
func (win *winDbgScr) paintDragAndDrop() {
// each datastream image can also be dropped onto
if imgui.BeginDragDropTarget() {
// drag and drop has ended on a legitimate target
payload := imgui.AcceptDragDropPayload(painDragDrop, imgui.DragDropFlagsNone)
if payload != nil {
mouse := win.currentMouse()
if mouse.valid {
ref := win.scr.crit.reflection[mouse.offset]
ff := floodFill{
win: win,
from: uint8((ref.Signal&signal.Color)>>signal.ColorShift) & 0xfe,
to: payload[0] & 0xfe,
}
if ff.from != ff.to {
ff.startingReflection = make([]reflection.ReflectedVideoStep, len(win.scr.crit.reflection))
copy(ff.startingReflection, win.scr.crit.reflection)
target := floodFillTarget{}
target.coord = win.img.lz.TV.Coords
target.coord.Clock = mouse.tv.Clock
target.coord.Scanline = mouse.tv.Scanline
target.offset = mouse.offset
ff.build(target)
logger.Logf("paint", "flood fill starting at %s", target.coord)
ff.resolve(0)
}
}
}
imgui.EndDragDropTarget()
}
}
type floodFillTarget struct {
coord coords.TelevisionCoords
offset int
}
type floodFill struct {
win *winDbgScr
startingReflection []reflection.ReflectedVideoStep
targets []floodFillTarget
from uint8
to uint8
}
func (ff *floodFill) build(target floodFillTarget) {
ref := ff.startingReflection[target.offset]
if ref.Signal&signal.VBlank == signal.VBlank {
return
}
px := uint8((ref.Signal&signal.Color)>>signal.ColorShift) & 0xfe
if px != ff.from {
return
}
ff.targets = append(ff.targets, target)
ref.Signal &= ^signal.Color
ref.Signal |= signal.SignalAttributes(ff.to) << signal.ColorShift
ff.startingReflection[target.offset] = ref
// down
down := target
down.offset += specification.ClksScanline
down.coord.Scanline++
if down.coord.Scanline <= ff.win.img.lz.TV.FrameInfo.TotalScanlines {
ff.build(down)
}
// up
up := target
up.offset -= specification.ClksScanline
up.coord.Scanline--
if up.coord.Scanline >= 0 {
ff.build(up)
}
// left
left := target
left.offset--
left.coord.Clock--
if left.coord.Clock >= 0 {
ff.build(left)
}
// right
right := target
right.offset++
right.coord.Clock++
if right.coord.Clock <= specification.ClksScanline {
ff.build(right)
}
}
func (ff *floodFill) resolve(offsetIdx int) {
if offsetIdx >= len(ff.targets) {
return
}
ff.win.img.dbg.PushFunctionImmediate(func() {
target := ff.targets[offsetIdx]
ref := ff.win.scr.crit.reflection[target.offset]
px := uint8((ref.Signal&signal.Color)>>signal.ColorShift) & 0xfe
if px != ff.from {
ff.resolve(offsetIdx + 1)
return
}
ff.win.img.dbg.GotoCoords(target.coord)
ff.win.img.dbg.PushFunctionImmediate(func() {
switch ref.VideoElement {
case video.ElementBackground:
ff.win.img.dbg.PushDeepPoke(cpubus.WriteAddress[cpubus.COLUBK], px, ff.to, 0xfe, func() { ff.resolve(offsetIdx + 1) })
case video.ElementPlayfield:
fallthrough
case video.ElementBall:
ff.win.img.dbg.PushDeepPoke(cpubus.WriteAddress[cpubus.COLUPF], px, ff.to, 0xfe, func() { ff.resolve(offsetIdx + 1) })
case video.ElementPlayer0:
fallthrough
case video.ElementMissile0:
ff.win.img.dbg.PushDeepPoke(cpubus.WriteAddress[cpubus.COLUP0], px, ff.to, 0xfe, func() { ff.resolve(offsetIdx + 1) })
case video.ElementPlayer1:
fallthrough
case video.ElementMissile1:
ff.win.img.dbg.PushDeepPoke(cpubus.WriteAddress[cpubus.COLUP1], px, ff.to, 0xfe, func() { ff.resolve(offsetIdx + 1) })
}
})
})
}