st-noxz

[fork] suckless st - personal fork
git clone https://noxz.tech/git/st-noxz.git
st-noxz

sixel.c
1// sixel.c (part of mintty)
2// originally written by kmiya@cluti (https://github.com/saitoha/sixel/blob/master/fromsixel.c)
3// Licensed under the terms of the GNU General Public License v3 or later.
4
5#include <stdlib.h>
6#include <string.h>  /* memcpy */
7
8#include "st.h"
9#include "win.h"
10#include "sixel.h"
11#include "sixel_hls.h"
12
13#define SIXEL_RGB(r, g, b) ((255 << 24) + ((r) << 16) + ((g) << 8) +  (b))
14#define SIXEL_PALVAL(n,a,m) (((n) * (a) + ((m) / 2)) / (m))
15#define SIXEL_XRGB(r,g,b) SIXEL_RGB(SIXEL_PALVAL(r, 255, 100), SIXEL_PALVAL(g, 255, 100), SIXEL_PALVAL(b, 255, 100))
16
17static sixel_color_t const sixel_default_color_table[] = {
18	SIXEL_XRGB( 0,  0,  0),  /*  0 Black    */
19	SIXEL_XRGB(20, 20, 80),  /*  1 Blue     */
20	SIXEL_XRGB(80, 13, 13),  /*  2 Red      */
21	SIXEL_XRGB(20, 80, 20),  /*  3 Green    */
22	SIXEL_XRGB(80, 20, 80),  /*  4 Magenta  */
23	SIXEL_XRGB(20, 80, 80),  /*  5 Cyan     */
24	SIXEL_XRGB(80, 80, 20),  /*  6 Yellow   */
25	SIXEL_XRGB(53, 53, 53),  /*  7 Gray 50% */
26	SIXEL_XRGB(26, 26, 26),  /*  8 Gray 25% */
27	SIXEL_XRGB(33, 33, 60),  /*  9 Blue*    */
28	SIXEL_XRGB(60, 26, 26),  /* 10 Red*     */
29	SIXEL_XRGB(33, 60, 33),  /* 11 Green*   */
30	SIXEL_XRGB(60, 33, 60),  /* 12 Magenta* */
31	SIXEL_XRGB(33, 60, 60),  /* 13 Cyan*    */
32	SIXEL_XRGB(60, 60, 33),  /* 14 Yellow*  */
33	SIXEL_XRGB(80, 80, 80),  /* 15 Gray 75% */
34};
35
36void
37scroll_images(int n) {
38	ImageList *im, *next;
39	#if SCROLLBACK_PATCH || REFLOW_PATCH
40	int top = tisaltscr() ? 0 : term.scr - HISTSIZE;
41	#else
42	int top = 0;
43	#endif // SCROLLBACK_PATCH
44
45	for (im = term.images; im; im = next) {
46		next = im->next;
47		im->y += n;
48
49		/* check if the current sixel has exceeded the maximum
50		 * draw distance, and should therefore be deleted */
51		if (im->y < top) {
52			//fprintf(stderr, "im@0x%08x exceeded maximum distance\n");
53			delete_image(im);
54		}
55	}
56}
57
58void
59delete_image(ImageList *im)
60{
61	if (im->prev)
62		im->prev->next = im->next;
63	else
64		term.images = im->next;
65	if (im->next)
66		im->next->prev = im->prev;
67	if (im->pixmap)
68		XFreePixmap(xw.dpy, (Drawable)im->pixmap);
69	if (im->clipmask)
70		XFreePixmap(xw.dpy, (Drawable)im->clipmask);
71	free(im->pixels);
72	free(im);
73}
74
75static int
76set_default_color(sixel_image_t *image)
77{
78	int i;
79	int n;
80	int r;
81	int g;
82	int b;
83
84	/* palette initialization */
85	for (n = 1; n < 17; n++) {
86		image->palette[n] = sixel_default_color_table[n - 1];
87	}
88
89	/* colors 17-232 are a 6x6x6 color cube */
90	for (r = 0; r < 6; r++) {
91		for (g = 0; g < 6; g++) {
92			for (b = 0; b < 6; b++) {
93				image->palette[n++] = SIXEL_RGB(r * 51, g * 51, b * 51);
94			}
95		}
96	}
97
98	/* colors 233-256 are a grayscale ramp, intentionally leaving out */
99	for (i = 0; i < 24; i++) {
100		image->palette[n++] = SIXEL_RGB(i * 11, i * 11, i * 11);
101	}
102
103	for (; n < DECSIXEL_PALETTE_MAX; n++) {
104		image->palette[n] = SIXEL_RGB(255, 255, 255);
105	}
106
107	return (0);
108}
109
110static int
111sixel_image_init(
112    sixel_image_t    *image,
113    int              width,
114    int              height,
115    int              fgcolor,
116    int              bgcolor,
117    int              use_private_register)
118{
119	int status = (-1);
120	size_t size;
121
122	size = (size_t)(width * height) * sizeof(sixel_color_no_t);
123	image->width = width;
124	image->height = height;
125	image->data = (sixel_color_no_t *)malloc(size);
126	image->ncolors = 2;
127	image->use_private_register = use_private_register;
128
129	if (image->data == NULL) {
130		status = (-1);
131		goto end;
132	}
133	memset(image->data, 0, size);
134
135	image->palette[0] = bgcolor;
136
137	if (image->use_private_register)
138		image->palette[1] = fgcolor;
139
140	image->palette_modified = 0;
141
142	status = (0);
143
144end:
145	return status;
146}
147
148
149static int
150image_buffer_resize(
151    sixel_image_t   *image,
152    int              width,
153    int              height)
154{
155	int status = (-1);
156	size_t size;
157	sixel_color_no_t *alt_buffer;
158	int n;
159	int min_height;
160
161	size = (size_t)(width * height) * sizeof(sixel_color_no_t);
162	alt_buffer = (sixel_color_no_t *)malloc(size);
163	if (alt_buffer == NULL) {
164		/* free source image */
165		free(image->data);
166		image->data = NULL;
167		status = (-1);
168		goto end;
169	}
170
171	min_height = height > image->height ? image->height: height;
172	if (width > image->width) {  /* if width is extended */
173		for (n = 0; n < min_height; ++n) {
174			/* copy from source image */
175			memcpy(alt_buffer + width * n,
176			       image->data + image->width * n,
177			       (size_t)image->width * sizeof(sixel_color_no_t));
178			/* fill extended area with background color */
179			memset(alt_buffer + width * n + image->width,
180			       0,
181			       (size_t)(width - image->width) * sizeof(sixel_color_no_t));
182		}
183	} else {
184		for (n = 0; n < min_height; ++n) {
185			/* copy from source image */
186			memcpy(alt_buffer + width * n,
187			       image->data + image->width * n,
188			       (size_t)width * sizeof(sixel_color_no_t));
189		}
190	}
191
192	if (height > image->height) {  /* if height is extended */
193		/* fill extended area with background color */
194		memset(alt_buffer + width * image->height,
195		       0,
196		       (size_t)(width * (height - image->height)) * sizeof(sixel_color_no_t));
197	}
198
199	/* free source image */
200	free(image->data);
201
202	image->data = alt_buffer;
203	image->width = width;
204	image->height = height;
205
206	status = (0);
207
208end:
209	return status;
210}
211
212static void
213sixel_image_deinit(sixel_image_t *image)
214{
215	if (image->data)
216		free(image->data);
217	image->data = NULL;
218}
219
220int
221sixel_parser_init(sixel_state_t *st,
222                  int transparent,
223                  sixel_color_t fgcolor, sixel_color_t bgcolor,
224                  unsigned char use_private_register,
225                  int cell_width, int cell_height)
226{
227	int status = (-1);
228
229	st->state = PS_DECSIXEL;
230	st->pos_x = 0;
231	st->pos_y = 0;
232	st->max_x = 0;
233	st->max_y = 0;
234	st->attributed_pan = 2;
235	st->attributed_pad = 1;
236	st->attributed_ph = 0;
237	st->attributed_pv = 0;
238	st->transparent = transparent;
239	st->repeat_count = 1;
240	st->color_index = 16;
241	st->grid_width = cell_width;
242	st->grid_height = cell_height;
243	st->nparams = 0;
244	st->param = 0;
245
246	/* buffer initialization */
247	status = sixel_image_init(&st->image, 1, 1, fgcolor, transparent ? 0 : bgcolor, use_private_register);
248
249	return status;
250}
251
252int
253sixel_parser_set_default_color(sixel_state_t *st)
254{
255	return set_default_color(&st->image);
256}
257
258int
259sixel_parser_finalize(sixel_state_t *st, ImageList **newimages, int cx, int cy, int cw, int ch)
260{
261	sixel_image_t *image = &st->image;
262	int x, y;
263	sixel_color_no_t *src;
264	sixel_color_t *dst;
265	int color;
266	int w, h;
267	int i, j, cols, numimages;
268	ImageList *im, *next, *tail;
269
270	if (!image->data)
271		return -1;
272
273	if (++st->max_x < st->attributed_ph)
274		st->max_x = st->attributed_ph;
275
276	if (++st->max_y < st->attributed_pv)
277		st->max_y = st->attributed_pv;
278
279	if (image->use_private_register && image->ncolors > 2 && !image->palette_modified) {
280		if (set_default_color(image) < 0)
281			return -1;
282	}
283
284	w = MIN(st->max_x, image->width);
285	h = MIN(st->max_y, image->height);
286
287	if ((numimages = (h + ch-1) / ch) <= 0)
288		return -1;
289
290	cols = (w + cw-1) / cw;
291
292	*newimages = NULL, tail = NULL;
293	for (y = 0, i = 0; i < numimages; i++) {
294		if ((im = malloc(sizeof(ImageList)))) {
295			if (!tail) {
296				*newimages = tail = im;
297				im->prev = im->next = NULL;
298			} else {
299				tail->next = im;
300				im->prev = tail;
301				im->next = NULL;
302				tail = im;
303			}
304			im->x = cx;
305			im->y = cy + i;
306			im->cols = cols;
307			im->width = w;
308			im->height = MIN(h - ch * i, ch);
309			im->pixels = malloc(im->width * im->height * 4);
310			im->pixmap = NULL;
311			im->clipmask = NULL;
312			im->cw = cw;
313			im->ch = ch;
314			im->transparent = st->transparent;
315		}
316		if (!im || !im->pixels) {
317			for (im = *newimages; im; im = next) {
318				next = im->next;
319				if (im->pixels)
320					free(im->pixels);
321				free(im);
322			}
323			*newimages = NULL;
324			return -1;
325		}
326		dst = (sixel_color_t *)im->pixels;
327		for (j = 0; j < im->height && y < h; j++, y++) {
328			src = st->image.data + image->width * y;
329			for (x = 0; x < w; x++)
330				*dst++ = st->image.palette[*src++];
331		}
332	}
333
334	return numimages;
335}
336
337/* convert sixel data into indexed pixel bytes and palette data */
338int
339sixel_parser_parse(sixel_state_t *st, const unsigned char *p, size_t len)
340{
341	int n = 0;
342	int i;
343	int x;
344	int y;
345	int bits;
346	int sx;
347	int sy;
348	int c;
349	int pos;
350	int width;
351	const unsigned char *p0 = p, *p2 = p + len;
352	sixel_image_t *image = &st->image;
353	sixel_color_no_t *data, color_index;
354
355	if (!image->data)
356		st->state = PS_ERROR;
357
358	while (p < p2) {
359		switch (st->state) {
360		case PS_ESC:
361			goto end;
362
363		case PS_DECSIXEL:
364			switch (*p) {
365			case '\x1b':
366				st->state = PS_ESC;
367				break;
368			case '"':
369				st->param = 0;
370				st->nparams = 0;
371				st->state = PS_DECGRA;
372				p++;
373				break;
374			case '!':
375				st->param = 0;
376				st->nparams = 0;
377				st->state = PS_DECGRI;
378				p++;
379				break;
380			case '#':
381				st->param = 0;
382				st->nparams = 0;
383				st->state = PS_DECGCI;
384				p++;
385				break;
386			case '$':
387				/* DECGCR Graphics Carriage Return */
388				st->pos_x = 0;
389				p++;
390				break;
391			case '-':
392				/* DECGNL Graphics Next Line */
393				st->pos_x = 0;
394				if (st->pos_y < DECSIXEL_HEIGHT_MAX - 5 - 6)
395					st->pos_y += 6;
396				else
397					st->pos_y = DECSIXEL_HEIGHT_MAX + 1;
398				p++;
399				break;
400			default:
401				if (*p >= '?' && *p <= '~') {  /* sixel characters */
402					if ((image->width < (st->pos_x + st->repeat_count) || image->height < (st->pos_y + 6))
403					        && image->width < DECSIXEL_WIDTH_MAX && image->height < DECSIXEL_HEIGHT_MAX) {
404						sx = image->width * 2;
405						sy = image->height * 2;
406						while (sx < (st->pos_x + st->repeat_count) || sy < (st->pos_y + 6)) {
407							sx *= 2;
408							sy *= 2;
409						}
410
411						sx = MIN(sx, DECSIXEL_WIDTH_MAX);
412						sy = MIN(sy, DECSIXEL_HEIGHT_MAX);
413
414						if (image_buffer_resize(image, sx, sy) < 0) {
415							perror("sixel_parser_parse() failed");
416							st->state = PS_ERROR;
417							p++;
418							break;
419						}
420					}
421
422					if (st->color_index > image->ncolors)
423						image->ncolors = st->color_index;
424
425					if (st->pos_x + st->repeat_count > image->width)
426						st->repeat_count = image->width - st->pos_x;
427
428					if (st->repeat_count > 0 && st->pos_y + 5 < image->height) {
429						bits = *p - '?';
430						if (bits != 0) {
431							data = image->data + image->width * st->pos_y + st->pos_x;
432							width = image->width;
433							color_index = st->color_index;
434							if (st->repeat_count <= 1) {
435								if (bits & 0x01)
436									*data = color_index, n = 0;
437								data += width;
438								if (bits & 0x02)
439									*data = color_index, n = 1;
440								data += width;
441								if (bits & 0x04)
442									*data = color_index, n = 2;
443								data += width;
444								if (bits & 0x08)
445									*data = color_index, n = 3;
446								data += width;
447								if (bits & 0x10)
448									*data = color_index, n = 4;
449								if (bits & 0x20)
450									data[width] = color_index, n = 5;
451								if (st->max_x < st->pos_x)
452									st->max_x = st->pos_x;
453							} else {
454								/* st->repeat_count > 1 */
455								for (i = 0; bits; bits >>= 1, i++, data += width) {
456									if (bits & 1) {
457										data[0] = color_index;
458										data[1] = color_index;
459										for (x = 2; x < st->repeat_count; x++)
460											data[x] = color_index;
461										n = i;
462									}
463								}
464								if (st->max_x < (st->pos_x + st->repeat_count - 1))
465									st->max_x = st->pos_x + st->repeat_count - 1;
466							}
467							if (st->max_y < (st->pos_y + n))
468								st->max_y = st->pos_y + n;
469						}
470					}
471					if (st->repeat_count > 0)
472						st->pos_x += st->repeat_count;
473					st->repeat_count = 1;
474				}
475				p++;
476				break;
477			}
478			break;
479
480		case PS_DECGRA:
481			/* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
482			switch (*p) {
483			case '\x1b':
484				st->state = PS_ESC;
485				break;
486			case '0':
487			case '1':
488			case '2':
489			case '3':
490			case '4':
491			case '5':
492			case '6':
493			case '7':
494			case '8':
495			case '9':
496				st->param = st->param * 10 + *p - '0';
497				st->param = MIN(st->param, DECSIXEL_PARAMVALUE_MAX);
498				p++;
499				break;
500			case ';':
501				if (st->nparams < DECSIXEL_PARAMS_MAX)
502					st->params[st->nparams++] = st->param;
503				st->param = 0;
504				p++;
505				break;
506			default:
507				if (st->nparams < DECSIXEL_PARAMS_MAX)
508					st->params[st->nparams++] = st->param;
509				if (st->nparams > 0)
510					st->attributed_pad = st->params[0];
511				if (st->nparams > 1)
512					st->attributed_pan = st->params[1];
513				if (st->nparams > 2 && st->params[2] > 0)
514					st->attributed_ph = st->params[2];
515				if (st->nparams > 3 && st->params[3] > 0)
516					st->attributed_pv = st->params[3];
517
518				if (st->attributed_pan <= 0)
519					st->attributed_pan = 1;
520				if (st->attributed_pad <= 0)
521					st->attributed_pad = 1;
522
523				if (image->width < st->attributed_ph ||
524				        image->height < st->attributed_pv) {
525					sx = MAX(image->width, st->attributed_ph);
526					sy = MAX(image->height, st->attributed_pv);
527
528					/* the height of the image buffer must be divisible by 6
529					 * to avoid unnecessary resizing of the image buffer when
530					 * parsing the last sixel line */
531					sy = (sy + 5) / 6 * 6;
532
533					sx = MIN(sx, DECSIXEL_WIDTH_MAX);
534					sy = MIN(sy, DECSIXEL_HEIGHT_MAX);
535
536					if (image_buffer_resize(image, sx, sy) < 0) {
537						perror("sixel_parser_parse() failed");
538						st->state = PS_ERROR;
539						break;
540					}
541				}
542				st->state = PS_DECSIXEL;
543				st->param = 0;
544				st->nparams = 0;
545			}
546			break;
547
548		case PS_DECGRI:
549			/* DECGRI Graphics Repeat Introducer ! Pn Ch */
550			switch (*p) {
551			case '\x1b':
552				st->state = PS_ESC;
553				break;
554			case '0':
555			case '1':
556			case '2':
557			case '3':
558			case '4':
559			case '5':
560			case '6':
561			case '7':
562			case '8':
563			case '9':
564				st->param = st->param * 10 + *p - '0';
565				st->param = MIN(st->param, DECSIXEL_PARAMVALUE_MAX);
566				p++;
567				break;
568			default:
569				st->repeat_count = MAX(st->param, 1);
570				st->state = PS_DECSIXEL;
571				st->param = 0;
572				st->nparams = 0;
573				break;
574			}
575			break;
576
577		case PS_DECGCI:
578			/* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
579			switch (*p) {
580			case '\x1b':
581				st->state = PS_ESC;
582				break;
583			case '0':
584			case '1':
585			case '2':
586			case '3':
587			case '4':
588			case '5':
589			case '6':
590			case '7':
591			case '8':
592			case '9':
593				st->param = st->param * 10 + *p - '0';
594				st->param = MIN(st->param, DECSIXEL_PARAMVALUE_MAX);
595				p++;
596				break;
597			case ';':
598				if (st->nparams < DECSIXEL_PARAMS_MAX)
599					st->params[st->nparams++] = st->param;
600				st->param = 0;
601				p++;
602				break;
603			default:
604				st->state = PS_DECSIXEL;
605				if (st->nparams < DECSIXEL_PARAMS_MAX)
606					st->params[st->nparams++] = st->param;
607				st->param = 0;
608
609				if (st->nparams > 0) {
610					st->color_index = 1 + st->params[0];  /* offset 1(background color) added */
611					if (st->color_index < 0)
612						st->color_index = 0;
613					else if (st->color_index >= DECSIXEL_PALETTE_MAX)
614						st->color_index = DECSIXEL_PALETTE_MAX - 1;
615				}
616
617				if (st->nparams > 4) {
618					st->image.palette_modified = 1;
619					if (st->params[1] == 1) {
620						/* HLS */
621						st->params[2] = MIN(st->params[2], 360);
622						st->params[3] = MIN(st->params[3], 100);
623						st->params[4] = MIN(st->params[4], 100);
624						image->palette[st->color_index]
625						    = hls_to_rgb(st->params[2], st->params[3], st->params[4]);
626					} else if (st->params[1] == 2) {
627						/* RGB */
628						st->params[2] = MIN(st->params[2], 100);
629						st->params[3] = MIN(st->params[3], 100);
630						st->params[4] = MIN(st->params[4], 100);
631						image->palette[st->color_index]
632						    = SIXEL_XRGB(st->params[2], st->params[3], st->params[4]);
633					}
634				}
635				break;
636			}
637			break;
638
639		case PS_ERROR:
640			if (*p == '\x1b') {
641				st->state = PS_ESC;
642				goto end;
643			}
644			p++;
645			break;
646		default:
647			break;
648		}
649	}
650
651end:
652	return p - p0;
653}
654
655void
656sixel_parser_deinit(sixel_state_t *st)
657{
658	if (st)
659		sixel_image_deinit(&st->image);
660}
661
662Pixmap
663sixel_create_clipmask(char *pixels, int width, int height)
664{
665	char c, *clipdata, *dst;
666	int b, i, n, y, w;
667	int msb = (XBitmapBitOrder(xw.dpy) == MSBFirst);
668	sixel_color_t *src = (sixel_color_t *)pixels;
669	Pixmap clipmask;
670
671	clipdata = dst = malloc((width+7)/8 * height);
672	if (!clipdata)
673		return (Pixmap)None;
674
675	for (y = 0; y < height; y++) {
676		for (w = width; w > 0; w -= n) {
677			n = MIN(w, 8);
678			if (msb) {
679				for (b = 0x80, c = 0, i = 0; i < n; i++, b >>= 1)
680					c |= (*src++) ? b : 0;
681			} else {
682				for (b = 0x01, c = 0, i = 0; i < n; i++, b <<= 1)
683					c |= (*src++) ? b : 0;
684			}
685			*dst++ = c;
686		}
687	}
688
689	clipmask = XCreateBitmapFromData(xw.dpy, xw.win, clipdata, width, height);
690	free(clipdata);
691	return clipmask;
692}