~dricottone/image2ascii

ref: 9e6742da409008fc764fa344fc324438ce7928eb image2ascii/convert/resize.go -rw-r--r-- 4.1 KiB
9e6742da — qeesung add the fit options to scale the image to fit the screen 6 years ago
                                                                                
c6c0ddbf qeesung
9b483cdb qeesung
a482eea7 qeesung
55dcbb9f qeesung
9b483cdb qeesung
55dcbb9f qeesung
9b483cdb qeesung
55dcbb9f qeesung
9b483cdb qeesung
49547468 qeesung
c6c0ddbf qeesung
9b483cdb qeesung
55dcbb9f qeesung
a482eea7 qeesung
55dcbb9f qeesung
9e6742da qeesung
55dcbb9f qeesung
9e6742da qeesung
55dcbb9f qeesung
9e6742da qeesung
55dcbb9f qeesung
c6c0ddbf qeesung
9e6742da qeesung
c6c0ddbf qeesung
9e6742da qeesung
a482eea7 qeesung
c6c0ddbf qeesung
9b483cdb qeesung
9e6742da qeesung
49547468 qeesung
9b483cdb qeesung
65420f04 qeesung
9b483cdb qeesung
49547468 qeesung
9b483cdb qeesung
55dcbb9f qeesung
a482eea7 qeesung
c6c0ddbf qeesung
a482eea7 qeesung
55dcbb9f qeesung
a482eea7 qeesung
55dcbb9f qeesung
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package convert

import (
	"errors"
	"github.com/mattn/go-isatty"
	"github.com/nfnt/resize"
	terminal "github.com/wayneashleyberry/terminal-dimensions"
	"image"
	"log"
	"os"
	"runtime"
)

// ScaleImage resize the convert to expected size base on the convert options
func ScaleImage(image image.Image, options *Options) (newImage image.Image) {
	sz := image.Bounds()
	ratio := options.Ratio
	newHeight := sz.Max.Y
	newWidth := sz.Max.X

	if options.FixedWidth != -1 {
		newWidth = options.FixedWidth
	}

	if options.FixedHeight != -1 {
		newHeight = options.FixedHeight
	}

	// use the ratio the scale the image
	if options.FixedHeight == -1 && options.FixedWidth == -1 && ratio != 1 {
		newWidth = ScaleWidthByRatio(float64(sz.Max.X), ratio)
		newHeight = ScaleHeightByRatio(float64(sz.Max.Y), ratio)
	}

	// fit the screen
	if ratio == 1 &&
		options.FixedWidth == -1 &&
		options.FixedHeight == -1 &&
		options.FitScreen {
		fitWidth, fitHeight, err := CalcProportionalFittingScreenSize(image)
		if err != nil {
			log.Fatal(err)
		}
		newWidth = int(fitWidth)
		newHeight = int(fitHeight)
	}

	//Stretch the picture to overspread the terminal
	if ratio == 1 &&
		options.FixedWidth == -1 &&
		options.FixedHeight == -1 &&
		!options.FitScreen &&
		options.StretchedScreen {
		screenWidth, screenHeight, err := getTerminalScreenSize()
		if err != nil {
			log.Fatal(err)
		}
		newWidth = int(screenWidth)
		newHeight = int(screenHeight)
	}

	newImage = resize.Resize(uint(newWidth), uint(newHeight), image, resize.Lanczos3)
	return
}

// CalcProportionalFittingScreenSize proportional scale the image
// so that the terminal can just show the picture.
func CalcProportionalFittingScreenSize(image image.Image) (newWidth, newHeight int, err error) {
	if !isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()) {
		return 0, 0,
			errors.New("can not detect the terminal, please disable the '-s fitScreen' option")
	}

	screenWidth, _ := terminal.Width()
	screenHeight, _ := terminal.Height()
	sz := image.Bounds()
	newWidth, newHeight = CalcFitSize(
		float64(screenWidth),
		float64(screenHeight),
		float64(sz.Max.X),
		float64(sz.Max.Y))
	return
}

// CalcFitSizeRatio through the given length and width,
// the computation can match the optimal scaling ratio of the length and width.
// In other words, it is able to give a given size rectangle to contain pictures
// Either match the width first, then scale the length equally,
// or match the length first, then scale the height equally.
// More detail please check the example
func CalcFitSizeRatio(width, height, imageWidth, imageHeight float64) (ratio float64) {
	ratio = 1.0
	// try to fit the height
	ratio = height / imageHeight
	scaledWidth := imageWidth * ratio / charWidth()
	if scaledWidth < width {
		return ratio / charWidth()
	}

	// try to fit the width
	ratio = width / imageWidth
	return ratio
}

// CalcFitSize through the given length and width ,
// Calculation is able to match the length and width of
// the specified size, and is proportional scaling.
func CalcFitSize(width, height, toBeFitWidth, toBeFitHeight float64) (fitWidth, fitHeight int) {
	ratio := CalcFitSizeRatio(width, height, toBeFitWidth, toBeFitHeight)
	fitWidth = ScaleWidthByRatio(toBeFitWidth, ratio)
	fitHeight = ScaleHeightByRatio(toBeFitHeight, ratio)
	return
}

func ScaleWidthByRatio(width float64, ratio float64) int {
	return int(width * ratio)
}

func ScaleHeightByRatio(height float64, ratio float64) int {
	return int(height * ratio * charWidth())
}

// charWidth get the terminal char width on different system
func charWidth() float64 {
	if isWindows() {
		return 0.714
	}
	return 0.5
}

// isWindows check if current system is windows
func isWindows() bool {
	return runtime.GOOS == "windows"
}

// getTerminalScreenSize get the current terminal screen size
func getTerminalScreenSize() (newWidth, newHeight uint, err error) {
	if !isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()) {
		return 0, 0,
			errors.New("can not detect the terminal, please disable the '-s fitScreen' option")
	}

	x, _ := terminal.Width()
	y, _ := terminal.Height()

	return x, y, nil
}