| /* libs/opengles/texture.cpp | 
 | ** | 
 | ** Copyright 2006, The Android Open Source Project | 
 | ** | 
 | ** Licensed under the Apache License, Version 2.0 (the "License"); | 
 | ** you may not use this file except in compliance with the License. | 
 | ** You may obtain a copy of the License at | 
 | ** | 
 | **     http://www.apache.org/licenses/LICENSE-2.0 | 
 | ** | 
 | ** Unless required by applicable law or agreed to in writing, software | 
 | ** distributed under the License is distributed on an "AS IS" BASIS, | 
 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | ** See the License for the specific language governing permissions and | 
 | ** limitations under the License. | 
 | */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include "context.h" | 
 | #include "fp.h" | 
 | #include "state.h" | 
 | #include "texture.h" | 
 | #include "TextureObjectManager.h" | 
 |  | 
 | #include <private/ui/android_natives_priv.h> | 
 | #include <ETC1/etc1.h> | 
 |  | 
 | namespace android { | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | static void bindTextureTmu( | 
 |     ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex); | 
 |  | 
 | static __attribute__((noinline)) | 
 | void generateMipmap(ogles_context_t* c, GLint level); | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | #if 0 | 
 | #pragma mark - | 
 | #pragma mark Init | 
 | #endif | 
 |  | 
 | void ogles_init_texture(ogles_context_t* c) | 
 | { | 
 |     c->textures.packAlignment   = 4; | 
 |     c->textures.unpackAlignment = 4; | 
 |  | 
 |     // each context has a default named (0) texture (not shared) | 
 |     c->textures.defaultTexture = new EGLTextureObject(); | 
 |     c->textures.defaultTexture->incStrong(c); | 
 |  | 
 |     // bind the default texture to each texture unit | 
 |     for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) { | 
 |         bindTextureTmu(c, i, 0, c->textures.defaultTexture); | 
 |         memset(c->current.texture[i].v, 0, sizeof(vec4_t)); | 
 |         c->current.texture[i].Q = 0x10000; | 
 |     } | 
 | } | 
 |  | 
 | void ogles_uninit_texture(ogles_context_t* c) | 
 | { | 
 |     if (c->textures.ggl) | 
 |         gglUninit(c->textures.ggl); | 
 |     c->textures.defaultTexture->decStrong(c); | 
 |     for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) { | 
 |         if (c->textures.tmu[i].texture) | 
 |             c->textures.tmu[i].texture->decStrong(c); | 
 |     } | 
 | } | 
 |  | 
 | static __attribute__((noinline)) | 
 | void validate_tmu(ogles_context_t* c, int i) | 
 | { | 
 |     texture_unit_t& u(c->textures.tmu[i]); | 
 |     if (u.dirty) { | 
 |         u.dirty = 0; | 
 |         c->rasterizer.procs.activeTexture(c, i); | 
 |         c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); | 
 |         c->rasterizer.procs.texGeni(c, GGL_S, | 
 |                 GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); | 
 |         c->rasterizer.procs.texGeni(c, GGL_T, | 
 |                 GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); | 
 |         c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, | 
 |                 GGL_TEXTURE_WRAP_S, u.texture->wraps); | 
 |         c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, | 
 |                 GGL_TEXTURE_WRAP_T, u.texture->wrapt); | 
 |         c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, | 
 |                 GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); | 
 |         c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, | 
 |                 GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); | 
 |  | 
 |         // disable this texture unit if it's not complete | 
 |         if (!u.texture->isComplete()) { | 
 |             c->rasterizer.procs.disable(c, GGL_TEXTURE_2D); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void ogles_validate_texture(ogles_context_t* c) | 
 | { | 
 |     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { | 
 |         if (c->rasterizer.state.texture[i].enable) | 
 |             validate_tmu(c, i); | 
 |     } | 
 |     c->rasterizer.procs.activeTexture(c, c->textures.active); | 
 | } | 
 |  | 
 | static | 
 | void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) { | 
 |     c->textures.tmu[tmu].dirty = flags; | 
 | } | 
 |  | 
 | /* | 
 |  * If the active textures are EGLImage, they need to be locked before | 
 |  * they can be used. | 
 |  * | 
 |  * FIXME: code below is far from being optimal | 
 |  * | 
 |  */ | 
 |  | 
 | void ogles_lock_textures(ogles_context_t* c) | 
 | { | 
 |     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { | 
 |         if (c->rasterizer.state.texture[i].enable) { | 
 |             texture_unit_t& u(c->textures.tmu[i]); | 
 |             ANativeWindowBuffer* native_buffer = u.texture->buffer; | 
 |             if (native_buffer) { | 
 |                 c->rasterizer.procs.activeTexture(c, i); | 
 |                 hw_module_t const* pModule; | 
 |                 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule)) | 
 |                     continue; | 
 |  | 
 |                 gralloc_module_t const* module = | 
 |                     reinterpret_cast<gralloc_module_t const*>(pModule); | 
 |  | 
 |                 void* vaddr; | 
 |                 int err = module->lock(module, native_buffer->handle, | 
 |                         GRALLOC_USAGE_SW_READ_OFTEN, | 
 |                         0, 0, native_buffer->width, native_buffer->height, | 
 |                         &vaddr); | 
 |  | 
 |                 u.texture->setImageBits(vaddr); | 
 |                 c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | void ogles_unlock_textures(ogles_context_t* c) | 
 | { | 
 |     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { | 
 |         if (c->rasterizer.state.texture[i].enable) { | 
 |             texture_unit_t& u(c->textures.tmu[i]); | 
 |             ANativeWindowBuffer* native_buffer = u.texture->buffer; | 
 |             if (native_buffer) { | 
 |                 c->rasterizer.procs.activeTexture(c, i); | 
 |                 hw_module_t const* pModule; | 
 |                 if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule)) | 
 |                     continue; | 
 |  | 
 |                 gralloc_module_t const* module = | 
 |                     reinterpret_cast<gralloc_module_t const*>(pModule); | 
 |  | 
 |                 module->unlock(module, native_buffer->handle); | 
 |                 u.texture->setImageBits(NULL); | 
 |                 c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); | 
 |             } | 
 |         } | 
 |     } | 
 |     c->rasterizer.procs.activeTexture(c, c->textures.active); | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 | #if 0 | 
 | #pragma mark - | 
 | #pragma mark Format conversion | 
 | #endif | 
 |  | 
 | static uint32_t gl2format_table[6][4] = { | 
 |     // BYTE, 565, 4444, 5551 | 
 |     { GGL_PIXEL_FORMAT_A_8, | 
 |       0, 0, 0 },                        // GL_ALPHA | 
 |     { GGL_PIXEL_FORMAT_RGB_888, | 
 |       GGL_PIXEL_FORMAT_RGB_565, | 
 |       0, 0 },                           // GL_RGB | 
 |     { GGL_PIXEL_FORMAT_RGBA_8888, | 
 |       0, | 
 |       GGL_PIXEL_FORMAT_RGBA_4444, | 
 |       GGL_PIXEL_FORMAT_RGBA_5551 },     // GL_RGBA | 
 |     { GGL_PIXEL_FORMAT_L_8, | 
 |       0, 0, 0 },                        // GL_LUMINANCE | 
 |     { GGL_PIXEL_FORMAT_LA_88, | 
 |       0, 0, 0 },                        // GL_LUMINANCE_ALPHA | 
 | }; | 
 |  | 
 | static int32_t convertGLPixelFormat(GLint format, GLenum type) | 
 | { | 
 |     int32_t fi = -1; | 
 |     int32_t ti = -1; | 
 |     switch (format) { | 
 |     case GL_ALPHA:              fi = 0;     break; | 
 |     case GL_RGB:                fi = 1;     break; | 
 |     case GL_RGBA:               fi = 2;     break; | 
 |     case GL_LUMINANCE:          fi = 3;     break; | 
 |     case GL_LUMINANCE_ALPHA:    fi = 4;     break; | 
 |     } | 
 |     switch (type) { | 
 |     case GL_UNSIGNED_BYTE:          ti = 0; break; | 
 |     case GL_UNSIGNED_SHORT_5_6_5:   ti = 1; break; | 
 |     case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break; | 
 |     case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break; | 
 |     } | 
 |     if (fi==-1 || ti==-1) | 
 |         return 0; | 
 |     return gl2format_table[fi][ti]; | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type) | 
 | { | 
 |     GLenum error = 0; | 
 |     if (format<GL_ALPHA || format>GL_LUMINANCE_ALPHA) { | 
 |         error = GL_INVALID_ENUM; | 
 |     } | 
 |     if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 && | 
 |         type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) { | 
 |         error = GL_INVALID_ENUM; | 
 |     } | 
 |     if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) { | 
 |         error = GL_INVALID_OPERATION; | 
 |     } | 
 |     if ((type == GL_UNSIGNED_SHORT_4_4_4_4 || | 
 |          type == GL_UNSIGNED_SHORT_5_5_5_1)  && format != GL_RGBA) { | 
 |         error = GL_INVALID_OPERATION; | 
 |     } | 
 |     if (error) { | 
 |         ogles_error(c, error); | 
 |     } | 
 |     return error; | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | GGLContext* getRasterizer(ogles_context_t* c) | 
 | { | 
 |     GGLContext* ggl = c->textures.ggl; | 
 |     if (ggl_unlikely(!ggl)) { | 
 |         // this is quite heavy the first time... | 
 |         gglInit(&ggl); | 
 |         if (!ggl) { | 
 |             return 0; | 
 |         } | 
 |         GGLfixed colors[4] = { 0, 0, 0, 0x10000 }; | 
 |         c->textures.ggl = ggl; | 
 |         ggl->activeTexture(ggl, 0); | 
 |         ggl->enable(ggl, GGL_TEXTURE_2D); | 
 |         ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); | 
 |         ggl->disable(ggl, GGL_DITHER); | 
 |         ggl->shadeModel(ggl, GGL_FLAT); | 
 |         ggl->color4xv(ggl, colors); | 
 |     } | 
 |     return ggl; | 
 | } | 
 |  | 
 | static __attribute__((noinline)) | 
 | int copyPixels( | 
 |         ogles_context_t* c, | 
 |         const GGLSurface& dst, | 
 |         GLint xoffset, GLint yoffset, | 
 |         const GGLSurface& src, | 
 |         GLint x, GLint y, GLsizei w, GLsizei h) | 
 | { | 
 |     if ((dst.format == src.format) && | 
 |         (dst.stride == src.stride) && | 
 |         (dst.width == src.width) && | 
 |         (dst.height == src.height) && | 
 |         (dst.stride > 0) && | 
 |         ((x|y) == 0) && | 
 |         ((xoffset|yoffset) == 0)) | 
 |     { | 
 |         // this is a common case... | 
 |         const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]); | 
 |         const size_t size = src.height * src.stride * pixelFormat.size; | 
 |         memcpy(dst.data, src.data, size); | 
 |         return 0; | 
 |     } | 
 |  | 
 |     // use pixel-flinger to handle all the conversions | 
 |     GGLContext* ggl = getRasterizer(c); | 
 |     if (!ggl) { | 
 |         // the only reason this would fail is because we ran out of memory | 
 |         return GL_OUT_OF_MEMORY; | 
 |     } | 
 |  | 
 |     ggl->colorBuffer(ggl, &dst); | 
 |     ggl->bindTexture(ggl, &src); | 
 |     ggl->texCoord2i(ggl, x-xoffset, y-yoffset); | 
 |     ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h); | 
 |     return 0; | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | static __attribute__((noinline)) | 
 | sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c) | 
 | { | 
 |     sp<EGLTextureObject> tex; | 
 |     const int active = c->textures.active; | 
 |     const GLuint name = c->textures.tmu[active].name; | 
 |  | 
 |     // free the reference to the previously bound object | 
 |     texture_unit_t& u(c->textures.tmu[active]); | 
 |     if (u.texture) | 
 |         u.texture->decStrong(c); | 
 |  | 
 |     if (name == 0) { | 
 |         // 0 is our local texture object, not shared with anyone. | 
 |         // But it affects all bound TMUs immediately. | 
 |         // (we need to invalidate all units bound to this texture object) | 
 |         tex = c->textures.defaultTexture; | 
 |         for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { | 
 |             if (c->textures.tmu[i].texture == tex.get()) | 
 |                 invalidate_texture(c, i); | 
 |         } | 
 |     } else { | 
 |         // get a new texture object for that name | 
 |         tex = c->surfaceManager->replaceTexture(name); | 
 |     } | 
 |  | 
 |     // bind this texture to the current active texture unit | 
 |     // and add a reference to this texture object | 
 |     u.texture = tex.get(); | 
 |     u.texture->incStrong(c); | 
 |     u.name = name; | 
 |     invalidate_texture(c, active); | 
 |     return tex; | 
 | } | 
 |  | 
 | void bindTextureTmu( | 
 |     ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex) | 
 | { | 
 |     if (tex.get() == c->textures.tmu[tmu].texture) | 
 |         return; | 
 |  | 
 |     // free the reference to the previously bound object | 
 |     texture_unit_t& u(c->textures.tmu[tmu]); | 
 |     if (u.texture) | 
 |         u.texture->decStrong(c); | 
 |  | 
 |     // bind this texture to the current active texture unit | 
 |     // and add a reference to this texture object | 
 |     u.texture = tex.get(); | 
 |     u.texture->incStrong(c); | 
 |     u.name = texture; | 
 |     invalidate_texture(c, tmu); | 
 | } | 
 |  | 
 | int createTextureSurface(ogles_context_t* c, | 
 |         GGLSurface** outSurface, int32_t* outSize, GLint level, | 
 |         GLenum format, GLenum type, GLsizei width, GLsizei height, | 
 |         GLenum compressedFormat = 0) | 
 | { | 
 |     // find out which texture is bound to the current unit | 
 |     const int active = c->textures.active; | 
 |     const GLuint name = c->textures.tmu[active].name; | 
 |  | 
 |     // convert the pixelformat to one we can handle | 
 |     const int32_t formatIdx = convertGLPixelFormat(format, type); | 
 |     if (formatIdx == 0) { // we don't know what to do with this | 
 |         return GL_INVALID_OPERATION; | 
 |     } | 
 |  | 
 |     // figure out the size we need as well as the stride | 
 |     const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); | 
 |     const int32_t align = c->textures.unpackAlignment-1; | 
 |     const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; | 
 |     const size_t size = bpr * height; | 
 |     const int32_t stride = bpr / pixelFormat.size; | 
 |  | 
 |     if (level > 0) { | 
 |         const int active = c->textures.active; | 
 |         EGLTextureObject* tex = c->textures.tmu[active].texture; | 
 |         status_t err = tex->reallocate(level, | 
 |                 width, height, stride, formatIdx, compressedFormat, bpr); | 
 |         if (err != NO_ERROR) | 
 |             return GL_OUT_OF_MEMORY; | 
 |         GGLSurface& surface = tex->editMip(level); | 
 |         *outSurface = &surface; | 
 |         *outSize = size; | 
 |         return 0; | 
 |     } | 
 |  | 
 |     sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); | 
 |     status_t err = tex->reallocate(level, | 
 |             width, height, stride, formatIdx, compressedFormat, bpr); | 
 |     if (err != NO_ERROR) | 
 |         return GL_OUT_OF_MEMORY; | 
 |  | 
 |     tex->internalformat = format; | 
 |     *outSurface = &tex->surface; | 
 |     *outSize = size; | 
 |     return 0; | 
 | } | 
 |  | 
 | static size_t dataSizePalette4(int numLevels, int width, int height, int format) | 
 | { | 
 |     int indexBits = 8; | 
 |     int entrySize = 0; | 
 |     switch (format) { | 
 |     case GL_PALETTE4_RGB8_OES: | 
 |         indexBits = 4; | 
 |         /* FALLTHROUGH */ | 
 |     case GL_PALETTE8_RGB8_OES: | 
 |         entrySize = 3; | 
 |         break; | 
 |  | 
 |     case GL_PALETTE4_RGBA8_OES: | 
 |         indexBits = 4; | 
 |         /* FALLTHROUGH */ | 
 |     case GL_PALETTE8_RGBA8_OES: | 
 |         entrySize = 4; | 
 |         break; | 
 |  | 
 |     case GL_PALETTE4_R5_G6_B5_OES: | 
 |     case GL_PALETTE4_RGBA4_OES: | 
 |     case GL_PALETTE4_RGB5_A1_OES: | 
 |         indexBits = 4; | 
 |         /* FALLTHROUGH */ | 
 |     case GL_PALETTE8_R5_G6_B5_OES: | 
 |     case GL_PALETTE8_RGBA4_OES: | 
 |     case GL_PALETTE8_RGB5_A1_OES: | 
 |         entrySize = 2; | 
 |         break; | 
 |     } | 
 |  | 
 |     size_t size = (1 << indexBits) * entrySize; // palette size | 
 |  | 
 |     for (int i=0 ; i< numLevels ; i++) { | 
 |         int w = (width  >> i) ? : 1; | 
 |         int h = (height >> i) ? : 1; | 
 |         int levelSize = h * ((w * indexBits) / 8) ? : 1; | 
 |         size += levelSize; | 
 |     } | 
 |  | 
 |     return size; | 
 | } | 
 |  | 
 | static void decodePalette4(const GLvoid *data, int level, int width, int height, | 
 |                            void *surface, int stride, int format) | 
 |  | 
 | { | 
 |     int indexBits = 8; | 
 |     int entrySize = 0; | 
 |     switch (format) { | 
 |     case GL_PALETTE4_RGB8_OES: | 
 |         indexBits = 4; | 
 |         /* FALLTHROUGH */ | 
 |     case GL_PALETTE8_RGB8_OES: | 
 |         entrySize = 3; | 
 |         break; | 
 |  | 
 |     case GL_PALETTE4_RGBA8_OES: | 
 |         indexBits = 4; | 
 |         /* FALLTHROUGH */ | 
 |     case GL_PALETTE8_RGBA8_OES: | 
 |         entrySize = 4; | 
 |         break; | 
 |  | 
 |     case GL_PALETTE4_R5_G6_B5_OES: | 
 |     case GL_PALETTE4_RGBA4_OES: | 
 |     case GL_PALETTE4_RGB5_A1_OES: | 
 |         indexBits = 4; | 
 |         /* FALLTHROUGH */ | 
 |     case GL_PALETTE8_R5_G6_B5_OES: | 
 |     case GL_PALETTE8_RGBA4_OES: | 
 |     case GL_PALETTE8_RGB5_A1_OES: | 
 |         entrySize = 2; | 
 |         break; | 
 |     } | 
 |  | 
 |     const int paletteSize = (1 << indexBits) * entrySize; | 
 |  | 
 |     uint8_t const* pixels = (uint8_t *)data + paletteSize; | 
 |     for (int i=0 ; i<level ; i++) { | 
 |         int w = (width  >> i) ? : 1; | 
 |         int h = (height >> i) ? : 1; | 
 |         pixels += h * ((w * indexBits) / 8); | 
 |     } | 
 |     width  = (width  >> level) ? : 1; | 
 |     height = (height >> level) ? : 1; | 
 |  | 
 |     if (entrySize == 2) { | 
 |         uint8_t const* const palette = (uint8_t*)data; | 
 |         for (int y=0 ; y<height ; y++) { | 
 |             uint8_t* p = (uint8_t*)surface + y*stride*2; | 
 |             if (indexBits == 8) { | 
 |                 for (int x=0 ; x<width ; x++) { | 
 |                     int index = 2 * (*pixels++); | 
 |                     *p++ = palette[index + 0]; | 
 |                     *p++ = palette[index + 1]; | 
 |                 } | 
 |             } else { | 
 |                 for (int x=0 ; x<width ; x+=2) { | 
 |                     int v = *pixels++; | 
 |                     int index = 2 * (v >> 4); | 
 |                     *p++ = palette[index + 0]; | 
 |                     *p++ = palette[index + 1]; | 
 |                     if (x+1 < width) { | 
 |                         index = 2 * (v & 0xF); | 
 |                         *p++ = palette[index + 0]; | 
 |                         *p++ = palette[index + 1]; | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |     } else if (entrySize == 3) { | 
 |         uint8_t const* const palette = (uint8_t*)data; | 
 |         for (int y=0 ; y<height ; y++) { | 
 |             uint8_t* p = (uint8_t*)surface + y*stride*3; | 
 |             if (indexBits == 8) { | 
 |                 for (int x=0 ; x<width ; x++) { | 
 |                     int index = 3 * (*pixels++); | 
 |                     *p++ = palette[index + 0]; | 
 |                     *p++ = palette[index + 1]; | 
 |                     *p++ = palette[index + 2]; | 
 |                 } | 
 |             } else { | 
 |                 for (int x=0 ; x<width ; x+=2) { | 
 |                     int v = *pixels++; | 
 |                     int index = 3 * (v >> 4); | 
 |                     *p++ = palette[index + 0]; | 
 |                     *p++ = palette[index + 1]; | 
 |                     *p++ = palette[index + 2]; | 
 |                     if (x+1 < width) { | 
 |                         index = 3 * (v & 0xF); | 
 |                         *p++ = palette[index + 0]; | 
 |                         *p++ = palette[index + 1]; | 
 |                         *p++ = palette[index + 2]; | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |     } else if (entrySize == 4) { | 
 |         uint8_t const* const palette = (uint8_t*)data; | 
 |         for (int y=0 ; y<height ; y++) { | 
 |             uint8_t* p = (uint8_t*)surface + y*stride*4; | 
 |             if (indexBits == 8) { | 
 |                 for (int x=0 ; x<width ; x++) { | 
 |                     int index = 4 * (*pixels++); | 
 |                     *p++ = palette[index + 0]; | 
 |                     *p++ = palette[index + 1]; | 
 |                     *p++ = palette[index + 2]; | 
 |                     *p++ = palette[index + 3]; | 
 |                 } | 
 |             } else { | 
 |                 for (int x=0 ; x<width ; x+=2) { | 
 |                     int v = *pixels++; | 
 |                     int index = 4 * (v >> 4); | 
 |                     *p++ = palette[index + 0]; | 
 |                     *p++ = palette[index + 1]; | 
 |                     *p++ = palette[index + 2]; | 
 |                     *p++ = palette[index + 3]; | 
 |                     if (x+1 < width) { | 
 |                         index = 4 * (v & 0xF); | 
 |                         *p++ = palette[index + 0]; | 
 |                         *p++ = palette[index + 1]; | 
 |                         *p++ = palette[index + 2]; | 
 |                         *p++ = palette[index + 3]; | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static __attribute__((noinline)) | 
 | void set_depth_and_fog(ogles_context_t* c, GGLfixed z) | 
 | { | 
 |     const uint32_t enables = c->rasterizer.state.enables; | 
 |     // we need to compute Zw | 
 |     int32_t iterators[3]; | 
 |     iterators[1] = iterators[2] = 0; | 
 |     GGLfixed Zw; | 
 |     GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear); | 
 |     GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar); | 
 |     if (z<=0)               Zw = n; | 
 |     else if (z>=0x10000)    Zw = f; | 
 |     else            Zw = gglMulAddx(z, (f-n), n); | 
 |     if (enables & GGL_ENABLE_FOG) { | 
 |         // set up fog if needed... | 
 |         iterators[0] = c->fog.fog(c, Zw); | 
 |         c->rasterizer.procs.fogGrad3xv(c, iterators); | 
 |     } | 
 |     if (enables & GGL_ENABLE_DEPTH_TEST) { | 
 |         // set up z-test if needed... | 
 |         int32_t z = (Zw & ~(Zw>>31)); | 
 |         if (z >= 0x10000) | 
 |             z = 0xFFFF; | 
 |         iterators[0] = (z << 16) | z; | 
 |         c->rasterizer.procs.zGrad3xv(c, iterators); | 
 |     } | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 | #if 0 | 
 | #pragma mark - | 
 | #pragma mark Generate mimaps | 
 | #endif | 
 |  | 
 | extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex); | 
 |  | 
 | void generateMipmap(ogles_context_t* c, GLint level) | 
 | { | 
 |     if (level == 0) { | 
 |         const int active = c->textures.active; | 
 |         EGLTextureObject* tex = c->textures.tmu[active].texture; | 
 |         if (tex->generate_mipmap) { | 
 |             if (buildAPyramid(c, tex) != NO_ERROR) { | 
 |                 ogles_error(c, GL_OUT_OF_MEMORY); | 
 |                 return; | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | static void texParameterx( | 
 |         GLenum target, GLenum pname, GLfixed param, ogles_context_t* c) | 
 | { | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |  | 
 |     EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; | 
 |     switch (pname) { | 
 |     case GL_TEXTURE_WRAP_S: | 
 |         if ((param == GL_REPEAT) || | 
 |             (param == GL_CLAMP_TO_EDGE)) { | 
 |             textureObject->wraps = param; | 
 |         } else { | 
 |             goto invalid_enum; | 
 |         } | 
 |         break; | 
 |     case GL_TEXTURE_WRAP_T: | 
 |         if ((param == GL_REPEAT) || | 
 |             (param == GL_CLAMP_TO_EDGE)) { | 
 |             textureObject->wrapt = param; | 
 |         } else { | 
 |             goto invalid_enum; | 
 |         } | 
 |         break; | 
 |     case GL_TEXTURE_MIN_FILTER: | 
 |         if ((param == GL_NEAREST) || | 
 |             (param == GL_LINEAR) || | 
 |             (param == GL_NEAREST_MIPMAP_NEAREST) || | 
 |             (param == GL_LINEAR_MIPMAP_NEAREST) || | 
 |             (param == GL_NEAREST_MIPMAP_LINEAR) || | 
 |             (param == GL_LINEAR_MIPMAP_LINEAR)) { | 
 |             textureObject->min_filter = param; | 
 |         } else { | 
 |             goto invalid_enum; | 
 |         } | 
 |         break; | 
 |     case GL_TEXTURE_MAG_FILTER: | 
 |         if ((param == GL_NEAREST) || | 
 |             (param == GL_LINEAR)) { | 
 |             textureObject->mag_filter = param; | 
 |         } else { | 
 |             goto invalid_enum; | 
 |         } | 
 |         break; | 
 |     case GL_GENERATE_MIPMAP: | 
 |         textureObject->generate_mipmap = param; | 
 |         break; | 
 |     default: | 
 | invalid_enum: | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     invalidate_texture(c, c->textures.active); | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, | 
 |         ogles_context_t* c) | 
 | { | 
 |     ogles_lock_textures(c); | 
 |  | 
 |     const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; | 
 |     y = gglIntToFixed(cbSurface.height) - (y + h); | 
 |     w >>= FIXED_BITS; | 
 |     h >>= FIXED_BITS; | 
 |  | 
 |     // set up all texture units | 
 |     for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { | 
 |         if (!c->rasterizer.state.texture[i].enable) | 
 |             continue; | 
 |  | 
 |         int32_t texcoords[8]; | 
 |         texture_unit_t& u(c->textures.tmu[i]); | 
 |  | 
 |         // validate this tmu (bind, wrap, filter) | 
 |         validate_tmu(c, i); | 
 |         // we CLAMP here, which works with premultiplied (s,t) | 
 |         c->rasterizer.procs.texParameteri(c, | 
 |                 GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP); | 
 |         c->rasterizer.procs.texParameteri(c, | 
 |                 GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP); | 
 |         u.dirty = 0xFF; // XXX: should be more subtle | 
 |  | 
 |         EGLTextureObject* textureObject = u.texture; | 
 |         const GLint Ucr = textureObject->crop_rect[0] << 16; | 
 |         const GLint Vcr = textureObject->crop_rect[1] << 16; | 
 |         const GLint Wcr = textureObject->crop_rect[2] << 16; | 
 |         const GLint Hcr = textureObject->crop_rect[3] << 16; | 
 |  | 
 |         // computes texture coordinates (pre-multiplied) | 
 |         int32_t dsdx = Wcr / w;   // dsdx =  ((Wcr/w)/Wt)*Wt | 
 |         int32_t dtdy =-Hcr / h;   // dtdy = -((Hcr/h)/Ht)*Ht | 
 |         int32_t s0   = Ucr       - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx | 
 |         int32_t t0   = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy | 
 |         texcoords[0] = s0; | 
 |         texcoords[1] = dsdx; | 
 |         texcoords[2] = 0; | 
 |         texcoords[3] = t0; | 
 |         texcoords[4] = 0; | 
 |         texcoords[5] = dtdy; | 
 |         texcoords[6] = 0; | 
 |         texcoords[7] = 0; | 
 |         c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords); | 
 |     } | 
 |  | 
 |     const uint32_t enables = c->rasterizer.state.enables; | 
 |     if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) | 
 |         set_depth_and_fog(c, z); | 
 |  | 
 |     c->rasterizer.procs.activeTexture(c, c->textures.active); | 
 |     c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); | 
 |     c->rasterizer.procs.disable(c, GGL_W_LERP); | 
 |     c->rasterizer.procs.disable(c, GGL_AA); | 
 |     c->rasterizer.procs.shadeModel(c, GL_FLAT); | 
 |     c->rasterizer.procs.recti(c, | 
 |             gglFixedToIntRound(x), | 
 |             gglFixedToIntRound(y), | 
 |             gglFixedToIntRound(x)+w, | 
 |             gglFixedToIntRound(y)+h); | 
 |  | 
 |     ogles_unlock_textures(c); | 
 | } | 
 |  | 
 | static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, | 
 |         ogles_context_t* c) | 
 | { | 
 |     // quickly reject empty rects | 
 |     if ((w|h) <= 0) | 
 |         return; | 
 |  | 
 |     drawTexxOESImp(x, y, z, w, h, c); | 
 | } | 
 |  | 
 | static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c) | 
 | { | 
 |     // All coordinates are integer, so if we have only one | 
 |     // texture unit active and no scaling is required | 
 |     // THEN, we can use our special 1:1 mapping | 
 |     // which is a lot faster. | 
 |  | 
 |     if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) { | 
 |         const int tmu = 0; | 
 |         texture_unit_t& u(c->textures.tmu[tmu]); | 
 |         EGLTextureObject* textureObject = u.texture; | 
 |         const GLint Wcr = textureObject->crop_rect[2]; | 
 |         const GLint Hcr = textureObject->crop_rect[3]; | 
 |  | 
 |         if ((w == Wcr) && (h == -Hcr)) { | 
 |             if ((w|h) <= 0) return; // quickly reject empty rects | 
 |  | 
 |             if (u.dirty) { | 
 |                 c->rasterizer.procs.activeTexture(c, tmu); | 
 |                 c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); | 
 |                 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, | 
 |                         GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); | 
 |                 c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, | 
 |                         GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); | 
 |             } | 
 |             c->rasterizer.procs.texGeni(c, GGL_S, | 
 |                     GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); | 
 |             c->rasterizer.procs.texGeni(c, GGL_T, | 
 |                     GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); | 
 |             u.dirty = 0xFF; // XXX: should be more subtle | 
 |             c->rasterizer.procs.activeTexture(c, c->textures.active); | 
 |  | 
 |             const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; | 
 |             y = cbSurface.height - (y + h); | 
 |             const GLint Ucr = textureObject->crop_rect[0]; | 
 |             const GLint Vcr = textureObject->crop_rect[1]; | 
 |             const GLint s0  = Ucr - x; | 
 |             const GLint t0  = (Vcr + Hcr) - y; | 
 |  | 
 |             const GLuint tw = textureObject->surface.width; | 
 |             const GLuint th = textureObject->surface.height; | 
 |             if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) { | 
 |                 // The GL spec is unclear about what should happen | 
 |                 // in this case, so we just use the slow case, which | 
 |                 // at least won't crash | 
 |                 goto slow_case; | 
 |             } | 
 |  | 
 |             ogles_lock_textures(c); | 
 |  | 
 |             c->rasterizer.procs.texCoord2i(c, s0, t0); | 
 |             const uint32_t enables = c->rasterizer.state.enables; | 
 |             if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) | 
 |                 set_depth_and_fog(c, gglIntToFixed(z)); | 
 |  | 
 |             c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); | 
 |             c->rasterizer.procs.disable(c, GGL_W_LERP); | 
 |             c->rasterizer.procs.disable(c, GGL_AA); | 
 |             c->rasterizer.procs.shadeModel(c, GL_FLAT); | 
 |             c->rasterizer.procs.recti(c, x, y, x+w, y+h); | 
 |  | 
 |             ogles_unlock_textures(c); | 
 |  | 
 |             return; | 
 |         } | 
 |     } | 
 |  | 
 | slow_case: | 
 |     drawTexxOESImp( | 
 |             gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z), | 
 |             gglIntToFixed(w), gglIntToFixed(h), | 
 |             c); | 
 | } | 
 |  | 
 |  | 
 | }; // namespace android | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | using namespace android; | 
 |  | 
 |  | 
 | #if 0 | 
 | #pragma mark - | 
 | #pragma mark Texture API | 
 | #endif | 
 |  | 
 | void glActiveTexture(GLenum texture) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     c->textures.active = texture - GL_TEXTURE0; | 
 |     c->rasterizer.procs.activeTexture(c, c->textures.active); | 
 | } | 
 |  | 
 | void glBindTexture(GLenum target, GLuint texture) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |  | 
 |     // Bind or create a texture | 
 |     sp<EGLTextureObject> tex; | 
 |     if (texture == 0) { | 
 |         // 0 is our local texture object | 
 |         tex = c->textures.defaultTexture; | 
 |     } else { | 
 |         tex = c->surfaceManager->texture(texture); | 
 |         if (ggl_unlikely(tex == 0)) { | 
 |             tex = c->surfaceManager->createTexture(texture); | 
 |             if (tex == 0) { | 
 |                 ogles_error(c, GL_OUT_OF_MEMORY); | 
 |                 return; | 
 |             } | 
 |         } | 
 |     } | 
 |     bindTextureTmu(c, c->textures.active, texture, tex); | 
 | } | 
 |  | 
 | void glGenTextures(GLsizei n, GLuint *textures) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (n<0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     // generate unique (shared) texture names | 
 |     c->surfaceManager->getToken(n, textures); | 
 | } | 
 |  | 
 | void glDeleteTextures(GLsizei n, const GLuint *textures) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (n<0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     // If deleting a bound texture, bind this unit to 0 | 
 |     for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) { | 
 |         if (c->textures.tmu[t].name == 0) | 
 |             continue; | 
 |         for (int i=0 ; i<n ; i++) { | 
 |             if (textures[i] && (textures[i] == c->textures.tmu[t].name)) { | 
 |                 // bind this tmu to texture 0 | 
 |                 sp<EGLTextureObject> tex(c->textures.defaultTexture); | 
 |                 bindTextureTmu(c, t, 0, tex); | 
 |             } | 
 |         } | 
 |     } | 
 |     c->surfaceManager->deleteTextures(n, textures); | 
 |     c->surfaceManager->recycleTokens(n, textures); | 
 | } | 
 |  | 
 | void glMultiTexCoord4f( | 
 |         GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     const int tmu = target-GL_TEXTURE0; | 
 |     c->current.texture[tmu].S = gglFloatToFixed(s); | 
 |     c->current.texture[tmu].T = gglFloatToFixed(t); | 
 |     c->current.texture[tmu].R = gglFloatToFixed(r); | 
 |     c->current.texture[tmu].Q = gglFloatToFixed(q); | 
 | } | 
 |  | 
 | void glMultiTexCoord4x( | 
 |         GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     const int tmu = target-GL_TEXTURE0; | 
 |     c->current.texture[tmu].S = s; | 
 |     c->current.texture[tmu].T = t; | 
 |     c->current.texture[tmu].R = r; | 
 |     c->current.texture[tmu].Q = q; | 
 | } | 
 |  | 
 | void glPixelStorei(GLenum pname, GLint param) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if ((param<=0 || param>8) || (param & (param-1))) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (pname == GL_PACK_ALIGNMENT) | 
 |         c->textures.packAlignment = param; | 
 |     if (pname == GL_UNPACK_ALIGNMENT) | 
 |         c->textures.unpackAlignment = param; | 
 | } | 
 |  | 
 | void glTexEnvf(GLenum target, GLenum pname, GLfloat param) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     c->rasterizer.procs.texEnvi(c, target, pname, GLint(param)); | 
 | } | 
 |  | 
 | void glTexEnvfv( | 
 |         GLenum target, GLenum pname, const GLfloat *params) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (pname == GL_TEXTURE_ENV_MODE) { | 
 |         c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params)); | 
 |         return; | 
 |     } | 
 |     if (pname == GL_TEXTURE_ENV_COLOR) { | 
 |         GGLfixed fixed[4]; | 
 |         for (int i=0 ; i<4 ; i++) | 
 |             fixed[i] = gglFloatToFixed(params[i]); | 
 |         c->rasterizer.procs.texEnvxv(c, target, pname, fixed); | 
 |         return; | 
 |     } | 
 |     ogles_error(c, GL_INVALID_ENUM); | 
 | } | 
 |  | 
 | void glTexEnvx(GLenum target, GLenum pname, GLfixed param) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     c->rasterizer.procs.texEnvi(c, target, pname, param); | 
 | } | 
 |  | 
 | void glTexEnvxv( | 
 |         GLenum target, GLenum pname, const GLfixed *params) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     c->rasterizer.procs.texEnvxv(c, target, pname, params); | 
 | } | 
 |  | 
 | void glTexParameteriv( | 
 |         GLenum target, GLenum pname, const GLint* params) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GGL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |  | 
 |     EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; | 
 |     switch (pname) { | 
 |     case GL_TEXTURE_CROP_RECT_OES: | 
 |         memcpy(textureObject->crop_rect, params, 4*sizeof(GLint)); | 
 |         break; | 
 |     default: | 
 |         texParameterx(target, pname, GLfixed(params[0]), c); | 
 |         return; | 
 |     } | 
 | } | 
 |  | 
 | void glTexParameterf( | 
 |         GLenum target, GLenum pname, GLfloat param) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     texParameterx(target, pname, GLfixed(param), c); | 
 | } | 
 |  | 
 | void glTexParameterx( | 
 |         GLenum target, GLenum pname, GLfixed param) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     texParameterx(target, pname, param, c); | 
 | } | 
 |  | 
 | void glTexParameteri( | 
 |         GLenum target, GLenum pname, GLint param) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     texParameterx(target, pname, GLfixed(param), c); | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 | #if 0 | 
 | #pragma mark - | 
 | #endif | 
 |  | 
 | void glCompressedTexImage2D( | 
 |         GLenum target, GLint level, GLenum internalformat, | 
 |         GLsizei width, GLsizei height, GLint border, | 
 |         GLsizei imageSize, const GLvoid *data) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if (width<0 || height<0 || border!=0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     // "uncompress" the texture since pixelflinger doesn't support | 
 |     // any compressed texture format natively. | 
 |     GLenum format; | 
 |     GLenum type; | 
 |     switch (internalformat) { | 
 |     case GL_PALETTE8_RGB8_OES: | 
 |     case GL_PALETTE4_RGB8_OES: | 
 |         format      = GL_RGB; | 
 |         type        = GL_UNSIGNED_BYTE; | 
 |         break; | 
 |     case GL_PALETTE8_RGBA8_OES: | 
 |     case GL_PALETTE4_RGBA8_OES: | 
 |         format      = GL_RGBA; | 
 |         type        = GL_UNSIGNED_BYTE; | 
 |         break; | 
 |     case GL_PALETTE8_R5_G6_B5_OES: | 
 |     case GL_PALETTE4_R5_G6_B5_OES: | 
 |         format      = GL_RGB; | 
 |         type        = GL_UNSIGNED_SHORT_5_6_5; | 
 |         break; | 
 |     case GL_PALETTE8_RGBA4_OES: | 
 |     case GL_PALETTE4_RGBA4_OES: | 
 |         format      = GL_RGBA; | 
 |         type        = GL_UNSIGNED_SHORT_4_4_4_4; | 
 |         break; | 
 |     case GL_PALETTE8_RGB5_A1_OES: | 
 |     case GL_PALETTE4_RGB5_A1_OES: | 
 |         format      = GL_RGBA; | 
 |         type        = GL_UNSIGNED_SHORT_5_5_5_1; | 
 |         break; | 
 | #ifdef GL_OES_compressed_ETC1_RGB8_texture | 
 |     case GL_ETC1_RGB8_OES: | 
 |         format      = GL_RGB; | 
 |         type        = GL_UNSIGNED_BYTE; | 
 |         break; | 
 | #endif | 
 |     default: | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |  | 
 |     if (!data || !width || !height) { | 
 |         // unclear if this is an error or not... | 
 |         return; | 
 |     } | 
 |  | 
 |     int32_t size; | 
 |     GGLSurface* surface; | 
 |  | 
 | #ifdef GL_OES_compressed_ETC1_RGB8_texture | 
 |     if (internalformat == GL_ETC1_RGB8_OES) { | 
 |         GLsizei compressedSize = etc1_get_encoded_data_size(width, height); | 
 |         if (compressedSize > imageSize) { | 
 |             ogles_error(c, GL_INVALID_VALUE); | 
 |             return; | 
 |         } | 
 |         int error = createTextureSurface(c, &surface, &size, | 
 |                 level, format, type, width, height); | 
 |         if (error) { | 
 |             ogles_error(c, error); | 
 |             return; | 
 |         } | 
 |         if (etc1_decode_image( | 
 |                 (const etc1_byte*)data, | 
 |                 (etc1_byte*)surface->data, | 
 |                 width, height, 3, surface->stride*3) != 0) { | 
 |             ogles_error(c, GL_INVALID_OPERATION); | 
 |         } | 
 |         return; | 
 |     } | 
 | #endif | 
 |  | 
 |     // all mipmap levels are specified at once. | 
 |     const int numLevels = level<0 ? -level : 1; | 
 |  | 
 |     if (dataSizePalette4(numLevels, width, height, format) > imageSize) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     for (int i=0 ; i<numLevels ; i++) { | 
 |         int lod_w = (width  >> i) ? : 1; | 
 |         int lod_h = (height >> i) ? : 1; | 
 |         int error = createTextureSurface(c, &surface, &size, | 
 |                 i, format, type, lod_w, lod_h); | 
 |         if (error) { | 
 |             ogles_error(c, error); | 
 |             return; | 
 |         } | 
 |         decodePalette4(data, i, width, height, | 
 |                 surface->data, surface->stride, internalformat); | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | void glTexImage2D( | 
 |         GLenum target, GLint level, GLint internalformat, | 
 |         GLsizei width, GLsizei height, GLint border, | 
 |         GLenum format, GLenum type, const GLvoid *pixels) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if (width<0 || height<0 || border!=0 || level < 0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (format != (GLenum)internalformat) { | 
 |         ogles_error(c, GL_INVALID_OPERATION); | 
 |         return; | 
 |     } | 
 |     if (validFormatType(c, format, type)) { | 
 |         return; | 
 |     } | 
 |  | 
 |     int32_t size = 0; | 
 |     GGLSurface* surface = 0; | 
 |     int error = createTextureSurface(c, &surface, &size, | 
 |             level, format, type, width, height); | 
 |     if (error) { | 
 |         ogles_error(c, error); | 
 |         return; | 
 |     } | 
 |  | 
 |     if (pixels) { | 
 |         const int32_t formatIdx = convertGLPixelFormat(format, type); | 
 |         const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); | 
 |         const int32_t align = c->textures.unpackAlignment-1; | 
 |         const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; | 
 |         const size_t size = bpr * height; | 
 |         const int32_t stride = bpr / pixelFormat.size; | 
 |  | 
 |         GGLSurface userSurface; | 
 |         userSurface.version = sizeof(userSurface); | 
 |         userSurface.width  = width; | 
 |         userSurface.height = height; | 
 |         userSurface.stride = stride; | 
 |         userSurface.format = formatIdx; | 
 |         userSurface.compressedFormat = 0; | 
 |         userSurface.data = (GLubyte*)pixels; | 
 |  | 
 |         int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height); | 
 |         if (err) { | 
 |             ogles_error(c, err); | 
 |             return; | 
 |         } | 
 |         generateMipmap(c, level); | 
 |     } | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | void glCompressedTexSubImage2D( | 
 |         GLenum target, GLint level, GLint xoffset, | 
 |         GLint yoffset, GLsizei width, GLsizei height, | 
 |         GLenum format, GLsizei imageSize, | 
 |         const GLvoid *data) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     ogles_error(c, GL_INVALID_ENUM); | 
 | } | 
 |  | 
 | void glTexSubImage2D( | 
 |         GLenum target, GLint level, GLint xoffset, | 
 |         GLint yoffset, GLsizei width, GLsizei height, | 
 |         GLenum format, GLenum type, const GLvoid *pixels) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (validFormatType(c, format, type)) { | 
 |         return; | 
 |     } | 
 |  | 
 |     // find out which texture is bound to the current unit | 
 |     const int active = c->textures.active; | 
 |     EGLTextureObject* tex = c->textures.tmu[active].texture; | 
 |     const GGLSurface& surface(tex->mip(level)); | 
 |  | 
 |     if (!tex->internalformat || tex->direct) { | 
 |         ogles_error(c, GL_INVALID_OPERATION); | 
 |         return; | 
 |     } | 
 |  | 
 |     if (format != tex->internalformat) { | 
 |         ogles_error(c, GL_INVALID_OPERATION); | 
 |         return; | 
 |     } | 
 |     if ((xoffset + width  > GLsizei(surface.width)) || | 
 |         (yoffset + height > GLsizei(surface.height))) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (!width || !height) { | 
 |         return; // okay, but no-op. | 
 |     } | 
 |  | 
 |     // figure out the size we need as well as the stride | 
 |     const int32_t formatIdx = convertGLPixelFormat(format, type); | 
 |     if (formatIdx == 0) { // we don't know what to do with this | 
 |         ogles_error(c, GL_INVALID_OPERATION); | 
 |         return; | 
 |     } | 
 |  | 
 |     const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); | 
 |     const int32_t align = c->textures.unpackAlignment-1; | 
 |     const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; | 
 |     const size_t size = bpr * height; | 
 |     const int32_t stride = bpr / pixelFormat.size; | 
 |     GGLSurface userSurface; | 
 |     userSurface.version = sizeof(userSurface); | 
 |     userSurface.width  = width; | 
 |     userSurface.height = height; | 
 |     userSurface.stride = stride; | 
 |     userSurface.format = formatIdx; | 
 |     userSurface.compressedFormat = 0; | 
 |     userSurface.data = (GLubyte*)pixels; | 
 |  | 
 |     int err = copyPixels(c, | 
 |             surface, xoffset, yoffset, | 
 |             userSurface, 0, 0, width, height); | 
 |     if (err) { | 
 |         ogles_error(c, err); | 
 |         return; | 
 |     } | 
 |  | 
 |     generateMipmap(c, level); | 
 |  | 
 |     // since we only changed the content of the texture, we don't need | 
 |     // to call bindTexture on the main rasterizer. | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 |  | 
 | void glCopyTexImage2D( | 
 |         GLenum target, GLint level, GLenum internalformat, | 
 |         GLint x, GLint y, GLsizei width, GLsizei height, | 
 |         GLint border) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if (internalformat<GL_ALPHA || internalformat>GL_LUMINANCE_ALPHA) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if (width<0 || height<0 || border!=0 || level<0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     GLenum format = 0; | 
 |     GLenum type = GL_UNSIGNED_BYTE; | 
 |     const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; | 
 |     const int cbFormatIdx = cbSurface.format; | 
 |     switch (cbFormatIdx) { | 
 |     case GGL_PIXEL_FORMAT_RGB_565: | 
 |         type = GL_UNSIGNED_SHORT_5_6_5; | 
 |         break; | 
 |     case GGL_PIXEL_FORMAT_RGBA_5551: | 
 |         type = GL_UNSIGNED_SHORT_5_5_5_1; | 
 |         break; | 
 |     case GGL_PIXEL_FORMAT_RGBA_4444: | 
 |         type = GL_UNSIGNED_SHORT_4_4_4_4; | 
 |         break; | 
 |     } | 
 |     switch (internalformat) { | 
 |     case GL_ALPHA: | 
 |     case GL_LUMINANCE_ALPHA: | 
 |     case GL_LUMINANCE: | 
 |         type = GL_UNSIGNED_BYTE; | 
 |         break; | 
 |     } | 
 |  | 
 |     // figure out the format to use for the new texture | 
 |     switch (cbFormatIdx) { | 
 |     case GGL_PIXEL_FORMAT_RGBA_8888: | 
 |     case GGL_PIXEL_FORMAT_A_8: | 
 |     case GGL_PIXEL_FORMAT_RGBA_5551: | 
 |     case GGL_PIXEL_FORMAT_RGBA_4444: | 
 |         format = internalformat; | 
 |         break; | 
 |     case GGL_PIXEL_FORMAT_RGBX_8888: | 
 |     case GGL_PIXEL_FORMAT_RGB_888: | 
 |     case GGL_PIXEL_FORMAT_RGB_565: | 
 |     case GGL_PIXEL_FORMAT_L_8: | 
 |         switch (internalformat) { | 
 |         case GL_LUMINANCE: | 
 |         case GL_RGB: | 
 |             format = internalformat; | 
 |             break; | 
 |         } | 
 |         break; | 
 |     } | 
 |  | 
 |     if (format == 0) { | 
 |         // invalid combination | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |  | 
 |     // create the new texture... | 
 |     int32_t size; | 
 |     GGLSurface* surface; | 
 |     int error = createTextureSurface(c, &surface, &size, | 
 |             level, format, type, width, height); | 
 |     if (error) { | 
 |         ogles_error(c, error); | 
 |         return; | 
 |     } | 
 |  | 
 |     // The bottom row is stored first in textures | 
 |     GGLSurface txSurface(*surface); | 
 |     txSurface.stride = -txSurface.stride; | 
 |  | 
 |     // (x,y) is the lower-left corner of colorBuffer | 
 |     y = cbSurface.height - (y + height); | 
 |  | 
 |     /* The GLES spec says: | 
 |      * If any of the pixels within the specified rectangle are outside | 
 |      * the framebuffer associated with the current rendering context, | 
 |      * then the values obtained for those pixels are undefined. | 
 |      */ | 
 |     if (x+width > GLint(cbSurface.width)) | 
 |         width = cbSurface.width - x; | 
 |  | 
 |     if (y+height > GLint(cbSurface.height)) | 
 |         height = cbSurface.height - y; | 
 |  | 
 |     int err = copyPixels(c, | 
 |             txSurface, 0, 0, | 
 |             cbSurface, x, y, width, height); | 
 |     if (err) { | 
 |         ogles_error(c, err); | 
 |     } | 
 |  | 
 |     generateMipmap(c, level); | 
 | } | 
 |  | 
 | void glCopyTexSubImage2D( | 
 |         GLenum target, GLint level, GLint xoffset, GLint yoffset, | 
 |         GLint x, GLint y, GLsizei width, GLsizei height) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (!width || !height) { | 
 |         return; // okay, but no-op. | 
 |     } | 
 |  | 
 |     // find out which texture is bound to the current unit | 
 |     const int active = c->textures.active; | 
 |     EGLTextureObject* tex = c->textures.tmu[active].texture; | 
 |     const GGLSurface& surface(tex->mip(level)); | 
 |  | 
 |     if (!tex->internalformat) { | 
 |         ogles_error(c, GL_INVALID_OPERATION); | 
 |         return; | 
 |     } | 
 |     if ((xoffset + width  > GLsizei(surface.width)) || | 
 |         (yoffset + height > GLsizei(surface.height))) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     // The bottom row is stored first in textures | 
 |     GGLSurface txSurface(surface); | 
 |     txSurface.stride = -txSurface.stride; | 
 |  | 
 |     // (x,y) is the lower-left corner of colorBuffer | 
 |     const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; | 
 |     y = cbSurface.height - (y + height); | 
 |  | 
 |     /* The GLES spec says: | 
 |      * If any of the pixels within the specified rectangle are outside | 
 |      * the framebuffer associated with the current rendering context, | 
 |      * then the values obtained for those pixels are undefined. | 
 |      */ | 
 |     if (x+width > GLint(cbSurface.width)) | 
 |         width = cbSurface.width - x; | 
 |  | 
 |     if (y+height > GLint(cbSurface.height)) | 
 |         height = cbSurface.height - y; | 
 |  | 
 |     int err = copyPixels(c, | 
 |             txSurface, xoffset, yoffset, | 
 |             cbSurface, x, y, width, height); | 
 |     if (err) { | 
 |         ogles_error(c, err); | 
 |         return; | 
 |     } | 
 |  | 
 |     generateMipmap(c, level); | 
 | } | 
 |  | 
 | void glReadPixels( | 
 |         GLint x, GLint y, GLsizei width, GLsizei height, | 
 |         GLenum format, GLenum type, GLvoid *pixels) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if ((format != GL_RGBA) && (format != GL_RGB)) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |     if (width<0 || height<0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (x<0 || y<0) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     int32_t formatIdx = GGL_PIXEL_FORMAT_NONE; | 
 |     if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) { | 
 |         formatIdx = GGL_PIXEL_FORMAT_RGBA_8888; | 
 |     } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) { | 
 |         formatIdx = GGL_PIXEL_FORMAT_RGB_565; | 
 |     } else { | 
 |         ogles_error(c, GL_INVALID_OPERATION); | 
 |         return; | 
 |     } | 
 |  | 
 |     const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s; | 
 |     if ((x+width > GLint(readSurface.width)) || | 
 |             (y+height > GLint(readSurface.height))) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); | 
 |     const int32_t align = c->textures.packAlignment-1; | 
 |     const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; | 
 |     const int32_t stride = bpr / pixelFormat.size; | 
 |  | 
 |     GGLSurface userSurface; | 
 |     userSurface.version = sizeof(userSurface); | 
 |     userSurface.width  = width; | 
 |     userSurface.height = height; | 
 |     userSurface.stride = -stride; // bottom row is transfered first | 
 |     userSurface.format = formatIdx; | 
 |     userSurface.compressedFormat = 0; | 
 |     userSurface.data = (GLubyte*)pixels; | 
 |  | 
 |     // use pixel-flinger to handle all the conversions | 
 |     GGLContext* ggl = getRasterizer(c); | 
 |     if (!ggl) { | 
 |         // the only reason this would fail is because we ran out of memory | 
 |         ogles_error(c, GL_OUT_OF_MEMORY); | 
 |         return; | 
 |     } | 
 |  | 
 |     ggl->colorBuffer(ggl, &userSurface);  // destination is user buffer | 
 |     ggl->bindTexture(ggl, &readSurface);  // source is read-buffer | 
 |     ggl->texCoord2i(ggl, x, readSurface.height - (y + height)); | 
 |     ggl->recti(ggl, 0, 0, width, height); | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 | #if 0 | 
 | #pragma mark - | 
 | #pragma mark DrawTexture Extension | 
 | #endif | 
 |  | 
 | void glDrawTexsvOES(const GLshort* coords) { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); | 
 | } | 
 | void glDrawTexivOES(const GLint* coords) { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); | 
 | } | 
 | void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexiOES(x, y, z, w, h, c); | 
 | } | 
 | void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexiOES(x, y, z, w, h, c); | 
 | } | 
 |  | 
 | void glDrawTexfvOES(const GLfloat* coords) { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexxOES( | 
 |             gglFloatToFixed(coords[0]), | 
 |             gglFloatToFixed(coords[1]), | 
 |             gglFloatToFixed(coords[2]), | 
 |             gglFloatToFixed(coords[3]), | 
 |             gglFloatToFixed(coords[4]), | 
 |             c); | 
 | } | 
 | void glDrawTexxvOES(const GLfixed* coords) { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); | 
 | } | 
 | void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){ | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexxOES( | 
 |             gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z), | 
 |             gglFloatToFixed(w), gglFloatToFixed(h), | 
 |             c); | 
 | } | 
 | void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     drawTexxOES(x, y, z, w, h, c); | 
 | } | 
 |  | 
 | // ---------------------------------------------------------------------------- | 
 | #if 0 | 
 | #pragma mark - | 
 | #pragma mark EGL Image Extension | 
 | #endif | 
 |  | 
 | void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_TEXTURE_2D) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |  | 
 |     if (image == EGL_NO_IMAGE_KHR) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image; | 
 |     if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     // bind it to the texture unit | 
 |     sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); | 
 |     tex->setImage(native_buffer); | 
 | } | 
 |  | 
 | void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) | 
 | { | 
 |     ogles_context_t* c = ogles_context_t::get(); | 
 |     if (target != GL_RENDERBUFFER_OES) { | 
 |         ogles_error(c, GL_INVALID_ENUM); | 
 |         return; | 
 |     } | 
 |  | 
 |     if (image == EGL_NO_IMAGE_KHR) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image; | 
 |     if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |     if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) { | 
 |         ogles_error(c, GL_INVALID_VALUE); | 
 |         return; | 
 |     } | 
 |  | 
 |     // well, we're not supporting this extension anyways | 
 | } |