2
// This unit is part of the GLScene Engine https://github.com/glscene
8
17/11/14 - PW - Renamed from RGBE.pas to GLSRGBE.pas
9
15/06/10 - Yar - Fixes for Linux x64
10
20/01/10 - Yar - Creation
22
GLVectorTypes, GLVectorGeometry, GLCrossPlatform;
24
procedure Float2rgbe(var RGBE: TVector4b; const Red, Green, Blue: Single);
25
procedure Rgbe2float(var Red, Green, Blue: Single; const RGBE: TVector4b);
26
procedure LoadRLEpixels(Stream: TStream; Dst: PSingle;
27
Scanline_width, Num_scanlines: Integer);
28
procedure LoadRGBEpixels(Stream: TStream; Dst: PSingle; Numpixels: Integer);
33
ERGBEexception = class(Exception);
35
{ Extract exponent and mantissa from X }
36
procedure Frexp(X: Extended; var Mantissa: Extended; var Exponent: Integer);
37
{ Mantissa ptr in EAX, Exponent ptr in EDX }
42
if (Abs(X) < 0.5) then
48
while (Abs(X) >= 1) do
58
MOV dword ptr [edx], 0 { if X = 0, return 0 }
66
FXTRACT // ST(1) = exponent, (pushed) ST = fraction
69
// The FXTRACT instruction normalizes the fraction 1 bit higher than
70
// wanted for the definition of frexp() so we need to tweak the result
71
// by scaling the fraction down and incrementing the exponent.
77
FSCALE // scale fraction
78
INC dword ptr [edx] // exponent biased to match
79
FSTP ST(1) // discard -1, leave fraction as TOS
88
function Ldexp(X: Extended; const P: Integer): Extended;
91
Ldexp := X * Intpower(2.0, P);
93
{ Result := X * (2^P) }
105
// standard conversion from float pixels to rgbe pixels
106
procedure Float2rgbe(var RGBE: TVector4b; const Red, Green, Blue: Single);
127
RGBE.V[0] := Floor(Red * V);
128
RGBE.V[1] := Floor(Green * V);
129
RGBE.V[2] := Floor(Blue * V);
130
RGBE.V[3] := Floor(E + 128);
134
// standard conversion from rgbe to float pixels
135
// note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels
136
// in the range [0,1] to map back into the range [0,1].
137
procedure Rgbe2float(var Red, Green, Blue: Single; const RGBE: TVector4b);
141
if RGBE.V[3] <> 0 then // nonzero pixel
143
F := Ldexp(1.0, RGBE.V[3] - (128 + 8));
144
Red := RGBE.V[0] * F;
145
Green := RGBE.V[1] * F;
146
Blue := RGBE.V[2] * F;
156
procedure LoadRLEpixels(Stream: TStream; Dst: PSingle;
157
Scanline_width, Num_scanlines: Integer);
162
Scanline_buffer: PByteArray;
167
if (Scanline_width < 8) or (Scanline_width > $7FFF) then
169
// run length encoding is not allowed so read flat
170
LoadRGBEPixels(Stream, Dst, Scanline_width * Num_scanlines);
174
Scanline_buffer := nil;
175
while Num_scanlines > 0 do
177
Stream.Read(RgbeTemp, SizeOf(TVector4b));
178
if (RgbeTemp.V[0] <> 2) or (RgbeTemp.V[1] <> 2) or
179
(RgbeTemp.V[2] and $80 <> 0) then
181
// this file is not run length encoded
182
Rgbe2float(Rf, Gf, Bf, RgbeTemp);
189
if Assigned(Scanline_buffer) then
190
FreeMem(Scanline_buffer);
191
LoadRGBEpixels(Stream, Dst, Scanline_width * Num_scanlines - 1);
194
if ((Integer(RgbeTemp.V[2]) shl 8) or RgbeTemp.V[3]) <> Scanline_width
197
if Assigned(Scanline_buffer) then
198
FreeMem(Scanline_buffer);
199
raise ERGBEexception.Create('Wrong scanline width.');
202
if not Assigned(Scanline_buffer) then
203
ReallocMem(Scanline_buffer, 4 * Scanline_width);
205
Ptr := PByte(Scanline_buffer);
206
// read each of the four channels for the scanline into the buffer
209
Ptr_end := @Scanline_buffer[(I + 1) * Scanline_width];
210
while PtrUInt(Ptr) < PtrUInt(Ptr_end) do
212
Stream.Read(Buf, SizeOf(TVector2b));
213
if Buf.V[0] > 128 then
214
begin // a run of the same value
215
Count := Buf.V[0] - 128;
216
if (Count = 0) or (Count > PtrUInt(Ptr_end) - PtrUInt(Ptr)) then
218
FreeMem(Scanline_buffer);
219
raise ERGBEexception.Create('Bad HDR scanline data.');
231
if (Count = 0) or (Count > PtrUInt(Ptr_end) - PtrUInt(Ptr)) then
233
FreeMem(Scanline_buffer);
234
raise ERGBEexception.Create('Bad HDR scanline data.');
240
Stream.Read(Ptr^, Count);
246
// now convert data from buffer into floats
247
for I := 0 to Scanline_width - 1 do
249
RgbeTemp.V[0] := Scanline_buffer[I];
250
RgbeTemp.V[1] := Scanline_buffer[I + Scanline_width];
251
RgbeTemp.V[2] := Scanline_buffer[I + 2 * Scanline_width];
252
RgbeTemp.V[3] := Scanline_buffer[I + 3 * Scanline_width];
253
Rgbe2float(Rf, Gf, Bf, RgbeTemp);
263
if Assigned(Scanline_buffer) then
264
FreeMem(Scanline_buffer);
267
procedure LoadRGBEpixels(Stream: TStream; Dst: PSingle; Numpixels: Integer);
272
while Numpixels > 0 do
274
Stream.Read(RgbeTemp, SizeOf(TVector4b));
275
Rgbe2float(Rf, Gf, Bf, RgbeTemp);