texture.cpp revision 7c1b96a165f970a09ed239bb4fb3f1b0d8f2a407
1/* libs/opengles/texture.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <stdio.h>
19#include <stdlib.h>
20#include "context.h"
21#include "fp.h"
22#include "state.h"
23#include "texture.h"
24#include "TextureObjectManager.h"
25
26namespace android {
27
28// ----------------------------------------------------------------------------
29
30static void bindTextureTmu(
31    ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex);
32
33static __attribute__((noinline))
34void generateMipmap(ogles_context_t* c, GLint level);
35
36// ----------------------------------------------------------------------------
37
38#if 0
39#pragma mark -
40#pragma mark Init
41#endif
42
43void ogles_init_texture(ogles_context_t* c)
44{
45    c->textures.packAlignment   = 4;
46    c->textures.unpackAlignment = 4;
47
48    // each context has a default named (0) texture (not shared)
49    c->textures.defaultTexture = new EGLTextureObject();
50    c->textures.defaultTexture->incStrong(c);
51
52    // bind the default texture to each texture unit
53    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
54        bindTextureTmu(c, i, 0, c->textures.defaultTexture);
55        memset(c->current.texture[i].v, 0, sizeof(vec4_t));
56        c->current.texture[i].Q = 0x10000;
57    }
58}
59
60void ogles_uninit_texture(ogles_context_t* c)
61{
62    if (c->textures.ggl)
63        gglUninit(c->textures.ggl);
64    c->textures.defaultTexture->decStrong(c);
65    for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
66        if (c->textures.tmu[i].texture)
67            c->textures.tmu[i].texture->decStrong(c);
68    }
69}
70
71static __attribute__((noinline))
72void validate_tmu(ogles_context_t* c, int i)
73{
74    texture_unit_t& u(c->textures.tmu[i]);
75    if (u.dirty) {
76        u.dirty = 0;
77        c->rasterizer.procs.activeTexture(c, i);
78        c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
79        c->rasterizer.procs.texGeni(c, GGL_S,
80                GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
81        c->rasterizer.procs.texGeni(c, GGL_T,
82                GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC);
83        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
84                GGL_TEXTURE_WRAP_S, u.texture->wraps);
85        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
86                GGL_TEXTURE_WRAP_T, u.texture->wrapt);
87        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
88                GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
89        c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
90                GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
91
92        // disable this texture unit if it's not complete
93        if (!u.texture->isComplete()) {
94            c->rasterizer.procs.disable(c, GGL_TEXTURE_2D);
95        }
96    }
97}
98
99void ogles_validate_texture_impl(ogles_context_t* c)
100{
101    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
102        if (c->rasterizer.state.texture[i].enable)
103            validate_tmu(c, i);
104    }
105    c->rasterizer.procs.activeTexture(c, c->textures.active);
106}
107
108static
109void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) {
110    c->textures.tmu[tmu].dirty = flags;
111}
112
113// ----------------------------------------------------------------------------
114#if 0
115#pragma mark -
116#pragma mark Format conversion
117#endif
118
119static uint32_t gl2format_table[6][4] = {
120    // BYTE, 565, 4444, 5551
121    { GGL_PIXEL_FORMAT_A_8,
122      0, 0, 0 },                        // GL_ALPHA
123    { GGL_PIXEL_FORMAT_RGB_888,
124      GGL_PIXEL_FORMAT_RGB_565,
125      0, 0 },                           // GL_RGB
126    { GGL_PIXEL_FORMAT_RGBA_8888,
127      0,
128      GGL_PIXEL_FORMAT_RGBA_4444,
129      GGL_PIXEL_FORMAT_RGBA_5551 },     // GL_RGBA
130    { GGL_PIXEL_FORMAT_L_8,
131      0, 0, 0 },                        // GL_LUMINANCE
132    { GGL_PIXEL_FORMAT_LA_88,
133      0, 0, 0 },                        // GL_LUMINANCE_ALPHA
134};
135
136static int32_t convertGLPixelFormat(GLint format, GLenum type)
137{
138    int32_t fi = -1;
139    int32_t ti = -1;
140    switch (format) {
141    case GL_ALPHA:              fi = 0;     break;
142    case GL_RGB:                fi = 1;     break;
143    case GL_RGBA:               fi = 2;     break;
144    case GL_LUMINANCE:          fi = 3;     break;
145    case GL_LUMINANCE_ALPHA:    fi = 4;     break;
146    }
147    switch (type) {
148    case GL_UNSIGNED_BYTE:          ti = 0; break;
149    case GL_UNSIGNED_SHORT_5_6_5:   ti = 1; break;
150    case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break;
151    case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break;
152    }
153    if (fi==-1 || ti==-1)
154        return 0;
155    return gl2format_table[fi][ti];
156}
157
158// ----------------------------------------------------------------------------
159
160static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type)
161{
162    GLenum error = 0;
163    if (format<GL_ALPHA || format>GL_LUMINANCE_ALPHA) {
164        error = GL_INVALID_ENUM;
165    }
166    if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 &&
167        type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) {
168        error = GL_INVALID_ENUM;
169    }
170    if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) {
171        error = GL_INVALID_OPERATION;
172    }
173    if ((type == GL_UNSIGNED_SHORT_4_4_4_4 ||
174         type == GL_UNSIGNED_SHORT_5_5_5_1)  && format != GL_RGBA) {
175        error = GL_INVALID_OPERATION;
176    }
177    if (error) {
178        ogles_error(c, error);
179    }
180    return error;
181}
182
183// ----------------------------------------------------------------------------
184
185GGLContext* getRasterizer(ogles_context_t* c)
186{
187    GGLContext* ggl = c->textures.ggl;
188    if (ggl_unlikely(!ggl)) {
189        // this is quite heavy the first time...
190        gglInit(&ggl);
191        if (!ggl) {
192            return 0;
193        }
194        GGLfixed colors[4] = { 0, 0, 0, 0x10000 };
195        c->textures.ggl = ggl;
196        ggl->activeTexture(ggl, 0);
197        ggl->enable(ggl, GGL_TEXTURE_2D);
198        ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE);
199        ggl->disable(ggl, GGL_DITHER);
200        ggl->shadeModel(ggl, GGL_FLAT);
201        ggl->color4xv(ggl, colors);
202    }
203    return ggl;
204}
205
206static __attribute__((noinline))
207int copyPixels(
208        ogles_context_t* c,
209        const GGLSurface& dst,
210        GLint xoffset, GLint yoffset,
211        const GGLSurface& src,
212        GLint x, GLint y, GLsizei w, GLsizei h)
213{
214    if ((dst.format == src.format) &&
215        (dst.stride == src.stride) &&
216        (dst.width == src.width) &&
217        (dst.height == src.height) &&
218        (dst.stride > 0) &&
219        ((x|y) == 0) &&
220        ((xoffset|yoffset) == 0))
221    {
222        // this is a common case...
223        const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]);
224        const size_t size = src.height * src.stride * pixelFormat.size;
225        memcpy(dst.data, src.data, size);
226        return 0;
227    }
228
229    // use pixel-flinger to handle all the conversions
230    GGLContext* ggl = getRasterizer(c);
231    if (!ggl) {
232        // the only reason this would fail is because we ran out of memory
233        return GL_OUT_OF_MEMORY;
234    }
235
236    ggl->colorBuffer(ggl, &dst);
237    ggl->bindTexture(ggl, &src);
238    ggl->texCoord2i(ggl, x-xoffset, y-yoffset);
239    ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h);
240    return 0;
241}
242
243// ----------------------------------------------------------------------------
244
245static __attribute__((noinline))
246sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c)
247{
248    sp<EGLTextureObject> tex;
249    const int active = c->textures.active;
250    const GLuint name = c->textures.tmu[active].name;
251
252    // free the reference to the previously bound object
253    texture_unit_t& u(c->textures.tmu[active]);
254    if (u.texture)
255        u.texture->decStrong(c);
256
257    if (name == 0) {
258        // 0 is our local texture object, not shared with anyone.
259        // But it affects all bound TMUs immediately.
260        // (we need to invalidate all units bound to this texture object)
261        tex = c->textures.defaultTexture;
262        for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
263            if (c->textures.tmu[i].texture == tex.get())
264                invalidate_texture(c, i);
265        }
266    } else {
267        // get a new texture object for that name
268        tex = c->surfaceManager->replaceTexture(name);
269    }
270
271    // bind this texture to the current active texture unit
272    // and add a reference to this texture object
273    u.texture = tex.get();
274    u.texture->incStrong(c);
275    u.name = name;
276    invalidate_texture(c, active);
277    return tex;
278}
279
280void bindTextureTmu(
281    ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex)
282{
283    if (tex.get() == c->textures.tmu[tmu].texture)
284        return;
285
286    // free the reference to the previously bound object
287    texture_unit_t& u(c->textures.tmu[tmu]);
288    if (u.texture)
289        u.texture->decStrong(c);
290
291    // bind this texture to the current active texture unit
292    // and add a reference to this texture object
293    u.texture = tex.get();
294    u.texture->incStrong(c);
295    u.name = texture;
296    invalidate_texture(c, tmu);
297}
298
299int createTextureSurface(ogles_context_t* c,
300        GGLSurface** outSurface, int32_t* outSize, GLint level,
301        GLenum format, GLenum type, GLsizei width, GLsizei height,
302        GLenum compressedFormat = 0)
303{
304    // find out which texture is bound to the current unit
305    const int active = c->textures.active;
306    const GLuint name = c->textures.tmu[active].name;
307
308    // convert the pixelformat to one we can handle
309    const int32_t formatIdx = convertGLPixelFormat(format, type);
310    if (formatIdx == 0) { // we don't know what to do with this
311        return GL_INVALID_OPERATION;
312    }
313
314    // figure out the size we need as well as the stride
315    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
316    const int32_t align = c->textures.unpackAlignment-1;
317    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
318    const size_t size = bpr * height;
319    const int32_t stride = bpr / pixelFormat.size;
320
321    if (level > 0) {
322        const int active = c->textures.active;
323        EGLTextureObject* tex = c->textures.tmu[active].texture;
324        status_t err = tex->reallocate(level,
325                width, height, stride, formatIdx, compressedFormat, bpr);
326        if (err != NO_ERROR)
327            return GL_OUT_OF_MEMORY;
328        GGLSurface& surface = tex->editMip(level);
329        *outSurface = &surface;
330        *outSize = size;
331        return 0;
332    }
333
334    sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
335    status_t err = tex->reallocate(level,
336            width, height, stride, formatIdx, compressedFormat, bpr);
337    if (err != NO_ERROR)
338        return GL_OUT_OF_MEMORY;
339
340    tex->internalformat = format;
341    *outSurface = &tex->surface;
342    *outSize = size;
343    return 0;
344}
345
346static void decodePalette4(const GLvoid *data, int level, int width, int height,
347                           void *surface, int stride, int format)
348
349{
350    int indexBits = 8;
351    int entrySize = 0;
352    switch (format) {
353    case GL_PALETTE4_RGB8_OES:
354        indexBits = 4;
355        /* FALLTHROUGH */
356    case GL_PALETTE8_RGB8_OES:
357        entrySize = 3;
358        break;
359
360    case GL_PALETTE4_RGBA8_OES:
361        indexBits = 4;
362        /* FALLTHROUGH */
363    case GL_PALETTE8_RGBA8_OES:
364        entrySize = 4;
365        break;
366
367    case GL_PALETTE4_R5_G6_B5_OES:
368    case GL_PALETTE4_RGBA4_OES:
369    case GL_PALETTE4_RGB5_A1_OES:
370        indexBits = 4;
371        /* FALLTHROUGH */
372    case GL_PALETTE8_R5_G6_B5_OES:
373    case GL_PALETTE8_RGBA4_OES:
374    case GL_PALETTE8_RGB5_A1_OES:
375        entrySize = 2;
376        break;
377    }
378
379    const int paletteSize = (1 << indexBits) * entrySize;
380    uint8_t const* pixels = (uint8_t *)data + paletteSize;
381    for (int i=0 ; i<level ; i++) {
382        int w = (width  >> i) ? : 1;
383        int h = (height >> i) ? : 1;
384        pixels += h * ((w * indexBits) / 8);
385    }
386    width  = (width  >> level) ? : 1;
387    height = (height >> level) ? : 1;
388
389    if (entrySize == 2) {
390        uint8_t const* const palette = (uint8_t*)data;
391        for (int y=0 ; y<height ; y++) {
392            uint8_t* p = (uint8_t*)surface + y*stride*2;
393            if (indexBits == 8) {
394                for (int x=0 ; x<width ; x++) {
395                    int index = 2 * (*pixels++);
396                    *p++ = palette[index + 0];
397                    *p++ = palette[index + 1];
398                }
399            } else {
400                for (int x=0 ; x<width ; x+=2) {
401                    int v = *pixels++;
402                    int index = 2 * (v >> 4);
403                    *p++ = palette[index + 0];
404                    *p++ = palette[index + 1];
405                    if (x+1 < width) {
406                        index = 2 * (v & 0xF);
407                        *p++ = palette[index + 0];
408                        *p++ = palette[index + 1];
409                    }
410                }
411            }
412        }
413    } else if (entrySize == 3) {
414        uint8_t const* const palette = (uint8_t*)data;
415        for (int y=0 ; y<height ; y++) {
416            uint8_t* p = (uint8_t*)surface + y*stride*3;
417            if (indexBits == 8) {
418                for (int x=0 ; x<width ; x++) {
419                    int index = 3 * (*pixels++);
420                    *p++ = palette[index + 0];
421                    *p++ = palette[index + 1];
422                    *p++ = palette[index + 2];
423                }
424            } else {
425                for (int x=0 ; x<width ; x+=2) {
426                    int v = *pixels++;
427                    int index = 3 * (v >> 4);
428                    *p++ = palette[index + 0];
429                    *p++ = palette[index + 1];
430                    *p++ = palette[index + 2];
431                    if (x+1 < width) {
432                        index = 3 * (v & 0xF);
433                        *p++ = palette[index + 0];
434                        *p++ = palette[index + 1];
435                        *p++ = palette[index + 2];
436                    }
437                }
438            }
439        }
440    } else if (entrySize == 4) {
441        uint8_t const* const palette = (uint8_t*)data;
442        for (int y=0 ; y<height ; y++) {
443            uint8_t* p = (uint8_t*)surface + y*stride*4;
444            if (indexBits == 8) {
445                for (int x=0 ; x<width ; x++) {
446                    int index = 4 * (*pixels++);
447                    *p++ = palette[index + 0];
448                    *p++ = palette[index + 1];
449                    *p++ = palette[index + 2];
450                    *p++ = palette[index + 3];
451                }
452            } else {
453                for (int x=0 ; x<width ; x+=2) {
454                    int v = *pixels++;
455                    int index = 4 * (v >> 4);
456                    *p++ = palette[index + 0];
457                    *p++ = palette[index + 1];
458                    *p++ = palette[index + 2];
459                    *p++ = palette[index + 3];
460                    if (x+1 < width) {
461                        index = 4 * (v & 0xF);
462                        *p++ = palette[index + 0];
463                        *p++ = palette[index + 1];
464                        *p++ = palette[index + 2];
465                        *p++ = palette[index + 3];
466                    }
467                }
468            }
469        }
470    }
471}
472
473
474
475static __attribute__((noinline))
476void set_depth_and_fog(ogles_context_t* c, GLint z)
477{
478    const uint32_t enables = c->rasterizer.state.enables;
479    // we need to compute Zw
480    int32_t iterators[3];
481    iterators[1] = iterators[2] = 0;
482    GGLfixed Zw;
483    GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear);
484    GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar);
485    if (z<=0)       Zw = n;
486    else if (z>=1)  Zw = f;
487    else            Zw = gglMulAddx(z, (f-n), n);
488    if (enables & GGL_ENABLE_FOG) {
489        // set up fog if needed...
490        iterators[0] = c->fog.fog(c, Zw);
491        c->rasterizer.procs.fogGrad3xv(c, iterators);
492    }
493    if (enables & GGL_ENABLE_DEPTH_TEST) {
494        // set up z-test if needed...
495        int32_t z = (Zw & ~(Zw>>31));
496        if (z >= 0x10000)
497            z = 0xFFFF;
498        iterators[0] = (z << 16) | z;
499        c->rasterizer.procs.zGrad3xv(c, iterators);
500    }
501}
502
503// ----------------------------------------------------------------------------
504#if 0
505#pragma mark -
506#pragma mark Generate mimaps
507#endif
508
509extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex);
510
511void generateMipmap(ogles_context_t* c, GLint level)
512{
513    if (level == 0) {
514        const int active = c->textures.active;
515        EGLTextureObject* tex = c->textures.tmu[active].texture;
516        if (tex->generate_mipmap) {
517            if (buildAPyramid(c, tex) != NO_ERROR) {
518                ogles_error(c, GL_OUT_OF_MEMORY);
519                return;
520            }
521        }
522    }
523}
524
525
526static void texParameterx(
527        GLenum target, GLenum pname, GLfixed param, ogles_context_t* c)
528{
529    if (target != GGL_TEXTURE_2D) {
530        ogles_error(c, GL_INVALID_ENUM);
531        return;
532    }
533
534    EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
535    switch (pname) {
536    case GL_TEXTURE_WRAP_S:
537        if ((param == GL_CLAMP) ||
538            (param == GL_REPEAT) ||
539            (param == GL_CLAMP_TO_EDGE)) {
540            textureObject->wraps = param;
541        } else {
542            goto invalid_enum;
543        }
544        break;
545    case GL_TEXTURE_WRAP_T:
546        if ((param == GGL_CLAMP) ||
547            (param == GGL_REPEAT) ||
548            (param == GGL_CLAMP_TO_EDGE)) {
549            textureObject->wrapt = param;
550        } else {
551            goto invalid_enum;
552        }
553        break;
554    case GL_TEXTURE_MIN_FILTER:
555        if ((param == GL_NEAREST) ||
556            (param == GL_LINEAR) ||
557            (param == GL_NEAREST_MIPMAP_NEAREST) ||
558            (param == GL_LINEAR_MIPMAP_NEAREST) ||
559            (param == GL_NEAREST_MIPMAP_LINEAR) ||
560            (param == GL_LINEAR_MIPMAP_LINEAR)) {
561            textureObject->min_filter = param;
562        } else {
563            goto invalid_enum;
564        }
565        break;
566    case GL_TEXTURE_MAG_FILTER:
567        if ((param == GL_NEAREST) ||
568            (param == GL_LINEAR)) {
569            textureObject->mag_filter = param;
570        } else {
571            goto invalid_enum;
572        }
573        break;
574    case GL_GENERATE_MIPMAP:
575        textureObject->generate_mipmap = param;
576        break;
577    default:
578invalid_enum:
579        ogles_error(c, GL_INVALID_ENUM);
580        return;
581    }
582    invalidate_texture(c, c->textures.active);
583}
584
585
586static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
587        ogles_context_t* c)
588{
589    // quickly reject empty rects
590    if ((w|h) <= 0)
591        return;
592
593    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
594    y = gglIntToFixed(cbSurface.height) - (y + h);
595    w >>= FIXED_BITS;
596    h >>= FIXED_BITS;
597
598    // set up all texture units
599    for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
600        if (!c->rasterizer.state.texture[i].enable)
601            continue;
602
603        int32_t texcoords[8];
604        texture_unit_t& u(c->textures.tmu[i]);
605
606        // validate this tmu (bind, wrap, filter)
607        validate_tmu(c, i);
608        // we CLAMP here, which works with premultiplied (s,t)
609        c->rasterizer.procs.texParameteri(c,
610                GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP);
611        c->rasterizer.procs.texParameteri(c,
612                GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
613        u.dirty = 0xFF; // XXX: should be more subtle
614
615        EGLTextureObject* textureObject = u.texture;
616        const GLint Ucr = textureObject->crop_rect[0] << 16;
617        const GLint Vcr = textureObject->crop_rect[1] << 16;
618        const GLint Wcr = textureObject->crop_rect[2] << 16;
619        const GLint Hcr = textureObject->crop_rect[3] << 16;
620
621        // computes texture coordinates (pre-multiplied)
622        int32_t dsdx = Wcr / w;   // dsdx =  ((Wcr/w)/Wt)*Wt
623        int32_t dtdy =-Hcr / h;   // dtdy = -((Hcr/h)/Ht)*Ht
624        int32_t s0   = Ucr       - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx
625        int32_t t0   = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy
626        texcoords[0] = s0;
627        texcoords[1] = dsdx;
628        texcoords[2] = 0;
629        texcoords[3] = t0;
630        texcoords[4] = 0;
631        texcoords[5] = dtdy;
632        texcoords[6] = 0;
633        texcoords[7] = 0;
634        c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords);
635    }
636
637    const uint32_t enables = c->rasterizer.state.enables;
638    if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
639        set_depth_and_fog(c, z);
640
641    c->rasterizer.procs.activeTexture(c, c->textures.active);
642    c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
643    c->rasterizer.procs.disable(c, GGL_W_LERP);
644    c->rasterizer.procs.disable(c, GGL_AA);
645    c->rasterizer.procs.shadeModel(c, GL_FLAT);
646    c->rasterizer.procs.recti(c,
647            gglFixedToIntRound(x),
648            gglFixedToIntRound(y),
649            gglFixedToIntRound(x)+w,
650            gglFixedToIntRound(y)+h);
651}
652
653static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c)
654{
655    // All coordinates are integer, so if we have only one
656    // texture unit active and no scaling is required
657    // THEN, we can use our special 1:1 mapping
658    // which is a lot faster.
659
660    if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) {
661        const int tmu = 0;
662        texture_unit_t& u(c->textures.tmu[tmu]);
663        EGLTextureObject* textureObject = u.texture;
664        const GLint Wcr = textureObject->crop_rect[2];
665        const GLint Hcr = textureObject->crop_rect[3];
666
667        if ((w == Wcr) && (h == -Hcr)) {
668            if ((w|h) <= 0) return; // quickly reject empty rects
669
670            if (u.dirty) {
671                c->rasterizer.procs.activeTexture(c, tmu);
672                c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
673                c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
674                        GGL_TEXTURE_MIN_FILTER, u.texture->min_filter);
675                c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D,
676                        GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter);
677            }
678            c->rasterizer.procs.texGeni(c, GGL_S,
679                    GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
680            c->rasterizer.procs.texGeni(c, GGL_T,
681                    GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
682            u.dirty = 0xFF; // XXX: should be more subtle
683            c->rasterizer.procs.activeTexture(c, c->textures.active);
684
685            const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
686            y = cbSurface.height - (y + h);
687            const GLint Ucr = textureObject->crop_rect[0];
688            const GLint Vcr = textureObject->crop_rect[1];
689            const GLint s0  = Ucr - x;
690            const GLint t0  = (Vcr + Hcr) - y;
691
692            const GLuint tw = textureObject->surface.width;
693            const GLuint th = textureObject->surface.height;
694            if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) {
695                // The GL spec is unclear about what should happen
696                // in this case, so we just use the slow case, which
697                // at least won't crash
698                goto slow_case;
699            }
700
701            c->rasterizer.procs.texCoord2i(c, s0, t0);
702            const uint32_t enables = c->rasterizer.state.enables;
703            if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)))
704                set_depth_and_fog(c, z);
705
706            c->rasterizer.procs.color4xv(c, c->currentColorClamped.v);
707            c->rasterizer.procs.disable(c, GGL_W_LERP);
708            c->rasterizer.procs.disable(c, GGL_AA);
709            c->rasterizer.procs.shadeModel(c, GL_FLAT);
710            c->rasterizer.procs.recti(c, x, y, x+w, y+h);
711            return;
712        }
713    }
714
715slow_case:
716    drawTexxOES(
717            gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z),
718            gglIntToFixed(w), gglIntToFixed(h),
719            c);
720}
721
722
723}; // namespace android
724// ----------------------------------------------------------------------------
725
726using namespace android;
727
728
729#if 0
730#pragma mark -
731#pragma mark Texture API
732#endif
733
734void glActiveTexture(GLenum texture)
735{
736    ogles_context_t* c = ogles_context_t::get();
737    if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
738        ogles_error(c, GL_INVALID_ENUM);
739        return;
740    }
741    c->textures.active = texture - GL_TEXTURE0;
742    c->rasterizer.procs.activeTexture(c, c->textures.active);
743}
744
745void glBindTexture(GLenum target, GLuint texture)
746{
747    ogles_context_t* c = ogles_context_t::get();
748    if (target != GL_TEXTURE_2D) {
749        ogles_error(c, GL_INVALID_ENUM);
750        return;
751    }
752
753    // Bind or create a texture
754    sp<EGLTextureObject> tex;
755    if (texture == 0) {
756        // 0 is our local texture object
757        tex = c->textures.defaultTexture;
758    } else {
759        tex = c->surfaceManager->texture(texture);
760        if (ggl_unlikely(tex == 0)) {
761            tex = c->surfaceManager->createTexture(texture);
762            if (tex == 0) {
763                ogles_error(c, GL_OUT_OF_MEMORY);
764                return;
765            }
766        }
767    }
768    bindTextureTmu(c, c->textures.active, texture, tex);
769}
770
771void glGenTextures(GLsizei n, GLuint *textures)
772{
773    ogles_context_t* c = ogles_context_t::get();
774    if (n<0) {
775        ogles_error(c, GL_INVALID_VALUE);
776        return;
777    }
778    // generate unique (shared) texture names
779    c->surfaceManager->getToken(n, textures);
780}
781
782void glDeleteTextures(GLsizei n, const GLuint *textures)
783{
784    ogles_context_t* c = ogles_context_t::get();
785    if (n<0) {
786        ogles_error(c, GL_INVALID_VALUE);
787        return;
788    }
789
790    // If deleting a bound texture, bind this unit to 0
791    for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) {
792        if (c->textures.tmu[t].name == 0)
793            continue;
794        for (int i=0 ; i<n ; i++) {
795            if (textures[i] && (textures[i] == c->textures.tmu[t].name)) {
796                // bind this tmu to texture 0
797                sp<EGLTextureObject> tex(c->textures.defaultTexture);
798                bindTextureTmu(c, t, 0, tex);
799            }
800        }
801    }
802    c->surfaceManager->deleteTextures(n, textures);
803    c->surfaceManager->recycleTokens(n, textures);
804}
805
806void glMultiTexCoord4f(
807        GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)
808{
809    ogles_context_t* c = ogles_context_t::get();
810    if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
811        ogles_error(c, GL_INVALID_ENUM);
812        return;
813    }
814    const int tmu = target-GL_TEXTURE0;
815    c->current.texture[tmu].S = gglFloatToFixed(s);
816    c->current.texture[tmu].T = gglFloatToFixed(t);
817    c->current.texture[tmu].R = gglFloatToFixed(r);
818    c->current.texture[tmu].Q = gglFloatToFixed(q);
819}
820
821void glMultiTexCoord4x(
822        GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q)
823{
824    ogles_context_t* c = ogles_context_t::get();
825    if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) {
826        ogles_error(c, GL_INVALID_ENUM);
827        return;
828    }
829    const int tmu = target-GL_TEXTURE0;
830    c->current.texture[tmu].S = s;
831    c->current.texture[tmu].T = t;
832    c->current.texture[tmu].R = r;
833    c->current.texture[tmu].Q = q;
834}
835
836void glPixelStorei(GLenum pname, GLint param)
837{
838    ogles_context_t* c = ogles_context_t::get();
839    if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) {
840        ogles_error(c, GL_INVALID_ENUM);
841        return;
842    }
843    if ((param<=0 || param>8) || (param & (param-1))) {
844        ogles_error(c, GL_INVALID_VALUE);
845        return;
846    }
847    if (pname == GL_PACK_ALIGNMENT)
848        c->textures.packAlignment = param;
849    if (pname == GL_UNPACK_ALIGNMENT)
850        c->textures.unpackAlignment = param;
851}
852
853void glTexEnvf(GLenum target, GLenum pname, GLfloat param)
854{
855    ogles_context_t* c = ogles_context_t::get();
856    c->rasterizer.procs.texEnvi(c, target, pname, GLint(param));
857}
858
859void glTexEnvfv(
860        GLenum target, GLenum pname, const GLfloat *params)
861{
862    ogles_context_t* c = ogles_context_t::get();
863    if (pname == GL_TEXTURE_ENV_MODE) {
864        c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params));
865        return;
866    }
867    if (pname == GL_TEXTURE_ENV_COLOR) {
868        GGLfixed fixed[4];
869        for (int i=0 ; i<4 ; i++)
870            fixed[i] = gglFloatToFixed(params[i]);
871        c->rasterizer.procs.texEnvxv(c, target, pname, fixed);
872        return;
873    }
874    ogles_error(c, GL_INVALID_ENUM);
875}
876
877void glTexEnvx(GLenum target, GLenum pname, GLfixed param)
878{
879    ogles_context_t* c = ogles_context_t::get();
880    c->rasterizer.procs.texEnvi(c, target, pname, param);
881}
882
883void glTexEnvxv(
884        GLenum target, GLenum pname, const GLfixed *params)
885{
886    ogles_context_t* c = ogles_context_t::get();
887    c->rasterizer.procs.texEnvxv(c, target, pname, params);
888}
889
890void glTexParameteriv(
891        GLenum target, GLenum pname, const GLint* params)
892{
893    ogles_context_t* c = ogles_context_t::get();
894    if (target != GGL_TEXTURE_2D) {
895        ogles_error(c, GL_INVALID_ENUM);
896        return;
897    }
898
899    EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
900    switch (pname) {
901    case GL_TEXTURE_CROP_RECT_OES:
902        memcpy(textureObject->crop_rect, params, 4*sizeof(GLint));
903        break;
904    default:
905        ogles_error(c, GL_INVALID_ENUM);
906        return;
907    }
908}
909
910void glTexParameterf(
911        GLenum target, GLenum pname, GLfloat param)
912{
913    ogles_context_t* c = ogles_context_t::get();
914    texParameterx(target, pname, GLfixed(param), c);
915}
916
917void glTexParameterx(
918        GLenum target, GLenum pname, GLfixed param)
919{
920    ogles_context_t* c = ogles_context_t::get();
921    texParameterx(target, pname, param, c);
922}
923
924// ----------------------------------------------------------------------------
925#if 0
926#pragma mark -
927#endif
928
929void glCompressedTexImage2D(
930        GLenum target, GLint level, GLenum internalformat,
931        GLsizei width, GLsizei height, GLint border,
932        GLsizei imageSize, const GLvoid *data)
933{
934    ogles_context_t* c = ogles_context_t::get();
935    if (target != GL_TEXTURE_2D) {
936        ogles_error(c, GL_INVALID_ENUM);
937        return;
938    }
939    if ((internalformat < GL_PALETTE4_RGB8_OES ||
940         internalformat > GL_PALETTE8_RGB5_A1_OES)) {
941        ogles_error(c, GL_INVALID_ENUM);
942        return;
943    }
944    if (width<0 || height<0 || border!=0) {
945        ogles_error(c, GL_INVALID_VALUE);
946        return;
947    }
948
949    // "uncompress" the texture since pixelflinger doesn't support
950    // any compressed texture format natively.
951    GLenum format;
952    GLenum type;
953    switch (internalformat) {
954    case GL_PALETTE8_RGB8_OES:
955    case GL_PALETTE4_RGB8_OES:
956        format      = GL_RGB;
957        type        = GL_UNSIGNED_BYTE;
958        break;
959    case GL_PALETTE8_RGBA8_OES:
960    case GL_PALETTE4_RGBA8_OES:
961        format      = GL_RGBA;
962        type        = GL_UNSIGNED_BYTE;
963        break;
964    case GL_PALETTE8_R5_G6_B5_OES:
965    case GL_PALETTE4_R5_G6_B5_OES:
966        format      = GL_RGB;
967        type        = GL_UNSIGNED_SHORT_5_6_5;
968        break;
969    case GL_PALETTE8_RGBA4_OES:
970    case GL_PALETTE4_RGBA4_OES:
971        format      = GL_RGBA;
972        type        = GL_UNSIGNED_SHORT_4_4_4_4;
973        break;
974    case GL_PALETTE8_RGB5_A1_OES:
975    case GL_PALETTE4_RGB5_A1_OES:
976        format      = GL_RGBA;
977        type        = GL_UNSIGNED_SHORT_5_5_5_1;
978        break;
979    default:
980        ogles_error(c, GL_INVALID_ENUM);
981        return;
982    }
983
984    if (!data || !width || !height) {
985        // unclear if this is an error or not...
986        return;
987    }
988
989    int32_t size;
990    GGLSurface* surface;
991    // all mipmap levels are specified at once.
992    const int numLevels = level<0 ? -level : 1;
993    for (int i=0 ; i<numLevels ; i++) {
994        int lod_w = (width  >> i) ? : 1;
995        int lod_h = (height >> i) ? : 1;
996        int error = createTextureSurface(c, &surface, &size,
997                i, format, type, lod_w, lod_h);
998        if (error) {
999            ogles_error(c, error);
1000            return;
1001        }
1002        decodePalette4(data, i, width, height,
1003                surface->data, surface->stride, internalformat);
1004    }
1005}
1006
1007
1008void glTexImage2D(
1009        GLenum target, GLint level, GLenum internalformat,
1010        GLsizei width, GLsizei height, GLint border,
1011        GLenum format, GLenum type, const GLvoid *pixels)
1012{
1013    ogles_context_t* c = ogles_context_t::get();
1014    if (target != GL_TEXTURE_2D && target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
1015        ogles_error(c, GL_INVALID_ENUM);
1016        return;
1017    }
1018    if (width<0 || height<0 || border!=0 || level < 0) {
1019        ogles_error(c, GL_INVALID_VALUE);
1020        return;
1021    }
1022    if (format != internalformat) {
1023        ogles_error(c, GL_INVALID_OPERATION);
1024        return;
1025    }
1026    if (validFormatType(c, format, type)) {
1027        return;
1028    }
1029
1030    int32_t size = 0;
1031    GGLSurface* surface = 0;
1032    if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
1033        int error = createTextureSurface(c, &surface, &size,
1034                level, format, type, width, height);
1035        if (error) {
1036            ogles_error(c, error);
1037            return;
1038        }
1039    } else if (pixels == 0 || level != 0) {
1040        // pixel can't be null for direct texture
1041        ogles_error(c, GL_INVALID_OPERATION);
1042        return;
1043    }
1044
1045    if (pixels) {
1046        const int32_t formatIdx = convertGLPixelFormat(format, type);
1047        const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
1048        const int32_t align = c->textures.unpackAlignment-1;
1049        const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
1050        const size_t size = bpr * height;
1051        const int32_t stride = bpr / pixelFormat.size;
1052
1053        GGLSurface userSurface;
1054        userSurface.version = sizeof(userSurface);
1055        userSurface.width  = width;
1056        userSurface.height = height;
1057        userSurface.stride = stride;
1058        userSurface.format = formatIdx;
1059        userSurface.compressedFormat = 0;
1060        userSurface.data = (GLubyte*)pixels;
1061
1062        if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
1063            int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height);
1064            if (err) {
1065                ogles_error(c, err);
1066                return;
1067            }
1068            generateMipmap(c, level);
1069        } else {
1070            // bind it to the texture unit
1071            sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
1072            tex->setSurface(&userSurface);
1073        }
1074    }
1075}
1076
1077// ----------------------------------------------------------------------------
1078
1079void glCompressedTexSubImage2D(
1080        GLenum target, GLint level, GLint xoffset,
1081        GLint yoffset, GLsizei width, GLsizei height,
1082        GLenum format, GLsizei imageSize,
1083        const GLvoid *data)
1084{
1085    ogles_context_t* c = ogles_context_t::get();
1086    ogles_error(c, GL_INVALID_ENUM);
1087}
1088
1089void glTexSubImage2D(
1090        GLenum target, GLint level, GLint xoffset,
1091        GLint yoffset, GLsizei width, GLsizei height,
1092        GLenum format, GLenum type, const GLvoid *pixels)
1093{
1094    ogles_context_t* c = ogles_context_t::get();
1095    if (target != GL_TEXTURE_2D) {
1096        ogles_error(c, GL_INVALID_ENUM);
1097        return;
1098    }
1099    if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
1100        ogles_error(c, GL_INVALID_VALUE);
1101        return;
1102    }
1103    if (validFormatType(c, format, type)) {
1104        return;
1105    }
1106
1107    // find out which texture is bound to the current unit
1108    const int active = c->textures.active;
1109    EGLTextureObject* tex = c->textures.tmu[active].texture;
1110    const GGLSurface& surface(tex->mip(level));
1111
1112    if (!tex->internalformat || tex->direct) {
1113        ogles_error(c, GL_INVALID_OPERATION);
1114        return;
1115    }
1116    if ((xoffset + width  > GLsizei(surface.width)) ||
1117        (yoffset + height > GLsizei(surface.height))) {
1118        ogles_error(c, GL_INVALID_VALUE);
1119        return;
1120    }
1121    if (!width || !height) {
1122        return; // okay, but no-op.
1123    }
1124
1125    // figure out the size we need as well as the stride
1126    const int32_t formatIdx = convertGLPixelFormat(format, type);
1127    if (formatIdx == 0) { // we don't know what to do with this
1128        ogles_error(c, GL_INVALID_OPERATION);
1129        return;
1130    }
1131
1132    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
1133    const int32_t align = c->textures.unpackAlignment-1;
1134    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
1135    const size_t size = bpr * height;
1136    const int32_t stride = bpr / pixelFormat.size;
1137    GGLSurface userSurface;
1138    userSurface.version = sizeof(userSurface);
1139    userSurface.width  = width;
1140    userSurface.height = height;
1141    userSurface.stride = stride;
1142    userSurface.format = formatIdx;
1143    userSurface.compressedFormat = 0;
1144    userSurface.data = (GLubyte*)pixels;
1145
1146    int err = copyPixels(c,
1147            surface, xoffset, yoffset,
1148            userSurface, 0, 0, width, height);
1149    if (err) {
1150        ogles_error(c, err);
1151        return;
1152    }
1153
1154    generateMipmap(c, level);
1155
1156    // since we only changed the content of the texture, we don't need
1157    // to call bindTexture on the main rasterizer.
1158}
1159
1160// ----------------------------------------------------------------------------
1161
1162void glCopyTexImage2D(
1163        GLenum target, GLint level, GLenum internalformat,
1164        GLint x, GLint y, GLsizei width, GLsizei height,
1165        GLint border)
1166{
1167    ogles_context_t* c = ogles_context_t::get();
1168    if (target != GL_TEXTURE_2D) {
1169        ogles_error(c, GL_INVALID_ENUM);
1170        return;
1171    }
1172    if (internalformat<GL_ALPHA || internalformat>GL_LUMINANCE_ALPHA) {
1173        ogles_error(c, GL_INVALID_ENUM);
1174        return;
1175    }
1176    if (width<0 || height<0 || border!=0 || level<0) {
1177        ogles_error(c, GL_INVALID_VALUE);
1178        return;
1179    }
1180
1181    GLenum format = 0;
1182    GLenum type = GL_UNSIGNED_BYTE;
1183    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
1184    const int cbFormatIdx = cbSurface.format;
1185    switch (cbFormatIdx) {
1186    case GGL_PIXEL_FORMAT_RGB_565:
1187        type = GL_UNSIGNED_SHORT_5_6_5;
1188        break;
1189    case GGL_PIXEL_FORMAT_RGBA_5551:
1190        type = GL_UNSIGNED_SHORT_5_5_5_1;
1191        break;
1192    case GGL_PIXEL_FORMAT_RGBA_4444:
1193        type = GL_UNSIGNED_SHORT_4_4_4_4;
1194        break;
1195    }
1196    switch (internalformat) {
1197    case GL_ALPHA:
1198    case GL_LUMINANCE_ALPHA:
1199    case GL_LUMINANCE:
1200        type = GL_UNSIGNED_BYTE;
1201        break;
1202    }
1203
1204    // figure out the format to use for the new texture
1205    switch (cbFormatIdx) {
1206    case GGL_PIXEL_FORMAT_RGBA_8888:
1207    case GGL_PIXEL_FORMAT_A_8:
1208    case GGL_PIXEL_FORMAT_RGBA_5551:
1209    case GGL_PIXEL_FORMAT_RGBA_4444:
1210        format = internalformat;
1211        break;
1212    case GGL_PIXEL_FORMAT_RGBX_8888:
1213    case GGL_PIXEL_FORMAT_RGB_888:
1214    case GGL_PIXEL_FORMAT_RGB_565:
1215    case GGL_PIXEL_FORMAT_L_8:
1216        switch (internalformat) {
1217        case GL_LUMINANCE:
1218        case GL_RGB:
1219            format = internalformat;
1220            break;
1221        }
1222        break;
1223    }
1224
1225    if (format == 0) {
1226        // invalid combination
1227        ogles_error(c, GL_INVALID_ENUM);
1228        return;
1229    }
1230
1231    // create the new texture...
1232    int32_t size;
1233    GGLSurface* surface;
1234    int error = createTextureSurface(c, &surface, &size,
1235            level, format, type, width, height);
1236    if (error) {
1237        ogles_error(c, error);
1238        return;
1239    }
1240
1241    // The bottom row is stored first in textures
1242    GGLSurface txSurface(*surface);
1243    txSurface.stride = -txSurface.stride;
1244
1245    // (x,y) is the lower-left corner of colorBuffer
1246    y = cbSurface.height - (y + height);
1247
1248    int err = copyPixels(c,
1249            txSurface, 0, 0,
1250            cbSurface, x, y, cbSurface.width, cbSurface.height);
1251    if (err) {
1252        ogles_error(c, err);
1253    }
1254
1255    generateMipmap(c, level);
1256}
1257
1258void glCopyTexSubImage2D(
1259        GLenum target, GLint level, GLint xoffset, GLint yoffset,
1260        GLint x, GLint y, GLsizei width, GLsizei height)
1261{
1262    ogles_context_t* c = ogles_context_t::get();
1263    if (target != GL_TEXTURE_2D) {
1264        ogles_error(c, GL_INVALID_ENUM);
1265        return;
1266    }
1267    if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) {
1268        ogles_error(c, GL_INVALID_VALUE);
1269        return;
1270    }
1271    if (!width || !height) {
1272        return; // okay, but no-op.
1273    }
1274
1275    // find out which texture is bound to the current unit
1276    const int active = c->textures.active;
1277    EGLTextureObject* tex = c->textures.tmu[active].texture;
1278    const GGLSurface& surface(tex->mip(level));
1279
1280    if (!tex->internalformat) {
1281        ogles_error(c, GL_INVALID_OPERATION);
1282        return;
1283    }
1284    if ((xoffset + width  > GLsizei(surface.width)) ||
1285        (yoffset + height > GLsizei(surface.height))) {
1286        ogles_error(c, GL_INVALID_VALUE);
1287        return;
1288    }
1289
1290    // The bottom row is stored first in textures
1291    GGLSurface txSurface(surface);
1292    txSurface.stride = -txSurface.stride;
1293
1294    // (x,y) is the lower-left corner of colorBuffer
1295    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
1296    y = cbSurface.height - (y + height);
1297
1298    int err = copyPixels(c,
1299            surface, xoffset, yoffset,
1300            cbSurface, x, y, width, height);
1301    if (err) {
1302        ogles_error(c, err);
1303        return;
1304    }
1305
1306    generateMipmap(c, level);
1307}
1308
1309void glReadPixels(
1310        GLint x, GLint y, GLsizei width, GLsizei height,
1311        GLenum format, GLenum type, GLvoid *pixels)
1312{
1313    ogles_context_t* c = ogles_context_t::get();
1314    if ((format != GL_RGBA) && (format != GL_RGB)) {
1315        ogles_error(c, GL_INVALID_ENUM);
1316        return;
1317    }
1318    if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) {
1319        ogles_error(c, GL_INVALID_ENUM);
1320        return;
1321    }
1322    if (width<0 || height<0) {
1323        ogles_error(c, GL_INVALID_VALUE);
1324        return;
1325    }
1326    if (x<0 || x<0) {
1327        ogles_error(c, GL_INVALID_VALUE);
1328        return;
1329    }
1330
1331    int32_t formatIdx = GGL_PIXEL_FORMAT_NONE;
1332    if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) {
1333        formatIdx = GGL_PIXEL_FORMAT_RGBA_8888;
1334    } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) {
1335        formatIdx = GGL_PIXEL_FORMAT_RGB_565;
1336    } else {
1337        ogles_error(c, GL_INVALID_OPERATION);
1338        return;
1339    }
1340
1341    const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s;
1342    if ((x+width > GLint(readSurface.width)) ||
1343            (y+height > GLint(readSurface.height))) {
1344        ogles_error(c, GL_INVALID_VALUE);
1345        return;
1346    }
1347
1348    const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
1349    const int32_t align = c->textures.packAlignment-1;
1350    const int32_t bpr = ((width * pixelFormat.size) + align) & ~align;
1351    const int32_t stride = bpr / pixelFormat.size;
1352
1353    GGLSurface userSurface;
1354    userSurface.version = sizeof(userSurface);
1355    userSurface.width  = width;
1356    userSurface.height = height;
1357    userSurface.stride = -stride; // bottom row is transfered first
1358    userSurface.format = formatIdx;
1359    userSurface.compressedFormat = 0;
1360    userSurface.data = (GLubyte*)pixels;
1361
1362    // use pixel-flinger to handle all the conversions
1363    GGLContext* ggl = getRasterizer(c);
1364    if (!ggl) {
1365        // the only reason this would fail is because we ran out of memory
1366        ogles_error(c, GL_OUT_OF_MEMORY);
1367        return;
1368    }
1369
1370    ggl->colorBuffer(ggl, &userSurface);  // destination is user buffer
1371    ggl->bindTexture(ggl, &readSurface);  // source is read-buffer
1372    ggl->texCoord2i(ggl, x, readSurface.height - (y + height));
1373    ggl->recti(ggl, 0, 0, width, height);
1374}
1375
1376// ----------------------------------------------------------------------------
1377#if 0
1378#pragma mark -
1379#pragma mark DrawTexture Extension
1380#endif
1381
1382void glDrawTexsvOES(const GLshort* coords) {
1383    ogles_context_t* c = ogles_context_t::get();
1384    drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
1385}
1386void glDrawTexivOES(const GLint* coords) {
1387    ogles_context_t* c = ogles_context_t::get();
1388    drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
1389}
1390void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) {
1391    ogles_context_t* c = ogles_context_t::get();
1392    drawTexiOES(x, y, z, w, h, c);
1393}
1394void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) {
1395    ogles_context_t* c = ogles_context_t::get();
1396    drawTexiOES(x, y, z, w, h, c);
1397}
1398
1399void glDrawTexfvOES(const GLfloat* coords) {
1400    ogles_context_t* c = ogles_context_t::get();
1401    drawTexxOES(
1402            gglFloatToFixed(coords[0]),
1403            gglFloatToFixed(coords[1]),
1404            gglFloatToFixed(coords[2]),
1405            gglFloatToFixed(coords[3]),
1406            gglFloatToFixed(coords[4]),
1407            c);
1408}
1409void glDrawTexxvOES(const GLfixed* coords) {
1410    ogles_context_t* c = ogles_context_t::get();
1411    drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c);
1412}
1413void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){
1414    ogles_context_t* c = ogles_context_t::get();
1415    drawTexxOES(
1416            gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z),
1417            gglFloatToFixed(w), gglFloatToFixed(h),
1418            c);
1419}
1420void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) {
1421    ogles_context_t* c = ogles_context_t::get();
1422    drawTexxOES(x, y, z, w, h, c);
1423}
1424