ImagingOpenGL.pas 33 KB

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