Statistics
| Branch: | Tag: | Revision:

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.