ImagingOpenGL.pas 30 KB

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