4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
26 #define SIXEL_WIDTH_LIMIT 10000
27 #define SIXEL_HEIGHT_LIMIT 10000
52 struct sixel_line
*lines
;
69 sixel_parse_expand_lines(struct sixel_image
*si
, u_int y
)
73 if (y
> SIXEL_HEIGHT_LIMIT
)
75 si
->lines
= xrecallocarray(si
->lines
, si
->y
, y
, sizeof *si
->lines
);
81 sixel_parse_expand_line(struct sixel_image
*si
, struct sixel_line
*sl
, u_int x
)
85 if (x
> SIXEL_WIDTH_LIMIT
)
89 sl
->data
= xrecallocarray(sl
->data
, sl
->x
, si
->x
, sizeof *sl
->data
);
95 sixel_get_pixel(struct sixel_image
*si
, u_int x
, u_int y
)
97 struct sixel_line
*sl
;
104 return (sl
->data
[x
]);
108 sixel_set_pixel(struct sixel_image
*si
, u_int x
, u_int y
, u_int c
)
110 struct sixel_line
*sl
;
112 if (sixel_parse_expand_lines(si
, y
+ 1) != 0)
115 if (sixel_parse_expand_line(si
, sl
, x
+ 1) != 0)
122 sixel_parse_write(struct sixel_image
*si
, u_int ch
)
124 struct sixel_line
*sl
;
127 if (sixel_parse_expand_lines(si
, si
->dy
+ 6) != 0)
129 sl
= &si
->lines
[si
->dy
];
131 for (i
= 0; i
< 6; i
++) {
132 if (sixel_parse_expand_line(si
, sl
, si
->dx
+ 1) != 0)
135 sl
->data
[si
->dx
] = si
->dc
;
142 sixel_parse_attributes(struct sixel_image
*si
, const char *cp
, const char *end
)
149 while (last
!= end
) {
150 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
154 strtoul(cp
, &endptr
, 10);
155 if (endptr
== last
|| *endptr
!= ';')
157 strtoul(endptr
+ 1, &endptr
, 10);
160 if (*endptr
!= ';') {
161 log_debug("%s: missing ;", __func__
);
165 x
= strtoul(endptr
+ 1, &endptr
, 10);
166 if (endptr
== last
|| *endptr
!= ';') {
167 log_debug("%s: missing ;", __func__
);
170 if (x
> SIXEL_WIDTH_LIMIT
) {
171 log_debug("%s: image is too wide", __func__
);
174 y
= strtoul(endptr
+ 1, &endptr
, 10);
175 if (endptr
!= last
) {
176 log_debug("%s: extra ;", __func__
);
179 if (y
> SIXEL_HEIGHT_LIMIT
) {
180 log_debug("%s: image is too tall", __func__
);
185 sixel_parse_expand_lines(si
, y
);
195 sixel_parse_colour(struct sixel_image
*si
, const char *cp
, const char *end
)
199 u_int c
, type
, r
, g
, b
;
202 while (last
!= end
) {
203 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
208 c
= strtoul(cp
, &endptr
, 10);
209 if (c
> SIXEL_COLOUR_REGISTERS
) {
210 log_debug("%s: too many colours", __func__
);
214 if (endptr
== last
|| *endptr
!= ';')
217 type
= strtoul(endptr
+ 1, &endptr
, 10);
218 if (endptr
== last
|| *endptr
!= ';') {
219 log_debug("%s: missing ;", __func__
);
222 r
= strtoul(endptr
+ 1, &endptr
, 10);
223 if (endptr
== last
|| *endptr
!= ';') {
224 log_debug("%s: missing ;", __func__
);
227 g
= strtoul(endptr
+ 1, &endptr
, 10);
228 if (endptr
== last
|| *endptr
!= ';') {
229 log_debug("%s: missing ;", __func__
);
232 b
= strtoul(endptr
+ 1, &endptr
, 10);
233 if (endptr
!= last
) {
234 log_debug("%s: missing ;", __func__
);
238 if (type
!= 1 && type
!= 2) {
239 log_debug("%s: invalid type %d", __func__
, type
);
242 if (c
+ 1 > si
->ncolours
) {
243 si
->colours
= xrecallocarray(si
->colours
, si
->ncolours
, c
+ 1,
244 sizeof *si
->colours
);
245 si
->ncolours
= c
+ 1;
247 si
->colours
[c
] = (type
<< 24) | (r
<< 16) | (g
<< 8) | b
;
252 sixel_parse_repeat(struct sixel_image
*si
, const char *cp
, const char *end
)
257 const char *errstr
= NULL
;
260 while (last
!= end
) {
261 if (*last
< '0' || *last
> '9')
264 if (n
== (sizeof tmp
) - 1) {
265 log_debug("%s: repeat not terminated", __func__
);
269 if (n
== 0 || last
== end
) {
270 log_debug("%s: repeat not terminated", __func__
);
275 n
= strtonum(tmp
, 1, SIXEL_WIDTH_LIMIT
, &errstr
);
276 if (n
== 0 || errstr
!= NULL
) {
277 log_debug("%s: repeat too wide", __func__
);
281 ch
= (*last
++) - 0x3f;
282 for (i
= 0; i
< n
; i
++) {
283 if (sixel_parse_write(si
, ch
) != 0) {
284 log_debug("%s: width limit reached", __func__
);
293 sixel_parse(const char *buf
, size_t len
, u_int p2
, u_int xpixel
, u_int ypixel
)
295 struct sixel_image
*si
;
296 const char *cp
= buf
, *end
= buf
+ len
;
299 if (len
== 0 || len
== 1 || *cp
++ != 'q') {
300 log_debug("%s: empty image", __func__
);
304 si
= xcalloc (1, sizeof *si
);
313 cp
= sixel_parse_attributes(si
, cp
, end
);
318 cp
= sixel_parse_colour(si
, cp
, end
);
323 cp
= sixel_parse_repeat(si
, cp
, end
);
337 if (ch
< 0x3f || ch
> 0x7e)
339 if (sixel_parse_write(si
, ch
- 0x3f) != 0) {
340 log_debug("%s: width limit reached", __func__
);
348 if (si
->x
== 0 || si
->y
== 0)
358 sixel_free(struct sixel_image
*si
)
362 for (y
= 0; y
< si
->y
; y
++)
363 free(si
->lines
[y
].data
);
371 sixel_log(struct sixel_image
*si
)
373 struct sixel_line
*sl
;
374 char s
[SIXEL_WIDTH_LIMIT
+ 1];
375 u_int i
, x
, y
, cx
, cy
;
377 sixel_size_in_cells(si
, &cx
, &cy
);
378 log_debug("%s: image %ux%u (%ux%u)", __func__
, si
->x
, si
->y
, cx
, cy
);
379 for (i
= 0; i
< si
->ncolours
; i
++)
380 log_debug("%s: colour %u is %07x", __func__
, i
, si
->colours
[i
]);
381 for (y
= 0; y
< si
->y
; y
++) {
383 for (x
= 0; x
< si
->x
; x
++) {
386 else if (sl
->data
[x
] != 0)
387 s
[x
] = '0' + (sl
->data
[x
] - 1) % 10;
392 log_debug("%s: %4u: %s", __func__
, y
, s
);
397 sixel_size_in_cells(struct sixel_image
*si
, u_int
*x
, u_int
*y
)
399 if ((si
->x
% si
->xpixel
) == 0)
400 *x
= (si
->x
/ si
->xpixel
);
402 *x
= 1 + (si
->x
/ si
->xpixel
);
403 if ((si
->y
% si
->ypixel
) == 0)
404 *y
= (si
->y
/ si
->ypixel
);
406 *y
= 1 + (si
->y
/ si
->ypixel
);
410 sixel_scale(struct sixel_image
*si
, u_int xpixel
, u_int ypixel
, u_int ox
,
411 u_int oy
, u_int sx
, u_int sy
, int colours
)
413 struct sixel_image
*new;
414 u_int cx
, cy
, pox
, poy
, psx
, psy
, tsx
, tsy
, px
, py
;
418 * We want to get the section of the image at ox,oy in image cells and
419 * map it onto the same size in terminal cells, remembering that we
420 * can only draw vertical sections of six pixels.
423 sixel_size_in_cells(si
, &cx
, &cy
);
438 pox
= ox
* si
->xpixel
;
439 poy
= oy
* si
->ypixel
;
440 psx
= sx
* si
->xpixel
;
441 psy
= sy
* si
->ypixel
;
444 tsy
= ((sy
* ypixel
) / 6) * 6;
446 new = xcalloc (1, sizeof *si
);
447 new->xpixel
= xpixel
;
448 new->ypixel
= ypixel
;
451 new->set_ra
= si
->set_ra
;
452 /* clamp to slice end */
453 new->ra_x
= si
->ra_x
< psx
? si
->ra_x
: psx
;
454 new->ra_y
= si
->ra_y
< psy
? si
->ra_y
: psy
;
455 /* subtract slice origin */
456 new->ra_x
= new->ra_x
> pox
? new->ra_x
- pox
: 0;
457 new->ra_y
= new->ra_y
> poy
? new->ra_y
- poy
: 0;
459 new->ra_x
= new->ra_x
* xpixel
/ si
->xpixel
;
460 new->ra_y
= new->ra_y
* ypixel
/ si
->ypixel
;
462 for (y
= 0; y
< tsy
; y
++) {
463 py
= poy
+ ((double)y
* psy
/ tsy
);
464 for (x
= 0; x
< tsx
; x
++) {
465 px
= pox
+ ((double)x
* psx
/ tsx
);
466 sixel_set_pixel(new, x
, y
, sixel_get_pixel(si
, px
, py
));
471 new->colours
= xmalloc(si
->ncolours
* sizeof *new->colours
);
472 for (i
= 0; i
< si
->ncolours
; i
++)
473 new->colours
[i
] = si
->colours
[i
];
474 new->ncolours
= si
->ncolours
;
480 sixel_print_add(char **buf
, size_t *len
, size_t *used
, const char *s
,
483 if (*used
+ slen
>= *len
+ 1) {
485 *buf
= xrealloc(*buf
, *len
);
487 memcpy(*buf
+ *used
, s
, slen
);
492 sixel_print_repeat(char **buf
, size_t *len
, size_t *used
, u_int count
, char ch
)
498 sixel_print_add(buf
, len
, used
, &ch
, 1);
499 else if (count
== 2) {
500 sixel_print_add(buf
, len
, used
, &ch
, 1);
501 sixel_print_add(buf
, len
, used
, &ch
, 1);
502 } else if (count
== 3) {
503 sixel_print_add(buf
, len
, used
, &ch
, 1);
504 sixel_print_add(buf
, len
, used
, &ch
, 1);
505 sixel_print_add(buf
, len
, used
, &ch
, 1);
506 } else if (count
!= 0) {
507 tmplen
= xsnprintf(tmp
, sizeof tmp
, "!%u%c", count
, ch
);
508 sixel_print_add(buf
, len
, used
, tmp
, tmplen
);
513 sixel_print_compress_colors(struct sixel_image
*si
, struct sixel_chunk
*chunks
,
514 u_int y
, u_int
*active
, u_int
*nactive
)
516 u_int i
, x
, c
, dx
, colors
[6];
517 struct sixel_chunk
*chunk
= NULL
;
518 struct sixel_line
*sl
;
520 for (x
= 0; x
< si
->x
; x
++) {
521 for (i
= 0; i
< 6; i
++) {
524 sl
= &si
->lines
[y
+ i
];
525 if (x
< sl
->x
&& sl
->data
[x
] != 0) {
526 colors
[i
] = sl
->data
[x
];
528 chunks
[c
].next_pattern
|= 1 << i
;
533 for (i
= 0; i
< 6; i
++) {
539 if (chunk
->next_x
== x
+ 1)
542 if (chunk
->next_y
< y
+ 1) {
543 chunk
->next_y
= y
+ 1;
544 active
[(*nactive
)++] = c
;
547 dx
= x
- chunk
->next_x
;
548 if (chunk
->pattern
!= chunk
->next_pattern
|| dx
!= 0) {
549 sixel_print_repeat(&chunk
->data
, &chunk
->len
,
550 &chunk
->used
, chunk
->count
,
551 chunk
->pattern
+ 0x3f);
552 sixel_print_repeat(&chunk
->data
, &chunk
->len
,
553 &chunk
->used
, dx
, '?');
554 chunk
->pattern
= chunk
->next_pattern
;
558 chunk
->next_pattern
= 0;
559 chunk
->next_x
= x
+ 1;
565 sixel_print(struct sixel_image
*si
, struct sixel_image
*map
, size_t *size
)
568 size_t len
, used
= 0, tmplen
;
569 u_int
*colours
, ncolours
, i
, c
, y
, *active
, nactive
;
570 struct sixel_chunk
*chunks
, *chunk
;
573 colours
= map
->colours
;
574 ncolours
= map
->ncolours
;
576 colours
= si
->colours
;
577 ncolours
= si
->ncolours
;
586 tmplen
= xsnprintf(tmp
, sizeof tmp
, "\033P0;%uq", si
->p2
);
587 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
590 tmplen
= xsnprintf(tmp
, sizeof tmp
, "\"1;1;%u;%u", si
->ra_x
,
592 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
595 chunks
= xcalloc(ncolours
, sizeof *chunks
);
596 active
= xcalloc(ncolours
, sizeof *active
);
598 for (i
= 0; i
< ncolours
; i
++) {
600 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u;%u;%u;%u;%u",
601 i
, c
>> 24, (c
>> 16) & 0xff, (c
>> 8) & 0xff, c
& 0xff);
602 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
606 chunk
->data
= xmalloc(chunk
->len
);
609 for (y
= 0; y
< si
->y
; y
+= 6) {
611 sixel_print_compress_colors(si
, chunks
, y
, active
, &nactive
);
613 for (i
= 0; i
< nactive
; i
++) {
616 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u", c
);
617 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
618 sixel_print_add(&buf
, &len
, &used
, chunk
->data
,
620 sixel_print_repeat(&buf
, &len
, &used
, chunk
->count
,
621 chunk
->pattern
+ 0x3f);
622 sixel_print_add(&buf
, &len
, &used
, "$", 1);
623 chunk
->used
= chunk
->next_x
= chunk
->count
= 0;
626 if (buf
[used
- 1] == '$')
628 sixel_print_add(&buf
, &len
, &used
, "-", 1);
630 if (buf
[used
- 1] == '-')
633 sixel_print_add(&buf
, &len
, &used
, "\033\\", 2);
639 for (i
= 0; i
< ncolours
; i
++)
640 free(chunks
[i
].data
);
648 sixel_to_screen(struct sixel_image
*si
)
651 struct screen_write_ctx ctx
;
655 sixel_size_in_cells(si
, &sx
, &sy
);
657 s
= xmalloc(sizeof *s
);
658 screen_init(s
, sx
, sy
, 0);
660 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
661 gc
.attr
|= (GRID_ATTR_CHARSET
|GRID_ATTR_DIM
);
662 utf8_set(&gc
.data
, '~');
664 screen_write_start(&ctx
, s
);
665 if (sx
== 1 || sy
== 1) {
666 for (y
= 0; y
< sy
; y
++) {
667 for (x
= 0; x
< sx
; x
++)
668 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
671 screen_write_box(&ctx
, sx
, sy
, BOX_LINES_DEFAULT
, NULL
, NULL
);
672 for (y
= 1; y
< sy
- 1; y
++) {
673 for (x
= 1; x
< sx
- 1; x
++)
674 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
677 screen_write_stop(&ctx
);