| /* |
| ** |
| ** 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 false |
| |
| // ---------------------------------------------------------------------------- |
| |
| namespace android { |
| |
| static void textureToCopyBitImage( |
| const GGLSurface* surface, int32_t opFormat, |
| android_native_buffer_t* buffer, copybit_image_t* img) |
| { |
| img->w = surface->stride; |
| img->h = 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: |
| 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 |
| |