Subversion Repositories AndroidProjects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1452 chris 1
#region --- License ---
2
/*
3
Copyright (c) 2006 - 2008 The Open Toolkit library.
4
 
5
Permission is hereby granted, free of charge, to any person obtaining a copy of
6
this software and associated documentation files (the "Software"), to deal in
7
the Software without restriction, including without limitation the rights to
8
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
of the Software, and to permit persons to whom the Software is furnished to do
10
so, subject to the following conditions:
11
 
12
The above copyright notice and this permission notice shall be included in all
13
copies or substantial portions of the Software.
14
 
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
SOFTWARE.
22
 */
23
/*
24
The conversion functions are derived from OpenEXR's implementation and are
25
governed by the following license:
26
 
27
Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
28
Digital Ltd. LLC
29
 
30
All rights reserved.
31
 
32
Redistribution and use in source and binary forms, with or without
33
modification, are permitted provided that the following conditions are
34
met:
35
*       Redistributions of source code must retain the above copyright
36
notice, this list of conditions and the following disclaimer.
37
*       Redistributions in binary form must reproduce the above
38
copyright notice, this list of conditions and the following disclaimer
39
in the documentation and/or other materials provided with the
40
distribution.
41
*       Neither the name of Industrial Light & Magic nor the names of
42
its contributors may be used to endorse or promote products derived
43
from this software without specific prior written permission.
44
 
45
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
47
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
48
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
49
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
51
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
52
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
53
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
54
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
55
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56
 */
57
#endregion --- License ---
58
 
59
using System;
60
using System.IO;
61
using System.Runtime.InteropServices;
62
using System.Runtime.Serialization;
63
 
64
namespace OpenTK.Math
65
{
66
 
67
    /// <summary>
68
    /// The name Half is derived from half-precision floating-point number.
69
    /// It occupies only 16 bits, which are split into 1 Sign bit, 5 Exponent bits and 10 Mantissa bits.
70
    /// </summary>
71
    /// <remarks>
72
    /// Quote from ARB_half_float_pixel specification:
73
    /// Any representable 16-bit floating-point value is legal as input to a GL command that accepts 16-bit floating-point data.  The
74
    /// result of providing a value that is not a floating-point number (such as infinity or NaN) to such a command is unspecified,
75
    /// but must not lead to GL interruption or termination. Providing a denormalized number or negative zero to GL must yield
76
    /// predictable results.
77
    /// </remarks>
78
    [Obsolete("OpenTK.Math functions have been moved to the root OpenTK namespace (reason: XNA compatibility")]
79
    [Serializable, StructLayout(LayoutKind.Sequential)]
80
    public struct Half : ISerializable, IComparable<Half>, IFormattable, IEquatable<Half>
81
    {
82
        #region Internal Field
83
 
84
        UInt16 bits;
85
 
86
        #endregion Internal Field
87
 
88
        #region Properties
89
 
90
        /// <summary>Returns true if the Half is zero.</summary>
91
        public bool IsZero { get { return (bits == 0) || (bits == 0x8000); } }
92
 
93
        /// <summary>Returns true if the Half represents Not A Number (NaN)</summary>
94
        public bool IsNaN { get { return (((bits & 0x7C00) == 0x7C00) && (bits & 0x03FF) != 0x0000); } }
95
 
96
        /// <summary>Returns true if the Half represents positive infinity.</summary>
97
        public bool IsPositiveInfinity { get { return (bits == 31744); } }
98
 
99
        /// <summary>Returns true if the Half represents negative infinity.</summary>
100
        public bool IsNegativeInfinity { get { return (bits == 64512); } }
101
 
102
        #endregion Properties
103
 
104
        #region Constructors
105
 
106
        /// <summary>
107
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
108
        /// </summary>
109
        /// <param name="f">32-bit single-precision floating-point number.</param>
110
        public Half(Single f)
111
            : this()
112
        {
113
            unsafe
114
            {
115
                bits = SingleToHalf(*(int*)&f);
116
            }
117
        }
118
 
119
        /// <summary>
120
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
121
        /// </summary>
122
        /// <param name="f">32-bit single-precision floating-point number.</param>
123
        /// <param name="throwOnError">Enable checks that will throw if the conversion result is not meaningful.</param>
124
        public Half(Single f, bool throwOnError)
125
            : this(f)
126
        {
127
            if (throwOnError)
128
            {
129
                // handle cases that cause overflow rather than silently ignoring it
130
                if (f > Half.MaxValue) throw new ArithmeticException("Half: Positive maximum value exceeded.");
131
                if (f < -Half.MaxValue) throw new ArithmeticException("Half: Negative minimum value exceeded.");
132
 
133
                // handle cases that make no sense
134
                if (Single.IsNaN(f)) throw new ArithmeticException("Half: Input is not a number (NaN).");
135
                if (Single.IsPositiveInfinity(f)) throw new ArithmeticException("Half: Input is positive infinity.");
136
                if (Single.IsNegativeInfinity(f)) throw new ArithmeticException("Half: Input is negative infinity.");
137
            }
138
        }
139
 
140
        /// <summary>
141
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
142
        /// </summary>
143
        /// <param name="d">64-bit double-precision floating-point number.</param>
144
        public Half(Double d) : this((Single)d) { }
145
 
146
        /// <summary>
147
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
148
        /// </summary>
149
        /// <param name="d">64-bit double-precision floating-point number.</param>
150
        /// <param name="throwOnError">Enable checks that will throw if the conversion result is not meaningful.</param>
151
        public Half(Double d, bool throwOnError) : this((Single)d, throwOnError) { }
152
 
153
        #endregion Constructors
154
 
155
        #region Single -> Half
156
 
157
        /// <summary>Ported from OpenEXR's IlmBase 1.0.1</summary>
158
        private UInt16 SingleToHalf(Int32 si32)
159
        {
160
            // Our floating point number, F, is represented by the bit pattern in integer i.
161
            // Disassemble that bit pattern into the sign, S, the exponent, E, and the significand, M.
162
            // Shift S into the position where it will go in in the resulting half number.
163
            // Adjust E, accounting for the different exponent bias of float and half (127 versus 15).
164
 
165
            Int32 sign = (si32 >> 16) & 0x00008000;
166
            Int32 exponent = ((si32 >> 23) & 0x000000ff) - (127 - 15);
167
            Int32 mantissa = si32 & 0x007fffff;
168
 
169
            // Now reassemble S, E and M into a half:
170
 
171
            if (exponent <= 0)
172
            {
173
                if (exponent < -10)
174
                {
175
                    // E is less than -10. The absolute value of F is less than Half.MinValue
176
                    // (F may be a small normalized float, a denormalized float or a zero).
177
                    //
178
                    // We convert F to a half zero with the same sign as F.
179
 
180
                    return (UInt16)sign;
181
                }
182
 
183
                // E is between -10 and 0. F is a normalized float whose magnitude is less than Half.MinNormalizedValue.
184
                //
185
                // We convert F to a denormalized half.
186
 
187
                // Add an explicit leading 1 to the significand.
188
 
189
                mantissa = mantissa | 0x00800000;
190
 
191
                // Round to M to the nearest (10+E)-bit value (with E between -10 and 0); in case of a tie, round to the nearest even value.
192
                //
193
                // Rounding may cause the significand to overflow and make our number normalized. Because of the way a half's bits
194
                // are laid out, we don't have to treat this case separately; the code below will handle it correctly.
195
 
196
                Int32 t = 14 - exponent;
197
                Int32 a = (1 << (t - 1)) - 1;
198
                Int32 b = (mantissa >> t) & 1;
199
 
200
                mantissa = (mantissa + a + b) >> t;
201
 
202
                // Assemble the half from S, E (==zero) and M.
203
 
204
                return (UInt16)(sign | mantissa);
205
            }
206
            else if (exponent == 0xff - (127 - 15))
207
            {
208
                if (mantissa == 0)
209
                {
210
                    // F is an infinity; convert F to a half infinity with the same sign as F.
211
 
212
                    return (UInt16)(sign | 0x7c00);
213
                }
214
                else
215
                {
216
                    // F is a NAN; we produce a half NAN that preserves the sign bit and the 10 leftmost bits of the
217
                    // significand of F, with one exception: If the 10 leftmost bits are all zero, the NAN would turn 
218
                    // into an infinity, so we have to set at least one bit in the significand.
219
 
220
                    mantissa >>= 13;
221
                    return (UInt16)(sign | 0x7c00 | mantissa | ((mantissa == 0) ? 1 : 0));
222
                }
223
            }
224
            else
225
            {
226
                // E is greater than zero.  F is a normalized float. We try to convert F to a normalized half.
227
 
228
                // Round to M to the nearest 10-bit value. In case of a tie, round to the nearest even value.
229
 
230
                mantissa = mantissa + 0x00000fff + ((mantissa >> 13) & 1);
231
 
232
                if ((mantissa & 0x00800000) == 1)
233
                {
234
                    mantissa = 0;        // overflow in significand,
235
                    exponent += 1;        // adjust exponent
236
                }
237
 
238
                // exponent overflow
239
                if (exponent > 30) throw new ArithmeticException("Half: Hardware floating-point overflow.");
240
 
241
                // Assemble the half from S, E and M.
242
 
243
                return (UInt16)(sign | (exponent << 10) | (mantissa >> 13));
244
            }
245
        }
246
 
247
        #endregion Single -> Half
248
 
249
        #region Half -> Single
250
 
251
        /// <summary>Converts the 16-bit half to 32-bit floating-point.</summary>
252
        /// <returns>A single-precision floating-point number.</returns>
253
        public Single ToSingle()
254
        {
255
            int i = HalfToFloat(bits);
256
 
257
            unsafe
258
            {
259
                return *(float*)&i;
260
            }
261
        }
262
 
263
        /// <summary>Ported from OpenEXR's IlmBase 1.0.1</summary>
264
        private Int32 HalfToFloat(UInt16 ui16)
265
        {
266
 
267
            Int32 sign = (ui16 >> 15) & 0x00000001;
268
            Int32 exponent = (ui16 >> 10) & 0x0000001f;
269
            Int32 mantissa = ui16 & 0x000003ff;
270
 
271
            if (exponent == 0)
272
            {
273
                if (mantissa == 0)
274
                {
275
                    // Plus or minus zero
276
 
277
                    return sign << 31;
278
                }
279
                else
280
                {
281
                    // Denormalized number -- renormalize it
282
 
283
                    while ((mantissa & 0x00000400) == 0)
284
                    {
285
                        mantissa <<= 1;
286
                        exponent -= 1;
287
                    }
288
 
289
                    exponent += 1;
290
                    mantissa &= ~0x00000400;
291
                }
292
            }
293
            else if (exponent == 31)
294
            {
295
                if (mantissa == 0)
296
                {
297
                    // Positive or negative infinity
298
 
299
                    return (sign << 31) | 0x7f800000;
300
                }
301
                else
302
                {
303
                    // Nan -- preserve sign and significand bits
304
 
305
                    return (sign << 31) | 0x7f800000 | (mantissa << 13);
306
                }
307
            }
308
 
309
            // Normalized number
310
 
311
            exponent = exponent + (127 - 15);
312
            mantissa = mantissa << 13;
313
 
314
            // Assemble S, E and M.
315
 
316
            return (sign << 31) | (exponent << 23) | mantissa;
317
        }
318
 
319
        #endregion Half -> Single
320
 
321
        #region Conversions
322
 
323
        /// <summary>
324
        /// Converts a System.Single to a OpenTK.Half.
325
        /// </summary>
326
        /// <param name="f">The value to convert.
327
        /// A <see cref="System.Single"/>
328
        /// </param>
329
        /// <returns>The result of the conversion.
330
        /// A <see cref="Half"/>
331
        /// </returns>
332
        public static explicit operator Half(float f)
333
        {
334
            return new Half(f);
335
        }
336
 
337
        /// <summary>
338
        /// Converts a System.Double to a OpenTK.Half.
339
        /// </summary>
340
        /// <param name="d">The value to convert.
341
        /// A <see cref="System.Double"/>
342
        /// </param>
343
        /// <returns>The result of the conversion.
344
        /// A <see cref="Half"/>
345
        /// </returns>
346
        public static explicit operator Half(double d)
347
        {
348
            return new Half(d);
349
        }
350
 
351
        /// <summary>
352
        /// Converts a OpenTK.Half to a System.Single.
353
        /// </summary>
354
        /// <param name="h">The value to convert.
355
        /// A <see cref="Half"/>
356
        /// </param>
357
        /// <returns>The result of the conversion.
358
        /// A <see cref="System.Single"/>
359
        /// </returns>
360
        public static implicit operator float(Half h)
361
        {
362
            return h.ToSingle();
363
        }
364
 
365
        /// <summary>
366
        /// Converts a OpenTK.Half to a System.Double.
367
        /// </summary>
368
        /// <param name="h">The value to convert.
369
        /// A <see cref="Half"/>
370
        /// </param>
371
        /// <returns>The result of the conversion.
372
        /// A <see cref="System.Double"/>
373
        /// </returns>
374
        public static implicit operator double(Half h)
375
        {
376
            return (double)h.ToSingle();
377
        }
378
 
379
        #endregion Conversions
380
 
381
        #region Constants
382
 
383
        /// <summary>The size in bytes for an instance of the Half struct.</summary>
384
        public static readonly Int32 SizeInBytes = 2;
385
 
386
        /// <summary>Smallest positive half</summary>
387
        public static readonly Single MinValue = 5.96046448e-08f;
388
 
389
        /// <summary>Smallest positive normalized half</summary>
390
        public static readonly Single MinNormalizedValue = 6.10351562e-05f;
391
 
392
        /// <summary>Largest positive half</summary>
393
        public static readonly Single MaxValue = 65504.0f;
394
 
395
        /// <summary>Smallest positive e for which half (1.0 + e) != half (1.0)</summary>
396
        public static readonly Single Epsilon = 0.00097656f;
397
 
398
        #endregion Constants
399
 
400
        #region ISerializable
401
 
402
        /// <summary>Constructor used by ISerializable to deserialize the object.</summary>
403
        /// <param name="info"></param>
404
        /// <param name="context"></param>
405
        public Half(SerializationInfo info, StreamingContext context)
406
        {
407
            this.bits = (ushort)info.GetValue("bits", typeof(ushort));
408
        }
409
 
410
        /// <summary>Used by ISerialize to serialize the object.</summary>
411
        /// <param name="info"></param>
412
        /// <param name="context"></param>
413
        public void GetObjectData(SerializationInfo info, StreamingContext context)
414
        {
415
            info.AddValue("bits", this.bits);
416
        }
417
 
418
        #endregion ISerializable
419
 
420
        #region Binary dump
421
 
422
        /// <summary>Updates the Half by reading from a Stream.</summary>
423
        /// <param name="bin">A BinaryReader instance associated with an open Stream.</param>
424
        public void FromBinaryStream(BinaryReader bin)
425
        {
426
            this.bits = bin.ReadUInt16();
427
 
428
        }
429
 
430
        /// <summary>Writes the Half into a Stream.</summary>
431
        /// <param name="bin">A BinaryWriter instance associated with an open Stream.</param>
432
        public void ToBinaryStream(BinaryWriter bin)
433
        {
434
            bin.Write(this.bits);
435
        }
436
 
437
        #endregion Binary dump
438
 
439
        #region IEquatable<Half> Members
440
 
441
        const int maxUlps = 1;
442
 
443
        /// <summary>
444
        /// Returns a value indicating whether this instance is equal to a specified OpenTK.Half value.
445
        /// </summary>
446
        /// <param name="other">OpenTK.Half object to compare to this instance..</param>
447
        /// <returns>True, if other is equal to this instance; false otherwise.</returns>
448
        public bool Equals(Half other)
449
        {
450
            short aInt, bInt;
451
            unchecked { aInt = (short)other.bits; }
452
            unchecked { bInt = (short)this.bits; }
453
 
454
            // Make aInt lexicographically ordered as a twos-complement int
455
            if (aInt < 0)
456
                aInt = (short)(0x8000 - aInt);
457
 
458
            // Make bInt lexicographically ordered as a twos-complement int
459
            if (bInt < 0)
460
                bInt = (short)(0x8000 - bInt);
461
 
462
            short intDiff = System.Math.Abs((short)(aInt - bInt));
463
 
464
            if (intDiff <= maxUlps)
465
                return true;
466
 
467
            return false;
468
        }
469
 
470
        #endregion
471
 
472
        #region IComparable<Half> Members
473
 
474
        /// <summary>
475
        /// Compares this instance to a specified half-precision floating-point number
476
        /// and returns an integer that indicates whether the value of this instance
477
        /// is less than, equal to, or greater than the value of the specified half-precision
478
        /// floating-point number. 
479
        /// </summary>
480
        /// <param name="other">A half-precision floating-point number to compare.</param>
481
        /// <returns>
482
        /// A signed number indicating the relative values of this instance and value. If the number is:
483
        /// <para>Less than zero, then this instance is less than other, or this instance is not a number
484
        /// (OpenTK.Half.NaN) and other is a number.</para>
485
        /// <para>Zero: this instance is equal to value, or both this instance and other
486
        /// are not a number (OpenTK.Half.NaN), OpenTK.Half.PositiveInfinity, or
487
        /// OpenTK.Half.NegativeInfinity.</para>
488
        /// <para>Greater than zero: this instance is greater than othrs, or this instance is a number
489
        /// and other is not a number (OpenTK.Half.NaN).</para>
490
        /// </returns>
491
        public int CompareTo(Half other)
492
        {
493
            return ((float)this).CompareTo((float)other);
494
        }
495
 
496
        #endregion IComparable<Half> Members
497
 
498
        #region IFormattable Members
499
 
500
        /// <summary>Converts this Half into a human-legible string representation.</summary>
501
        /// <returns>The string representation of this instance.</returns>
502
        public override string ToString()
503
        {
504
            return this.ToSingle().ToString();
505
        }
506
 
507
        /// <summary>Converts this Half into a human-legible string representation.</summary>
508
        /// <param name="format">Formatting for the output string.</param>
509
        /// <param name="formatProvider">Culture-specific formatting information.</param>
510
        /// <returns>The string representation of this instance.</returns>
511
        public string ToString(string format, IFormatProvider formatProvider)
512
        {
513
            return this.ToSingle().ToString(format, formatProvider);
514
        }
515
 
516
        #endregion IFormattable Members
517
 
518
        #region String -> Half
519
 
520
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent.</summary>
521
        /// <param name="s">String representation of the number to convert.</param>
522
        /// <returns>A new Half instance.</returns>
523
        public static Half Parse(string s)
524
        {
525
            return (Half)Single.Parse(s);
526
        }
527
 
528
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent.</summary>
529
        /// <param name="s">String representation of the number to convert.</param>
530
        /// <param name="style">Specifies the format of s.</param>
531
        /// <param name="provider">Culture-specific formatting information.</param>
532
        /// <returns>A new Half instance.</returns>
533
        public static Half Parse(string s, System.Globalization.NumberStyles style, IFormatProvider provider)
534
        {
535
            return (Half)Single.Parse(s, style, provider);
536
        }
537
 
538
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent. Returns success.</summary>
539
        /// <param name="s">String representation of the number to convert.</param>
540
        /// <param name="result">The Half instance to write to.</param>
541
        /// <returns>Success.</returns>
542
        public static bool TryParse(string s, out Half result)
543
        {
544
            float f;
545
            bool b = Single.TryParse(s, out f);
546
            result = (Half)f;
547
            return b;
548
        }
549
 
550
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent. Returns success.</summary>
551
        /// <param name="s">string representation of the number to convert.</param>
552
        /// <param name="style">specifies the format of s.</param>
553
        /// <param name="provider">Culture-specific formatting information.</param>
554
        /// <param name="result">The Half instance to write to.</param>
555
        /// <returns>Success.</returns>
556
        public static bool TryParse(string s, System.Globalization.NumberStyles style, IFormatProvider provider, out Half result)
557
        {
558
            float f;
559
            bool b = Single.TryParse(s, style, provider, out f);
560
            result = (Half)f;
561
            return b;
562
        }
563
 
564
        #endregion String -> Half
565
 
566
        #region BitConverter
567
 
568
        /// <summary>Returns the Half as an array of bytes.</summary>
569
        /// <param name="h">The Half to convert.</param>
570
        /// <returns>The input as byte array.</returns>
571
        public static byte[] GetBytes(Half h)
572
        {
573
            return BitConverter.GetBytes(h.bits);
574
        }
575
 
576
        /// <summary>Converts an array of bytes into Half.</summary>
577
        /// <param name="value">A Half in it's byte[] representation.</param>
578
        /// <param name="startIndex">The starting position within value.</param>
579
        /// <returns>A new Half instance.</returns>
580
        public static Half FromBytes(byte[] value, int startIndex)
581
        {
582
            Half h;
583
            h.bits = BitConverter.ToUInt16(value, startIndex);
584
            return h;
585
        }
586
 
587
        #endregion BitConverter
588
    }
589
}