]> Pileus Git - ~andy/gtk/blob - gdk/broadway/broadway.c
[broadway] Fix all unnecessary warnings
[~andy/gtk] / gdk / broadway / broadway.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <assert.h>
5 #include <errno.h>
6 #include <zlib.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <netinet/in.h>
10 #include <netinet/tcp.h>
11
12 #include "broadway.h"
13
14 /************************************************************************
15  *                Base64 functions                                      *
16  ************************************************************************/
17
18 static const char base64_alphabet[] =
19         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
20
21 #if 0 /* Unused for now */
22 static void
23 base64_uint8 (guint8 v, char *c)
24 {
25   c[0] = base64_alphabet[(v >> 0) & 0x3f];
26   c[1] = base64_alphabet[(v >> 6) & 0x3];
27 }
28 #endif
29
30 static void
31 base64_uint16 (guint32 v, char *c)
32 {
33   c[0] = base64_alphabet[(v >> 0) & 0x3f];
34   c[1] = base64_alphabet[(v >> 6) & 0x3f];
35   c[2] = base64_alphabet[(v >> 12) & 0xf];
36 }
37
38 #if 0
39 static void
40 base64_uint24 (guint32 v, char *c)
41 {
42   c[0] = base64_alphabet[(v >> 0) & 0x3f];
43   c[1] = base64_alphabet[(v >> 6) & 0x3f];
44   c[2] = base64_alphabet[(v >> 12) & 0x3f];
45   c[3] = base64_alphabet[(v >> 18) & 0x3f];
46 }
47 #endif
48
49 static void
50 base64_uint32 (guint32 v, char *c)
51 {
52   c[0] = base64_alphabet[(v >> 0) & 0x3f];
53   c[1] = base64_alphabet[(v >> 6) & 0x3f];
54   c[2] = base64_alphabet[(v >> 12) & 0x3f];
55   c[3] = base64_alphabet[(v >> 18) & 0x3f];
56   c[4] = base64_alphabet[(v >> 24) & 0x3f];
57   c[5] = base64_alphabet[(v >> 30) & 0x2];
58 }
59
60 /************************************************************************
61  *  conversion of raw image data to uncompressed png data: uris         *
62  ************************************************************************/
63
64 /* Table of CRCs of all 8-bit messages. */
65 static unsigned long crc_table[256];
66
67 /* Flag: has the table been computed? Initially false. */
68 static int crc_table_computed = 0;
69
70 /* Make the table for a fast CRC. */
71 static void
72 make_crc_table(void)
73 {
74   unsigned long c;
75   int n, k;
76
77   for (n = 0; n < 256; n++) {
78     c = (unsigned long) n;
79     for (k = 0; k < 8; k++) {
80       if (c & 1)
81         c = 0xedb88320L ^ (c >> 1);
82       else
83         c = c >> 1;
84     }
85     crc_table[n] = c;
86   }
87   crc_table_computed = 1;
88 }
89
90 static unsigned long
91 update_crc(unsigned long crc, unsigned char *buf, int len)
92 {
93   unsigned long c = crc;
94   int n;
95
96   if (!crc_table_computed)
97     make_crc_table();
98   for (n = 0; n < len; n++) {
99     c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
100   }
101   return c;
102 }
103
104 static unsigned long
105 crc(unsigned char *buf, int len)
106 {
107   return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
108 }
109
110 #define BASE 65521 /* largest prime smaller than 65536 */
111 static unsigned long
112 update_adler32(unsigned long adler, unsigned char *buf, int len)
113 {
114   unsigned long s1 = adler & 0xffff;
115   unsigned long s2 = (adler >> 16) & 0xffff;
116   int n;
117
118   for (n = 0; n < len; n++) {
119     s1 = (s1 + buf[n]) % BASE;
120     s2 = (s2 + s1)     % BASE;
121   }
122   return (s2 << 16) + s1;
123 }
124
125 static char *
126 to_png_rgb (int w, int h, int byte_stride, guint32 *data)
127 {
128   guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
129   guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
130                         /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
131                         /* bpp: */ 8, /* color type: */ 2,
132                         0, 0, 0};
133   guchar idat_start[8] = { /* len: */0, 0, 0, 0,   'I', 'D', 'A', 'T' };
134   guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
135   gsize data_size, row_size;
136   char row_header[6];
137   guint8 *png, *p, *p_row, *p_idat;
138   guint32 *row;
139   unsigned long adler;
140   guint32 pixel;
141   gsize png_size;
142   int x, y;
143   char *url, *url_base64;
144   int state = 0, outlen;
145   int save = 0;
146
147   *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
148   *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
149   *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
150
151   row_size = 1 + w * 3;
152   row_header[0] = 0;
153   row_header[1] = row_size & 0xff;
154   row_header[2] = (row_size >> 8) & 0xff;
155   row_header[3] = ~row_header[1];
156   row_header[4] = ~row_header[2];
157   row_header[5] = 0;
158
159   data_size = 2 + (6 + w * 3) * h + 4;
160
161   *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
162
163   png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
164   png = g_malloc (png_size);
165
166   p = png;
167   memcpy (p, header, sizeof(header));
168   p += sizeof(header);
169   memcpy (p, ihdr, sizeof(ihdr));
170   p += sizeof(ihdr);
171   memcpy (p, idat_start, sizeof(idat_start));
172   p += sizeof(idat_start);
173
174   /* IDAT data:
175
176      zlib header:  0x78, 0x01 ,
177      h * scanline: row_header[] + width * r,g,b
178      checksum: adler32
179   */
180
181   p_idat = p - 4;
182
183   /* zlib header */
184   *p++ = 0x78;
185   *p++ = 0x01;
186
187   adler = 1;
188
189   /* scanline data */
190   for (y = 0; y < h; y++) {
191     if (y == h - 1)
192       row_header[0] = 1; /* final block */
193     memcpy (p, row_header, sizeof(row_header));
194     p += sizeof(row_header);
195     p_row = p - 1;
196     row = data;
197     data += byte_stride / 4;
198     for (x = 0; x < w; x++) {
199       pixel = *row++;
200       *p++ = (pixel >> 16) & 0xff; /* red */
201       *p++ = (pixel >> 8) & 0xff; /* green */
202       *p++ = (pixel >> 0) & 0xff; /* blue */
203     }
204     adler = update_adler32(adler, p_row, p - p_row);
205   }
206
207   /* adler32 */
208   *(guint32 *)p = GUINT32_TO_BE(adler);
209   p += 4;
210   *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
211   p += 4;
212
213   memcpy (p, iend, sizeof(iend));
214   p += sizeof(iend);
215
216   assert(p - png == png_size);
217
218   url = g_malloc (strlen("data:image/png;base64,") +
219                    ((png_size / 3 + 1) * 4 + 4) + 1);
220   strcpy (url, "data:image/png;base64,");
221
222   url_base64 = url + strlen("data:image/png;base64,");
223   outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
224   outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
225   url_base64[outlen] = 0;
226
227   free (png);
228
229   return url;
230 }
231
232 static char *
233 to_png_rgba (int w, int h, int byte_stride, guint32 *data)
234 {
235   guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
236   guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
237                         /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
238                         /* bpp: */ 8, /* color type: */ 6,
239                         0, 0, 0};
240   guchar idat_start[8] = { /* len: */0, 0, 0, 0,   'I', 'D', 'A', 'T' };
241   guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
242   gsize data_size, row_size;
243   char row_header[6];
244   guint8 *png, *p, *p_row, *p_idat;
245   guint32 *row;
246   unsigned long adler;
247   guint32 pixel;
248   gsize png_size;
249   int x, y;
250   char *url, *url_base64;
251   int state = 0, outlen;
252   int save = 0;
253
254   *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
255   *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
256   *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
257
258   row_size = 1 + w * 4;
259   row_header[0] = 0;
260   row_header[1] = row_size & 0xff;
261   row_header[2] = (row_size >> 8) & 0xff;
262   row_header[3] = ~row_header[1];
263   row_header[4] = ~row_header[2];
264   row_header[5] = 0;
265
266   data_size = 2 + (6 + w * 4) * h + 4;
267
268   *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
269
270   png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
271   png = g_malloc (png_size);
272
273   p = png;
274   memcpy (p, header, sizeof(header));
275   p += sizeof(header);
276   memcpy (p, ihdr, sizeof(ihdr));
277   p += sizeof(ihdr);
278   memcpy (p, idat_start, sizeof(idat_start));
279   p += sizeof(idat_start);
280
281   /* IDAT data:
282
283      zlib header:  0x78, 0x01 ,
284      h * scanline: row_header[] + width * r,g,b,a
285      checksum: adler32
286   */
287
288   p_idat = p - 4;
289
290   /* zlib header */
291   *p++ = 0x78;
292   *p++ = 0x01;
293
294   adler = 1;
295
296   /* scanline data */
297   for (y = 0; y < h; y++) {
298     if (y == h - 1)
299       row_header[0] = 1; /* final block */
300     memcpy (p, row_header, sizeof(row_header));
301     p += sizeof(row_header);
302     p_row = p - 1;
303     row = data;
304     data += byte_stride / 4;
305     for (x = 0; x < w; x++) {
306       pixel = *row++;
307       *p++ = (pixel >> 16) & 0xff; /* red */
308       *p++ = (pixel >> 8) & 0xff; /* green */
309       *p++ = (pixel >> 0) & 0xff; /* blue */
310       *p++ = (pixel >> 24) & 0xff; /* alpha */
311     }
312     adler = update_adler32(adler, p_row, p - p_row);
313   }
314
315   /* adler32 */
316   *(guint32 *)p = GUINT32_TO_BE(adler);
317   p += 4;
318   *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
319   p += 4;
320
321   memcpy (p, iend, sizeof(iend));
322   p += sizeof(iend);
323
324   assert(p - png == png_size);
325
326   url = g_malloc (strlen("data:image/png;base64,") +
327                    ((png_size / 3 + 1) * 4 + 4) + 1);
328   strcpy (url, "data:image/png;base64,");
329
330   url_base64 = url + strlen("data:image/png;base64,");
331   outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
332   outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
333   url_base64[outlen] = 0;
334
335   free (png);
336
337   return url;
338 }
339
340 #if 0
341 static char *
342 to_png_a (int w, int h, int byte_stride, guint8 *data)
343 {
344   guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
345   guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
346                         /* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
347                         /* bpp: */ 8, /* color type: */ 4,
348                         0, 0, 0};
349   guchar idat_start[8] = { /* len: */0, 0, 0, 0,   'I', 'D', 'A', 'T' };
350   guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
351   gsize data_size, row_size;
352   char row_header[6];
353   guint8 *png, *p, *p_row, *p_idat;
354   guint8 *row;
355   unsigned long adler;
356   guint32 pixel;
357   gsize png_size;
358   int x, y;
359   char *url, *url_base64;
360   int state = 0, outlen;
361   int save = 0;
362
363   *(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
364   *(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
365   *(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
366
367   row_size = 1 + w * 2;
368   row_header[0] = 0;
369   row_header[1] = row_size & 0xff;
370   row_header[2] = (row_size >> 8) & 0xff;
371   row_header[3] = ~row_header[1];
372   row_header[4] = ~row_header[2];
373   row_header[5] = 0;
374
375   data_size = 2 + (6 + w * 2) * h + 4;
376
377   *(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
378
379   png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
380   png = g_malloc (png_size);
381
382   p = png;
383   memcpy (p, header, sizeof(header));
384   p += sizeof(header);
385   memcpy (p, ihdr, sizeof(ihdr));
386   p += sizeof(ihdr);
387   memcpy (p, idat_start, sizeof(idat_start));
388   p += sizeof(idat_start);
389
390   /* IDAT data:
391
392      zlib header:  0x78, 0x01 ,
393      h * scanline: row_header[] + width * r,g,b,a
394      checksum: adler32
395   */
396
397   p_idat = p - 4;
398
399   /* zlib header */
400   *p++ = 0x78;
401   *p++ = 0x01;
402
403   adler = 1;
404
405   /* scanline data */
406   for (y = 0; y < h; y++) {
407     if (y == h - 1)
408       row_header[0] = 1; /* final block */
409     memcpy (p, row_header, sizeof(row_header));
410     p += sizeof(row_header);
411     p_row = p - 1;
412     row = data;
413     data += byte_stride / 4;
414     for (x = 0; x < w; x++) {
415       pixel = *row++;
416       *p++ = 0x00; /* gray */
417       *p++ = pixel; /* alpha */
418     }
419     adler = update_adler32(adler, p_row, p - p_row);
420   }
421
422   /* adler32 */
423   *(guint32 *)p = GUINT32_TO_BE(adler);
424   p += 4;
425   *(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
426   p += 4;
427
428   memcpy (p, iend, sizeof(iend));
429   p += sizeof(iend);
430
431   assert(p - png == png_size);
432
433   url = g_malloc (strlen("data:image/png;base64,") +
434                   ((png_size / 3 + 1) * 4 + 4) + 1);
435   strcpy (url, "data:image/png;base64,");
436
437   url_base64 = url + strlen("data:image/png;base64,");
438   outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
439   outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
440   url_base64[outlen] = 0;
441
442   free (png);
443
444   return url;
445 }
446 #endif
447
448 /************************************************************************
449  *                Basic I/O primitives                                  *
450  ************************************************************************/
451
452 struct BroadwayOutput {
453   int fd;
454   gzFile *zfd;
455   int error;
456   guint32 serial;
457 };
458
459 static void
460 broadway_output_write_raw (BroadwayOutput *output,
461                            const void *buf, gsize count)
462 {
463   gssize res;
464   int errsave;
465   const char *ptr = (const char *)buf;
466
467   if (output->error)
468     return;
469
470   while (count > 0)
471     {
472       res = write(output->fd, ptr, count);
473       if (res == -1)
474         {
475           errsave = errno;
476           if (errsave == EINTR)
477             continue;
478           output->error = TRUE;
479           return;
480         }
481       if (res == 0)
482         {
483           output->error = TRUE;
484           return;
485         }
486       count -= res;
487       ptr += res;
488     }
489 }
490
491 static void
492 broadway_output_write (BroadwayOutput *output,
493                        const void *buf, gsize count)
494 {
495   gssize res;
496   const char *ptr = (const char *)buf;
497
498   if (output->error)
499     return;
500
501   while (count > 0)
502     {
503       res = gzwrite(output->zfd, ptr, count);
504       if (res == -1)
505         {
506           output->error = TRUE;
507           return;
508         }
509       if (res == 0)
510         {
511           output->error = TRUE;
512           return;
513         }
514       count -= res;
515       ptr += res;
516     }
517 }
518
519 static void
520 broadway_output_write_header (BroadwayOutput *output)
521 {
522   char *header;
523
524   header =
525     "HTTP/1.1 200 OK\r\n"
526     "Content-type: multipart/x-mixed-replace;boundary=x\r\n"
527     "Content-Encoding: gzip\r\n"
528     "\r\n";
529   broadway_output_write_raw (output,
530                              header, strlen (header));
531 }
532
533 static void
534 send_boundary (BroadwayOutput *output)
535 {
536   char *boundary =
537     "--x\r\n"
538     "\r\n";
539
540   broadway_output_write (output, boundary, strlen (boundary));
541 }
542
543 BroadwayOutput *
544 broadway_output_new(int fd, guint32 serial)
545 {
546   BroadwayOutput *output;
547   int flag = 1;
548
549   output = g_new0 (BroadwayOutput, 1);
550
551   output->fd = fd;
552   output->serial = serial;
553
554   setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
555
556   broadway_output_write_header (output);
557
558   output->zfd = gzdopen(fd, "wb");
559
560   /* Need an initial multipart boundary */
561   send_boundary (output);
562
563   return output;
564 }
565
566 void
567 broadway_output_free (BroadwayOutput *output)
568 {
569   if (output->zfd)
570     gzclose (output->zfd);
571   else
572     close (output->fd);
573   free (output);
574 }
575
576 guint32
577 broadway_output_get_next_serial (BroadwayOutput *output)
578 {
579   return output->serial;
580 }
581
582 int
583 broadway_output_flush (BroadwayOutput *output)
584 {
585   send_boundary (output);
586   gzflush (output->zfd, Z_SYNC_FLUSH);
587   return !output->error;
588 }
589
590
591 /************************************************************************
592  *                     Core rendering operations                        *
593  ************************************************************************/
594
595 #define HEADER_LEN (1+6)
596
597 static void
598 append_uint16 (guint32 v, char *buf, int *p)
599 {
600   base64_uint16 (v, &buf[*p]);
601   *p += 3;
602 }
603
604 static void
605 append_uint32 (guint32 v, char *buf, int *p)
606 {
607   base64_uint32 (v, &buf[*p]);
608   *p += 6;
609 }
610
611 static int
612 write_header(BroadwayOutput *output, char *buf, char op)
613 {
614   int p;
615
616   p = 0;
617   buf[p++] = op;
618   append_uint32 (output->serial++, buf, &p);
619
620   return p;
621 }
622
623 void
624 broadway_output_copy_rectangles (BroadwayOutput *output,  int id,
625                                  BroadwayRect *rects, int n_rects,
626                                  int dx, int dy)
627 {
628   char *buf;
629   int len, i, p;
630
631   len = HEADER_LEN + 3 + 3 + 3*4*n_rects + 3 + 3;
632
633   buf = g_malloc (len);
634   p = write_header (output, buf, 'b');
635   append_uint16 (id, buf, &p);
636   append_uint16 (n_rects, buf, &p);
637   for (i = 0; i < n_rects; i++)
638     {
639       append_uint16 (rects[i].x, buf, &p);
640       append_uint16 (rects[i].y, buf, &p);
641       append_uint16 (rects[i].width, buf, &p);
642       append_uint16 (rects[i].height, buf, &p);
643     }
644   append_uint16 (dx, buf, &p);
645   append_uint16 (dy, buf, &p);
646
647   assert (p == len);
648
649   broadway_output_write (output, buf, len);
650   free (buf);
651 }
652
653 void
654 broadway_output_grab_pointer (BroadwayOutput *output,
655                               int id,
656                               gboolean owner_event)
657 {
658   char buf[HEADER_LEN + 3 + 1];
659   int p;
660
661   p = write_header (output, buf, 'g');
662   append_uint16 (id, buf, &p);
663   buf[p++] = owner_event ? '1': '0';
664
665   assert (p == sizeof (buf));
666
667   broadway_output_write (output, buf, sizeof (buf));
668 }
669
670 guint32
671 broadway_output_ungrab_pointer (BroadwayOutput *output)
672 {
673   char buf[HEADER_LEN];
674   guint32 serial;
675   int p;
676
677   serial = output->serial;
678   p = write_header (output, buf, 'u');
679
680   assert (p == sizeof (buf));
681
682   broadway_output_write (output, buf, sizeof (buf));
683
684   return serial;
685 }
686
687 void
688 broadway_output_new_surface(BroadwayOutput *output,
689                             int id, int x, int y, int w, int h,
690                             gboolean is_temp)
691 {
692   char buf[HEADER_LEN + 16];
693   int p;
694
695   p = write_header (output, buf, 's');
696   append_uint16 (id, buf, &p);
697   append_uint16 (x, buf, &p);
698   append_uint16 (y, buf, &p);
699   append_uint16 (w, buf, &p);
700   append_uint16 (h, buf, &p);
701   buf[p++] = is_temp ? '1' : '0';
702
703   assert (p == sizeof (buf));
704
705   broadway_output_write (output, buf, sizeof (buf));
706 }
707
708 void
709 broadway_output_show_surface(BroadwayOutput *output,  int id)
710 {
711   char buf[HEADER_LEN + 3];
712   int p;
713
714   p = write_header (output, buf, 'S');
715   append_uint16 (id, buf, &p);
716
717   assert (p == sizeof (buf));
718
719   broadway_output_write (output, buf, sizeof (buf));
720 }
721
722 void
723 broadway_output_hide_surface(BroadwayOutput *output,  int id)
724 {
725   char buf[HEADER_LEN + 3];
726   int p;
727
728   p = write_header (output, buf, 'H');
729   append_uint16 (id, buf, &p);
730
731   assert (p == sizeof (buf));
732
733   broadway_output_write (output, buf, sizeof (buf));
734 }
735
736 void
737 broadway_output_destroy_surface(BroadwayOutput *output,  int id)
738 {
739   char buf[HEADER_LEN + 3];
740   int p;
741
742   p = write_header (output, buf, 'd');
743   append_uint16 (id, buf, &p);
744
745   assert (p == sizeof (buf));
746
747   broadway_output_write (output, buf, sizeof (buf));
748 }
749
750 void
751 broadway_output_move_surface(BroadwayOutput *output,  int id, int x, int y)
752 {
753   char buf[HEADER_LEN + 9];
754   int p;
755
756   p = write_header (output, buf, 'm');
757
758   append_uint16 (id, buf, &p);
759   append_uint16 (x, buf, &p);
760   append_uint16 (y, buf, &p);
761
762   assert (p == sizeof (buf));
763
764   broadway_output_write (output, buf, sizeof (buf));
765 }
766
767 void
768 broadway_output_resize_surface(BroadwayOutput *output,  int id, int w, int h)
769 {
770   char buf[HEADER_LEN + 9];
771   int p;
772
773   p = write_header (output, buf, 'r');
774
775   append_uint16 (id, buf, &p);
776   append_uint16 (w, buf, &p);
777   append_uint16 (h, buf, &p);
778
779   assert (p == sizeof (buf));
780
781   broadway_output_write (output, buf, sizeof (buf));
782 }
783
784 void
785 broadway_output_set_transient_for (BroadwayOutput *output,
786                                    int             id,
787                                    int             parent_id)
788 {
789   char buf[HEADER_LEN + 6];
790   int p;
791
792   p = write_header (output, buf, 'p');
793
794   append_uint16 (id, buf, &p);
795   append_uint16 (parent_id, buf, &p);
796
797   assert (p == sizeof (buf));
798
799   broadway_output_write (output, buf, sizeof (buf));
800 }
801
802
803 void
804 broadway_output_put_rgb (BroadwayOutput *output,  int id, int x, int y,
805                          int w, int h, int byte_stride, void *data)
806 {
807   char buf[HEADER_LEN + 15];
808   gsize len;
809   char *url;
810   int p;
811
812   p = write_header (output, buf, 'i');
813
814   append_uint16 (id, buf, &p);
815   append_uint16 (x, buf, &p);
816   append_uint16 (y, buf, &p);
817
818   url = to_png_rgb (w, h, byte_stride, (guint32*)data);
819   len = strlen (url);
820   append_uint32 (len, buf, &p);
821
822   assert (p == sizeof (buf));
823
824   broadway_output_write (output, buf, sizeof (buf));
825
826   broadway_output_write (output, url, len);
827
828   free (url);
829 }
830
831 typedef struct  {
832   int x1, y1;
833   int x2, y2;
834 } BroadwayBox;
835
836 static int
837 is_any_x_set (unsigned char *data,
838               int box_x1, int box_x2,
839               int x1, int x2, int y, int *x_set,
840               int byte_stride)
841 {
842   int w ;
843   guint32 *ptr;
844
845   if (x1 < box_x1)
846     x1 = box_x1;
847
848   if (x2 > box_x2)
849     x2 = box_x2;
850
851   w = x2 - x1;
852   if (w > 0)
853     {
854       ptr = (guint32 *)(data + y * byte_stride + x1 * 4);
855       while (w-- > 0)
856         {
857           if (*ptr != 0)
858             {
859               if (x_set)
860                 *x_set = x1;
861               return 1;
862             }
863           ptr++;
864           x1++;
865         }
866     }
867   return 0;
868 }
869
870
871 #define EXTEND_X_FUZZ 10
872 #define EXTEND_Y_FUZZ 10
873
874 static int
875 extend_x_range (unsigned char *data,
876                 int box_x1, int box_y1,
877                 int box_x2, int box_y2,
878                 int *x1, int *x2, int y,
879                 int byte_stride)
880 {
881   int extended = 0;
882   int new_x;
883
884   while (is_any_x_set (data, box_x1, box_x2, *x1 - EXTEND_X_FUZZ, *x1, y, &new_x, byte_stride))
885     {
886       *x1 = new_x;
887       extended = 1;
888     }
889
890   while (is_any_x_set (data, box_x1, box_x2, *x2, *x2 + EXTEND_X_FUZZ, y, &new_x, byte_stride))
891     {
892       *x2 = new_x + 1;
893       extended = 1;
894     }
895
896   return extended;
897 }
898
899 static int
900 extend_y_range (unsigned char *data,
901                 int box_x1, int box_y1,
902                 int box_x2, int box_y2,
903                 int x1, int x2, int *y,
904                 int byte_stride)
905 {
906   int extended = 0;
907   int found_set;
908   int yy, y2;
909
910   while (*y < box_y2)
911     {
912       found_set = 0;
913
914       y2 = *y + EXTEND_Y_FUZZ;
915       if (y2 > box_y2)
916         y2 = box_y2;
917
918       for (yy = y2; yy > *y + 1; yy--)
919         {
920           if (is_any_x_set (data, box_x1, box_x2, x1, x2, yy - 1, NULL, byte_stride))
921             {
922               found_set = 1;
923               break;
924             }
925         }
926       if (!found_set)
927         break;
928       *y = yy;
929       extended = 1;
930     }
931
932   return extended;
933 }
934
935
936 static void
937 rgba_find_rects_extents (unsigned char *data,
938                          int box_x1, int box_y1,
939                          int box_x2, int box_y2,
940                          int x, int y,
941                          BroadwayBox *rect,
942                          int byte_stride)
943 {
944   int x1, x2, y1, y2, yy;
945   int extended;
946
947   x1 = x;
948   x2 = x + 1;
949   y1 = y;
950   y2 = y + 1;
951
952   do
953     {
954       /* Expand maximally for all known rows */
955       do
956         {
957           extended = 0;
958
959           for (yy = y1; yy < y2; yy++)
960             extended |= extend_x_range (data,
961                                         box_x1, box_y1,
962                                         box_x2, box_y2,
963                                         &x1, &x2, yy,
964                                         byte_stride);
965         }
966       while (extended);
967     }
968   while (extend_y_range(data,
969                         box_x1, box_y1,
970                         box_x2, box_y2,
971                         x1, x2, &y2,
972                         byte_stride));
973
974   rect->x1 = x1;
975   rect->x2 = x2;
976   rect->y1 = y1;
977   rect->y2 = y2;
978 }
979
980 static void
981 rgba_find_rects_sub (unsigned char *data,
982                      int box_x1, int box_y1,
983                      int box_x2, int box_y2,
984                      int byte_stride,
985                      BroadwayBox **rects,
986                      int *n_rects, int *alloc_rects)
987 {
988   guint32 *line;
989   BroadwayBox rect;
990   int x, y;
991
992   if (box_x1 == box_x2 || box_y1 == box_y2)
993     return;
994
995   for (y = box_y1; y < box_y2; y++)
996     {
997       line = (guint32 *)(data + y * byte_stride + box_x1 * 4);
998
999       for (x = box_x1; x < box_x2; x++)
1000         {
1001           if (*line != 0)
1002             {
1003               rgba_find_rects_extents (data,
1004                                        box_x1, box_y1, box_x2, box_y2,
1005                                        x, y, &rect, byte_stride);
1006               if (*n_rects == *alloc_rects)
1007                 {
1008                   (*alloc_rects) *= 2;
1009                   *rects = g_renew (BroadwayBox, *rects, *alloc_rects);
1010                 }
1011               (*rects)[*n_rects] = rect;
1012               (*n_rects)++;
1013               rgba_find_rects_sub (data,
1014                                    box_x1, rect.y1,
1015                                    rect.x1, rect.y2,
1016                                    byte_stride,
1017                                    rects, n_rects, alloc_rects);
1018               rgba_find_rects_sub (data,
1019                                    rect.x2, rect.y1,
1020                                    box_x2, rect.y2,
1021                                    byte_stride,
1022                                    rects, n_rects, alloc_rects);
1023               rgba_find_rects_sub (data,
1024                                    box_x1, rect.y2,
1025                                    box_x2, box_y2,
1026                                    byte_stride,
1027                                    rects, n_rects, alloc_rects);
1028               return;
1029             }
1030           line++;
1031         }
1032     }
1033 }
1034
1035 static BroadwayBox *
1036 rgba_find_rects (unsigned char *data,
1037                  int w, int h, int byte_stride,
1038                  int *n_rects)
1039 {
1040   BroadwayBox *rects;
1041   int alloc_rects;
1042
1043   alloc_rects = 20;
1044   rects = g_new (BroadwayBox, alloc_rects);
1045
1046   *n_rects = 0;
1047   rgba_find_rects_sub (data,
1048                        0, 0, w, h, byte_stride,
1049                        &rects, n_rects, &alloc_rects);
1050
1051   return rects;
1052 }
1053
1054 void
1055 broadway_output_put_rgba (BroadwayOutput *output,  int id, int x, int y,
1056                           int w, int h, int byte_stride, void *data)
1057 {
1058   char buf[HEADER_LEN + 15];
1059   gsize len;
1060   char *url;
1061   BroadwayBox *rects;
1062   int p, i, n_rects;
1063   guint8 *subdata;
1064
1065   rects = rgba_find_rects (data, w, h, byte_stride, &n_rects);
1066
1067   for (i = 0; i < n_rects; i++)
1068     {
1069       subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
1070
1071       p = write_header (output, buf, 'i');
1072       append_uint16 (id, buf, &p);
1073       append_uint16 (x + rects[i].x1, buf, &p);
1074       append_uint16 (y + rects[i].y1, buf, &p);
1075
1076       url = to_png_rgba (rects[i].x2 - rects[i].x1,
1077                          rects[i].y2 - rects[i].y1,
1078                          byte_stride, (guint32*)subdata);
1079       len = strlen (url);
1080       append_uint32 (len, buf, &p);
1081
1082       assert (p == sizeof (buf));
1083
1084       broadway_output_write (output, buf, sizeof (buf));
1085
1086       broadway_output_write (output, url, len);
1087
1088       free (url);
1089     }
1090
1091   free (rects);
1092 }
1093
1094 void
1095 broadway_output_surface_flush (BroadwayOutput *output,
1096                                int             id)
1097 {
1098   char buf[HEADER_LEN + 3];
1099   int p;
1100
1101   p = write_header (output, buf, 'f');
1102   append_uint16 (id, buf, &p);
1103
1104   assert (p == sizeof (buf));
1105
1106   broadway_output_write (output, buf, sizeof (buf));
1107 }
1108
1109 #if 0
1110 static void
1111 send_image_a (BroadwayOutput *output,  int id, int x, int y,
1112               int w, int h, int byte_stride, guint8 *data)
1113 {
1114   char buf[HEADER_LEN + 15];
1115   gsize len;
1116   char *url;
1117
1118   p = write_header (output, buf, 'i');
1119   append_uint16 (id, buf, &p);
1120   append_uint16 (x, buf, &p);
1121   append_uint16 (y, buf, &p);
1122
1123   url = to_png_a (w, h, byte_stride, data);
1124   len = strlen (url);
1125   append_uint32 (len, buf, &p);
1126
1127   assert (p == sizeof (buf));
1128
1129   broadway_output_write (output, buf, sizeof (buf));
1130
1131   broadway_output_write (output, url, len);
1132
1133   free (url);
1134 }
1135 #endif