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
47 struct sixel_line
*lines
;
51 sixel_parse_expand_lines(struct sixel_image
*si
, u_int y
)
55 if (y
> SIXEL_HEIGHT_LIMIT
)
57 si
->lines
= xrecallocarray(si
->lines
, si
->y
, y
, sizeof *si
->lines
);
63 sixel_parse_expand_line(struct sixel_image
*si
, struct sixel_line
*sl
, u_int x
)
67 if (x
> SIXEL_WIDTH_LIMIT
)
71 sl
->data
= xrecallocarray(sl
->data
, sl
->x
, si
->x
, sizeof *sl
->data
);
77 sixel_get_pixel(struct sixel_image
*si
, u_int x
, u_int y
)
79 struct sixel_line
*sl
;
90 sixel_set_pixel(struct sixel_image
*si
, u_int x
, u_int y
, u_int c
)
92 struct sixel_line
*sl
;
94 if (sixel_parse_expand_lines(si
, y
+ 1) != 0)
97 if (sixel_parse_expand_line(si
, sl
, x
+ 1) != 0)
104 sixel_parse_write(struct sixel_image
*si
, u_int ch
)
106 struct sixel_line
*sl
;
109 if (sixel_parse_expand_lines(si
, si
->dy
+ 6) != 0)
111 sl
= &si
->lines
[si
->dy
];
113 for (i
= 0; i
< 6; i
++) {
114 if (sixel_parse_expand_line(si
, sl
, si
->dx
+ 1) != 0)
117 sl
->data
[si
->dx
] = si
->dc
;
124 sixel_parse_attributes(struct sixel_image
*si
, const char *cp
, const char *end
)
131 while (last
!= end
) {
132 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
136 strtoul(cp
, &endptr
, 10);
137 if (endptr
== last
|| *endptr
!= ';')
139 strtoul(endptr
+ 1, &endptr
, 10);
142 if (*endptr
!= ';') {
143 log_debug("%s: missing ;", __func__
);
147 x
= strtoul(endptr
+ 1, &endptr
, 10);
148 if (endptr
== last
|| *endptr
!= ';') {
149 log_debug("%s: missing ;", __func__
);
152 if (x
> SIXEL_WIDTH_LIMIT
) {
153 log_debug("%s: image is too wide", __func__
);
156 y
= strtoul(endptr
+ 1, &endptr
, 10);
157 if (endptr
!= last
) {
158 log_debug("%s: extra ;", __func__
);
161 if (y
> SIXEL_HEIGHT_LIMIT
) {
162 log_debug("%s: image is too tall", __func__
);
167 sixel_parse_expand_lines(si
, y
);
173 sixel_parse_colour(struct sixel_image
*si
, const char *cp
, const char *end
)
177 u_int c
, type
, r
, g
, b
;
180 while (last
!= end
) {
181 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
186 c
= strtoul(cp
, &endptr
, 10);
187 if (c
> SIXEL_COLOUR_REGISTERS
) {
188 log_debug("%s: too many colours", __func__
);
192 if (endptr
== last
|| *endptr
!= ';')
195 type
= strtoul(endptr
+ 1, &endptr
, 10);
196 if (endptr
== last
|| *endptr
!= ';') {
197 log_debug("%s: missing ;", __func__
);
200 r
= strtoul(endptr
+ 1, &endptr
, 10);
201 if (endptr
== last
|| *endptr
!= ';') {
202 log_debug("%s: missing ;", __func__
);
205 g
= strtoul(endptr
+ 1, &endptr
, 10);
206 if (endptr
== last
|| *endptr
!= ';') {
207 log_debug("%s: missing ;", __func__
);
210 b
= strtoul(endptr
+ 1, &endptr
, 10);
211 if (endptr
!= last
) {
212 log_debug("%s: missing ;", __func__
);
216 if (type
!= 1 && type
!= 2) {
217 log_debug("%s: invalid type %d", __func__
, type
);
220 if (c
+ 1 > si
->ncolours
) {
221 si
->colours
= xrecallocarray(si
->colours
, si
->ncolours
, c
+ 1,
222 sizeof *si
->colours
);
223 si
->ncolours
= c
+ 1;
225 si
->colours
[c
] = (type
<< 24) | (r
<< 16) | (g
<< 8) | b
;
230 sixel_parse_repeat(struct sixel_image
*si
, const char *cp
, const char *end
)
235 const char *errstr
= NULL
;
238 while (last
!= end
) {
239 if (*last
< '0' || *last
> '9')
242 if (n
== (sizeof tmp
) - 1) {
243 log_debug("%s: repeat not terminated", __func__
);
247 if (n
== 0 || last
== end
) {
248 log_debug("%s: repeat not terminated", __func__
);
253 n
= strtonum(tmp
, 1, SIXEL_WIDTH_LIMIT
, &errstr
);
254 if (n
== 0 || errstr
!= NULL
) {
255 log_debug("%s: repeat too wide", __func__
);
259 ch
= (*last
++) - 0x3f;
260 for (i
= 0; i
< n
; i
++) {
261 if (sixel_parse_write(si
, ch
) != 0) {
262 log_debug("%s: width limit reached", __func__
);
271 sixel_parse(const char *buf
, size_t len
, u_int xpixel
, u_int ypixel
)
273 struct sixel_image
*si
;
274 const char *cp
= buf
, *end
= buf
+ len
;
277 if (len
== 0 || len
== 1 || *cp
++ != 'q') {
278 log_debug("%s: empty image", __func__
);
282 si
= xcalloc (1, sizeof *si
);
290 cp
= sixel_parse_attributes(si
, cp
, end
);
295 cp
= sixel_parse_colour(si
, cp
, end
);
300 cp
= sixel_parse_repeat(si
, cp
, end
);
314 if (ch
< 0x3f || ch
> 0x7e)
316 if (sixel_parse_write(si
, ch
- 0x3f) != 0) {
317 log_debug("%s: width limit reached", __func__
);
325 if (si
->x
== 0 || si
->y
== 0)
335 sixel_free(struct sixel_image
*si
)
339 for (y
= 0; y
< si
->y
; y
++)
340 free(si
->lines
[y
].data
);
348 sixel_log(struct sixel_image
*si
)
350 struct sixel_line
*sl
;
351 char s
[SIXEL_WIDTH_LIMIT
+ 1];
352 u_int i
, x
, y
, cx
, cy
;
354 sixel_size_in_cells(si
, &cx
, &cy
);
355 log_debug("%s: image %ux%u (%ux%u)", __func__
, si
->x
, si
->y
, cx
, cy
);
356 for (i
= 0; i
< si
->ncolours
; i
++)
357 log_debug("%s: colour %u is %07x", __func__
, i
, si
->colours
[i
]);
358 for (y
= 0; y
< si
->y
; y
++) {
360 for (x
= 0; x
< si
->x
; x
++) {
363 else if (sl
->data
[x
] != 0)
364 s
[x
] = '0' + (sl
->data
[x
] - 1) % 10;
369 log_debug("%s: %4u: %s", __func__
, y
, s
);
374 sixel_size_in_cells(struct sixel_image
*si
, u_int
*x
, u_int
*y
)
376 if ((si
->x
% si
->xpixel
) == 0)
377 *x
= (si
->x
/ si
->xpixel
);
379 *x
= 1 + (si
->x
/ si
->xpixel
);
380 if ((si
->y
% si
->ypixel
) == 0)
381 *y
= (si
->y
/ si
->ypixel
);
383 *y
= 1 + (si
->y
/ si
->ypixel
);
387 sixel_scale(struct sixel_image
*si
, u_int xpixel
, u_int ypixel
, u_int ox
,
388 u_int oy
, u_int sx
, u_int sy
, int colours
)
390 struct sixel_image
*new;
391 u_int cx
, cy
, pox
, poy
, psx
, psy
, tsx
, tsy
, px
, py
;
395 * We want to get the section of the image at ox,oy in image cells and
396 * map it onto the same size in terminal cells, remembering that we
397 * can only draw vertical sections of six pixels.
400 sixel_size_in_cells(si
, &cx
, &cy
);
415 pox
= ox
* si
->xpixel
;
416 poy
= oy
* si
->ypixel
;
417 psx
= sx
* si
->xpixel
;
418 psy
= sy
* si
->ypixel
;
421 tsy
= ((sy
* ypixel
) / 6) * 6;
423 new = xcalloc (1, sizeof *si
);
424 new->xpixel
= xpixel
;
425 new->ypixel
= ypixel
;
427 for (y
= 0; y
< tsy
; y
++) {
428 py
= poy
+ ((double)y
* psy
/ tsy
);
429 for (x
= 0; x
< tsx
; x
++) {
430 px
= pox
+ ((double)x
* psx
/ tsx
);
431 sixel_set_pixel(new, x
, y
, sixel_get_pixel(si
, px
, py
));
436 new->colours
= xmalloc(si
->ncolours
* sizeof *new->colours
);
437 for (i
= 0; i
< si
->ncolours
; i
++)
438 new->colours
[i
] = si
->colours
[i
];
439 new->ncolours
= si
->ncolours
;
445 sixel_print_add(char **buf
, size_t *len
, size_t *used
, const char *s
,
448 if (*used
+ slen
>= *len
+ 1) {
450 *buf
= xrealloc(*buf
, *len
);
452 memcpy(*buf
+ *used
, s
, slen
);
457 sixel_print_repeat(char **buf
, size_t *len
, size_t *used
, u_int count
, char ch
)
463 sixel_print_add(buf
, len
, used
, &ch
, 1);
464 else if (count
== 2) {
465 sixel_print_add(buf
, len
, used
, &ch
, 1);
466 sixel_print_add(buf
, len
, used
, &ch
, 1);
467 } else if (count
== 3) {
468 sixel_print_add(buf
, len
, used
, &ch
, 1);
469 sixel_print_add(buf
, len
, used
, &ch
, 1);
470 sixel_print_add(buf
, len
, used
, &ch
, 1);
471 } else if (count
!= 0) {
472 tmplen
= xsnprintf(tmp
, sizeof tmp
, "!%u%c", count
, ch
);
473 sixel_print_add(buf
, len
, used
, tmp
, tmplen
);
478 sixel_print(struct sixel_image
*si
, struct sixel_image
*map
, size_t *size
)
480 char *buf
, tmp
[64], *contains
, data
, last
= 0;
481 size_t len
, used
= 0, tmplen
;
482 u_int
*colours
, ncolours
, i
, c
, x
, y
, count
;
483 struct sixel_line
*sl
;
486 colours
= map
->colours
;
487 ncolours
= map
->ncolours
;
489 colours
= si
->colours
;
490 ncolours
= si
->ncolours
;
495 contains
= xcalloc(1, ncolours
);
500 sixel_print_add(&buf
, &len
, &used
, "\033Pq", 3);
502 tmplen
= xsnprintf(tmp
, sizeof tmp
, "\"1;1;%u;%u", si
->x
, si
->y
);
503 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
505 for (i
= 0; i
< ncolours
; i
++) {
507 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u;%u;%u;%u;%u",
508 i
, c
>> 24, (c
>> 16) & 0xff, (c
>> 8) & 0xff, c
& 0xff);
509 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
512 for (y
= 0; y
< si
->y
; y
+= 6) {
513 memset(contains
, 0, ncolours
);
514 for (x
= 0; x
< si
->x
; x
++) {
515 for (i
= 0; i
< 6; i
++) {
518 sl
= &si
->lines
[y
+ i
];
519 if (x
< sl
->x
&& sl
->data
[x
] != 0)
520 contains
[sl
->data
[x
] - 1] = 1;
524 for (c
= 0; c
< ncolours
; c
++) {
527 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u", c
);
528 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
531 for (x
= 0; x
< si
->x
; x
++) {
533 for (i
= 0; i
< 6; i
++) {
536 sl
= &si
->lines
[y
+ i
];
537 if (x
< sl
->x
&& sl
->data
[x
] == c
+ 1)
542 sixel_print_repeat(&buf
, &len
, &used
,
549 sixel_print_repeat(&buf
, &len
, &used
, count
, data
);
550 sixel_print_add(&buf
, &len
, &used
, "$", 1);
553 if (buf
[used
- 1] == '$')
555 if (buf
[used
- 1] != '-')
556 sixel_print_add(&buf
, &len
, &used
, "-", 1);
558 if (buf
[used
- 1] == '$' || buf
[used
- 1] == '-')
561 sixel_print_add(&buf
, &len
, &used
, "\033\\", 2);
572 sixel_to_screen(struct sixel_image
*si
)
575 struct screen_write_ctx ctx
;
579 sixel_size_in_cells(si
, &sx
, &sy
);
581 s
= xmalloc(sizeof *s
);
582 screen_init(s
, sx
, sy
, 0);
584 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
585 gc
.attr
|= (GRID_ATTR_CHARSET
|GRID_ATTR_DIM
);
586 utf8_set(&gc
.data
, '~');
588 screen_write_start(&ctx
, s
);
589 if (sx
== 1 || sy
== 1) {
590 for (y
= 0; y
< sy
; y
++) {
591 for (x
= 0; x
< sx
; x
++)
592 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
595 screen_write_box(&ctx
, sx
, sy
, BOX_LINES_DEFAULT
, NULL
, NULL
);
596 for (y
= 1; y
< sy
- 1; y
++) {
597 for (x
= 1; x
< sx
- 1; x
++)
598 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
601 screen_write_stop(&ctx
);