ImagingOpenGL.pas 36 KB

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