surface.c (62639B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "config.h" 21#include "common/rect.h" 22#include "common/surface.h" 23 24#include <cairo/cairo.h> 25#include <guacamole/client.h> 26#include <guacamole/layer.h> 27#include <guacamole/mem.h> 28#include <guacamole/protocol.h> 29#include <guacamole/socket.h> 30#include <guacamole/timestamp.h> 31#include <guacamole/user.h> 32 33#include <pthread.h> 34#include <stdlib.h> 35#include <stdint.h> 36#include <string.h> 37 38/** 39 * The width of an update which should be considered negible and thus 40 * trivial overhead compared ot the cost of two updates. 41 */ 42#define GUAC_SURFACE_NEGLIGIBLE_WIDTH 64 43 44/** 45 * The height of an update which should be considered negible and thus 46 * trivial overhead compared ot the cost of two updates. 47 */ 48#define GUAC_SURFACE_NEGLIGIBLE_HEIGHT 64 49 50/** 51 * The proportional increase in cost contributed by transfer and processing of 52 * image data, compared to processing an equivalent amount of client-side 53 * data. 54 */ 55#define GUAC_SURFACE_DATA_FACTOR 16 56 57/** 58 * The base cost of every update. Each update should be considered to have 59 * this starting cost, plus any additional cost estimated from its 60 * content. 61 */ 62#define GUAC_SURFACE_BASE_COST 4096 63 64/** 65 * An increase in cost is negligible if it is less than 66 * 1/GUAC_SURFACE_NEGLIGIBLE_INCREASE of the old cost. 67 */ 68#define GUAC_SURFACE_NEGLIGIBLE_INCREASE 4 69 70/** 71 * If combining an update because it appears to be follow a fill pattern, 72 * the combined cost must not exceed 73 * GUAC_SURFACE_FILL_PATTERN_FACTOR * (total uncombined cost). 74 */ 75#define GUAC_SURFACE_FILL_PATTERN_FACTOR 3 76 77/* Define cairo_format_stride_for_width() if missing */ 78#ifndef HAVE_CAIRO_FORMAT_STRIDE_FOR_WIDTH 79#define cairo_format_stride_for_width(format, width) (width*4) 80#endif 81 82/** 83 * The framerate which, if exceeded, indicates that JPEG is preferred. 84 */ 85#define GUAC_COMMON_SURFACE_JPEG_FRAMERATE 3 86 87/** 88 * Minimum JPEG bitmap size (area). If the bitmap is smaller than this threshold, 89 * it should be compressed as a PNG image to avoid the JPEG compression tax. 90 */ 91#define GUAC_SURFACE_JPEG_MIN_BITMAP_SIZE 4096 92 93/** 94 * The JPEG compression min block size. This defines the optimal rectangle block 95 * size factor for JPEG compression. Usually 8x8 would suffice, but use 16 to 96 * reduce the occurrence of ringing artifacts further. 97 */ 98#define GUAC_SURFACE_JPEG_BLOCK_SIZE 16 99 100/** 101 * The WebP compression min block size. This defines the optimal rectangle block 102 * size factor for WebP compression. WebP does utilize variable block size, but 103 * ensuring a block size factor reduces any noise on the image edges. 104 */ 105#define GUAC_SURFACE_WEBP_BLOCK_SIZE 8 106 107void guac_common_surface_set_multitouch(guac_common_surface* surface, 108 int touches) { 109 110 pthread_mutex_lock(&surface->_lock); 111 112 surface->touches = touches; 113 guac_protocol_send_set_int(surface->socket, surface->layer, 114 GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH, touches); 115 116 pthread_mutex_unlock(&surface->_lock); 117 118} 119 120void guac_common_surface_set_lossless(guac_common_surface* surface, 121 int lossless) { 122 123 pthread_mutex_lock(&surface->_lock); 124 surface->lossless = lossless; 125 pthread_mutex_unlock(&surface->_lock); 126 127} 128 129void guac_common_surface_move(guac_common_surface* surface, int x, int y) { 130 131 pthread_mutex_lock(&surface->_lock); 132 133 surface->x = x; 134 surface->y = y; 135 surface->location_dirty = 1; 136 137 pthread_mutex_unlock(&surface->_lock); 138 139} 140 141void guac_common_surface_stack(guac_common_surface* surface, int z) { 142 143 pthread_mutex_lock(&surface->_lock); 144 145 surface->z = z; 146 surface->location_dirty = 1; 147 148 pthread_mutex_unlock(&surface->_lock); 149 150} 151 152void guac_common_surface_set_parent(guac_common_surface* surface, 153 const guac_layer* parent) { 154 155 pthread_mutex_lock(&surface->_lock); 156 157 surface->parent = parent; 158 surface->location_dirty = 1; 159 160 pthread_mutex_unlock(&surface->_lock); 161 162} 163 164void guac_common_surface_set_opacity(guac_common_surface* surface, 165 int opacity) { 166 167 pthread_mutex_lock(&surface->_lock); 168 169 surface->opacity = opacity; 170 surface->opacity_dirty = 1; 171 172 pthread_mutex_unlock(&surface->_lock); 173 174} 175 176/** 177 * Updates the coordinates of the given rectangle to be within the bounds of 178 * the given surface. 179 * 180 * @param surface The surface to use for clipping. 181 * @param rect The rectangle to clip. 182 * @param sx The X coordinate of the source rectangle, if any. 183 * @param sy The Y coordinate of the source rectangle, if any. 184 */ 185static void __guac_common_bound_rect(guac_common_surface* surface, 186 guac_common_rect* rect, int* sx, int* sy) { 187 188 guac_common_rect bounds_rect = { 189 .x = 0, 190 .y = 0, 191 .width = surface->width, 192 .height = surface->height 193 }; 194 195 int orig_x = rect->x; 196 int orig_y = rect->y; 197 198 guac_common_rect_constrain(rect, &bounds_rect); 199 200 /* Update source X/Y if given */ 201 if (sx != NULL) *sx += rect->x - orig_x; 202 if (sy != NULL) *sy += rect->y - orig_y; 203 204} 205 206/** 207 * Updates the coordinates of the given rectangle to be within the clipping 208 * rectangle of the given surface, which must always be within the bounding 209 * rectangle of the given surface. 210 * 211 * @param surface The surface to use for clipping. 212 * @param rect The rectangle to clip. 213 * @param sx The X coordinate of the source rectangle, if any. 214 * @param sy The Y coordinate of the source rectangle, if any. 215 */ 216static void __guac_common_clip_rect(guac_common_surface* surface, 217 guac_common_rect* rect, int* sx, int* sy) { 218 219 int orig_x = rect->x; 220 int orig_y = rect->y; 221 222 /* Just bound within surface if no clipping rectangle applied */ 223 if (!surface->clipped) { 224 __guac_common_bound_rect(surface, rect, sx, sy); 225 return; 226 } 227 228 guac_common_rect_constrain(rect, &surface->clip_rect); 229 230 /* Update source X/Y if given */ 231 if (sx != NULL) *sx += rect->x - orig_x; 232 if (sy != NULL) *sy += rect->y - orig_y; 233 234} 235 236/** 237 * Returns whether a rectangle within the given surface contains only fully 238 * opaque pixels. 239 * 240 * @param surface 241 * The surface to check. 242 * 243 * @param rect 244 * The rectangle to check. 245 * 246 * @return 247 * Non-zero if the rectangle contains only fully opaque pixels, zero 248 * otherwise. 249 */ 250static int __guac_common_surface_is_opaque(guac_common_surface* surface, 251 guac_common_rect* rect) { 252 253 int x, y; 254 255 int stride = surface ->stride; 256 unsigned char* buffer = 257 surface->buffer + (stride * rect->y) + (4 * rect->x); 258 259 /* For each row */ 260 for (y = 0; y < rect->height; y++) { 261 262 /* Search for a non-opaque pixel */ 263 uint32_t* current = (uint32_t*) buffer; 264 for (x=0; x < rect->width; x++) { 265 266 /* Rectangle is non-opaque if a single non-opaque pixel is found */ 267 uint32_t color = *(current++); 268 if ((color & 0xFF000000) != 0xFF000000) 269 return 0; 270 271 } 272 273 /* Next row */ 274 buffer += stride; 275 276 } 277 278 /* Rectangle is opaque */ 279 return 1; 280 281} 282 283 284/** 285 * Returns whether the given rectangle should be combined into the existing 286 * dirty rectangle, to be eventually flushed as image data, or would be best 287 * kept independent of the current rectangle. 288 * 289 * @param surface 290 * The surface being updated. 291 * 292 * @param rect 293 * The bounding rectangle of the update being made to the surface. 294 * 295 * @param rect_only 296 * Non-zero if this update, by its nature, contains only metainformation 297 * about the update's bounding rectangle, zero if the update also contains 298 * image data. 299 * 300 * @return 301 * Non-zero if the update should be combined with any existing update, zero 302 * otherwise. 303 */ 304static int __guac_common_should_combine(guac_common_surface* surface, const guac_common_rect* rect, int rect_only) { 305 306 /* Always favor combining updates if surface is currently a purely 307 * server-side scratch area */ 308 if (!surface->realized) 309 return 1; 310 311 if (surface->dirty) { 312 313 int combined_cost, dirty_cost, update_cost; 314 315 /* Simulate combination */ 316 guac_common_rect combined = surface->dirty_rect; 317 guac_common_rect_extend(&combined, rect); 318 319 /* Combine if result is still small */ 320 if (combined.width <= GUAC_SURFACE_NEGLIGIBLE_WIDTH && combined.height <= GUAC_SURFACE_NEGLIGIBLE_HEIGHT) 321 return 1; 322 323 /* Estimate costs of the existing update, new update, and both combined */ 324 combined_cost = GUAC_SURFACE_BASE_COST + combined.width * combined.height; 325 dirty_cost = GUAC_SURFACE_BASE_COST + surface->dirty_rect.width * surface->dirty_rect.height; 326 update_cost = GUAC_SURFACE_BASE_COST + rect->width * rect->height; 327 328 /* Reduce cost if no image data */ 329 if (rect_only) 330 update_cost /= GUAC_SURFACE_DATA_FACTOR; 331 332 /* Combine if cost estimate shows benefit */ 333 if (combined_cost <= update_cost + dirty_cost) 334 return 1; 335 336 /* Combine if increase in cost is negligible */ 337 if (combined_cost - dirty_cost <= dirty_cost / GUAC_SURFACE_NEGLIGIBLE_INCREASE) 338 return 1; 339 340 if (combined_cost - update_cost <= update_cost / GUAC_SURFACE_NEGLIGIBLE_INCREASE) 341 return 1; 342 343 /* Combine if we anticipate further updates, as this update follows a common fill pattern */ 344 if (rect->x == surface->dirty_rect.x && rect->y == surface->dirty_rect.y + surface->dirty_rect.height) { 345 if (combined_cost <= (dirty_cost + update_cost) * GUAC_SURFACE_FILL_PATTERN_FACTOR) 346 return 1; 347 } 348 349 } 350 351 /* Otherwise, do not combine */ 352 return 0; 353 354} 355 356/** 357 * Expands the dirty rect of the given surface to contain the rect described by the given 358 * coordinates. 359 * 360 * @param surface The surface to mark as dirty. 361 * @param rect The rectangle of the update which is dirtying the surface. 362 */ 363static void __guac_common_mark_dirty(guac_common_surface* surface, const guac_common_rect* rect) { 364 365 /* Ignore empty rects */ 366 if (rect->width <= 0 || rect->height <= 0) 367 return; 368 369 /* If already dirty, update existing rect */ 370 if (surface->dirty) 371 guac_common_rect_extend(&surface->dirty_rect, rect); 372 373 /* Otherwise init dirty rect */ 374 else { 375 surface->dirty_rect = *rect; 376 surface->dirty = 1; 377 } 378 379} 380 381/** 382 * Calculate the current average framerate for a given area on the surface. 383 * 384 * @param surface 385 * The surface on which the framerate will be calculated. 386 * 387 * @param rect 388 * The rect containing the area for which the average framerate will be 389 * calculated. 390 * 391 * @return 392 * The average framerate of the given area, in frames per second. 393 */ 394static unsigned int __guac_common_surface_calculate_framerate( 395 guac_common_surface* surface, const guac_common_rect* rect) { 396 397 int x, y; 398 399 /* Calculate heat map dimensions */ 400 size_t heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(surface->width); 401 402 /* Calculate minimum X/Y coordinates intersecting given rect */ 403 int min_x = rect->x / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 404 int min_y = rect->y / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 405 406 /* Calculate maximum X/Y coordinates intersecting given rect */ 407 int max_x = min_x + (rect->width - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 408 int max_y = min_y + (rect->height - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 409 410 unsigned int sum_framerate = 0; 411 unsigned int count = 0; 412 413 /* Get start of buffer at given coordinates */ 414 const guac_common_surface_heat_cell* heat_row = 415 surface->heat_map + min_y * heat_width + min_x; 416 417 /* Iterate over all the heat map cells for the area 418 * and calculate the average framerate */ 419 for (y = min_y; y < max_y; y++) { 420 421 /* Get current row of heat map */ 422 const guac_common_surface_heat_cell* heat_cell = heat_row; 423 424 /* For each cell in subset of row */ 425 for (x = min_x; x < max_x; x++) { 426 427 /* Calculate indicies for latest and oldest history entries */ 428 int oldest_entry = heat_cell->oldest_entry; 429 int latest_entry = oldest_entry - 1; 430 if (latest_entry < 0) 431 latest_entry = GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE - 1; 432 433 /* Calculate elapsed time covering entire history for this cell */ 434 int elapsed_time = heat_cell->history[latest_entry] 435 - heat_cell->history[oldest_entry]; 436 437 /* Calculate and add framerate */ 438 if (elapsed_time) 439 sum_framerate += GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE 440 * 1000 / elapsed_time; 441 442 /* Next heat map cell */ 443 heat_cell++; 444 count++; 445 446 } 447 448 /* Next heat map row */ 449 heat_row += heat_width; 450 451 } 452 453 /* Calculate the average framerate over entire rect */ 454 if (count) 455 return sum_framerate / count; 456 457 return 0; 458 459} 460 461 /** 462 * Guesses whether a rectangle within a particular surface would be better 463 * compressed as PNG or using a lossy format like JPEG. Positive values 464 * indicate PNG is likely to be superior, while negative values indicate the 465 * opposite. 466 * 467 * @param surface 468 * The surface containing the image data to check. 469 * 470 * @param rect 471 * The rect to check within the given surface. 472 * 473 * @return 474 * Positive values if PNG compression is likely to perform better than 475 * lossy alternatives, or negative values if PNG is likely to perform 476 * worse. 477 */ 478static int __guac_common_surface_png_optimality(guac_common_surface* surface, 479 const guac_common_rect* rect) { 480 481 int x, y; 482 483 int num_same = 0; 484 int num_different = 1; 485 486 /* Get image/buffer metrics */ 487 int width = rect->width; 488 int height = rect->height; 489 int stride = surface->stride; 490 491 /* Get buffer from surface */ 492 unsigned char* buffer = surface->buffer + rect->y * stride + rect->x * 4; 493 494 /* Image must be at least 1x1 */ 495 if (width < 1 || height < 1) 496 return 0; 497 498 /* For each row */ 499 for (y = 0; y < height; y++) { 500 501 uint32_t* row = (uint32_t*) buffer; 502 uint32_t last_pixel = *(row++) | 0xFF000000; 503 504 /* For each pixel in current row */ 505 for (x = 1; x < width; x++) { 506 507 /* Get next pixel */ 508 uint32_t current_pixel = *(row++) | 0xFF000000; 509 510 /* Update same/different counts according to pixel value */ 511 if (current_pixel == last_pixel) 512 num_same++; 513 else 514 num_different++; 515 516 last_pixel = current_pixel; 517 518 } 519 520 /* Advance to next row */ 521 buffer += stride; 522 523 } 524 525 /* Return rough approximation of optimality for PNG compression */ 526 return 0x100 * num_same / num_different - 0x400; 527 528} 529 530/** 531 * Returns whether the given rectangle would be optimally encoded as JPEG 532 * rather than PNG. 533 * 534 * @param surface 535 * The surface to be queried. 536 * 537 * @param rect 538 * The rectangle to check. 539 * 540 * @return 541 * Non-zero if the rectangle would be optimally encoded as JPEG, zero 542 * otherwise. 543 */ 544static int __guac_common_surface_should_use_jpeg(guac_common_surface* surface, 545 const guac_common_rect* rect) { 546 547 /* Do not use JPEG if lossless quality is required */ 548 if (surface->lossless) 549 return 0; 550 551 /* Calculate the average framerate for the given rect */ 552 int framerate = __guac_common_surface_calculate_framerate(surface, rect); 553 554 int rect_size = rect->width * rect->height; 555 556 /* JPEG is preferred if: 557 * - frame rate is high enough 558 * - image size is large enough 559 * - PNG is not more optimal based on image contents */ 560 return framerate >= GUAC_COMMON_SURFACE_JPEG_FRAMERATE 561 && rect_size > GUAC_SURFACE_JPEG_MIN_BITMAP_SIZE 562 && __guac_common_surface_png_optimality(surface, rect) < 0; 563 564} 565 566/** 567 * Returns whether the given rectangle would be optimally encoded as WebP 568 * rather than PNG. 569 * 570 * @param surface 571 * The surface to be queried. 572 * 573 * @param rect 574 * The rectangle to check. 575 * 576 * @return 577 * Non-zero if the rectangle would be optimally encoded as WebP, zero 578 * otherwise. 579 */ 580static int __guac_common_surface_should_use_webp(guac_common_surface* surface, 581 const guac_common_rect* rect) { 582 583 /* Do not use WebP if not supported */ 584 if (!guac_client_supports_webp(surface->client)) 585 return 0; 586 587 /* Calculate the average framerate for the given rect */ 588 int framerate = __guac_common_surface_calculate_framerate(surface, rect); 589 590 /* WebP is preferred if: 591 * - frame rate is high enough 592 * - PNG is not more optimal based on image contents */ 593 return framerate >= GUAC_COMMON_SURFACE_JPEG_FRAMERATE 594 && __guac_common_surface_png_optimality(surface, rect) < 0; 595 596} 597 598/** 599 * Updates the heat map cells which intersect the given rectangle using the 600 * given timestamp. This timestamp, along with timestamps from past updates, 601 * is used to calculate the framerate of each heat cell. 602 * 603 * @param surface 604 * The surface containing the heat map cells to be updated. 605 * 606 * @param rect 607 * The rectangle containing the heat map cells to be updated. 608 * 609 * @param time 610 * The timestamp to use when updating the heat map cells which intersect 611 * the given rectangle. 612 */ 613static void __guac_common_surface_touch_rect(guac_common_surface* surface, 614 guac_common_rect* rect, guac_timestamp time) { 615 616 int x, y; 617 618 /* Calculate heat map dimensions */ 619 size_t heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(surface->width); 620 621 /* Calculate minimum X/Y coordinates intersecting given rect */ 622 int min_x = rect->x / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 623 int min_y = rect->y / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 624 625 /* Calculate maximum X/Y coordinates intersecting given rect */ 626 int max_x = min_x + (rect->width - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 627 int max_y = min_y + (rect->height - 1) / GUAC_COMMON_SURFACE_HEAT_CELL_SIZE; 628 629 /* Get start of buffer at given coordinates */ 630 guac_common_surface_heat_cell* heat_row = 631 surface->heat_map + min_y * heat_width + min_x; 632 633 /* Update all heat map cells which intersect with rectangle */ 634 for (y = min_y; y <= max_y; y++) { 635 636 /* Get current row of heat map */ 637 guac_common_surface_heat_cell* heat_cell = heat_row; 638 639 /* For each cell in subset of row */ 640 for (x = min_x; x <= max_x; x++) { 641 642 /* Replace oldest entry with new timestamp */ 643 heat_cell->history[heat_cell->oldest_entry] = time; 644 645 /* Update to next oldest entry */ 646 heat_cell->oldest_entry++; 647 if (heat_cell->oldest_entry >= 648 GUAC_COMMON_SURFACE_HEAT_CELL_HISTORY_SIZE) 649 heat_cell->oldest_entry = 0; 650 651 /* Advance to next heat map cell */ 652 heat_cell++; 653 654 } 655 656 /* Next heat map row */ 657 heat_row += heat_width; 658 659 } 660 661} 662 663/** 664 * Flushes the bitmap update currently described by the dirty rectangle within the 665 * given surface to that surface's bitmap queue. There MUST be space within the 666 * queue. 667 * 668 * @param surface The surface to flush. 669 */ 670static void __guac_common_surface_flush_to_queue(guac_common_surface* surface) { 671 672 guac_common_surface_bitmap_rect* rect; 673 674 /* Do not flush if not dirty */ 675 if (!surface->dirty) 676 return; 677 678 /* Add new rect to queue */ 679 rect = &(surface->bitmap_queue[surface->bitmap_queue_length++]); 680 rect->rect = surface->dirty_rect; 681 rect->flushed = 0; 682 683 /* Surface now flushed */ 684 surface->dirty = 0; 685 686} 687 688/** 689 * Flushes the given surface, drawing any pending operations on the remote 690 * display. Surface properties are not flushed. 691 * 692 * @param surface 693 * The surface to flush. 694 */ 695static void __guac_common_surface_flush(guac_common_surface* surface); 696 697/** 698 * Schedules a deferred flush of the given surface. This will not immediately 699 * flush the surface to the client. Instead, the result of the flush is 700 * added to a queue which is reinspected and combined (if possible) with other 701 * deferred flushes during the call to guac_common_surface_flush(). 702 * 703 * @param surface The surface to flush. 704 */ 705static void __guac_common_surface_flush_deferred(guac_common_surface* surface) { 706 707 /* Do not flush if not dirty */ 708 if (!surface->dirty) 709 return; 710 711 /* Flush if queue size has reached maximum (space is reserved for the final 712 * dirty rect, as __guac_common_surface_flush() MAY add an additional rect 713 * to the queue */ 714 if (surface->bitmap_queue_length == GUAC_COMMON_SURFACE_QUEUE_SIZE-1) 715 __guac_common_surface_flush(surface); 716 717 /* Append dirty rect to queue */ 718 __guac_common_surface_flush_to_queue(surface); 719 720} 721 722/** 723 * Transfers a single uint32_t using the given transfer function. 724 * 725 * @param op The transfer function to use. 726 * @param src The source of the uint32_t value. 727 * @param dst THe destination which will hold the result of the transfer. 728 * @return Non-zero if the destination value was changed, zero otherwise. 729 */ 730static int __guac_common_surface_transfer_int(guac_transfer_function op, uint32_t* src, uint32_t* dst) { 731 732 uint32_t orig = *dst; 733 734 switch (op) { 735 736 case GUAC_TRANSFER_BINARY_BLACK: 737 *dst = 0xFF000000; 738 break; 739 740 case GUAC_TRANSFER_BINARY_WHITE: 741 *dst = 0xFFFFFFFF; 742 break; 743 744 case GUAC_TRANSFER_BINARY_SRC: 745 *dst = *src; 746 break; 747 748 case GUAC_TRANSFER_BINARY_DEST: 749 /* NOP */ 750 break; 751 752 case GUAC_TRANSFER_BINARY_NSRC: 753 *dst = *src ^ 0x00FFFFFF; 754 break; 755 756 case GUAC_TRANSFER_BINARY_NDEST: 757 *dst = *dst ^ 0x00FFFFFF; 758 break; 759 760 case GUAC_TRANSFER_BINARY_AND: 761 *dst = ((*dst) & (0xFF000000 | *src)); 762 break; 763 764 case GUAC_TRANSFER_BINARY_NAND: 765 *dst = ((*dst) & (0xFF000000 | *src)) ^ 0x00FFFFFF; 766 break; 767 768 case GUAC_TRANSFER_BINARY_OR: 769 *dst = ((*dst) | (0x00FFFFFF & *src)); 770 break; 771 772 case GUAC_TRANSFER_BINARY_NOR: 773 *dst = ((*dst) | (0x00FFFFFF & *src)) ^ 0x00FFFFFF; 774 break; 775 776 case GUAC_TRANSFER_BINARY_XOR: 777 *dst = ((*dst) ^ (0x00FFFFFF & *src)); 778 break; 779 780 case GUAC_TRANSFER_BINARY_XNOR: 781 *dst = ((*dst) ^ (0x00FFFFFF & *src)) ^ 0x00FFFFFF; 782 break; 783 784 case GUAC_TRANSFER_BINARY_NSRC_AND: 785 *dst = ((*dst) & (0xFF000000 | (*src ^ 0x00FFFFFF))); 786 break; 787 788 case GUAC_TRANSFER_BINARY_NSRC_NAND: 789 *dst = ((*dst) & (0xFF000000 | (*src ^ 0x00FFFFFF))) ^ 0x00FFFFFF; 790 break; 791 792 case GUAC_TRANSFER_BINARY_NSRC_OR: 793 *dst = ((*dst) | (0x00FFFFFF & (*src ^ 0x00FFFFFF))); 794 break; 795 796 case GUAC_TRANSFER_BINARY_NSRC_NOR: 797 *dst = ((*dst) | (0x00FFFFFF & (*src ^ 0x00FFFFFF))) ^ 0x00FFFFFF; 798 break; 799 800 } 801 802 return *dst != orig; 803 804} 805 806/** 807 * Assigns the given value to all pixels within a rectangle of the backing 808 * surface of the given destination surface. The color of all pixels within the 809 * rectangle, including the alpha component, is entirely replaced. 810 * 811 * @param dst 812 * The destination surface. 813 * 814 * @param rect 815 * The rectangle to draw. 816 * 817 * @param red 818 * The red component of the color value to assign to all pixels within the 819 * rectangle. 820 * 821 * @param green 822 * The green component of the color value to assign to all pixels within 823 * the rectangle. 824 * 825 * @param blue 826 * The blue component of the color value to assign to all pixels within the 827 * rectangle. 828 * 829 * @param alpha 830 * The alpha component of the color value to assign to all pixels within 831 * the rectangle. 832 */ 833static void __guac_common_surface_set(guac_common_surface* dst, 834 guac_common_rect* rect, int red, int green, int blue, int alpha) { 835 836 int x, y; 837 838 int dst_stride; 839 unsigned char* dst_buffer; 840 841 uint32_t color = (alpha << 24) | (red << 16) | (green << 8) | blue; 842 843 int min_x = rect->width - 1; 844 int min_y = rect->height - 1; 845 int max_x = 0; 846 int max_y = 0; 847 848 dst_stride = dst->stride; 849 dst_buffer = dst->buffer + (dst_stride * rect->y) + (4 * rect->x); 850 851 /* For each row */ 852 for (y=0; y < rect->height; y++) { 853 854 uint32_t* dst_current = (uint32_t*) dst_buffer; 855 856 /* Set row */ 857 for (x=0; x < rect->width; x++) { 858 859 uint32_t old_color = *dst_current; 860 861 if (old_color != color) { 862 if (x < min_x) min_x = x; 863 if (y < min_y) min_y = y; 864 if (x > max_x) max_x = x; 865 if (y > max_y) max_y = y; 866 *dst_current = color; 867 } 868 869 dst_current++; 870 } 871 872 /* Next row */ 873 dst_buffer += dst_stride; 874 875 } 876 877 /* Restrict destination rect to only updated pixels */ 878 if (max_x >= min_x && max_y >= min_y) { 879 rect->x += min_x; 880 rect->y += min_y; 881 rect->width = max_x - min_x + 1; 882 rect->height = max_y - min_y + 1; 883 } 884 else { 885 rect->width = 0; 886 rect->height = 0; 887 } 888 889} 890 891/** 892 * Applies the Porter-Duff "over" composite operator, blending the two given 893 * color components using the given alpha value. 894 * 895 * @param dst 896 * The destination color component. 897 * 898 * @param src 899 * The source color component. 900 * 901 * @param alpha 902 * The alpha value which applies to the blending operation. 903 * 904 * @return 905 * The result of applying the Porter-Duff "over" composite operator to the 906 * given source and destination components. 907 */ 908static int guac_common_surface_blend_component(int dst, int src, int alpha) { 909 910 int blended = src + dst * (0xFF - alpha); 911 912 /* Do not exceed maximum component value */ 913 if (blended > 0xFF) 914 return 0xFF; 915 916 return blended; 917 918} 919 920/** 921 * Applies the Porter-Duff "over" composite operator, blending each component 922 * of the two given ARGB colors. 923 * 924 * @param dst 925 * The destination ARGB color. 926 * 927 * @param src 928 * The source ARGB color. 929 * 930 * @return 931 * The result of applying the Porter-Duff "over" composite operator to the 932 * given source and destination colors. 933 */ 934static uint32_t guac_common_surface_argb_blend(uint32_t dst, uint32_t src) { 935 936 /* Separate destination ARGB color into its components */ 937 int dst_a = (dst >> 24) & 0xFF; 938 int dst_r = (dst >> 16) & 0xFF; 939 int dst_g = (dst >> 8) & 0xFF; 940 int dst_b = dst & 0xFF; 941 942 /* Separate source ARGB color into its components */ 943 int src_a = (src >> 24) & 0xFF; 944 int src_r = (src >> 16) & 0xFF; 945 int src_g = (src >> 8) & 0xFF; 946 int src_b = src & 0xFF; 947 948 /* If source is fully opaque (or destination is fully transparent), the 949 * blended result is the source */ 950 if (src_a == 0xFF || dst_a == 0x00) 951 return src; 952 953 /* If source is fully transparent, the blended result is the destination */ 954 if (src_a == 0x00) 955 return dst; 956 957 /* Otherwise, blend each ARGB component, assuming pre-multiplied alpha */ 958 int r = guac_common_surface_blend_component(dst_r, src_r, src_a); 959 int g = guac_common_surface_blend_component(dst_g, src_g, src_a); 960 int b = guac_common_surface_blend_component(dst_b, src_b, src_a); 961 int a = guac_common_surface_blend_component(dst_a, src_a, src_a); 962 963 /* Recombine blended components */ 964 return (a << 24) | (r << 16) | (g << 8) | b; 965 966} 967 968/** 969 * Copies data from the given buffer to the surface at the given coordinates. 970 * The dimensions and location of the destination rectangle will be altered 971 * to remove as many unchanged pixels as possible. 972 * 973 * @param src_buffer The buffer to copy. 974 * @param src_stride The number of bytes in each row of the source buffer. 975 * @param sx The X coordinate of the source rectangle. 976 * @param sy The Y coordinate of the source rectangle. 977 * @param dst The destination surface. 978 * @param rect The destination rectangle. 979 * @param opaque Non-zero if the source surface is opaque (its alpha channel 980 * should be ignored), zero otherwise. 981 */ 982static void __guac_common_surface_put(unsigned char* src_buffer, int src_stride, 983 int* sx, int* sy, 984 guac_common_surface* dst, guac_common_rect* rect, 985 int opaque) { 986 987 unsigned char* dst_buffer = dst->buffer; 988 int dst_stride = dst->stride; 989 990 int x, y; 991 992 int min_x = rect->width; 993 int min_y = rect->height; 994 int max_x = 0; 995 int max_y = 0; 996 997 int orig_x = rect->x; 998 int orig_y = rect->y; 999 1000 src_buffer += src_stride * (*sy) + 4 * (*sx); 1001 dst_buffer += (dst_stride * rect->y) + (4 * rect->x); 1002 1003 /* For each row */ 1004 for (y=0; y < rect->height; y++) { 1005 1006 uint32_t* src_current = (uint32_t*) src_buffer; 1007 uint32_t* dst_current = (uint32_t*) dst_buffer; 1008 1009 /* Copy row */ 1010 for (x=0; x < rect->width; x++) { 1011 1012 uint32_t color; 1013 1014 /* Get source and destination color values */ 1015 uint32_t src_color = *src_current; 1016 uint32_t dst_color = *dst_current; 1017 1018 /* Ignore alpha channel if opaque */ 1019 if (opaque) 1020 color = src_color | 0xFF000000; 1021 1022 /* Otherwise, perform alpha blending operation */ 1023 else 1024 color = guac_common_surface_argb_blend(dst_color, src_color); 1025 1026 /* If the destination color is changing, update rectangle bounds 1027 * and store the new color */ 1028 if (dst_color != color) { 1029 if (x < min_x) min_x = x; 1030 if (y < min_y) min_y = y; 1031 if (x > max_x) max_x = x; 1032 if (y > max_y) max_y = y; 1033 *dst_current = color; 1034 } 1035 1036 /* Advance to next pixel */ 1037 src_current++; 1038 dst_current++; 1039 1040 } 1041 1042 /* Next row */ 1043 src_buffer += src_stride; 1044 dst_buffer += dst_stride; 1045 1046 } 1047 1048 /* Restrict destination rect to only updated pixels */ 1049 if (max_x >= min_x && max_y >= min_y) { 1050 rect->x += min_x; 1051 rect->y += min_y; 1052 rect->width = max_x - min_x + 1; 1053 rect->height = max_y - min_y + 1; 1054 } 1055 else { 1056 rect->width = 0; 1057 rect->height = 0; 1058 } 1059 1060 /* Update source X/Y */ 1061 *sx += rect->x - orig_x; 1062 *sy += rect->y - orig_y; 1063 1064} 1065 1066/** 1067 * Fills the given surface with color, using the given buffer as a mask. Color 1068 * will be added to the given surface iff the corresponding pixel within the 1069 * buffer is opaque. 1070 * 1071 * @param src_buffer The buffer to use as a mask. 1072 * @param src_stride The number of bytes in each row of the source buffer. 1073 * @param sx The X coordinate of the source rectangle. 1074 * @param sy The Y coordinate of the source rectangle. 1075 * @param dst The destination surface. 1076 * @param rect The destination rectangle. 1077 * @param red The red component of the color of the fill. 1078 * @param green The green component of the color of the fill. 1079 * @param blue The blue component of the color of the fill. 1080 */ 1081static void __guac_common_surface_fill_mask(unsigned char* src_buffer, int src_stride, 1082 int sx, int sy, 1083 guac_common_surface* dst, guac_common_rect* rect, 1084 int red, int green, int blue) { 1085 1086 unsigned char* dst_buffer = dst->buffer; 1087 int dst_stride = dst->stride; 1088 1089 uint32_t color = 0xFF000000 | (red << 16) | (green << 8) | blue; 1090 int x, y; 1091 1092 src_buffer += src_stride*sy + 4*sx; 1093 dst_buffer += (dst_stride * rect->y) + (4 * rect->x); 1094 1095 /* For each row */ 1096 for (y=0; y < rect->height; y++) { 1097 1098 uint32_t* src_current = (uint32_t*) src_buffer; 1099 uint32_t* dst_current = (uint32_t*) dst_buffer; 1100 1101 /* Stencil row */ 1102 for (x=0; x < rect->width; x++) { 1103 1104 /* Fill with color if opaque */ 1105 if (*src_current & 0xFF000000) 1106 *dst_current = color; 1107 1108 src_current++; 1109 dst_current++; 1110 } 1111 1112 /* Next row */ 1113 src_buffer += src_stride; 1114 dst_buffer += dst_stride; 1115 1116 } 1117 1118} 1119 1120/** 1121 * Copies data from the given surface to the given destination surface using 1122 * the specified transfer function. 1123 * 1124 * @param src_buffer The buffer to copy. 1125 * @param src_stride The number of bytes in each row of the source buffer. 1126 * @param sx The X coordinate of the source rectangle. 1127 * @param sy The Y coordinate of the source rectangle. 1128 * @param op The transfer function to use. 1129 * @param dst The destination surface. 1130 * @param rect The destination rectangle. 1131 */ 1132static void __guac_common_surface_transfer(guac_common_surface* src, int* sx, int* sy, 1133 guac_transfer_function op, 1134 guac_common_surface* dst, guac_common_rect* rect) { 1135 1136 unsigned char* src_buffer = src->buffer; 1137 unsigned char* dst_buffer = dst->buffer; 1138 1139 int x, y; 1140 int src_stride, dst_stride; 1141 int step = 1; 1142 1143 int min_x = rect->width - 1; 1144 int min_y = rect->height - 1; 1145 int max_x = 0; 1146 int max_y = 0; 1147 1148 int orig_x = rect->x; 1149 int orig_y = rect->y; 1150 1151 /* Copy forwards only if destination is in a different surface or is before source */ 1152 if (src != dst || rect->y < *sy || (rect->y == *sy && rect->x < *sx)) { 1153 src_buffer += src->stride * (*sy) + 4 * (*sx); 1154 dst_buffer += (dst->stride * rect->y) + (4 * rect->x); 1155 src_stride = src->stride; 1156 dst_stride = dst->stride; 1157 step = 1; 1158 } 1159 1160 /* Otherwise, copy backwards */ 1161 else { 1162 src_buffer += src->stride * (*sy + rect->height - 1) + 4 * (*sx + rect->width - 1); 1163 dst_buffer += dst->stride * (rect->y + rect->height - 1) + 4 * (rect->x + rect->width - 1); 1164 src_stride = -src->stride; 1165 dst_stride = -dst->stride; 1166 step = -1; 1167 } 1168 1169 /* For each row */ 1170 for (y=0; y < rect->height; y++) { 1171 1172 uint32_t* src_current = (uint32_t*) src_buffer; 1173 uint32_t* dst_current = (uint32_t*) dst_buffer; 1174 1175 /* Transfer each pixel in row */ 1176 for (x=0; x < rect->width; x++) { 1177 1178 if (__guac_common_surface_transfer_int(op, src_current, dst_current)) { 1179 if (x < min_x) min_x = x; 1180 if (y < min_y) min_y = y; 1181 if (x > max_x) max_x = x; 1182 if (y > max_y) max_y = y; 1183 } 1184 1185 src_current += step; 1186 dst_current += step; 1187 } 1188 1189 /* Next row */ 1190 src_buffer += src_stride; 1191 dst_buffer += dst_stride; 1192 1193 } 1194 1195 /* Translate X coordinate space of moving backwards */ 1196 if (step < 0) { 1197 int old_max_x = max_x; 1198 max_x = rect->width - 1 - min_x; 1199 min_x = rect->width - 1 - old_max_x; 1200 } 1201 1202 /* Translate Y coordinate space of moving backwards */ 1203 if (dst_stride < 0) { 1204 int old_max_y = max_y; 1205 max_y = rect->height - 1 - min_y; 1206 min_y = rect->height - 1 - old_max_y; 1207 } 1208 1209 /* Restrict destination rect to only updated pixels */ 1210 if (max_x >= min_x && max_y >= min_y) { 1211 rect->x += min_x; 1212 rect->y += min_y; 1213 rect->width = max_x - min_x + 1; 1214 rect->height = max_y - min_y + 1; 1215 } 1216 else { 1217 rect->width = 0; 1218 rect->height = 0; 1219 } 1220 1221 /* Update source X/Y */ 1222 *sx += rect->x - orig_x; 1223 *sy += rect->y - orig_y; 1224 1225} 1226 1227guac_common_surface* guac_common_surface_alloc(guac_client* client, 1228 guac_socket* socket, const guac_layer* layer, int w, int h) { 1229 1230 /* Calculate heat map dimensions */ 1231 size_t heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(w); 1232 size_t heat_height = GUAC_COMMON_SURFACE_HEAT_DIMENSION(h); 1233 1234 /* Init surface */ 1235 guac_common_surface* surface = guac_mem_zalloc(sizeof(guac_common_surface)); 1236 surface->client = client; 1237 surface->socket = socket; 1238 surface->layer = layer; 1239 surface->parent = GUAC_DEFAULT_LAYER; 1240 surface->opacity = 0xFF; 1241 surface->width = w; 1242 surface->height = h; 1243 1244 pthread_mutex_init(&surface->_lock, NULL); 1245 1246 /* Create corresponding Cairo surface */ 1247 surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w); 1248 surface->buffer = guac_mem_zalloc(h, surface->stride); 1249 1250 /* Create corresponding heat map */ 1251 surface->heat_map = guac_mem_zalloc(heat_width, heat_height, 1252 sizeof(guac_common_surface_heat_cell)); 1253 1254 /* Reset clipping rect */ 1255 guac_common_surface_reset_clip(surface); 1256 1257 /* Layers must initially exist */ 1258 if (layer->index >= 0) { 1259 guac_protocol_send_size(socket, layer, w, h); 1260 surface->realized = 1; 1261 } 1262 1263 /* Defer creation of buffers */ 1264 else 1265 surface->realized = 0; 1266 1267 return surface; 1268} 1269 1270void guac_common_surface_free(guac_common_surface* surface) { 1271 1272 /* Only dispose of surface if it exists */ 1273 if (surface->realized) 1274 guac_protocol_send_dispose(surface->socket, surface->layer); 1275 1276 pthread_mutex_destroy(&surface->_lock); 1277 1278 guac_mem_free(surface->heat_map); 1279 guac_mem_free(surface->buffer); 1280 guac_mem_free(surface); 1281 1282} 1283 1284void guac_common_surface_resize(guac_common_surface* surface, int w, int h) { 1285 1286 pthread_mutex_lock(&surface->_lock); 1287 1288 /* Ignore if resize will have no effect */ 1289 if (w == surface->width && h == surface->height) 1290 goto complete; 1291 1292 guac_socket* socket = surface->socket; 1293 const guac_layer* layer = surface->layer; 1294 1295 unsigned char* old_buffer; 1296 int old_stride; 1297 guac_common_rect old_rect; 1298 1299 int sx = 0; 1300 int sy = 0; 1301 1302 /* Calculate heat map dimensions */ 1303 size_t heat_width = GUAC_COMMON_SURFACE_HEAT_DIMENSION(w); 1304 size_t heat_height = GUAC_COMMON_SURFACE_HEAT_DIMENSION(h); 1305 1306 /* Copy old surface data */ 1307 old_buffer = surface->buffer; 1308 old_stride = surface->stride; 1309 guac_common_rect_init(&old_rect, 0, 0, surface->width, surface->height); 1310 1311 /* Re-initialize at new size */ 1312 surface->width = w; 1313 surface->height = h; 1314 surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, w); 1315 surface->buffer = guac_mem_zalloc(h, surface->stride); 1316 __guac_common_bound_rect(surface, &surface->clip_rect, NULL, NULL); 1317 1318 /* Copy relevant old data */ 1319 __guac_common_bound_rect(surface, &old_rect, NULL, NULL); 1320 __guac_common_surface_put(old_buffer, old_stride, &sx, &sy, surface, &old_rect, 1); 1321 1322 /* Free old data */ 1323 guac_mem_free(old_buffer); 1324 1325 /* Allocate completely new heat map (can safely discard old stats) */ 1326 guac_mem_free(surface->heat_map); 1327 surface->heat_map = guac_mem_alloc(heat_width, heat_height, 1328 sizeof(guac_common_surface_heat_cell)); 1329 1330 /* Resize dirty rect to fit new surface dimensions */ 1331 if (surface->dirty) { 1332 __guac_common_bound_rect(surface, &surface->dirty_rect, NULL, NULL); 1333 if (surface->dirty_rect.width <= 0 || surface->dirty_rect.height <= 0) 1334 surface->dirty = 0; 1335 } 1336 1337 /* Update Guacamole layer */ 1338 if (surface->realized) 1339 guac_protocol_send_size(socket, layer, w, h); 1340 1341complete: 1342 pthread_mutex_unlock(&surface->_lock); 1343 1344} 1345 1346void guac_common_surface_draw(guac_common_surface* surface, int x, int y, cairo_surface_t* src) { 1347 1348 pthread_mutex_lock(&surface->_lock); 1349 1350 unsigned char* buffer = cairo_image_surface_get_data(src); 1351 cairo_format_t format = cairo_image_surface_get_format(src); 1352 int stride = cairo_image_surface_get_stride(src); 1353 int w = cairo_image_surface_get_width(src); 1354 int h = cairo_image_surface_get_height(src); 1355 1356 int sx = 0; 1357 int sy = 0; 1358 1359 guac_common_rect rect; 1360 guac_common_rect_init(&rect, x, y, w, h); 1361 1362 /* Clip operation */ 1363 __guac_common_clip_rect(surface, &rect, &sx, &sy); 1364 if (rect.width <= 0 || rect.height <= 0) 1365 goto complete; 1366 1367 /* Update backing surface */ 1368 __guac_common_surface_put(buffer, stride, &sx, &sy, surface, &rect, format != CAIRO_FORMAT_ARGB32); 1369 if (rect.width <= 0 || rect.height <= 0) 1370 goto complete; 1371 1372 /* Update the heat map for the update rectangle. */ 1373 guac_timestamp time = guac_timestamp_current(); 1374 __guac_common_surface_touch_rect(surface, &rect, time); 1375 1376 /* Flush if not combining */ 1377 if (!__guac_common_should_combine(surface, &rect, 0)) 1378 __guac_common_surface_flush_deferred(surface); 1379 1380 /* Always defer draws */ 1381 __guac_common_mark_dirty(surface, &rect); 1382 1383complete: 1384 pthread_mutex_unlock(&surface->_lock); 1385 1386} 1387 1388void guac_common_surface_paint(guac_common_surface* surface, int x, int y, 1389 cairo_surface_t* src, int red, int green, int blue) { 1390 1391 pthread_mutex_lock(&surface->_lock); 1392 1393 unsigned char* buffer = cairo_image_surface_get_data(src); 1394 int stride = cairo_image_surface_get_stride(src); 1395 int w = cairo_image_surface_get_width(src); 1396 int h = cairo_image_surface_get_height(src); 1397 1398 int sx = 0; 1399 int sy = 0; 1400 1401 guac_common_rect rect; 1402 guac_common_rect_init(&rect, x, y, w, h); 1403 1404 /* Clip operation */ 1405 __guac_common_clip_rect(surface, &rect, &sx, &sy); 1406 if (rect.width <= 0 || rect.height <= 0) 1407 goto complete; 1408 1409 /* Update backing surface */ 1410 __guac_common_surface_fill_mask(buffer, stride, sx, sy, surface, &rect, red, green, blue); 1411 1412 /* Flush if not combining */ 1413 if (!__guac_common_should_combine(surface, &rect, 0)) 1414 __guac_common_surface_flush_deferred(surface); 1415 1416 /* Always defer draws */ 1417 __guac_common_mark_dirty(surface, &rect); 1418 1419complete: 1420 pthread_mutex_unlock(&surface->_lock); 1421 1422} 1423 1424void guac_common_surface_copy(guac_common_surface* src, int sx, int sy, 1425 int w, int h, guac_common_surface* dst, int dx, int dy) { 1426 1427 /* Lock both surfaces */ 1428 pthread_mutex_lock(&dst->_lock); 1429 if (src != dst) 1430 pthread_mutex_lock(&src->_lock); 1431 1432 guac_socket* socket = dst->socket; 1433 const guac_layer* src_layer = src->layer; 1434 const guac_layer* dst_layer = dst->layer; 1435 1436 guac_common_rect srect; 1437 guac_common_rect_init(&srect, sx, sy, w, h); 1438 1439 /* Clip operation source rect to bounds */ 1440 __guac_common_bound_rect(src, &srect, &dx, &dy); 1441 if (srect.width <= 0 || srect.height <= 0) 1442 goto complete; 1443 1444 guac_common_rect drect; 1445 guac_common_rect_init(&drect, dx, dy, 1446 srect.width, srect.height); 1447 1448 /* Clip operation destination rect */ 1449 __guac_common_clip_rect(dst, &drect, &srect.x, &srect.y); 1450 if (drect.width <= 0 || drect.height <= 0) 1451 goto complete; 1452 1453 /* NOTE: Being the last rectangle to be adjusted, only the width/height of 1454 * drect is now correct! */ 1455 1456 /* Update backing surface first only if drect cannot intersect srect */ 1457 if (src != dst) { 1458 __guac_common_surface_transfer(src, &srect.x, &srect.y, 1459 GUAC_TRANSFER_BINARY_SRC, dst, &drect); 1460 if (drect.width <= 0 || drect.height <= 0) 1461 goto complete; 1462 } 1463 1464 /* Defer if combining */ 1465 if (__guac_common_should_combine(dst, &drect, 1)) 1466 __guac_common_mark_dirty(dst, &drect); 1467 1468 /* Otherwise, flush and draw immediately */ 1469 else { 1470 __guac_common_surface_flush(dst); 1471 __guac_common_surface_flush(src); 1472 guac_protocol_send_copy(socket, src_layer, srect.x, srect.y, 1473 drect.width, drect.height, GUAC_COMP_OVER, dst_layer, 1474 drect.x, drect.y); 1475 dst->realized = 1; 1476 } 1477 1478 /* Update backing surface last if drect can intersect srect */ 1479 if (src == dst) 1480 __guac_common_surface_transfer(src, &srect.x, &srect.y, 1481 GUAC_TRANSFER_BINARY_SRC, dst, &drect); 1482 1483complete: 1484 1485 /* Unlock both surfaces */ 1486 pthread_mutex_unlock(&dst->_lock); 1487 if (src != dst) 1488 pthread_mutex_unlock(&src->_lock); 1489 1490} 1491 1492void guac_common_surface_transfer(guac_common_surface* src, int sx, int sy, int w, int h, 1493 guac_transfer_function op, guac_common_surface* dst, int dx, int dy) { 1494 1495 /* Lock both surfaces */ 1496 pthread_mutex_lock(&dst->_lock); 1497 if (src != dst) 1498 pthread_mutex_lock(&src->_lock); 1499 1500 guac_socket* socket = dst->socket; 1501 const guac_layer* src_layer = src->layer; 1502 const guac_layer* dst_layer = dst->layer; 1503 1504 guac_common_rect srect; 1505 guac_common_rect_init(&srect, sx, sy, w, h); 1506 1507 /* Clip operation source rect to bounds */ 1508 __guac_common_bound_rect(src, &srect, &dx, &dy); 1509 if (srect.width <= 0 || srect.height <= 0) 1510 goto complete; 1511 1512 guac_common_rect drect; 1513 guac_common_rect_init(&drect, dx, dy, 1514 srect.width, srect.height); 1515 1516 /* Clip operation destination rect */ 1517 __guac_common_clip_rect(dst, &drect, &srect.x, &srect.y); 1518 if (drect.width <= 0 || drect.height <= 0) 1519 goto complete; 1520 1521 /* NOTE: Being the last rectangle to be adjusted, only the width/height of 1522 * drect is now correct! */ 1523 1524 /* Update backing surface first only if drect cannot intersect srect */ 1525 if (src != dst) { 1526 __guac_common_surface_transfer(src, &srect.x, &srect.y, op, dst, &drect); 1527 if (drect.width <= 0 || drect.height <= 0) 1528 goto complete; 1529 } 1530 1531 /* Defer if combining */ 1532 if (__guac_common_should_combine(dst, &drect, 1)) 1533 __guac_common_mark_dirty(dst, &drect); 1534 1535 /* Otherwise, flush and draw immediately */ 1536 else { 1537 __guac_common_surface_flush(dst); 1538 __guac_common_surface_flush(src); 1539 guac_protocol_send_transfer(socket, src_layer, srect.x, srect.y, 1540 drect.width, drect.height, op, dst_layer, drect.x, drect.y); 1541 dst->realized = 1; 1542 } 1543 1544 /* Update backing surface last if drect can intersect srect */ 1545 if (src == dst) 1546 __guac_common_surface_transfer(src, &srect.x, &srect.y, op, dst, &drect); 1547 1548complete: 1549 1550 /* Unlock both surfaces */ 1551 pthread_mutex_unlock(&dst->_lock); 1552 if (src != dst) 1553 pthread_mutex_unlock(&src->_lock); 1554 1555} 1556 1557void guac_common_surface_set(guac_common_surface* surface, 1558 int x, int y, int w, int h, int red, int green, int blue, int alpha) { 1559 1560 pthread_mutex_lock(&surface->_lock); 1561 1562 guac_socket* socket = surface->socket; 1563 const guac_layer* layer = surface->layer; 1564 1565 guac_common_rect rect; 1566 guac_common_rect_init(&rect, x, y, w, h); 1567 1568 /* Clip operation */ 1569 __guac_common_clip_rect(surface, &rect, NULL, NULL); 1570 if (rect.width <= 0 || rect.height <= 0) 1571 goto complete; 1572 1573 /* Update backing surface */ 1574 __guac_common_surface_set(surface, &rect, red, green, blue, alpha); 1575 if (rect.width <= 0 || rect.height <= 0) 1576 goto complete; 1577 1578 /* Handle as normal draw if non-opaque */ 1579 if (alpha != 0xFF) { 1580 1581 /* Flush if not combining */ 1582 if (!__guac_common_should_combine(surface, &rect, 0)) 1583 __guac_common_surface_flush_deferred(surface); 1584 1585 /* Always defer draws */ 1586 __guac_common_mark_dirty(surface, &rect); 1587 1588 } 1589 1590 /* Defer if combining */ 1591 else if (__guac_common_should_combine(surface, &rect, 1)) 1592 __guac_common_mark_dirty(surface, &rect); 1593 1594 /* Otherwise, flush and draw immediately */ 1595 else { 1596 __guac_common_surface_flush(surface); 1597 guac_protocol_send_rect(socket, layer, rect.x, rect.y, rect.width, rect.height); 1598 guac_protocol_send_cfill(socket, GUAC_COMP_OVER, layer, red, green, blue, alpha); 1599 surface->realized = 1; 1600 } 1601 1602complete: 1603 pthread_mutex_unlock(&surface->_lock); 1604 1605} 1606 1607void guac_common_surface_clip(guac_common_surface* surface, int x, int y, int w, int h) { 1608 1609 pthread_mutex_lock(&surface->_lock); 1610 1611 guac_common_rect clip; 1612 1613 /* Init clipping rectangle if clipping not already applied */ 1614 if (!surface->clipped) { 1615 guac_common_rect_init(&surface->clip_rect, 0, 0, surface->width, surface->height); 1616 surface->clipped = 1; 1617 } 1618 1619 guac_common_rect_init(&clip, x, y, w, h); 1620 guac_common_rect_constrain(&surface->clip_rect, &clip); 1621 1622 pthread_mutex_unlock(&surface->_lock); 1623 1624} 1625 1626void guac_common_surface_reset_clip(guac_common_surface* surface) { 1627 pthread_mutex_lock(&surface->_lock); 1628 surface->clipped = 0; 1629 pthread_mutex_unlock(&surface->_lock); 1630} 1631 1632/** 1633 * Flushes the bitmap update currently described by the dirty rectangle within 1634 * the given surface directly via an "img" instruction as PNG data. The 1635 * resulting instructions will be sent over the socket associated with the 1636 * given surface. 1637 * 1638 * @param surface 1639 * The surface to flush. 1640 * 1641 * @param opaque 1642 * Whether the rectangle being flushed contains only fully-opaque pixels. 1643 */ 1644static void __guac_common_surface_flush_to_png(guac_common_surface* surface, 1645 int opaque) { 1646 1647 if (surface->dirty) { 1648 1649 guac_socket* socket = surface->socket; 1650 const guac_layer* layer = surface->layer; 1651 1652 /* Get Cairo surface for specified rect */ 1653 unsigned char* buffer = surface->buffer 1654 + surface->dirty_rect.y * surface->stride 1655 + surface->dirty_rect.x * 4; 1656 1657 cairo_surface_t* rect; 1658 1659 /* Use RGB24 if the image is fully opaque */ 1660 if (opaque) 1661 rect = cairo_image_surface_create_for_data(buffer, 1662 CAIRO_FORMAT_RGB24, surface->dirty_rect.width, 1663 surface->dirty_rect.height, surface->stride); 1664 1665 /* Otherwise ARGB32 is needed */ 1666 else { 1667 1668 rect = cairo_image_surface_create_for_data(buffer, 1669 CAIRO_FORMAT_ARGB32, surface->dirty_rect.width, 1670 surface->dirty_rect.height, surface->stride); 1671 1672 /* Clear destination rect first */ 1673 guac_protocol_send_rect(socket, layer, 1674 surface->dirty_rect.x, surface->dirty_rect.y, 1675 surface->dirty_rect.width, surface->dirty_rect.height); 1676 guac_protocol_send_cfill(socket, GUAC_COMP_ROUT, layer, 1677 0x00, 0x00, 0x00, 0xFF); 1678 1679 } 1680 1681 /* Send PNG for rect */ 1682 guac_client_stream_png(surface->client, socket, GUAC_COMP_OVER, 1683 layer, surface->dirty_rect.x, surface->dirty_rect.y, rect); 1684 1685 cairo_surface_destroy(rect); 1686 surface->realized = 1; 1687 1688 /* Surface is no longer dirty */ 1689 surface->dirty = 0; 1690 1691 } 1692 1693} 1694 1695/** 1696 * Returns an appropriate quality between 0 and 100 for lossy encoding 1697 * depending on the current processing lag calculated for the given client. 1698 * 1699 * @param client 1700 * The client for which the lossy quality is being calculated. 1701 * 1702 * @return 1703 * A value between 0 and 100 inclusive which seems appropriate for the 1704 * client based on lag measurements. 1705 */ 1706static int guac_common_surface_suggest_quality(guac_client* client) { 1707 1708 int lag = guac_client_get_processing_lag(client); 1709 1710 /* Scale quality linearly from 90 to 30 as lag varies from 20ms to 80ms */ 1711 int quality = 90 - (lag - 20); 1712 1713 /* Do not exceed 90 for quality */ 1714 if (quality > 90) 1715 return 90; 1716 1717 /* Do not go below 30 for quality */ 1718 if (quality < 30) 1719 return 30; 1720 1721 return quality; 1722 1723} 1724 1725/** 1726 * Flushes the bitmap update currently described by the dirty rectangle within 1727 * the given surface directly via an "img" instruction as JPEG data. The 1728 * resulting instructions will be sent over the socket associated with the 1729 * given surface. 1730 * 1731 * @param surface 1732 * The surface to flush. 1733 */ 1734static void __guac_common_surface_flush_to_jpeg(guac_common_surface* surface) { 1735 1736 if (surface->dirty) { 1737 1738 guac_socket* socket = surface->socket; 1739 const guac_layer* layer = surface->layer; 1740 1741 guac_common_rect max; 1742 guac_common_rect_init(&max, 0, 0, surface->width, surface->height); 1743 1744 /* Expand the dirty rect size to fit in a grid with cells equal to the 1745 * minimum JPEG block size */ 1746 guac_common_rect_expand_to_grid(GUAC_SURFACE_JPEG_BLOCK_SIZE, 1747 &surface->dirty_rect, &max); 1748 1749 /* Get Cairo surface for specified rect */ 1750 unsigned char* buffer = surface->buffer 1751 + surface->dirty_rect.y * surface->stride 1752 + surface->dirty_rect.x * 4; 1753 1754 cairo_surface_t* rect = cairo_image_surface_create_for_data(buffer, 1755 CAIRO_FORMAT_RGB24, surface->dirty_rect.width, 1756 surface->dirty_rect.height, surface->stride); 1757 1758 /* Send JPEG for rect */ 1759 guac_client_stream_jpeg(surface->client, socket, GUAC_COMP_OVER, layer, 1760 surface->dirty_rect.x, surface->dirty_rect.y, rect, 1761 guac_common_surface_suggest_quality(surface->client)); 1762 1763 cairo_surface_destroy(rect); 1764 surface->realized = 1; 1765 1766 /* Surface is no longer dirty */ 1767 surface->dirty = 0; 1768 1769 } 1770 1771} 1772 1773/** 1774 * Flushes the bitmap update currently described by the dirty rectangle within 1775 * the given surface directly via an "img" instruction as WebP data. The 1776 * resulting instructions will be sent over the socket associated with the 1777 * given surface. 1778 * 1779 * @param surface 1780 * The surface to flush. 1781 * 1782 * @param opaque 1783 * Whether the rectangle being flushed contains only fully-opaque pixels. 1784 */ 1785static void __guac_common_surface_flush_to_webp(guac_common_surface* surface, 1786 int opaque) { 1787 1788 if (surface->dirty) { 1789 1790 guac_socket* socket = surface->socket; 1791 const guac_layer* layer = surface->layer; 1792 1793 guac_common_rect max; 1794 guac_common_rect_init(&max, 0, 0, surface->width, surface->height); 1795 1796 /* Expand the dirty rect size to fit in a grid with cells equal to the 1797 * minimum WebP block size */ 1798 guac_common_rect_expand_to_grid(GUAC_SURFACE_WEBP_BLOCK_SIZE, 1799 &surface->dirty_rect, &max); 1800 1801 /* Get Cairo surface for specified rect */ 1802 unsigned char* buffer = surface->buffer 1803 + surface->dirty_rect.y * surface->stride 1804 + surface->dirty_rect.x * 4; 1805 1806 cairo_surface_t* rect; 1807 1808 /* Use RGB24 if the image is fully opaque */ 1809 if (opaque) 1810 rect = cairo_image_surface_create_for_data(buffer, 1811 CAIRO_FORMAT_RGB24, surface->dirty_rect.width, 1812 surface->dirty_rect.height, surface->stride); 1813 1814 /* Otherwise ARGB32 is needed */ 1815 else 1816 rect = cairo_image_surface_create_for_data(buffer, 1817 CAIRO_FORMAT_ARGB32, surface->dirty_rect.width, 1818 surface->dirty_rect.height, surface->stride); 1819 1820 /* Send WebP for rect */ 1821 guac_client_stream_webp(surface->client, socket, GUAC_COMP_OVER, layer, 1822 surface->dirty_rect.x, surface->dirty_rect.y, rect, 1823 guac_common_surface_suggest_quality(surface->client), 1824 surface->lossless ? 1 : 0); 1825 1826 cairo_surface_destroy(rect); 1827 surface->realized = 1; 1828 1829 /* Surface is no longer dirty */ 1830 surface->dirty = 0; 1831 1832 } 1833 1834} 1835 1836/** 1837 * Comparator for instances of guac_common_surface_bitmap_rect, the elements 1838 * which make up a surface's bitmap buffer. 1839 * 1840 * @see qsort 1841 */ 1842static int __guac_common_surface_bitmap_rect_compare(const void* a, const void* b) { 1843 1844 guac_common_surface_bitmap_rect* ra = (guac_common_surface_bitmap_rect*) a; 1845 guac_common_surface_bitmap_rect* rb = (guac_common_surface_bitmap_rect*) b; 1846 1847 /* Order roughly top to bottom, left to right */ 1848 if (ra->rect.y != rb->rect.y) return ra->rect.y - rb->rect.y; 1849 if (ra->rect.x != rb->rect.x) return ra->rect.x - rb->rect.x; 1850 1851 /* Wider updates should come first (more likely to intersect later) */ 1852 if (ra->rect.width != rb->rect.width) return rb->rect.width - ra->rect.width; 1853 1854 /* Shorter updates should come first (less likely to increase cost) */ 1855 return ra->rect.height - rb->rect.height; 1856 1857} 1858 1859/** 1860 * Flushes only the properties of the given surface, such as layer location or 1861 * opacity. Image state is not flushed. If the surface represents a buffer or 1862 * the default layer, this function has no effect. 1863 * 1864 * @param surface 1865 * The surface to flush. 1866 */ 1867static void __guac_common_surface_flush_properties( 1868 guac_common_surface* surface) { 1869 1870 guac_socket* socket = surface->socket; 1871 1872 /* Only applicable to non-default visible layers */ 1873 if (surface->layer->index <= 0) 1874 return; 1875 1876 /* Flush opacity */ 1877 if (surface->opacity_dirty) { 1878 guac_protocol_send_shade(socket, surface->layer, surface->opacity); 1879 surface->opacity_dirty = 0; 1880 } 1881 1882 /* Flush location and hierarchy */ 1883 if (surface->location_dirty) { 1884 guac_protocol_send_move(socket, surface->layer, 1885 surface->parent, surface->x, surface->y, surface->z); 1886 surface->location_dirty = 0; 1887 } 1888 1889} 1890 1891static void __guac_common_surface_flush(guac_common_surface* surface) { 1892 1893 /* Flush final dirty rectangle to queue. */ 1894 __guac_common_surface_flush_to_queue(surface); 1895 1896 guac_common_surface_bitmap_rect* current = surface->bitmap_queue; 1897 int i, j; 1898 int original_queue_length; 1899 int flushed = 0; 1900 1901 original_queue_length = surface->bitmap_queue_length; 1902 1903 /* Sort updates to make combination less costly */ 1904 qsort(surface->bitmap_queue, surface->bitmap_queue_length, sizeof(guac_common_surface_bitmap_rect), 1905 __guac_common_surface_bitmap_rect_compare); 1906 1907 /* Flush all rects in queue */ 1908 for (i=0; i < surface->bitmap_queue_length; i++) { 1909 1910 /* Get next unflushed candidate */ 1911 guac_common_surface_bitmap_rect* candidate = current; 1912 if (!candidate->flushed) { 1913 1914 int combined = 0; 1915 1916 /* Build up rect as much as possible */ 1917 for (j=i; j < surface->bitmap_queue_length; j++) { 1918 1919 if (!candidate->flushed) { 1920 1921 /* Clip candidate within current bounds */ 1922 __guac_common_bound_rect(surface, &candidate->rect, NULL, NULL); 1923 if (candidate->rect.width <= 0 || candidate->rect.height <= 0) 1924 candidate->flushed = 1; 1925 1926 /* Combine if reasonable */ 1927 else if (__guac_common_should_combine(surface, &candidate->rect, 0) || !surface->dirty) { 1928 __guac_common_mark_dirty(surface, &candidate->rect); 1929 candidate->flushed = 1; 1930 combined++; 1931 } 1932 1933 } 1934 1935 candidate++; 1936 1937 } 1938 1939 /* Re-add to queue if there's room and this update was modified or we expect others might be */ 1940 if ((combined > 1 || i < original_queue_length) 1941 && surface->bitmap_queue_length < GUAC_COMMON_SURFACE_QUEUE_SIZE) 1942 __guac_common_surface_flush_to_queue(surface); 1943 1944 /* Flush as bitmap otherwise */ 1945 else if (surface->dirty) { 1946 1947 flushed++; 1948 1949 int opaque = __guac_common_surface_is_opaque(surface, 1950 &surface->dirty_rect); 1951 1952 /* Prefer WebP when reasonable */ 1953 if (__guac_common_surface_should_use_webp(surface, 1954 &surface->dirty_rect)) 1955 __guac_common_surface_flush_to_webp(surface, opaque); 1956 1957 /* If not WebP, JPEG is the next best (lossy) choice */ 1958 else if (opaque && __guac_common_surface_should_use_jpeg( 1959 surface, &surface->dirty_rect)) 1960 __guac_common_surface_flush_to_jpeg(surface); 1961 1962 /* Use PNG if no lossy formats are appropriate */ 1963 else 1964 __guac_common_surface_flush_to_png(surface, opaque); 1965 1966 } 1967 1968 } 1969 1970 current++; 1971 1972 } 1973 1974 /* Flush complete */ 1975 surface->bitmap_queue_length = 0; 1976 1977} 1978 1979void guac_common_surface_flush(guac_common_surface* surface) { 1980 1981 pthread_mutex_lock(&surface->_lock); 1982 1983 /* Flush any applicable layer properties */ 1984 __guac_common_surface_flush_properties(surface); 1985 1986 /* Flush surface contents */ 1987 __guac_common_surface_flush(surface); 1988 1989 pthread_mutex_unlock(&surface->_lock); 1990 1991} 1992 1993void guac_common_surface_dup(guac_common_surface* surface, 1994 guac_client* client, guac_socket* socket) { 1995 1996 pthread_mutex_lock(&surface->_lock); 1997 1998 /* Do nothing if not realized */ 1999 if (!surface->realized) 2000 goto complete; 2001 2002 /* Synchronize layer-specific properties if applicable */ 2003 if (surface->layer->index > 0) { 2004 2005 /* Synchronize opacity */ 2006 guac_protocol_send_shade(socket, surface->layer, surface->opacity); 2007 2008 /* Synchronize location and hierarchy */ 2009 guac_protocol_send_move(socket, surface->layer, 2010 surface->parent, surface->x, surface->y, surface->z); 2011 2012 /* Synchronize multi-touch support level */ 2013 guac_protocol_send_set_int(surface->socket, surface->layer, 2014 GUAC_PROTOCOL_LAYER_PARAMETER_MULTI_TOUCH, 2015 surface->touches); 2016 2017 } 2018 2019 /* Sync size to new socket */ 2020 guac_protocol_send_size(socket, surface->layer, 2021 surface->width, surface->height); 2022 2023 /* Send contents of layer, if non-empty */ 2024 if (surface->width > 0 && surface->height > 0) { 2025 2026 /* Get entire surface */ 2027 cairo_surface_t* rect = cairo_image_surface_create_for_data( 2028 surface->buffer, CAIRO_FORMAT_ARGB32, 2029 surface->width, surface->height, surface->stride); 2030 2031 /* Send PNG for rect */ 2032 guac_client_stream_png(client, socket, GUAC_COMP_OVER, surface->layer, 2033 0, 0, rect); 2034 cairo_surface_destroy(rect); 2035 2036 } 2037 2038complete: 2039 pthread_mutex_unlock(&surface->_lock); 2040 2041}