mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-02-16 08:30:41 +01:00
601 lines
21 KiB
C
601 lines
21 KiB
C
/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
|
|
/* cairo - a vector graphics library with display and print output
|
|
*
|
|
* Copyright © 2002 University of Southern California
|
|
* Copyright © 2005 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it either under the terms of the GNU Lesser General Public
|
|
* License version 2.1 as published by the Free Software Foundation
|
|
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
|
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
|
* notice, a recipient may use your version of this file under either
|
|
* the MPL or the LGPL.
|
|
*
|
|
* You should have received a copy of the LGPL along with this library
|
|
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* You should have received a copy of the MPL along with this library
|
|
* in the file COPYING-MPL-1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.1 (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.mozilla.org/MPL/
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
|
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
|
* the specific language governing rights and limitations.
|
|
*
|
|
* The Original Code is the cairo graphics library.
|
|
*
|
|
* The Initial Developer of the Original Code is University of Southern
|
|
* California.
|
|
*
|
|
* Contributor(s):
|
|
* Carl D. Worth <cworth@cworth.org>
|
|
*/
|
|
|
|
#include "cairoint.h"
|
|
|
|
COMPILE_TIME_ASSERT (CAIRO_STATUS_LAST_STATUS < CAIRO_INT_STATUS_UNSUPPORTED);
|
|
COMPILE_TIME_ASSERT (CAIRO_INT_STATUS_LAST_STATUS <= 127);
|
|
|
|
/* Public stuff */
|
|
|
|
/**
|
|
* cairo_status_to_string:
|
|
* @status: a cairo status
|
|
*
|
|
* Provides a human-readable description of a #cairo_status_t.
|
|
*
|
|
* Returns: a string representation of the status
|
|
*/
|
|
const char *
|
|
cairo_status_to_string (cairo_status_t status)
|
|
{
|
|
switch (status) {
|
|
case CAIRO_STATUS_SUCCESS:
|
|
return "success";
|
|
case CAIRO_STATUS_NO_MEMORY:
|
|
return "out of memory";
|
|
case CAIRO_STATUS_INVALID_RESTORE:
|
|
return "cairo_restore without matching cairo_save";
|
|
case CAIRO_STATUS_INVALID_POP_GROUP:
|
|
return "cairo_pop_group without matching cairo_push_group";
|
|
case CAIRO_STATUS_NO_CURRENT_POINT:
|
|
return "no current point defined";
|
|
case CAIRO_STATUS_INVALID_MATRIX:
|
|
return "invalid matrix (not invertible)";
|
|
case CAIRO_STATUS_INVALID_STATUS:
|
|
return "invalid value for an input cairo_status_t";
|
|
case CAIRO_STATUS_NULL_POINTER:
|
|
return "NULL pointer";
|
|
case CAIRO_STATUS_INVALID_STRING:
|
|
return "input string not valid UTF-8";
|
|
case CAIRO_STATUS_INVALID_PATH_DATA:
|
|
return "input path data not valid";
|
|
case CAIRO_STATUS_READ_ERROR:
|
|
return "error while reading from input stream";
|
|
case CAIRO_STATUS_WRITE_ERROR:
|
|
return "error while writing to output stream";
|
|
case CAIRO_STATUS_SURFACE_FINISHED:
|
|
return "the target surface has been finished";
|
|
case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
|
|
return "the surface type is not appropriate for the operation";
|
|
case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
|
|
return "the pattern type is not appropriate for the operation";
|
|
case CAIRO_STATUS_INVALID_CONTENT:
|
|
return "invalid value for an input cairo_content_t";
|
|
case CAIRO_STATUS_INVALID_FORMAT:
|
|
return "invalid value for an input cairo_format_t";
|
|
case CAIRO_STATUS_INVALID_VISUAL:
|
|
return "invalid value for an input Visual*";
|
|
case CAIRO_STATUS_FILE_NOT_FOUND:
|
|
return "file not found";
|
|
case CAIRO_STATUS_INVALID_DASH:
|
|
return "invalid value for a dash setting";
|
|
case CAIRO_STATUS_INVALID_DSC_COMMENT:
|
|
return "invalid value for a DSC comment";
|
|
case CAIRO_STATUS_INVALID_INDEX:
|
|
return "invalid index passed to getter";
|
|
case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
|
|
return "clip region not representable in desired format";
|
|
case CAIRO_STATUS_TEMP_FILE_ERROR:
|
|
return "error creating or writing to a temporary file";
|
|
case CAIRO_STATUS_INVALID_STRIDE:
|
|
return "invalid value for stride";
|
|
case CAIRO_STATUS_FONT_TYPE_MISMATCH:
|
|
return "the font type is not appropriate for the operation";
|
|
case CAIRO_STATUS_USER_FONT_IMMUTABLE:
|
|
return "the user-font is immutable";
|
|
case CAIRO_STATUS_USER_FONT_ERROR:
|
|
return "error occurred in a user-font callback function";
|
|
case CAIRO_STATUS_NEGATIVE_COUNT:
|
|
return "negative number used where it is not allowed";
|
|
case CAIRO_STATUS_INVALID_CLUSTERS:
|
|
return "input clusters do not represent the accompanying text and glyph arrays";
|
|
case CAIRO_STATUS_INVALID_SLANT:
|
|
return "invalid value for an input #cairo_font_slant_t";
|
|
case CAIRO_STATUS_INVALID_WEIGHT:
|
|
return "input value for an input #cairo_font_weight_t";
|
|
}
|
|
|
|
return "<unknown error status>";
|
|
}
|
|
|
|
|
|
/**
|
|
* cairo_glyph_allocate:
|
|
* @num_glyphs: number of glyphs to allocate
|
|
*
|
|
* Allocates an array of #cairo_glyph_t's.
|
|
* This function is only useful in implementations of
|
|
* #cairo_user_scaled_font_text_to_glyphs_func_t where the user
|
|
* needs to allocate an array of glyphs that cairo will free.
|
|
* For all other uses, user can use their own allocation method
|
|
* for glyphs.
|
|
*
|
|
* This function returns %NULL if @num_glyphs is not positive,
|
|
* or if out of memory. That means, the %NULL return value
|
|
* signals out-of-memory only if @num_glyphs was positive.
|
|
*
|
|
* Returns: the newly allocated array of glyphs that should be
|
|
* freed using cairo_glyph_free()
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
cairo_glyph_t *
|
|
cairo_glyph_allocate (int num_glyphs)
|
|
{
|
|
if (num_glyphs <= 0)
|
|
return NULL;
|
|
|
|
return _cairo_malloc_ab (num_glyphs, sizeof (cairo_glyph_t));
|
|
}
|
|
slim_hidden_def (cairo_glyph_allocate);
|
|
|
|
/**
|
|
* cairo_glyph_free:
|
|
* @glyphs: array of glyphs to free, or %NULL
|
|
*
|
|
* Frees an array of #cairo_glyph_t's allocated using cairo_glyph_allocate().
|
|
* This function is only useful to free glyph array returned
|
|
* by cairo_scaled_font_text_to_glyphs() where cairo returns
|
|
* an array of glyphs that the user will free.
|
|
* For all other uses, user can use their own allocation method
|
|
* for glyphs.
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
void
|
|
cairo_glyph_free (cairo_glyph_t *glyphs)
|
|
{
|
|
if (glyphs)
|
|
free (glyphs);
|
|
}
|
|
slim_hidden_def (cairo_glyph_free);
|
|
|
|
/**
|
|
* cairo_text_cluster_allocate:
|
|
* @num_clusters: number of text_clusters to allocate
|
|
*
|
|
* Allocates an array of #cairo_text_cluster_t's.
|
|
* This function is only useful in implementations of
|
|
* #cairo_user_scaled_font_text_to_glyphs_func_t where the user
|
|
* needs to allocate an array of text clusters that cairo will free.
|
|
* For all other uses, user can use their own allocation method
|
|
* for text clusters.
|
|
*
|
|
* This function returns %NULL if @num_clusters is not positive,
|
|
* or if out of memory. That means, the %NULL return value
|
|
* signals out-of-memory only if @num_clusters was positive.
|
|
*
|
|
* Returns: the newly allocated array of text clusters that should be
|
|
* freed using cairo_text_cluster_free()
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
cairo_text_cluster_t *
|
|
cairo_text_cluster_allocate (int num_clusters)
|
|
{
|
|
if (num_clusters <= 0)
|
|
return NULL;
|
|
|
|
return _cairo_malloc_ab (num_clusters, sizeof (cairo_text_cluster_t));
|
|
}
|
|
slim_hidden_def (cairo_text_cluster_allocate);
|
|
|
|
/**
|
|
* cairo_text_cluster_free:
|
|
* @clusters: array of text clusters to free, or %NULL
|
|
*
|
|
* Frees an array of #cairo_text_cluster's allocated using cairo_text_cluster_allocate().
|
|
* This function is only useful to free text cluster array returned
|
|
* by cairo_scaled_font_text_to_glyphs() where cairo returns
|
|
* an array of text clusters that the user will free.
|
|
* For all other uses, user can use their own allocation method
|
|
* for text clusters.
|
|
*
|
|
* Since: 1.8
|
|
*/
|
|
void
|
|
cairo_text_cluster_free (cairo_text_cluster_t *clusters)
|
|
{
|
|
if (clusters)
|
|
free (clusters);
|
|
}
|
|
slim_hidden_def (cairo_text_cluster_free);
|
|
|
|
|
|
/* Private stuff */
|
|
|
|
/**
|
|
* _cairo_validate_text_clusters:
|
|
* @utf8: UTF-8 text
|
|
* @utf8_len: length of @utf8 in bytes
|
|
* @glyphs: array of glyphs
|
|
* @num_glyphs: number of glyphs
|
|
* @clusters: array of cluster mapping information
|
|
* @num_clusters: number of clusters in the mapping
|
|
* @backward: whether the text to glyphs mapping goes backward
|
|
*
|
|
* Check that clusters cover the entire glyphs and utf8 arrays,
|
|
* and that cluster boundaries are UTF-8 boundaries.
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS upon success, or
|
|
* %CAIRO_STATUS_INVALID_CLUSTERS on error.
|
|
* The error is either invalid UTF-8 input,
|
|
* or bad cluster mapping.
|
|
*/
|
|
cairo_status_t
|
|
_cairo_validate_text_clusters (const char *utf8,
|
|
int utf8_len,
|
|
const cairo_glyph_t *glyphs,
|
|
int num_glyphs,
|
|
const cairo_text_cluster_t *clusters,
|
|
int num_clusters,
|
|
cairo_bool_t backward)
|
|
{
|
|
cairo_status_t status;
|
|
unsigned int n_bytes = 0;
|
|
unsigned int n_glyphs = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < num_clusters; i++) {
|
|
int cluster_bytes = clusters[i].num_bytes;
|
|
int cluster_glyphs = clusters[i].num_glyphs;
|
|
|
|
if (cluster_bytes < 0 || cluster_glyphs < 0)
|
|
goto BAD;
|
|
|
|
/* A cluster should cover at least one character or glyph.
|
|
* I can't see any use for a 0,0 cluster.
|
|
* I can't see an immediate use for a zero-text cluster
|
|
* right now either, but they don't harm.
|
|
* Zero-glyph clusters on the other hand are useful for
|
|
* things like U+200C ZERO WIDTH NON-JOINER */
|
|
if (cluster_bytes == 0 && cluster_glyphs == 0)
|
|
goto BAD;
|
|
|
|
/* Since n_bytes and n_glyphs are unsigned, but the rest of
|
|
* values involved are signed, we can detect overflow easily */
|
|
if (n_bytes+cluster_bytes > (unsigned int)utf8_len || n_glyphs+cluster_glyphs > (unsigned int)num_glyphs)
|
|
goto BAD;
|
|
|
|
/* Make sure we've got valid UTF-8 for the cluster */
|
|
status = _cairo_utf8_to_ucs4 (utf8+n_bytes, cluster_bytes, NULL, NULL);
|
|
if (status)
|
|
return CAIRO_STATUS_INVALID_CLUSTERS;
|
|
|
|
n_bytes += cluster_bytes ;
|
|
n_glyphs += cluster_glyphs;
|
|
}
|
|
|
|
if (n_bytes != (unsigned int) utf8_len || n_glyphs != (unsigned int) num_glyphs) {
|
|
BAD:
|
|
return CAIRO_STATUS_INVALID_CLUSTERS;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* _cairo_operator_bounded_by_mask:
|
|
* @op: a #cairo_operator_t
|
|
*
|
|
* A bounded operator is one where mask pixel
|
|
* of zero results in no effect on the destination image.
|
|
*
|
|
* Unbounded operators often require special handling; if you, for
|
|
* example, draw trapezoids with an unbounded operator, the effect
|
|
* extends past the bounding box of the trapezoids.
|
|
*
|
|
* Return value: %TRUE if the operator is bounded by the mask operand
|
|
**/
|
|
cairo_bool_t
|
|
_cairo_operator_bounded_by_mask (cairo_operator_t op)
|
|
{
|
|
switch (op) {
|
|
case CAIRO_OPERATOR_CLEAR:
|
|
case CAIRO_OPERATOR_SOURCE:
|
|
case CAIRO_OPERATOR_OVER:
|
|
case CAIRO_OPERATOR_ATOP:
|
|
case CAIRO_OPERATOR_DEST:
|
|
case CAIRO_OPERATOR_DEST_OVER:
|
|
case CAIRO_OPERATOR_DEST_OUT:
|
|
case CAIRO_OPERATOR_XOR:
|
|
case CAIRO_OPERATOR_ADD:
|
|
case CAIRO_OPERATOR_SATURATE:
|
|
return TRUE;
|
|
case CAIRO_OPERATOR_OUT:
|
|
case CAIRO_OPERATOR_IN:
|
|
case CAIRO_OPERATOR_DEST_IN:
|
|
case CAIRO_OPERATOR_DEST_ATOP:
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT_NOT_REACHED;
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* _cairo_operator_bounded_by_source:
|
|
* @op: a #cairo_operator_t
|
|
*
|
|
* A bounded operator is one where source pixels of zero
|
|
* (in all four components, r, g, b and a) effect no change
|
|
* in the resulting destination image.
|
|
*
|
|
* Unbounded operators often require special handling; if you, for
|
|
* example, copy a surface with the SOURCE operator, the effect
|
|
* extends past the bounding box of the source surface.
|
|
*
|
|
* Return value: %TRUE if the operator is bounded by the source operand
|
|
**/
|
|
cairo_bool_t
|
|
_cairo_operator_bounded_by_source (cairo_operator_t op)
|
|
{
|
|
switch (op) {
|
|
case CAIRO_OPERATOR_OVER:
|
|
case CAIRO_OPERATOR_ATOP:
|
|
case CAIRO_OPERATOR_DEST:
|
|
case CAIRO_OPERATOR_DEST_OVER:
|
|
case CAIRO_OPERATOR_DEST_OUT:
|
|
case CAIRO_OPERATOR_XOR:
|
|
case CAIRO_OPERATOR_ADD:
|
|
case CAIRO_OPERATOR_SATURATE:
|
|
return TRUE;
|
|
case CAIRO_OPERATOR_CLEAR:
|
|
case CAIRO_OPERATOR_SOURCE:
|
|
case CAIRO_OPERATOR_OUT:
|
|
case CAIRO_OPERATOR_IN:
|
|
case CAIRO_OPERATOR_DEST_IN:
|
|
case CAIRO_OPERATOR_DEST_ATOP:
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT_NOT_REACHED;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void
|
|
_cairo_restrict_value (double *value, double min, double max)
|
|
{
|
|
if (*value < min)
|
|
*value = min;
|
|
else if (*value > max)
|
|
*value = max;
|
|
}
|
|
|
|
/* This function is identical to the C99 function lround(), except that it
|
|
* performs arithmetic rounding (instead of away-from-zero rounding) and
|
|
* has a valid input range of (INT_MIN, INT_MAX] instead of
|
|
* [INT_MIN, INT_MAX]. It is much faster on both x86 and FPU-less systems
|
|
* than other commonly used methods for rounding (lround, round, rint, lrint
|
|
* or float (d + 0.5)).
|
|
*
|
|
* The reason why this function is much faster on x86 than other
|
|
* methods is due to the fact that it avoids the fldcw instruction.
|
|
* This instruction incurs a large performance penalty on modern Intel
|
|
* processors due to how it prevents efficient instruction pipelining.
|
|
*
|
|
* The reason why this function is much faster on FPU-less systems is for
|
|
* an entirely different reason. All common rounding methods involve multiple
|
|
* floating-point operations. Each one of these operations has to be
|
|
* emulated in software, which adds up to be a large performance penalty.
|
|
* This function doesn't perform any floating-point calculations, and thus
|
|
* avoids this penalty.
|
|
*/
|
|
int
|
|
_cairo_lround (double d)
|
|
{
|
|
uint32_t top, shift_amount, output;
|
|
union {
|
|
double d;
|
|
uint64_t ui64;
|
|
uint32_t ui32[2];
|
|
} u;
|
|
|
|
u.d = d;
|
|
|
|
/* If the integer word order doesn't match the float word order, we swap
|
|
* the words of the input double. This is needed because we will be
|
|
* treating the whole double as a 64-bit unsigned integer. Notice that we
|
|
* use WORDS_BIGENDIAN to detect the integer word order, which isn't
|
|
* exactly correct because WORDS_BIGENDIAN refers to byte order, not word
|
|
* order. Thus, we are making the assumption that the byte order is the
|
|
* same as the integer word order which, on the modern machines that we
|
|
* care about, is OK.
|
|
*/
|
|
#if ( defined(FLOAT_WORDS_BIGENDIAN) && !defined(WORDS_BIGENDIAN)) || \
|
|
(!defined(FLOAT_WORDS_BIGENDIAN) && defined(WORDS_BIGENDIAN))
|
|
{
|
|
uint32_t temp = u.ui32[0];
|
|
u.ui32[0] = u.ui32[1];
|
|
u.ui32[1] = temp;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
#define MSW (0) /* Most Significant Word */
|
|
#define LSW (1) /* Least Significant Word */
|
|
#else
|
|
#define MSW (1)
|
|
#define LSW (0)
|
|
#endif
|
|
|
|
/* By shifting the most significant word of the input double to the
|
|
* right 20 places, we get the very "top" of the double where the exponent
|
|
* and sign bit lie.
|
|
*/
|
|
top = u.ui32[MSW] >> 20;
|
|
|
|
/* Here, we calculate how much we have to shift the mantissa to normalize
|
|
* it to an integer value. We extract the exponent "top" by masking out the
|
|
* sign bit, then we calculate the shift amount by subtracting the exponent
|
|
* from the bias. Notice that the correct bias for 64-bit doubles is
|
|
* actually 1075, but we use 1053 instead for two reasons:
|
|
*
|
|
* 1) To perform rounding later on, we will first need the target
|
|
* value in a 31.1 fixed-point format. Thus, the bias needs to be one
|
|
* less: (1075 - 1: 1074).
|
|
*
|
|
* 2) To avoid shifting the mantissa as a full 64-bit integer (which is
|
|
* costly on certain architectures), we break the shift into two parts.
|
|
* First, the upper and lower parts of the mantissa are shifted
|
|
* individually by a constant amount that all valid inputs will require
|
|
* at the very least. This amount is chosen to be 21, because this will
|
|
* allow the two parts of the mantissa to later be combined into a
|
|
* single 32-bit representation, on which the remainder of the shift
|
|
* will be performed. Thus, we decrease the bias by an additional 21:
|
|
* (1074 - 21: 1053).
|
|
*/
|
|
shift_amount = 1053 - (top & 0x7FF);
|
|
|
|
/* We are done with the exponent portion in "top", so here we shift it off
|
|
* the end.
|
|
*/
|
|
top >>= 11;
|
|
|
|
/* Before we perform any operations on the mantissa, we need to OR in
|
|
* the implicit 1 at the top (see the IEEE-754 spec). We needn't mask
|
|
* off the sign bit nor the exponent bits because these higher bits won't
|
|
* make a bit of difference in the rest of our calculations.
|
|
*/
|
|
u.ui32[MSW] |= 0x100000;
|
|
|
|
/* If the input double is negative, we have to decrease the mantissa
|
|
* by a hair. This is an important part of performing arithmetic rounding,
|
|
* as negative numbers must round towards positive infinity in the
|
|
* halfwase case of -x.5. Since "top" contains only the sign bit at this
|
|
* point, we can just decrease the mantissa by the value of "top".
|
|
*/
|
|
u.ui64 -= top;
|
|
|
|
/* By decrementing "top", we create a bitmask with a value of either
|
|
* 0x0 (if the input was negative) or 0xFFFFFFFF (if the input was positive
|
|
* and thus the unsigned subtraction underflowed) that we'll use later.
|
|
*/
|
|
top--;
|
|
|
|
/* Here, we shift the mantissa by the constant value as described above.
|
|
* We can emulate a 64-bit shift right by 21 through shifting the top 32
|
|
* bits left 11 places and ORing in the bottom 32 bits shifted 21 places
|
|
* to the right. Both parts of the mantissa are now packed into a single
|
|
* 32-bit integer. Although we severely truncate the lower part in the
|
|
* process, we still have enough significant bits to perform the conversion
|
|
* without error (for all valid inputs).
|
|
*/
|
|
output = (u.ui32[MSW] << 11) | (u.ui32[LSW] >> 21);
|
|
|
|
/* Next, we perform the shift that converts the X.Y fixed-point number
|
|
* currently found in "output" to the desired 31.1 fixed-point format
|
|
* needed for the following rounding step. It is important to consider
|
|
* all possible values for "shift_amount" at this point:
|
|
*
|
|
* - {shift_amount < 0} Since shift_amount is an unsigned integer, it
|
|
* really can't have a value less than zero. But, if the shift_amount
|
|
* calculation above caused underflow (which would happen with
|
|
* input > INT_MAX or input <= INT_MIN) then shift_amount will now be
|
|
* a very large number, and so this shift will result in complete
|
|
* garbage. But that's OK, as the input was out of our range, so our
|
|
* output is undefined.
|
|
*
|
|
* - {shift_amount > 31} If the magnitude of the input was very small
|
|
* (i.e. |input| << 1.0), shift_amount will have a value greater than
|
|
* 31. Thus, this shift will also result in garbage. After performing
|
|
* the shift, we will zero-out "output" if this is the case.
|
|
*
|
|
* - {0 <= shift_amount < 32} In this case, the shift will properly convert
|
|
* the mantissa into a 31.1 fixed-point number.
|
|
*/
|
|
output >>= shift_amount;
|
|
|
|
/* This is where we perform rounding with the 31.1 fixed-point number.
|
|
* Since what we're after is arithmetic rounding, we simply add the single
|
|
* fractional bit into the integer part of "output", and just keep the
|
|
* integer part.
|
|
*/
|
|
output = (output >> 1) + (output & 1);
|
|
|
|
/* Here, we zero-out the result if the magnitude if the input was very small
|
|
* (as explained in the section above). Notice that all input out of the
|
|
* valid range is also caught by this condition, which means we produce 0
|
|
* for all invalid input, which is a nice side effect.
|
|
*
|
|
* The most straightforward way to do this would be:
|
|
*
|
|
* if (shift_amount > 31)
|
|
* output = 0;
|
|
*
|
|
* But we can use a little trick to avoid the potential branch. The
|
|
* expression (shift_amount > 31) will be either 1 or 0, which when
|
|
* decremented will be either 0x0 or 0xFFFFFFFF (unsigned underflow),
|
|
* which can be used to conditionally mask away all the bits in "output"
|
|
* (in the 0x0 case), effectively zeroing it out. Certain, compilers would
|
|
* have done this for us automatically.
|
|
*/
|
|
output &= ((shift_amount > 31) - 1);
|
|
|
|
/* If the input double was a negative number, then we have to negate our
|
|
* output. The most straightforward way to do this would be:
|
|
*
|
|
* if (!top)
|
|
* output = -output;
|
|
*
|
|
* as "top" at this point is either 0x0 (if the input was negative) or
|
|
* 0xFFFFFFFF (if the input was positive). But, we can use a trick to
|
|
* avoid the branch. Observe that the following snippet of code has the
|
|
* same effect as the reference snippet above:
|
|
*
|
|
* if (!top)
|
|
* output = 0 - output;
|
|
* else
|
|
* output = output - 0;
|
|
*
|
|
* Armed with the bitmask found in "top", we can condense the two statements
|
|
* into the following:
|
|
*
|
|
* output = (output & top) - (output & ~top);
|
|
*
|
|
* where, in the case that the input double was negative, "top" will be 0,
|
|
* and the statement will be equivalent to:
|
|
*
|
|
* output = (0) - (output);
|
|
*
|
|
* and if the input double was positive, "top" will be 0xFFFFFFFF, and the
|
|
* statement will be equivalent to:
|
|
*
|
|
* output = (output) - (0);
|
|
*
|
|
* Which, as pointed out earlier, is equivalent to the original reference
|
|
* snippet.
|
|
*/
|
|
output = (output & top) - (output & ~top);
|
|
|
|
return output;
|
|
#undef MSW
|
|
#undef LSW
|
|
}
|