Home | History | Annotate | Download | only in libagl
      1 /*
      2 **
      3 ** Copyright 2009, 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 <stdlib.h>
     19 #include <stdio.h>
     20 
     21 #include "context.h"
     22 #include "fp.h"
     23 #include "state.h"
     24 #include "matrix.h"
     25 #include "vertex.h"
     26 #include "light.h"
     27 #include "primitives.h"
     28 #include "texture.h"
     29 #include "BufferObjectManager.h"
     30 #include "TextureObjectManager.h"
     31 
     32 #include <hardware/gralloc.h>
     33 #include <hardware/copybit.h>
     34 #include <private/ui/android_natives_priv.h>
     35 
     36 #include <ui/GraphicBuffer.h>
     37 #include <ui/Region.h>
     38 #include <ui/Rect.h>
     39 
     40 
     41 #define DEBUG_COPYBIT false
     42 
     43 // ----------------------------------------------------------------------------
     44 
     45 namespace android {
     46 
     47 static void textureToCopyBitImage(
     48         const GGLSurface* surface, int32_t opFormat,
     49         android_native_buffer_t* buffer, copybit_image_t* img)
     50 {
     51     img->w      = surface->stride;
     52     img->h      = surface->height;
     53     img->format = opFormat;
     54     img->base   = surface->data;
     55     img->handle = (native_handle_t *)buffer->handle;
     56 }
     57 
     58 struct clipRectRegion : public copybit_region_t {
     59     clipRectRegion(ogles_context_t* c)
     60     {
     61         scissor_t const* scissor = &c->rasterizer.state.scissor;
     62         r.l = scissor->left;
     63         r.t = scissor->top;
     64         r.r = scissor->right;
     65         r.b = scissor->bottom;
     66         next = iterate;
     67     }
     68 private:
     69     static int iterate(copybit_region_t const * self, copybit_rect_t* rect) {
     70         *rect = static_cast<clipRectRegion const*>(self)->r;
     71         const_cast<copybit_region_t *>(self)->next = iterate_done;
     72         return 1;
     73     }
     74     static int iterate_done(copybit_region_t const *, copybit_rect_t*) {
     75         return 0;
     76     }
     77 public:
     78     copybit_rect_t r;
     79 };
     80 
     81 static bool supportedCopybitsFormat(int format) {
     82     switch (format) {
     83     case COPYBIT_FORMAT_RGBA_8888:
     84     case COPYBIT_FORMAT_RGBX_8888:
     85     case COPYBIT_FORMAT_RGB_888:
     86     case COPYBIT_FORMAT_RGB_565:
     87     case COPYBIT_FORMAT_BGRA_8888:
     88     case COPYBIT_FORMAT_RGBA_5551:
     89     case COPYBIT_FORMAT_RGBA_4444:
     90         return true;
     91     default:
     92         return false;
     93     }
     94 }
     95 
     96 static bool hasAlpha(int format) {
     97     switch (format) {
     98     case COPYBIT_FORMAT_RGBA_8888:
     99     case COPYBIT_FORMAT_BGRA_8888:
    100     case COPYBIT_FORMAT_RGBA_5551:
    101     case COPYBIT_FORMAT_RGBA_4444:
    102         return true;
    103     default:
    104         return false;
    105     }
    106 }
    107 
    108 static inline int fixedToByte(GGLfixed val) {
    109     return (val - (val >> 8)) >> 8;
    110 }
    111 
    112 /**
    113  * Performs a quick check of the rendering state. If this function returns
    114  * false we cannot use the copybit driver.
    115  */
    116 
    117 static bool checkContext(ogles_context_t* c) {
    118 
    119 	// By convention copybitQuickCheckContext() has already returned true.
    120 	// avoid checking the same information again.
    121 
    122     if (c->copybits.blitEngine == NULL) {
    123         LOGD_IF(DEBUG_COPYBIT, "no copybit hal");
    124         return false;
    125     }
    126 
    127     if (c->rasterizer.state.enables
    128                     & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) {
    129         LOGD_IF(DEBUG_COPYBIT, "depth test and/or fog");
    130         return false;
    131     }
    132 
    133     // Note: The drawSurfaceBuffer is only set for destination
    134     // surfaces types that are supported by the hardware and
    135     // do not have an alpha channel. So we don't have to re-check that here.
    136 
    137     static const int tmu = 0;
    138     texture_unit_t& u(c->textures.tmu[tmu]);
    139     EGLTextureObject* textureObject = u.texture;
    140 
    141     if (!supportedCopybitsFormat(textureObject->surface.format)) {
    142         LOGD_IF(DEBUG_COPYBIT, "texture format not supported");
    143         return false;
    144     }
    145     return true;
    146 }
    147 
    148 
    149 static bool copybit(GLint x, GLint y,
    150         GLint w, GLint h,
    151         EGLTextureObject* textureObject,
    152         const GLint* crop_rect,
    153         int transform,
    154         ogles_context_t* c)
    155 {
    156     status_t err = NO_ERROR;
    157 
    158     // We assume checkContext has already been called and has already
    159     // returned true.
    160 
    161     const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
    162 
    163     y = cbSurface.height - (y + h);
    164 
    165     const GLint Ucr = crop_rect[0];
    166     const GLint Vcr = crop_rect[1];
    167     const GLint Wcr = crop_rect[2];
    168     const GLint Hcr = crop_rect[3];
    169 
    170     GLint screen_w = w;
    171     GLint screen_h = h;
    172     int32_t dsdx = Wcr << 16;   // dsdx =  ((Wcr/screen_w)/Wt)*Wt
    173     int32_t dtdy = Hcr << 16;   // dtdy = -((Hcr/screen_h)/Ht)*Ht
    174     if (transform & COPYBIT_TRANSFORM_ROT_90) {
    175         swap(screen_w, screen_h);
    176     }
    177     if (dsdx!=screen_w || dtdy!=screen_h) {
    178         // in most cases the divide is not needed
    179         dsdx /= screen_w;
    180         dtdy /= screen_h;
    181     }
    182     dtdy = -dtdy; // see equation of dtdy above
    183 
    184     // copybit doesn't say anything about filtering, so we can't
    185     // discriminate. On msm7k, copybit will always filter.
    186     // the code below handles min/mag filters, we keep it as a reference.
    187 
    188 #ifdef MIN_MAG_FILTER
    189     int32_t texelArea = gglMulx(dtdy, dsdx);
    190     if (texelArea < FIXED_ONE && textureObject->mag_filter != GL_LINEAR) {
    191         // Non-linear filtering on a texture enlargement.
    192         LOGD_IF(DEBUG_COPYBIT, "mag filter is not GL_LINEAR");
    193         return false;
    194     }
    195     if (texelArea > FIXED_ONE && textureObject->min_filter != GL_LINEAR) {
    196         // Non-linear filtering on an texture shrink.
    197         LOGD_IF(DEBUG_COPYBIT, "min filter is not GL_LINEAR");
    198         return false;
    199     }
    200 #endif
    201 
    202     const uint32_t enables = c->rasterizer.state.enables;
    203     int planeAlpha = 255;
    204     bool alphaPlaneWorkaround = false;
    205     static const int tmu = 0;
    206     texture_t& tev(c->rasterizer.state.texture[tmu]);
    207     int32_t opFormat = textureObject->surface.format;
    208     const bool srcTextureHasAlpha = hasAlpha(opFormat);
    209     if (!srcTextureHasAlpha) {
    210         planeAlpha = fixedToByte(c->currentColorClamped.a);
    211     }
    212 
    213     const bool cbHasAlpha = hasAlpha(cbSurface.format);
    214     bool blending = false;
    215     if ((enables & GGL_ENABLE_BLENDING)
    216             && !(c->rasterizer.state.blend.src == GL_ONE
    217                     && c->rasterizer.state.blend.dst == GL_ZERO)) {
    218         // Blending is OK if it is
    219         // the exact kind of blending that the copybits hardware supports.
    220         // Note: The hardware only supports
    221         // GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA,
    222         // But the surface flinger uses GL_ONE / GL_ONE_MINUS_SRC_ALPHA.
    223         // We substitute GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA in that case,
    224         // because the performance is worth it, even if the results are
    225         // not correct.
    226         if (!((c->rasterizer.state.blend.src == GL_SRC_ALPHA
    227                 || c->rasterizer.state.blend.src == GL_ONE)
    228                 && c->rasterizer.state.blend.dst == GL_ONE_MINUS_SRC_ALPHA
    229                 && c->rasterizer.state.blend.alpha_separate == 0)) {
    230             // Incompatible blend mode.
    231             LOGD_IF(DEBUG_COPYBIT, "incompatible blend mode");
    232             return false;
    233         }
    234         blending = true;
    235     } else {
    236         if (cbHasAlpha) {
    237             // NOTE: the result will be slightly wrong in this case because
    238             // the destination alpha channel will be set to 1.0 instead of
    239             // the iterated alpha value. *shrug*.
    240         }
    241         // disable plane blending and src blending for supported formats
    242         planeAlpha = 255;
    243         if (opFormat == COPYBIT_FORMAT_RGBA_8888) {
    244             opFormat = COPYBIT_FORMAT_RGBX_8888;
    245         } else {
    246             if (srcTextureHasAlpha) {
    247                 LOGD_IF(DEBUG_COPYBIT, "texture format requires blending");
    248                 return false;
    249             }
    250         }
    251     }
    252 
    253     switch (tev.env) {
    254     case GGL_REPLACE:
    255         break;
    256     case GGL_MODULATE:
    257         // only cases allowed is:
    258         // RGB  source, color={1,1,1,a} -> can be done with GL_REPLACE
    259         // RGBA source, color={1,1,1,1} -> can be done with GL_REPLACE
    260         if (blending) {
    261             if (c->currentColorClamped.r == c->currentColorClamped.a &&
    262                 c->currentColorClamped.g == c->currentColorClamped.a &&
    263                 c->currentColorClamped.b == c->currentColorClamped.a) {
    264                 // TODO: RGBA source, color={1,1,1,a} / regular-blending
    265                 // is equivalent
    266                 alphaPlaneWorkaround = true;
    267                 break;
    268             }
    269         }
    270         LOGD_IF(DEBUG_COPYBIT, "GGL_MODULATE");
    271         return false;
    272     default:
    273         // Incompatible texture environment.
    274         LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment");
    275         return false;
    276     }
    277 
    278     copybit_device_t* copybit = c->copybits.blitEngine;
    279     copybit_image_t src;
    280     textureToCopyBitImage(&textureObject->surface, opFormat,
    281             textureObject->buffer, &src);
    282     copybit_rect_t srect = { Ucr, Vcr + Hcr, Ucr + Wcr, Vcr };
    283 
    284     /*
    285      *  Below we perform extra passes needed to emulate things the h/w
    286      * cannot do.
    287      */
    288 
    289     const GLfixed minScaleInv = gglDivQ(0x10000, c->copybits.minScale, 16);
    290     const GLfixed maxScaleInv = gglDivQ(0x10000, c->copybits.maxScale, 16);
    291 
    292     sp<GraphicBuffer> tempBitmap;
    293 
    294     if (dsdx < maxScaleInv || dsdx > minScaleInv ||
    295         dtdy < maxScaleInv || dtdy > minScaleInv)
    296     {
    297         // The requested scale is out of the range the hardware
    298         // can support.
    299         LOGD_IF(DEBUG_COPYBIT,
    300                 "scale out of range dsdx=%08x (Wcr=%d / w=%d), "
    301                 "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d",
    302                 dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr);
    303 
    304         int32_t xscale=0x10000, yscale=0x10000;
    305         if (dsdx > minScaleInv)         xscale = c->copybits.minScale;
    306         else if (dsdx < maxScaleInv)    xscale = c->copybits.maxScale;
    307         if (dtdy > minScaleInv)         yscale = c->copybits.minScale;
    308         else if (dtdy < maxScaleInv)    yscale = c->copybits.maxScale;
    309         dsdx = gglMulx(dsdx, xscale);
    310         dtdy = gglMulx(dtdy, yscale);
    311 
    312         /* we handle only one step of resizing below. Handling an arbitrary
    313          * number is relatively easy (replace "if" above by "while"), but requires
    314          * two intermediate buffers and so far we never had the need.
    315          */
    316 
    317         if (dsdx < maxScaleInv || dsdx > minScaleInv ||
    318             dtdy < maxScaleInv || dtdy > minScaleInv) {
    319             LOGD_IF(DEBUG_COPYBIT,
    320                     "scale out of range dsdx=%08x (Wcr=%d / w=%d), "
    321                     "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d",
    322                     dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr);
    323             return false;
    324         }
    325 
    326         const int tmp_w = gglMulx(srect.r - srect.l, xscale, 16);
    327         const int tmp_h = gglMulx(srect.b - srect.t, yscale, 16);
    328 
    329         LOGD_IF(DEBUG_COPYBIT,
    330                 "xscale=%08x, yscale=%08x, dsdx=%08x, dtdy=%08x, tmp_w=%d, tmp_h=%d",
    331                 xscale, yscale, dsdx, dtdy, tmp_w, tmp_h);
    332 
    333         tempBitmap = new GraphicBuffer(
    334                     tmp_w, tmp_h, src.format,
    335                     GraphicBuffer::USAGE_HW_2D);
    336 
    337         err = tempBitmap->initCheck();
    338         if (err == NO_ERROR) {
    339             copybit_image_t tmp_dst;
    340             copybit_rect_t tmp_rect;
    341             tmp_dst.w = tmp_w;
    342             tmp_dst.h = tmp_h;
    343             tmp_dst.format = tempBitmap->format;
    344             tmp_dst.handle = (native_handle_t*)tempBitmap->getNativeBuffer()->handle;
    345             tmp_rect.l = 0;
    346             tmp_rect.t = 0;
    347             tmp_rect.r = tmp_dst.w;
    348             tmp_rect.b = tmp_dst.h;
    349             region_iterator tmp_it(Region(Rect(tmp_rect.r, tmp_rect.b)));
    350             copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
    351             copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
    352             copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
    353             err = copybit->stretch(copybit,
    354                     &tmp_dst, &src, &tmp_rect, &srect, &tmp_it);
    355             src = tmp_dst;
    356             srect = tmp_rect;
    357         }
    358     }
    359 
    360     copybit_image_t dst;
    361     textureToCopyBitImage(&cbSurface, cbSurface.format,
    362             c->copybits.drawSurfaceBuffer, &dst);
    363     copybit_rect_t drect = {x, y, x+w, y+h};
    364 
    365 
    366     /* and now the alpha-plane hack. This handles the "Fade" case of a
    367      * texture with an alpha channel.
    368      */
    369     if (alphaPlaneWorkaround) {
    370         sp<GraphicBuffer> tempCb = new GraphicBuffer(
    371                     w, h, COPYBIT_FORMAT_RGB_565,
    372                     GraphicBuffer::USAGE_HW_2D);
    373 
    374         err = tempCb->initCheck();
    375 
    376         copybit_image_t tmpCbImg;
    377         copybit_rect_t tmpCbRect;
    378         copybit_rect_t tmpdrect = drect;
    379         tmpCbImg.w = w;
    380         tmpCbImg.h = h;
    381         tmpCbImg.format = tempCb->format;
    382         tmpCbImg.handle = (native_handle_t*)tempCb->getNativeBuffer()->handle;
    383         tmpCbRect.l = 0;
    384         tmpCbRect.t = 0;
    385 
    386         if (drect.l < 0) {
    387             tmpCbRect.l = -tmpdrect.l;
    388             tmpdrect.l = 0;
    389         }
    390         if (drect.t < 0) {
    391             tmpCbRect.t = -tmpdrect.t;
    392             tmpdrect.t = 0;
    393         }
    394         if (drect.l + tmpCbImg.w > dst.w) {
    395             tmpCbImg.w = dst.w - drect.l;
    396             tmpdrect.r = dst.w;
    397         }
    398         if (drect.t + tmpCbImg.h > dst.h) {
    399             tmpCbImg.h = dst.h - drect.t;
    400             tmpdrect.b = dst.h;
    401         }
    402 
    403         tmpCbRect.r = tmpCbImg.w;
    404         tmpCbRect.b = tmpCbImg.h;
    405 
    406         if (!err) {
    407             // first make a copy of the destination buffer
    408             region_iterator tmp_it(Region(Rect(w, h)));
    409             copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
    410             copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
    411             copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
    412             err = copybit->stretch(copybit,
    413                     &tmpCbImg, &dst, &tmpCbRect, &tmpdrect, &tmp_it);
    414         }
    415         if (!err) {
    416             // then proceed as usual, but without the alpha plane
    417             copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform);
    418             copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
    419             copybit->set_parameter(copybit, COPYBIT_DITHER,
    420                     (enables & GGL_ENABLE_DITHER) ?
    421                             COPYBIT_ENABLE : COPYBIT_DISABLE);
    422             clipRectRegion it(c);
    423             err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
    424         }
    425         if (!err) {
    426             // finally copy back the destination on top with 1-alphaplane
    427             int invPlaneAlpha = 0xFF - fixedToByte(c->currentColorClamped.a);
    428             clipRectRegion it(c);
    429             copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
    430             copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, invPlaneAlpha);
    431             copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
    432             err = copybit->stretch(copybit,
    433                     &dst, &tmpCbImg, &tmpdrect, &tmpCbRect, &it);
    434         }
    435     } else {
    436         copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform);
    437         copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, planeAlpha);
    438         copybit->set_parameter(copybit, COPYBIT_DITHER,
    439                 (enables & GGL_ENABLE_DITHER) ?
    440                         COPYBIT_ENABLE : COPYBIT_DISABLE);
    441         clipRectRegion it(c);
    442 
    443         LOGD_IF(0,
    444              "dst={%d, %d, %d, %p, %p}, "
    445              "src={%d, %d, %d, %p, %p}, "
    446              "drect={%d,%d,%d,%d}, "
    447              "srect={%d,%d,%d,%d}, "
    448              "it={%d,%d,%d,%d}, " ,
    449              dst.w, dst.h, dst.format, dst.base, dst.handle,
    450              src.w, src.h, src.format, src.base, src.handle,
    451              drect.l, drect.t, drect.r, drect.b,
    452              srect.l, srect.t, srect.r, srect.b,
    453              it.r.l, it.r.t, it.r.r, it.r.b
    454         );
    455 
    456         err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
    457     }
    458     if (err != NO_ERROR) {
    459         c->textures.tmu[0].texture->try_copybit = false;
    460     }
    461     return err == NO_ERROR ? true : false;
    462 }
    463 
    464 /*
    465  * Try to draw a triangle fan with copybit, return false if we fail.
    466  */
    467 bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, GLsizei count)
    468 {
    469     if (!checkContext(c)) {
    470         return false;
    471     }
    472 
    473     // FIXME: we should handle culling  here
    474     c->arrays.compileElements(c, c->vc.vBuffer, 0, 4);
    475 
    476     // we detect if we're dealing with a rectangle, by comparing the
    477     // rectangles {v0,v2} and {v1,v3} which should be identical.
    478 
    479     // NOTE: we should check that the rectangle is window aligned, however
    480     // if we do that, the optimization won't be taken in a lot of cases.
    481     // Since this code is intended to be used with SurfaceFlinger only,
    482     // so it's okay...
    483 
    484     const vec4_t& v0 = c->vc.vBuffer[0].window;
    485     const vec4_t& v1 = c->vc.vBuffer[1].window;
    486     const vec4_t& v2 = c->vc.vBuffer[2].window;
    487     const vec4_t& v3 = c->vc.vBuffer[3].window;
    488     int l = min(v0.x, v2.x);
    489     int b = min(v0.y, v2.y);
    490     int r = max(v0.x, v2.x);
    491     int t = max(v0.y, v2.y);
    492     if ((l != min(v1.x, v3.x)) || (b != min(v1.y, v3.y)) ||
    493         (r != max(v1.x, v3.x)) || (t != max(v1.y, v3.y))) {
    494         LOGD_IF(DEBUG_COPYBIT, "geometry not a rectangle");
    495         return false;
    496     }
    497 
    498     // fetch and transform texture coordinates
    499     // NOTE: maybe it would be better to have a "compileElementsAll" method
    500     // that would ensure all vertex data are fetched and transformed
    501     const transform_t& tr = c->transforms.texture[0].transform;
    502     for (size_t i=0 ; i<4 ; i++) {
    503         const GLubyte* tp = c->arrays.texture[0].element(i);
    504         vertex_t* const v = &c->vc.vBuffer[i];
    505         c->arrays.texture[0].fetch(c, v->texture[0].v, tp);
    506         // FIXME: we should bail if q!=1
    507         c->arrays.tex_transform[0](&tr, &v->texture[0], &v->texture[0]);
    508     }
    509 
    510     const vec4_t& t0 = c->vc.vBuffer[0].texture[0];
    511     const vec4_t& t1 = c->vc.vBuffer[1].texture[0];
    512     const vec4_t& t2 = c->vc.vBuffer[2].texture[0];
    513     const vec4_t& t3 = c->vc.vBuffer[3].texture[0];
    514     int txl = min(t0.x, t2.x);
    515     int txb = min(t0.y, t2.y);
    516     int txr = max(t0.x, t2.x);
    517     int txt = max(t0.y, t2.y);
    518     if ((txl != min(t1.x, t3.x)) || (txb != min(t1.y, t3.y)) ||
    519         (txr != max(t1.x, t3.x)) || (txt != max(t1.y, t3.y))) {
    520         LOGD_IF(DEBUG_COPYBIT, "texcoord not a rectangle");
    521         return false;
    522     }
    523     if ((txl != 0) || (txb != 0) ||
    524         (txr != FIXED_ONE) || (txt != FIXED_ONE)) {
    525         // we could probably handle this case, if we wanted to
    526         LOGD_IF(DEBUG_COPYBIT, "texture is cropped: %08x,%08x,%08x,%08x",
    527                 txl, txb, txr, txt);
    528         return false;
    529     }
    530 
    531     // at this point, we know we are dealing with a rectangle, so we
    532     // only need to consider 3 vertices for computing the jacobians
    533 
    534     const int dx01 = v1.x - v0.x;
    535     const int dx02 = v2.x - v0.x;
    536     const int dy01 = v1.y - v0.y;
    537     const int dy02 = v2.y - v0.y;
    538     const int ds01 = t1.S - t0.S;
    539     const int ds02 = t2.S - t0.S;
    540     const int dt01 = t1.T - t0.T;
    541     const int dt02 = t2.T - t0.T;
    542     const int area = dx01*dy02 - dy01*dx02;
    543     int dsdx, dsdy, dtdx, dtdy;
    544     if (area >= 0) {
    545         dsdx = ds01*dy02 - ds02*dy01;
    546         dtdx = dt01*dy02 - dt02*dy01;
    547         dsdy = ds02*dx01 - ds01*dx02;
    548         dtdy = dt02*dx01 - dt01*dx02;
    549     } else {
    550         dsdx = ds02*dy01 - ds01*dy02;
    551         dtdx = dt02*dy01 - dt01*dy02;
    552         dsdy = ds01*dx02 - ds02*dx01;
    553         dtdy = dt01*dx02 - dt02*dx01;
    554     }
    555 
    556     // here we rely on the fact that we know the transform is
    557     // a rigid-body transform AND that it can only rotate in 90 degrees
    558     // increments
    559 
    560     int transform = 0;
    561     if (dsdx == 0) {
    562         // 90 deg rotation case
    563         // [ 0    dtdx  ]
    564         // [ dsdx    0  ]
    565         transform |= COPYBIT_TRANSFORM_ROT_90;
    566         // FIXME: not sure if FLIP_H and FLIP_V shouldn't be inverted
    567         if (dtdx > 0)
    568             transform |= COPYBIT_TRANSFORM_FLIP_H;
    569         if (dsdy < 0)
    570             transform |= COPYBIT_TRANSFORM_FLIP_V;
    571     } else {
    572         // [ dsdx    0  ]
    573         // [ 0     dtdy ]
    574         if (dsdx < 0)
    575             transform |= COPYBIT_TRANSFORM_FLIP_H;
    576         if (dtdy < 0)
    577             transform |= COPYBIT_TRANSFORM_FLIP_V;
    578     }
    579 
    580     //LOGD("l=%d, b=%d, w=%d, h=%d, tr=%d", x, y, w, h, transform);
    581     //LOGD("A=%f\tB=%f\nC=%f\tD=%f",
    582     //      dsdx/65536.0, dtdx/65536.0, dsdy/65536.0, dtdy/65536.0);
    583 
    584     int x = l >> 4;
    585     int y = b >> 4;
    586     int w = (r-l) >> 4;
    587     int h = (t-b) >> 4;
    588     texture_unit_t& u(c->textures.tmu[0]);
    589     EGLTextureObject* textureObject = u.texture;
    590     GLint tWidth = textureObject->surface.width;
    591     GLint tHeight = textureObject->surface.height;
    592     GLint crop_rect[4] = {0, tHeight, tWidth, -tHeight};
    593     const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
    594     y = cbSurface.height - (y + h);
    595     return copybit(x, y, w, h, textureObject, crop_rect, transform, c);
    596 }
    597 
    598 /*
    599  * Try to drawTexiOESWithCopybit, return false if we fail.
    600  */
    601 
    602 bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z,
    603         GLint w, GLint h, ogles_context_t* c)
    604 {
    605     // quickly process empty rects
    606     if ((w|h) <= 0) {
    607         return true;
    608     }
    609     if (!checkContext(c)) {
    610         return false;
    611     }
    612     texture_unit_t& u(c->textures.tmu[0]);
    613     EGLTextureObject* textureObject = u.texture;
    614     return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c);
    615 }
    616 
    617 } // namespace android
    618 
    619