mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
printimage: keep aspect ratio when scaling to fit (#479)
When `printimage` is invoked without `-w` or `-h`, we now preserve the aspect ratio of each input image when scaling it to fit in the window. A new flag `-i` ignores the aspect ratio, recovering the old behavior when neither `-w` nor `-h` is passed. When `-i` is passed alongside exactly one of `-w` or `-h`, the other dimension is just taken from the window size, ignoring aspect ratio. We also unconditionally print a newline between images to prevent them from overlapping. wchargin-branch: printimage-fit wchargin-source: 4c2cfcffe9ce1a3b30c0ff051e3c6a2c166ae1c7
This commit is contained in:
parent
9308463b0f
commit
0272f638a5
1 changed files with 33 additions and 23 deletions
|
@ -52,15 +52,17 @@ static struct Flags {
|
||||||
bool dither;
|
bool dither;
|
||||||
bool ruler;
|
bool ruler;
|
||||||
bool magikarp;
|
bool magikarp;
|
||||||
bool trailingnewline;
|
|
||||||
long half;
|
long half;
|
||||||
bool full;
|
bool full;
|
||||||
|
bool ignoreaspect;
|
||||||
long width;
|
long width;
|
||||||
long height;
|
long height;
|
||||||
enum TtyBlocksSelection blocks;
|
enum TtyBlocksSelection blocks;
|
||||||
enum TtyQuantizationAlgorithm quant;
|
enum TtyQuantizationAlgorithm quant;
|
||||||
} g_flags;
|
} g_flags;
|
||||||
|
|
||||||
|
struct winsize g_winsize;
|
||||||
|
|
||||||
static wontreturn void PrintUsage(int rc, FILE *f) {
|
static wontreturn void PrintUsage(int rc, FILE *f) {
|
||||||
fprintf(f, "Usage: %s%s", program_invocation_name, "\
|
fprintf(f, "Usage: %s%s", program_invocation_name, "\
|
||||||
[FLAGS] [PATH]\n\
|
[FLAGS] [PATH]\n\
|
||||||
|
@ -70,12 +72,13 @@ FLAGS\n\
|
||||||
-o PATH output path\n\
|
-o PATH output path\n\
|
||||||
-w INT manual width\n\
|
-w INT manual width\n\
|
||||||
-h INT manual height\n\
|
-h INT manual height\n\
|
||||||
|
-f display full size\n\
|
||||||
|
-i ignore aspect ratio\n\
|
||||||
-4 unicode blocks\n\
|
-4 unicode blocks\n\
|
||||||
-a ansi color mode\n\
|
-a ansi color mode\n\
|
||||||
-t true color mode\n\
|
-t true color mode\n\
|
||||||
-2 use half blocks\n\
|
-2 use half blocks\n\
|
||||||
-3 ibm cp437 blocks\n\
|
-3 ibm cp437 blocks\n\
|
||||||
-f display full size\n\
|
|
||||||
-s unsharp sharpening\n\
|
-s unsharp sharpening\n\
|
||||||
-x xterm256 color mode\n\
|
-x xterm256 color mode\n\
|
||||||
-m use magikarp scaling\n\
|
-m use magikarp scaling\n\
|
||||||
|
@ -104,14 +107,13 @@ static int ParseNumberOption(const char *arg) {
|
||||||
|
|
||||||
static void GetOpts(int *argc, char *argv[]) {
|
static void GetOpts(int *argc, char *argv[]) {
|
||||||
int opt;
|
int opt;
|
||||||
struct winsize ws;
|
|
||||||
g_flags.quant = kTtyQuantTrue;
|
g_flags.quant = kTtyQuantTrue;
|
||||||
g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode;
|
g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode;
|
||||||
if (*argc == 2 &&
|
if (*argc == 2 &&
|
||||||
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) {
|
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) {
|
||||||
PrintUsage(EXIT_SUCCESS, stdout);
|
PrintUsage(EXIT_SUCCESS, stdout);
|
||||||
}
|
}
|
||||||
while ((opt = getopt(*argc, argv, "?vpmfrtxads234o:w:h:")) != -1) {
|
while ((opt = getopt(*argc, argv, "?vpmfirtxads234o:w:h:")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'o':
|
case 'o':
|
||||||
g_flags.out = optarg;
|
g_flags.out = optarg;
|
||||||
|
@ -123,16 +125,17 @@ static void GetOpts(int *argc, char *argv[]) {
|
||||||
g_flags.unsharp = true;
|
g_flags.unsharp = true;
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
g_flags.trailingnewline = true;
|
|
||||||
g_flags.width = ParseNumberOption(optarg);
|
g_flags.width = ParseNumberOption(optarg);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
g_flags.trailingnewline = true;
|
|
||||||
g_flags.height = ParseNumberOption(optarg);
|
g_flags.height = ParseNumberOption(optarg);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
g_flags.full = true;
|
g_flags.full = true;
|
||||||
break;
|
break;
|
||||||
|
case 'i':
|
||||||
|
g_flags.ignoreaspect = true;
|
||||||
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
g_flags.half = true;
|
g_flags.half = true;
|
||||||
break;
|
break;
|
||||||
|
@ -169,14 +172,11 @@ static void GetOpts(int *argc, char *argv[]) {
|
||||||
PrintUsage(EX_USAGE, stderr);
|
PrintUsage(EX_USAGE, stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!g_flags.full && (!g_flags.width && !g_flags.height)) {
|
g_winsize.ws_col = 80;
|
||||||
ws.ws_col = 80;
|
g_winsize.ws_row = 24;
|
||||||
ws.ws_row = 24;
|
if (!g_flags.full && (!g_flags.width || !g_flags.height)) {
|
||||||
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) != -1 ||
|
ioctl(STDIN_FILENO, TIOCGWINSZ, &g_winsize) != -1 ||
|
||||||
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) {
|
ioctl(STDOUT_FILENO, TIOCGWINSZ, &g_winsize);
|
||||||
g_flags.width = ws.ws_col * (1 + !g_flags.half);
|
|
||||||
g_flags.height = ws.ws_row * 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ttyquantsetup(g_flags.quant, kTtyQuantRgb, g_flags.blocks);
|
ttyquantsetup(g_flags.quant, kTtyQuantRgb, g_flags.blocks);
|
||||||
}
|
}
|
||||||
|
@ -335,9 +335,7 @@ static void PrintImageSerious(long yn, long xn, unsigned char RGB[3][yn][xn],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p = ttyraster(vt, (void *)TTY, tyn, txn, bg, fg);
|
p = ttyraster(vt, (void *)TTY, tyn, txn, bg, fg);
|
||||||
*p++ = '\r';
|
p = stpcpy(p, "\e[0m\r\n");
|
||||||
if (g_flags.trailingnewline) *p++ = '\n';
|
|
||||||
p = stpcpy(p, "\e[0m");
|
|
||||||
ttywrite(STDOUT_FILENO, vt, p - vt);
|
ttywrite(STDOUT_FILENO, vt, p - vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,7 +361,7 @@ void WithImageFile(const char *path,
|
||||||
void fn(long yn, long xn, unsigned char RGB[3][yn][xn])) {
|
void fn(long yn, long xn, unsigned char RGB[3][yn][xn])) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
void *map, *data, *data2;
|
void *map, *data, *data2;
|
||||||
int fd, yn, xn, cn, dyn, dxn, syn, sxn;
|
int fd, yn, xn, cn, dyn, dxn, syn, sxn, wyn, wxn;
|
||||||
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
|
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
|
||||||
CHECK_NE(-1, fstat(fd, &st));
|
CHECK_NE(-1, fstat(fd, &st));
|
||||||
CHECK_GT(st.st_size, 0);
|
CHECK_GT(st.st_size, 0);
|
||||||
|
@ -385,16 +383,28 @@ void WithImageFile(const char *path,
|
||||||
data, 0, yn, 0, xn);
|
data, 0, yn, 0, xn);
|
||||||
cn = 3;
|
cn = 3;
|
||||||
}
|
}
|
||||||
if (g_flags.height || g_flags.width) {
|
if (!g_flags.full) {
|
||||||
syn = yn;
|
syn = yn;
|
||||||
sxn = xn;
|
sxn = xn;
|
||||||
dyn = g_flags.height;
|
dyn = g_flags.height;
|
||||||
dxn = g_flags.width;
|
dxn = g_flags.width;
|
||||||
if (dyn && !dxn) {
|
wyn = g_winsize.ws_row * 2;
|
||||||
dxn = dyn * xn * (1 + !g_flags.half) / yn;
|
wxn = g_winsize.ws_col;
|
||||||
|
if (g_flags.ignoreaspect) {
|
||||||
|
if (!dyn) dyn = wyn;
|
||||||
|
if (!dxn) dxn = wxn * (1 + !g_flags.half);
|
||||||
}
|
}
|
||||||
if (dxn && !dyn) {
|
if (!dyn && !dxn) {
|
||||||
dyn = dxn * yn / (xn * (1 + !g_flags.half));
|
if (sxn * wyn > syn * wxn) {
|
||||||
|
dxn = wxn * (1 + !g_flags.half);
|
||||||
|
} else {
|
||||||
|
dyn = wyn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dyn && !dxn) {
|
||||||
|
dxn = dyn * sxn * (1 + !g_flags.half) / syn;
|
||||||
|
} else if (dxn && !dyn) {
|
||||||
|
dyn = dxn * syn / (sxn * (1 + !g_flags.half));
|
||||||
}
|
}
|
||||||
if (g_flags.magikarp) {
|
if (g_flags.magikarp) {
|
||||||
while (HALF(syn) > dyn || HALF(sxn) > dxn) {
|
while (HALF(syn) > dyn || HALF(sxn) > dxn) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue