blob: 2d372fe67f2553725155c1beb9213a2cbc6a7d31 [file]
/*
* 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 "guacamole/rect.h"
/**
* Given a bitmask that is one less than a power of two (ie: 0xF, 0x1F, etc.),
* rounds the given value in the negative direction to the nearest multiple of
* that power of two. Positive values are rounded down towards zero while
* negative values are rounded up toward negative values of greater magnitude.
*
* @param value
* The value to round.
*
* @param mask
* A bitmask whose integer value is one less than a power of two.
*
* @return
* The given value, rounded to the nearest multiple of the power of two
* represented by the given mask, where that rounding is performed in the
* negative direction.
*/
#define GUAC_RECT_ROUND_NEG(value, mask) (value & ~mask)
/**
* Given a bitmask that is one less than a power of two (ie: 0xF, 0x1F, etc.),
* rounds the given value in the positive direction to the nearest multiple of
* that power of two. Negative values are rounded down towards zero while
* positive values are rounded up toward positive values of greater magnitude.
*
* @param value
* The value to round.
*
* @param mask
* A bitmask whose integer value is one less than a power of two.
*
* @return
* The given value, rounded to the nearest multiple of the power of two
* represented by the given mask, where that rounding is performed in the
* positive direction.
*/
#define GUAC_RECT_ROUND_POS(value, mask) ((value + mask) & ~mask)
void guac_rect_init(guac_rect* rect, int x, int y, int width, int height) {
*rect = (guac_rect) {
.left = x,
.top = y,
.right = width > 0 ? x + width : x,
.bottom = height > 0 ? y + height : y
};
}
void guac_rect_extend(guac_rect* rect, const guac_rect* min) {
/* The union of an empty rect and the provided rect should be that provided
* rect. Considering the garbage coordinates that may be present in an
* empty rect can otherwise produce incorrect results. */
if (guac_rect_is_empty(rect)) {
*rect = *min;
return;
}
/* Extend edges of rectangle such that it contains the provided minimum
* rectangle */
if (min->left < rect->left) rect->left = min->left;
if (min->top < rect->top) rect->top = min->top;
if (min->right > rect->right) rect->right = min->right;
if (min->bottom > rect->bottom) rect->bottom = min->bottom;
}
void guac_rect_constrain(guac_rect* rect, const guac_rect* max) {
/* Shrink edges of rectangle such that it is contained by the provided
* maximum rectangle */
if (max->left > rect->left) rect->left = max->left;
if (max->top > rect->top) rect->top = max->top;
if (max->right < rect->right) rect->right = max->right;
if (max->bottom < rect->bottom) rect->bottom = max->bottom;
}
void guac_rect_shrink(guac_rect* rect, int max_width, int max_height) {
int original_width = guac_rect_width(rect);
int original_height = guac_rect_height(rect);
/* Shrink only; do not _expand_ to reach the max width/height */
if (original_width < max_width) max_width = original_width;
if (original_height < max_height) max_height = original_height;
/* BOTH the width and height must be adjusted by the same factor in
* order to preserve aspect ratio. Choosing the smallest adjustment
* factor guarantees that the rectangle will be within bounds while
* preserving aspect ratio to the greatest degree possible (there
* is unavoidable integer rounding error). */
int scale_numerator, scale_denominator;
/* NOTE: The following test is mathematically equivalent to:
*
* if (max_width / original_width < max_height / original_height) {
* ...
* }
*
* but does not require floating point arithmetic. */
if (max_width * original_height < max_height * original_width) {
scale_numerator = max_width;
scale_denominator = original_width;
}
else {
scale_numerator = max_height;
scale_denominator = original_height;
}
rect->right = rect->left + original_width * scale_numerator / scale_denominator;
rect->bottom = rect->top + original_height * scale_numerator / scale_denominator;
}
void guac_rect_align(guac_rect* rect, unsigned int bits) {
if (bits == 0)
return;
int factor = 1 << bits;
int mask = factor - 1;
/* Expand and shift rectangle as necessary for its edges to be aligned
* along multiples of the given power of two */
rect->left = GUAC_RECT_ROUND_NEG(rect->left, mask);
rect->top = GUAC_RECT_ROUND_NEG(rect->top, mask);
rect->right = GUAC_RECT_ROUND_POS(rect->right, mask);
rect->bottom = GUAC_RECT_ROUND_POS(rect->bottom, mask);
}
int guac_rect_intersects(const guac_rect* a, const guac_rect* b) {
/* Two rectangles intersect if neither rectangle is wholly outside the
* other */
return !(
b->right <= a->left || a->right <= b->left
|| b->bottom <= a->top || a->bottom <= b->top
);
}
int guac_rect_is_empty(const guac_rect* rect) {
return rect->right <= rect->left || rect->bottom <= rect->top;
}
int guac_rect_width(const guac_rect* rect) {
int width = rect->right - rect->left;
return width > 0 ? width : 0;
}
int guac_rect_height(const guac_rect* rect) {
int height = rect->bottom - rect->top;
return height > 0 ? height : 0;
}