Skip to content

Commit c3e1819

Browse files
committed
Calculate resize before doing it and do not crash if the image is smaller than the terminal
1 parent e1dfe88 commit c3e1819

File tree

4 files changed

+119
-40
lines changed

4 files changed

+119
-40
lines changed

cmd/icat/icat.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func main() {
1515
os.Exit(0)
1616
}
1717

18-
for _, arg := range args {
18+
for i, arg := range args {
1919
var err error
2020

2121
url, err := url.Parse(arg)
@@ -27,5 +27,8 @@ func main() {
2727
if err != nil {
2828
os.Stderr.WriteString(err.Error())
2929
}
30+
if i < len(args)-1 {
31+
os.Stdout.WriteString("\n")
32+
}
3033
}
3134
}

print_image.go

Lines changed: 13 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,11 @@ func PrintImageFile(imageFileName string) error {
3535
return err
3636
}
3737

38-
imageConfig, err := DecodeImageConfig(imageData)
39-
if err != nil {
40-
return err
41-
}
42-
4338
img, err := DecodeImage(imageData)
4439
if err != nil {
4540
return err
4641
}
47-
return PrintImage(img, &imageConfig, imageFileName, imageSize)
42+
return PrintImage(img, imageFileName, imageSize)
4843
}
4944

5045
func PrintImageURL(imageURL string) error {
@@ -63,43 +58,31 @@ func PrintImageURL(imageURL string) error {
6358
return err
6459
}
6560

66-
imageConfig, err := DecodeImageConfig(imageData)
67-
if err != nil {
68-
fmt.Println("Error decoding image config:", err)
69-
return err
70-
}
71-
7261
img, err := DecodeImage(imageData)
7362
if err != nil {
7463
return err
7564
}
76-
return PrintImage(img, &imageConfig, imageURL, resp.ContentLength)
65+
return PrintImage(img, imageURL, resp.ContentLength)
7766
}
7867

79-
func PrintImage(img *image.Image, imageConfig *image.Config, filename string, imageSize int64) error {
80-
var img2 image.Image
68+
func PrintImage(img *image.Image, filename string, imageSize int64) error {
69+
var img2 image.Image = *img
8170
sixelCapable, _ := rasterm.IsSixelCapable()
8271

8372
_, _, pw, ph := TermSize() // Get terminal height and width in pixels
73+
size, resizeOption := resizeConstraints(img2.Bounds(), int(pw), int(ph))
8474

85-
kittyOpts := rasterm.KittyImgOpts{SrcWidth: uint32(pw), SrcHeight: uint32(ph)}
86-
87-
if pw < uint16(imageConfig.Width) {
88-
kittyOpts.SrcWidth = uint32(pw)
89-
}
90-
if ph < uint16(imageConfig.Height) {
91-
kittyOpts.SrcHeight = uint32(ph)
75+
switch resizeOption {
76+
case 'x':
77+
img2 = resize.Resize(uint(size), 0, img2, resize.NearestNeighbor)
78+
case 'y':
79+
img2 = resize.Resize(0, uint(size), img2, resize.NearestNeighbor)
9280
}
9381

94-
if pw < uint16(imageConfig.Width) {
95-
img2 = resize.Resize(uint(pw), 0, *img, resize.NearestNeighbor)
96-
}
82+
newWidth := img2.Bounds().Max.X
83+
newHeight := img2.Bounds().Max.Y
9784

98-
img2Height := img2.Bounds().Max.Y
99-
100-
if ph < uint16(img2Height) {
101-
img2 = resize.Resize(0, uint(ph), img2, resize.NearestNeighbor)
102-
}
85+
kittyOpts := rasterm.KittyImgOpts{SrcWidth: uint32(newWidth), SrcHeight: uint32(newHeight)}
10386

10487
switch {
10588
case rasterm.IsKittyCapable():
@@ -126,15 +109,6 @@ func PrintImage(img *image.Image, imageConfig *image.Config, filename string, im
126109
return nil
127110
}
128111

129-
func DecodeImageConfig(imageData []byte) (imageConfig image.Config, err error) {
130-
imageDataReader := bytes.NewReader(imageData)
131-
imageConfig, _, err = image.DecodeConfig(imageDataReader)
132-
if err != nil {
133-
return image.Config{}, err
134-
}
135-
return imageConfig, nil
136-
}
137-
138112
func DecodeImage(imageData []byte) (*image.Image, error) {
139113
imageDataReader := bytes.NewReader(imageData)
140114
image, _, err := image.Decode(imageDataReader)

resize_constraints.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package icat
2+
3+
import (
4+
"image"
5+
)
6+
7+
func resizeConstraints(imageBounds image.Rectangle, maxHeight, maxWidth int) (size int, option rune) {
8+
imgHeight := imageBounds.Dy()
9+
imgWidth := imageBounds.Dx()
10+
11+
if imgHeight <= maxHeight && imgWidth <= maxWidth {
12+
return 0, '0' // Don't resize
13+
}
14+
15+
if imgHeight <= maxHeight && imgWidth > maxWidth {
16+
return maxWidth, 'x'
17+
}
18+
19+
if imgWidth <= maxWidth && imgHeight > maxHeight {
20+
return maxHeight, 'y'
21+
}
22+
23+
// OK, both x and y are too big. Let's figure out which one is the biggest
24+
// and use that to calculate the resize factor.
25+
hRatio := float32(imgHeight) / float32(maxHeight)
26+
wRatio := float32(imgWidth) / float32(maxWidth)
27+
if hRatio > wRatio {
28+
return maxHeight, 'y'
29+
} else {
30+
return maxWidth, 'x'
31+
}
32+
}

resize_constraints_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package icat
2+
3+
import (
4+
"image"
5+
"testing"
6+
)
7+
8+
func Test_resizeFactor(t *testing.T) {
9+
type args struct {
10+
imageBounds image.Rectangle
11+
height int
12+
width int
13+
}
14+
tests := []struct {
15+
name string
16+
args args
17+
want_size int
18+
want_option rune
19+
}{
20+
{
21+
name: "no resize",
22+
args: args{
23+
imageBounds: image.Rect(0, 0, 100, 100),
24+
height: 200,
25+
width: 200,
26+
},
27+
want_size: 0,
28+
want_option: '0',
29+
},
30+
{
31+
name: "resize height",
32+
args: args{
33+
imageBounds: image.Rect(0, 0, 100, 100),
34+
height: 50,
35+
width: 200,
36+
},
37+
want_size: 50,
38+
want_option: 'y',
39+
},
40+
{
41+
name: "resize width",
42+
args: args{
43+
imageBounds: image.Rect(0, 0, 100, 100),
44+
height: 200,
45+
width: 50,
46+
},
47+
want_size: 50,
48+
want_option: 'x',
49+
},
50+
{
51+
name: "resize both x and y",
52+
args: args{
53+
imageBounds: image.Rect(0, 0, 100, 100),
54+
height: 50,
55+
width: 50,
56+
},
57+
want_size: 50,
58+
want_option: 'x',
59+
},
60+
}
61+
62+
for _, tt := range tests {
63+
t.Run(tt.name, func(t *testing.T) {
64+
got_size, got_option := resizeConstraints(tt.args.imageBounds, tt.args.height, tt.args.width)
65+
if got_size != int(tt.want_size) || got_option != tt.want_option {
66+
t.Errorf("resizeConstraints() = %v, %s, want %v, %s", got_size, string(got_option), tt.want_size, string(tt.want_option))
67+
}
68+
})
69+
}
70+
}

0 commit comments

Comments
 (0)