ImagingOpenGL.pas 37 KB

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