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