ImagingOpenGL.pas 29 KB

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