| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "display-plan.h" |
| #include "display-priv.h" |
| #include "guacamole/display.h" |
| #include "guacamole/mem.h" |
| #include "guacamole/rect.h" |
| |
| #include <string.h> |
| #include <stdint.h> |
| |
| /** |
| * Rounds the given value down to the nearest power of two. |
| * |
| * @param value |
| * The value to round. |
| * |
| * @return |
| * The power of two that is closest to the given value without exceeding |
| * that value. |
| */ |
| static size_t guac_display_plan_round_pot(size_t value) { |
| |
| if (value <= 2) |
| return value; |
| |
| size_t rounded = 1; |
| while (value >>= 1) |
| rounded <<= 1; |
| |
| return rounded; |
| |
| } |
| |
| /** |
| * Returns whether the given buffer consists entirely of the same 32-bit |
| * quantity (ie: a single ARGB pixel), repeated throughout the buffer. |
| * |
| * This function attempts to perform a fast comparison leveraging memcmp() to |
| * reduce the search space, rather than simply looping through each pixel one |
| * at a time. Basic benchmarks show this approach to be roughly twice as fast |
| * as a simple loop for arbitrary buffer lengths and four times as fast for |
| * buffer lengths that are powers of two. |
| * |
| * @param buffer |
| * The buffer to check. |
| * |
| * @param length |
| * The number of bytes in the buffer. |
| * |
| * @param color |
| * A pointer to a uint32_t to receive the value of the 32-bit quantity that |
| * is repeated, if applicable. |
| * |
| * @return |
| * Non-zero if the same 32-bit quantity is repeated throughout the buffer, |
| * zero otherwise. If the same value is indeed repeated throughout the |
| * buffer, that value is stored in the variable pointed to by the "color" |
| * pointer. If the value is not repeated, the variable pointed to by the |
| * "color" pointer is left untouched. |
| */ |
| static int guac_display_plan_is_single_color(const unsigned char* restrict buffer, |
| size_t length, uint32_t* restrict color) { |
| |
| /* It is vacuously true that all the 32-bit quantities in an empty buffer |
| * are the same */ |
| if (length == 0) { |
| *color = 0x00000000; |
| return 1; |
| } |
| |
| /* A single 32-bit value is the same as itself */ |
| if (length == 4) { |
| *color = ((const uint32_t*) buffer)[0]; |
| return 1; |
| } |
| |
| /* Simply directly compare if there are only two values */ |
| if (length == 8) { |
| uint32_t a = ((const uint32_t*) buffer)[0]; |
| uint32_t b = ((const uint32_t*) buffer)[1]; |
| if (a == b) { |
| *color = a; |
| return 1; |
| } |
| } |
| |
| /* For all other lengths, avoid comparing if finding a match is impossible. |
| * A buffer can consist entirely of the same 32-bit (4-byte) quantity |
| * repeated throughout the buffer only if that buffer's length is a |
| * multiple of 4. */ |
| if ((length % 4) != 0) |
| return 0; |
| |
| /* A buffer consists entirely of the same 32-bit quantity repeated |
| * throughout if (1) the two halves of the buffer are the same and (2) one |
| * of those halves is known to consist entirely of the same 32-bit quantity |
| * repeated throughout. */ |
| |
| size_t pot_length = guac_display_plan_round_pot(guac_mem_ckd_sub_or_die(length, 1)); |
| size_t remaining_length = guac_mem_ckd_sub_or_die(length, pot_length); |
| |
| /* Easiest recursive case: the buffer is already a power of two and can be |
| * split into two very easy-to-compare halves */ |
| if (pot_length == remaining_length) { |
| return !memcmp(buffer, buffer + pot_length, pot_length) |
| && guac_display_plan_is_single_color(buffer, pot_length, color); |
| } |
| |
| /* For buffers that can't be split into two power-of-two halves, decide |
| * based on one easy power-of-two case and one not-so-easy case of whatever |
| * remains */ |
| uint32_t color_a = 0, color_b = 0; |
| if (guac_display_plan_is_single_color(buffer, pot_length, &color_a) |
| && guac_display_plan_is_single_color(buffer + pot_length, remaining_length, &color_b) |
| && color_a == color_b) { |
| |
| *color = color_a; |
| return 1; |
| |
| } |
| |
| return 0; |
| |
| } |
| |
| /** |
| * Returns whether the given rectangle within given buffer consists entirely of |
| * the same 32-bit quantity (ie: a single ARGB pixel), repeated throughout the |
| * rectangular region. |
| * |
| * This function attempts to perform a fast comparison leveraging memcmp() to |
| * reduce the search space, rather than simply looping through each pixel one |
| * at a time. Basic benchmarks show this approach to be roughly twice as fast |
| * as a simple loop for arbitrary buffer lengths and four times as fast for |
| * buffer lengths that are powers of two. |
| * |
| * @param buffer |
| * The buffer to check. |
| * |
| * @param stride |
| * The number of bytes in each row of image data within the buffer. |
| * |
| * @param rect |
| * The rectangle representing the region to be checked within the buffer. |
| * |
| * @param color |
| * A pointer to a uint32_t to receive the value of the 32-bit quantity that |
| * is repeated, if applicable. |
| * |
| * @return |
| * Non-zero if the same 32-bit quantity is repeated throughout the |
| * rectangular region, zero otherwise. If the same value is indeed repeated |
| * throughout the rectangle, that value is stored in the variable pointed |
| * to by the "color" pointer. If the value is not repeated, the variable |
| * pointed to by the "color" pointer is left untouched. |
| */ |
| static int guac_display_plan_is_rect_single_color(const unsigned char* restrict buffer, |
| size_t stride, const guac_rect* restrict rect, uint32_t* restrict color) { |
| |
| size_t row_length = guac_mem_ckd_mul_or_die(guac_rect_width(rect), GUAC_DISPLAY_LAYER_RAW_BPP); |
| buffer = GUAC_RECT_CONST_BUFFER(*rect, buffer, stride, GUAC_DISPLAY_LAYER_RAW_BPP); |
| |
| /* Verify that the first row consists of a single color */ |
| uint32_t first_color = 0x00000000; |
| if (!guac_display_plan_is_single_color(buffer, row_length, &first_color)) |
| return 0; |
| |
| /* The whole rectangle consists of a single color if each row is identical |
| * and it's already known that one of those rows consists of the a single |
| * color */ |
| const unsigned char* previous = buffer; |
| for (int y = rect->top + 1; y < rect->bottom; y++) { |
| |
| const unsigned char* current = previous + stride; |
| if (memcmp(previous, current, row_length)) |
| return 0; |
| |
| previous = current; |
| |
| } |
| |
| *color = first_color; |
| return 1; |
| |
| } |
| |
| void PFR_guac_display_plan_rewrite_as_rects(guac_display_plan* plan) { |
| |
| uint32_t color = 0x00000000; |
| |
| guac_display_plan_operation* op = plan->ops; |
| for (int i = 0; i < plan->length; i++) { |
| |
| if (op->type == GUAC_DISPLAY_PLAN_OPERATION_IMG) { |
| |
| guac_display_layer* layer = op->layer; |
| size_t stride = layer->pending_frame.buffer_stride; |
| const unsigned char* buffer = layer->pending_frame.buffer; |
| |
| /* NOTE: Processing of operations referring to layers whose buffers |
| * have been replaced with NULL is intentionally allowed to ensure |
| * references to external buffers can be safely removed if |
| * necessary, even before guac_display is freed */ |
| |
| if (buffer != NULL && guac_display_plan_is_rect_single_color(buffer, stride, &op->dest, &color)) { |
| |
| /* Ignore alpha channel for opaque layers */ |
| if (layer->opaque) |
| color |= 0xFF000000; |
| |
| op->type = GUAC_DISPLAY_PLAN_OPERATION_RECT; |
| op->src.color = color; |
| |
| } |
| |
| } |
| |
| op++; |
| |
| } |
| |
| } |