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