src/libguac/display-plan-rect.c (85 lines of code) (raw):
/*
* 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++;
}
}