836 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			836 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <assert.h>
 | |
| #include <errno.h>
 | |
| #include <cairo.h>
 | |
| 
 | |
| #include "broadway-output.h"
 | |
| 
 | |
| /************************************************************************
 | |
|  *                Base64 functions                                      *
 | |
|  ************************************************************************/
 | |
| 
 | |
| static const char base64_alphabet[] =
 | |
| 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 | |
| 
 | |
| #if 0 /* Unused for now */
 | |
| 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 png data: uris         *
 | |
|  ***********************************************************/
 | |
| 
 | |
| static cairo_status_t
 | |
| write_png_data (void		  *closure,
 | |
| 		const unsigned char *data,
 | |
| 		unsigned int	   data_len)
 | |
| {
 | |
|   GString *buf = closure;
 | |
| 
 | |
|   g_string_append_len (buf,  (char *)data, data_len);
 | |
| 
 | |
|   return CAIRO_STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static void
 | |
| to_png_rgb (GString *buf, int w, int h, int byte_stride, guint32 *data)
 | |
| {
 | |
|   cairo_surface_t *surface;
 | |
| 
 | |
|   surface = cairo_image_surface_create_for_data ((guchar *)data,
 | |
| 						 CAIRO_FORMAT_RGB24, w, h, byte_stride);
 | |
| 
 | |
|   cairo_surface_write_to_png_stream (surface, write_png_data, buf);
 | |
|   cairo_surface_destroy (surface);
 | |
| }
 | |
| 
 | |
| static void
 | |
| to_png_rgba (GString *buf, int w, int h, int byte_stride, guint32 *data)
 | |
| {
 | |
|   cairo_surface_t *surface;
 | |
| 
 | |
|   surface = cairo_image_surface_create_for_data ((guchar *)data,
 | |
| 						 CAIRO_FORMAT_ARGB32, w, h, byte_stride);
 | |
| 
 | |
|   cairo_surface_write_to_png_stream (surface, write_png_data, buf);
 | |
|   cairo_surface_destroy (surface);
 | |
| }
 | |
| 
 | |
| struct PngTarget {
 | |
|   GString *buf;
 | |
|   int state;
 | |
|   int save;
 | |
| };
 | |
| 
 | |
| static cairo_status_t
 | |
| write_png_url (void		  *closure,
 | |
| 	       const unsigned char *data,
 | |
| 	       unsigned int	   data_len)
 | |
| {
 | |
|   struct PngTarget *target = closure;
 | |
|   gsize res, old_len;
 | |
| 
 | |
|   old_len = target->buf->len;
 | |
|   g_string_set_size (target->buf,
 | |
| 		     old_len + (data_len / 3 + 1) * 4 + 4);
 | |
| 
 | |
|   res = g_base64_encode_step (data, data_len, FALSE,
 | |
| 			      target->buf->str + old_len,
 | |
| 			      &target->state, &target->save);
 | |
| 
 | |
|   g_string_set_size (target->buf,  old_len + res);
 | |
| 
 | |
|   return CAIRO_STATUS_SUCCESS;
 | |
| }
 | |
| 
 | |
| static void
 | |
| to_png_url_rgb (GString *buf, int w, int h, int byte_stride, guint32 *data)
 | |
| {
 | |
|   cairo_surface_t *surface;
 | |
|   struct PngTarget target;
 | |
|   gsize res, old_len;
 | |
| 
 | |
|   target.buf = buf;
 | |
|   target.state = 0;
 | |
|   target.save = 0;
 | |
| 
 | |
|   g_string_append (buf, "data:image/png;base64,");
 | |
| 
 | |
|   surface = cairo_image_surface_create_for_data ((guchar *)data,
 | |
| 						 CAIRO_FORMAT_RGB24, w, h, byte_stride);
 | |
| 
 | |
|   cairo_surface_write_to_png_stream (surface, write_png_url, &target);
 | |
|   cairo_surface_destroy (surface);
 | |
| 
 | |
|   old_len = buf->len;
 | |
| 
 | |
|   g_string_set_size (buf, old_len + 4);
 | |
|   res = g_base64_encode_close (FALSE,
 | |
| 			       buf->str + old_len,
 | |
| 			       &target.state, &target.save);
 | |
|   g_string_set_size (buf, old_len + res);
 | |
| }
 | |
| 
 | |
| static void
 | |
| to_png_url_rgba (GString *buf, int w, int h, int byte_stride, guint32 *data)
 | |
| {
 | |
|   cairo_surface_t *surface;
 | |
|   struct PngTarget target;
 | |
|   gsize res, old_len;
 | |
| 
 | |
|   target.buf = buf;
 | |
|   target.state = 0;
 | |
|   target.save = 0;
 | |
| 
 | |
|   g_string_append (buf, "data:image/png;base64,");
 | |
| 
 | |
|   surface = cairo_image_surface_create_for_data ((guchar *)data,
 | |
| 						 CAIRO_FORMAT_ARGB32, w, h, byte_stride);
 | |
| 
 | |
|   cairo_surface_write_to_png_stream (surface, write_png_url, &target);
 | |
|   cairo_surface_destroy (surface);
 | |
| 
 | |
|   old_len = buf->len;
 | |
| 
 | |
|   g_string_set_size (buf, old_len + 4);
 | |
|   res = g_base64_encode_close (FALSE,
 | |
| 			       buf->str + old_len,
 | |
| 			       &target.state, &target.save);
 | |
|   g_string_set_size (buf, old_len + res);
 | |
| }
 | |
| 
 | |
| #if 0
 | |
| static char *
 | |
| to_png_a (int w, int h, int byte_stride, guint8 *data)
 | |
| {
 | |
|   cairo_surface_t *surface;
 | |
|   struct PngTarget target;
 | |
|   gsize res, old_len;
 | |
| 
 | |
|   target.url = g_string_new ("data:image/png;base64,");
 | |
|   target.state = 0;
 | |
|   target.save = 0;
 | |
| 
 | |
|   surface = cairo_image_surface_create_for_data (data,
 | |
| 						 CAIRO_FORMAT_A8, w, h, byte_stride);
 | |
| 
 | |
|   cairo_surface_write_to_png_stream (surface, write_png_url, &target);
 | |
| 
 | |
|   old_len = target.url->len;
 | |
| 
 | |
|   g_string_set_size (target.url, old_len + 4);
 | |
|   res = g_base64_encode_close (FALSE,
 | |
| 			       target.url->str + old_len,
 | |
| 			       &target.state, &target.save);
 | |
|   g_string_set_size (target.url, old_len + res);
 | |
| 
 | |
|   return g_string_free (target.url, FALSE);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /************************************************************************
 | |
|  *                Basic I/O primitives                                  *
 | |
|  ************************************************************************/
 | |
| 
 | |
| struct BroadwayOutput {
 | |
|   GOutputStream *out;
 | |
|   GString *buf;
 | |
|   int error;
 | |
|   guint32 serial;
 | |
|   gboolean proto_v7_plus;
 | |
|   gboolean binary;
 | |
| };
 | |
| 
 | |
| static void
 | |
| broadway_output_send_cmd (BroadwayOutput *output,
 | |
| 			  gboolean fin, BroadwayWSOpCode code,
 | |
| 			  const void *buf, gsize count)
 | |
| {
 | |
|   gboolean mask = FALSE;
 | |
|   guchar header[16];
 | |
|   size_t p;
 | |
| 
 | |
|   gboolean mid_header = count > 125 && count <= 65535;
 | |
|   gboolean long_header = count > 65535;
 | |
| 
 | |
|   /* NB. big-endian spec => bit 0 == MSB */
 | |
|   header[0] = ( (fin ? 0x80 : 0) | (code & 0x0f) );
 | |
|   header[1] = ( (mask ? 0x80 : 0) |
 | |
|                 (mid_header ? 126 : long_header ? 127 : count) );
 | |
|   p = 2;
 | |
|   if (mid_header)
 | |
|     {
 | |
|       *(guint16 *)(header + p) = GUINT16_TO_BE( (guint16)count );
 | |
|       p += 2;
 | |
|     }
 | |
|   else if (long_header)
 | |
|     {
 | |
|       *(guint64 *)(header + p) = GUINT64_TO_BE( count );
 | |
|       p += 8;
 | |
|     }
 | |
|   // FIXME: if we are paranoid we should 'mask' the data
 | |
|   // FIXME: we should really emit these as a single write
 | |
|   g_output_stream_write_all (output->out, header, p, NULL, NULL, NULL);
 | |
|   g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| broadway_output_send_cmd_pre_v7 (BroadwayOutput *output,
 | |
| 				 const void *buf, gsize count)
 | |
| {
 | |
|   g_output_stream_write_all (output->out, "\0", 1, NULL, NULL, NULL);
 | |
|   g_output_stream_write_all (output->out, buf, count, NULL, NULL, NULL);
 | |
|   g_output_stream_write_all (output->out, "\xff", 1, NULL, NULL, NULL);
 | |
| }
 | |
| 
 | |
| void broadway_output_pong (BroadwayOutput *output)
 | |
| {
 | |
|   if (output->proto_v7_plus)
 | |
|     broadway_output_send_cmd (output, TRUE, BROADWAY_WS_CNX_PONG, NULL, 0);
 | |
| }
 | |
| 
 | |
| int
 | |
| broadway_output_flush (BroadwayOutput *output)
 | |
| {
 | |
|   if (output->buf->len == 0)
 | |
|     return TRUE;
 | |
| 
 | |
|   if (!output->proto_v7_plus)
 | |
|     broadway_output_send_cmd_pre_v7 (output, output->buf->str, output->buf->len);
 | |
|   else if (output->binary)
 | |
|     broadway_output_send_cmd (output, TRUE, BROADWAY_WS_BINARY,
 | |
| 			      output->buf->str, output->buf->len);
 | |
|   else
 | |
|     broadway_output_send_cmd (output, TRUE, BROADWAY_WS_TEXT,
 | |
| 			      output->buf->str, output->buf->len);
 | |
| 
 | |
|   g_string_set_size (output->buf, 0);
 | |
| 
 | |
|   return !output->error;
 | |
| 
 | |
| }
 | |
| 
 | |
| BroadwayOutput *
 | |
| broadway_output_new (GOutputStream *out, guint32 serial,
 | |
| 		     gboolean proto_v7_plus, gboolean binary)
 | |
| {
 | |
|   BroadwayOutput *output;
 | |
| 
 | |
|   output = g_new0 (BroadwayOutput, 1);
 | |
| 
 | |
|   output->out = g_object_ref (out);
 | |
|   output->buf = g_string_new ("");
 | |
|   output->serial = serial;
 | |
|   output->proto_v7_plus = proto_v7_plus;
 | |
|   output->binary = binary;
 | |
| 
 | |
|   return output;
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_free (BroadwayOutput *output)
 | |
| {
 | |
|   g_object_unref (output->out);
 | |
|   free (output);
 | |
| }
 | |
| 
 | |
| guint32
 | |
| broadway_output_get_next_serial (BroadwayOutput *output)
 | |
| {
 | |
|   return output->serial;
 | |
| }
 | |
| 
 | |
| 
 | |
| /************************************************************************
 | |
|  *                     Core rendering operations                        *
 | |
|  ************************************************************************/
 | |
| 
 | |
| static void
 | |
| append_char (BroadwayOutput *output, char c)
 | |
| {
 | |
|   g_string_append_c (output->buf, c);
 | |
| }
 | |
| 
 | |
| static void
 | |
| append_bool (BroadwayOutput *output, gboolean val)
 | |
| {
 | |
|   if (output->binary)
 | |
|     g_string_append_c (output->buf, val ? 1: 0);
 | |
|   else
 | |
|     g_string_append_c (output->buf, val ? '1': '0');
 | |
| }
 | |
| 
 | |
| static void
 | |
| append_flags (BroadwayOutput *output, guint32 val)
 | |
| {
 | |
|   if (output->binary)
 | |
|     g_string_append_c (output->buf, val);
 | |
|   else
 | |
|     g_string_append_c (output->buf, val + '0');
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| append_uint16 (BroadwayOutput *output, guint32 v)
 | |
| {
 | |
|   gsize old_len = output->buf->len;
 | |
|   guint8 *buf;
 | |
| 
 | |
|   if (output->binary)
 | |
|     {
 | |
|       g_string_set_size (output->buf, old_len + 2);
 | |
|       buf = (guint8 *)output->buf->str + old_len;
 | |
|       buf[0] = (v >> 0) & 0xff;
 | |
|       buf[1] = (v >> 8) & 0xff;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       g_string_set_size (output->buf, old_len + 3);
 | |
|       base64_uint16 (v, output->buf->str + old_len);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| append_uint32 (BroadwayOutput *output, guint32 v)
 | |
| {
 | |
|   gsize old_len = output->buf->len;
 | |
|   guint8 *buf;
 | |
| 
 | |
|   if (output->binary)
 | |
|     {
 | |
|       g_string_set_size (output->buf, old_len + 4);
 | |
|       buf = (guint8 *)output->buf->str + old_len;
 | |
|       buf[0] = (v >> 0) & 0xff;
 | |
|       buf[1] = (v >> 8) & 0xff;
 | |
|       buf[2] = (v >> 16) & 0xff;
 | |
|       buf[3] = (v >> 24) & 0xff;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       g_string_set_size (output->buf, old_len + 6);
 | |
|       base64_uint32 (v, output->buf->str + old_len);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| overwrite_uint32 (BroadwayOutput *output, gsize pos, guint32 v)
 | |
| {
 | |
|   if (output->binary)
 | |
|     {
 | |
|       guint8 *buf = (guint8 *)output->buf->str + pos;
 | |
| 
 | |
|       buf[0] = (v >> 0) & 0xff;
 | |
|       buf[1] = (v >> 8) & 0xff;
 | |
|       buf[2] = (v >> 16) & 0xff;
 | |
|       buf[3] = (v >> 24) & 0xff;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       base64_uint32 (v, output->buf->str + pos);
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| static void
 | |
| write_header(BroadwayOutput *output, char op)
 | |
| {
 | |
|   append_char (output, op);
 | |
|   append_uint32 (output, output->serial++);
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_copy_rectangles (BroadwayOutput *output,  int id,
 | |
| 				 BroadwayRect *rects, int n_rects,
 | |
| 				 int dx, int dy)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   write_header (output, BROADWAY_OP_COPY_RECTANGLES);
 | |
|   append_uint16 (output, id);
 | |
|   append_uint16 (output, n_rects);
 | |
|   for (i = 0; i < n_rects; i++)
 | |
|     {
 | |
|       append_uint16 (output, rects[i].x);
 | |
|       append_uint16 (output, rects[i].y);
 | |
|       append_uint16 (output, rects[i].width);
 | |
|       append_uint16 (output, rects[i].height);
 | |
|     }
 | |
|   append_uint16 (output, dx);
 | |
|   append_uint16 (output, dy);
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_grab_pointer (BroadwayOutput *output,
 | |
| 			      int id,
 | |
| 			      gboolean owner_event)
 | |
| {
 | |
|   write_header (output, BROADWAY_OP_GRAB_POINTER);
 | |
|   append_uint16 (output, id);
 | |
|   append_bool (output, owner_event);
 | |
| }
 | |
| 
 | |
| guint32
 | |
| broadway_output_ungrab_pointer (BroadwayOutput *output)
 | |
| {
 | |
|   guint32 serial;
 | |
| 
 | |
|   serial = output->serial;
 | |
|   write_header (output, BROADWAY_OP_UNGRAB_POINTER);
 | |
| 
 | |
|   return serial;
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_new_surface(BroadwayOutput *output,
 | |
| 			    int id, int x, int y, int w, int h,
 | |
| 			    gboolean is_temp)
 | |
| {
 | |
|   write_header (output, BROADWAY_OP_NEW_SURFACE);
 | |
|   append_uint16 (output, id);
 | |
|   append_uint16 (output, x);
 | |
|   append_uint16 (output, y);
 | |
|   append_uint16 (output, w);
 | |
|   append_uint16 (output, h);
 | |
|   append_bool (output, is_temp);
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_show_surface(BroadwayOutput *output,  int id)
 | |
| {
 | |
|   write_header (output, BROADWAY_OP_SHOW_SURFACE);
 | |
|   append_uint16 (output, id);
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_hide_surface(BroadwayOutput *output,  int id)
 | |
| {
 | |
|   write_header (output, BROADWAY_OP_HIDE_SURFACE);
 | |
|   append_uint16 (output, id);
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_destroy_surface(BroadwayOutput *output,  int id)
 | |
| {
 | |
|   write_header (output, BROADWAY_OP_DESTROY_SURFACE);
 | |
|   append_uint16 (output, id);
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| broadway_output_move_resize_surface (BroadwayOutput *output,
 | |
| 				     int             id,
 | |
| 				     gboolean        has_pos,
 | |
| 				     int             x,
 | |
| 				     int             y,
 | |
| 				     gboolean        has_size,
 | |
| 				     int             w,
 | |
| 				     int             h)
 | |
| {
 | |
|   int val;
 | |
| 
 | |
|   if (!has_pos && !has_size)
 | |
|     return;
 | |
| 
 | |
|   write_header (output, BROADWAY_OP_MOVE_RESIZE);
 | |
|   val = (!!has_pos) | ((!!has_size) << 1);
 | |
|   append_uint16 (output, id);
 | |
|   append_flags (output, val);
 | |
|   if (has_pos)
 | |
|     {
 | |
|       append_uint16 (output, x);
 | |
|       append_uint16 (output, y);
 | |
|     }
 | |
|   if (has_size)
 | |
|     {
 | |
|       append_uint16 (output, w);
 | |
|       append_uint16 (output, h);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_set_transient_for (BroadwayOutput *output,
 | |
| 				   int             id,
 | |
| 				   int             parent_id)
 | |
| {
 | |
|   write_header (output, BROADWAY_OP_SET_TRANSIENT_FOR);
 | |
|   append_uint16 (output, id);
 | |
|   append_uint16 (output, parent_id);
 | |
| }
 | |
| 
 | |
| 
 | |
| void
 | |
| broadway_output_put_rgb (BroadwayOutput *output,  int id, int x, int y,
 | |
| 			 int w, int h, int byte_stride, void *data)
 | |
| {
 | |
|   gsize size_start, image_start, len;
 | |
| 
 | |
|   write_header (output, BROADWAY_OP_PUT_RGB);
 | |
| 
 | |
|   append_uint16 (output, id);
 | |
|   append_uint16 (output, x);
 | |
|   append_uint16 (output, y);
 | |
| 
 | |
|   size_start = output->buf->len;
 | |
|   append_uint32 (output, 0);
 | |
| 
 | |
|   image_start = output->buf->len;
 | |
|   if (output->binary)
 | |
|     to_png_rgb (output->buf, w, h, byte_stride, (guint32*)data);
 | |
|   else
 | |
|     to_png_url_rgb (output->buf, w, h, byte_stride, (guint32*)data);
 | |
| 
 | |
|   len = output->buf->len - image_start;
 | |
| 
 | |
|   overwrite_uint32 (output, size_start, len);
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
|   BroadwayBox *rects;
 | |
|   int i, n_rects;
 | |
|   gsize size_start, image_start, len;
 | |
| 
 | |
|   rects = rgba_find_rects (data, w, h, byte_stride, &n_rects);
 | |
| 
 | |
|   for (i = 0; i < n_rects; i++)
 | |
|     {
 | |
|       guint8 *subdata;
 | |
| 
 | |
|       write_header (output, BROADWAY_OP_PUT_RGB);
 | |
|       append_uint16 (output, id);
 | |
|       append_uint16 (output, x + rects[i].x1);
 | |
|       append_uint16 (output, y + rects[i].y1);
 | |
| 
 | |
|       size_start = output->buf->len;
 | |
|       append_uint32 (output, 0);
 | |
| 
 | |
|       image_start = output->buf->len;
 | |
| 
 | |
|       subdata = (guint8 *)data + rects[i].x1 * 4 + rects[i].y1 * byte_stride;
 | |
|       if (output->binary)
 | |
| 	to_png_rgba (output->buf, rects[i].x2 - rects[i].x1,
 | |
| 		     rects[i].y2 - rects[i].y1,
 | |
| 		     byte_stride, (guint32*)subdata);
 | |
|       else
 | |
| 	to_png_url_rgba (output->buf, rects[i].x2 - rects[i].x1,
 | |
| 			 rects[i].y2 - rects[i].y1,
 | |
| 			 byte_stride, (guint32*)subdata);
 | |
| 
 | |
|       len = output->buf->len - image_start;
 | |
| 
 | |
|       overwrite_uint32 (output, size_start, len);
 | |
|     }
 | |
| 
 | |
|   free (rects);
 | |
| }
 | |
| 
 | |
| void
 | |
| broadway_output_surface_flush (BroadwayOutput *output,
 | |
| 			       int             id)
 | |
| {
 | |
|   write_header (output, BROADWAY_OP_FLUSH);
 | |
|   append_uint16 (output, id);
 | |
| }
 | 
