| /* 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 <ETC1/etc1.h> |
| |
| #include <ui/GraphicBufferMapper.h> |
| #include <ui/Rect.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); |
| |
| auto& mapper = GraphicBufferMapper::get(); |
| void* vaddr; |
| mapper.lock(native_buffer->handle, GRALLOC_USAGE_SW_READ_OFTEN, |
| Rect(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); |
| |
| auto& mapper = GraphicBufferMapper::get(); |
| mapper.unlock(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) |
| { |
| // 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 GLsizei 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 && target != GL_TEXTURE_EXTERNAL_OES) { |
| 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 && target != GL_TEXTURE_EXTERNAL_OES) { |
| 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 != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) { |
| 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 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 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 && target != GL_TEXTURE_EXTERNAL_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; |
| } |
| |
| // 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 |
| } |