ImagingOpenGL.pas 36 KB

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