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
;
56 sixel_parse_expand_lines(struct sixel_image
*si
, u_int y
)
60 if (y
> SIXEL_HEIGHT_LIMIT
)
62 si
->lines
= xrecallocarray(si
->lines
, si
->y
, y
, sizeof *si
->lines
);
68 sixel_parse_expand_line(struct sixel_image
*si
, struct sixel_line
*sl
, u_int x
)
72 if (x
> SIXEL_WIDTH_LIMIT
)
76 sl
->data
= xrecallocarray(sl
->data
, sl
->x
, si
->x
, sizeof *sl
->data
);
82 sixel_get_pixel(struct sixel_image
*si
, u_int x
, u_int y
)
84 struct sixel_line
*sl
;
95 sixel_set_pixel(struct sixel_image
*si
, u_int x
, u_int y
, u_int c
)
97 struct sixel_line
*sl
;
99 if (sixel_parse_expand_lines(si
, y
+ 1) != 0)
102 if (sixel_parse_expand_line(si
, sl
, x
+ 1) != 0)
109 sixel_parse_write(struct sixel_image
*si
, u_int ch
)
111 struct sixel_line
*sl
;
114 if (sixel_parse_expand_lines(si
, si
->dy
+ 6) != 0)
116 sl
= &si
->lines
[si
->dy
];
118 for (i
= 0; i
< 6; i
++) {
119 if (sixel_parse_expand_line(si
, sl
, si
->dx
+ 1) != 0)
122 sl
->data
[si
->dx
] = si
->dc
;
129 sixel_parse_attributes(struct sixel_image
*si
, const char *cp
, const char *end
)
136 while (last
!= end
) {
137 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
141 strtoul(cp
, &endptr
, 10);
142 if (endptr
== last
|| *endptr
!= ';')
144 strtoul(endptr
+ 1, &endptr
, 10);
147 if (*endptr
!= ';') {
148 log_debug("%s: missing ;", __func__
);
152 x
= strtoul(endptr
+ 1, &endptr
, 10);
153 if (endptr
== last
|| *endptr
!= ';') {
154 log_debug("%s: missing ;", __func__
);
157 if (x
> SIXEL_WIDTH_LIMIT
) {
158 log_debug("%s: image is too wide", __func__
);
161 y
= strtoul(endptr
+ 1, &endptr
, 10);
162 if (endptr
!= last
) {
163 log_debug("%s: extra ;", __func__
);
166 if (y
> SIXEL_HEIGHT_LIMIT
) {
167 log_debug("%s: image is too tall", __func__
);
172 sixel_parse_expand_lines(si
, y
);
182 sixel_parse_colour(struct sixel_image
*si
, const char *cp
, const char *end
)
186 u_int c
, type
, r
, g
, b
;
189 while (last
!= end
) {
190 if (*last
!= ';' && (*last
< '0' || *last
> '9'))
195 c
= strtoul(cp
, &endptr
, 10);
196 if (c
> SIXEL_COLOUR_REGISTERS
) {
197 log_debug("%s: too many colours", __func__
);
201 if (endptr
== last
|| *endptr
!= ';')
204 type
= strtoul(endptr
+ 1, &endptr
, 10);
205 if (endptr
== last
|| *endptr
!= ';') {
206 log_debug("%s: missing ;", __func__
);
209 r
= strtoul(endptr
+ 1, &endptr
, 10);
210 if (endptr
== last
|| *endptr
!= ';') {
211 log_debug("%s: missing ;", __func__
);
214 g
= strtoul(endptr
+ 1, &endptr
, 10);
215 if (endptr
== last
|| *endptr
!= ';') {
216 log_debug("%s: missing ;", __func__
);
219 b
= strtoul(endptr
+ 1, &endptr
, 10);
220 if (endptr
!= last
) {
221 log_debug("%s: missing ;", __func__
);
225 if (type
!= 1 && type
!= 2) {
226 log_debug("%s: invalid type %d", __func__
, type
);
229 if (c
+ 1 > si
->ncolours
) {
230 si
->colours
= xrecallocarray(si
->colours
, si
->ncolours
, c
+ 1,
231 sizeof *si
->colours
);
232 si
->ncolours
= c
+ 1;
234 si
->colours
[c
] = (type
<< 24) | (r
<< 16) | (g
<< 8) | b
;
239 sixel_parse_repeat(struct sixel_image
*si
, const char *cp
, const char *end
)
244 const char *errstr
= NULL
;
247 while (last
!= end
) {
248 if (*last
< '0' || *last
> '9')
251 if (n
== (sizeof tmp
) - 1) {
252 log_debug("%s: repeat not terminated", __func__
);
256 if (n
== 0 || last
== end
) {
257 log_debug("%s: repeat not terminated", __func__
);
262 n
= strtonum(tmp
, 1, SIXEL_WIDTH_LIMIT
, &errstr
);
263 if (n
== 0 || errstr
!= NULL
) {
264 log_debug("%s: repeat too wide", __func__
);
268 ch
= (*last
++) - 0x3f;
269 for (i
= 0; i
< n
; i
++) {
270 if (sixel_parse_write(si
, ch
) != 0) {
271 log_debug("%s: width limit reached", __func__
);
280 sixel_parse(const char *buf
, size_t len
, u_int p2
, u_int xpixel
, u_int ypixel
)
282 struct sixel_image
*si
;
283 const char *cp
= buf
, *end
= buf
+ len
;
286 if (len
== 0 || len
== 1 || *cp
++ != 'q') {
287 log_debug("%s: empty image", __func__
);
291 si
= xcalloc (1, sizeof *si
);
300 cp
= sixel_parse_attributes(si
, cp
, end
);
305 cp
= sixel_parse_colour(si
, cp
, end
);
310 cp
= sixel_parse_repeat(si
, cp
, end
);
324 if (ch
< 0x3f || ch
> 0x7e)
326 if (sixel_parse_write(si
, ch
- 0x3f) != 0) {
327 log_debug("%s: width limit reached", __func__
);
335 if (si
->x
== 0 || si
->y
== 0)
345 sixel_free(struct sixel_image
*si
)
349 for (y
= 0; y
< si
->y
; y
++)
350 free(si
->lines
[y
].data
);
358 sixel_log(struct sixel_image
*si
)
360 struct sixel_line
*sl
;
361 char s
[SIXEL_WIDTH_LIMIT
+ 1];
362 u_int i
, x
, y
, cx
, cy
;
364 sixel_size_in_cells(si
, &cx
, &cy
);
365 log_debug("%s: image %ux%u (%ux%u)", __func__
, si
->x
, si
->y
, cx
, cy
);
366 for (i
= 0; i
< si
->ncolours
; i
++)
367 log_debug("%s: colour %u is %07x", __func__
, i
, si
->colours
[i
]);
368 for (y
= 0; y
< si
->y
; y
++) {
370 for (x
= 0; x
< si
->x
; x
++) {
373 else if (sl
->data
[x
] != 0)
374 s
[x
] = '0' + (sl
->data
[x
] - 1) % 10;
379 log_debug("%s: %4u: %s", __func__
, y
, s
);
384 sixel_size_in_cells(struct sixel_image
*si
, u_int
*x
, u_int
*y
)
386 if ((si
->x
% si
->xpixel
) == 0)
387 *x
= (si
->x
/ si
->xpixel
);
389 *x
= 1 + (si
->x
/ si
->xpixel
);
390 if ((si
->y
% si
->ypixel
) == 0)
391 *y
= (si
->y
/ si
->ypixel
);
393 *y
= 1 + (si
->y
/ si
->ypixel
);
397 sixel_scale(struct sixel_image
*si
, u_int xpixel
, u_int ypixel
, u_int ox
,
398 u_int oy
, u_int sx
, u_int sy
, int colours
)
400 struct sixel_image
*new;
401 u_int cx
, cy
, pox
, poy
, psx
, psy
, tsx
, tsy
, px
, py
;
405 * We want to get the section of the image at ox,oy in image cells and
406 * map it onto the same size in terminal cells, remembering that we
407 * can only draw vertical sections of six pixels.
410 sixel_size_in_cells(si
, &cx
, &cy
);
425 pox
= ox
* si
->xpixel
;
426 poy
= oy
* si
->ypixel
;
427 psx
= sx
* si
->xpixel
;
428 psy
= sy
* si
->ypixel
;
431 tsy
= ((sy
* ypixel
) / 6) * 6;
433 new = xcalloc (1, sizeof *si
);
434 new->xpixel
= xpixel
;
435 new->ypixel
= ypixel
;
438 new->set_ra
= si
->set_ra
;
439 /* clamp to slice end */
440 new->ra_x
= si
->ra_x
< psx
? si
->ra_x
: psx
;
441 new->ra_y
= si
->ra_y
< psy
? si
->ra_y
: psy
;
442 /* subtract slice origin */
443 new->ra_x
= new->ra_x
> pox
? new->ra_x
- pox
: 0;
444 new->ra_y
= new->ra_y
> poy
? new->ra_y
- poy
: 0;
446 new->ra_x
= new->ra_x
* xpixel
/ si
->xpixel
;
447 new->ra_y
= new->ra_y
* ypixel
/ si
->ypixel
;
449 for (y
= 0; y
< tsy
; y
++) {
450 py
= poy
+ ((double)y
* psy
/ tsy
);
451 for (x
= 0; x
< tsx
; x
++) {
452 px
= pox
+ ((double)x
* psx
/ tsx
);
453 sixel_set_pixel(new, x
, y
, sixel_get_pixel(si
, px
, py
));
458 new->colours
= xmalloc(si
->ncolours
* sizeof *new->colours
);
459 for (i
= 0; i
< si
->ncolours
; i
++)
460 new->colours
[i
] = si
->colours
[i
];
461 new->ncolours
= si
->ncolours
;
467 sixel_print_add(char **buf
, size_t *len
, size_t *used
, const char *s
,
470 if (*used
+ slen
>= *len
+ 1) {
472 *buf
= xrealloc(*buf
, *len
);
474 memcpy(*buf
+ *used
, s
, slen
);
479 sixel_print_repeat(char **buf
, size_t *len
, size_t *used
, u_int count
, char ch
)
485 sixel_print_add(buf
, len
, used
, &ch
, 1);
486 else if (count
== 2) {
487 sixel_print_add(buf
, len
, used
, &ch
, 1);
488 sixel_print_add(buf
, len
, used
, &ch
, 1);
489 } else if (count
== 3) {
490 sixel_print_add(buf
, len
, used
, &ch
, 1);
491 sixel_print_add(buf
, len
, used
, &ch
, 1);
492 sixel_print_add(buf
, len
, used
, &ch
, 1);
493 } else if (count
!= 0) {
494 tmplen
= xsnprintf(tmp
, sizeof tmp
, "!%u%c", count
, ch
);
495 sixel_print_add(buf
, len
, used
, tmp
, tmplen
);
500 sixel_print(struct sixel_image
*si
, struct sixel_image
*map
, size_t *size
)
502 char *buf
, tmp
[64], *contains
, data
, last
= 0;
503 size_t len
, used
= 0, tmplen
;
504 u_int
*colours
, ncolours
, i
, c
, x
, y
, count
;
505 struct sixel_line
*sl
;
508 colours
= map
->colours
;
509 ncolours
= map
->ncolours
;
511 colours
= si
->colours
;
512 ncolours
= si
->ncolours
;
517 contains
= xcalloc(1, ncolours
);
522 tmplen
= xsnprintf(tmp
, sizeof tmp
, "\033P0;%uq", si
->p2
);
523 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
526 tmplen
= xsnprintf(tmp
, sizeof tmp
, "\"1;1;%u;%u", si
->ra_x
,
528 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
531 for (i
= 0; i
< ncolours
; i
++) {
533 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u;%u;%u;%u;%u",
534 i
, c
>> 24, (c
>> 16) & 0xff, (c
>> 8) & 0xff, c
& 0xff);
535 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
538 for (y
= 0; y
< si
->y
; y
+= 6) {
539 memset(contains
, 0, ncolours
);
540 for (x
= 0; x
< si
->x
; x
++) {
541 for (i
= 0; i
< 6; i
++) {
544 sl
= &si
->lines
[y
+ i
];
545 if (x
< sl
->x
&& sl
->data
[x
] != 0)
546 contains
[sl
->data
[x
] - 1] = 1;
550 for (c
= 0; c
< ncolours
; c
++) {
553 tmplen
= xsnprintf(tmp
, sizeof tmp
, "#%u", c
);
554 sixel_print_add(&buf
, &len
, &used
, tmp
, tmplen
);
557 for (x
= 0; x
< si
->x
; x
++) {
559 for (i
= 0; i
< 6; i
++) {
562 sl
= &si
->lines
[y
+ i
];
563 if (x
< sl
->x
&& sl
->data
[x
] == c
+ 1)
568 sixel_print_repeat(&buf
, &len
, &used
,
575 sixel_print_repeat(&buf
, &len
, &used
, count
, data
);
576 sixel_print_add(&buf
, &len
, &used
, "$", 1);
579 if (buf
[used
- 1] == '$')
581 sixel_print_add(&buf
, &len
, &used
, "-", 1);
583 if (buf
[used
- 1] == '$' || buf
[used
- 1] == '-')
586 sixel_print_add(&buf
, &len
, &used
, "\033\\", 2);
597 sixel_to_screen(struct sixel_image
*si
)
600 struct screen_write_ctx ctx
;
604 sixel_size_in_cells(si
, &sx
, &sy
);
606 s
= xmalloc(sizeof *s
);
607 screen_init(s
, sx
, sy
, 0);
609 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
610 gc
.attr
|= (GRID_ATTR_CHARSET
|GRID_ATTR_DIM
);
611 utf8_set(&gc
.data
, '~');
613 screen_write_start(&ctx
, s
);
614 if (sx
== 1 || sy
== 1) {
615 for (y
= 0; y
< sy
; y
++) {
616 for (x
= 0; x
< sx
; x
++)
617 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
620 screen_write_box(&ctx
, sx
, sy
, BOX_LINES_DEFAULT
, NULL
, NULL
);
621 for (y
= 1; y
< sy
- 1; y
++) {
622 for (x
= 1; x
< sx
- 1; x
++)
623 grid_view_set_cell(s
->grid
, x
, y
, &gc
);
626 screen_write_stop(&ctx
);