eclair snapshot
diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp
new file mode 100644
index 0000000..a68750e
--- /dev/null
+++ b/opengl/libagl/copybit.cpp
@@ -0,0 +1,631 @@
+/*
+**
+** Copyright 2009, 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 <stdlib.h>
+#include <stdio.h>
+
+#include "context.h"
+#include "fp.h"
+#include "state.h"
+#include "matrix.h"
+#include "vertex.h"
+#include "light.h"
+#include "primitives.h"
+#include "texture.h"
+#include "BufferObjectManager.h"
+#include "TextureObjectManager.h"
+
+#include <hardware/gralloc.h>
+#include <hardware/copybit.h>
+#include <private/ui/android_natives_priv.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/Region.h>
+#include <ui/Rect.h>
+
+
+#define DEBUG_COPYBIT true
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+static void textureToCopyBitImage(
+        const GGLSurface* surface, int32_t opFormat, 
+        android_native_buffer_t* buffer, copybit_image_t* img)
+{
+    uint32_t vstride = 0;
+    if (opFormat == COPYBIT_FORMAT_YCbCr_422_SP ||
+            opFormat == COPYBIT_FORMAT_YCbCr_420_SP) {
+        // NOTE: this static_cast is really not safe b/c we can't know for
+        // sure the buffer passed is of the right type.
+        // However, since we do this only for YUV formats, we should be safe
+        // since only SurfaceFlinger makes use of them.
+        GraphicBuffer* graphicBuffer = static_cast<GraphicBuffer*>(buffer);
+        vstride = graphicBuffer->getVerticalStride();
+    }
+
+    img->w      = surface->stride;
+    img->h      = vstride ? vstride : surface->height;
+    img->format = opFormat;
+    img->base   = surface->data;
+    img->handle = (native_handle_t *)buffer->handle;
+}
+
+struct clipRectRegion : public copybit_region_t {
+    clipRectRegion(ogles_context_t* c) 
+    {
+        scissor_t const* scissor = &c->rasterizer.state.scissor;
+        r.l = scissor->left;
+        r.t = scissor->top;
+        r.r = scissor->right;
+        r.b = scissor->bottom;
+        next = iterate; 
+    }
+private:
+    static int iterate(copybit_region_t const * self, copybit_rect_t* rect) {
+        *rect = static_cast<clipRectRegion const*>(self)->r;
+        const_cast<copybit_region_t *>(self)->next = iterate_done;
+        return 1;
+    }
+    static int iterate_done(copybit_region_t const *, copybit_rect_t*) {
+        return 0;
+    }
+public:
+    copybit_rect_t r;
+};
+
+static bool supportedCopybitsFormat(int format) {
+    switch (format) {
+    case COPYBIT_FORMAT_RGBA_8888:
+    case COPYBIT_FORMAT_RGBX_8888:
+    case COPYBIT_FORMAT_RGB_888:
+    case COPYBIT_FORMAT_RGB_565:
+    case COPYBIT_FORMAT_BGRA_8888:
+    case COPYBIT_FORMAT_RGBA_5551:
+    case COPYBIT_FORMAT_RGBA_4444:
+    case COPYBIT_FORMAT_YCbCr_422_SP:
+    case COPYBIT_FORMAT_YCbCr_420_SP:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static bool hasAlpha(int format) {
+    switch (format) {
+    case COPYBIT_FORMAT_RGBA_8888:
+    case COPYBIT_FORMAT_BGRA_8888:
+    case COPYBIT_FORMAT_RGBA_5551:
+    case COPYBIT_FORMAT_RGBA_4444:
+        return true;
+    default:
+        return false;
+    }
+}
+
+static inline int fixedToByte(GGLfixed val) {
+    return (val - (val >> 8)) >> 8;
+}
+
+/**
+ * Performs a quick check of the rendering state. If this function returns
+ * false we cannot use the copybit driver.
+ */
+
+static bool checkContext(ogles_context_t* c) {
+
+	// By convention copybitQuickCheckContext() has already returned true.
+	// avoid checking the same information again.
+	
+    if (c->copybits.blitEngine == NULL) {
+        LOGD_IF(DEBUG_COPYBIT, "no copybit hal");
+        return false;
+    }
+
+    if (c->rasterizer.state.enables
+                    & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) {
+        LOGD_IF(DEBUG_COPYBIT, "depth test and/or fog");
+        return false;
+    }
+
+    // Note: The drawSurfaceBuffer is only set for destination
+    // surfaces types that are supported by the hardware and
+    // do not have an alpha channel. So we don't have to re-check that here.
+
+    static const int tmu = 0;
+    texture_unit_t& u(c->textures.tmu[tmu]);
+    EGLTextureObject* textureObject = u.texture;
+
+    if (!supportedCopybitsFormat(textureObject->surface.format)) {
+        LOGD_IF(DEBUG_COPYBIT, "texture format not supported");
+        return false;
+    }
+    return true;
+}
+
+
+static bool copybit(GLint x, GLint y,
+        GLint w, GLint h,
+        EGLTextureObject* textureObject,
+        const GLint* crop_rect,
+        int transform,
+        ogles_context_t* c)
+{
+    status_t err = NO_ERROR;
+
+    // We assume checkContext has already been called and has already
+    // returned true.
+
+    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+
+    y = cbSurface.height - (y + h);
+
+    const GLint Ucr = crop_rect[0];
+    const GLint Vcr = crop_rect[1];
+    const GLint Wcr = crop_rect[2];
+    const GLint Hcr = crop_rect[3];
+
+    GLint screen_w = w;
+    GLint screen_h = h;
+    int32_t dsdx = Wcr << 16;   // dsdx =  ((Wcr/screen_w)/Wt)*Wt
+    int32_t dtdy = Hcr << 16;   // dtdy = -((Hcr/screen_h)/Ht)*Ht
+    if (transform & COPYBIT_TRANSFORM_ROT_90) {
+        swap(screen_w, screen_h);
+    }
+    if (dsdx!=screen_w || dtdy!=screen_h) {
+        // in most cases the divide is not needed
+        dsdx /= screen_w;
+        dtdy /= screen_h;
+    }
+    dtdy = -dtdy; // see equation of dtdy above
+
+    // copybit doesn't say anything about filtering, so we can't
+    // discriminate. On msm7k, copybit will always filter.
+    // the code below handles min/mag filters, we keep it as a reference.
+    
+#ifdef MIN_MAG_FILTER
+    int32_t texelArea = gglMulx(dtdy, dsdx);
+    if (texelArea < FIXED_ONE && textureObject->mag_filter != GL_LINEAR) {
+        // Non-linear filtering on a texture enlargement.
+        LOGD_IF(DEBUG_COPYBIT, "mag filter is not GL_LINEAR");
+        return false;
+    }
+    if (texelArea > FIXED_ONE && textureObject->min_filter != GL_LINEAR) {
+        // Non-linear filtering on an texture shrink.
+        LOGD_IF(DEBUG_COPYBIT, "min filter is not GL_LINEAR");
+        return false;
+    }
+#endif
+    
+    const uint32_t enables = c->rasterizer.state.enables;
+    int planeAlpha = 255;
+    bool alphaPlaneWorkaround = false;
+    static const int tmu = 0;
+    texture_t& tev(c->rasterizer.state.texture[tmu]);
+    int32_t opFormat = textureObject->surface.format;
+    const bool srcTextureHasAlpha = hasAlpha(opFormat);
+    if (!srcTextureHasAlpha) {
+        planeAlpha = fixedToByte(c->currentColorClamped.a);
+    }
+
+    const bool cbHasAlpha = hasAlpha(cbSurface.format);
+    bool blending = false;
+    if ((enables & GGL_ENABLE_BLENDING)
+            && !(c->rasterizer.state.blend.src == GL_ONE
+                    && c->rasterizer.state.blend.dst == GL_ZERO)) {
+        // Blending is OK if it is
+        // the exact kind of blending that the copybits hardware supports.
+        // Note: The hardware only supports
+        // GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA,
+        // But the surface flinger uses GL_ONE / GL_ONE_MINUS_SRC_ALPHA.
+        // We substitute GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA in that case,
+        // because the performance is worth it, even if the results are
+        // not correct.
+        if (!((c->rasterizer.state.blend.src == GL_SRC_ALPHA
+                || c->rasterizer.state.blend.src == GL_ONE)
+                && c->rasterizer.state.blend.dst == GL_ONE_MINUS_SRC_ALPHA
+                && c->rasterizer.state.blend.alpha_separate == 0)) {
+            // Incompatible blend mode.
+            LOGD_IF(DEBUG_COPYBIT, "incompatible blend mode");
+            return false;
+        }
+        blending = true;
+    } else {
+        if (cbHasAlpha) {
+            // NOTE: the result will be slightly wrong in this case because
+            // the destination alpha channel will be set to 1.0 instead of
+            // the iterated alpha value. *shrug*.
+        }
+        // disable plane blending and src blending for supported formats
+        planeAlpha = 255;
+        if (opFormat == COPYBIT_FORMAT_RGBA_8888) {
+            opFormat = COPYBIT_FORMAT_RGBX_8888;
+        } else {
+            if (srcTextureHasAlpha) {
+                LOGD_IF(DEBUG_COPYBIT, "texture format requires blending");
+                return false;
+            }
+        }
+    }
+
+    switch (tev.env) {
+    case GGL_REPLACE:
+        break;
+    case GGL_MODULATE:
+        // only cases allowed is:
+        // RGB  source, color={1,1,1,a} -> can be done with GL_REPLACE
+        // RGBA source, color={1,1,1,1} -> can be done with GL_REPLACE
+        if (blending) {
+            if (c->currentColorClamped.r == c->currentColorClamped.a &&
+                c->currentColorClamped.g == c->currentColorClamped.a &&
+                c->currentColorClamped.b == c->currentColorClamped.a) {
+                // TODO: RGBA source, color={1,1,1,a} / regular-blending
+                // is equivalent
+                alphaPlaneWorkaround = true;
+                break;
+            }
+        }
+        LOGD_IF(DEBUG_COPYBIT, "GGL_MODULATE");
+        return false;
+    default:
+        // Incompatible texture environment.
+        LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment");
+        return false;
+    }
+
+    copybit_device_t* copybit = c->copybits.blitEngine;
+    copybit_image_t src;
+    textureToCopyBitImage(&textureObject->surface, opFormat,
+            textureObject->buffer, &src);
+    copybit_rect_t srect = { Ucr, Vcr + Hcr, Ucr + Wcr, Vcr };
+
+    /*
+     *  Below we perform extra passes needed to emulate things the h/w
+     * cannot do.
+     */
+
+    const GLfixed minScaleInv = gglDivQ(0x10000, c->copybits.minScale, 16);
+    const GLfixed maxScaleInv = gglDivQ(0x10000, c->copybits.maxScale, 16);
+
+    sp<GraphicBuffer> tempBitmap;
+
+    if (dsdx < maxScaleInv || dsdx > minScaleInv ||
+        dtdy < maxScaleInv || dtdy > minScaleInv)
+    {
+        // The requested scale is out of the range the hardware
+        // can support.
+        LOGD_IF(DEBUG_COPYBIT,
+                "scale out of range dsdx=%08x (Wcr=%d / w=%d), "
+                "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d",
+                dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr);
+
+        int32_t xscale=0x10000, yscale=0x10000;
+        if (dsdx > minScaleInv)         xscale = c->copybits.minScale;
+        else if (dsdx < maxScaleInv)    xscale = c->copybits.maxScale;
+        if (dtdy > minScaleInv)         yscale = c->copybits.minScale;
+        else if (dtdy < maxScaleInv)    yscale = c->copybits.maxScale;
+        dsdx = gglMulx(dsdx, xscale);
+        dtdy = gglMulx(dtdy, yscale);
+
+        /* we handle only one step of resizing below. Handling an arbitrary
+         * number is relatively easy (replace "if" above by "while"), but requires
+         * two intermediate buffers and so far we never had the need.
+         */
+
+        if (dsdx < maxScaleInv || dsdx > minScaleInv ||
+            dtdy < maxScaleInv || dtdy > minScaleInv) {
+            LOGD_IF(DEBUG_COPYBIT,
+                    "scale out of range dsdx=%08x (Wcr=%d / w=%d), "
+                    "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d",
+                    dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr);
+            return false;
+        }
+
+        const int tmp_w = gglMulx(srect.r - srect.l, xscale, 16);
+        const int tmp_h = gglMulx(srect.b - srect.t, yscale, 16);
+
+        LOGD_IF(DEBUG_COPYBIT,
+                "xscale=%08x, yscale=%08x, dsdx=%08x, dtdy=%08x, tmp_w=%d, tmp_h=%d",
+                xscale, yscale, dsdx, dtdy, tmp_w, tmp_h);
+
+        tempBitmap = new GraphicBuffer(
+                    tmp_w, tmp_h, src.format,
+                    GraphicBuffer::USAGE_HW_2D);
+
+        err = tempBitmap->initCheck();
+        if (err == NO_ERROR) {
+            copybit_image_t tmp_dst;
+            copybit_rect_t tmp_rect;
+            tmp_dst.w = tmp_w;
+            tmp_dst.h = tmp_h;
+            tmp_dst.format = tempBitmap->format;
+            tmp_dst.handle = (native_handle_t*)tempBitmap->getNativeBuffer()->handle;
+            tmp_rect.l = 0;
+            tmp_rect.t = 0;
+            tmp_rect.r = tmp_dst.w;
+            tmp_rect.b = tmp_dst.h;
+            region_iterator tmp_it(Region(Rect(tmp_rect.r, tmp_rect.b)));
+            copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+            copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
+            copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
+            err = copybit->stretch(copybit,
+                    &tmp_dst, &src, &tmp_rect, &srect, &tmp_it);
+            src = tmp_dst;
+            srect = tmp_rect;
+        }
+    }
+
+    copybit_image_t dst;
+    textureToCopyBitImage(&cbSurface, cbSurface.format,
+            c->copybits.drawSurfaceBuffer, &dst);
+    copybit_rect_t drect = {x, y, x+w, y+h};
+
+
+    /* and now the alpha-plane hack. This handles the "Fade" case of a
+     * texture with an alpha channel.
+     */
+    if (alphaPlaneWorkaround) {
+        sp<GraphicBuffer> tempCb = new GraphicBuffer(
+                    w, h, COPYBIT_FORMAT_RGB_565,
+                    GraphicBuffer::USAGE_HW_2D);
+
+        err = tempCb->initCheck();
+
+        copybit_image_t tmpCbImg;
+        copybit_rect_t tmpCbRect;
+        copybit_rect_t tmpdrect = drect;
+        tmpCbImg.w = w;
+        tmpCbImg.h = h;
+        tmpCbImg.format = tempCb->format;
+        tmpCbImg.handle = (native_handle_t*)tempCb->getNativeBuffer()->handle;
+        tmpCbRect.l = 0;
+        tmpCbRect.t = 0;
+
+        if (drect.l < 0) {
+            tmpCbRect.l = -tmpdrect.l;
+            tmpdrect.l = 0;
+        }
+        if (drect.t < 0) {
+            tmpCbRect.t = -tmpdrect.t;
+            tmpdrect.t = 0;
+        }
+        if (drect.l + tmpCbImg.w > dst.w) {
+            tmpCbImg.w = dst.w - drect.l;
+            tmpdrect.r = dst.w;
+        }
+        if (drect.t + tmpCbImg.h > dst.h) {
+            tmpCbImg.h = dst.h - drect.t;
+            tmpdrect.b = dst.h;
+        }
+
+        tmpCbRect.r = tmpCbImg.w;
+        tmpCbRect.b = tmpCbImg.h;
+
+        if (!err) {
+            // first make a copy of the destination buffer
+            region_iterator tmp_it(Region(Rect(w, h)));
+            copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+            copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
+            copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
+            err = copybit->stretch(copybit,
+                    &tmpCbImg, &dst, &tmpCbRect, &tmpdrect, &tmp_it);
+        }
+        if (!err) {
+            // then proceed as usual, but without the alpha plane
+            copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform);
+            copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
+            copybit->set_parameter(copybit, COPYBIT_DITHER,
+                    (enables & GGL_ENABLE_DITHER) ?
+                            COPYBIT_ENABLE : COPYBIT_DISABLE);
+            clipRectRegion it(c);
+            err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
+        }
+        if (!err) {
+            // finally copy back the destination on top with 1-alphaplane
+            int invPlaneAlpha = 0xFF - fixedToByte(c->currentColorClamped.a);
+            clipRectRegion it(c);
+            copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+            copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, invPlaneAlpha);
+            copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
+            err = copybit->stretch(copybit,
+                    &dst, &tmpCbImg, &tmpdrect, &tmpCbRect, &it);
+        }
+    } else {
+        copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform);
+        copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, planeAlpha);
+        copybit->set_parameter(copybit, COPYBIT_DITHER,
+                (enables & GGL_ENABLE_DITHER) ?
+                        COPYBIT_ENABLE : COPYBIT_DISABLE);
+        clipRectRegion it(c);
+
+        LOGD_IF(0,
+             "dst={%d, %d, %d, %p, %p}, "
+             "src={%d, %d, %d, %p, %p}, "
+             "drect={%d,%d,%d,%d}, "
+             "srect={%d,%d,%d,%d}, "
+             "it={%d,%d,%d,%d}, " ,
+             dst.w, dst.h, dst.format, dst.base, dst.handle,
+             src.w, src.h, src.format, src.base, src.handle,
+             drect.l, drect.t, drect.r, drect.b,
+             srect.l, srect.t, srect.r, srect.b,
+             it.r.l, it.r.t, it.r.r, it.r.b
+        );
+
+        err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
+    }
+    if (err != NO_ERROR) {
+        c->textures.tmu[0].texture->try_copybit = false;
+    }
+    return err == NO_ERROR ? true : false;
+}
+
+/*
+ * Try to draw a triangle fan with copybit, return false if we fail.
+ */
+bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, GLsizei count)
+{
+    if (!checkContext(c)) {
+        return false;
+    }
+
+    // FIXME: we should handle culling  here
+    c->arrays.compileElements(c, c->vc.vBuffer, 0, 4);
+
+    // we detect if we're dealing with a rectangle, by comparing the
+    // rectangles {v0,v2} and {v1,v3} which should be identical.
+    
+    // NOTE: we should check that the rectangle is window aligned, however
+    // if we do that, the optimization won't be taken in a lot of cases.
+    // Since this code is intended to be used with SurfaceFlinger only,
+    // so it's okay...
+    
+    const vec4_t& v0 = c->vc.vBuffer[0].window;
+    const vec4_t& v1 = c->vc.vBuffer[1].window;
+    const vec4_t& v2 = c->vc.vBuffer[2].window;
+    const vec4_t& v3 = c->vc.vBuffer[3].window;
+    int l = min(v0.x, v2.x);
+    int b = min(v0.y, v2.y);
+    int r = max(v0.x, v2.x);
+    int t = max(v0.y, v2.y);
+    if ((l != min(v1.x, v3.x)) || (b != min(v1.y, v3.y)) ||
+        (r != max(v1.x, v3.x)) || (t != max(v1.y, v3.y))) {
+        LOGD_IF(DEBUG_COPYBIT, "geometry not a rectangle");
+        return false;
+    }
+
+    // fetch and transform texture coordinates
+    // NOTE: maybe it would be better to have a "compileElementsAll" method
+    // that would ensure all vertex data are fetched and transformed
+    const transform_t& tr = c->transforms.texture[0].transform; 
+    for (size_t i=0 ; i<4 ; i++) {
+        const GLubyte* tp = c->arrays.texture[0].element(i);
+        vertex_t* const v = &c->vc.vBuffer[i];
+        c->arrays.texture[0].fetch(c, v->texture[0].v, tp);
+        // FIXME: we should bail if q!=1
+        c->arrays.tex_transform[0](&tr, &v->texture[0], &v->texture[0]);
+    }
+    
+    const vec4_t& t0 = c->vc.vBuffer[0].texture[0];
+    const vec4_t& t1 = c->vc.vBuffer[1].texture[0];
+    const vec4_t& t2 = c->vc.vBuffer[2].texture[0];
+    const vec4_t& t3 = c->vc.vBuffer[3].texture[0];
+    int txl = min(t0.x, t2.x);
+    int txb = min(t0.y, t2.y);
+    int txr = max(t0.x, t2.x);
+    int txt = max(t0.y, t2.y);
+    if ((txl != min(t1.x, t3.x)) || (txb != min(t1.y, t3.y)) ||
+        (txr != max(t1.x, t3.x)) || (txt != max(t1.y, t3.y))) {
+        LOGD_IF(DEBUG_COPYBIT, "texcoord not a rectangle");
+        return false;
+    }
+    if ((txl != 0) || (txb != 0) ||
+        (txr != FIXED_ONE) || (txt != FIXED_ONE)) {
+        // we could probably handle this case, if we wanted to
+        LOGD_IF(DEBUG_COPYBIT, "texture is cropped: %08x,%08x,%08x,%08x",
+                txl, txb, txr, txt);
+        return false;
+    }
+
+    // at this point, we know we are dealing with a rectangle, so we 
+    // only need to consider 3 vertices for computing the jacobians
+    
+    const int dx01 = v1.x - v0.x;
+    const int dx02 = v2.x - v0.x;
+    const int dy01 = v1.y - v0.y;
+    const int dy02 = v2.y - v0.y;
+    const int ds01 = t1.S - t0.S;
+    const int ds02 = t2.S - t0.S;
+    const int dt01 = t1.T - t0.T;
+    const int dt02 = t2.T - t0.T;
+    const int area = dx01*dy02 - dy01*dx02;
+    int dsdx, dsdy, dtdx, dtdy;
+    if (area >= 0) {
+        dsdx = ds01*dy02 - ds02*dy01;
+        dtdx = dt01*dy02 - dt02*dy01;
+        dsdy = ds02*dx01 - ds01*dx02;
+        dtdy = dt02*dx01 - dt01*dx02;
+    } else {
+        dsdx = ds02*dy01 - ds01*dy02;
+        dtdx = dt02*dy01 - dt01*dy02;
+        dsdy = ds01*dx02 - ds02*dx01;
+        dtdy = dt01*dx02 - dt02*dx01;
+    }
+
+    // here we rely on the fact that we know the transform is
+    // a rigid-body transform AND that it can only rotate in 90 degrees
+    // increments
+
+    int transform = 0;
+    if (dsdx == 0) {
+        // 90 deg rotation case
+        // [ 0    dtdx  ]
+        // [ dsdx    0  ]
+        transform |= COPYBIT_TRANSFORM_ROT_90;
+        // FIXME: not sure if FLIP_H and FLIP_V shouldn't be inverted
+        if (dtdx > 0)
+            transform |= COPYBIT_TRANSFORM_FLIP_H;
+        if (dsdy < 0)
+            transform |= COPYBIT_TRANSFORM_FLIP_V;
+    } else {
+        // [ dsdx    0  ]
+        // [ 0     dtdy ]
+        if (dsdx < 0)
+            transform |= COPYBIT_TRANSFORM_FLIP_H;
+        if (dtdy < 0)
+            transform |= COPYBIT_TRANSFORM_FLIP_V;
+    }
+
+    //LOGD("l=%d, b=%d, w=%d, h=%d, tr=%d", x, y, w, h, transform);
+    //LOGD("A=%f\tB=%f\nC=%f\tD=%f",
+    //      dsdx/65536.0, dtdx/65536.0, dsdy/65536.0, dtdy/65536.0);
+
+    int x = l >> 4;
+    int y = b >> 4;
+    int w = (r-l) >> 4;
+    int h = (t-b) >> 4;
+    texture_unit_t& u(c->textures.tmu[0]);
+    EGLTextureObject* textureObject = u.texture;
+    GLint tWidth = textureObject->surface.width;
+    GLint tHeight = textureObject->surface.height;
+    GLint crop_rect[4] = {0, tHeight, tWidth, -tHeight};
+    const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+    y = cbSurface.height - (y + h);
+    return copybit(x, y, w, h, textureObject, crop_rect, transform, c);
+}
+
+/*
+ * Try to drawTexiOESWithCopybit, return false if we fail.
+ */
+
+bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z,
+        GLint w, GLint h, ogles_context_t* c)
+{
+    // quickly process empty rects
+    if ((w|h) <= 0) {
+        return true;
+    }
+    if (!checkContext(c)) {
+        return false;
+    }
+    texture_unit_t& u(c->textures.tmu[0]);
+    EGLTextureObject* textureObject = u.texture;
+    return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c);
+}
+
+} // namespace android
+