1424 lines
45 KiB
C++
1424 lines
45 KiB
C++
/* 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 != GGL_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_CLAMP) ||
|
|
(param == GL_REPEAT) ||
|
|
(param == GL_CLAMP_TO_EDGE)) {
|
|
textureObject->wraps = param;
|
|
} else {
|
|
goto invalid_enum;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_WRAP_T:
|
|
if ((param == GGL_CLAMP) ||
|
|
(param == GGL_REPEAT) ||
|
|
(param == GGL_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, GLenum 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);
|
|
}
|