Skip to content

Commit

Permalink
Zoom in/out with Up/Down arrows; pan around with WSAD (upper bigger s…
Browse files Browse the repository at this point in the history
…tep, lower wsad exacly 1 screen pixel) (#41)

* Zoom in/out and pan with WSAD

* slightly less ugly
  • Loading branch information
ldemailly authored Sep 25, 2024
1 parent 0f2c9f5 commit 4d303af
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 20 deletions.
68 changes: 53 additions & 15 deletions ansipixels/fps/fps.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,17 @@ var fpsJpg []byte
//go:embed fps_colors.jpg
var fpsColorsJpg []byte

func imagesViewer(ap *ansipixels.AnsiPixels, imageFiles []string) int { //nolint:gocognit // yeah well...
func imagesViewer(ap *ansipixels.AnsiPixels, imageFiles []string) int { //nolint:gocognit,gocyclo,funlen // yeah well...
ap.Data = make([]byte, 3)
i := 0
l := len(imageFiles)
showInfo := l > 1
ap.SignalChannel()
tv := terminal.TimeoutToTimeval(100 * time.Millisecond)
for {
zoom := 1.0
offsetX := 0
offsetY := 0
if i >= l {
i = 0
}
Expand All @@ -109,7 +112,7 @@ func imagesViewer(ap *ansipixels.AnsiPixels, imageFiles []string) int { //nolint
info := fmt.Sprintf("%s (%dx%d %s%s)", imageFile, img.Width, img.Height, img.Format, extra)
ap.ClearScreen()
redraw:
if err = ap.ShowImage(img, "\033[34m"); err != nil {
if err = ap.ShowImage(img, zoom, offsetX, offsetY, "\033[34m"); err != nil {
return log.FErrf("Error showing image: %v", err)
}
if showInfo {
Expand Down Expand Up @@ -139,23 +142,58 @@ func imagesViewer(ap *ansipixels.AnsiPixels, imageFiles []string) int { //nolint
}
}
ap.Data = ap.Data[0:n]
if ap.Data[0] == '?' || ap.Data[0] == 'h' || ap.Data[0] == 'H' {
ap.WriteCentered(ap.H/2-1, "Showing %d out of %d images, hit any key to continue,", i+1, l)
ap.WriteCentered(ap.H/2, "'q' to exit, left arrow to go back, 'i' to toggle image information")
largeSteps := int(10. * zoom)
c := ap.Data[0]
switch c {
case '?', 'h', 'H':
ap.WriteCentered(ap.H/2-1, "Showing %d out of %d images, hit any key to continue, up/down for zoom,", i+1, l)
ap.WriteCentered(ap.H/2, "WSAD to pan, 'q' to exit, left arrow to go back, 'i' to toggle image information")
goto wait
}
if ap.Data[0] == 'i' || ap.Data[0] == 'I' {
case 'i', 'I':
showInfo = !showInfo
goto redraw
}
if ap.Data[0] == 27 {
case 'W':
offsetY -= largeSteps
goto redraw
case 'S':
offsetY += largeSteps
goto redraw
case 'A':
offsetX -= largeSteps
goto redraw
case 'D':
offsetX += largeSteps
goto redraw
case 'w':
offsetY--
goto redraw
case 's':
offsetY++
goto redraw
case 'a':
offsetX--
goto redraw
case 'd':
offsetX++
goto redraw
case 27:
n, _ := ap.In.Read(ap.Data[1:3])
ap.Data = ap.Data[:1+n]
}
// check for left/right arrow to go to next/previous image
if len(ap.Data) >= 3 && ap.Data[0] == 27 && ap.Data[1] == '[' && ap.Data[2] == 'D' {
i = (i + l - 1) % l
continue
// check for left arrow to go to next/previous image
if len(ap.Data) >= 3 && c == 27 && ap.Data[1] == '[' {
// Arrow key
switch ap.Data[2] {
case 'D': // left arrow
i = (i + l - 1) % l
continue
case 'A': // up arrow
zoom *= 1.25
goto redraw
case 'B': // down arrow
zoom /= 1.25
goto redraw
}
}
if isStopKey(ap) || l == 1 {
return 0
Expand Down Expand Up @@ -242,7 +280,7 @@ func Main() int { //nolint:funlen,gocognit // color and mode if/else are a bit l
if err != nil {
return log.FErrf("Error reading image: %v", err)
}
if err = ap.ShowImage(background, "\033[34m"); err != nil {
if err = ap.ShowImage(background, 1.0, 0, 0, "\033[34m"); err != nil {
return log.FErrf("Error showing image: %v", err)
}
buf := [256]byte{}
Expand Down Expand Up @@ -287,7 +325,7 @@ func Main() int { //nolint:funlen,gocognit // color and mode if/else are a bit l
if ap.IsResizeSignal(s) {
_ = ap.GetSize()
ap.ClearScreen()
_ = ap.ShowImage(background, "\033[34m")
_ = ap.ShowImage(background, 1.0, 0, 0, "\033[34m")
if !*noboxFlag {
drawBox(ap)
}
Expand Down
11 changes: 6 additions & 5 deletions ansipixels/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func toGrey(rgbaImg *image.RGBA, img image.Image) {
}
}

func resizeAndCenter(img *image.RGBA, maxW, maxH int) *image.RGBA {
func resizeAndCenter(img *image.RGBA, maxW, maxH int, zoom float64, offsetX, offsetY int) *image.RGBA {
// Get original image dimensions
origBounds := img.Bounds()
origW := origBounds.Dx()
Expand All @@ -183,6 +183,7 @@ func resizeAndCenter(img *image.RGBA, maxW, maxH int) *image.RGBA {
scaleW := float64(maxW) / float64(origW)
scaleH := float64(maxH) / float64(origH)
scale := min(scaleW, scaleH) // Choose the smallest scale to fit within bounds
scale *= zoom

// Calculate new dimensions while preserving aspect ratio
newW := int(float64(origW) * scale)
Expand All @@ -191,8 +192,8 @@ func resizeAndCenter(img *image.RGBA, maxW, maxH int) *image.RGBA {
canvas := image.NewRGBA(image.Rect(0, 0, maxW, maxH))

// Calculate the offset to center the image
offsetX := (maxW - newW) / 2
offsetY := (maxH - newH) / 2
offsetX += (maxW - newW) / 2
offsetY += (maxH - newH) / 2

// Resize the image
resized := image.NewRGBA(image.Rect(0, 0, newW, newH))
Expand Down Expand Up @@ -270,13 +271,13 @@ func (ap *AnsiPixels) DecodeImage(inp io.Reader) (*Image, error) {
}

// Color string is the fallback mono color to use when AnsiPixels.TrueColor is false.
func (ap *AnsiPixels) ShowImage(imagesRGBA *Image, colorString string) error {
func (ap *AnsiPixels) ShowImage(imagesRGBA *Image, zoom float64, offsetX, offsetY int, colorString string) error {
err := ap.GetSize()
if err != nil {
return err
}
for i, imgRGBA := range imagesRGBA.Images {
img := resizeAndCenter(imgRGBA, ap.W-2*ap.Margin, 2*ap.H-2*ap.Margin)
img := resizeAndCenter(imgRGBA, ap.W-2*ap.Margin, 2*ap.H-2*ap.Margin, zoom, offsetX, offsetY)
if ap.Gray {
toGrey(img, img)
}
Expand Down

0 comments on commit 4d303af

Please sign in to comment.