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_COLOUR_REGISTERS 1024
27 #define SIXEL_WIDTH_LIMIT 10000
28 #define SIXEL_HEIGHT_LIMIT 10000
48 struct sixel_line
*lines
;
52 sixel_parse_expand_lines(struct sixel_image
*si
, u_int y
)
56 if (y
> SIXEL_HEIGHT_LIMIT
)
58 si
->lines
= xrecallocarray(si
->lines
, si
->y
, y
, sizeof *si
->lines
);
64 sixel_parse_expand_line(struct sixel_image
*si
, struct sixel_line
*sl
, u_int x
)
68 if (x
> SIXEL_WIDTH_LIMIT
)
72 sl
->data
= xrecallocarray(sl
->data
, sl
->x
, si
->x
, sizeof *sl
->data
);
78 sixel_get_pixel(struct sixel_image
*si
, u_int x
, u_int y
)
80 struct sixel_line
*sl
;
91 sixel_set_pixel(struct sixel_image
*si
, u_int x
, u_int y
, u_int c
)
93 struct sixel_line
*sl
;
95 if (sixel_parse_expand_lines(si
, y
+ 1) != 0)
98 if (sixel_parse_expand_line(si
, sl
, x
+ 1) != 0)
105 sixel_parse_write(struct sixel_image
*si
, u_int ch
)
107 struct sixel_line
*sl
;
110 if (sixel_parse_expand_lines(si
, si
->dy
+ 6) != 0)
112 sl
= &si
->lines
[si
->dy
];
114 for (i
= 0; i
< 6; i
++) {
115 if (sixel_parse_expand_line(si
, sl
, si
->dx
+ 1) != 0)
118 sl
->data
[si
->dx
] = si
->dc
;
125 sixel_parse_attributes(struct sixel_image
*si
, const char *cp
, const char *end
)
132 while (last
!= end
) {
133 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
137 strtoul(cp
, &endptr
, 10);
138 if (endptr
== last
|| *endptr
!= ';')
140 strtoul(endptr
+ 1, &endptr
, 10);
141 if (endptr
== last
|| *endptr
!= ';') {
142 log_debug("%s: missing ;", __func__
);
146 x
= strtoul(endptr
+ 1, &endptr
, 10);
147 if (endptr
== last
|| *endptr
!= ';') {
148 log_debug("%s: missing ;", __func__
);
151 if (x
> SIXEL_WIDTH_LIMIT
) {
152 log_debug("%s: image is too wide", __func__
);
155 y
= strtoul(endptr
+ 1, &endptr
, 10);
156 if (endptr
!= last
) {
157 log_debug("%s: extra ;", __func__
);
160 if (y
> SIXEL_HEIGHT_LIMIT
) {
161 log_debug("%s: image is too tall", __func__
);
166 sixel_parse_expand_lines(si
, y
);
172 sixel_parse_colour(struct sixel_image
*si
, const char *cp
, const char *end
)
176 u_int c
, type
, r
, g
, b
;
179 while (last
!= end
) {
180 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
185 c
= strtoul(cp
, &endptr
, 10);
186 if (c
> SIXEL_COLOUR_REGISTERS
) {
187 log_debug("%s: too many colours", __func__
);
191 if (endptr
== last
|| *endptr
!= ';')
194 type
= strtoul(endptr
+ 1, &endptr
, 10);
195 if (endptr
== last
|| *endptr
!= ';') {
196 log_debug("%s: missing ;", __func__
);
199 r
= strtoul(endptr
+ 1, &endptr
, 10);
200 if (endptr
== last
|| *endptr
!= ';') {
201 log_debug("%s: missing ;", __func__
);
204 g
= strtoul(endptr
+ 1, &endptr
, 10);
205 if (endptr
== last
|| *endptr
!= ';') {
206 log_debug("%s: missing ;", __func__
);
209 b
= strtoul(endptr
+ 1, &endptr
, 10);
210 if (endptr
!= last
) {
211 log_debug("%s: missing ;", __func__
);
215 if (type
!= 1 && type
!= 2) {
216 log_debug("%s: invalid type %d", __func__
, type
);
219 if (c
+ 1 > si
->ncolours
) {
220 si
->colours
= xrecallocarray(si
->colours
, si
->ncolours
, c
+ 1,
221 sizeof *si
->colours
);
222 si
->ncolours
= c
+ 1;
224 si
->colours
[c
] = (type
<< 24) | (r
<< 16) | (g
<< 8) | b
;
229 sixel_parse_repeat(struct sixel_image
*si
, const char *cp
, const char *end
)
234 const char *errstr
= NULL
;
237 while (last
!= end
) {
238 if (*last
< '0' || *last
> '9')
241 if (n
== (sizeof tmp
) - 1) {
242 log_debug("%s: repeat not terminated", __func__
);
246 if (n
== 0 || last
== end
) {
247 log_debug("%s: repeat not terminated", __func__
);
252 n
= strtonum(tmp
, 1, SIXEL_WIDTH_LIMIT
, &errstr
);
253 if (n
== 0 || errstr
!= NULL
) {
254 log_debug("%s: repeat too wide", __func__
);
258 ch
= (*last
++) - 0x3f;
259 for (i
= 0; i
< n
; i
++) {
260 if (sixel_parse_write(si
, ch
) != 0) {
261 log_debug("%s: width limit reached", __func__
);
270 sixel_parse(const char *buf
, size_t len
, u_int xpixel
, u_int ypixel
)
272 struct sixel_image
*si
;
273 const char *cp
= buf
, *end
= buf
+ len
;
276 if (len
== 0 || len
== 1 || *cp
++ != 'q') {
277 log_debug("%s: empty image", __func__
);
281 si
= xcalloc (1, sizeof *si
);
289 cp
= sixel_parse_attributes(si
, cp
, end
);
294 cp
= sixel_parse_colour(si
, cp
, end
);
299 cp
= sixel_parse_repeat(si
, cp
, end
);
313 if (ch
< 0x3f || ch
> 0x7e)
315 if (sixel_parse_write(si
, ch
- 0x3f) != 0) {
316 log_debug("%s: width limit reached", __func__
);
324 if (si
->x
== 0 || si
->y
== 0)
334 sixel_free(struct sixel_image
*si
)
338 for (y
= 0; y
< si
->y
; y
++)
339 free(si
->lines
[y
].data
);
347 sixel_log(struct sixel_image
*si
)
349 struct sixel_line
*sl
;
350 char s
[SIXEL_WIDTH_LIMIT
+ 1];
351 u_int i
, x
, y
, cx
, cy
;
353 sixel_size_in_cells(si
, &cx
, &cy
);
354 log_debug("%s: image %ux%u (%ux%u)", __func__
, si
->x
, si
->y
, cx
, cy
);
355 for (i
= 0; i
< si
->ncolours
; i
++)
356 log_debug("%s: colour %u is %07x", __func__
, i
, si
->colours
[i
]);
357 for (y
= 0; y
< si
->y
; y
++) {
359 for (x
= 0; x
< si
->x
; x
++) {
362 else if (sl
->data
[x
] != 0)
363 s
[x
] = '0' + (sl
->data
[x
] - 1) % 10;
368 log_debug("%s: %4u: %s", __func__
, y
, s
);
373 sixel_size_in_cells(struct sixel_image
*si
, u_int
*x
, u_int
*y
)
375 if ((si
->x
% si
->xpixel
) == 0)
376 *x
= (si
->x
/ si
->xpixel
);
378 *x
= 1 + (si
->x
/ si
->xpixel
);
379 if ((si
->y
% si
->ypixel
) == 0)
380 *y
= (si
->y
/ si
->ypixel
);
382 *y
= 1 + (si
->y
/ si
->ypixel
);
386 sixel_scale(struct sixel_image
*si
, u_int xpixel
, u_int ypixel
, u_int ox
,
387 u_int oy
, u_int sx
, u_int sy
, int colours
)
389 struct sixel_image
*new;
390 u_int cx
, cy
, pox
, poy
, psx
, psy
, tsx
, tsy
, px
, py
;
394 * We want to get the section of the image at ox,oy in image cells and
395 * map it onto the same size in terminal cells, remembering that we
396 * can only draw vertical sections of six pixels.
399 sixel_size_in_cells(si
, &cx
, &cy
);
414 pox
= ox
* si
->xpixel
;
415 poy
= oy
* si
->ypixel
;
416 psx
= sx
* si
->xpixel
;
417 psy
= sy
* si
->ypixel
;
420 tsy
= ((sy
* ypixel
) / 6) * 6;
422 new = xcalloc (1, sizeof *si
);
423 new->xpixel
= xpixel
;
424 new->ypixel
= ypixel
;
426 for (y
= 0; y
< tsy
; y
++) {
427 py
= poy
+ ((double)y
* psy
/ tsy
);
428 for (x
= 0; x
< tsx
; x
++) {
429 px
= pox
+ ((double)x
* psx
/ tsx
);
430 sixel_set_pixel(new, x
, y
, sixel_get_pixel(si
, px
, py
));
435 new->colours
= xmalloc(si
->ncolours
* sizeof *new->colours
);
436 for (i
= 0; i
< si
->ncolours
; i
++)
437 new->colours
[i
] = si
->colours
[i
];
438 new->ncolours
= si
->ncolours
;
444 sixel_print_add(char **buf
, size_t *len
, size_t *used
, const char *s
,
447 if (*used
+ slen
>= *len
+ 1) {
449 *buf
= xrealloc(*buf
, *len
);
451 memcpy(*buf
+ *used
, s
, slen
);
456 sixel_print_repeat(char **buf
, size_t *len
, size_t *used
, u_int count
, char ch
)
462 sixel_print_add(buf
, len
, used
, &ch
, 1);
463 else if (count
== 2) {
464 sixel_print_add(buf
, len
, used
, &ch
, 1);
465 sixel_print_add(buf
, len
, used
, &ch
, 1);
466 } else if (count
== 3) {
467 sixel_print_add(buf
, len
, used
, &ch
, 1);
468 sixel_print_add(buf
, len
, used
, &ch
, 1);
469 sixel_print_add(buf
, len
, used
, &ch
, 1);
470 } else if (count
!= 0) {
471 tmplen
= xsnprintf(tmp
, sizeof tmp
, "!%u%c", count
, ch
);
472 sixel_print_add(buf
, len
, used
, tmp
, tmplen
);
477 sixel_print(struct sixel_image
*si
, struct sixel_image
*map
, size_t *size
)
479 char *buf
, tmp
[64], *contains
, data
, last
= 0;
480 size_t len
, used
= 0, tmplen
;
481 u_int
*colours
, ncolours
, i
, c
, x
, y
, count
;
482 struct sixel_line
*sl
;
485 colours
= map
->colours
;
486 ncolours
= map
->ncolours
;
488 colours
= si
->colours
;
489 ncolours
= si
->ncolours
;
491 contains
= xcalloc(1, ncolours
);
496 sixel_print_add(&buf
, &len
, &used
, "\033Pq", 3);
498 tmplen
= xsnprintf(tmp
, sizeof tmp
, "\"1;1;%u;%u", si
->x
, si
->y
);
499 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
501 for (i
= 0; i
< ncolours
; i
++) {
503 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u;%u;%u;%u;%u",
504 i
, c
>> 24, (c
>> 16) & 0xff, (c
>> 8) & 0xff, c
& 0xff);
505 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
508 for (y
= 0; y
< si
->y
; y
+= 6) {
509 memset(contains
, 0, ncolours
);
510 for (x
= 0; x
< si
->x
; x
++) {
511 for (i
= 0; i
< 6; i
++) {
514 sl
= &si
->lines
[y
+ i
];
515 if (x
< sl
->x
&& sl
->data
[x
] != 0)
516 contains
[sl
->data
[x
] - 1] = 1;
520 for (c
= 0; c
< ncolours
; c
++) {
523 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u", c
);
524 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
527 for (x
= 0; x
< si
->x
; x
++) {
529 for (i
= 0; i
< 6; i
++) {
532 sl
= &si
->lines
[y
+ i
];
533 if (x
< sl
->x
&& sl
->data
[x
] == c
+ 1)
538 sixel_print_repeat(&buf
, &len
, &used
,
545 sixel_print_repeat(&buf
, &len
, &used
, count
, data
);
546 sixel_print_add(&buf
, &len
, &used
, "$", 1);
549 if (buf
[used
- 1] == '$')
551 if (buf
[used
- 1] != '-')
552 sixel_print_add(&buf
, &len
, &used
, "-", 1);
554 if (buf
[used
- 1] == '$' || buf
[used
- 1] == '-')
557 sixel_print_add(&buf
, &len
, &used
, "\033\\", 2);
568 sixel_to_screen(struct sixel_image
*si
)
571 struct screen_write_ctx ctx
;
575 sixel_size_in_cells(si
, &sx
, &sy
);
577 s
= xmalloc(sizeof *s
);
578 screen_init(s
, sx
, sy
, 0);
580 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
581 gc
.attr
|= (GRID_ATTR_CHARSET
|GRID_ATTR_DIM
);
582 utf8_set(&gc
.data
, '~');
584 screen_write_start(&ctx
, s
);
585 if (sx
== 1 || sy
== 1) {
586 for (y
= 0; y
< sy
; y
++) {
587 for (x
= 0; x
< sx
; x
++)
588 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
591 screen_write_box(&ctx
, sx
, sy
, BOX_LINES_DEFAULT
, NULL
, NULL
);
592 for (y
= 1; y
< sy
- 1; y
++) {
593 for (x
= 1; x
< sx
- 1; x
++)
594 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
597 screen_write_stop(&ctx
);