display-flatten.c (6403B)
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 "display.h" 22#include "layer.h" 23#include "log.h" 24 25#include <cairo/cairo.h> 26#include <guacamole/client.h> 27 28#include <assert.h> 29#include <stdlib.h> 30#include <string.h> 31 32/** 33 * The Guacamole video encoder display related to the current qsort() 34 * operation. As qsort() does not provide a means of passing arbitrary data to 35 * the comparitor, this value must be set prior to invoking qsort() with 36 * guacenc_display_layer_comparator. 37 */ 38guacenc_display* __qsort_display; 39 40/** 41 * Comparator which orders layer pointers such that (1) NULL pointers are last, 42 * (2) layers with the same parent_index are adjacent, and (3) layers with the 43 * same parent_index are ordered by Z. 44 * 45 * @see qsort() 46 */ 47static int guacenc_display_layer_comparator(const void* a, const void* b) { 48 49 guacenc_layer* layer_a = *((guacenc_layer**) a); 50 guacenc_layer* layer_b = *((guacenc_layer**) b); 51 52 /* If a is NULL, sort it to bottom */ 53 if (layer_a == NULL) { 54 55 /* ... unless b is also NULL, in which case they are equal */ 56 if (layer_b == NULL) 57 return 0; 58 59 return 1; 60 } 61 62 /* If b is NULL (and a is not NULL), sort it to bottom */ 63 if (layer_b == NULL) 64 return -1; 65 66 /* Order such that the deepest layers are first */ 67 int a_depth = guacenc_display_get_depth(__qsort_display, layer_a); 68 int b_depth = guacenc_display_get_depth(__qsort_display, layer_b); 69 if (b_depth != a_depth) 70 return b_depth - a_depth; 71 72 /* Order such that sibling layers are adjacent */ 73 if (layer_b->parent_index != layer_a->parent_index) 74 return layer_b->parent_index - layer_a->parent_index; 75 76 /* Order sibling layers according to descending Z */ 77 return layer_b->z - layer_a->z; 78 79} 80 81/** 82 * Renders the mouse cursor on top of the frame buffer of the default layer of 83 * the given display. 84 * 85 * @param display 86 * The display whose mouse cursor should be rendered to the frame buffer 87 * of its default layer. 88 * 89 * @return 90 * Zero if rendering succeeds, non-zero otherwise. 91 */ 92static int guacenc_display_render_cursor(guacenc_display* display) { 93 94 guacenc_cursor* cursor = display->cursor; 95 96 /* Do not render cursor if coordinates are negative */ 97 if (cursor->x < 0 || cursor->y < 0) 98 return 0; 99 100 /* Retrieve default layer (guaranteed to not be NULL) */ 101 guacenc_layer* def_layer = guacenc_display_get_layer(display, 0); 102 assert(def_layer != NULL); 103 104 /* Get source and destination buffers */ 105 guacenc_buffer* src = cursor->buffer; 106 guacenc_buffer* dst = def_layer->frame; 107 108 /* Render cursor to layer */ 109 if (src->width > 0 && src->height > 0) { 110 cairo_set_source_surface(dst->cairo, src->surface, 111 cursor->x - cursor->hotspot_x, 112 cursor->y - cursor->hotspot_y); 113 cairo_rectangle(dst->cairo, 114 cursor->x - cursor->hotspot_x, 115 cursor->y - cursor->hotspot_y, 116 src->width, src->height); 117 cairo_fill(dst->cairo); 118 } 119 120 /* Always succeeds */ 121 return 0; 122 123} 124 125int guacenc_display_flatten(guacenc_display* display) { 126 127 int i; 128 guacenc_layer* render_order[GUACENC_DISPLAY_MAX_LAYERS]; 129 130 /* Copy list of layers within display */ 131 memcpy(render_order, display->layers, sizeof(render_order)); 132 133 /* Sort layers by depth, parent, and Z */ 134 __qsort_display = display; 135 qsort(render_order, GUACENC_DISPLAY_MAX_LAYERS, sizeof(guacenc_layer*), 136 guacenc_display_layer_comparator); 137 138 /* Reset layer frame buffers */ 139 for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) { 140 141 /* Pull current layer, ignoring unallocated layers */ 142 guacenc_layer* layer = render_order[i]; 143 if (layer == NULL) 144 continue; 145 146 /* Get source buffer and destination frame buffer */ 147 guacenc_buffer* buffer = layer->buffer; 148 guacenc_buffer* frame = layer->frame; 149 150 /* Reset frame contents */ 151 guacenc_buffer_copy(frame, buffer); 152 153 } 154 155 /* Render each layer, in order */ 156 for (i = 0; i < GUACENC_DISPLAY_MAX_LAYERS; i++) { 157 158 /* Pull current layer, ignoring unallocated layers */ 159 guacenc_layer* layer = render_order[i]; 160 if (layer == NULL) 161 continue; 162 163 /* Skip fully-transparent layers */ 164 if (layer->opacity == 0) 165 continue; 166 167 /* Ignore layers without a parent */ 168 int parent_index = layer->parent_index; 169 if (parent_index == GUACENC_LAYER_NO_PARENT) 170 continue; 171 172 /* Retrieve parent layer, ignoring layers with invalid parents */ 173 guacenc_layer* parent = guacenc_display_get_layer(display, parent_index); 174 if (parent == NULL) 175 continue; 176 177 /* Get source and destination frame buffer */ 178 guacenc_buffer* src = layer->frame; 179 guacenc_buffer* dst = parent->frame; 180 181 /* Ignore layers with empty buffers */ 182 cairo_surface_t* surface = src->surface; 183 if (surface == NULL) 184 continue; 185 186 /* Ignore if parent has no pixels */ 187 cairo_t* cairo = dst->cairo; 188 if (cairo == NULL) 189 continue; 190 191 /* Render buffer to layer */ 192 cairo_reset_clip(cairo); 193 cairo_rectangle(cairo, layer->x, layer->y, src->width, src->height); 194 cairo_clip(cairo); 195 196 cairo_set_source_surface(cairo, surface, layer->x, layer->y); 197 cairo_paint_with_alpha(cairo, layer->opacity / 255.0); 198 199 } 200 201 /* Render cursor on top of everything else */ 202 return guacenc_display_render_cursor(display); 203 204} 205