gtk2/gdk/broadway/broadway.c

994 lines
21 KiB
C
Raw Normal View History

#include <glib.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <zlib.h>
#include "broadway.h"
/************************************************************************
* Base64 functions *
************************************************************************/
static const char base64_alphabet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#if 0
static void
base64_uint8 (guint8 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3];
}
#endif
static void
base64_uint16 (guint32 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3f];
c[2] = base64_alphabet[(v >> 12) & 0xf];
}
#if 0
static void
base64_uint24 (guint32 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3f];
c[2] = base64_alphabet[(v >> 12) & 0x3f];
c[3] = base64_alphabet[(v >> 18) & 0x3f];
}
#endif
static void
base64_uint32 (guint32 v, char *c)
{
c[0] = base64_alphabet[(v >> 0) & 0x3f];
c[1] = base64_alphabet[(v >> 6) & 0x3f];
c[2] = base64_alphabet[(v >> 12) & 0x3f];
c[3] = base64_alphabet[(v >> 18) & 0x3f];
c[4] = base64_alphabet[(v >> 24) & 0x3f];
c[5] = base64_alphabet[(v >> 30) & 0x2];
}
/************************************************************************
* conversion of raw image data to uncompressed png data: uris *
************************************************************************/
/* Table of CRCs of all 8-bit messages. */
static unsigned long crc_table[256];
/* Flag: has the table been computed? Initially false. */
static int crc_table_computed = 0;
/* Make the table for a fast CRC. */
static void
make_crc_table(void)
{
unsigned long c;
int n, k;
for (n = 0; n < 256; n++) {
c = (unsigned long) n;
for (k = 0; k < 8; k++) {
if (c & 1)
c = 0xedb88320L ^ (c >> 1);
else
c = c >> 1;
}
crc_table[n] = c;
}
crc_table_computed = 1;
}
static unsigned long
update_crc(unsigned long crc, unsigned char *buf, int len)
{
unsigned long c = crc;
int n;
if (!crc_table_computed)
make_crc_table();
for (n = 0; n < len; n++) {
c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
}
return c;
}
static unsigned long
crc(unsigned char *buf, int len)
{
return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
}
#define BASE 65521 /* largest prime smaller than 65536 */
static unsigned long
update_adler32(unsigned long adler, unsigned char *buf, int len)
{
unsigned long s1 = adler & 0xffff;
unsigned long s2 = (adler >> 16) & 0xffff;
int n;
for (n = 0; n < len; n++) {
s1 = (s1 + buf[n]) % BASE;
s2 = (s2 + s1) % BASE;
}
return (s2 << 16) + s1;
}
static char *
to_png_rgb (int w, int h, int byte_stride, guint32 *data)
{
guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
/* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
/* bpp: */ 8, /* color type: */ 2,
0, 0, 0};
guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
gsize data_size, row_size;
char row_header[6];
guint8 *png, *p, *p_row, *p_idat;
guint32 *row;
unsigned long adler;
guint32 pixel;
gsize png_size;
int x, y;
char *url, *url_base64;
int state = 0, outlen;
int save = 0;
*(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
*(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
*(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
row_size = 1 + w * 3;
row_header[0] = 0;
row_header[1] = row_size & 0xff;
row_header[2] = (row_size >> 8) & 0xff;
row_header[3] = ~row_header[1];
row_header[4] = ~row_header[2];
row_header[5] = 0;
data_size = 2 + (6 + w * 3) * h + 4;
*(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
png = g_malloc (png_size);
p = png;
memcpy (p, header, sizeof(header));
p += sizeof(header);
memcpy (p, ihdr, sizeof(ihdr));
p += sizeof(ihdr);
memcpy (p, idat_start, sizeof(idat_start));
p += sizeof(idat_start);
/* IDAT data:
zlib header: 0x78, 0x01 ,
h * scanline: row_header[] + width * r,g,b
checksum: adler32
*/
p_idat = p - 4;
/* zlib header */
*p++ = 0x78;
*p++ = 0x01;
adler = 1;
/* scanline data */
for (y = 0; y < h; y++) {
if (y == h - 1)
row_header[0] = 1; /* final block */
memcpy (p, row_header, sizeof(row_header));
p += sizeof(row_header);
p_row = p - 1;
row = data;
data += byte_stride / 4;
for (x = 0; x < w; x++) {
pixel = *row++;
*p++ = (pixel >> 16) & 0xff; /* red */
*p++ = (pixel >> 8) & 0xff; /* green */
*p++ = (pixel >> 0) & 0xff; /* blue */
}
adler = update_adler32(adler, p_row, p - p_row);
}
/* adler32 */
*(guint32 *)p = GUINT32_TO_BE(adler);
p += 4;
*(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
p += 4;
memcpy (p, iend, sizeof(iend));
p += sizeof(iend);
assert(p - png == png_size);
url = g_malloc (strlen("data:image/png;base64,") +
((png_size / 3 + 1) * 4 + 4) + 1);
strcpy (url, "data:image/png;base64,");
url_base64 = url + strlen("data:image/png;base64,");
outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
url_base64[outlen] = 0;
free (png);
return url;
}
static char *
to_png_rgba (int w, int h, int byte_stride, guint32 *data)
{
guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
/* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
/* bpp: */ 8, /* color type: */ 6,
0, 0, 0};
guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
gsize data_size, row_size;
char row_header[6];
guint8 *png, *p, *p_row, *p_idat;
guint32 *row;
unsigned long adler;
guint32 pixel;
gsize png_size;
int x, y;
char *url, *url_base64;
int state = 0, outlen;
int save = 0;
*(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
*(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
*(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
row_size = 1 + w * 4;
row_header[0] = 0;
row_header[1] = row_size & 0xff;
row_header[2] = (row_size >> 8) & 0xff;
row_header[3] = ~row_header[1];
row_header[4] = ~row_header[2];
row_header[5] = 0;
data_size = 2 + (6 + w * 4) * h + 4;
*(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
png = g_malloc (png_size);
p = png;
memcpy (p, header, sizeof(header));
p += sizeof(header);
memcpy (p, ihdr, sizeof(ihdr));
p += sizeof(ihdr);
memcpy (p, idat_start, sizeof(idat_start));
p += sizeof(idat_start);
/* IDAT data:
zlib header: 0x78, 0x01 ,
h * scanline: row_header[] + width * r,g,b,a
checksum: adler32
*/
p_idat = p - 4;
/* zlib header */
*p++ = 0x78;
*p++ = 0x01;
adler = 1;
/* scanline data */
for (y = 0; y < h; y++) {
if (y == h - 1)
row_header[0] = 1; /* final block */
memcpy (p, row_header, sizeof(row_header));
p += sizeof(row_header);
p_row = p - 1;
row = data;
data += byte_stride / 4;
for (x = 0; x < w; x++) {
pixel = *row++;
*p++ = (pixel >> 16) & 0xff; /* red */
*p++ = (pixel >> 8) & 0xff; /* green */
*p++ = (pixel >> 0) & 0xff; /* blue */
*p++ = (pixel >> 24) & 0xff; /* alpha */
}
adler = update_adler32(adler, p_row, p - p_row);
}
/* adler32 */
*(guint32 *)p = GUINT32_TO_BE(adler);
p += 4;
*(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
p += 4;
memcpy (p, iend, sizeof(iend));
p += sizeof(iend);
assert(p - png == png_size);
url = g_malloc (strlen("data:image/png;base64,") +
((png_size / 3 + 1) * 4 + 4) + 1);
strcpy (url, "data:image/png;base64,");
url_base64 = url + strlen("data:image/png;base64,");
outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
url_base64[outlen] = 0;
free (png);
return url;
}
#if 0
static char *
to_png_a (int w, int h, int byte_stride, guint8 *data)
{
guchar header[] = {137, 80, 78, 71, 13, 10, 26, 10};
guchar ihdr[13+12] = {0, 0, 0, 13, 'I', 'H', 'D', 'R',
/* w: */ 0, 0, 0, 0, /* h: */ 0,0,0,0,
/* bpp: */ 8, /* color type: */ 4,
0, 0, 0};
guchar idat_start[8] = { /* len: */0, 0, 0, 0, 'I', 'D', 'A', 'T' };
guchar iend[12] = {0, 0, 0, 0, 'I', 'E', 'N', 'D', 0xae, 0x42, 0x60, 0x82};
gsize data_size, row_size;
char row_header[6];
guint8 *png, *p, *p_row, *p_idat;
guint8 *row;
unsigned long adler;
guint32 pixel;
gsize png_size;
int x, y;
char *url, *url_base64;
int state = 0, outlen;
int save = 0;
*(guint32 *)&ihdr[8] = GUINT32_TO_BE(w);
*(guint32 *)&ihdr[12] = GUINT32_TO_BE(h);
*(guint32 *)&ihdr[21] = GUINT32_TO_BE(crc(&ihdr[4], 13 + 4));
row_size = 1 + w * 2;
row_header[0] = 0;
row_header[1] = row_size & 0xff;
row_header[2] = (row_size >> 8) & 0xff;
row_header[3] = ~row_header[1];
row_header[4] = ~row_header[2];
row_header[5] = 0;
data_size = 2 + (6 + w * 2) * h + 4;
*(guint32 *)&idat_start[0] = GUINT32_TO_BE(data_size);
png_size = sizeof(header) + sizeof(ihdr) + 12 + data_size + sizeof(iend);
png = g_malloc (png_size);
p = png;
memcpy (p, header, sizeof(header));
p += sizeof(header);
memcpy (p, ihdr, sizeof(ihdr));
p += sizeof(ihdr);
memcpy (p, idat_start, sizeof(idat_start));
p += sizeof(idat_start);
/* IDAT data:
zlib header: 0x78, 0x01 ,
h * scanline: row_header[] + width * r,g,b,a
checksum: adler32
*/
p_idat = p - 4;
/* zlib header */
*p++ = 0x78;
*p++ = 0x01;
adler = 1;
/* scanline data */
for (y = 0; y < h; y++) {
if (y == h - 1)
row_header[0] = 1; /* final block */
memcpy (p, row_header, sizeof(row_header));
p += sizeof(row_header);
p_row = p - 1;
row = data;
data += byte_stride / 4;
for (x = 0; x < w; x++) {
pixel = *row++;
*p++ = 0x00; /* gray */
*p++ = pixel; /* alpha */
}
adler = update_adler32(adler, p_row, p - p_row);
}
/* adler32 */
*(guint32 *)p = GUINT32_TO_BE(adler);
p += 4;
*(guint32 *)p = GUINT32_TO_BE(crc(p_idat, p - p_idat));
p += 4;
memcpy (p, iend, sizeof(iend));
p += sizeof(iend);
assert(p - png == png_size);
url = g_malloc (strlen("data:image/png;base64,") +
((png_size / 3 + 1) * 4 + 4) + 1);
strcpy (url, "data:image/png;base64,");
url_base64 = url + strlen("data:image/png;base64,");
outlen = g_base64_encode_step (png, png_size, FALSE, url_base64, &state, &save);
outlen += g_base64_encode_close (FALSE, url_base64 + outlen, &state, &save);
url_base64[outlen] = 0;
free (png);
return url;
}
#endif
/************************************************************************
* Basic I/O primitives *
************************************************************************/
struct BroadwayOutput {
int fd;
gzFile *zfd;
int error;
};
static void
broadway_output_write_raw (BroadwayOutput *output,
const void *buf, gsize count)
{
gssize res;
int errsave;
const char *ptr = (const char *)buf;
if (output->error)
return;
while (count > 0)
{
res = write(output->fd, ptr, count);
if (res == -1)
{
errsave = errno;
if (errsave == EINTR)
continue;
output->error = TRUE;
return;
}
if (res == 0)
{
output->error = TRUE;
return;
}
count -= res;
ptr += res;
}
}
static void
broadway_output_write (BroadwayOutput *output,
const void *buf, gsize count)
{
gssize res;
const char *ptr = (const char *)buf;
if (output->error)
return;
while (count > 0)
{
res = gzwrite(output->zfd, ptr, count);
if (res == -1)
{
output->error = TRUE;
return;
}
if (res == 0)
{
output->error = TRUE;
return;
}
count -= res;
ptr += res;
}
}
static void
broadway_output_write_header (BroadwayOutput *output)
{
char *header;
header =
"HTTP/1.1 200 OK\r\n"
"Content-type: multipart/x-mixed-replace;boundary=x\r\n"
"Content-Encoding: gzip\r\n"
"\r\n";
broadway_output_write_raw (output,
header, strlen (header));
}
static void
send_boundary (BroadwayOutput *output)
{
char *boundary =
"--x\r\n"
"\r\n";
broadway_output_write (output, boundary, strlen (boundary));
}
BroadwayOutput *
broadway_output_new(int fd)
{
BroadwayOutput *output;
output = g_new0 (BroadwayOutput, 1);
output->fd = fd;
broadway_output_write_header (output);
output->zfd = gzdopen(fd, "wb");
/* Need an initial multipart boundary */
send_boundary (output);
return output;
}
void
broadway_output_free (BroadwayOutput *output)
{
if (output->zfd)
gzclose (output->zfd);
else
close (output->fd);
free (output);
}
int
broadway_output_flush (BroadwayOutput *output)
{
send_boundary (output);
gzflush (output->zfd, Z_SYNC_FLUSH);
return !output->error;
}
/************************************************************************
* Core rendering operations *
************************************************************************/
void
broadway_output_copy_rectangles (BroadwayOutput *output, int id,
BroadwayRect *rects, int n_rects,
int dx, int dy)
{
char *buf;
int len, i, p;
len = 1 + 3 + 3 + 3*4*n_rects + 3 + 3;
buf = g_malloc (len);
p = 0;
buf[p++] = 'b';
base64_uint16(id, &buf[p]); p +=3;
base64_uint16(n_rects, &buf[p]); p +=3;
for (i = 0; i < n_rects; i++)
{
base64_uint16(rects[i].x, &buf[p]); p +=3;
base64_uint16(rects[i].y, &buf[p]); p +=3;
base64_uint16(rects[i].width, &buf[p]); p +=3;
base64_uint16(rects[i].height, &buf[p]); p +=3;
}
base64_uint16(dx, &buf[p]); p +=3;
base64_uint16(dy, &buf[p]); p +=3;
broadway_output_write (output, buf, len);
free (buf);
}
void
broadway_output_new_surface(BroadwayOutput *output, int id, int x, int y, int w, int h)
{
char buf[16];
buf[0] = 's';
base64_uint16(id, &buf[1]);
base64_uint16(x, &buf[4]);
base64_uint16(y, &buf[7]);
base64_uint16(w, &buf[10]);
base64_uint16(h, &buf[13]);
broadway_output_write (output, buf, 16);
}
void
broadway_output_show_surface(BroadwayOutput *output, int id)
{
char buf[4];
buf[0] = 'S';
base64_uint16(id, &buf[1]);
broadway_output_write (output, buf, 4);
}
void
broadway_output_hide_surface(BroadwayOutput *output, int id)
{
char buf[4];
buf[0] = 'H';
base64_uint16(id, &buf[1]);
broadway_output_write (output, buf, 4);
}
void
broadway_output_destroy_surface(BroadwayOutput *output, int id)
{
char buf[4];
buf[0] = 'd';
base64_uint16(id, &buf[1]);
broadway_output_write (output, buf, 4);
}
void
broadway_output_move_surface(BroadwayOutput *output, int id, int x, int y)
{
char buf[10];
buf[0] = 'm';
base64_uint16(id, &buf[1]);
base64_uint16(x, &buf[4]);
base64_uint16(y, &buf[7]);
broadway_output_write (output, buf, 10);
}
2010-11-23 11:52:10 +00:00
void
broadway_output_resize_surface(BroadwayOutput *output, int id, int w, int h)
2010-11-23 11:52:10 +00:00
{
char buf[10];
buf[0] = 'r';
base64_uint16(id, &buf[1]);
base64_uint16(w, &buf[4]);
base64_uint16(h, &buf[7]);
broadway_output_write (output, buf, 10);
2010-11-23 11:52:10 +00:00
}
void
broadway_output_put_rgb (BroadwayOutput *output, int id, int x, int y,
int w, int h, int byte_stride, void *data)
{
char buf[16];
gsize len;
char *url;
buf[0] = 'i';
base64_uint16(id, &buf[1]);
base64_uint16(x, &buf[4]);
base64_uint16(y, &buf[7]);
url = to_png_rgb (w, h, byte_stride, (guint32*)data);
len = strlen (url);
base64_uint32(len, &buf[10]);
broadway_output_write (output, buf, 16);
broadway_output_write (output, url, len);
free (url);
}
typedef struct {
int x1, y1;
int x2, y2;
} BroadwayBox;
static int
is_any_x_set (unsigned char *data,
int box_x1, int box_x2,
int x1, int x2, int y, int *x_set,
int byte_stride)
{
int w ;
guint32 *ptr;
if (x1 < box_x1)
x1 = box_x1;
if (x2 > box_x2)
x2 = box_x2;
w = x2 - x1;
if (w > 0)
{
ptr = (guint32 *)(data + y * byte_stride + x1 * 4);
while (w-- > 0)
{
if (*ptr != 0)
{
if (x_set)
*x_set = x1;
return 1;
}
ptr++;
x1++;
}
}
return 0;
}
#define EXTEND_X_FUZZ 10
#define EXTEND_Y_FUZZ 10
static int
extend_x_range (unsigned char *data,
int box_x1, int box_y1,
int box_x2, int box_y2,
int *x1, int *x2, int y,
int byte_stride)
{
int extended = 0;
int new_x;
while (is_any_x_set (data, box_x1, box_x2, *x1 - EXTEND_X_FUZZ, *x1, y, &new_x, byte_stride))
{
*x1 = new_x;
extended = 1;
}
while (is_any_x_set (data, box_x1, box_x2, *x2, *x2 + EXTEND_X_FUZZ, y, &new_x, byte_stride))
{
*x2 = new_x + 1;
extended = 1;
}
return extended;
}
static int
extend_y_range (unsigned char *data,
int box_x1, int box_y1,
int box_x2, int box_y2,
int x1, int x2, int *y,
int byte_stride)
{
int extended = 0;
int found_set;
int yy, y2;
while (*y < box_y2)
{
found_set = 0;
y2 = *y + EXTEND_Y_FUZZ;
if (y2 > box_y2)
y2 = box_y2;
for (yy = y2; yy > *y + 1; yy--)
{
if (is_any_x_set (data, box_x1, box_x2, x1, x2, yy - 1, NULL, byte_stride))
{
found_set = 1;
break;
}
}
if (!found_set)
break;
*y = yy;
extended = 1;
}
return extended;
}
static void
rgba_find_rects_extents (unsigned char *data,
int box_x1, int box_y1,
int box_x2, int box_y2,
int x, int y,
BroadwayBox *rect,
int byte_stride)
{
int x1, x2, y1, y2, yy;
int extended;
x1 = x;
x2 = x + 1;
y1 = y;
y2 = y + 1;
do
{
/* Expand maximally for all known rows */
do
{
extended = 0;
for (yy = y1; yy < y2; yy++)
extended |= extend_x_range (data,
box_x1, box_y1,
box_x2, box_y2,
&x1, &x2, yy,
byte_stride);
}
while (extended);
}
while (extend_y_range(data,
box_x1, box_y1,
box_x2, box_y2,
x1, x2, &y2,
byte_stride));
rect->x1 = x1;
rect->x2 = x2;
rect->y1 = y1;
rect->y2 = y2;
}
static void
rgba_find_rects_sub (unsigned char *data,
int box_x1, int box_y1,
int box_x2, int box_y2,
int byte_stride,
BroadwayBox **rects,
int *n_rects, int *alloc_rects)
{
guint32 *line;
BroadwayBox rect;
int x, y;
if (box_x1 == box_x2 || box_y1 == box_y2)
return;
for (y = box_y1; y < box_y2; y++)
{
line = (guint32 *)(data + y * byte_stride + box_x1 * 4);
for (x = box_x1; x < box_x2; x++)
{
if (*line != 0)
{
rgba_find_rects_extents (data,
box_x1, box_y1, box_x2, box_y2,
x, y, &rect, byte_stride);
if (*n_rects == *alloc_rects)
{
(*alloc_rects) *= 2;
*rects = g_renew (BroadwayBox, *rects, *alloc_rects);
}
(*rects)[*n_rects] = rect;
(*n_rects)++;
rgba_find_rects_sub (data,
box_x1, rect.y1,
rect.x1, rect.y2,
byte_stride,
rects, n_rects, alloc_rects);
rgba_find_rects_sub (data,
rect.x2, rect.y1,
box_x2, rect.y2,
byte_stride,
rects, n_rects, alloc_rects);
rgba_find_rects_sub (data,
box_x1, rect.y2,
box_x2, box_y2,
byte_stride,
rects, n_rects, alloc_rects);
return;
}
line++;
}
}
}
static BroadwayBox *
rgba_find_rects (unsigned char *data,
int w, int h, int byte_stride,
int *n_rects)
{
BroadwayBox *rects;
int alloc_rects;
alloc_rects = 20;
rects = g_new (BroadwayBox, alloc_rects);
*n_rects = 0;
rgba_find_rects_sub (data,
0, 0, w, h, byte_stride,
&rects, n_rects, &alloc_rects);
return rects;
}
void
broadway_output_put_rgba (BroadwayOutput *output, int id, int x, int y,
int w, int h, int byte_stride, void *data)
{
char buf[16];
gsize len;
char *url;
BroadwayBox *rects;
int i, n_rects;
guint8 *subdata;
rects = rgba_find_rects (data, w, h, byte_stride, &n_rects);
for (i = 0; i < n_rects; i++)
{
subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
buf[0] = 'i';
base64_uint16(id, &buf[1]);
base64_uint16(x + rects[i].x1, &buf[4]);
base64_uint16(y + rects[i].y1, &buf[7]);
url = to_png_rgba (rects[i].x2 - rects[i].x1,
rects[i].y2 - rects[i].y1,
byte_stride, (guint32*)subdata);
len = strlen (url);
base64_uint32(len, &buf[10]);
broadway_output_write (output, buf, 16);
broadway_output_write (output, url, len);
free (url);
}
free (rects);
}
#if 0
static void
send_image_a (BroadwayOutput *output, int id, int x, int y,
int w, int h, int byte_stride, guint8 *data)
{
char buf[16];
gsize len;
char *url;
buf[0] = 'i';
base64_uint16(id, &buf[1]);
base64_uint16(x, &buf[4]);
base64_uint16(y, &buf[7]);
url = to_png_a (w, h, byte_stride, data);
len = strlen (url);
base64_uint32(len, &buf[10]);
broadway_output_write (output, buf, 16);
broadway_output_write (output, url, len);
free (url);
}
#endif