1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott (at) gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 #define SIXEL_WIDTH_LIMIT 10000 27 #define SIXEL_HEIGHT_LIMIT 10000 28 29 struct sixel_line { 30 u_int x; 31 uint16_t *data; 32 }; 33 34 struct sixel_image { 35 u_int x; 36 u_int y; 37 u_int xpixel; 38 u_int ypixel; 39 40 u_int set_ra; 41 u_int ra_x; 42 u_int ra_y; 43 44 u_int *colours; 45 u_int ncolours; 46 u_int used_colours; 47 u_int p2; 48 49 u_int dx; 50 u_int dy; 51 u_int dc; 52 53 struct sixel_line *lines; 54 }; 55 56 struct sixel_chunk { 57 u_int next_x; 58 u_int next_y; 59 60 u_int count; 61 char pattern; 62 char next_pattern; 63 64 size_t len; 65 size_t used; 66 char *data; 67 }; 68 69 static int 70 sixel_parse_expand_lines(struct sixel_image *si, u_int y) 71 { 72 if (y <= si->y) 73 return (0); 74 if (y > SIXEL_HEIGHT_LIMIT) 75 return (1); 76 si->lines = xrecallocarray(si->lines, si->y, y, sizeof *si->lines); 77 si->y = y; 78 return (0); 79 } 80 81 static int 82 sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x) 83 { 84 if (x <= sl->x) 85 return (0); 86 if (x > SIXEL_WIDTH_LIMIT) 87 return (1); 88 if (x > si->x) 89 si->x = x; 90 sl->data = xrecallocarray(sl->data, sl->x, si->x, sizeof *sl->data); 91 sl->x = si->x; 92 return (0); 93 } 94 95 static u_int 96 sixel_get_pixel(struct sixel_image *si, u_int x, u_int y) 97 { 98 struct sixel_line *sl; 99 100 if (y >= si->y) 101 return (0); 102 sl = &si->lines[y]; 103 if (x >= sl->x) 104 return (0); 105 return (sl->data[x]); 106 } 107 108 static int 109 sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c) 110 { 111 struct sixel_line *sl; 112 113 if (sixel_parse_expand_lines(si, y + 1) != 0) 114 return (1); 115 sl = &si->lines[y]; 116 if (sixel_parse_expand_line(si, sl, x + 1) != 0) 117 return (1); 118 sl->data[x] = c; 119 return (0); 120 } 121 122 static int 123 sixel_parse_write(struct sixel_image *si, u_int ch) 124 { 125 struct sixel_line *sl; 126 u_int i; 127 128 if (sixel_parse_expand_lines(si, si->dy + 6) != 0) 129 return (1); 130 sl = &si->lines[si->dy]; 131 132 for (i = 0; i < 6; i++) { 133 if (sixel_parse_expand_line(si, sl, si->dx + 1) != 0) 134 return (1); 135 if (ch & (1 << i)) 136 sl->data[si->dx] = si->dc; 137 sl++; 138 } 139 return (0); 140 } 141 142 static const char * 143 sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end) 144 { 145 const char *last; 146 char *endptr; 147 u_int x, y; 148 149 last = cp; 150 while (last != end) { 151 if (*last != ';' && (*last < '0' || *last > '9')) 152 break; 153 last++; 154 } 155 strtoul(cp, &endptr, 10); 156 if (endptr == last || *endptr != ';') 157 return (last); 158 strtoul(endptr + 1, &endptr, 10); 159 if (endptr == last) 160 return (last); 161 if (*endptr != ';') { 162 log_debug("%s: missing ;", __func__); 163 return (NULL); 164 } 165 166 x = strtoul(endptr + 1, &endptr, 10); 167 if (endptr == last || *endptr != ';') { 168 log_debug("%s: missing ;", __func__); 169 return (NULL); 170 } 171 if (x > SIXEL_WIDTH_LIMIT) { 172 log_debug("%s: image is too wide", __func__); 173 return (NULL); 174 } 175 y = strtoul(endptr + 1, &endptr, 10); 176 if (endptr != last) { 177 log_debug("%s: extra ;", __func__); 178 return (NULL); 179 } 180 if (y > SIXEL_HEIGHT_LIMIT) { 181 log_debug("%s: image is too tall", __func__); 182 return (NULL); 183 } 184 185 si->x = x; 186 sixel_parse_expand_lines(si, y); 187 188 si->set_ra = 1; 189 si->ra_x = x; 190 si->ra_y = y; 191 192 return (last); 193 } 194 195 static const char * 196 sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end) 197 { 198 const char *last; 199 char *endptr; 200 u_int c, type, c1, c2, c3; 201 202 last = cp; 203 while (last != end) { 204 if (*last != ';' && (*last < '0' || *last > '9')) 205 break; 206 last++; 207 } 208 209 c = strtoul(cp, &endptr, 10); 210 if (c > SIXEL_COLOUR_REGISTERS) { 211 log_debug("%s: too many colours", __func__); 212 return (NULL); 213 } 214 if (si->used_colours <= c) 215 si->used_colours = c + 1; 216 si->dc = c + 1; 217 if (endptr == last || *endptr != ';') 218 return (last); 219 220 type = strtoul(endptr + 1, &endptr, 10); 221 if (endptr == last || *endptr != ';') { 222 log_debug("%s: missing ;", __func__); 223 return (NULL); 224 } 225 c1 = strtoul(endptr + 1, &endptr, 10); 226 if (endptr == last || *endptr != ';') { 227 log_debug("%s: missing ;", __func__); 228 return (NULL); 229 } 230 c2 = strtoul(endptr + 1, &endptr, 10); 231 if (endptr == last || *endptr != ';') { 232 log_debug("%s: missing ;", __func__); 233 return (NULL); 234 } 235 c3 = strtoul(endptr + 1, &endptr, 10); 236 if (endptr != last) { 237 log_debug("%s: missing ;", __func__); 238 return (NULL); 239 } 240 241 if ((type != 1 && type != 2) || 242 (type == 1 && (c1 > 360 || c2 > 100 || c3 > 100)) || 243 (type == 2 && (c1 > 100 || c2 > 100 || c3 > 100))) { 244 log_debug("%s: invalid color %u;%u;%u;%u", __func__, type, 245 c1, c2, c3); 246 return (NULL); 247 } 248 249 if (c + 1 > si->ncolours) { 250 si->colours = xrecallocarray(si->colours, si->ncolours, c + 1, 251 sizeof *si->colours); 252 si->ncolours = c + 1; 253 } 254 si->colours[c] = (type << 25) | (c1 << 16) | (c2 << 8) | c3; 255 return (last); 256 } 257 258 static const char * 259 sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end) 260 { 261 const char *last; 262 char tmp[32], ch; 263 u_int n = 0, i; 264 const char *errstr = NULL; 265 266 last = cp; 267 while (last != end) { 268 if (*last < '0' || *last > '9') 269 break; 270 tmp[n++] = *last++; 271 if (n == (sizeof tmp) - 1) { 272 log_debug("%s: repeat not terminated", __func__); 273 return (NULL); 274 } 275 } 276 if (n == 0 || last == end) { 277 log_debug("%s: repeat not terminated", __func__); 278 return (NULL); 279 } 280 tmp[n] = '\0'; 281 282 n = strtonum(tmp, 1, SIXEL_WIDTH_LIMIT, &errstr); 283 if (n == 0 || errstr != NULL) { 284 log_debug("%s: repeat too wide", __func__); 285 return (NULL); 286 } 287 288 ch = (*last++) - 0x3f; 289 for (i = 0; i < n; i++) { 290 if (sixel_parse_write(si, ch) != 0) { 291 log_debug("%s: width limit reached", __func__); 292 return (NULL); 293 } 294 si->dx++; 295 } 296 return (last); 297 } 298 299 struct sixel_image * 300 sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel) 301 { 302 struct sixel_image *si; 303 const char *cp = buf, *end = buf + len; 304 char ch; 305 306 if (len == 0 || len == 1 || *cp++ != 'q') { 307 log_debug("%s: empty image", __func__); 308 return (NULL); 309 } 310 311 si = xcalloc (1, sizeof *si); 312 si->xpixel = xpixel; 313 si->ypixel = ypixel; 314 si->p2 = p2; 315 316 while (cp != end) { 317 ch = *cp++; 318 switch (ch) { 319 case '"': 320 cp = sixel_parse_attributes(si, cp, end); 321 if (cp == NULL) 322 goto bad; 323 break; 324 case '#': 325 cp = sixel_parse_colour(si, cp, end); 326 if (cp == NULL) 327 goto bad; 328 break; 329 case '!': 330 cp = sixel_parse_repeat(si, cp, end); 331 if (cp == NULL) 332 goto bad; 333 break; 334 case '-': 335 si->dx = 0; 336 si->dy += 6; 337 break; 338 case '$': 339 si->dx = 0; 340 break; 341 default: 342 if (ch < 0x20) 343 break; 344 if (ch < 0x3f || ch > 0x7e) 345 goto bad; 346 if (sixel_parse_write(si, ch - 0x3f) != 0) { 347 log_debug("%s: width limit reached", __func__); 348 goto bad; 349 } 350 si->dx++; 351 break; 352 } 353 } 354 355 if (si->x == 0 || si->y == 0) 356 goto bad; 357 return (si); 358 359 bad: 360 free(si); 361 return (NULL); 362 } 363 364 void 365 sixel_free(struct sixel_image *si) 366 { 367 u_int y; 368 369 for (y = 0; y < si->y; y++) 370 free(si->lines[y].data); 371 free(si->lines); 372 373 free(si->colours); 374 free(si); 375 } 376 377 void 378 sixel_log(struct sixel_image *si) 379 { 380 struct sixel_line *sl; 381 char s[SIXEL_WIDTH_LIMIT + 1]; 382 u_int i, x, y, cx, cy; 383 384 sixel_size_in_cells(si, &cx, &cy); 385 log_debug("%s: image %ux%u (%ux%u)", __func__, si->x, si->y, cx, cy); 386 for (i = 0; i < si->ncolours; i++) 387 log_debug("%s: colour %u is %07x", __func__, i, si->colours[i]); 388 for (y = 0; y < si->y; y++) { 389 sl = &si->lines[y]; 390 for (x = 0; x < si->x; x++) { 391 if (x >= sl->x) 392 s[x] = '_'; 393 else if (sl->data[x] != 0) 394 s[x] = '0' + (sl->data[x] - 1) % 10; 395 else 396 s[x] = '.'; 397 } 398 s[x] = '\0'; 399 log_debug("%s: %4u: %s", __func__, y, s); 400 } 401 } 402 403 void 404 sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y) 405 { 406 if ((si->x % si->xpixel) == 0) 407 *x = (si->x / si->xpixel); 408 else 409 *x = 1 + (si->x / si->xpixel); 410 if ((si->y % si->ypixel) == 0) 411 *y = (si->y / si->ypixel); 412 else 413 *y = 1 + (si->y / si->ypixel); 414 } 415 416 struct sixel_image * 417 sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox, 418 u_int oy, u_int sx, u_int sy, int colours) 419 { 420 struct sixel_image *new; 421 u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py; 422 u_int x, y, i; 423 424 /* 425 * We want to get the section of the image at ox,oy in image cells and 426 * map it onto the same size in terminal cells. 427 */ 428 429 sixel_size_in_cells(si, &cx, &cy); 430 if (ox >= cx) 431 return (NULL); 432 if (oy >= cy) 433 return (NULL); 434 if (ox + sx >= cx) 435 sx = cx - ox; 436 if (oy + sy >= cy) 437 sy = cy - oy; 438 439 if (xpixel == 0) 440 xpixel = si->xpixel; 441 if (ypixel == 0) 442 ypixel = si->ypixel; 443 444 pox = ox * si->xpixel; 445 poy = oy * si->ypixel; 446 psx = sx * si->xpixel; 447 psy = sy * si->ypixel; 448 449 tsx = sx * xpixel; 450 tsy = sy * ypixel; 451 452 new = xcalloc (1, sizeof *si); 453 new->xpixel = xpixel; 454 new->ypixel = ypixel; 455 new->p2 = si->p2; 456 457 new->set_ra = si->set_ra; 458 /* clamp to slice end */ 459 new->ra_x = si->ra_x < psx ? si->ra_x : psx; 460 new->ra_y = si->ra_y < psy ? si->ra_y : psy; 461 /* subtract slice origin */ 462 new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0; 463 new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0; 464 /* resize */ 465 new->ra_x = new->ra_x * xpixel / si->xpixel; 466 new->ra_y = new->ra_y * ypixel / si->ypixel; 467 468 new->used_colours = si->used_colours; 469 for (y = 0; y < tsy; y++) { 470 py = poy + ((double)y * psy / tsy); 471 for (x = 0; x < tsx; x++) { 472 px = pox + ((double)x * psx / tsx); 473 sixel_set_pixel(new, x, y, sixel_get_pixel(si, px, py)); 474 } 475 } 476 477 if (colours && si->ncolours != 0) { 478 new->colours = xmalloc(si->ncolours * sizeof *new->colours); 479 for (i = 0; i < si->ncolours; i++) 480 new->colours[i] = si->colours[i]; 481 new->ncolours = si->ncolours; 482 } 483 return (new); 484 } 485 486 static void 487 sixel_print_add(char **buf, size_t *len, size_t *used, const char *s, 488 size_t slen) 489 { 490 if (*used + slen >= *len + 1) { 491 (*len) *= 2; 492 *buf = xrealloc(*buf, *len); 493 } 494 memcpy(*buf + *used, s, slen); 495 (*used) += slen; 496 } 497 498 static void 499 sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch) 500 { 501 char tmp[16]; 502 size_t tmplen; 503 504 if (count == 1) 505 sixel_print_add(buf, len, used, &ch, 1); 506 else if (count == 2) { 507 sixel_print_add(buf, len, used, &ch, 1); 508 sixel_print_add(buf, len, used, &ch, 1); 509 } else if (count == 3) { 510 sixel_print_add(buf, len, used, &ch, 1); 511 sixel_print_add(buf, len, used, &ch, 1); 512 sixel_print_add(buf, len, used, &ch, 1); 513 } else if (count != 0) { 514 tmplen = xsnprintf(tmp, sizeof tmp, "!%u%c", count, ch); 515 sixel_print_add(buf, len, used, tmp, tmplen); 516 } 517 } 518 519 static void 520 sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks, 521 u_int y, u_int *active, u_int *nactive) 522 { 523 u_int i, x, c, dx, colors[6]; 524 struct sixel_chunk *chunk = NULL; 525 struct sixel_line *sl; 526 527 for (x = 0; x < si->x; x++) { 528 for (i = 0; i < 6; i++) { 529 colors[i] = 0; 530 if (y + i < si->y) { 531 sl = &si->lines[y + i]; 532 if (x < sl->x && sl->data[x] != 0) { 533 colors[i] = sl->data[x]; 534 c = sl->data[x] - 1; 535 chunks[c].next_pattern |= 1 << i; 536 } 537 } 538 } 539 540 for (i = 0; i < 6; i++) { 541 if (colors[i] == 0) 542 continue; 543 544 c = colors[i] - 1; 545 chunk = &chunks[c]; 546 if (chunk->next_x == x + 1) 547 continue; 548 549 if (chunk->next_y < y + 1) { 550 chunk->next_y = y + 1; 551 active[(*nactive)++] = c; 552 } 553 554 dx = x - chunk->next_x; 555 if (chunk->pattern != chunk->next_pattern || dx != 0) { 556 sixel_print_repeat(&chunk->data, &chunk->len, 557 &chunk->used, chunk->count, 558 chunk->pattern + 0x3f); 559 sixel_print_repeat(&chunk->data, &chunk->len, 560 &chunk->used, dx, '?'); 561 chunk->pattern = chunk->next_pattern; 562 chunk->count = 0; 563 } 564 chunk->count++; 565 chunk->next_pattern = 0; 566 chunk->next_x = x + 1; 567 } 568 } 569 } 570 571 char * 572 sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size) 573 { 574 char *buf, tmp[64]; 575 size_t len, used = 0, tmplen; 576 u_int *colours, ncolours, used_colours, i, c, y; 577 u_int *active, nactive; 578 struct sixel_chunk *chunks, *chunk; 579 580 if (map != NULL) { 581 colours = map->colours; 582 ncolours = map->ncolours; 583 } else { 584 colours = si->colours; 585 ncolours = si->ncolours; 586 } 587 588 used_colours = si->used_colours; 589 if (used_colours == 0) 590 return (NULL); 591 592 len = 8192; 593 buf = xmalloc(len); 594 595 tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2); 596 sixel_print_add(&buf, &len, &used, tmp, tmplen); 597 598 if (si->set_ra) { 599 tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->ra_x, 600 si->ra_y); 601 sixel_print_add(&buf, &len, &used, tmp, tmplen); 602 } 603 604 chunks = xcalloc(used_colours, sizeof *chunks); 605 active = xcalloc(used_colours, sizeof *active); 606 607 for (i = 0; i < ncolours; i++) { 608 c = colours[i]; 609 tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u", 610 i, c >> 25, (c >> 16) & 0x1ff, (c >> 8) & 0xff, c & 0xff); 611 sixel_print_add(&buf, &len, &used, tmp, tmplen); 612 } 613 614 for (i = 0; i < used_colours; i++) { 615 chunk = &chunks[i]; 616 chunk->len = 8; 617 chunk->data = xmalloc(chunk->len); 618 } 619 620 for (y = 0; y < si->y; y += 6) { 621 nactive = 0; 622 sixel_print_compress_colors(si, chunks, y, active, &nactive); 623 624 for (i = 0; i < nactive; i++) { 625 c = active[i]; 626 chunk = &chunks[c]; 627 tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c); 628 sixel_print_add(&buf, &len, &used, tmp, tmplen); 629 sixel_print_add(&buf, &len, &used, chunk->data, 630 chunk->used); 631 sixel_print_repeat(&buf, &len, &used, chunk->count, 632 chunk->pattern + 0x3f); 633 sixel_print_add(&buf, &len, &used, "$", 1); 634 chunk->used = chunk->next_x = chunk->count = 0; 635 } 636 637 if (buf[used - 1] == '$') 638 used--; 639 sixel_print_add(&buf, &len, &used, "-", 1); 640 } 641 if (buf[used - 1] == '-') 642 used--; 643 644 sixel_print_add(&buf, &len, &used, "\033\\", 2); 645 646 buf[used] = '\0'; 647 if (size != NULL) 648 *size = used; 649 650 for (i = 0; i < used_colours; i++) 651 free(chunks[i].data); 652 free(active); 653 free(chunks); 654 655 return (buf); 656 } 657 658 struct screen * 659 sixel_to_screen(struct sixel_image *si) 660 { 661 struct screen *s; 662 struct screen_write_ctx ctx; 663 struct grid_cell gc; 664 u_int x, y, sx, sy; 665 666 sixel_size_in_cells(si, &sx, &sy); 667 668 s = xmalloc(sizeof *s); 669 screen_init(s, sx, sy, 0); 670 671 memcpy(&gc, &grid_default_cell, sizeof gc); 672 gc.attr |= (GRID_ATTR_CHARSET|GRID_ATTR_DIM); 673 utf8_set(&gc.data, '~'); 674 675 screen_write_start(&ctx, s); 676 if (sx == 1 || sy == 1) { 677 for (y = 0; y < sy; y++) { 678 for (x = 0; x < sx; x++) 679 grid_view_set_cell(s->grid, x, y, &gc); 680 } 681 } else { 682 screen_write_box(&ctx, sx, sy, BOX_LINES_DEFAULT, NULL, NULL); 683 for (y = 1; y < sy - 1; y++) { 684 for (x = 1; x < sx - 1; x++) 685 grid_view_set_cell(s->grid, x, y, &gc); 686 } 687 } 688 screen_write_stop(&ctx); 689 return (s); 690 } 691