1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkFloatBits.h"
9#include "SkMathPriv.h"
10
11/******************************************************************************
12    SkFloatBits_toInt[Floor, Round, Ceil] are identical except for what they
13    do right before they return ... >> exp;
14    Floor - adds nothing
15    Round - adds 1 << (exp - 1)
16    Ceil - adds (1 << exp) - 1
17
18    Floor and Cast are very similar, but Cast applies its sign after all other
19    computations on value. Also, Cast does not need to check for negative zero,
20    as that value (0x80000000) "does the right thing" for Ceil. Note that it
21    doesn't for Floor/Round/Ceil, hence the explicit check.
22******************************************************************************/
23
24#define EXP_BIAS            (127+23)
25#define MATISSA_MAGIC_BIG   (1 << 23)
26
27static inline int unpack_exp(uint32_t packed) {
28    return (packed << 1 >> 24);
29}
30
31#if 0
32// the ARM compiler generates an extra BIC, so I use the dirty version instead
33static inline int unpack_matissa(uint32_t packed) {
34    // we could mask with 0x7FFFFF, but that is harder for ARM to encode
35    return (packed & ~0xFF000000) | MATISSA_MAGIC_BIG;
36}
37#endif
38
39// returns the low 24-bits, so we need to OR in the magic_bit afterwards
40static inline int unpack_matissa_dirty(uint32_t packed) {
41    return packed & ~0xFF000000;
42}
43
44// same as (int)float
45int32_t SkFloatBits_toIntCast(int32_t packed) {
46    int exp = unpack_exp(packed) - EXP_BIAS;
47    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
48
49    if (exp >= 0) {
50        if (exp > 7) {    // overflow
51            value = SK_MaxS32;
52        } else {
53            value <<= exp;
54        }
55    } else {
56        exp = -exp;
57        if (exp > 25) {   // underflow
58            exp = 25;
59        }
60        value >>= exp;
61    }
62    return SkApplySign(value, SkExtractSign(packed));
63}
64
65// same as (int)floor(float)
66int32_t SkFloatBits_toIntFloor(int32_t packed) {
67    // curse you negative 0
68    if ((packed << 1) == 0) {
69        return 0;
70    }
71
72    int exp = unpack_exp(packed) - EXP_BIAS;
73    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
74
75    if (exp >= 0) {
76        if (exp > 7) {    // overflow
77            value = SK_MaxS32;
78        } else {
79            value <<= exp;
80        }
81        // apply the sign after we check for overflow
82        return SkApplySign(value, SkExtractSign(packed));
83    } else {
84        // apply the sign before we right-shift
85        value = SkApplySign(value, SkExtractSign(packed));
86        exp = -exp;
87        if (exp > 25) {   // underflow
88            exp = 25;
89        }
90        // int add = 0;
91        return value >> exp;
92    }
93}
94
95// same as (int)floor(float + 0.5)
96int32_t SkFloatBits_toIntRound(int32_t packed) {
97    // curse you negative 0
98    if ((packed << 1) == 0) {
99        return 0;
100    }
101
102    int exp = unpack_exp(packed) - EXP_BIAS;
103    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
104
105    if (exp >= 0) {
106        if (exp > 7) {    // overflow
107            value = SK_MaxS32;
108        } else {
109            value <<= exp;
110        }
111        // apply the sign after we check for overflow
112        return SkApplySign(value, SkExtractSign(packed));
113    } else {
114        // apply the sign before we right-shift
115        value = SkApplySign(value, SkExtractSign(packed));
116        exp = -exp;
117        if (exp > 25) {   // underflow
118            exp = 25;
119        }
120        int add = 1 << (exp - 1);
121        return (value + add) >> exp;
122    }
123}
124
125// same as (int)ceil(float)
126int32_t SkFloatBits_toIntCeil(int32_t packed) {
127    // curse you negative 0
128    if ((packed << 1) == 0) {
129        return 0;
130    }
131
132    int exp = unpack_exp(packed) - EXP_BIAS;
133    int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
134
135    if (exp >= 0) {
136        if (exp > 7) {    // overflow
137            value = SK_MaxS32;
138        } else {
139            value <<= exp;
140        }
141        // apply the sign after we check for overflow
142        return SkApplySign(value, SkExtractSign(packed));
143    } else {
144        // apply the sign before we right-shift
145        value = SkApplySign(value, SkExtractSign(packed));
146        exp = -exp;
147        if (exp > 25) {   // underflow
148            exp = 25;
149        }
150        int add = (1 << exp) - 1;
151        return (value + add) >> exp;
152    }
153}
154
155float SkIntToFloatCast(int32_t value) {
156    if (0 == value) {
157        return 0;
158    }
159
160    int shift = EXP_BIAS;
161
162    // record the sign and make value positive
163    int sign = SkExtractSign(value);
164    value = SkApplySign(value, sign);
165
166    if (value >> 24) {    // value is too big (has more than 24 bits set)
167        int bias = 8 - SkCLZ(value);
168        SkDebugf("value = %d, bias = %d\n", value, bias);
169        SkASSERT(bias > 0 && bias < 8);
170        value >>= bias; // need to round?
171        shift += bias;
172    } else {
173        int zeros = SkCLZ(value << 8);
174        SkASSERT(zeros >= 0 && zeros <= 23);
175        value <<= zeros;
176        shift -= zeros;
177    }
178
179    // now value is left-aligned to 24 bits
180    SkASSERT((value >> 23) == 1);
181    SkASSERT(shift >= 0 && shift <= 255);
182
183    SkFloatIntUnion data;
184    data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
185    return data.fFloat;
186}
187
188float SkIntToFloatCast_NoOverflowCheck(int32_t value) {
189    if (0 == value) {
190        return 0;
191    }
192
193    int shift = EXP_BIAS;
194
195    // record the sign and make value positive
196    int sign = SkExtractSign(value);
197    value = SkApplySign(value, sign);
198
199    int zeros = SkCLZ(value << 8);
200    value <<= zeros;
201    shift -= zeros;
202
203    SkFloatIntUnion data;
204    data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
205    return data.fFloat;
206}
207