diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /opengl/libagl/texture.cpp | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'opengl/libagl/texture.cpp')
-rw-r--r-- | opengl/libagl/texture.cpp | 1421 |
1 files changed, 1421 insertions, 0 deletions
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp new file mode 100644 index 00000000000..b6f534b7ea5 --- /dev/null +++ b/opengl/libagl/texture.cpp @@ -0,0 +1,1421 @@ +/* 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" + +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_impl(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 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 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, GLint 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>=1) 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 drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, + ogles_context_t* c) +{ + // quickly reject empty rects + if ((w|h) <= 0) + return; + + 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); +} + +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; + } + + 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, 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); + return; + } + } + +slow_case: + drawTexxOES( + 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: + ogles_error(c, GL_INVALID_ENUM); + 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); +} + +// ---------------------------------------------------------------------------- +#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 ((internalformat < GL_PALETTE4_RGB8_OES || + internalformat > GL_PALETTE8_RGB5_A1_OES)) { + 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; + 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; + // all mipmap levels are specified at once. + const int numLevels = level<0 ? -level : 1; + 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 && target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + 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 != internalformat) { + ogles_error(c, GL_INVALID_OPERATION); + return; + } + if (validFormatType(c, format, type)) { + return; + } + + int32_t size = 0; + GGLSurface* surface = 0; + if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + int error = createTextureSurface(c, &surface, &size, + level, format, type, width, height); + if (error) { + ogles_error(c, error); + return; + } + } else if (pixels == 0 || level != 0) { + // pixel can't be null for direct texture + ogles_error(c, GL_INVALID_OPERATION); + 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; + + if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) { + int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height); + if (err) { + ogles_error(c, err); + return; + } + generateMipmap(c, level); + } else { + // bind it to the texture unit + sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); + tex->setSurface(&userSurface); + } + } +} + +// ---------------------------------------------------------------------------- + +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 ((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); + + int err = copyPixels(c, + txSurface, 0, 0, + cbSurface, x, y, cbSurface.width, cbSurface.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); + + int err = copyPixels(c, + surface, 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 || x<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); +} |