mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-28 19:21:38 +02:00
@@ -139,13 +139,17 @@ Detailed list of changes
|
|||||||
|
|
||||||
- Add support for the `paste events protocol <https://rockorager.dev/misc/bracketed-paste-mime/>`__ (:iss:`9183`)
|
- Add support for the `paste events protocol <https://rockorager.dev/misc/bracketed-paste-mime/>`__ (:iss:`9183`)
|
||||||
|
|
||||||
|
- icat kitten: Add support for animated PNG and animated WebP, netPBM images, ICC color profiles and CCIP metadata to the builtin engine
|
||||||
|
|
||||||
|
- icat kitten: Add a new flag :option:`kitty +kitten icat --fit` to control how images are scaled to fit the screen (:iss:`9201`)
|
||||||
|
|
||||||
|
- icat kitten: The :option:`kitty +kitten icat --scale-up` flag now takes effect when not using :option:`kitty +kitten icat --place` as well
|
||||||
|
|
||||||
- Add a mappable action :ac:`copy_last_command_output` to copy the output of the last
|
- Add a mappable action :ac:`copy_last_command_output` to copy the output of the last
|
||||||
command to the clipboard (:pull:`9185`)
|
command to the clipboard (:pull:`9185`)
|
||||||
|
|
||||||
- ssh kitten: Fix a bug where automatic login was not working (:iss:`9187`)
|
- ssh kitten: Fix a bug where automatic login was not working (:iss:`9187`)
|
||||||
|
|
||||||
- icat kitten: Add support for animated PNG and animated WebP, netPBM images, ICC color profiles and CCIP metadata to the builtin engine
|
|
||||||
|
|
||||||
- Graphics: Fix overwrite composition mode for animation frames not being honored
|
- Graphics: Fix overwrite composition mode for animation frames not being honored
|
||||||
|
|
||||||
- Automatic color scheme switching: Fix title bar and scroll bar colors not being updated (:iss:`9167`)
|
- Automatic color scheme switching: Fix title bar and scroll bar colors not being updated (:iss:`9167`)
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ const (
|
|||||||
supported
|
supported
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type fit_t int
|
||||||
|
|
||||||
|
const (
|
||||||
|
fit_none fit_t = iota
|
||||||
|
fit_width
|
||||||
|
fit_height
|
||||||
|
fit_both
|
||||||
|
)
|
||||||
|
|
||||||
var transfer_by_file, transfer_by_memory transfer_mode
|
var transfer_by_file, transfer_by_memory transfer_mode
|
||||||
|
|
||||||
var files_channel chan input_arg
|
var files_channel chan input_arg
|
||||||
@@ -49,6 +58,7 @@ var output_channel chan *image_data
|
|||||||
var num_of_items int
|
var num_of_items int
|
||||||
var keep_going *atomic.Bool
|
var keep_going *atomic.Bool
|
||||||
var screen_size *unix.Winsize
|
var screen_size *unix.Winsize
|
||||||
|
var fit_mode fit_t
|
||||||
|
|
||||||
func send_output(imgd *image_data) {
|
func send_output(imgd *image_data) {
|
||||||
output_channel <- imgd
|
output_channel <- imgd
|
||||||
@@ -87,6 +97,22 @@ func parse_z_index() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parse_fit() (err error) {
|
||||||
|
switch strings.ToLower(opts.Fit) {
|
||||||
|
case "width":
|
||||||
|
fit_mode = fit_width
|
||||||
|
case "height":
|
||||||
|
fit_mode = fit_height
|
||||||
|
case "none", "neither":
|
||||||
|
fit_mode = fit_none
|
||||||
|
case "both":
|
||||||
|
fit_mode = fit_both
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown fit specification: %#v", opts.Fit)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func parse_place() (err error) {
|
func parse_place() (err error) {
|
||||||
if opts.Place == "" {
|
if opts.Place == "" {
|
||||||
return nil
|
return nil
|
||||||
@@ -130,8 +156,10 @@ func print_error(format string, args ...any) {
|
|||||||
|
|
||||||
func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
|
func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
|
||||||
opts = o
|
opts = o
|
||||||
err = parse_place()
|
if err = parse_place(); err != nil {
|
||||||
if err != nil {
|
return 1, err
|
||||||
|
}
|
||||||
|
if err = parse_fit(); err != nil {
|
||||||
return 1, err
|
return 1, err
|
||||||
}
|
}
|
||||||
err = parse_z_index()
|
err = parse_z_index()
|
||||||
|
|||||||
@@ -22,9 +22,16 @@ be positioned at the top left corner of the image, instead of on the line after
|
|||||||
|
|
||||||
--scale-up
|
--scale-up
|
||||||
type=bool-set
|
type=bool-set
|
||||||
When used in combination with :option:`--place` it will cause images that are
|
Cause images that are smaller than the specified area to be scaled up to use as much
|
||||||
smaller than the specified area to be scaled up to use as much of the specified
|
of the specified area as possible. The specified area depends on either the :option:`--place`
|
||||||
area as possible.
|
or the :option:`--fit` options.
|
||||||
|
|
||||||
|
|
||||||
|
--fit
|
||||||
|
choices=width,height,both,none
|
||||||
|
default=width
|
||||||
|
When not using :option:`--place`, control how the image is scaled relative to the screen.
|
||||||
|
You can have it fit in the screen width or height or both or neither.
|
||||||
|
|
||||||
|
|
||||||
--background
|
--background
|
||||||
|
|||||||
@@ -91,12 +91,37 @@ func add_frame(ctx *images.Context, imgd *image_data, img image.Image, left, top
|
|||||||
return &f
|
return &f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scale_up(width, height, maxWidth, maxHeight int) (newWidth, newHeight int) {
|
||||||
|
if width == 0 || height == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the ratio to scale the width and the ratio to scale the height.
|
||||||
|
// We use floating-point division for precision.
|
||||||
|
widthRatio := float64(maxWidth) / float64(width)
|
||||||
|
heightRatio := float64(maxHeight) / float64(height)
|
||||||
|
|
||||||
|
// To preserve the aspect ratio and fit within the limits, we must use the
|
||||||
|
// smaller of the two scaling ratios.
|
||||||
|
var ratio float64
|
||||||
|
if widthRatio < heightRatio {
|
||||||
|
ratio = widthRatio
|
||||||
|
} else {
|
||||||
|
ratio = heightRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the new dimensions and convert them back to uints.
|
||||||
|
newWidth = int(float64(width) * ratio)
|
||||||
|
newHeight = int(float64(height) * ratio)
|
||||||
|
|
||||||
|
return newWidth, newHeight
|
||||||
|
}
|
||||||
|
|
||||||
func scale_image(imgd *image_data) bool {
|
func scale_image(imgd *image_data) bool {
|
||||||
if imgd.needs_scaling {
|
if imgd.needs_scaling {
|
||||||
width, height := imgd.canvas_width, imgd.canvas_height
|
width, height := imgd.canvas_width, imgd.canvas_height
|
||||||
if imgd.canvas_width < imgd.available_width && opts.ScaleUp && place != nil {
|
if opts.ScaleUp && (imgd.canvas_width < imgd.available_width || imgd.canvas_height < imgd.available_height) && (imgd.available_height != inf || imgd.available_width != inf) {
|
||||||
r := float64(imgd.available_width) / float64(imgd.canvas_width)
|
imgd.canvas_width, imgd.canvas_height = scale_up(imgd.canvas_width, imgd.canvas_height, imgd.available_width, imgd.available_height)
|
||||||
imgd.canvas_width, imgd.canvas_height = imgd.available_width, int(r*float64(imgd.canvas_height))
|
|
||||||
}
|
}
|
||||||
neww, newh := images.FitImage(imgd.canvas_width, imgd.canvas_height, imgd.available_width, imgd.available_height)
|
neww, newh := images.FitImage(imgd.canvas_width, imgd.canvas_height, imgd.available_width, imgd.available_height)
|
||||||
imgd.needs_scaling = false
|
imgd.needs_scaling = false
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"image"
|
"image"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -196,15 +197,29 @@ type image_data struct {
|
|||||||
source_name string
|
source_name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inf = math.MaxInt
|
||||||
|
|
||||||
func set_basic_metadata(imgd *image_data) {
|
func set_basic_metadata(imgd *image_data) {
|
||||||
if imgd.frames == nil {
|
if imgd.frames == nil {
|
||||||
imgd.frames = make([]*image_frame, 0, 32)
|
imgd.frames = make([]*image_frame, 0, 32)
|
||||||
}
|
}
|
||||||
imgd.available_width = int(screen_size.Xpixel)
|
|
||||||
imgd.available_height = 10 * imgd.canvas_height
|
|
||||||
if place != nil {
|
if place != nil {
|
||||||
imgd.available_width = place.width * int(screen_size.Xpixel) / int(screen_size.Col)
|
imgd.available_width = place.width * int(screen_size.Xpixel) / int(screen_size.Col)
|
||||||
imgd.available_height = place.height * int(screen_size.Ypixel) / int(screen_size.Row)
|
imgd.available_height = place.height * int(screen_size.Ypixel) / int(screen_size.Row)
|
||||||
|
} else {
|
||||||
|
switch fit_mode {
|
||||||
|
case fit_none:
|
||||||
|
imgd.available_width, imgd.available_height = inf, inf
|
||||||
|
case fit_both:
|
||||||
|
imgd.available_width = int(screen_size.Xpixel)
|
||||||
|
imgd.available_height = int(screen_size.Ypixel)
|
||||||
|
case fit_width:
|
||||||
|
imgd.available_width = int(screen_size.Xpixel)
|
||||||
|
imgd.available_height = inf
|
||||||
|
case fit_height:
|
||||||
|
imgd.available_width = inf
|
||||||
|
imgd.available_height = int(screen_size.Ypixel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
imgd.needs_scaling = imgd.canvas_width > imgd.available_width || imgd.canvas_height > imgd.available_height || opts.ScaleUp
|
imgd.needs_scaling = imgd.canvas_width > imgd.available_width || imgd.canvas_height > imgd.available_height || opts.ScaleUp
|
||||||
imgd.needs_conversion = imgd.needs_scaling || remove_alpha != nil || flip || flop || imgd.format_uppercase != "PNG"
|
imgd.needs_conversion = imgd.needs_scaling || remove_alpha != nil || flip || flop || imgd.format_uppercase != "PNG"
|
||||||
|
|||||||
29
kittens/icat/scaling_test.go
Normal file
29
kittens/icat/scaling_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package icat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func TestScaling(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
w, h, pw, ph, ew, eh int
|
||||||
|
}{
|
||||||
|
{1000, 50, 800, 600, 800, 40},
|
||||||
|
{1000, 50, 800000, 600, 12000, 600},
|
||||||
|
{100, 50, 800, 600, 800, 400},
|
||||||
|
{1920, 1080, 800, 600, 800, 450},
|
||||||
|
{300, 900, 800, 600, 200, 600},
|
||||||
|
{400, 300, 800, 600, 800, 600},
|
||||||
|
} {
|
||||||
|
aw, ah := scale_up(tc.w, tc.h, tc.pw, tc.ph)
|
||||||
|
actual := image.Pt(aw, ah)
|
||||||
|
expected := image.Pt(tc.ew, tc.eh)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("want: %v got: %v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user