GLS.FileHDR.pas 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.FileHDR;
  5. (* HDR File support *)
  6. interface
  7. {$I GLScene.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. System.Classes,
  12. System.SysUtils,
  13. GLS.OpenGLTokens,
  14. GLS.Context,
  15. GLS.Graphics,
  16. GLS.TextureFormat,
  17. GLS.ApplicationFileIO,
  18. GLS.RGBE,
  19. GLS.VectorTypes,
  20. GLS.VectorGeometry;
  21. type
  22. TGLHDRImage = class(TGLBaseImage)
  23. private
  24. function GetProgramType: Ansistring;
  25. procedure SetProgramType(aval: Ansistring);
  26. protected
  27. fGamma: Single; // image has already been gamma corrected with
  28. // given gamma. defaults to 1.0 (no correction) */
  29. fExposure: Single; // a value of 1.0 in an image corresponds to
  30. // <exposure> watts/steradian/m^2.
  31. // defaults to 1.0
  32. fProgramType: string[16];
  33. public
  34. class function Capabilities: TGLDataFileCapabilities; override;
  35. procedure LoadFromFile(const filename: string); override;
  36. procedure LoadFromStream(stream: TStream); override;
  37. procedure AssignFromTexture(textureContext: TGLContext; const textureHandle: Cardinal;
  38. textureTarget: TGLTextureTarget; const CurrentFormat: Boolean;
  39. const intFormat: TGLInternalFormat); reintroduce;
  40. property Gamma: Single read fGamma;
  41. property Exposure: Single read fExposure;
  42. property ProgramType: Ansistring read GetProgramType write SetProgramType;
  43. end;
  44. //---------------------------------------------------------------------
  45. implementation
  46. //---------------------------------------------------------------------
  47. // ------------------
  48. // ------------------ TGLHDRImage ------------------
  49. // ------------------
  50. procedure TGLHDRImage.LoadFromFile(const filename: string);
  51. var
  52. fs: TStream;
  53. begin
  54. if FileStreamExists(fileName) then
  55. begin
  56. fs := TFileStream.Create(fileName, fmOpenRead);
  57. try
  58. LoadFromStream(fs);
  59. finally
  60. fs.Free;
  61. ResourceName := filename;
  62. end;
  63. end
  64. else
  65. raise EInvalidRasterFile.CreateFmt('File %s not found', [filename]);
  66. end;
  67. procedure TGLHDRImage.LoadFromStream(stream: TStream);
  68. const
  69. cRgbeFormat32bit = 'FORMAT=32-bit_rle_rgbe';
  70. cGamma = 'GAMMA=';
  71. cEXPOSURE = 'EXPOSURE=';
  72. cY = '-Y ';
  73. var
  74. buf: array[0..1023] of AnsiChar;
  75. header: TStringList;
  76. s, sn: string;
  77. lineSize: integer;
  78. tempBuf, top, bottom: PByte;
  79. i, j, err: Integer;
  80. formatDefined: boolean;
  81. function CmpWord(const word: string): boolean;
  82. var
  83. l: Integer;
  84. ts: string;
  85. begin
  86. Result := false;
  87. ts := header.Strings[i];
  88. if Length(word) > Length(ts) then
  89. Exit;
  90. for l := 1 to Length(word) do
  91. if word[l] <> ts[l] then
  92. Exit;
  93. Result := true;
  94. end;
  95. begin
  96. fProgramtype := '';
  97. fGamma := 1.0;
  98. fExposure := 1.0;
  99. UnMipmap;
  100. // Read HDR header
  101. stream.Read(buf, Length(buf) * sizeOf(AnsiChar));
  102. header := TStringList.Create;
  103. s := '';
  104. i := 0;
  105. j := 0;
  106. while i < Length(buf) do
  107. begin
  108. if buf[i] = #0 then
  109. Break;
  110. if buf[i] = #10 then
  111. begin
  112. header.Add(s);
  113. s := '';
  114. Inc(i);
  115. j := i;
  116. Continue;
  117. end;
  118. s := s + string(buf[i]);
  119. Inc(i);
  120. end;
  121. if i < Length(buf) then
  122. stream.Position := j
  123. else
  124. raise EInvalidRasterFile.Create('Can''t find HDR header end.');
  125. if (header.Strings[0][1] <> '#') or (header.Strings[0][2] <> '?') then
  126. begin
  127. header.Free;
  128. raise EInvalidRasterFile.Create('Bad HDR initial token.');
  129. end;
  130. // Get program type
  131. SetProgramtype(AnsiString(Copy(header.Strings[0], 3, Length(header.Strings[0])
  132. - 2)));
  133. formatDefined := false;
  134. for i := 1 to header.Count - 1 do
  135. begin
  136. if header.Strings[i] = cRgbeFormat32bit then
  137. formatDefined := true
  138. else if CmpWord(cGamma) then
  139. begin
  140. j := Length(cGamma);
  141. s := Copy(header.Strings[i], j + 1, Length(header.Strings[i]) - j);
  142. val(s, fGamma, err);
  143. if err <> 0 then
  144. raise EInvalidRasterFile.Create('Bad HDR header.');
  145. end
  146. else if CmpWord(cEXPOSURE) then
  147. begin
  148. j := Length(cEXPOSURE);
  149. s := Copy(header.Strings[i], j + 1, Length(header.Strings[i]) - j);
  150. val(s, fExposure, err);
  151. if err <> 0 then
  152. raise EInvalidRasterFile.Create('Bad HDR header.');
  153. end
  154. else if CmpWord(cY) then
  155. begin
  156. j := Length(cY);
  157. s := Copy(header.Strings[i], j + 1, Length(header.Strings[i]) - j);
  158. j := Pos(' ', s);
  159. sn := Copy(s, 1, j - 1);
  160. val(sn, FLOD[0].Height, err);
  161. Delete(s, 1, j + 3); // scip '+X '
  162. val(s, FLOD[0].Width, err);
  163. if err <> 0 then
  164. raise EInvalidRasterFile.Create('Bad HDR header.');
  165. end
  166. end; // for i
  167. header.Free;
  168. if not formatDefined then
  169. raise EInvalidRasterFile.Create('no FORMAT specifier found.');
  170. if (FLOD[0].Width = 0) or (FLOD[0].Height = 0) then
  171. raise EInvalidRasterFile.Create('Bad image dimension.');
  172. //set all the parameters
  173. FLOD[0].Depth := 0;
  174. fColorFormat := GL_RGB;
  175. fInternalFormat := tfRGBA_FLOAT32;
  176. fDataType := GL_FLOAT;
  177. fCubeMap := false;
  178. fTextureArray := false;
  179. fElementSize := GetTextureElementSize(tfFLOAT_RGB32);
  180. ReallocMem(fData, DataSize);
  181. LoadRLEpixels(stream, PSingle(fData), FLOD[0].Width, FLOD[0].Height);
  182. //hdr images come in upside down then flip it
  183. lineSize := fElementSize * FLOD[0].Width;
  184. GetMem(tempBuf, lineSize);
  185. top := PByte(fData);
  186. bottom := top;
  187. Inc(bottom, lineSize * (FLOD[0].Height - 1));
  188. for j := 0 to (FLOD[0].Height div 2) - 1 do
  189. begin
  190. Move(top^, tempBuf^, lineSize);
  191. Move(bottom^, top^, lineSize);
  192. Move(tempBuf^, bottom^, lineSize);
  193. Inc(top, lineSize);
  194. Dec(bottom, lineSize);
  195. end;
  196. FreeMem(tempBuf);
  197. end;
  198. function TGLHDRImage.GetProgramType: Ansistring;
  199. begin
  200. Result := fProgramType;
  201. end;
  202. procedure TGLHDRImage.SetProgramType(aval: Ansistring);
  203. var
  204. i: integer;
  205. begin
  206. for i := 1 to Length(fProgramType) do
  207. fProgramType[i] := aval[i];
  208. end;
  209. procedure TGLHDRImage.AssignFromTexture(textureContext: TGLContext;
  210. const textureHandle: Cardinal;
  211. textureTarget: TGLTextureTarget;
  212. const CurrentFormat: Boolean;
  213. const intFormat: TGLInternalFormat);
  214. var
  215. oldContext: TGLContext;
  216. contextActivate: Boolean;
  217. texFormat: Cardinal;
  218. residentFormat: TGLInternalFormat;
  219. glTarget: Cardinal;
  220. begin
  221. glTarget := DecodeTextureTarget(textureTarget);
  222. if not ((glTarget = GL_TEXTURE_2D)
  223. or (glTarget = GL_TEXTURE_RECTANGLE)) then
  224. Exit;
  225. oldContext := CurrentGLContext;
  226. contextActivate := (oldContext <> textureContext);
  227. if contextActivate then
  228. begin
  229. if Assigned(oldContext) then
  230. oldContext.Deactivate;
  231. textureContext.Activate;
  232. end;
  233. try
  234. textureContext.GLStates.TextureBinding[0, textureTarget] := textureHandle;
  235. fLevelCount := 0;
  236. fCubeMap := false;
  237. fTextureArray := false;
  238. fColorFormat := GL_RGB;
  239. fDataType := GL_FLOAT;
  240. // Check level existence
  241. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_INTERNAL_FORMAT, @texFormat);
  242. if texFormat > 1 then
  243. begin
  244. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_WIDTH, @FLOD[0].Width);
  245. gl.GetTexLevelParameteriv(glTarget, 0, GL_TEXTURE_HEIGHT, @FLOD[0].Height);
  246. FLOD[0].Depth := 0;
  247. residentFormat := OpenGLFormatToInternalFormat(texFormat);
  248. if CurrentFormat then
  249. fInternalFormat := residentFormat
  250. else
  251. fInternalFormat := intFormat;
  252. Inc(fLevelCount);
  253. end;
  254. if fLevelCount > 0 then
  255. begin
  256. fElementSize := GetTextureElementSize(fColorFormat, fDataType);
  257. ReallocMem(FData, DataSize);
  258. gl.GetTexImage(glTarget, 0, fColorFormat, fDataType, fData);
  259. end
  260. else
  261. fLevelCount := 1;
  262. gl.CheckError;
  263. finally
  264. if contextActivate then
  265. begin
  266. textureContext.Deactivate;
  267. if Assigned(oldContext) then
  268. oldContext.Activate;
  269. end;
  270. end;
  271. end;
  272. class function TGLHDRImage.Capabilities: TGLDataFileCapabilities;
  273. begin
  274. Result := [dfcRead {, dfcWrite}];
  275. end;
  276. initialization
  277. { Register this Fileformat-Handler with GLScene }
  278. RegisterRasterFormat('hdr', 'High Dynamic Range Image', TGLHDRImage);
  279. end.