35
35
#include <pthread.h>
36
36
37
37
/**
38
- * Sends the contents of the given dirty rectangle from the given layer using
39
- * lossless PNG compression . The resulting instructions will be sent over the
40
- * client-wide broadcast socket associated with the given layer . The graphical
41
- * contents sent will be pulled from the layer's last_frame buffer. If sending
42
- * the contents of a pending frame, that pending frame must have been copied
43
- * over to the last_frame buffer before calling this function.
38
+ * Returns a new Cairo surface representing the contents of the given dirty
39
+ * rectangle from the given layer . The returned surface must eventually be
40
+ * freed with a call to cairo_surface_destroy() . The graphical contents will be
41
+ * referenced from the layer's last_frame buffer. If sending the contents of a
42
+ * pending frame, that pending frame must have been copied over to the
43
+ * last_frame buffer before calling this function.
44
44
*
45
45
* @param display_layer
46
- * The layer whose data should be sent to connected users .
46
+ * The layer whose data should be referenced by the returned Cairo surface .
47
47
*
48
48
* @param dirty
49
- * The region of the layer that should be sent.
49
+ * The region of the layer that should be referenced by the returned Cairo
50
+ * surface.
51
+ *
52
+ * @return
53
+ * A new Cairo surface that points to the given rectangle of image data
54
+ * from the last_frame buffer of the given layer. This surface must
55
+ * eventually be freed with a call to cairo_surface_destroy().
50
56
*/
51
- static void LFR_guac_display_layer_flush_to_png (guac_display_layer * display_layer ,
57
+ static cairo_surface_t * LFR_guac_display_layer_cairo_rect (guac_display_layer * display_layer ,
52
58
guac_rect * dirty ) {
53
59
54
- guac_display * display = display_layer -> display ;
55
- guac_client * client = display -> client ;
56
- guac_socket * socket = client -> socket ;
57
- const guac_layer * layer = display_layer -> layer ;
58
-
59
60
/* Get Cairo surface covering dirty rect */
60
61
unsigned char * buffer = GUAC_DISPLAY_LAYER_STATE_MUTABLE_BUFFER (display_layer -> last_frame , * dirty );
61
62
cairo_surface_t * rect ;
@@ -67,12 +68,39 @@ static void LFR_guac_display_layer_flush_to_png(guac_display_layer* display_laye
67
68
guac_rect_height (dirty ), display_layer -> last_frame .buffer_stride );
68
69
69
70
/* Otherwise ARGB32 is needed, and the destination must be cleared */
70
- else {
71
-
71
+ else
72
72
rect = cairo_image_surface_create_for_data (buffer ,
73
73
CAIRO_FORMAT_ARGB32 , guac_rect_width (dirty ),
74
74
guac_rect_height (dirty ), display_layer -> last_frame .buffer_stride );
75
75
76
+ return rect ;
77
+
78
+ }
79
+
80
+ /**
81
+ * Sends instructions over the Guacamole connection to clear the given
82
+ * rectangle of the given layer if that layer is non-opaque. This is necessary
83
+ * prior to sending image data to layers with alpha transparency, as image data
84
+ * from multiple updates will otherwise be composited together.
85
+ *
86
+ * @param display_layer
87
+ * The layer that should possibly be cleared in preparation for a future
88
+ * drawing operation.
89
+ *
90
+ * @param dirty
91
+ * The rectangular region of the drawing operation.
92
+ */
93
+ static void guac_display_layer_clear_non_opaque (guac_display_layer * display_layer ,
94
+ guac_rect * dirty ) {
95
+
96
+ guac_display * display = display_layer -> display ;
97
+ const guac_layer * layer = display_layer -> layer ;
98
+
99
+ guac_client * client = display -> client ;
100
+ guac_socket * socket = client -> socket ;
101
+
102
+ if (!display_layer -> opaque ) {
103
+
76
104
/* Clear destination rect first */
77
105
pthread_mutex_lock (& display -> op_path_lock );
78
106
guac_protocol_send_rect (socket , layer ,
@@ -84,12 +112,6 @@ static void LFR_guac_display_layer_flush_to_png(guac_display_layer* display_laye
84
112
85
113
}
86
114
87
- /* Send PNG for rect */
88
- guac_client_stream_png (client , socket , GUAC_COMP_OVER ,
89
- layer , dirty -> left , dirty -> top , rect );
90
-
91
- cairo_surface_destroy (rect );
92
-
93
115
}
94
116
95
117
/**
@@ -122,108 +144,6 @@ static int guac_display_suggest_quality(guac_client* client) {
122
144
123
145
}
124
146
125
- /**
126
- * Sends the contents of the given dirty rectangle from the given layer using
127
- * lossy JPEG compression. The resulting instructions will be sent over the
128
- * client-wide broadcast socket associated with the given layer. The graphical
129
- * contents sent will be pulled from the layer's last_frame buffer. If sending
130
- * the contents of a pending frame, that pending frame must have been copied
131
- * over to the last_frame buffer before calling this function.
132
- *
133
- * @param layer
134
- * The layer whose data should be sent to connected users.
135
- *
136
- * @param dirty
137
- * The region of the layer that should be sent.
138
- */
139
- static void LFR_guac_display_layer_flush_to_jpeg (guac_display_layer * display_layer ,
140
- guac_rect * dirty ) {
141
-
142
- guac_display * display = display_layer -> display ;
143
- guac_client * client = display -> client ;
144
- guac_socket * socket = client -> socket ;
145
- const guac_layer * layer = display_layer -> layer ;
146
-
147
- guac_rect max = {
148
- .left = 0 ,
149
- .top = 0 ,
150
- .right = display_layer -> last_frame .width ,
151
- .bottom = display_layer -> last_frame .height
152
- };
153
-
154
- /* Expand the dirty rect size to fit in a grid with cells equal to the
155
- * minimum JPEG block size */
156
- guac_rect_align (dirty , GUAC_SURFACE_JPEG_BLOCK_SIZE );
157
- guac_rect_constrain (dirty , & max );
158
-
159
- /* Get Cairo surface covering dirty rect */
160
- unsigned char * buffer = GUAC_DISPLAY_LAYER_STATE_MUTABLE_BUFFER (display_layer -> last_frame , * dirty );
161
- cairo_surface_t * rect = cairo_image_surface_create_for_data (buffer ,
162
- CAIRO_FORMAT_RGB24 , guac_rect_width (dirty ),
163
- guac_rect_height (dirty ), display_layer -> last_frame .buffer_stride );
164
-
165
- /* Send JPEG for rect */
166
- guac_client_stream_jpeg (client , socket , GUAC_COMP_OVER , layer ,
167
- dirty -> left , dirty -> top , rect ,
168
- guac_display_suggest_quality (client ));
169
-
170
- cairo_surface_destroy (rect );
171
-
172
- }
173
-
174
- /**
175
- * Sends the contents of the given dirty rectangle from the given layer using
176
- * WebP compression. Whether that WebP compression is lossless depends on the
177
- * lossless setting of the layer's last frame. The resulting instructions will
178
- * be sent over the client-wide broadcast socket associated with the given
179
- * layer. The graphical contents sent will be pulled from the layer's
180
- * last_frame buffer. If sending the contents of a pending frame, that pending
181
- * frame must have been copied over to the last_frame buffer before calling
182
- * this function.
183
- *
184
- * @param layer
185
- * The layer whose data should be sent to connected users.
186
- *
187
- * @param dirty
188
- * The region of the layer that should be sent.
189
- */
190
- static void LFR_guac_display_layer_flush_to_webp (guac_display_layer * display_layer ,
191
- guac_rect * dirty ) {
192
-
193
- guac_display * display = display_layer -> display ;
194
- guac_client * client = display -> client ;
195
- guac_socket * socket = client -> socket ;
196
- const guac_layer * layer = display_layer -> layer ;
197
-
198
- guac_rect max = {
199
- .left = 0 ,
200
- .top = 0 ,
201
- .right = display_layer -> last_frame .width ,
202
- .bottom = display_layer -> last_frame .height
203
- };
204
-
205
- /* Expand the dirty rect size to fit in a grid with cells equal to the
206
- * minimum WebP block size */
207
- guac_rect_align (dirty , GUAC_SURFACE_WEBP_BLOCK_SIZE );
208
- guac_rect_constrain (dirty , & max );
209
-
210
- /* Get Cairo surface covering dirty rect */
211
- unsigned char * buffer = GUAC_DISPLAY_LAYER_STATE_MUTABLE_BUFFER (display_layer -> last_frame , * dirty );
212
- cairo_surface_t * rect = cairo_image_surface_create_for_data (buffer ,
213
- display_layer -> opaque ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32 ,
214
- guac_rect_width (dirty ), guac_rect_height (dirty ),
215
- display_layer -> last_frame .buffer_stride );
216
-
217
- /* Send WebP for rect */
218
- guac_client_stream_webp (client , socket , GUAC_COMP_OVER , layer ,
219
- dirty -> left , dirty -> top , rect ,
220
- guac_display_suggest_quality (client ),
221
- display_layer -> last_frame .lossless ? 1 : 0 );
222
-
223
- cairo_surface_destroy (rect );
224
-
225
- }
226
-
227
147
/**
228
148
* Guesses whether a rectangle within a particular layer would be better
229
149
* compressed as PNG or using a lossy format like JPEG. Positive values
@@ -372,6 +292,7 @@ void* guac_display_worker_thread(void* data) {
372
292
373
293
guac_display * display = (guac_display * ) data ;
374
294
guac_client * client = display -> client ;
295
+ guac_socket * socket = client -> socket ;
375
296
376
297
guac_display_plan_operation op ;
377
298
while (guac_fifo_dequeue_and_lock (& display -> ops , & op )) {
@@ -416,18 +337,33 @@ void* guac_display_worker_thread(void* data) {
416
337
* the size of the original image. Compositing via Guacamole
417
338
* protocol instructions can reassemble those stages. */
418
339
340
+ cairo_surface_t * rect = LFR_guac_display_layer_cairo_rect (display_layer , dirty );
341
+ const guac_layer * layer = display_layer -> layer ;
342
+
343
+ /* Clear relevant rect of destination layer if necessary to
344
+ * ensure fresh data is not drawn on top of old data for layers
345
+ * with alpha transparency */
346
+ guac_display_layer_clear_non_opaque (display_layer , dirty );
347
+
419
348
/* Prefer WebP when reasonable */
420
349
if (LFR_guac_display_layer_should_use_webp (display_layer , dirty , framerate ))
421
- LFR_guac_display_layer_flush_to_webp (display_layer , dirty );
350
+ guac_client_stream_webp (client , socket , GUAC_COMP_OVER , layer ,
351
+ dirty -> left , dirty -> top , rect ,
352
+ guac_display_suggest_quality (client ),
353
+ display_layer -> last_frame .lossless ? 1 : 0 );
422
354
423
355
/* If not WebP, JPEG is the next best (lossy) choice */
424
356
else if (display_layer -> opaque && LFR_guac_display_layer_should_use_jpeg (display_layer , dirty , framerate ))
425
- LFR_guac_display_layer_flush_to_jpeg (display_layer , dirty );
357
+ guac_client_stream_jpeg (client , socket , GUAC_COMP_OVER , layer ,
358
+ dirty -> left , dirty -> top , rect ,
359
+ guac_display_suggest_quality (client ));
426
360
427
361
/* Use PNG if no lossy formats are appropriate */
428
362
else
429
- LFR_guac_display_layer_flush_to_png (display_layer , dirty );
363
+ guac_client_stream_png (client , socket , GUAC_COMP_OVER ,
364
+ layer , dirty -> left , dirty -> top , rect );
430
365
366
+ cairo_surface_destroy (rect );
431
367
break ;
432
368
433
369
case GUAC_DISPLAY_PLAN_OPERATION_COPY :
0 commit comments