hostsupport/hostopengles20/src/texture.c
author Matt Plumtree <matt.plumtree@nokia.com>
Thu, 07 Oct 2010 18:44:04 +0100
branchbug235_bringup_0
changeset 56 40cc73c24bf8
parent 55 09263774e342
child 76 24381b61de5c
permissions -rw-r--r--
Host components now buildable in their correct locations (although stil using CMake). Use build.bat in package root. Note holdingarea build no longer works and everything except llvm will be removed soon. Fix ups to references to components moved to vghwinterface. Move package common headers up to pacakge level inc directory.

/* 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();
}