10 /************************************************************************
12 ************************************************************************/
14 static const char base64_alphabet[] =
15 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
19 base64_uint8 (guint8 v, char *c)
21 c[0] = base64_alphabet[(v >> 0) & 0x3f];
22 c[1] = base64_alphabet[(v >> 6) & 0x3];
27 base64_uint16 (guint32 v, char *c)
29 c[0] = base64_alphabet[(v >> 0) & 0x3f];
30 c[1] = base64_alphabet[(v >> 6) & 0x3f];
31 c[2] = base64_alphabet[(v >> 12) & 0xf];
36 base64_uint24 (guint32 v, char *c)
38 c[0] = base64_alphabet[(v >> 0) & 0x3f];
39 c[1] = base64_alphabet[(v >> 6) & 0x3f];
40 c[2] = base64_alphabet[(v >> 12) & 0x3f];
41 c[3] = base64_alphabet[(v >> 18) & 0x3f];
46 base64_uint32 (guint32 v, char *c)
48 c[0] = base64_alphabet[(v >> 0) & 0x3f];
49 c[1] = base64_alphabet[(v >> 6) & 0x3f];
50 c[2] = base64_alphabet[(v >> 12) & 0x3f];
51 c[3] = base64_alphabet[(v >> 18) & 0x3f];
52 c[4] = base64_alphabet[(v >> 24) & 0x3f];
53 c[5] = base64_alphabet[(v >> 30) & 0x2];
56 /************************************************************************
57 * conversion of raw image data to uncompressed png data: uris *
58 ************************************************************************/
60 /* Table of CRCs of all 8-bit messages. */
61 static unsigned long crc_table[256];
63 /* Flag: has the table been computed? Initially false. */
64 static int crc_table_computed = 0;
66 /* Make the table for a fast CRC. */
73 for (n = 0; n < 256; n++) {
74 c = (unsigned long) n;
75 for (k = 0; k < 8; k++) {
77 c = 0xedb88320L ^ (c >> 1);
83 crc_table_computed = 1;
87 update_crc(unsigned long crc, unsigned char *buf, int len)
89 unsigned long c = crc;
92 if (!crc_table_computed)
94 for (n = 0; n < len; n++) {
95 c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
101 crc(unsigned char *buf, int len)
103 return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
106 #define BASE 65521 /* largest prime smaller than 65536 */
108 update_adler32(unsigned long adler, unsigned char *buf, int len)
110 unsigned long s1 = adler & 0xffff;
111 unsigned long s2 = (adler >> 16) & 0xffff;
114 for (n = 0; n < len; n++) {
115 s1 = (s1 + buf[n]) % BASE;
116 s2 = (s2 + s1) % BASE;
118 return (s2 << 16) + s1;
122 to_png_rgb (int w, int h, int byte_stride, guint32 *data)
124 guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
125 guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
126 /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
127 /* bpp: */ 8, /* color type: */ 2,
129 guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
130 guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
131 gsize data_size, row_size;
133 guint8 *png, *p, *p_row, *p_idat;
139 char *url, *url_base64;
140 int state = 0, outlen;
143 *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
144 *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
145 *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
147 row_size = 1 + w * 3;
149 row_header[1] = row_size & 0xff;
150 row_header[2] = (row_size >> 8) & 0xff;
151 row_header[3] = ~row_header[1];
152 row_header[4] = ~row_header[2];
155 data_size = 2 + (6 + w * 3) * h + 4;
157 *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
159 png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
160 png = g_malloc (png_size);
163 memcpy (p, header, sizeof(header));
165 memcpy (p, ihdr, sizeof(ihdr));
167 memcpy (p, idat_start, sizeof(idat_start));
168 p += sizeof(idat_start);
172 zlib header: 0x78, 0x01 ,
173 h * scanline: row_header[] + width * r,g,b
186 for (y = 0; y < h; y++) {
188 row_header[0] = 1; /* final block */
189 memcpy (p, row_header, sizeof(row_header));
190 p += sizeof(row_header);
193 data += byte_stride / 4;
194 for (x = 0; x < w; x++) {
196 *p++ = (pixel >> 16) & 0xff; /* red */
197 *p++ = (pixel >> 8) & 0xff; /* green */
198 *p++ = (pixel >> 0) & 0xff; /* blue */
200 adler = update_adler32(adler, p_row, p - p_row);
204 *(guint32 *)p = GUINT32_TO_BE(adler);
206 *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
209 memcpy (p, iend, sizeof(iend));
212 assert(p - png == png_size);
214 url = g_malloc (strlen("data:image/png;base64,") +
215 ((png_size / 3 + 1) * 4 + 4) + 1);
216 strcpy (url, "data:image/png;base64,");
218 url_base64 = url + strlen("data:image/png;base64,");
219 outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
220 outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
221 url_base64[outlen] = 0;
229 to_png_rgba (int w, int h, int byte_stride, guint32 *data)
231 guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
232 guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
233 /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
234 /* bpp: */ 8, /* color type: */ 6,
236 guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
237 guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
238 gsize data_size, row_size;
240 guint8 *png, *p, *p_row, *p_idat;
246 char *url, *url_base64;
247 int state = 0, outlen;
250 *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
251 *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
252 *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
254 row_size = 1 + w * 4;
256 row_header[1] = row_size & 0xff;
257 row_header[2] = (row_size >> 8) & 0xff;
258 row_header[3] = ~row_header[1];
259 row_header[4] = ~row_header[2];
262 data_size = 2 + (6 + w * 4) * h + 4;
264 *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
266 png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
267 png = g_malloc (png_size);
270 memcpy (p, header, sizeof(header));
272 memcpy (p, ihdr, sizeof(ihdr));
274 memcpy (p, idat_start, sizeof(idat_start));
275 p += sizeof(idat_start);
279 zlib header: 0x78, 0x01 ,
280 h * scanline: row_header[] + width * r,g,b,a
293 for (y = 0; y < h; y++) {
295 row_header[0] = 1; /* final block */
296 memcpy (p, row_header, sizeof(row_header));
297 p += sizeof(row_header);
300 data += byte_stride / 4;
301 for (x = 0; x < w; x++) {
303 *p++ = (pixel >> 16) & 0xff; /* red */
304 *p++ = (pixel >> 8) & 0xff; /* green */
305 *p++ = (pixel >> 0) & 0xff; /* blue */
306 *p++ = (pixel >> 24) & 0xff; /* alpha */
308 adler = update_adler32(adler, p_row, p - p_row);
312 *(guint32 *)p = GUINT32_TO_BE(adler);
314 *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
317 memcpy (p, iend, sizeof(iend));
320 assert(p - png == png_size);
322 url = g_malloc (strlen("data:image/png;base64,") +
323 ((png_size / 3 + 1) * 4 + 4) + 1);
324 strcpy (url, "data:image/png;base64,");
326 url_base64 = url + strlen("data:image/png;base64,");
327 outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
328 outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
329 url_base64[outlen] = 0;
338 to_png_a (int w, int h, int byte_stride, guint8 *data)
340 guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
341 guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
342 /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
343 /* bpp: */ 8, /* color type: */ 4,
345 guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
346 guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
347 gsize data_size, row_size;
349 guint8 *png, *p, *p_row, *p_idat;
355 char *url, *url_base64;
356 int state = 0, outlen;
359 *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
360 *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
361 *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
363 row_size = 1 + w * 2;
365 row_header[1] = row_size & 0xff;
366 row_header[2] = (row_size >> 8) & 0xff;
367 row_header[3] = ~row_header[1];
368 row_header[4] = ~row_header[2];
371 data_size = 2 + (6 + w * 2) * h + 4;
373 *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
375 png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
376 png = g_malloc (png_size);
379 memcpy (p, header, sizeof(header));
381 memcpy (p, ihdr, sizeof(ihdr));
383 memcpy (p, idat_start, sizeof(idat_start));
384 p += sizeof(idat_start);
388 zlib header: 0x78, 0x01 ,
389 h * scanline: row_header[] + width * r,g,b,a
402 for (y = 0; y < h; y++) {
404 row_header[0] = 1; /* final block */
405 memcpy (p, row_header, sizeof(row_header));
406 p += sizeof(row_header);
409 data += byte_stride / 4;
410 for (x = 0; x < w; x++) {
412 *p++ = 0x00; /* gray */
413 *p++ = pixel; /* alpha */
415 adler = update_adler32(adler, p_row, p - p_row);
419 *(guint32 *)p = GUINT32_TO_BE(adler);
421 *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
424 memcpy (p, iend, sizeof(iend));
427 assert(p - png == png_size);
429 url = g_malloc (strlen("data:image/png;base64,") +
430 ((png_size / 3 + 1) * 4 + 4) + 1);
431 strcpy (url, "data:image/png;base64,");
433 url_base64 = url + strlen("data:image/png;base64,");
434 outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
435 outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
436 url_base64[outlen] = 0;
444 /************************************************************************
445 * Basic I/O primitives *
446 ************************************************************************/
448 struct BroadwayOutput {
456 broadway_output_write_raw (BroadwayOutput *output,
457 const void *buf, gsize count)
461 const char *ptr = (const char *)buf;
468 res = write(output->fd, ptr, count);
472 if (errsave == EINTR)
474 output->error = TRUE;
479 output->error = TRUE;
488 broadway_output_write (BroadwayOutput *output,
489 const void *buf, gsize count)
492 const char *ptr = (const char *)buf;
499 res = gzwrite(output->zfd, ptr, count);
502 output->error = TRUE;
507 output->error = TRUE;
516 broadway_output_write_header (BroadwayOutput *output)
521 "HTTP/1.1 200 OK\r\n"
522 "Content-type: multipart/x-mixed-replace;boundary=x\r\n"
523 "Content-Encoding: gzip\r\n"
525 broadway_output_write_raw (output,
526 header, strlen (header));
530 send_boundary (BroadwayOutput *output)
536 broadway_output_write (output, boundary, strlen (boundary));
540 broadway_output_new(int fd, guint32 serial)
542 BroadwayOutput *output;
544 output = g_new0 (BroadwayOutput, 1);
547 output->serial = serial;
549 broadway_output_write_header (output);
551 output->zfd = gzdopen(fd, "wb");
553 /* Need an initial multipart boundary */
554 send_boundary (output);
560 broadway_output_free (BroadwayOutput *output)
563 gzclose (output->zfd);
570 broadway_output_get_next_serial (BroadwayOutput *output)
572 return output->serial;
576 broadway_output_flush (BroadwayOutput *output)
578 send_boundary (output);
579 gzflush (output->zfd, Z_SYNC_FLUSH);
580 return !output->error;
584 /************************************************************************
585 * Core rendering operations *
586 ************************************************************************/
588 #define HEADER_LEN (1+6)
591 append_uint16 (guint32 v, char *buf, int *p)
593 base64_uint16 (v, &buf[*p]);
598 append_uint32 (guint32 v, char *buf, int *p)
600 base64_uint32 (v, &buf[*p]);
605 write_header(BroadwayOutput *output, char *buf, char op)
611 append_uint32 (output->serial++, buf, &p);
617 broadway_output_copy_rectangles (BroadwayOutput *output, int id,
618 BroadwayRect *rects, int n_rects,
624 len = HEADER_LEN + 3 + 3 + 3*4*n_rects + 3 + 3;
626 buf = g_malloc (len);
627 p = write_header (output, buf, 'b');
628 append_uint16 (id, buf, &p);
629 append_uint16 (n_rects, buf, &p);
630 for (i = 0; i < n_rects; i++)
632 append_uint16 (rects[i].x, buf, &p);
633 append_uint16 (rects[i].y, buf, &p);
634 append_uint16 (rects[i].width, buf, &p);
635 append_uint16 (rects[i].height, buf, &p);
637 append_uint16 (dx, buf, &p);
638 append_uint16 (dy, buf, &p);
642 broadway_output_write (output, buf, len);
647 broadway_output_query_pointer (BroadwayOutput *output, int id)
649 char buf[HEADER_LEN + 3];
653 serial = output->serial;
654 p = write_header (output, buf, 'q');
655 append_uint16 (id, buf, &p);
657 assert (p == sizeof (buf));
659 broadway_output_write (output, buf, sizeof (buf));
665 broadway_output_new_surface(BroadwayOutput *output, int id, int x, int y, int w, int h)
667 char buf[HEADER_LEN + 15];
670 p = write_header (output, buf, 's');
671 append_uint16 (id, buf, &p);
672 append_uint16 (x, buf, &p);
673 append_uint16 (y, buf, &p);
674 append_uint16 (w, buf, &p);
675 append_uint16 (h, buf, &p);
677 assert (p == sizeof (buf));
679 broadway_output_write (output, buf, sizeof (buf));
683 broadway_output_show_surface(BroadwayOutput *output, int id)
685 char buf[HEADER_LEN + 3];
688 p = write_header (output, buf, 'S');
689 append_uint16 (id, buf, &p);
691 assert (p == sizeof (buf));
693 broadway_output_write (output, buf, sizeof (buf));
697 broadway_output_hide_surface(BroadwayOutput *output, int id)
699 char buf[HEADER_LEN + 3];
702 p = write_header (output, buf, 'H');
703 append_uint16 (id, buf, &p);
705 assert (p == sizeof (buf));
707 broadway_output_write (output, buf, sizeof (buf));
711 broadway_output_destroy_surface(BroadwayOutput *output, int id)
713 char buf[HEADER_LEN + 3];
716 p = write_header (output, buf, 'd');
717 append_uint16 (id, buf, &p);
719 assert (p == sizeof (buf));
721 broadway_output_write (output, buf, sizeof (buf));
725 broadway_output_move_surface(BroadwayOutput *output, int id, int x, int y)
727 char buf[HEADER_LEN + 9];
730 p = write_header (output, buf, 'm');
732 append_uint16 (id, buf, &p);
733 append_uint16 (x, buf, &p);
734 append_uint16 (y, buf, &p);
736 assert (p == sizeof (buf));
738 broadway_output_write (output, buf, sizeof (buf));
742 broadway_output_resize_surface(BroadwayOutput *output, int id, int w, int h)
744 char buf[HEADER_LEN + 9];
747 p = write_header (output, buf, 'r');
749 append_uint16 (id, buf, &p);
750 append_uint16 (w, buf, &p);
751 append_uint16 (h, buf, &p);
753 assert (p == sizeof (buf));
755 broadway_output_write (output, buf, sizeof (buf));
759 broadway_output_put_rgb (BroadwayOutput *output, int id, int x, int y,
760 int w, int h, int byte_stride, void *data)
762 char buf[HEADER_LEN + 15];
767 p = write_header (output, buf, 'i');
769 append_uint16 (id, buf, &p);
770 append_uint16 (x, buf, &p);
771 append_uint16 (y, buf, &p);
773 url = to_png_rgb (w, h, byte_stride, (guint32*)data);
775 append_uint32 (len, buf, &p);
777 assert (p == sizeof (buf));
779 broadway_output_write (output, buf, sizeof (buf));
781 broadway_output_write (output, url, len);
792 is_any_x_set (unsigned char *data,
793 int box_x1, int box_x2,
794 int x1, int x2, int y, int *x_set,
809 ptr = (guint32 *)(data + y * byte_stride + x1 * 4);
826 #define EXTEND_X_FUZZ 10
827 #define EXTEND_Y_FUZZ 10
830 extend_x_range (unsigned char *data,
831 int box_x1, int box_y1,
832 int box_x2, int box_y2,
833 int *x1, int *x2, int y,
839 while (is_any_x_set (data, box_x1, box_x2, *x1 - EXTEND_X_FUZZ, *x1, y, &new_x, byte_stride))
845 while (is_any_x_set (data, box_x1, box_x2, *x2, *x2 + EXTEND_X_FUZZ, y, &new_x, byte_stride))
855 extend_y_range (unsigned char *data,
856 int box_x1, int box_y1,
857 int box_x2, int box_y2,
858 int x1, int x2, int *y,
869 y2 = *y + EXTEND_Y_FUZZ;
873 for (yy = y2; yy > *y + 1; yy--)
875 if (is_any_x_set (data, box_x1, box_x2, x1, x2, yy - 1, NULL, byte_stride))
892 rgba_find_rects_extents (unsigned char *data,
893 int box_x1, int box_y1,
894 int box_x2, int box_y2,
899 int x1, x2, y1, y2, yy;
909 /* Expand maximally for all known rows */
914 for (yy = y1; yy < y2; yy++)
915 extended |= extend_x_range (data,
923 while (extend_y_range(data,
936 rgba_find_rects_sub (unsigned char *data,
937 int box_x1, int box_y1,
938 int box_x2, int box_y2,
941 int *n_rects, int *alloc_rects)
947 if (box_x1 == box_x2 || box_y1 == box_y2)
950 for (y = box_y1; y < box_y2; y++)
952 line = (guint32 *)(data + y * byte_stride + box_x1 * 4);
954 for (x = box_x1; x < box_x2; x++)
958 rgba_find_rects_extents (data,
959 box_x1, box_y1, box_x2, box_y2,
960 x, y, &rect, byte_stride);
961 if (*n_rects == *alloc_rects)
964 *rects = g_renew (BroadwayBox, *rects, *alloc_rects);
966 (*rects)[*n_rects] = rect;
968 rgba_find_rects_sub (data,
972 rects, n_rects, alloc_rects);
973 rgba_find_rects_sub (data,
977 rects, n_rects, alloc_rects);
978 rgba_find_rects_sub (data,
982 rects, n_rects, alloc_rects);
991 rgba_find_rects (unsigned char *data,
992 int w, int h, int byte_stride,
999 rects = g_new (BroadwayBox, alloc_rects);
1002 rgba_find_rects_sub (data,
1003 0, 0, w, h, byte_stride,
1004 &rects, n_rects, &alloc_rects);
1010 broadway_output_put_rgba (BroadwayOutput *output, int id, int x, int y,
1011 int w, int h, int byte_stride, void *data)
1013 char buf[HEADER_LEN + 15];
1020 rects = rgba_find_rects (data, w, h, byte_stride, &n_rects);
1022 for (i = 0; i < n_rects; i++)
1024 subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
1026 p = write_header (output, buf, 'i');
1027 append_uint16 (id, buf, &p);
1028 append_uint16 (x + rects[i].x1, buf, &p);
1029 append_uint16 (y + rects[i].y1, buf, &p);
1031 url = to_png_rgba (rects[i].x2 - rects[i].x1,
1032 rects[i].y2 - rects[i].y1,
1033 byte_stride, (guint32*)subdata);
1035 append_uint32 (len, buf, &p);
1037 assert (p == sizeof (buf));
1039 broadway_output_write (output, buf, sizeof (buf));
1041 broadway_output_write (output, url, len);
1051 send_image_a (BroadwayOutput *output, int id, int x, int y,
1052 int w, int h, int byte_stride, guint8 *data)
1054 char buf[HEADER_LEN + 15];
1058 p = write_header (output, buf, 'i');
1059 append_uint16 (id, buf, &p);
1060 append_uint16 (x, buf, &p);
1061 append_uint16 (y, buf, &p);
1063 url = to_png_a (w, h, byte_stride, data);
1065 append_uint32 (len, buf, &p);
1067 assert (p == sizeof (buf));
1069 broadway_output_write (output, buf, sizeof (buf));
1071 broadway_output_write (output, url, len);