123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- //
- // The graphics engine GLXEngine
- //
- unit Stage.RGBE;
- (* Stage RGBE utils *)
- interface
- uses
- System.Classes,
- System.SysUtils,
- System.Math,
- Stage.VectorTypes,
- Stage.VectorGeometry;
- procedure Float2rgbe(var RGBE: TVector4b; const Red, Green, Blue: Single);
- procedure Rgbe2float(var Red, Green, Blue: Single; const RGBE: TVector4b);
- procedure LoadRLEpixels(Stream: TStream; Dst: PSingle;
- Scanline_width, Num_scanlines: Integer);
- procedure LoadRGBEpixels(Stream: TStream; Dst: PSingle; Numpixels: Integer);
- implementation //----------------------------------------------------------
- type
- ERGBEexception = class(Exception);
- // Extract exponent and mantissa from X
- procedure Frexp(X: Extended; var Mantissa: Extended; var Exponent: Integer);
- begin
- Exponent := 0;
- if (X <> 0) then
- if (Abs(X) < 0.5) then
- repeat
- X := X * 2;
- Dec(Exponent);
- until (Abs(X) >= 0.5)
- else
- while (Abs(X) >= 1) do
- begin
- X := X / 2;
- Inc(Exponent);
- end;
- Mantissa := X;
- end;
- function Ldexp(X: Extended; const P: Integer): Extended;
- begin
- Ldexp := X * PowerSingle(2.0, P); // Result := X * (2^P)
- end;
- // standard conversion from float pixels to rgbe pixels
- procedure Float2rgbe(var RGBE: TVector4b; const Red, Green, Blue: Single);
- var
- V, M: Extended;
- E: Integer;
- begin
- V := Red;
- if (Green > V) then
- V := Green;
- if (Blue > V) then
- V := Blue;
- if (V < 1E-32) then
- begin
- RGBE.X := 0;
- RGBE.Y := 0;
- RGBE.Z := 0;
- RGBE.W := 0;
- end
- else
- begin
- FrExp(V, M, E);
- M := M * 256 / V;
- RGBE.X := Floor(Red * V);
- RGBE.Y := Floor(Green * V);
- RGBE.Z := Floor(Blue * V);
- RGBE.W := Floor(E + 128);
- end;
- end;
- // standard conversion from rgbe to float pixels
- // note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels
- // in the range [0,1] to map back into the range [0,1].
- procedure Rgbe2float(var Red, Green, Blue: Single; const RGBE: TVector4b);
- var
- F: Single;
- begin
- if RGBE.W <> 0 then // nonzero pixel
- begin
- F := Ldexp(1.0, RGBE.W - (128 + 8));
- Red := RGBE.X * F;
- Green := RGBE.Y * F;
- Blue := RGBE.Z * F;
- end
- else
- begin
- Red := 0;
- Green := 0;
- Blue := 0;
- end;
- end;
- procedure LoadRLEpixels(Stream: TStream; Dst: PSingle;
- Scanline_width, Num_scanlines: Integer);
- var
- RgbeTemp: TVector4b;
- Buf: TVector2b;
- Rf, Gf, Bf: Single;
- Scanline_buffer: PByteArray;
- Ptr, Ptr_end: PByte;
- I: Integer;
- Count: Cardinal;
- begin
- if (Scanline_width < 8) or (Scanline_width > $7FFF) then
- begin
- // run length encoding is not allowed so read flat
- LoadRGBEPixels(Stream, Dst, Scanline_width * Num_scanlines);
- Exit;
- end;
- Scanline_buffer := nil;
- while Num_scanlines > 0 do
- begin
- Stream.Read(RgbeTemp, SizeOf(TVector4b));
- if (RgbeTemp.X <> 2) or (RgbeTemp.Y <> 2) or
- (RgbeTemp.Z and $80 <> 0) then
- begin
- // this file is not run length encoded
- Rgbe2float(Rf, Gf, Bf, RgbeTemp);
- Dst^ := Rf;
- Inc(Dst);
- Dst^ := Gf;
- Inc(Dst);
- Dst^ := Bf;
- Inc(Dst);
- if Assigned(Scanline_buffer) then
- FreeMem(Scanline_buffer);
- LoadRGBEpixels(Stream, Dst, Scanline_width * Num_scanlines - 1);
- Exit;
- end;
- if ((Integer(RgbeTemp.Z) shl 8) or RgbeTemp.W) <> Scanline_width
- then
- begin
- if Assigned(Scanline_buffer) then
- FreeMem(Scanline_buffer);
- raise ERGBEexception.Create('Wrong scanline width.');
- end;
- if not Assigned(Scanline_buffer) then
- ReallocMem(Scanline_buffer, 4 * Scanline_width);
- Ptr := PByte(Scanline_buffer);
- // read each of the four channels for the scanline into the buffer
- for I := 0 to 3 do
- begin
- Ptr_end := @Scanline_buffer[(I + 1) * Scanline_width];
- while Cardinal(Ptr) < Cardinal(Ptr_end) do
- begin
- Stream.Read(Buf, SizeOf(TVector2b));
- if Buf.X > 128 then
- begin // a run of the same value
- Count := Buf.X - 128;
- if (Count = 0) or (Count > Cardinal(Ptr_end) - Cardinal(Ptr)) then
- begin
- FreeMem(Scanline_buffer);
- raise ERGBEexception.Create('Bad HDR scanline data.');
- end;
- while Count > 0 do
- begin
- Ptr^ := Buf.Y;
- Dec(Count);
- Inc(Ptr);
- end;
- end
- else
- begin // a non-run
- Count := Buf.X;
- if (Count = 0) or (Count > Cardinal(Ptr_end) - Cardinal(Ptr)) then
- begin
- FreeMem(Scanline_buffer);
- raise ERGBEexception.Create('Bad HDR scanline data.');
- end;
- Ptr^ := Buf.Y;
- Dec(Count);
- Inc(Ptr);
- if Count > 0 then
- Stream.Read(Ptr^, Count);
- Inc(Ptr, Count);
- end;
- end;
- end;
- // now convert data from buffer into floats
- for I := 0 to Scanline_width - 1 do
- begin
- RgbeTemp.X := Scanline_buffer[I];
- RgbeTemp.Y := Scanline_buffer[I + Scanline_width];
- RgbeTemp.Z := Scanline_buffer[I + 2 * Scanline_width];
- RgbeTemp.W := Scanline_buffer[I + 3 * Scanline_width];
- Rgbe2float(Rf, Gf, Bf, RgbeTemp);
- Dst^ := Rf;
- Inc(Dst);
- Dst^ := Gf;
- Inc(Dst);
- Dst^ := Bf;
- Inc(Dst);
- end;
- Dec(Num_scanlines);
- end;
- if Assigned(Scanline_buffer) then
- FreeMem(Scanline_buffer);
- end;
- procedure LoadRGBEpixels(Stream: TStream; Dst: PSingle; Numpixels: Integer);
- var
- RgbeTemp: TVector4b;
- Rf, Gf, Bf: Single;
- begin
- while Numpixels > 0 do
- begin
- Stream.Read(RgbeTemp, SizeOf(TVector4b));
- Rgbe2float(Rf, Gf, Bf, RgbeTemp);
- Dst^ := Rf;
- Inc(Dst);
- Dst^ := Gf;
- Inc(Dst);
- Dst^ := Bf;
- Inc(Dst);
- Dec(Numpixels);
- end;
- end;
- end.
|