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
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
    [Serializable, StructLayout(LayoutKind.Sequential)]
79
    public struct Half : ISerializable, IComparable<Half>, IFormattable, IEquatable<Half>
80
    {
81
        #region Internal Field
82
 
83
        UInt16 bits;
84
 
85
        #endregion Internal Field
86
 
87
        #region Properties
88
 
89
        /// <summary>Returns true if the Half is zero.</summary>
90
        public bool IsZero { get { return (bits == 0) || (bits == 0x8000); } }
91
 
92
        /// <summary>Returns true if the Half represents Not A Number (NaN)</summary>
93
        public bool IsNaN { get { return (((bits & 0x7C00) == 0x7C00) && (bits & 0x03FF) != 0x0000); } }
94
 
95
        /// <summary>Returns true if the Half represents positive infinity.</summary>
96
        public bool IsPositiveInfinity { get { return (bits == 31744); } }
97
 
98
        /// <summary>Returns true if the Half represents negative infinity.</summary>
99
        public bool IsNegativeInfinity { get { return (bits == 64512); } }
100
 
101
        #endregion Properties
102
 
103
        #region Constructors
104
 
105
        /// <summary>
106
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
107
        /// </summary>
108
        /// <param name="f">32-bit single-precision floating-point number.</param>
109
        public Half(Single f)
110
            : this()
111
        {
112
            unsafe
113
            {
114
                bits = SingleToHalf(*(int*)&f);
115
            }
116
        }
117
 
118
        /// <summary>
119
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
120
        /// </summary>
121
        /// <param name="f">32-bit single-precision floating-point number.</param>
122
        /// <param name="throwOnError">Enable checks that will throw if the conversion result is not meaningful.</param>
123
        public Half(Single f, bool throwOnError)
124
            : this(f)
125
        {
126
            if (throwOnError)
127
            {
128
                // handle cases that cause overflow rather than silently ignoring it
129
                if (f > Half.MaxValue) throw new ArithmeticException("Half: Positive maximum value exceeded.");
130
                if (f < -Half.MaxValue) throw new ArithmeticException("Half: Negative minimum value exceeded.");
131
 
132
                // handle cases that make no sense
133
                if (Single.IsNaN(f)) throw new ArithmeticException("Half: Input is not a number (NaN).");
134
                if (Single.IsPositiveInfinity(f)) throw new ArithmeticException("Half: Input is positive infinity.");
135
                if (Single.IsNegativeInfinity(f)) throw new ArithmeticException("Half: Input is negative infinity.");
136
            }
137
        }
138
 
139
        /// <summary>
140
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
141
        /// </summary>
142
        /// <param name="d">64-bit double-precision floating-point number.</param>
143
        public Half(Double d) : this((Single)d) { }
144
 
145
        /// <summary>
146
        /// The new Half instance will convert the parameter into 16-bit half-precision floating-point.
147
        /// </summary>
148
        /// <param name="d">64-bit double-precision floating-point number.</param>
149
        /// <param name="throwOnError">Enable checks that will throw if the conversion result is not meaningful.</param>
150
        public Half(Double d, bool throwOnError) : this((Single)d, throwOnError) { }
151
 
152
        #endregion Constructors
153
 
154
        #region Single -> Half
155
 
156
        /// <summary>Ported from OpenEXR's IlmBase 1.0.1</summary>
157
        private UInt16 SingleToHalf(Int32 si32)
158
        {
159
            // Our floating point number, F, is represented by the bit pattern in integer i.
160
            // Disassemble that bit pattern into the sign, S, the exponent, E, and the significand, M.
161
            // Shift S into the position where it will go in in the resulting half number.
162
            // Adjust E, accounting for the different exponent bias of float and half (127 versus 15).
163
 
164
            Int32 sign = (si32 >> 16) & 0x00008000;
165
            Int32 exponent = ((si32 >> 23) & 0x000000ff) - (127 - 15);
166
            Int32 mantissa = si32 & 0x007fffff;
167
 
168
            // Now reassemble S, E and M into a half:
169
 
170
            if (exponent <= 0)
171
            {
172
                if (exponent < -10)
173
                {
174
                    // E is less than -10. The absolute value of F is less than Half.MinValue
175
                    // (F may be a small normalized float, a denormalized float or a zero).
176
                    //
177
                    // We convert F to a half zero with the same sign as F.
178
 
179
                    return (UInt16)sign;
180
                }
181
 
182
                // E is between -10 and 0. F is a normalized float whose magnitude is less than Half.MinNormalizedValue.
183
                //
184
                // We convert F to a denormalized half.
185
 
186
                // Add an explicit leading 1 to the significand.
187
 
188
                mantissa = mantissa | 0x00800000;
189
 
190
                // 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.
191
                //
192
                // Rounding may cause the significand to overflow and make our number normalized. Because of the way a half's bits
193
                // are laid out, we don't have to treat this case separately; the code below will handle it correctly.
194
 
195
                Int32 t = 14 - exponent;
196
                Int32 a = (1 << (t - 1)) - 1;
197
                Int32 b = (mantissa >> t) & 1;
198
 
199
                mantissa = (mantissa + a + b) >> t;
200
 
201
                // Assemble the half from S, E (==zero) and M.
202
 
203
                return (UInt16)(sign | mantissa);
204
            }
205
            else if (exponent == 0xff - (127 - 15))
206
            {
207
                if (mantissa == 0)
208
                {
209
                    // F is an infinity; convert F to a half infinity with the same sign as F.
210
 
211
                    return (UInt16)(sign | 0x7c00);
212
                }
213
                else
214
                {
215
                    // F is a NAN; we produce a half NAN that preserves the sign bit and the 10 leftmost bits of the
216
                    // significand of F, with one exception: If the 10 leftmost bits are all zero, the NAN would turn 
217
                    // into an infinity, so we have to set at least one bit in the significand.
218
 
219
                    mantissa >>= 13;
220
                    return (UInt16)(sign | 0x7c00 | mantissa | ((mantissa == 0) ? 1 : 0));
221
                }
222
            }
223
            else
224
            {
225
                // E is greater than zero.  F is a normalized float. We try to convert F to a normalized half.
226
 
227
                // Round to M to the nearest 10-bit value. In case of a tie, round to the nearest even value.
228
 
229
                mantissa = mantissa + 0x00000fff + ((mantissa >> 13) & 1);
230
 
231
                if ((mantissa & 0x00800000) == 1)
232
                {
233
                    mantissa = 0;        // overflow in significand,
234
                    exponent += 1;        // adjust exponent
235
                }
236
 
237
                // exponent overflow
238
                if (exponent > 30) throw new ArithmeticException("Half: Hardware floating-point overflow.");
239
 
240
                // Assemble the half from S, E and M.
241
 
242
                return (UInt16)(sign | (exponent << 10) | (mantissa >> 13));
243
            }
244
        }
245
 
246
        #endregion Single -> Half
247
 
248
        #region Half -> Single
249
 
250
        /// <summary>Converts the 16-bit half to 32-bit floating-point.</summary>
251
        /// <returns>A single-precision floating-point number.</returns>
252
        public Single ToSingle()
253
        {
254
            int i = HalfToFloat(bits);
255
 
256
            unsafe
257
            {
258
                return *(float*)&i;
259
            }
260
        }
261
 
262
        /// <summary>Ported from OpenEXR's IlmBase 1.0.1</summary>
263
        private Int32 HalfToFloat(UInt16 ui16)
264
        {
265
 
266
            Int32 sign = (ui16 >> 15) & 0x00000001;
267
            Int32 exponent = (ui16 >> 10) & 0x0000001f;
268
            Int32 mantissa = ui16 & 0x000003ff;
269
 
270
            if (exponent == 0)
271
            {
272
                if (mantissa == 0)
273
                {
274
                    // Plus or minus zero
275
 
276
                    return sign << 31;
277
                }
278
                else
279
                {
280
                    // Denormalized number -- renormalize it
281
 
282
                    while ((mantissa & 0x00000400) == 0)
283
                    {
284
                        mantissa <<= 1;
285
                        exponent -= 1;
286
                    }
287
 
288
                    exponent += 1;
289
                    mantissa &= ~0x00000400;
290
                }
291
            }
292
            else if (exponent == 31)
293
            {
294
                if (mantissa == 0)
295
                {
296
                    // Positive or negative infinity
297
 
298
                    return (sign << 31) | 0x7f800000;
299
                }
300
                else
301
                {
302
                    // Nan -- preserve sign and significand bits
303
 
304
                    return (sign << 31) | 0x7f800000 | (mantissa << 13);
305
                }
306
            }
307
 
308
            // Normalized number
309
 
310
            exponent = exponent + (127 - 15);
311
            mantissa = mantissa << 13;
312
 
313
            // Assemble S, E and M.
314
 
315
            return (sign << 31) | (exponent << 23) | mantissa;
316
        }
317
 
318
        #endregion Half -> Single
319
 
320
        #region Conversions
321
 
322
        /// <summary>
323
        /// Converts a System.Single to a OpenTK.Half.
324
        /// </summary>
325
        /// <param name="f">The value to convert.
326
        /// A <see cref="System.Single"/>
327
        /// </param>
328
        /// <returns>The result of the conversion.
329
        /// A <see cref="Half"/>
330
        /// </returns>
331
        public static explicit operator Half(float f)
332
        {
333
            return new Half(f);
334
        }
335
 
336
        /// <summary>
337
        /// Converts a System.Double to a OpenTK.Half.
338
        /// </summary>
339
        /// <param name="d">The value to convert.
340
        /// A <see cref="System.Double"/>
341
        /// </param>
342
        /// <returns>The result of the conversion.
343
        /// A <see cref="Half"/>
344
        /// </returns>
345
        public static explicit operator Half(double d)
346
        {
347
            return new Half(d);
348
        }
349
 
350
        /// <summary>
351
        /// Converts a OpenTK.Half to a System.Single.
352
        /// </summary>
353
        /// <param name="h">The value to convert.
354
        /// A <see cref="Half"/>
355
        /// </param>
356
        /// <returns>The result of the conversion.
357
        /// A <see cref="System.Single"/>
358
        /// </returns>
359
        public static implicit operator float(Half h)
360
        {
361
            return h.ToSingle();
362
        }
363
 
364
        /// <summary>
365
        /// Converts a OpenTK.Half to a System.Double.
366
        /// </summary>
367
        /// <param name="h">The value to convert.
368
        /// A <see cref="Half"/>
369
        /// </param>
370
        /// <returns>The result of the conversion.
371
        /// A <see cref="System.Double"/>
372
        /// </returns>
373
        public static implicit operator double(Half h)
374
        {
375
            return (double)h.ToSingle();
376
        }
377
 
378
        #endregion Conversions
379
 
380
        #region Constants
381
 
382
        /// <summary>The size in bytes for an instance of the Half struct.</summary>
383
        public static readonly Int32 SizeInBytes = 2;
384
 
385
        /// <summary>Smallest positive half</summary>
386
        public static readonly Single MinValue = 5.96046448e-08f;
387
 
388
        /// <summary>Smallest positive normalized half</summary>
389
        public static readonly Single MinNormalizedValue = 6.10351562e-05f;
390
 
391
        /// <summary>Largest positive half</summary>
392
        public static readonly Single MaxValue = 65504.0f;
393
 
394
        /// <summary>Smallest positive e for which half (1.0 + e) != half (1.0)</summary>
395
        public static readonly Single Epsilon = 0.00097656f;
396
 
397
        #endregion Constants
398
 
399
        #region ISerializable
400
 
401
        /// <summary>Constructor used by ISerializable to deserialize the object.</summary>
402
        /// <param name="info"></param>
403
        /// <param name="context"></param>
404
        public Half(SerializationInfo info, StreamingContext context)
405
        {
406
            this.bits = (ushort)info.GetValue("bits", typeof(ushort));
407
        }
408
 
409
        /// <summary>Used by ISerialize to serialize the object.</summary>
410
        /// <param name="info"></param>
411
        /// <param name="context"></param>
412
        public void GetObjectData(SerializationInfo info, StreamingContext context)
413
        {
414
            info.AddValue("bits", this.bits);
415
        }
416
 
417
        #endregion ISerializable
418
 
419
        #region Binary dump
420
 
421
        /// <summary>Updates the Half by reading from a Stream.</summary>
422
        /// <param name="bin">A BinaryReader instance associated with an open Stream.</param>
423
        public void FromBinaryStream(BinaryReader bin)
424
        {
425
            this.bits = bin.ReadUInt16();
426
 
427
        }
428
 
429
        /// <summary>Writes the Half into a Stream.</summary>
430
        /// <param name="bin">A BinaryWriter instance associated with an open Stream.</param>
431
        public void ToBinaryStream(BinaryWriter bin)
432
        {
433
            bin.Write(this.bits);
434
        }
435
 
436
        #endregion Binary dump
437
 
438
        #region IEquatable<Half> Members
439
 
440
        const int maxUlps = 1;
441
 
442
        /// <summary>
443
        /// Returns a value indicating whether this instance is equal to a specified OpenTK.Half value.
444
        /// </summary>
445
        /// <param name="other">OpenTK.Half object to compare to this instance..</param>
446
        /// <returns>True, if other is equal to this instance; false otherwise.</returns>
447
        public bool Equals(Half other)
448
        {
449
            short aInt, bInt;
450
            unchecked { aInt = (short)other.bits; }
451
            unchecked { bInt = (short)this.bits; }
452
 
453
            // Make aInt lexicographically ordered as a twos-complement int
454
            if (aInt < 0)
455
                aInt = (short)(0x8000 - aInt);
456
 
457
            // Make bInt lexicographically ordered as a twos-complement int
458
            if (bInt < 0)
459
                bInt = (short)(0x8000 - bInt);
460
 
461
            short intDiff = System.Math.Abs((short)(aInt - bInt));
462
 
463
            if (intDiff <= maxUlps)
464
                return true;
465
 
466
            return false;
467
        }
468
 
469
        #endregion
470
 
471
        #region IComparable<Half> Members
472
 
473
        /// <summary>
474
        /// Compares this instance to a specified half-precision floating-point number
475
        /// and returns an integer that indicates whether the value of this instance
476
        /// is less than, equal to, or greater than the value of the specified half-precision
477
        /// floating-point number. 
478
        /// </summary>
479
        /// <param name="other">A half-precision floating-point number to compare.</param>
480
        /// <returns>
481
        /// A signed number indicating the relative values of this instance and value. If the number is:
482
        /// <para>Less than zero, then this instance is less than other, or this instance is not a number
483
        /// (OpenTK.Half.NaN) and other is a number.</para>
484
        /// <para>Zero: this instance is equal to value, or both this instance and other
485
        /// are not a number (OpenTK.Half.NaN), OpenTK.Half.PositiveInfinity, or
486
        /// OpenTK.Half.NegativeInfinity.</para>
487
        /// <para>Greater than zero: this instance is greater than othrs, or this instance is a number
488
        /// and other is not a number (OpenTK.Half.NaN).</para>
489
        /// </returns>
490
        public int CompareTo(Half other)
491
        {
492
            return ((float)this).CompareTo((float)other);
493
        }
494
 
495
        #endregion IComparable<Half> Members
496
 
497
        #region IFormattable Members
498
 
499
        /// <summary>Converts this Half into a human-legible string representation.</summary>
500
        /// <returns>The string representation of this instance.</returns>
501
        public override string ToString()
502
        {
503
            return this.ToSingle().ToString();
504
        }
505
 
506
        /// <summary>Converts this Half into a human-legible string representation.</summary>
507
        /// <param name="format">Formatting for the output string.</param>
508
        /// <param name="formatProvider">Culture-specific formatting information.</param>
509
        /// <returns>The string representation of this instance.</returns>
510
        public string ToString(string format, IFormatProvider formatProvider)
511
        {
512
            return this.ToSingle().ToString(format, formatProvider);
513
        }
514
 
515
        #endregion IFormattable Members
516
 
517
        #region String -> Half
518
 
519
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent.</summary>
520
        /// <param name="s">String representation of the number to convert.</param>
521
        /// <returns>A new Half instance.</returns>
522
        public static Half Parse(string s)
523
        {
524
            return (Half)Single.Parse(s);
525
        }
526
 
527
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent.</summary>
528
        /// <param name="s">String representation of the number to convert.</param>
529
        /// <param name="style">Specifies the format of s.</param>
530
        /// <param name="provider">Culture-specific formatting information.</param>
531
        /// <returns>A new Half instance.</returns>
532
        public static Half Parse(string s, System.Globalization.NumberStyles style, IFormatProvider provider)
533
        {
534
            return (Half)Single.Parse(s, style, provider);
535
        }
536
 
537
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent. Returns success.</summary>
538
        /// <param name="s">String representation of the number to convert.</param>
539
        /// <param name="result">The Half instance to write to.</param>
540
        /// <returns>Success.</returns>
541
        public static bool TryParse(string s, out Half result)
542
        {
543
            float f;
544
            bool b = Single.TryParse(s, out f);
545
            result = (Half)f;
546
            return b;
547
        }
548
 
549
        /// <summary>Converts the string representation of a number to a half-precision floating-point equivalent. Returns success.</summary>
550
        /// <param name="s">String representation of the number to convert.</param>
551
        /// <param name="style">Specifies the format of s.</param>
552
        /// <param name="provider">Culture-specific formatting information.</param>
553
        /// <param name="result">The Half instance to write to.</param>
554
        /// <returns>Success.</returns>
555
        public static bool TryParse(string s, System.Globalization.NumberStyles style, IFormatProvider provider, out Half result)
556
        {
557
            float f;
558
            bool b = Single.TryParse(s, style, provider, out f);
559
            result = (Half)f;
560
            return b;
561
        }
562
 
563
        #endregion String -> Half
564
 
565
        #region BitConverter
566
 
567
        /// <summary>Returns the Half as an array of bytes.</summary>
568
        /// <param name="h">The Half to convert.</param>
569
        /// <returns>The input as byte array.</returns>
570
        public static byte[] GetBytes(Half h)
571
        {
572
            return BitConverter.GetBytes(h.bits);
573
        }
574
 
575
        /// <summary>Converts an array of bytes into Half.</summary>
576
        /// <param name="value">A Half in it's byte[] representation.</param>
577
        /// <param name="startIndex">The starting position within value.</param>
578
        /// <returns>A new Half instance.</returns>
579
        public static Half FromBytes(byte[] value, int startIndex)
580
        {
581
            Half h;
582
            h.bits = BitConverter.ToUInt16(value, startIndex);
583
            return h;
584
        }
585
 
586
        #endregion BitConverter
587
    }
588
}