libktx Reference 4.3.2
Libraries and tools to create and read KTX image texture files.
Loading...
Searching...
No Matches
glloader.c

This is an example of using the low-level ktxTexture API to create and load an OpenGL texture. It is a fragment of the code used by ktxTexture1_GLUpload and ktxTexture2_GLUpload.

#include <ktx.h>
Declares the public functions and structures of the KTX API.

This structure is used to pass to a callback function data that is uniform across all images.

typedef struct ktx_cbdata {
GLenum glTarget;
GLenum glFormat;
GLenum glInternalformat;
GLenum glType;
GLenum glError;
GLuint numLayers;
} ktx_cbdata;

One of these callbacks, selected by ktxTexture1_GLUpload or ktxTexture2_GLUpload based on the dimensionality and arrayness of the texture, is called from ktxTexture_IterateLevelFaces to upload the texture data to OpenGL.

texImage1DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(faceLodSize);
UNUSED(depth);
UNUSED(height);
assert(gl.glTexImage1D != NULL);
gl.glTexImage1D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width, 0,
cbData->glFormat, cbData->glType, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
compressedTexImage1DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(depth);
UNUSED(height);
if (faceLodSize > UINT32_MAX)
return KTX_INVALID_OPERATION; // Too big for OpenGL {,ES}.
assert(gl.glCompressedTexImage1D != NULL);
gl.glCompressedTexImage1D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width, 0,
(ktx_uint32_t)faceLodSize, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
texImage2DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(depth);
UNUSED(faceLodSize);
glTexImage2D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width,
cbData->numLayers == 0 ? (GLuint)height : cbData->numLayers, 0,
cbData->glFormat, cbData->glType, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
compressedTexImage2DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
GLenum glerror;
UNUSED(depth);
if (faceLodSize > UINT32_MAX)
return KTX_INVALID_OPERATION; // Too big for OpenGL {,ES}.
// It is simpler to just attempt to load the format, rather than divine
// which formats are supported by the implementation. In the event of an
// error, software unpacking can be attempted.
glCompressedTexImage2D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width,
cbData->numLayers == 0 ? (GLuint)height : cbData->numLayers,
0,
(ktx_uint32_t)faceLodSize, pixels);
glerror = glGetError();
#if SUPPORT_SOFTWARE_ETC_UNPACK
// Renderion is returning INVALID_VALUE. Oops!!
if ((glerror == GL_INVALID_ENUM || glerror == GL_INVALID_VALUE)
&& (cbData->glInternalformat == GL_ETC1_RGB8_OES
|| (cbData->glInternalformat >= GL_COMPRESSED_R11_EAC
&& cbData->glInternalformat <= GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
))
{
GLubyte* unpacked;
GLenum format, internalformat, type;
result = _ktxUnpackETC((GLubyte*)pixels, cbData->glInternalformat,
width, height, &unpacked,
&format, &internalformat,
&type, R16Formats, supportsSRGB);
if (result != KTX_SUCCESS) {
return result;
}
if (!(sizedFormats & _NON_LEGACY_FORMATS)) {
if (internalformat == GL_RGB8)
internalformat = GL_RGB;
else if (internalformat == GL_RGBA8)
internalformat = GL_RGBA;
}
glTexImage2D(cbData->glTarget + face, miplevel,
internalformat, width,
cbData->numLayers == 0 ? (GLuint)height : cbData->numLayers, 0,
format, type, unpacked);
free(unpacked);
glerror = glGetError();
}
#endif
if ((cbData->glError = glerror) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
texImage3DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(faceLodSize);
assert(gl.glTexImage3D != NULL);
gl.glTexImage3D(cbData->glTarget + face, miplevel,
cbData->glInternalformat,
width, height,
cbData->numLayers == 0 ? (GLuint)depth : cbData->numLayers,
0,
cbData->glFormat, cbData->glType, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
compressedTexImage3DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
if (faceLodSize > UINT32_MAX)
return KTX_INVALID_OPERATION; // Too big for OpenGL {,ES}.
assert(gl.glCompressedTexImage3D != NULL);
gl.glCompressedTexImage3D(cbData->glTarget + face, miplevel,
cbData->glInternalformat,
width, height,
cbData->numLayers == 0 ? (GLuint)depth : cbData->numLayers,
0,
(ktx_uint32_t)faceLodSize, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
@ KTX_GL_ERROR
Definition: ktx.h:179
@ KTX_INVALID_OPERATION
Definition: ktx.h:180
@ KTX_SUCCESS
Definition: ktx.h:170
#define KTX_error_code
For backward compatibility.
Definition: ktx.h:198

This function creates the GL texture object and sets up the callbacks to load the image data into it.

ktxTexture_GLUploadPrivate(ktxTexture* This, ktx_glformatinfo* formatInfo,
GLuint* pTexture, GLenum* pTarget, GLenum* pGlerror)
{
GLuint texname;
GLenum target = GL_TEXTURE_2D;
int texnameUser;
ktx_cbdata cbData;
PFNKTXITERCB iterCb = NULL;
int dimensions;
if (pGlerror)
*pGlerror = GL_NO_ERROR;
assert(This && pTarget);
if (contextProfile == 0)
discoverContextCapabilities();
texnameUser = pTexture && *pTexture;
if (texnameUser) {
texname = *pTexture;
} else {
glGenTextures(1, &texname);
}
cbData.glFormat = formatInfo->glFormat;
cbData.glInternalformat = formatInfo->glInternalformat;
cbData.glType = formatInfo->glType;
dimensions = This->numDimensions;
if (This->isArray) {
dimensions += 1;
if (This->numFaces == 6) {
/* ktxCheckHeader1_ should have caught this. */
assert(This->numDimensions == 2);
target = GL_TEXTURE_CUBE_MAP_ARRAY;
} else {
switch (This->numDimensions) {
case 1: target = GL_TEXTURE_1D_ARRAY; break;
case 2: target = GL_TEXTURE_2D_ARRAY; break;
/* _ktxCheckHeader should have caught this. */
default: assert(KTX_TRUE);
}
}
cbData.numLayers = This->numLayers;
} else {
if (This->numFaces == 6) {
/* ktxCheckHeader1_ should have caught this. */
assert(This->numDimensions == 2);
target = GL_TEXTURE_CUBE_MAP;
} else {
switch (This->numDimensions) {
case 1: target = GL_TEXTURE_1D; break;
case 2: target = GL_TEXTURE_2D; break;
case 3: target = GL_TEXTURE_3D; break;
/* _ktxCheckHeader shold have caught this. */
default: assert(KTX_TRUE);
}
}
cbData.numLayers = 0;
}
if (target == GL_TEXTURE_1D &&
((This->isCompressed && (gl.glCompressedTexImage1D == NULL)) ||
(!This->isCompressed && (gl.glTexImage1D == NULL))))
{
}
/* Reject 3D texture if unsupported. */
if (target == GL_TEXTURE_3D &&
((This->isCompressed && (gl.glCompressedTexImage3D == NULL)) ||
(!This->isCompressed && (gl.glTexImage3D == NULL))))
{
}
/* Reject cube map arrays if not supported. */
if (target == GL_TEXTURE_CUBE_MAP_ARRAY && !supportsCubeMapArrays) {
}
/* XXX Need to reject other array textures & cube maps if not supported. */
switch (dimensions) {
case 1:
iterCb = This->isCompressed
? compressedTexImage1DCallback : texImage1DCallback;
break;
case 2:
iterCb = This->isCompressed
? compressedTexImage2DCallback : texImage2DCallback;
break;
case 3:
iterCb = This->isCompressed
? compressedTexImage3DCallback : texImage3DCallback;
break;
default:
assert(KTX_TRUE);
}
glBindTexture(target, texname);
// Prefer glGenerateMipmaps over GL_GENERATE_MIPMAP
if (This->generateMipmaps && (gl.glGenerateMipmap == NULL)) {
glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
}
if (!This->generateMipmaps && supportsMaxLevel)
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, This->numLevels - 1);
if (target == GL_TEXTURE_CUBE_MAP) {
cbData.glTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
} else {
cbData.glTarget = target;
}
if (!This->isCompressed) {
#if SUPPORT_LEGACY_FORMAT_CONVERSION
// If sized legacy formats are supported there is no need to convert.
// If only unsized formats are supported, there is no point in
// converting as the modern formats aren't supported either.
if (sizedFormats == _NON_LEGACY_FORMATS && supportsSwizzle) {
convertFormat(target, &cbData.glFormat, &cbData.glInternalformat);
} else if (sizedFormats == _NO_SIZED_FORMATS)
cbData.glInternalformat = formatInfo->glBaseInternalformat;
#else
// When no sized formats are supported, or legacy sized formats are not
// supported, must change internal format.
if (sizedFormats == _NO_SIZED_FORMATS
|| (!(sizedFormats & _LEGACY_FORMATS) &&
(formatInfo->glBaseInternalformat == GL_ALPHA
|| formatInfo->glBaseInternalformat == GL_LUMINANCE
|| formatInfo->glBaseInternalformat == GL_LUMINANCE_ALPHA
|| formatInfo->glBaseInternalformat == GL_INTENSITY))) {
cbData.glInternalformat = formatInfo->glBaseInternalformat;
}
#endif
}
if (ktxTexture_isActiveStream(ktxTexture(This)))
result = ktxTexture_IterateLoadLevelFaces(This, iterCb, &cbData);
else
result = ktxTexture_IterateLevelFaces(This, iterCb, &cbData);
/* GL errors are the only reason for failure. */
if (result != KTX_SUCCESS && cbData.glError != GL_NO_ERROR) {
if (pGlerror)
*pGlerror = cbData.glError;
}
if (result == KTX_SUCCESS)
{
// Prefer glGenerateMipmaps over GL_GENERATE_MIPMAP
if (This->generateMipmaps && gl.glGenerateMipmap) {
gl.glGenerateMipmap(target);
}
*pTarget = target;
if (pTexture) {
*pTexture = texname;
}
} else if (!texnameUser) {
glDeleteTextures(1, &texname);
}
return result;
}
@ KTX_UNSUPPORTED_TEXTURE_TYPE
Definition: ktx.h:186
#define ktxTexture(t)
Helper for casting ktxTexture1 and ktxTexture2 to ktxTexture.
Definition: ktx.h:716
#define ktxTexture_IterateLoadLevelFaces(This, iterCb, userdata)
Helper for calling the IterateLoadLevelFaces virtual method of a ktxTexture.
Definition: ktx.h:574
Base class representing a texture.
Definition: ktx.h:287
ktx_bool_t generateMipmaps
Definition: ktx.h:288
ktx_uint32_t numLevels
Number of mip levels in the texture.
Definition: ktx.h:288
ktx_uint32_t numFaces
Number of faces: 6 for cube maps, 1 otherwise.
Definition: ktx.h:288
ktx_uint32_t numDimensions
Number of dimensions in the texture: 1, 2 or 3.
Definition: ktx.h:288
ktx_bool_t isArray
Definition: ktx.h:288
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2010-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/*
* @internal
* @file
* @~English
*
* @brief Functions for instantiating GL or GLES textures from KTX files.
*
* @author Georg Kolling, Imagination Technology
* @author Mark Callow, HI Corporation & Edgewise Consulting
*/
#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "gl_funcs.h"
#include "ktx.h"
#include "ktxint.h"
#include "texture.h"
#include "vk2gl.h"
#include "unused.h"
/*
* These are defined only in compatibility mode (gl.h) not glcorearb.h
*/
#if !defined( GL_LUMINANCE )
#define GL_LUMINANCE 0x1909 // deprecated
#endif
#if !defined( GL_LUMINANCE_ALPHA )
#define GL_LUMINANCE_ALPHA 0x190A // deprecated
#endif
#if !defined( GL_INTENSITY )
#define GL_INTENSITY 0x8049 // deprecated
#endif
/*
* N.B. As of Doxygen 1.9.6 non-class members must use fully qualified
* names with @ref and @copy* references to classes. This means prefixing
* a reference with the name of the (pseudo-)class of which it is a member.
* We use @memberof to improve the index and toc for the doc for our
* pseudo classes so we need to prefix. Since we don't want, e.g.,
* ktxTexture1::ktxTexture1_GLUpload appearing in the documentation we have
* to explicitly provide the link text making references very long-winded.
* Sigh!
*/
#define GL_GENERATE_MIPMAP 0x8191
#define _CONTEXT_ES_PROFILE_BIT 0x4
#define _NON_LEGACY_FORMATS 0x1 /*< @internal Non-legacy sized formats are supported. */
#define _LEGACY_FORMATS 0x2 /*< @internal Legacy sized formats are supported. */
#define _ALL_SIZED_FORMATS (_NON_LEGACY_FORMATS | _LEGACY_FORMATS)
#define _NO_SIZED_FORMATS 0 /*< @internal No sized formats are supported. */
static GLint contextProfile = 0;
static GLint sizedFormats = _ALL_SIZED_FORMATS;
static GLboolean supportsSwizzle = GL_TRUE;
static GLint R16Formats = _KTX_ALL_R16_FORMATS;
static GLboolean supportsSRGB = GL_TRUE;
static GLboolean supportsCubeMapArrays = GL_FALSE;
static GLboolean supportsMaxLevel = GL_FALSE;
static GLboolean
hasExtension(const char* extension)
{
if (gl.glGetStringi == NULL) {
if (strstr(glGetString(GL_EXTENSIONS), extension) != NULL) {
return GL_TRUE;
} else {
return GL_FALSE;
}
} else {
int i, n;
glGetIntegerv(GL_NUM_EXTENSIONS, &n);
for (i = 0; i < n; i++) {
if (strcmp((const char*)gl.glGetStringi(GL_EXTENSIONS, i), extension) == 0)
return GL_TRUE;
}
return GL_FALSE;
}
}
static void
discoverContextCapabilities(void)
{
GLint majorVersion = 1;
GLint minorVersion = 0;
if (strstr(glGetString(GL_VERSION), "GL ES") != NULL)
contextProfile = _CONTEXT_ES_PROFILE_BIT;
// MAJOR & MINOR only introduced in GL {,ES} 3.0
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
if (glGetError() != GL_NO_ERROR) {
// < v3.0; resort to the old-fashioned way.
if (contextProfile & _CONTEXT_ES_PROFILE_BIT)
sscanf(glGetString(GL_VERSION), "OpenGL ES %d.%d ",
&majorVersion, &minorVersion);
else
sscanf(glGetString(GL_VERSION), "OpenGL %d.%d ",
&majorVersion, &minorVersion);
}
if (contextProfile & _CONTEXT_ES_PROFILE_BIT) {
if (majorVersion < 3) {
supportsSwizzle = GL_FALSE;
sizedFormats = _NO_SIZED_FORMATS;
R16Formats = _KTX_NO_R16_FORMATS;
supportsSRGB = GL_FALSE;
// These things could be found by dlsym when, e.g. the same driver
// supports ES1, ES2 and ES3. For all but Tex*3D, there's no
// corresponding extension whose presence we could check. Just zero
// the pointers to prevent use.
gl.glGetStringi = NULL;
gl.glCompressedTexImage1D = NULL;
gl.glTexStorage1D = NULL;
gl.glTexStorage2D = NULL;
gl.glTexStorage3D = NULL;
if (!hasExtension("GL_OES_texture_3D")) {
gl.glCompressedTexImage3D = NULL;
gl.glCompressedTexSubImage3D = NULL;
gl.glTexImage3D = NULL;
gl.glTexSubImage3D = NULL;
}
if (majorVersion < 2)
gl.glGenerateMipmap = NULL;
} else {
sizedFormats = _NON_LEGACY_FORMATS;
if (hasExtension("GL_EXT_texture_cube_map_array")) {
supportsCubeMapArrays = GL_TRUE;
}
supportsMaxLevel = GL_TRUE;
}
if (hasExtension("GL_OES_required_internalformat")) {
sizedFormats |= _ALL_SIZED_FORMATS;
}
// There are no OES extensions for sRGB textures or R16 formats.
} else {
// PROFILE_MASK was introduced in OpenGL 3.2.
// Profiles: CONTEXT_CORE_PROFILE_BIT 0x1,
// CONTEXT_COMPATIBILITY_PROFILE_BIT 0x2.
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &contextProfile);
if (glGetError() == GL_NO_ERROR) {
// >= 3.2
if (majorVersion == 3 && minorVersion < 3)
supportsSwizzle = GL_FALSE;
if ((contextProfile & GL_CONTEXT_CORE_PROFILE_BIT))
sizedFormats &= ~_LEGACY_FORMATS;
if (majorVersion >= 4)
supportsCubeMapArrays = GL_TRUE;
supportsMaxLevel = GL_TRUE;
} else {
// < 3.2
contextProfile = GL_CONTEXT_COMPATIBILITY_PROFILE_BIT;
supportsSwizzle = GL_FALSE;
// sRGB textures introduced in 2.0
if (majorVersion < 2 && !hasExtension("GL_EXT_texture_sRGB")) {
supportsSRGB = GL_FALSE;
}
// R{,G]16 introduced in 3.0; R{,G}16_SNORM introduced in 3.1.
if (majorVersion == 3) {
if (minorVersion == 0)
R16Formats &= ~_KTX_R16_FORMATS_SNORM;
if (minorVersion < 1) {
if (hasExtension("GL_ARB_texture_query_levels"))
supportsMaxLevel = GL_TRUE;
} else {
supportsMaxLevel = GL_TRUE;
}
} else if (hasExtension("GL_ARB_texture_rg")) {
R16Formats &= ~_KTX_R16_FORMATS_SNORM;
} else {
R16Formats = _KTX_NO_R16_FORMATS;
}
}
if (!supportsCubeMapArrays) {
if (hasExtension("GL_ARB_texture_cube_map_array")) {
supportsCubeMapArrays = GL_TRUE;
}
}
}
}
#if SUPPORT_LEGACY_FORMAT_CONVERSION
static void convertFormat(GLenum target, GLenum* pFormat, GLenum* pInternalformat) {
switch (*pFormat) {
case GL_ALPHA:
{
GLint swizzle[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED};
*pFormat = GL_RED;
glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
switch (*pInternalformat) {
case GL_ALPHA:
case GL_ALPHA4:
case GL_ALPHA8:
*pInternalformat = GL_R8;
break;
case GL_ALPHA12:
case GL_ALPHA16:
*pInternalformat = GL_R16;
break;
}
}
case GL_LUMINANCE:
{
GLint swizzle[] = {GL_RED, GL_RED, GL_RED, GL_ONE};
*pFormat = GL_RED;
glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
switch (*pInternalformat) {
case GL_LUMINANCE:
case GL_LUMINANCE4:
case GL_LUMINANCE8:
*pInternalformat = GL_R8;
break;
case GL_LUMINANCE12:
case GL_LUMINANCE16:
*pInternalformat = GL_R16;
break;
#if 0
// XXX Must avoid setting TEXTURE_SWIZZLE in these cases
// XXX Must manually swizzle.
case GL_SLUMINANCE:
case GL_SLUMINANCE8:
*pInternalformat = GL_SRGB8;
break;
#endif
}
break;
}
case GL_LUMINANCE_ALPHA:
{
GLint swizzle[] = {GL_RED, GL_RED, GL_RED, GL_GREEN};
*pFormat = GL_RG;
glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
switch (*pInternalformat) {
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE4_ALPHA4:
case GL_LUMINANCE6_ALPHA2:
case GL_LUMINANCE8_ALPHA8:
*pInternalformat = GL_RG8;
break;
case GL_LUMINANCE12_ALPHA4:
case GL_LUMINANCE12_ALPHA12:
case GL_LUMINANCE16_ALPHA16:
*pInternalformat = GL_RG16;
break;
#if 0
// XXX Must avoid setting TEXTURE_SWIZZLE in these cases
// XXX Must manually swizzle.
case GL_SLUMINANCE_ALPHA:
case GL_SLUMINANCE8_ALPHA8:
*pInternalformat = GL_SRGB8_ALPHA8;
break;
#endif
}
break;
}
case GL_INTENSITY:
{
GLint swizzle[] = {GL_RED, GL_RED, GL_RED, GL_RED};
*pFormat = GL_RED;
glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle);
switch (*pInternalformat) {
case GL_INTENSITY:
case GL_INTENSITY4:
case GL_INTENSITY8:
*pInternalformat = GL_R8;
break;
case GL_INTENSITY12:
case GL_INTENSITY16:
*pInternalformat = GL_R16;
break;
}
break;
}
default:
break;
}
}
#endif /* SUPPORT_LEGACY_FORMAT_CONVERSION */
typedef struct ktx_glformatinfo {
ktx_uint32_t glFormat;
ktx_uint32_t glInternalformat;
ktx_uint32_t glBaseInternalformat;
ktx_uint32_t glType;
} ktx_glformatinfo;
/* [cbdata] */
typedef struct ktx_cbdata {
GLenum glTarget;
GLenum glFormat;
GLenum glInternalformat;
GLenum glType;
GLenum glError;
GLuint numLayers;
} ktx_cbdata;
/* [cbdata] */
/* [imageCallbacks] */
texImage1DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(faceLodSize);
UNUSED(depth);
UNUSED(height);
assert(gl.glTexImage1D != NULL);
gl.glTexImage1D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width, 0,
cbData->glFormat, cbData->glType, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
compressedTexImage1DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(depth);
UNUSED(height);
if (faceLodSize > UINT32_MAX)
return KTX_INVALID_OPERATION; // Too big for OpenGL {,ES}.
assert(gl.glCompressedTexImage1D != NULL);
gl.glCompressedTexImage1D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width, 0,
(ktx_uint32_t)faceLodSize, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
texImage2DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(depth);
UNUSED(faceLodSize);
glTexImage2D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width,
cbData->numLayers == 0 ? (GLuint)height : cbData->numLayers, 0,
cbData->glFormat, cbData->glType, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
compressedTexImage2DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
GLenum glerror;
UNUSED(depth);
if (faceLodSize > UINT32_MAX)
return KTX_INVALID_OPERATION; // Too big for OpenGL {,ES}.
// It is simpler to just attempt to load the format, rather than divine
// which formats are supported by the implementation. In the event of an
// error, software unpacking can be attempted.
glCompressedTexImage2D(cbData->glTarget + face, miplevel,
cbData->glInternalformat, width,
cbData->numLayers == 0 ? (GLuint)height : cbData->numLayers,
0,
(ktx_uint32_t)faceLodSize, pixels);
glerror = glGetError();
#if SUPPORT_SOFTWARE_ETC_UNPACK
// Renderion is returning INVALID_VALUE. Oops!!
if ((glerror == GL_INVALID_ENUM || glerror == GL_INVALID_VALUE)
&& (cbData->glInternalformat == GL_ETC1_RGB8_OES
|| (cbData->glInternalformat >= GL_COMPRESSED_R11_EAC
&& cbData->glInternalformat <= GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
))
{
GLubyte* unpacked;
GLenum format, internalformat, type;
result = _ktxUnpackETC((GLubyte*)pixels, cbData->glInternalformat,
width, height, &unpacked,
&format, &internalformat,
&type, R16Formats, supportsSRGB);
if (result != KTX_SUCCESS) {
return result;
}
if (!(sizedFormats & _NON_LEGACY_FORMATS)) {
if (internalformat == GL_RGB8)
internalformat = GL_RGB;
else if (internalformat == GL_RGBA8)
internalformat = GL_RGBA;
}
glTexImage2D(cbData->glTarget + face, miplevel,
internalformat, width,
cbData->numLayers == 0 ? (GLuint)height : cbData->numLayers, 0,
format, type, unpacked);
free(unpacked);
glerror = glGetError();
}
#endif
if ((cbData->glError = glerror) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
texImage3DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
UNUSED(faceLodSize);
assert(gl.glTexImage3D != NULL);
gl.glTexImage3D(cbData->glTarget + face, miplevel,
cbData->glInternalformat,
width, height,
cbData->numLayers == 0 ? (GLuint)depth : cbData->numLayers,
0,
cbData->glFormat, cbData->glType, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
compressedTexImage3DCallback(int miplevel, int face,
int width, int height,
int depth,
ktx_uint64_t faceLodSize,
void* pixels, void* userdata)
{
ktx_cbdata* cbData = (ktx_cbdata*)userdata;
if (faceLodSize > UINT32_MAX)
return KTX_INVALID_OPERATION; // Too big for OpenGL {,ES}.
assert(gl.glCompressedTexImage3D != NULL);
gl.glCompressedTexImage3D(cbData->glTarget + face, miplevel,
cbData->glInternalformat,
width, height,
cbData->numLayers == 0 ? (GLuint)depth : cbData->numLayers,
0,
(ktx_uint32_t)faceLodSize, pixels);
if ((cbData->glError = glGetError()) == GL_NO_ERROR) {
return KTX_SUCCESS;
} else {
return KTX_GL_ERROR;
}
}
/* [imageCallbacks] */
/* [loadGLTexture] */
ktxTexture_GLUploadPrivate(ktxTexture* This, ktx_glformatinfo* formatInfo,
GLuint* pTexture, GLenum* pTarget, GLenum* pGlerror)
{
GLuint texname;
GLenum target = GL_TEXTURE_2D;
int texnameUser;
ktx_cbdata cbData;
PFNKTXITERCB iterCb = NULL;
int dimensions;
if (pGlerror)
*pGlerror = GL_NO_ERROR;
assert(This && pTarget);
if (contextProfile == 0)
discoverContextCapabilities();
texnameUser = pTexture && *pTexture;
if (texnameUser) {
texname = *pTexture;
} else {
glGenTextures(1, &texname);
}
cbData.glFormat = formatInfo->glFormat;
cbData.glInternalformat = formatInfo->glInternalformat;
cbData.glType = formatInfo->glType;
dimensions = This->numDimensions;
if (This->isArray) {
dimensions += 1;
if (This->numFaces == 6) {
/* ktxCheckHeader1_ should have caught this. */
assert(This->numDimensions == 2);
target = GL_TEXTURE_CUBE_MAP_ARRAY;
} else {
switch (This->numDimensions) {
case 1: target = GL_TEXTURE_1D_ARRAY; break;
case 2: target = GL_TEXTURE_2D_ARRAY; break;
/* _ktxCheckHeader should have caught this. */
default: assert(KTX_TRUE);
}
}
cbData.numLayers = This->numLayers;
} else {
if (This->numFaces == 6) {
/* ktxCheckHeader1_ should have caught this. */
assert(This->numDimensions == 2);
target = GL_TEXTURE_CUBE_MAP;
} else {
switch (This->numDimensions) {
case 1: target = GL_TEXTURE_1D; break;
case 2: target = GL_TEXTURE_2D; break;
case 3: target = GL_TEXTURE_3D; break;
/* _ktxCheckHeader shold have caught this. */
default: assert(KTX_TRUE);
}
}
cbData.numLayers = 0;
}
if (target == GL_TEXTURE_1D &&
((This->isCompressed && (gl.glCompressedTexImage1D == NULL)) ||
(!This->isCompressed && (gl.glTexImage1D == NULL))))
{
}
/* Reject 3D texture if unsupported. */
if (target == GL_TEXTURE_3D &&
((This->isCompressed && (gl.glCompressedTexImage3D == NULL)) ||
(!This->isCompressed && (gl.glTexImage3D == NULL))))
{
}
/* Reject cube map arrays if not supported. */
if (target == GL_TEXTURE_CUBE_MAP_ARRAY && !supportsCubeMapArrays) {
}
/* XXX Need to reject other array textures & cube maps if not supported. */
switch (dimensions) {
case 1:
iterCb = This->isCompressed
? compressedTexImage1DCallback : texImage1DCallback;
break;
case 2:
iterCb = This->isCompressed
? compressedTexImage2DCallback : texImage2DCallback;
break;
case 3:
iterCb = This->isCompressed
? compressedTexImage3DCallback : texImage3DCallback;
break;
default:
assert(KTX_TRUE);
}
glBindTexture(target, texname);
// Prefer glGenerateMipmaps over GL_GENERATE_MIPMAP
if (This->generateMipmaps && (gl.glGenerateMipmap == NULL)) {
glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE);
}
if (!This->generateMipmaps && supportsMaxLevel)
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, This->numLevels - 1);
if (target == GL_TEXTURE_CUBE_MAP) {
cbData.glTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X;
} else {
cbData.glTarget = target;
}
if (!This->isCompressed) {
#if SUPPORT_LEGACY_FORMAT_CONVERSION
// If sized legacy formats are supported there is no need to convert.
// If only unsized formats are supported, there is no point in
// converting as the modern formats aren't supported either.
if (sizedFormats == _NON_LEGACY_FORMATS && supportsSwizzle) {
convertFormat(target, &cbData.glFormat, &cbData.glInternalformat);
} else if (sizedFormats == _NO_SIZED_FORMATS)
cbData.glInternalformat = formatInfo->glBaseInternalformat;
#else
// When no sized formats are supported, or legacy sized formats are not
// supported, must change internal format.
if (sizedFormats == _NO_SIZED_FORMATS
|| (!(sizedFormats & _LEGACY_FORMATS) &&
(formatInfo->glBaseInternalformat == GL_ALPHA
|| formatInfo->glBaseInternalformat == GL_LUMINANCE
|| formatInfo->glBaseInternalformat == GL_LUMINANCE_ALPHA
|| formatInfo->glBaseInternalformat == GL_INTENSITY))) {
cbData.glInternalformat = formatInfo->glBaseInternalformat;
}
#endif
}
if (ktxTexture_isActiveStream(ktxTexture(This)))
result = ktxTexture_IterateLoadLevelFaces(This, iterCb, &cbData);
else
result = ktxTexture_IterateLevelFaces(This, iterCb, &cbData);
/* GL errors are the only reason for failure. */
if (result != KTX_SUCCESS && cbData.glError != GL_NO_ERROR) {
if (pGlerror)
*pGlerror = cbData.glError;
}
if (result == KTX_SUCCESS)
{
// Prefer glGenerateMipmaps over GL_GENERATE_MIPMAP
if (This->generateMipmaps && gl.glGenerateMipmap) {
gl.glGenerateMipmap(target);
}
*pTarget = target;
if (pTexture) {
*pTexture = texname;
}
} else if (!texnameUser) {
glDeleteTextures(1, &texname);
}
return result;
}
/* [loadGLTexture] */
ktxTexture1_GLUpload(ktxTexture1* This, GLuint* pTexture, GLenum* pTarget,
GLenum* pGlerror)
{
GLint previousUnpackAlignment;
ktx_glformatinfo formatInfo;
if (!This) {
}
if (!pTarget) {
}
if (!ktxOpenGLModuleHandle) {
result = ktxLoadOpenGLLibrary();
if (result != KTX_SUCCESS) {
return result;
}
}
/* KTX 1 files require an unpack alignment of 4 */
glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousUnpackAlignment);
if (previousUnpackAlignment != KTX_GL_UNPACK_ALIGNMENT) {
glPixelStorei(GL_UNPACK_ALIGNMENT, KTX_GL_UNPACK_ALIGNMENT);
}
formatInfo.glFormat = This->glFormat;
formatInfo.glInternalformat = This->glInternalformat;
formatInfo.glBaseInternalformat = This->glBaseInternalformat;
formatInfo.glType = This->glType;
result = ktxTexture_GLUploadPrivate(ktxTexture(This), &formatInfo,
pTexture, pTarget, pGlerror);
/* restore previous GL state */
if (previousUnpackAlignment != KTX_GL_UNPACK_ALIGNMENT) {
glPixelStorei(GL_UNPACK_ALIGNMENT, previousUnpackAlignment);
}
return result;
}
ktxTexture2_GLUpload(ktxTexture2* This, GLuint* pTexture, GLenum* pTarget,
GLenum* pGlerror)
{
GLint previousUnpackAlignment;
ktx_glformatinfo formatInfo;
if (!This) {
}
if (!pTarget) {
}
if (!ktxOpenGLModuleHandle) {
result = ktxLoadOpenGLLibrary();
if (result != KTX_SUCCESS) {
return result;
}
}
if (This->vkFormat != VK_FORMAT_UNDEFINED) {
formatInfo.glInternalformat =
vkFormat2glInternalFormat(This->vkFormat);
if (formatInfo.glInternalformat == GL_INVALID_VALUE) {
// TODO Check for mapping metadata. If none
}
} else {
// TODO: Check DFD for ASTC HDR or 3D or RGB[DEM] and figure out format.
return KTX_INVALID_OPERATION; // BasisU textures must be transcoded
// before upload.
}
if (This->isCompressed) {
/* Unused. */
formatInfo.glFormat = GL_INVALID_VALUE;
formatInfo.glType = GL_INVALID_VALUE;
formatInfo.glBaseInternalformat = GL_INVALID_VALUE;
} else {
formatInfo.glFormat = vkFormat2glFormat(This->vkFormat);
formatInfo.glType = vkFormat2glType(This->vkFormat);
formatInfo.glBaseInternalformat = formatInfo.glInternalformat;
if (formatInfo.glFormat == GL_INVALID_VALUE || formatInfo.glType == GL_INVALID_VALUE)
}
/* KTX 2 files require an unpack alignment of 1. OGL default is 4. */
glGetIntegerv(GL_UNPACK_ALIGNMENT, &previousUnpackAlignment);
if (previousUnpackAlignment != 1) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
result = ktxTexture_GLUploadPrivate(ktxTexture(This), &formatInfo,
pTexture, pTarget, pGlerror);
/* restore previous GL state */
if (previousUnpackAlignment != 1) {
glPixelStorei(GL_UNPACK_ALIGNMENT, previousUnpackAlignment);
}
return result;
}
ktxTexture_GLUpload(ktxTexture* This, GLuint* pTexture, GLenum* pTarget,
GLenum* pGlerror)
{
if (This->classId == ktxTexture2_c)
return ktxTexture2_GLUpload((ktxTexture2*)This, pTexture, pTarget,
pGlerror);
else
return ktxTexture1_GLUpload((ktxTexture1*)This, pTexture, pTarget,
pGlerror);
}
ktx_error_code_e
Error codes returned by library functions.
Definition: ktx.h:169
@ KTX_INVALID_VALUE
Definition: ktx.h:181
#define KTX_GL_UNPACK_ALIGNMENT
Required unpack alignment.
Definition: ktx.h:159
Class representing a KTX version 1 format texture.
Definition: ktx.h:656
ktx_uint32_t glFormat
Definition: ktx.h:658
ktx_uint32_t glInternalformat
Definition: ktx.h:659
ktx_uint32_t glBaseInternalformat
Definition: ktx.h:661
ktx_uint32_t glType
Definition: ktx.h:663
Class representing a KTX version 2 format texture.
Definition: ktx.h:699
class_id classId
Identify the class type.
Definition: ktx.h:288