11 /************************************************************************
13 ************************************************************************/
15 static const char base64_alphabet[] =
16 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
20 base64_uint8 (guint8 v, char *c)
22 c[0] = base64_alphabet[(v >> 0) & 0x3f];
23 c[1] = base64_alphabet[(v >> 6) & 0x3];
28 base64_uint16 (guint32 v, char *c)
30 c[0] = base64_alphabet[(v >> 0) & 0x3f];
31 c[1] = base64_alphabet[(v >> 6) & 0x3f];
32 c[2] = base64_alphabet[(v >> 12) & 0xf];
37 base64_uint24 (guint32 v, char *c)
39 c[0] = base64_alphabet[(v >> 0) & 0x3f];
40 c[1] = base64_alphabet[(v >> 6) & 0x3f];
41 c[2] = base64_alphabet[(v >> 12) & 0x3f];
42 c[3] = base64_alphabet[(v >> 18) & 0x3f];
47 base64_uint32 (guint32 v, char *c)
49 c[0] = base64_alphabet[(v >> 0) & 0x3f];
50 c[1] = base64_alphabet[(v >> 6) & 0x3f];
51 c[2] = base64_alphabet[(v >> 12) & 0x3f];
52 c[3] = base64_alphabet[(v >> 18) & 0x3f];
53 c[4] = base64_alphabet[(v >> 24) & 0x3f];
54 c[5] = base64_alphabet[(v >> 30) & 0x2];
57 /************************************************************************
58 * conversion of raw image data to uncompressed png data: uris *
59 ************************************************************************/
61 /* Table of CRCs of all 8-bit messages. */
62 static unsigned long crc_table[256];
64 /* Flag: has the table been computed? Initially false. */
65 static int crc_table_computed = 0;
67 /* Make the table for a fast CRC. */
74 for (n = 0; n < 256; n++) {
75 c = (unsigned long) n;
76 for (k = 0; k < 8; k++) {
78 c = 0xedb88320L ^ (c >> 1);
84 crc_table_computed = 1;
88 update_crc(unsigned long crc, unsigned char *buf, int len)
90 unsigned long c = crc;
93 if (!crc_table_computed)
95 for (n = 0; n < len; n++) {
96 c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
102 crc(unsigned char *buf, int len)
104 return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
107 #define BASE 65521 /* largest prime smaller than 65536 */
109 update_adler32(unsigned long adler, unsigned char *buf, int len)
111 unsigned long s1 = adler & 0xffff;
112 unsigned long s2 = (adler >> 16) & 0xffff;
115 for (n = 0; n < len; n++) {
116 s1 = (s1 + buf[n]) % BASE;
117 s2 = (s2 + s1) % BASE;
119 return (s2 << 16) + s1;
123 to_png_rgb (int w, int h, int byte_stride, guint32 *data)
125 guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
126 guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
127 /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
128 /* bpp: */ 8, /* color type: */ 2,
130 guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
131 guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
132 gsize data_size, row_size;
134 guint8 *png, *p, *p_row, *p_idat;
140 char *url, *url_base64;
141 int state = 0, outlen;
144 *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
145 *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
146 *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
148 row_size = 1 + w * 3;
150 row_header[1] = row_size & 0xff;
151 row_header[2] = (row_size >> 8) & 0xff;
152 row_header[3] = ~row_header[1];
153 row_header[4] = ~row_header[2];
156 data_size = 2 + (6 + w * 3) * h + 4;
158 *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
160 png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
161 png = g_malloc (png_size);
164 memcpy (p, header, sizeof(header));
166 memcpy (p, ihdr, sizeof(ihdr));
168 memcpy (p, idat_start, sizeof(idat_start));
169 p += sizeof(idat_start);
173 zlib header: 0x78, 0x01 ,
174 h * scanline: row_header[] + width * r,g,b
187 for (y = 0; y < h; y++) {
189 row_header[0] = 1; /* final block */
190 memcpy (p, row_header, sizeof(row_header));
191 p += sizeof(row_header);
194 data += byte_stride / 4;
195 for (x = 0; x < w; x++) {
197 *p++ = (pixel >> 16) & 0xff; /* red */
198 *p++ = (pixel >> 8) & 0xff; /* green */
199 *p++ = (pixel >> 0) & 0xff; /* blue */
201 adler = update_adler32(adler, p_row, p - p_row);
205 *(guint32 *)p = GUINT32_TO_BE(adler);
207 *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
210 memcpy (p, iend, sizeof(iend));
213 assert(p - png == png_size);
215 url = g_malloc (strlen("data:image/png;base64,") +
216 ((png_size / 3 + 1) * 4 + 4) + 1);
217 strcpy (url, "data:image/png;base64,");
219 url_base64 = url + strlen("data:image/png;base64,");
220 outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
221 outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
222 url_base64[outlen] = 0;
230 to_png_rgba (int w, int h, int byte_stride, guint32 *data)
232 guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
233 guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
234 /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
235 /* bpp: */ 8, /* color type: */ 6,
237 guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
238 guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
239 gsize data_size, row_size;
241 guint8 *png, *p, *p_row, *p_idat;
247 char *url, *url_base64;
248 int state = 0, outlen;
251 *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
252 *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
253 *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
255 row_size = 1 + w * 4;
257 row_header[1] = row_size & 0xff;
258 row_header[2] = (row_size >> 8) & 0xff;
259 row_header[3] = ~row_header[1];
260 row_header[4] = ~row_header[2];
263 data_size = 2 + (6 + w * 4) * h + 4;
265 *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
267 png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
268 png = g_malloc (png_size);
271 memcpy (p, header, sizeof(header));
273 memcpy (p, ihdr, sizeof(ihdr));
275 memcpy (p, idat_start, sizeof(idat_start));
276 p += sizeof(idat_start);
280 zlib header: 0x78, 0x01 ,
281 h * scanline: row_header[] + width * r,g,b,a
294 for (y = 0; y < h; y++) {
296 row_header[0] = 1; /* final block */
297 memcpy (p, row_header, sizeof(row_header));
298 p += sizeof(row_header);
301 data += byte_stride / 4;
302 for (x = 0; x < w; x++) {
304 *p++ = (pixel >> 16) & 0xff; /* red */
305 *p++ = (pixel >> 8) & 0xff; /* green */
306 *p++ = (pixel >> 0) & 0xff; /* blue */
307 *p++ = (pixel >> 24) & 0xff; /* alpha */
309 adler = update_adler32(adler, p_row, p - p_row);
313 *(guint32 *)p = GUINT32_TO_BE(adler);
315 *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
318 memcpy (p, iend, sizeof(iend));
321 assert(p - png == png_size);
323 url = g_malloc (strlen("data:image/png;base64,") +
324 ((png_size / 3 + 1) * 4 + 4) + 1);
325 strcpy (url, "data:image/png;base64,");
327 url_base64 = url + strlen("data:image/png;base64,");
328 outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
329 outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
330 url_base64[outlen] = 0;
339 to_png_a (int w, int h, int byte_stride, guint8 *data)
341 guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
342 guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
343 /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
344 /* bpp: */ 8, /* color type: */ 4,
346 guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
347 guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
348 gsize data_size, row_size;
350 guint8 *png, *p, *p_row, *p_idat;
356 char *url, *url_base64;
357 int state = 0, outlen;
360 *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
361 *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
362 *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
364 row_size = 1 + w * 2;
366 row_header[1] = row_size & 0xff;
367 row_header[2] = (row_size >> 8) & 0xff;
368 row_header[3] = ~row_header[1];
369 row_header[4] = ~row_header[2];
372 data_size = 2 + (6 + w * 2) * h + 4;
374 *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
376 png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
377 png = g_malloc (png_size);
380 memcpy (p, header, sizeof(header));
382 memcpy (p, ihdr, sizeof(ihdr));
384 memcpy (p, idat_start, sizeof(idat_start));
385 p += sizeof(idat_start);
389 zlib header: 0x78, 0x01 ,
390 h * scanline: row_header[] + width * r,g,b,a
403 for (y = 0; y < h; y++) {
405 row_header[0] = 1; /* final block */
406 memcpy (p, row_header, sizeof(row_header));
407 p += sizeof(row_header);
410 data += byte_stride / 4;
411 for (x = 0; x < w; x++) {
413 *p++ = 0x00; /* gray */
414 *p++ = pixel; /* alpha */
416 adler = update_adler32(adler, p_row, p - p_row);
420 *(guint32 *)p = GUINT32_TO_BE(adler);
422 *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
425 memcpy (p, iend, sizeof(iend));
428 assert(p - png == png_size);
430 url = g_malloc (strlen("data:image/png;base64,") +
431 ((png_size / 3 + 1) * 4 + 4) + 1);
432 strcpy (url, "data:image/png;base64,");
434 url_base64 = url + strlen("data:image/png;base64,");
435 outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
436 outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
437 url_base64[outlen] = 0;
445 /************************************************************************
446 * Basic I/O primitives *
447 ************************************************************************/
449 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)
542 BroadwayOutput *output;
544 output = g_new0 (BroadwayOutput, 1);
548 broadway_output_write_header (output);
550 output->zfd = gzdopen(fd, "wb");
552 /* Need an initial multipart boundary */
553 send_boundary (output);
559 broadway_output_free (BroadwayOutput *output)
562 gzclose (output->zfd);
569 broadway_output_flush (BroadwayOutput *output)
571 send_boundary (output);
572 gzflush (output->zfd, Z_SYNC_FLUSH);
573 return !output->error;
577 /************************************************************************
578 * Core rendering operations *
579 ************************************************************************/
582 broadway_output_copy_rectangles (BroadwayOutput *output, int id,
583 BroadwayRect *rects, int n_rects,
589 len = 1 + 3 + 3 + 3*4*n_rects + 3 + 3;
591 buf = g_malloc (len);
594 base64_uint16(id, &buf[p]); p +=3;
595 base64_uint16(n_rects, &buf[p]); p +=3;
596 for (i = 0; i < n_rects; i++)
598 base64_uint16(rects[i].x, &buf[p]); p +=3;
599 base64_uint16(rects[i].y, &buf[p]); p +=3;
600 base64_uint16(rects[i].width, &buf[p]); p +=3;
601 base64_uint16(rects[i].height, &buf[p]); p +=3;
603 base64_uint16(dx, &buf[p]); p +=3;
604 base64_uint16(dy, &buf[p]); p +=3;
606 broadway_output_write (output, buf, len);
611 broadway_output_new_surface(BroadwayOutput *output, int id, int x, int y, int w, int h)
616 base64_uint16(id, &buf[1]);
617 base64_uint16(x, &buf[4]);
618 base64_uint16(y, &buf[7]);
619 base64_uint16(w, &buf[10]);
620 base64_uint16(h, &buf[13]);
622 broadway_output_write (output, buf, 16);
626 broadway_output_show_surface(BroadwayOutput *output, int id)
631 base64_uint16(id, &buf[1]);
633 broadway_output_write (output, buf, 4);
637 broadway_output_hide_surface(BroadwayOutput *output, int id)
642 base64_uint16(id, &buf[1]);
644 broadway_output_write (output, buf, 4);
648 broadway_output_destroy_surface(BroadwayOutput *output, int id)
653 base64_uint16(id, &buf[1]);
655 broadway_output_write (output, buf, 4);
659 broadway_output_move_surface(BroadwayOutput *output, int id, int x, int y)
664 base64_uint16(id, &buf[1]);
665 base64_uint16(x, &buf[4]);
666 base64_uint16(y, &buf[7]);
668 broadway_output_write (output, buf, 10);
672 broadway_output_resize_surface(BroadwayOutput *output, int id, int w, int h)
677 base64_uint16(id, &buf[1]);
678 base64_uint16(w, &buf[4]);
679 base64_uint16(h, &buf[7]);
681 broadway_output_write (output, buf, 10);
685 broadway_output_put_rgb (BroadwayOutput *output, int id, int x, int y,
686 int w, int h, int byte_stride, void *data)
693 base64_uint16(id, &buf[1]);
694 base64_uint16(x, &buf[4]);
695 base64_uint16(y, &buf[7]);
697 url = to_png_rgb (w, h, byte_stride, (guint32*)data);
699 base64_uint32(len, &buf[10]);
701 broadway_output_write (output, buf, 16);
703 broadway_output_write (output, url, len);
714 is_any_x_set (unsigned char *data,
715 int box_x1, int box_x2,
716 int x1, int x2, int y, int *x_set,
731 ptr = (guint32 *)(data + y * byte_stride + x1 * 4);
748 #define EXTEND_X_FUZZ 10
749 #define EXTEND_Y_FUZZ 10
752 extend_x_range (unsigned char *data,
753 int box_x1, int box_y1,
754 int box_x2, int box_y2,
755 int *x1, int *x2, int y,
761 while (is_any_x_set (data, box_x1, box_x2, *x1 - EXTEND_X_FUZZ, *x1, y, &new_x, byte_stride))
767 while (is_any_x_set (data, box_x1, box_x2, *x2, *x2 + EXTEND_X_FUZZ, y, &new_x, byte_stride))
777 extend_y_range (unsigned char *data,
778 int box_x1, int box_y1,
779 int box_x2, int box_y2,
780 int x1, int x2, int *y,
791 y2 = *y + EXTEND_Y_FUZZ;
795 for (yy = y2; yy > *y + 1; yy--)
797 if (is_any_x_set (data, box_x1, box_x2, x1, x2, yy - 1, NULL, byte_stride))
814 rgba_find_rects_extents (unsigned char *data,
815 int box_x1, int box_y1,
816 int box_x2, int box_y2,
821 int x1, x2, y1, y2, yy;
831 /* Expand maximally for all known rows */
836 for (yy = y1; yy < y2; yy++)
837 extended |= extend_x_range (data,
845 while (extend_y_range(data,
858 rgba_find_rects_sub (unsigned char *data,
859 int box_x1, int box_y1,
860 int box_x2, int box_y2,
863 int *n_rects, int *alloc_rects)
869 if (box_x1 == box_x2 || box_y1 == box_y2)
872 for (y = box_y1; y < box_y2; y++)
874 line = (guint32 *)(data + y * byte_stride + box_x1 * 4);
876 for (x = box_x1; x < box_x2; x++)
880 rgba_find_rects_extents (data,
881 box_x1, box_y1, box_x2, box_y2,
882 x, y, &rect, byte_stride);
883 if (*n_rects == *alloc_rects)
886 *rects = g_renew (BroadwayBox, *rects, *alloc_rects);
888 (*rects)[*n_rects] = rect;
890 rgba_find_rects_sub (data,
894 rects, n_rects, alloc_rects);
895 rgba_find_rects_sub (data,
899 rects, n_rects, alloc_rects);
900 rgba_find_rects_sub (data,
904 rects, n_rects, alloc_rects);
913 rgba_find_rects (unsigned char *data,
914 int w, int h, int byte_stride,
921 rects = g_new (BroadwayBox, alloc_rects);
924 rgba_find_rects_sub (data,
925 0, 0, w, h, byte_stride,
926 &rects, n_rects, &alloc_rects);
932 broadway_output_put_rgba (BroadwayOutput *output, int id, int x, int y,
933 int w, int h, int byte_stride, void *data)
942 rects = rgba_find_rects (data, w, h, byte_stride, &n_rects);
944 for (i = 0; i < n_rects; i++)
946 subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
949 base64_uint16(id, &buf[1]);
950 base64_uint16(x + rects[i].x1, &buf[4]);
951 base64_uint16(y + rects[i].y1, &buf[7]);
953 url = to_png_rgba (rects[i].x2 - rects[i].x1,
954 rects[i].y2 - rects[i].y1,
955 byte_stride, (guint32*)subdata);
957 base64_uint32(len, &buf[10]);
959 broadway_output_write (output, buf, 16);
961 broadway_output_write (output, url, len);
971 send_image_a (BroadwayOutput *output, int id, int x, int y,
972 int w, int h, int byte_stride, guint8 *data)
979 base64_uint16(id, &buf[1]);
980 base64_uint16(x, &buf[4]);
981 base64_uint16(y, &buf[7]);
983 url = to_png_a (w, h, byte_stride, data);
985 base64_uint32(len, &buf[10]);
987 broadway_output_write (output, buf, 16);
989 broadway_output_write (output, url, len);