diff -r da7c1a80df0d -r d2d6724aef32 holdingarea/libGLESv2/src/GLES2/texture.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/holdingarea/libGLESv2/src/GLES2/texture.c Thu Sep 16 09:43:14 2010 +0100 @@ -0,0 +1,1344 @@ +/* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Initial Contributors: + * Nokia Corporation - initial contribution. + * + * Contributors: + * + * Description: + * + */ + +#include "common.h" +#include "hgl.h" +#include "context.h" +#include "half.h" +#include "util.h" +#include "degl.h" + +DGLTexture* DGLTexture_create(GLuint name, DGLTextureType type, GLint num_levels) +{ + DGLTexture* texture = malloc(sizeof(DGLTexture)); + if(texture == NULL) + { + return NULL; + } + + texture->obj.name = name; + texture->obj.next = NULL; + + texture->type = type; + + { + int face; + for(face = 0; face < 6; face++) + { + texture->num_levels[face] = 0; + texture->levels[face] = malloc(num_levels * sizeof(DGLTextureLevel)); + if(texture->levels[face] == NULL) + { + while(face--) + { + free(texture->levels[face]); + } + free(texture); + return NULL; + } + { + int level; + for(level = 0; level < num_levels; level++) + { + texture->levels[face][level].specified = GL_FALSE; + texture->levels[face][level].format = 0; + texture->levels[face][level].width = 0; + texture->levels[face][level].height = 0; + texture->levels[face][level].bound_surface = NULL; + } + } + texture->egl_image[face] = NULL; + } + } + + return texture; +} + +static GLenum dglFaceToTarget(DGLTexture* texture, int face) +{ + DGLES2_ASSERT(texture != NULL); + { + switch(face) + { + case 0: + if(texture->type == DGLES2_TEXTURE_2D) + { + return GL_TEXTURE_2D; + } + else + { + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + } + + case 1: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + + case 2: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + + case 3: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + + case 4: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + + case 5: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + + default: + DGLES2_ASSERT(GL_FALSE); + return -1; + } + } +} + +void DGLTexture_destroy(DGLTexture *texture) +{ + DGLES2_ASSERT(texture != NULL); + { + int face; + for(face = 0; face < 6; face++) + { + DGLES2_ASSERT(texture->levels[face] != NULL); + free(texture->levels[face]); + texture->levels[face] = NULL; + + if(texture->egl_image[face] != NULL) + { + deglUnregisterImageTarget(texture->egl_image[face], dglFaceToTarget(texture, face), texture->obj.name); + texture->egl_image[face] = NULL; + } + } + } + free(texture); +} + +GLboolean DGLTexture_isComplete(const DGLTexture* texture) +{ + DGLES2_ASSERT(texture != NULL); + { + int num_faces = 6 ? texture->type == DGLES2_TEXTURE_CUBE_MAP : 1; + int face; + for(face = 0; face < num_faces; face++) + { + if(texture->num_levels[face] < 1) + { + return GL_FALSE; + } + else + { + int i; + const DGLTextureLevel* level_zero; + int width; + int height; + + level_zero = &texture->levels[face][0]; + width = level_zero->width; + height = level_zero->height; + + if(width <= 0 || height <= 0) + { + return GL_FALSE; + } + + for(i = 1; i < texture->num_levels[face]; i++) + { + const DGLTextureLevel* level = &texture->levels[face][i]; + + if(width > 1) width /= 2; + if(height > 1) height /= 2; + + if(level->format != level_zero->format || + level->width != width || + level->height != height || + level->width == 0 || + level->height == 0) + { + return GL_FALSE; + } + } + } + } + + return GL_TRUE; + } +} + +GLboolean DGLTexture_hasLevelZero(const DGLTexture* texture) +{ + DGLES2_ASSERT(texture != NULL); + { + int num_faces = 6 ? texture->type == DGLES2_TEXTURE_CUBE_MAP : 1; + int face; + for(face = 0; face < num_faces; face++) + { + if(texture->num_levels[face] <= 0 || !texture->levels[face][0].specified) + { + return GL_FALSE; + } + } + + return GL_TRUE; + } +} + +GLboolean DGLTexture_hasLevelsOtherThanZero(const DGLTexture* texture) +{ + DGLES2_ASSERT(texture != NULL); + { + int num_faces = 6 ? texture->type == DGLES2_TEXTURE_CUBE_MAP : 1; + int face; + for(face = 0; face < num_faces; face++) + { + int level; + for(level = 1; level < texture->num_levels[face]; level++) + { + if(texture->levels[face][level].specified) + { + return GL_TRUE; + } + } + } + + return GL_FALSE; + } +} + +static int dglTargetToFace(DGLTexture* texture, GLenum target) +{ + DGLES2_ASSERT(texture != NULL); + { + switch(target) + { + case GL_TEXTURE_2D: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_2D); + return 0; + + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return 0; + + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return 1; + + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return 2; + + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return 3; + + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return 4; + + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + DGLES2_ASSERT(texture->type == DGLES2_TEXTURE_CUBE_MAP); + return 5; + + default: + DGLES2_ASSERT(GL_FALSE); + return -1; + } + } +} + +DGLTextureLevel* DGLTexture_getLevel(DGLTexture* texture, GLenum target, GLint level) +{ + DGLES2_ASSERT(texture != NULL); + return &texture->levels[dglTargetToFace(texture, target)][level]; +} + +void DGLTexture_setLevel(DGLTexture* texture, GLenum target, GLint level, GLenum format, GLsizei width, GLsizei height) +{ + DGLES2_ASSERT(texture != NULL); + { + DGLTextureLevel* level_obj = DGLTexture_getLevel(texture, target, level); + level_obj->format = format; + level_obj->width = width; + level_obj->height = height; + level_obj->specified = GL_TRUE; + } +} + +GLeglImageOES DGLTexture_getEGLImage(DGLTexture* texture, GLenum target) +{ + return texture->egl_image[dglTargetToFace(texture, target)]; +} + +void DGLTexture_setEGLImage(DGLTexture* texture, GLenum target, GLeglImageOES image) +{ + texture->egl_image[dglTargetToFace(texture, target)] = image; +} + +void DGLTexture_generateMipmap(DGLTexture* texture) +{ + DGLES2_ASSERT(texture != NULL); + { + int face; + int num_faces; + const DGLTextureLevel* level_zero; + int level; + int num_levels; + int width, height; + + num_faces = 6 ? texture->type == DGLES2_TEXTURE_CUBE_MAP : 1; + for(face = 0; face < num_faces; face++) + { + level_zero = &texture->levels[face][0]; + + num_levels = dglLog2(dglMax(level_zero->width, level_zero->height)) + 1; + texture->num_levels[face] = num_levels; + + width = level_zero->width; + height = level_zero->height; + + for(level = 0; level < num_levels; level++) + { + if(width > 1) width /= 2; + if(height > 1) height /= 2; + + DGLES2_ASSERT(level < num_levels - 1 || (width > 1 || height > 1)); + + if(texture->levels[face][level].bound_surface != NULL) + { + // Texture image is respecified. Release the bound EGLSurface. + deglReleaseTexImage(texture->levels[face][level].bound_surface, texture->obj.name, level); + } + + texture->levels[face][level].format = level_zero->format; + texture->levels[face][level].width = width; + texture->levels[face][level].height = height; + texture->levels[face][level].bound_surface = NULL; + texture->levels[face][level].specified = GL_TRUE; + } + } + } +} + +// Add a 3-bit two's complement integer to an integer. +static int dglAddTwosComplement(int a, char b) +{ + if(b & 0x4) + { + // Negative. + return a - ((~b + 1) & 0x7); + } + else + { + // Positive. + return a + b; + } +} + +static int dglClamp(int x, int min, int max) +{ + if(x < min) + { + return min; + } + else if(x > max) + { + return max; + } + else + { + return x; + } +} + +static void* dglDecompressETCTexture(int width, int height, const unsigned char* data) +{ + int bytes_per_pixel = 3; // RGB888 + + unsigned char* decompressed = malloc(width * height * bytes_per_pixel); + if(decompressed == NULL) + { + return NULL; + } + + { + int xblock, yblock; + + char dr, dg, db; + + // Number of 4x4 blocks horizontally and vertically. + int num_xblocks = (width + 3) / 4; + int num_yblocks = (height + 3) / 4; + + for(yblock = 0; yblock < num_yblocks; yblock++) + { + for(xblock = 0; xblock < num_xblocks; xblock++) + { + int i; + char pixel; + + khronos_int64_t blockbits; + int diffbit, flipbit; + + unsigned char r[2], g[2], b[2]; + + int table[2]; + + // Construct 64 bits from 8 bytes. + blockbits = data[0]; + for(i = 1; i < 8; i++) + { + blockbits <<= 8; + blockbits |= data[i]; + } + + diffbit = (blockbits >> 33) & 1; + flipbit = (blockbits >> 32) & 1; + + // Base color. + + if(!diffbit) + { + // Individual mode. + + // Subblock 1. + r[0] = (blockbits >> 60) & 0xf; + g[0] = (blockbits >> 52) & 0xf; + b[0] = (blockbits >> 44) & 0xf; + + r[0] |= r[0] << 4; + g[0] |= g[0] << 4; + b[0] |= b[0] << 4; + + // Subblock 2. + r[1] = (blockbits >> 56) & 0xf; + g[1] = (blockbits >> 48) & 0xf; + b[1] = (blockbits >> 40) & 0xf; + + r[1] |= r[1] << 4; + g[1] |= g[1] << 4; + b[1] |= b[1] << 4; + } + else + { + // Differential mode. + + r[0] = (blockbits >> 59) & 0x1f; + g[0] = (blockbits >> 51) & 0x1f; + b[0] = (blockbits >> 43) & 0x1f; + + dr = (blockbits >> 56) & 0x7; + dg = (blockbits >> 48) & 0x7; + db = (blockbits >> 40) & 0x7; + + // Subblock 2. + r[1] = dglAddTwosComplement(r[0], dr); + g[1] = dglAddTwosComplement(g[0], dg); + b[1] = dglAddTwosComplement(b[0], db); + + r[1] = (r[1] << 3) | ((r[1] >> 2) & 0x7); + g[1] = (g[1] << 3) | ((g[1] >> 2) & 0x7); + b[1] = (b[1] << 3) | ((b[1] >> 2) & 0x7); + + // Subblock 1. + r[0] = (r[0] << 3) | ((r[0] >> 2) & 0x7); + g[0] = (g[0] << 3) | ((g[0] >> 2) & 0x7); + b[0] = (b[0] << 3) | ((b[0] >> 2) & 0x7); + } + + // Modifier tables. + + table[0] = (blockbits >> 37) & 0x7; + table[1] = (blockbits >> 34) & 0x7; + + // Write final pixel colors in a top-down left-right order per block. + for(pixel = 0; pixel < 4 * 4; pixel++) + { + static const int tables[][8] = {{2, 8}, {15, 17}, {9, 29}, {13, 42}, + {18, 60}, {24, 80}, {33, 106}, {47, 183}}; + + int x, y; + int loc; + int subblock; + int modifier; + + x = 4 * xblock + pixel / 4; + y = 4 * yblock + pixel % 4; + + if(x >= width || y >= height) + { + continue; + } + + // Memory location of destination pixel. + loc = y * width + x; + loc *= bytes_per_pixel; + + if(flipbit) + { + subblock = (pixel / 2) & 1; + } + else + { + subblock = pixel / 8; + } + + DGLES2_ASSERT(subblock == 0 || subblock == 1); + + modifier = tables[table[subblock]][(blockbits >> pixel) & 1]; + if((blockbits >> (16 + pixel)) & 1) + { + modifier *= -1; + } + + decompressed[loc + 0] = dglClamp(r[subblock] + modifier, 0, 255); + decompressed[loc + 1] = dglClamp(g[subblock] + modifier, 0, 255); + decompressed[loc + 2] = dglClamp(b[subblock] + modifier, 0, 255); + } + + // Move to next block. + data += 8; + } + } + } + + return decompressed; +} + +static GLboolean dglIsPalettedFormat(GLenum format) +{ + switch(format) + { + case GL_PALETTE4_RGB8_OES: + case GL_PALETTE4_RGBA8_OES: + case GL_PALETTE4_R5_G6_B5_OES: + case GL_PALETTE4_RGBA4_OES: + case GL_PALETTE4_RGB5_A1_OES: + case GL_PALETTE8_RGB8_OES: + case GL_PALETTE8_RGBA8_OES: + case GL_PALETTE8_R5_G6_B5_OES: + case GL_PALETTE8_RGBA4_OES: + case GL_PALETTE8_RGB5_A1_OES: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +static GLenum dglMapPalettedToBaseFormat(GLenum format) +{ + switch(format) + { + case GL_PALETTE4_RGB8_OES: + case GL_PALETTE4_R5_G6_B5_OES: + case GL_PALETTE8_RGB8_OES: + case GL_PALETTE8_R5_G6_B5_OES: + return GL_RGB; + + case GL_PALETTE4_RGBA8_OES: + case GL_PALETTE4_RGBA4_OES: + case GL_PALETTE4_RGB5_A1_OES: + case GL_PALETTE8_RGBA8_OES: + case GL_PALETTE8_RGBA4_OES: + case GL_PALETTE8_RGB5_A1_OES: + return GL_RGBA; + + default: + DGLES2_ASSERT(GL_FALSE); + } + + // not reached + return 0; +} + +static void* dglDecompressPalettedTexture(int level, GLenum format, int width, int height, int imageSize, const void* data) +{ + const unsigned char* palette = data; + int bits_per_pixel; + int palette_entry_size; + int num_palette_entries; + const unsigned char* image_data; + int i; + int bytes_per_pixel; + GLenum base_format; + char* decompressed_data; + int pixels_per_byte; + int max_pixels; + int end; + int r, g, b, a; + + switch(format) + { + case GL_PALETTE4_RGB8_OES: + bits_per_pixel = 4; + palette_entry_size = 3; + break; + + case GL_PALETTE4_RGBA8_OES: + bits_per_pixel = 4; + palette_entry_size = 4; + break; + + case GL_PALETTE4_R5_G6_B5_OES: + case GL_PALETTE4_RGB5_A1_OES: + case GL_PALETTE4_RGBA4_OES: + bits_per_pixel = 4; + palette_entry_size = 2; + break; + + case GL_PALETTE8_RGB8_OES: + bits_per_pixel = 8; + palette_entry_size = 3; + break; + + case GL_PALETTE8_RGBA8_OES: + bits_per_pixel = 8; + palette_entry_size = 4; + break; + + case GL_PALETTE8_R5_G6_B5_OES: + case GL_PALETTE8_RGBA4_OES: + case GL_PALETTE8_RGB5_A1_OES: + bits_per_pixel = 8; + palette_entry_size = 2; + break; + + default: + DGLES2_ASSERT(GL_FALSE); + } + + num_palette_entries = 2 << (bits_per_pixel - 1); + image_data = palette + num_palette_entries * palette_entry_size; + + // Skip to the correct mip level + for(i = 0; i < level; i++) + { + if(bits_per_pixel == 8) + { + image_data += width * height * bits_per_pixel / 8; + } + else + { + DGLES2_ASSERT(bits_per_pixel == 4); + image_data += width * height * bits_per_pixel / 8 / 2; + } + width /= 2; + height /= 2; + } + + base_format = dglMapPalettedToBaseFormat(format); + if(base_format == GL_RGB) + { + bytes_per_pixel = 3; + } + else + { + DGLES2_ASSERT(base_format == GL_RGBA); + bytes_per_pixel = 4; + } + + decompressed_data = malloc(width * height * bytes_per_pixel); + if(decompressed_data == NULL) + { + return NULL; + } + + // Don't go past the end of the data + pixels_per_byte = 8 / bits_per_pixel; + max_pixels = ((const unsigned char*)data + imageSize - image_data) * pixels_per_byte; + end = dglMin(width * height, max_pixels); + + for(i = 0; i < end; i++) + { + int index; + if(bits_per_pixel == 4) + { + if(i & 1) + { + index = image_data[i / 2] & 15; + } + else + { + index = image_data[i / 2] >> 4; + } + } + else + { + DGLES2_ASSERT(bits_per_pixel == 8); + index = image_data[i]; + } + + switch(format) + { + case GL_PALETTE4_RGB8_OES: + case GL_PALETTE8_RGB8_OES: + r = palette[index*3]; + g = palette[index*3+1]; + b = palette[index*3+2]; + break; + + case GL_PALETTE4_RGBA8_OES: + case GL_PALETTE8_RGBA8_OES: + r = palette[index*4]; + g = palette[index*4+1]; + b = palette[index*4+2]; + a = palette[index*4+3]; + break; + + case GL_PALETTE4_R5_G6_B5_OES: + case GL_PALETTE8_R5_G6_B5_OES: + r = palette[index*2+1] >> 3; + r = (r << 3) | (r >> 2); + g = ((palette[index*2+1] & 7) << 3) | (palette[index*2] >> 5); + g = (g << 2) | (g >> 4); + b = palette[index*2] & 0x1f; + b = (b << 3) | (b >> 2); + break; + + case GL_PALETTE4_RGBA4_OES: + case GL_PALETTE8_RGBA4_OES: + r = palette[index*2+1] >> 4; + r |= (r << 4) | r; + g = palette[index*2+1] & 0xf; + g |= (g << 4) | g; + b = palette[index*2] >> 4; + b |= (b << 4) | b; + a = palette[index*2] & 0xf; + a |= (a << 4) | a; + break; + + case GL_PALETTE4_RGB5_A1_OES: + case GL_PALETTE8_RGB5_A1_OES: + r = palette[index*2+1] >> 3; + r = (r << 3) | (r >> 2); + g = ((palette[index*2+1] & 7) << 2) | (palette[index*2] >> 6); + g = (g << 3) | (g >> 2); + b = (palette[index*2] >> 1) & 0x1f; + b = (b << 3) | (b >> 2); + a = (palette[index*2] & 1) ? 255 : 0; + break; + + default: + DGLES2_ASSERT(GL_FALSE); + } + + if(base_format == GL_RGB) + { + decompressed_data[i*3+0] = r; + decompressed_data[i*3+1] = g; + decompressed_data[i*3+2] = b; + } + else + { + DGLES2_ASSERT(base_format == GL_RGBA); + decompressed_data[i*4+0] = r; + decompressed_data[i*4+1] = g; + decompressed_data[i*4+2] = b; + decompressed_data[i*4+3] = a; + } + } + + return decompressed_data; +} + +GL_APICALL_BUILD void GL_APIENTRY glActiveTexture(GLenum texture) +{ + DGLES2_ENTER(); + ctx->hgl.ActiveTexture(texture); + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glBindTexture (GLenum target, GLuint texture) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLContext_getHostError(ctx); +// Dprintf("glBindTexture(%x, %d)\n", target, texture); + ctx->hgl.BindTexture(target, texture); + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + if(!DGLContext_bindTexture(ctx, target, texture)) + { + DGLES2_ERROR(GL_OUT_OF_MEMORY); + } + } + DGLES2_LEAVE_NO_ERROR_CHECK(); +} + +static GLboolean dglIsValid2DTextureTarget(GLenum target) +{ + switch(target) + { + case GL_TEXTURE_2D: + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + return GL_TRUE; + + default: + return GL_FALSE; + } +} + +GL_APICALL_BUILD void GL_APIENTRY glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(!dglIsValid2DTextureTarget(target), GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsPalettedFormat(internalformat) && + internalformat != GL_ETC1_RGB8_OES, + GL_INVALID_ENUM); + DGLES2_ERROR_IF(height < 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(width < 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(border != 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(imageSize < 0, GL_INVALID_VALUE); + { + if(dglIsPalettedFormat(internalformat)) + { + int num_levels, cur_level; + GLenum base_format; + DGLTexture* texture; + + base_format = dglMapPalettedToBaseFormat(internalformat); + texture = DGLContext_getTexture(ctx, target); + DGLES2_ASSERT(texture != NULL); + + DGLES2_ERROR_IF(level > 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(level < -ctx->max_texture_level, GL_INVALID_VALUE); + + num_levels = -level + 1; + for(cur_level = 0; cur_level < num_levels; cur_level++) + { + if(data != NULL) + { + void* decompressed_data = dglDecompressPalettedTexture(cur_level, internalformat, width, height, imageSize, data); + if(decompressed_data == NULL) + { + DGLES2_ERROR(GL_OUT_OF_MEMORY); + } + ctx->hgl.TexImage2D(target, cur_level, base_format, width, height, border, base_format, GL_UNSIGNED_BYTE, decompressed_data); + free(decompressed_data); + } + else + { + ctx->hgl.TexImage2D(target, cur_level, base_format, width, height, border, base_format, GL_UNSIGNED_BYTE, NULL); + } + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + DGLTexture_setLevel(texture, target, level, internalformat, width, height); + DGLTexture_setEGLImage(texture, target, NULL); + } + width /= 2; + height /= 2; + } + } + else + { + void* decompressed_data; + int numblocks; + + DGLES2_ASSERT(internalformat == GL_ETC1_RGB8_OES); + + DGLES2_ERROR_IF(level < 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(level > ctx->max_texture_level, GL_INVALID_VALUE); + + numblocks = ((width + 3) / 4) * ((height + 3) / 4); + + if(imageSize != numblocks * 8) + { + DGLES2_ERROR(GL_INVALID_VALUE); + } + + decompressed_data = dglDecompressETCTexture(width, height, data); + ctx->hgl.TexImage2D(target, level, GL_RGB, width, height, border, GL_RGB, GL_UNSIGNED_BYTE, decompressed_data); + free(decompressed_data); + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + DGLTexture* texture; + GLeglImageOES image; + + texture = DGLContext_getTexture(ctx, target); + DGLES2_ASSERT(texture != NULL); + DGLTexture_setLevel(texture, target, level, internalformat, width, height); + + image = DGLTexture_getEGLImage(texture, target); + if(image != NULL) + { + // Texture is respecified. It is no longer an EGLImage sibling. + deglUnregisterImageTarget(image, target, texture->obj.name); + DGLTexture_setEGLImage(texture, target, NULL); + } + + { + DGLTextureLevel* level_obj = DGLTexture_getLevel(texture, target, level); + if(level_obj->bound_surface != NULL) + { + // Texture is respecified. Release the bound EGLSurface. + deglReleaseTexImage(level_obj->bound_surface, texture->obj.name, level); + level_obj->bound_surface = NULL; + } + } + } + } + } + DGLES2_LEAVE_NO_ERROR_CHECK(); +} + +GL_APICALL_BUILD void GL_APIENTRY glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(!dglIsValid2DTextureTarget(target), GL_INVALID_ENUM); + DGLES2_ERROR_IF(level < 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(level > ctx->max_texture_level, GL_INVALID_VALUE); + DGLES2_ERROR_IF(!dglIsPalettedFormat(format) && format != GL_ETC1_RGB8_OES, GL_INVALID_ENUM); + // No supported formats. + DGLES2_ERROR(GL_INVALID_OPERATION); + DGLES2_LEAVE(); +} + +static GLboolean dglIsValidFormat(GLenum format) +{ + switch(format) + { + case GL_ALPHA: + case GL_RGB: + case GL_RGBA: + case GL_LUMINANCE: + case GL_LUMINANCE_ALPHA: + return GL_TRUE; + + default: + return GL_FALSE; + } +} + +GL_APICALL_BUILD void GL_APIENTRY glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(!dglIsValid2DTextureTarget(target), GL_INVALID_ENUM); + DGLES2_ERROR_IF(level < 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(level > ctx->max_texture_level, GL_INVALID_VALUE); + DGLES2_ERROR_IF(!dglIsValidFormat(internalformat), GL_INVALID_ENUM); + DGLES2_ERROR_IF(border != 0, GL_INVALID_VALUE); + { + DGLContext_getHostError(ctx); + + DGLES2_BEGIN_READING(); + ctx->hgl.CopyTexImage2D(target, level, internalformat, x, y, width, height, border); + DGLES2_END_READING(); + + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) { + DGLTexture* texture; + GLeglImageOES image; + + texture = DGLContext_getTexture(ctx, target); + DGLES2_ASSERT(texture != NULL); + DGLTexture_setLevel(texture, target, level, internalformat, width, height); + + image = DGLTexture_getEGLImage(texture, target); + if(image != NULL) + { + // Texture is respecified. It is no longer an EGLImage sibling. + deglUnregisterImageTarget(image, target, texture->obj.name); + DGLTexture_setEGLImage(texture, target, NULL); + } + + { + DGLTextureLevel* level_obj = DGLTexture_getLevel(texture, target, level); + if(level_obj->bound_surface != NULL) + { + // Texture is respecified. Release the bound EGLSurface. + deglReleaseTexImage(level_obj->bound_surface, texture->obj.name, level); + level_obj->bound_surface = NULL; + } + } + } + } + DGLES2_LEAVE_NO_ERROR_CHECK(); +} + +GL_APICALL_BUILD void GL_APIENTRY glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(!dglIsValid2DTextureTarget(target), GL_INVALID_ENUM); + { + DGLTextureLevel* level_obj = DGLContext_getTextureLevel(ctx, target, level); + DGLES2_ASSERT(level_obj != NULL); + if(dglIsPalettedFormat(level_obj->format) || level_obj->format == GL_ETC1_RGB8_OES) + { + DGLES2_ERROR(GL_INVALID_OPERATION); + } + } + DGLContext_getHostError(ctx); + DGLES2_BEGIN_READING(); + ctx->hgl.CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + DGLES2_END_READING(); + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + DGLTexture* texture; + GLeglImageOES image; + + texture = DGLContext_getTexture(ctx, target); + DGLES2_ASSERT(texture != NULL); + image = DGLTexture_getEGLImage(texture, target); + if(image != NULL) + { + deglUpdateImageSiblings(image, target, texture->obj.name); + } + } + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glDeleteTextures(GLsizei n, const GLuint* textures) +{ + DGLES2_ENTER(); + DGLContext_getHostError(ctx); + ctx->hgl.DeleteTextures(n, textures); + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + int i; + for(i = 0; i < n; i++) + { + DGLContext_destroyTexture(ctx, textures[n]); + } + } + DGLES2_LEAVE_NO_ERROR_CHECK(); +} + +GL_APICALL_BUILD void GL_APIENTRY glGenerateMipmap (GLenum target) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLContext_getHostError(ctx); + ctx->hgl.GenerateMipmapEXT(target); + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + DGLTexture* texture; + GLeglImageOES image; + + texture = DGLContext_getTexture(ctx, target); + DGLTexture_generateMipmap(texture); + image = DGLTexture_getEGLImage(texture, target); + if(image != NULL) + { + // Texture is respecified. It is no longer an EGLImage sibling. + deglUnregisterImageTarget(image, target, texture->obj.name); + DGLTexture_setEGLImage(texture, target, NULL); + } + } + DGLES2_LEAVE_NO_ERROR_CHECK(); +} + +GL_APICALL_BUILD void GL_APIENTRY glGenTextures(GLsizei n, GLuint* textures) +{ + DGLES2_ENTER(); + ctx->hgl.GenTextures(n, textures); + DGLES2_LEAVE(); +} + +static GLboolean dglIsValidTextureParameter(GLenum pname) +{ + switch(pname) + { + case GL_TEXTURE_WRAP_S: + case GL_TEXTURE_WRAP_T: + case GL_TEXTURE_MIN_FILTER: + case GL_TEXTURE_MAG_FILTER: + return GL_TRUE; + + default: + return GL_FALSE; + } +} + +GL_APICALL_BUILD void GL_APIENTRY glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidTextureParameter(pname), GL_INVALID_ENUM); + ctx->hgl.GetTexParameterfv(target, pname, params); + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glGetTexParameteriv(GLenum target, GLenum pname, GLint* params) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidTextureParameter(pname), GL_INVALID_ENUM); + ctx->hgl.GetTexParameteriv(target, pname, params); + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD GLboolean GL_APIENTRY glIsTexture(GLuint texture) +{ + DGLES2_ENTER_RET(GL_FALSE); + DGLES2_LEAVE_RET(ctx->hgl.IsTexture(texture)); +} + +static GLfloat* dglConvertHalfTextureToFloat(GLsizei width, GLsizei height, GLenum format, const void* pixels) +{ + int components; + GLfloat* conv; + int i; + + switch(format) + { + case GL_ALPHA: + case GL_LUMINANCE: + components = 1; + break; + + case GL_LUMINANCE_ALPHA: + components = 2; + break; + + case GL_RGB: + components = 3; + break; + + case GL_RGBA: + components = 4; + break; + + default: + DGLES2_ASSERT(GL_FALSE); + } + + conv = malloc(width * height * components * sizeof(GLfloat)); + if(conv == NULL) + { + return NULL; + } + + for(i = 0; i < width * height * components; i++) + { + conv[i] = dglConvertHalfToFloat(((GLfixed*)pixels)[i]); + } + + return conv; +} + +static GLboolean dglIsValidType(GLenum type) +{ + switch(type) + { + case GL_UNSIGNED_BYTE: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_4_4_4_4: + case GL_UNSIGNED_SHORT_5_5_5_1: + return GL_TRUE; + + default: + return GL_FALSE; + } +} + +GL_APICALL_BUILD void GL_APIENTRY glTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(!dglIsValid2DTextureTarget(target), GL_INVALID_ENUM); + DGLES2_ERROR_IF(level < 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(level > ctx->max_texture_level, GL_INVALID_VALUE); + DGLES2_ERROR_IF(dglIsPalettedFormat(internalformat), GL_INVALID_OPERATION); + DGLES2_ERROR_IF(!dglIsValidFormat(internalformat), GL_INVALID_VALUE); + DGLES2_ERROR_IF(!dglIsValidFormat(format), GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidType(type), GL_INVALID_ENUM); + DGLES2_ERROR_IF(border != 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(format != internalformat, GL_INVALID_OPERATION); + + DGLContext_getHostError(ctx); + + if(pixels != NULL && type == GL_HALF_FLOAT_OES) + { + GLfloat* conv = dglConvertHalfTextureToFloat(width, height, format, pixels); + if(conv == NULL) + { + DGLES2_ERROR(GL_OUT_OF_MEMORY); + } + ctx->hgl.TexImage2D(target, level, internalformat, width, height, border, format, GL_FLOAT, conv); + free(conv); + } + else + { + ctx->hgl.TexImage2D(target, level, internalformat, width, height, border, format, type, pixels); + } + + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + DGLTexture* texture; + GLeglImageOES image; + + texture = DGLContext_getTexture(ctx, target); + DGLES2_ASSERT(texture != NULL); + DGLTexture_setLevel(texture, target, level, internalformat, width, height); + + image = DGLTexture_getEGLImage(texture, target); + if(image != NULL) + { + // Texture is respecified. It is no longer an EGLImage sibling. + deglUnregisterImageTarget(image, target, texture->obj.name); + DGLTexture_setEGLImage(texture, target, NULL); + } + + { + DGLTextureLevel* level_obj = DGLTexture_getLevel(texture, target, level); + if(level_obj->bound_surface != NULL) + { + // Texture is respecified. Release the bound EGLSurface. + deglReleaseTexImage(level_obj->bound_surface, texture->obj.name, level); + level_obj->bound_surface = NULL; + } + } + } + + DGLES2_LEAVE_NO_ERROR_CHECK(); +} + +GL_APICALL_BUILD void GL_APIENTRY glTexParameterf(GLenum target, GLenum pname, GLfloat param) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidTextureParameter(pname), GL_INVALID_ENUM); + ctx->hgl.TexParameterf(target, pname, param); + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glTexParameterfv(GLenum target, GLenum pname, const GLfloat* params) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidTextureParameter(pname), GL_INVALID_ENUM); + ctx->hgl.TexParameterfv(target, pname, params); + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glTexParameteri(GLenum target, GLenum pname, GLint param) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidTextureParameter(pname), GL_INVALID_ENUM); + ctx->hgl.TexParameteri(target, pname, param); + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glTexParameteriv(GLenum target, GLenum pname, const GLint* params) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP, GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidTextureParameter(pname), GL_INVALID_ENUM); + ctx->hgl.TexParameteriv(target, pname, params); + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(!dglIsValid2DTextureTarget(target), GL_INVALID_ENUM); + DGLES2_ERROR_IF(level < 0, GL_INVALID_VALUE); + DGLES2_ERROR_IF(level > ctx->max_texture_level, GL_INVALID_VALUE); + DGLES2_ERROR_IF(!dglIsValidFormat(format), GL_INVALID_ENUM); + DGLES2_ERROR_IF(!dglIsValidType(type), GL_INVALID_ENUM); + { + DGLTextureLevel* level_obj = DGLContext_getTextureLevel(ctx, target, level); + DGLES2_ASSERT(level_obj != NULL); + if(format != level_obj->format) + { + DGLES2_ERROR(GL_INVALID_OPERATION); + } + + DGLContext_getHostError(ctx); + + if(pixels != NULL && type == GL_HALF_FLOAT_OES) + { + GLfloat* conv = dglConvertHalfTextureToFloat(width, height, format, pixels); + if(conv == NULL) + { + DGLES2_ERROR(GL_OUT_OF_MEMORY); + } + ctx->hgl.TexSubImage2D(target, level, xoffset, yoffset, width, height, format, GL_FLOAT, conv); + free(conv); + } + else + { + ctx->hgl.TexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); + } + + if(DGLContext_getHostError(ctx) == GL_NO_ERROR) + { + DGLTexture* texture; + GLeglImageOES image; + + texture = DGLContext_getTexture(ctx, target); + DGLES2_ASSERT(texture != NULL); + image = DGLTexture_getEGLImage(texture, target); + if(image != NULL) + { + deglUpdateImageSiblings(image, target, texture->obj.name); + } + } + } + DGLES2_LEAVE(); +} + +GL_APICALL_BUILD void GL_APIENTRY glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) +{ + DGLES2_ENTER(); + DGLES2_ERROR_IF(target != GL_TEXTURE_2D, GL_INVALID_ENUM); + DGLES2_ERROR_IF(image == NULL, GL_INVALID_OPERATION); + { + // Clear all mipmap levels. + int level; + for(level = 0; level <= ctx->max_texture_level; level++) + { + ctx->hgl.TexImage2D(target, level, GL_RGBA, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + + if(!DGLContext_specifyTextureFromEGLImage(ctx, image, target)) + { + DGLES2_ERROR(GL_INVALID_OPERATION); + } + + { + DGLTexture* texture = DGLContext_getTexture(ctx, target); + DGLTextureLevel* level_obj = DGLTexture_getLevel(texture, target, 0); + if(level_obj->bound_surface != NULL) + { + // Texture is respecified. Release the bound EGLSurface. + deglReleaseTexImage(level_obj->bound_surface, texture->obj.name, 0); + level_obj->bound_surface = NULL; + } + } + } + DGLES2_LEAVE_NO_ERROR_CHECK(); +} \ No newline at end of file