ImagingOpenGL.pas 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains functions for loading and saving OpenGL textures
  25. using Imaging and for converting images to textures and vice versa.}
  26. unit ImagingOpenGL;
  27. {$I ImagingOptions.inc}
  28. interface
  29. uses
  30. SysUtils, Classes, ImagingTypes, Imaging, ImagingFormats,
  31. ImagingUtility, gl, glext{,dglOpenGL};
  32. type
  33. { Various texture capabilities of installed OpenGL driver.}
  34. TGLTextureCaps = record
  35. MaxTextureSize: LongInt;
  36. PowerOfTwo: Boolean;
  37. DXTCompression: Boolean;
  38. FloatTextures: Boolean;
  39. MaxAnisotropy: LongInt;
  40. MaxSimultaneousTextures: LongInt;
  41. end;
  42. { Returns texture capabilities of installed OpenGL driver.}
  43. function GetGLTextureCaps(var Caps: TGLTextureCaps): Boolean;
  44. { Function which can be used to retrieve GL extension functions.}
  45. function GetGLProcAddress(const ProcName: string): Pointer;
  46. { Returns True if the given GL extension is supported.}
  47. function IsGLExtensionSupported(const Extension: string): Boolean;
  48. { Returns True if the given image format can be represented as GL texture
  49. format. GLFormat, GLType, and GLInternal are parameters for functions like
  50. glTexImage. Note that GLU functions like gluBuildMipmaps cannot handle some
  51. formats returned by this function (i.e. GL_UNSIGNED_SHORT_5_5_5_1 as GLType).
  52. If you are using compressed or floating-point images make sure that they are
  53. supported by hardware using GetGLTextureCaps, ImageFormatToGL does not
  54. check this.}
  55. function ImageFormatToGL(Format: TImageFormat; var GLFormat: GLenum;
  56. var GLType: GLenum; var GLInternal: GLint): Boolean;
  57. { All GL textures created by Imaging functions have default parameters set -
  58. that means that no glTexParameter calls are made so default filtering,
  59. wrapping, and other parameters are used. Created textures
  60. are left bound by glBindTexture when function is exited.}
  61. { Creates GL texture from image in file in format supported by Imaging.
  62. You can use CreatedWidth and Height parameters to query dimensions of created textures
  63. (it could differ from dimensions of source image).}
  64. function LoadGLTextureFromFile(const FileName: string; CreatedWidth: PLongInt = nil;
  65. CreatedHeight: PLongInt = nil): GLuint;
  66. { Creates GL texture from image in stream in format supported by Imaging.
  67. You can use CreatedWidth and Height parameters to query dimensions of created textures
  68. (it could differ from dimensions of source image).}
  69. function LoadGLTextureFromStream(Stream: TStream; CreatedWidth: PLongInt = nil;
  70. CreatedHeight: PLongInt = nil): GLuint;
  71. { Creates GL texture from image in memory in format supported by Imaging.
  72. You can use CreatedWidth and Height parameters to query dimensions of created textures
  73. (it could differ from dimensions of source image).}
  74. function LoadGLTextureFromMemory(Data: Pointer; Size: LongInt;
  75. CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): GLuint;
  76. { Converts TImageData structure to OpenGL texture.
  77. Input images is used as main mipmap level and additional requested
  78. levels are generated from this one. For the details on parameters
  79. look at CreateGLTextureFromMultiImage function.}
  80. function CreateGLTextureFromImage(const Image: TImageData;
  81. Width: LongInt = 0; Height: LongInt = 0; MipMaps: Boolean = True;
  82. OverrideFormat: TImageFormat = ifUnknown; CreatedWidth: PLongInt = nil;
  83. CreatedHeight: PLongInt = nil): GLuint;
  84. { Converts images in TDymImageDataArray to one OpenGL texture.
  85. First image in array is used as main mipmap level and additional images
  86. are used as subsequent levels. If there is not enough images
  87. in array missing levels are automatically generated.
  88. If driver supports only power of two sized textures images are resized.
  89. OverrideFormat can be used to convert image into specific format before
  90. it is passed to OpenGL, ifUnknown means no conversion.
  91. If desired texture format is not supported by hardware default
  92. A8R8G8B8 format is used instead for color images and ifGray8 is used
  93. for luminance images. DXTC (S3TC) compressed and floating point textures
  94. are created if supported by hardware. Width and Height of 0 mean
  95. use width and height of main image. MipMaps set to True mean build
  96. all possible levels, False means use only level 0.
  97. You can use CreatedWidth and CreatedHeight parameters to query dimensions of
  98. created texture's largest mipmap level (it could differ from dimensions
  99. of source image).}
  100. function CreateGLTextureFromMultiImage(const Images: TDynImageDataArray;
  101. Width: LongInt = 0; Height: LongInt = 0; MipMaps: Boolean = True;
  102. OverrideFormat: TImageFormat = ifUnknown; CreatedWidth: PLongInt = nil;
  103. CreatedHeight: PLongInt = nil): GLuint;
  104. { Saves GL texture to file in one of formats supported by Imaging.
  105. Saves all present mipmap levels.}
  106. function SaveGLTextureToFile(const FileName: string; const Texture: GLuint): Boolean;
  107. { Saves GL texture to stream in one of formats supported by Imaging.
  108. Saves all present mipmap levels.}
  109. function SaveGLTextureToStream(const Ext: string; Stream: TStream; const Texture: GLuint): Boolean;
  110. { Saves GL texture to memory in one of formats supported by Imaging.
  111. Saves all present mipmap levels.}
  112. function SaveGLTextureToMemory(const Ext: string; Data: Pointer; var Size: LongInt; const Texture: GLuint): Boolean;
  113. { Converts main level of the GL texture to TImageData strucrue. OverrideFormat
  114. can be used to convert output image to the specified format rather
  115. than use the format taken from GL texture, ifUnknown means no conversion.}
  116. function CreateImageFromGLTexture(const Texture: GLuint;
  117. var Image: TImageData; OverrideFormat: TImageFormat = ifUnknown): Boolean;
  118. { Converts GL texture to TDynImageDataArray array of images. You can specify
  119. how many mipmap levels of the input texture you want to be converted
  120. (default is all levels). OverrideFormat can be used to convert output images to
  121. the specified format rather than use the format taken from GL texture,
  122. ifUnknown means no conversion.}
  123. function CreateMultiImageFromGLTexture(const Texture: GLuint;
  124. var Images: TDynImageDataArray; MipLevels: LongInt = 0;
  125. OverrideFormat: TImageFormat = ifUnknown): Boolean;
  126. implementation
  127. const
  128. // cube map consts
  129. GL_TEXTURE_BINDING_CUBE_MAP = $8514;
  130. GL_TEXTURE_CUBE_MAP_POSITIVE_X = $8515;
  131. GL_TEXTURE_CUBE_MAP_NEGATIVE_X = $8516;
  132. GL_TEXTURE_CUBE_MAP_POSITIVE_Y = $8517;
  133. GL_TEXTURE_CUBE_MAP_NEGATIVE_Y = $8518;
  134. GL_TEXTURE_CUBE_MAP_POSITIVE_Z = $8519;
  135. GL_TEXTURE_CUBE_MAP_NEGATIVE_Z = $851A;
  136. // texture formats
  137. GL_COLOR_INDEX = $1900;
  138. GL_STENCIL_INDEX = $1901;
  139. GL_DEPTH_COMPONENT = $1902;
  140. GL_RED = $1903;
  141. GL_GREEN = $1904;
  142. GL_BLUE = $1905;
  143. GL_ALPHA = $1906;
  144. GL_RGB = $1907;
  145. GL_RGBA = $1908;
  146. GL_LUMINANCE = $1909;
  147. GL_LUMINANCE_ALPHA = $190A;
  148. GL_BGR_EXT = $80E0;
  149. GL_BGRA_EXT = $80E1;
  150. // texture internal formats
  151. GL_ALPHA4 = $803B;
  152. GL_ALPHA8 = $803C;
  153. GL_ALPHA12 = $803D;
  154. GL_ALPHA16 = $803E;
  155. GL_LUMINANCE4 = $803F;
  156. GL_LUMINANCE8 = $8040;
  157. GL_LUMINANCE12 = $8041;
  158. GL_LUMINANCE16 = $8042;
  159. GL_LUMINANCE4_ALPHA4 = $8043;
  160. GL_LUMINANCE6_ALPHA2 = $8044;
  161. GL_LUMINANCE8_ALPHA8 = $8045;
  162. GL_LUMINANCE12_ALPHA4 = $8046;
  163. GL_LUMINANCE12_ALPHA12 = $8047;
  164. GL_LUMINANCE16_ALPHA16 = $8048;
  165. GL_INTENSITY = $8049;
  166. GL_INTENSITY4 = $804A;
  167. GL_INTENSITY8 = $804B;
  168. GL_INTENSITY12 = $804C;
  169. GL_INTENSITY16 = $804D;
  170. GL_R3_G3_B2 = $2A10;
  171. GL_RGB4 = $804F;
  172. GL_RGB5 = $8050;
  173. GL_RGB8 = $8051;
  174. GL_RGB10 = $8052;
  175. GL_RGB12 = $8053;
  176. GL_RGB16 = $8054;
  177. GL_RGBA2 = $8055;
  178. GL_RGBA4 = $8056;
  179. GL_RGB5_A1 = $8057;
  180. GL_RGBA8 = $8058;
  181. GL_RGB10_A2 = $8059;
  182. GL_RGBA12 = $805A;
  183. GL_RGBA16 = $805B;
  184. // floating point texture formats
  185. GL_RGBA32F_ARB = $8814;
  186. GL_INTENSITY32F_ARB = $8817;
  187. GL_LUMINANCE32F_ARB = $8818;
  188. GL_RGBA16F_ARB = $881A;
  189. GL_INTENSITY16F_ARB = $881D;
  190. GL_LUMINANCE16F_ARB = $881E;
  191. // compressed texture formats
  192. GL_COMPRESSED_RGBA_S3TC_DXT1_EXT = $83F1;
  193. GL_COMPRESSED_RGBA_S3TC_DXT3_EXT = $83F2;
  194. GL_COMPRESSED_RGBA_S3TC_DXT5_EXT = $83F3;
  195. // various GL extension constants
  196. GL_MAX_TEXTURE_UNITS = $84E2;
  197. GL_TEXTURE_MAX_ANISOTROPY_EXT = $84FE;
  198. GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = $84FF;
  199. // texture source data formats
  200. GL_UNSIGNED_BYTE_3_3_2 = $8032;
  201. GL_UNSIGNED_SHORT_4_4_4_4 = $8033;
  202. GL_UNSIGNED_SHORT_5_5_5_1 = $8034;
  203. GL_UNSIGNED_INT_8_8_8_8 = $8035;
  204. GL_UNSIGNED_INT_10_10_10_2 = $8036;
  205. GL_UNSIGNED_BYTE_2_3_3_REV = $8362;
  206. GL_UNSIGNED_SHORT_5_6_5 = $8363;
  207. GL_UNSIGNED_SHORT_5_6_5_REV = $8364;
  208. GL_UNSIGNED_SHORT_4_4_4_4_REV = $8365;
  209. GL_UNSIGNED_SHORT_1_5_5_5_REV = $8366;
  210. GL_UNSIGNED_INT_8_8_8_8_REV = $8367;
  211. GL_UNSIGNED_INT_2_10_10_10_REV = $8368;
  212. GL_HALF_FLOAT_ARB = $140B;
  213. {$IFDEF MSWINDOWS}
  214. GLLibName = 'opengl32.dll';
  215. {$ENDIF}
  216. {$IFDEF UNIX}
  217. GLLibName = 'libGL.so';
  218. {$ENDIF}
  219. type
  220. TglCompressedTexImage2D = procedure (Target: GLenum; Level: GLint;
  221. InternalFormat: GLenum; Width: GLsizei; Height: GLsizei; Border: GLint;
  222. ImageSize: GLsizei; const Data: PGLvoid);
  223. {$IFDEF MSWINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  224. var
  225. glCompressedTexImage2D: TglCompressedTexImage2D = nil;
  226. ExtensionBuffer: string = '';
  227. {$IFDEF MSWINDOWS}
  228. function wglGetProcAddress(ProcName: PChar): Pointer; stdcall; external GLLibName;
  229. {$ENDIF}
  230. {$IFDEF UNIX}
  231. function glXGetProcAddress(ProcName: PChar): Pointer; cdecl; external GLLibName;
  232. {$ENDIF}
  233. function IsGLExtensionSupported(const Extension: string): Boolean;
  234. var
  235. ExtPos: LongInt;
  236. begin
  237. if ExtensionBuffer = '' then
  238. ExtensionBuffer := glGetString(GL_EXTENSIONS);
  239. ExtPos := Pos(Extension, ExtensionBuffer);
  240. Result := ExtPos > 0;
  241. if Result then
  242. begin
  243. Result := ((ExtPos + Length(Extension) - 1) = Length(ExtensionBuffer)) or
  244. not (ExtensionBuffer[ExtPos + Length(Extension)] in ['_', 'A'..'Z', 'a'..'z']);
  245. end;
  246. end;
  247. function GetGLProcAddress(const ProcName: string): Pointer;
  248. begin
  249. {$IFDEF MSWINDOWS}
  250. Result := wglGetProcAddress(PChar(ProcName));
  251. {$ENDIF}
  252. {$IFDEF UNIX}
  253. Result := glXGetProcAddress(PChar(ProcName));
  254. {$ENDIF}
  255. end;
  256. function GetGLTextureCaps(var Caps: TGLTextureCaps): Boolean;
  257. begin
  258. // check DXTC support and load extension functions if necesary
  259. Caps.DXTCompression := IsGLExtensionSupported('GL_ARB_texture_compression') and
  260. IsGLExtensionSupported('GL_EXT_texture_compression_s3tc');
  261. if Caps.DXTCompression then
  262. glCompressedTexImage2D := GetGLProcAddress('glCompressedTexImage2D');
  263. Caps.DXTCompression := Caps.DXTCompression and (@glCompressedTexImage2D <> nil);
  264. // check non power of 2 textures
  265. Caps.PowerOfTwo := not IsGLExtensionSupported('GL_ARB_texture_non_power_of_two');
  266. // check for floating point textures support
  267. Caps.FloatTextures := IsGLExtensionSupported('GL_ARB_texture_float');
  268. // get max texture size
  269. glGetIntegerv(GL_MAX_TEXTURE_SIZE, @Caps.MaxTextureSize);
  270. // get max anisotropy
  271. if IsGLExtensionSupported('GL_EXT_texture_filter_anisotropic') then
  272. glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, @Caps.MaxAnisotropy)
  273. else
  274. Caps.MaxAnisotropy := 0;
  275. // get number of texture units
  276. if IsGLExtensionSupported('GL_ARB_multitexture') then
  277. glGetIntegerv(GL_MAX_TEXTURE_UNITS, @Caps.MaxSimultaneousTextures)
  278. else
  279. Caps.MaxSimultaneousTextures := 1;
  280. // get max texture size
  281. glGetIntegerv(GL_MAX_TEXTURE_SIZE, @Caps.MaxTextureSize);
  282. Result := True;
  283. end;
  284. function ImageFormatToGL(Format: TImageFormat; var GLFormat: GLenum;
  285. var GLType: GLenum; var GLInternal: GLint): Boolean;
  286. begin
  287. GLFormat := 0;
  288. GLType := 0;
  289. GLInternal := 0;
  290. case Format of
  291. // Gray formats
  292. ifGray8, ifGray16:
  293. begin
  294. GLFormat := GL_LUMINANCE;
  295. GLType := Iff(Format = ifGray8, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT);
  296. GLInternal := Iff(Format = ifGray8, GL_LUMINANCE8, GL_LUMINANCE16);
  297. end;
  298. ifA8Gray8, ifA16Gray16:
  299. begin
  300. GLFormat := GL_LUMINANCE_ALPHA;
  301. GLType := Iff(Format = ifA8Gray8, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT);
  302. GLInternal := Iff(Format = ifA8Gray8, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE16_ALPHA16);
  303. end;
  304. // RGBA formats
  305. ifR3G3B2:
  306. begin
  307. GLFormat := GL_RGB;
  308. GLType := GL_UNSIGNED_BYTE_3_3_2;
  309. GLInternal := GL_R3_G3_B2;
  310. end;
  311. ifR5G6B5:
  312. begin
  313. GLFormat := GL_RGB;
  314. GLType := GL_UNSIGNED_SHORT_5_6_5;
  315. GLInternal := GL_RGB5;
  316. end;
  317. ifA1R5G5B5, ifX1R5G5B5:
  318. begin
  319. GLFormat := GL_BGRA_EXT;
  320. GLType := GL_UNSIGNED_SHORT_1_5_5_5_REV;
  321. GLInternal := Iff(Format = ifA1R5G5B5, GL_RGB5_A1, GL_RGB5);
  322. end;
  323. ifA4R4G4B4, ifX4R4G4B4:
  324. begin
  325. GLFormat := GL_BGRA_EXT;
  326. GLType := GL_UNSIGNED_SHORT_4_4_4_4_REV;
  327. GLInternal := Iff(Format = ifA4R4G4B4, GL_RGBA4, GL_RGB4);
  328. end;
  329. ifR8G8B8:
  330. begin
  331. GLFormat := GL_BGR_EXT;
  332. GLType := GL_UNSIGNED_BYTE;
  333. GLInternal := GL_RGB8;
  334. end;
  335. ifA8R8G8B8, ifX8R8G8B8:
  336. begin
  337. GLFormat := GL_BGRA_EXT;
  338. GLType := GL_UNSIGNED_BYTE;
  339. GLInternal := Iff(Format = ifA8R8G8B8, GL_RGBA8, GL_RGB8);
  340. end;
  341. ifR16G16B16, ifB16G16R16:
  342. begin
  343. GLFormat := Iff(Format = ifR16G16B16, GL_BGR_EXT, GL_RGB);
  344. GLType := GL_UNSIGNED_SHORT;
  345. GLInternal := GL_RGB16;
  346. end;
  347. ifA16R16G16B16, ifA16B16G16R16:
  348. begin
  349. GLFormat := Iff(Format = ifA16R16G16B16, GL_BGRA_EXT, GL_RGBA);
  350. GLType := GL_UNSIGNED_SHORT;
  351. GLInternal := GL_RGBA16;
  352. end;
  353. // Floating-Point formats
  354. ifR32F:
  355. begin
  356. GLFormat := GL_RED;
  357. GLType := GL_FLOAT;
  358. GLInternal := GL_LUMINANCE32F_ARB;
  359. end;
  360. ifA32R32G32B32F, ifA32B32G32R32F:
  361. begin
  362. GLFormat := Iff(Format = ifA32R32G32B32F, GL_BGRA_EXT, GL_RGBA);
  363. GLType := GL_FLOAT;
  364. GLInternal := GL_RGBA32F_ARB;
  365. end;
  366. ifR16F:
  367. begin
  368. GLFormat := GL_RED;
  369. GLType := GL_HALF_FLOAT_ARB;
  370. GLInternal := GL_LUMINANCE16F_ARB;
  371. end;
  372. ifA16R16G16B16F, ifA16B16G16R16F:
  373. begin
  374. GLFormat := Iff(Format = ifA16R16G16B16F, GL_BGRA_EXT, GL_RGBA);
  375. GLType := GL_HALF_FLOAT_ARB;
  376. GLInternal := GL_RGBA16F_ARB;
  377. end;
  378. // Special formats
  379. ifDXT1: GLInternal := GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
  380. ifDXT3: GLInternal := GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
  381. ifDXT5: GLInternal := GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
  382. end;
  383. Result := GLInternal <> 0;
  384. end;
  385. function LoadGLTextureFromFile(const FileName: string; CreatedWidth, CreatedHeight: PLongInt): GLuint;
  386. var
  387. Images: TDynImageDataArray;
  388. begin
  389. if LoadMultiImageFromFile(FileName, Images) and (Length(Images) > 0) then
  390. begin
  391. Result := CreateGLTextureFromMultiImage(Images, Images[0].Width,
  392. Images[0].Height, True, ifUnknown, CreatedWidth, CreatedHeight);
  393. end
  394. else
  395. Result := 0;
  396. FreeImagesInArray(Images);
  397. end;
  398. function LoadGLTextureFromStream(Stream: TStream; CreatedWidth, CreatedHeight: PLongInt): GLuint;
  399. var
  400. Images: TDynImageDataArray;
  401. begin
  402. if LoadMultiImageFromStream(Stream, Images) and (Length(Images) > 0) then
  403. begin
  404. Result := CreateGLTextureFromMultiImage(Images, Images[0].Width,
  405. Images[0].Height, True, ifUnknown, CreatedWidth, CreatedHeight);
  406. end
  407. else
  408. Result := 0;
  409. FreeImagesInArray(Images);
  410. end;
  411. function LoadGLTextureFromMemory(Data: Pointer; Size: LongInt; CreatedWidth, CreatedHeight: PLongInt): GLuint;
  412. var
  413. Images: TDynImageDataArray;
  414. begin
  415. if LoadMultiImageFromMemory(Data, Size, Images) and (Length(Images) > 0) then
  416. begin
  417. Result := CreateGLTextureFromMultiImage(Images, Images[0].Width,
  418. Images[0].Height, True, ifUnknown, CreatedWidth, CreatedHeight);
  419. end
  420. else
  421. Result := 0;
  422. FreeImagesInArray(Images);
  423. end;
  424. function CreateGLTextureFromImage(const Image: TImageData;
  425. Width, Height: LongInt; MipMaps: Boolean; OverrideFormat: TImageFormat;
  426. CreatedWidth, CreatedHeight: PLongInt): GLuint;
  427. var
  428. Arr: TDynImageDataArray;
  429. begin
  430. // Just calls function operating on image arrays
  431. SetLength(Arr, 1);
  432. Arr[0] := Image;
  433. Result := CreateGLTextureFromMultiImage(Arr, Width, Height, MipMaps,
  434. OverrideFormat, CreatedWidth, CreatedHeight);
  435. end;
  436. function CreateGLTextureFromMultiImage(const Images: TDynImageDataArray;
  437. Width, Height: LongInt; MipMaps: Boolean; OverrideFormat: TImageFormat;
  438. CreatedWidth, CreatedHeight: PLongInt): GLuint;
  439. var
  440. I, MipLevels, PossibleLevels, ExistingLevels, CurrentWidth, CurrentHeight: LongInt;
  441. Caps: TGLTextureCaps;
  442. GLFormat: GLenum;
  443. GLType: GLenum;
  444. GLInternal: GLint;
  445. Desired, ConvTo: TImageFormat;
  446. Info: TImageFormatInfo;
  447. LevelsArray: TDynImageDataArray;
  448. NeedsResize, NeedsConvert: Boolean;
  449. UnpackAlignment, UnpackSkipRows, UnpackSkipPixels, UnpackRowLength: LongInt;
  450. begin
  451. Result := 0;
  452. ExistingLevels := 0;
  453. if GetGLTextureCaps(Caps) and (Length(Images) > 0) then
  454. try
  455. // First check desired size and modify it if necessary
  456. if Width <= 0 then Width := Images[0].Width;
  457. if Height <= 0 then Height := Images[0].Height;
  458. if Caps.PowerOfTwo then
  459. begin
  460. // If device supports only power of 2 texture sizes
  461. Width := NextPow2(Width);
  462. Height := NextPow2(Height);
  463. end;
  464. Width := ClampInt(Width, 1, Caps.MaxTextureSize);
  465. Height := ClampInt(Height, 1, Caps.MaxTextureSize);
  466. // Get various mipmap level counts and modify
  467. // desired MipLevels if its value is invalid
  468. ExistingLevels := Length(Images);
  469. PossibleLevels := GetNumMipMapLevels(Width, Height);
  470. if MipMaps then
  471. MipLevels := PossibleLevels
  472. else
  473. MipLevels := 1;
  474. // Now determine which image format will be used
  475. if OverrideFormat = ifUnknown then
  476. Desired := Images[0].Format
  477. else
  478. Desired := OverrideFormat;
  479. // Check if the hardware supports floating point and compressed textures
  480. GetImageFormatInfo(Desired, Info);
  481. if Info.IsFloatingPoint and not Caps.FloatTextures then
  482. Desired := ifA8R8G8B8;
  483. if (Desired in [ifDXT1, ifDXT3, ifDXT5]) and not Caps.DXTCompression then
  484. Desired := ifA8R8G8B8;
  485. // Try to find GL format equivalent to image format and if it is not
  486. // found use one of default formats
  487. if not ImageFormatToGL(Desired, GLFormat, GLType, GLInternal) then
  488. begin
  489. GetImageFormatInfo(Desired, Info);
  490. if Info.HasGrayChannel then
  491. ConvTo := ifGray8
  492. else
  493. ConvTo := ifA8R8G8B8;
  494. if not ImageFormatToGL(ConvTo, GLFormat, GLType, GLInternal) then
  495. Exit;
  496. end
  497. else
  498. ConvTo := Desired;
  499. // Prepare array for mipmap levels
  500. SetLength(LevelsArray, MipLevels);
  501. CurrentWidth := Width;
  502. CurrentHeight := Height;
  503. // If user is interested in width and height of created texture lets
  504. // give him that
  505. if CreatedWidth <> nil then CreatedWidth^ := CurrentWidth;
  506. if CreatedHeight <> nil then CreatedHeight^ := CurrentHeight;
  507. // Store old pixel unpacking settings
  508. glGetIntegerv(GL_UNPACK_ALIGNMENT, @UnpackAlignment);
  509. glGetIntegerv(GL_UNPACK_SKIP_ROWS, @UnpackSkipRows);
  510. glGetIntegerv(GL_UNPACK_SKIP_PIXELS, @UnpackSkipPixels);
  511. glGetIntegerv(GL_UNPACK_ROW_LENGTH, @UnpackRowLength);
  512. // Set new pixel unpacking settings
  513. glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  514. glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
  515. glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
  516. glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
  517. // Generate new texture, bind it and set
  518. glGenTextures(1, @Result);
  519. glBindTexture(GL_TEXTURE_2D, Result);
  520. if Byte(glIsTexture(Result)) <> GL_TRUE then
  521. Exit;
  522. for I := 0 to MipLevels - 1 do
  523. begin
  524. // Check if we can use input image array as a source for this mipmap level
  525. if I < ExistingLevels then
  526. begin
  527. // Check if input image for this mipmap level has the right
  528. // size and format
  529. NeedsResize := not ((Images[I].Width = CurrentWidth) and (Images[I].Height = CurrentHeight));
  530. NeedsConvert := not (Images[I].Format = ConvTo);
  531. if NeedsResize or NeedsConvert then
  532. begin
  533. // Input image must be resized or converted to different format
  534. // to become valid mipmap level
  535. CloneImage(Images[I], LevelsArray[I]);
  536. if NeedsConvert then
  537. ConvertImage(LevelsArray[I], ConvTo);
  538. if NeedsResize then
  539. ResizeImage(LevelsArray[I], CurrentWidth, CurrentHeight, rfBilinear);
  540. end
  541. else
  542. // Input image can be used without any changes
  543. LevelsArray[I] := Images[I];
  544. end
  545. else
  546. begin
  547. // This mipmap level is not present in the input image array
  548. // so we create a new level
  549. FillMipMapLevel(LevelsArray[I - 1], CurrentWidth, CurrentHeight, LevelsArray[I]);
  550. end;
  551. if ConvTo in [ifDXT1, ifDXT3, ifDXT5] then
  552. begin
  553. // Note: GL DXTC texture snaller than 4x4 must have width and height
  554. // as expected for non-DXTC texture (like 1x1 - we cannot
  555. // use LevelsArray[I].Width and LevelsArray[I].Height - they are
  556. // at least 4 for DXTC images). But Bits and Size passed to
  557. // glCompressedTexImage2D must contain regular 4x4 DXTC block.
  558. glCompressedTexImage2D(GL_TEXTURE_2D, I, GLInternal, CurrentWidth,
  559. CurrentHeight, 0, LevelsArray[I].Size, LevelsArray[I].Bits)
  560. end
  561. else
  562. begin
  563. glTexImage2D(GL_TEXTURE_2D, I, GLInternal, LevelsArray[I].Width,
  564. LevelsArray[I].Height, 0, GLFormat, GLType, LevelsArray[I].Bits);
  565. end;
  566. // Calculate width and height of the next mipmap level
  567. CurrentWidth := ClampInt(CurrentWidth div 2, 1, CurrentWidth);
  568. CurrentHeight := ClampInt(CurrentHeight div 2, 1, CurrentHeight);
  569. end;
  570. // Restore old pixel unpacking settings
  571. glPixelStorei(GL_UNPACK_ALIGNMENT, UnpackAlignment);
  572. glPixelStorei(GL_UNPACK_SKIP_ROWS, UnpackSkipRows);
  573. glPixelStorei(GL_UNPACK_SKIP_PIXELS, UnpackSkipPixels);
  574. glPixelStorei(GL_UNPACK_ROW_LENGTH, UnpackRowLength);
  575. finally
  576. // Free local image copies
  577. for I := 0 to Length(LevelsArray) - 1 do
  578. begin
  579. if ((I < ExistingLevels) and (LevelsArray[I].Bits <> Images[I].Bits)) or
  580. (I >= ExistingLevels) then
  581. FreeImage(LevelsArray[I]);
  582. end;
  583. end;
  584. end;
  585. function SaveGLTextureToFile(const FileName: string; const Texture: GLuint): Boolean;
  586. var
  587. Arr: TDynImageDataArray;
  588. Fmt: TImageFileFormat;
  589. IsDDS: Boolean;
  590. begin
  591. Result := CreateMultiImageFromGLTexture(Texture, Arr);
  592. if Result then
  593. begin
  594. Fmt := FindImageFileFormatByName(FileName);
  595. if Fmt <> nil then
  596. begin
  597. IsDDS := SameText(Fmt.Extensions[0], 'dds');
  598. if IsDDS then
  599. begin
  600. PushOptions;
  601. SetOption(ImagingDDSSaveMipMapCount, Length(Arr));
  602. end;
  603. Result := SaveMultiImageToFile(FileName, Arr);
  604. if IsDDS then
  605. PopOptions;
  606. end;
  607. FreeImagesInArray(Arr);
  608. end;
  609. end;
  610. function SaveGLTextureToStream(const Ext: string; Stream: TStream; const Texture: GLuint): Boolean;
  611. var
  612. Arr: TDynImageDataArray;
  613. Fmt: TImageFileFormat;
  614. IsDDS: Boolean;
  615. begin
  616. Result := CreateMultiImageFromGLTexture(Texture, Arr);
  617. if Result then
  618. begin
  619. Fmt := FindImageFileFormatByExt(Ext);
  620. if Fmt <> nil then
  621. begin
  622. IsDDS := SameText(Fmt.Extensions[0], 'dds');
  623. if IsDDS then
  624. begin
  625. PushOptions;
  626. SetOption(ImagingDDSSaveMipMapCount, Length(Arr));
  627. end;
  628. Result := SaveMultiImageToStream(Ext, Stream, Arr);
  629. if IsDDS then
  630. PopOptions;
  631. end;
  632. FreeImagesInArray(Arr);
  633. end;
  634. end;
  635. function SaveGLTextureToMemory(const Ext: string; Data: Pointer; var Size: LongInt; const Texture: GLuint): Boolean;
  636. var
  637. Arr: TDynImageDataArray;
  638. Fmt: TImageFileFormat;
  639. IsDDS: Boolean;
  640. begin
  641. Result := CreateMultiImageFromGLTexture(Texture, Arr);
  642. if Result then
  643. begin
  644. Fmt := FindImageFileFormatByExt(Ext);
  645. if Fmt <> nil then
  646. begin
  647. IsDDS := SameText(Fmt.Extensions[0], 'dds');
  648. if IsDDS then
  649. begin
  650. PushOptions;
  651. SetOption(ImagingDDSSaveMipMapCount, Length(Arr));
  652. end;
  653. Result := SaveMultiImageToMemory(Ext, Data, Size, Arr);
  654. if IsDDS then
  655. PopOptions;
  656. end;
  657. FreeImagesInArray(Arr);
  658. end;
  659. end;
  660. function CreateImageFromGLTexture(const Texture: GLuint;
  661. var Image: TImageData; OverrideFormat: TImageFormat): Boolean;
  662. var
  663. Arr: TDynImageDataArray;
  664. begin
  665. // Just calls function operating on image arrays
  666. FreeImage(Image);
  667. SetLength(Arr, 1);
  668. Result := CreateMultiImageFromGLTexture(Texture, Arr, 1, OverrideFormat);
  669. Image := Arr[0];
  670. end;
  671. function CreateMultiImageFromGLTexture(const Texture: GLuint;
  672. var Images: TDynImageDataArray; MipLevels: LongInt; OverrideFormat: TImageFormat): Boolean;
  673. var
  674. I, Width, Height, ExistingLevels: LongInt;
  675. begin
  676. FreeImagesInArray(Images);
  677. SetLength(Images, 0);
  678. Result := False;
  679. if Byte(glIsTexture(Texture)) = GL_TRUE then
  680. begin
  681. // Check if desired mipmap level count is valid
  682. glBindTexture(GL_TEXTURE_2D, Texture);
  683. if MipLevels <= 0 then
  684. MipLevels := GetNumMipMapLevels(Width, Height);
  685. SetLength(Images, MipLevels);
  686. ExistingLevels := 0;
  687. for I := 0 to MipLevels - 1 do
  688. begin
  689. // Get the current level size
  690. glGetTexLevelParameteriv(GL_TEXTURE_2D, I, GL_TEXTURE_WIDTH, @Width);
  691. glGetTexLevelParameteriv(GL_TEXTURE_2D, I, GL_TEXTURE_HEIGHT, @Height);
  692. // Break when the mipmap chain is broken
  693. if (Width = 0) or (Height = 0) then
  694. Break;
  695. // Create new image and copy texture data
  696. NewImage(Width, Height, ifA8R8G8B8, Images[I]);
  697. glGetTexImage(GL_TEXTURE_2D, I, GL_BGRA_EXT, GL_UNSIGNED_BYTE, Images[I].Bits);
  698. Inc(ExistingLevels);
  699. end;
  700. // Resize mipmap array if necessary
  701. if MipLevels <> ExistingLevels then
  702. SetLength(Images, ExistingLevels);
  703. // Convert images to desired format if set
  704. if OverrideFormat <> ifUnknown then
  705. for I := 0 to Length(Images) - 1 do
  706. ConvertImage(Images[I], OverrideFormat);
  707. Result := True;
  708. end;
  709. end;
  710. initialization
  711. {
  712. File Notes:
  713. -- TODOS ----------------------------------------------------
  714. - use internal format of texture in CreateMultiImageFromGLTexture
  715. not only A8R8G8B8
  716. - support for cube and 3D maps
  717. -- 0.21 Changes/Bug Fixes -----------------------------------
  718. - Added CreatedWidth and CreatedHeight parameters to most
  719. LoadGLTextureFromXXX/CreateGLTextureFromXXX functions.
  720. -- 0.19 Changes/Bug Fixes -----------------------------------
  721. - fixed bug in CreateGLTextureFromMultiImage which caused assert failure
  722. when creating mipmaps (using FillMipMapLevel) for DXTC formats
  723. - changed single channel floating point texture formats from
  724. GL_INTENSITY..._ARB to GL_LUMINANCE..._ARB
  725. - added support for half float texture formats (GL_RGBA16F_ARB etc.)
  726. -- 0.17 Changes/Bug Fixes -----------------------------------
  727. - filtered mipmap creation
  728. - more texture caps added
  729. - fixed memory leaks in SaveGLTextureTo... functions
  730. -- 0.15 Changes/Bug Fixes -----------------------------------
  731. - unit created and initial stuff added
  732. }
  733. end.