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