| /* libs/pixelflinger/trap.cpp |
| ** |
| ** Copyright 2006, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "trap.h" |
| #include "picker.h" |
| |
| #include <cutils/log.h> |
| #include <cutils/memory.h> |
| |
| namespace android { |
| |
| // ---------------------------------------------------------------------------- |
| |
| // enable to see triangles edges |
| #define DEBUG_TRANGLES 0 |
| |
| // ---------------------------------------------------------------------------- |
| |
| static void pointx_validate(void *con, const GGLcoord* c, GGLcoord r); |
| static void pointx(void *con, const GGLcoord* c, GGLcoord r); |
| static void aa_pointx(void *con, const GGLcoord* c, GGLcoord r); |
| static void aa_nice_pointx(void *con, const GGLcoord* c, GGLcoord r); |
| |
| static void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w); |
| static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w); |
| static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w); |
| |
| static void recti_validate(void* c, GGLint l, GGLint t, GGLint r, GGLint b); |
| static void recti(void* c, GGLint l, GGLint t, GGLint r, GGLint b); |
| |
| static void trianglex_validate(void*, |
| const GGLcoord*, const GGLcoord*, const GGLcoord*); |
| static void trianglex_small(void*, |
| const GGLcoord*, const GGLcoord*, const GGLcoord*); |
| static void trianglex_big(void*, |
| const GGLcoord*, const GGLcoord*, const GGLcoord*); |
| static void aa_trianglex(void*, |
| const GGLcoord*, const GGLcoord*, const GGLcoord*); |
| static void trianglex_debug(void* con, |
| const GGLcoord*, const GGLcoord*, const GGLcoord*); |
| |
| static void aapolyx(void* con, |
| const GGLcoord* pts, int count); |
| |
| static inline int min(int a, int b) CONST; |
| static inline int max(int a, int b) CONST; |
| static inline int min(int a, int b, int c) CONST; |
| static inline int max(int a, int b, int c) CONST; |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Tools |
| #endif |
| |
| inline int min(int a, int b) { |
| return a<b ? a : b; |
| } |
| inline int max(int a, int b) { |
| return a<b ? b : a; |
| } |
| inline int min(int a, int b, int c) { |
| return min(a,min(b,c)); |
| } |
| inline int max(int a, int b, int c) { |
| return max(a,max(b,c)); |
| } |
| |
| template <typename T> |
| static inline void swap(T& a, T& b) { |
| T t(a); |
| a = b; |
| b = t; |
| } |
| |
| static void |
| triangle_dump_points( const GGLcoord* v0, |
| const GGLcoord* v1, |
| const GGLcoord* v2 ) |
| { |
| float tri = 1.0f / TRI_ONE; |
| ALOGD(" P0=(%.3f, %.3f) [%08x, %08x]\n" |
| " P1=(%.3f, %.3f) [%08x, %08x]\n" |
| " P2=(%.3f, %.3f) [%08x, %08x]\n", |
| v0[0]*tri, v0[1]*tri, v0[0], v0[1], |
| v1[0]*tri, v1[1]*tri, v1[0], v1[1], |
| v2[0]*tri, v2[1]*tri, v2[0], v2[1] ); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Misc |
| #endif |
| |
| void ggl_init_trap(context_t* c) |
| { |
| ggl_state_changed(c, GGL_PIXEL_PIPELINE_STATE|GGL_TMU_STATE|GGL_CB_STATE); |
| } |
| |
| void ggl_state_changed(context_t* c, int flags) |
| { |
| if (ggl_likely(!c->dirty)) { |
| c->procs.pointx = pointx_validate; |
| c->procs.linex = linex_validate; |
| c->procs.recti = recti_validate; |
| c->procs.trianglex = trianglex_validate; |
| } |
| c->dirty |= uint32_t(flags); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Point |
| #endif |
| |
| void pointx_validate(void *con, const GGLcoord* v, GGLcoord rad) |
| { |
| GGL_CONTEXT(c, con); |
| ggl_pick(c); |
| if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { |
| if (c->state.enables & GGL_ENABLE_POINT_AA_NICE) { |
| c->procs.pointx = aa_nice_pointx; |
| } else { |
| c->procs.pointx = aa_pointx; |
| } |
| } else { |
| c->procs.pointx = pointx; |
| } |
| c->procs.pointx(con, v, rad); |
| } |
| |
| void pointx(void *con, const GGLcoord* v, GGLcoord rad) |
| { |
| GGL_CONTEXT(c, con); |
| GGLcoord halfSize = TRI_ROUND(rad) >> 1; |
| if (halfSize == 0) |
| halfSize = TRI_HALF; |
| GGLcoord xc = v[0]; |
| GGLcoord yc = v[1]; |
| if (halfSize & TRI_HALF) { // size odd |
| xc = TRI_FLOOR(xc) + TRI_HALF; |
| yc = TRI_FLOOR(yc) + TRI_HALF; |
| } else { // size even |
| xc = TRI_ROUND(xc); |
| yc = TRI_ROUND(yc); |
| } |
| GGLint l = (xc - halfSize) >> TRI_FRACTION_BITS; |
| GGLint t = (yc - halfSize) >> TRI_FRACTION_BITS; |
| GGLint r = (xc + halfSize) >> TRI_FRACTION_BITS; |
| GGLint b = (yc + halfSize) >> TRI_FRACTION_BITS; |
| recti(c, l, t, r, b); |
| } |
| |
| // This way of computing the coverage factor, is more accurate and gives |
| // better results for small circles, but it is also a lot slower. |
| // Here we use super-sampling. |
| static int32_t coverageNice(GGLcoord x, GGLcoord y, |
| GGLcoord rmin, GGLcoord rmax, GGLcoord rr) |
| { |
| const GGLcoord d2 = x*x + y*y; |
| if (d2 >= rmax) return 0; |
| if (d2 < rmin) return 0x7FFF; |
| |
| const int kSamples = 4; |
| const int kInc = 4; // 1/4 = 0.25 |
| const int kCoverageUnit = 1; // 1/(4^2) = 0.0625 |
| const GGLcoord kCoordOffset = -6; // -0.375 |
| |
| int hits = 0; |
| int x_sample = x + kCoordOffset; |
| for (int i=0 ; i<kSamples ; i++, x_sample += kInc) { |
| const int xval = rr - (x_sample * x_sample); |
| int y_sample = y + kCoordOffset; |
| for (int j=0 ; j<kSamples ; j++, y_sample += kInc) { |
| if (xval - (y_sample * y_sample) > 0) |
| hits += kCoverageUnit; |
| } |
| } |
| return min(0x7FFF, hits << (15 - kSamples)); |
| } |
| |
| |
| void aa_nice_pointx(void *con, const GGLcoord* v, GGLcoord size) |
| { |
| GGL_CONTEXT(c, con); |
| |
| GGLcoord rad = ((size + 1)>>1); |
| GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS; |
| GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS; |
| GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; |
| GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; |
| GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF; |
| GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF; |
| |
| // scissor... |
| if (l < GGLint(c->state.scissor.left)) { |
| xstart += TRI_FROM_INT(c->state.scissor.left-l); |
| l = GGLint(c->state.scissor.left); |
| } |
| if (t < GGLint(c->state.scissor.top)) { |
| ystart += TRI_FROM_INT(c->state.scissor.top-t); |
| t = GGLint(c->state.scissor.top); |
| } |
| if (r > GGLint(c->state.scissor.right)) { |
| r = GGLint(c->state.scissor.right); |
| } |
| if (b > GGLint(c->state.scissor.bottom)) { |
| b = GGLint(c->state.scissor.bottom); |
| } |
| |
| int xc = r - l; |
| int yc = b - t; |
| if (xc>0 && yc>0) { |
| int16_t* covPtr = c->state.buffers.coverage; |
| const int32_t sqr2Over2 = 0xC; // rounded up |
| GGLcoord rr = rad*rad; |
| GGLcoord rmin = (rad - sqr2Over2)*(rad - sqr2Over2); |
| GGLcoord rmax = (rad + sqr2Over2)*(rad + sqr2Over2); |
| GGLcoord y = ystart; |
| c->iterators.xl = l; |
| c->iterators.xr = r; |
| c->init_y(c, t); |
| do { |
| // compute coverage factors for each pixel |
| GGLcoord x = xstart; |
| for (int i=l ; i<r ; i++) { |
| covPtr[i] = coverageNice(x, y, rmin, rmax, rr); |
| x += TRI_ONE; |
| } |
| y += TRI_ONE; |
| c->scanline(c); |
| c->step_y(c); |
| } while (--yc); |
| } |
| } |
| |
| // This is a cheap way of computing the coverage factor for a circle. |
| // We just lerp between the circles of radii r-sqrt(2)/2 and r+sqrt(2)/2 |
| static inline int32_t coverageFast(GGLcoord x, GGLcoord y, |
| GGLcoord rmin, GGLcoord rmax, GGLcoord scale) |
| { |
| const GGLcoord d2 = x*x + y*y; |
| if (d2 >= rmax) return 0; |
| if (d2 < rmin) return 0x7FFF; |
| return 0x7FFF - (d2-rmin)*scale; |
| } |
| |
| void aa_pointx(void *con, const GGLcoord* v, GGLcoord size) |
| { |
| GGL_CONTEXT(c, con); |
| |
| GGLcoord rad = ((size + 1)>>1); |
| GGLint l = (v[0] - rad) >> TRI_FRACTION_BITS; |
| GGLint t = (v[1] - rad) >> TRI_FRACTION_BITS; |
| GGLint r = (v[0] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; |
| GGLint b = (v[1] + rad + (TRI_ONE-1)) >> TRI_FRACTION_BITS; |
| GGLcoord xstart = TRI_FROM_INT(l) - v[0] + TRI_HALF; |
| GGLcoord ystart = TRI_FROM_INT(t) - v[1] + TRI_HALF; |
| |
| // scissor... |
| if (l < GGLint(c->state.scissor.left)) { |
| xstart += TRI_FROM_INT(c->state.scissor.left-l); |
| l = GGLint(c->state.scissor.left); |
| } |
| if (t < GGLint(c->state.scissor.top)) { |
| ystart += TRI_FROM_INT(c->state.scissor.top-t); |
| t = GGLint(c->state.scissor.top); |
| } |
| if (r > GGLint(c->state.scissor.right)) { |
| r = GGLint(c->state.scissor.right); |
| } |
| if (b > GGLint(c->state.scissor.bottom)) { |
| b = GGLint(c->state.scissor.bottom); |
| } |
| |
| int xc = r - l; |
| int yc = b - t; |
| if (xc>0 && yc>0) { |
| int16_t* covPtr = c->state.buffers.coverage; |
| rad <<= 4; |
| const int32_t sqr2Over2 = 0xB5; // fixed-point 24.8 |
| GGLcoord rmin = rad - sqr2Over2; |
| GGLcoord rmax = rad + sqr2Over2; |
| GGLcoord scale; |
| rmin *= rmin; |
| rmax *= rmax; |
| scale = 0x800000 / (rmax - rmin); |
| rmin >>= 8; |
| rmax >>= 8; |
| |
| GGLcoord y = ystart; |
| c->iterators.xl = l; |
| c->iterators.xr = r; |
| c->init_y(c, t); |
| |
| do { |
| // compute coverage factors for each pixel |
| GGLcoord x = xstart; |
| for (int i=l ; i<r ; i++) { |
| covPtr[i] = coverageFast(x, y, rmin, rmax, scale); |
| x += TRI_ONE; |
| } |
| y += TRI_ONE; |
| c->scanline(c); |
| c->step_y(c); |
| } while (--yc); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Line |
| #endif |
| |
| void linex_validate(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord w) |
| { |
| GGL_CONTEXT(c, con); |
| ggl_pick(c); |
| if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { |
| c->procs.linex = aa_linex; |
| } else { |
| c->procs.linex = linex; |
| } |
| c->procs.linex(con, v0, v1, w); |
| } |
| |
| static void linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width) |
| { |
| GGL_CONTEXT(c, con); |
| GGLcoord v[4][2]; |
| v[0][0] = v0[0]; v[0][1] = v0[1]; |
| v[1][0] = v1[0]; v[1][1] = v1[1]; |
| v0 = v[0]; |
| v1 = v[1]; |
| const GGLcoord dx = abs(v0[0] - v1[0]); |
| const GGLcoord dy = abs(v0[1] - v1[1]); |
| GGLcoord nx, ny; |
| nx = ny = 0; |
| |
| GGLcoord halfWidth = TRI_ROUND(width) >> 1; |
| if (halfWidth == 0) |
| halfWidth = TRI_HALF; |
| |
| ((dx > dy) ? ny : nx) = halfWidth; |
| v[2][0] = v1[0]; v[2][1] = v1[1]; |
| v[3][0] = v0[0]; v[3][1] = v0[1]; |
| v[0][0] += nx; v[0][1] += ny; |
| v[1][0] += nx; v[1][1] += ny; |
| v[2][0] -= nx; v[2][1] -= ny; |
| v[3][0] -= nx; v[3][1] -= ny; |
| trianglex_big(con, v[0], v[1], v[2]); |
| trianglex_big(con, v[0], v[2], v[3]); |
| } |
| |
| static void aa_linex(void *con, const GGLcoord* v0, const GGLcoord* v1, GGLcoord width) |
| { |
| GGL_CONTEXT(c, con); |
| GGLcoord v[4][2]; |
| v[0][0] = v0[0]; v[0][1] = v0[1]; |
| v[1][0] = v1[0]; v[1][1] = v1[1]; |
| v0 = v[0]; |
| v1 = v[1]; |
| |
| const GGLcoord dx = v0[0] - v1[0]; |
| const GGLcoord dy = v0[1] - v1[1]; |
| GGLcoord nx = -dy; |
| GGLcoord ny = dx; |
| |
| // generally, this will be well below 1.0 |
| const GGLfixed norm = gglMulx(width, gglSqrtRecipx(nx*nx+ny*ny), 4); |
| nx = gglMulx(nx, norm, 21); |
| ny = gglMulx(ny, norm, 21); |
| |
| v[2][0] = v1[0]; v[2][1] = v1[1]; |
| v[3][0] = v0[0]; v[3][1] = v0[1]; |
| v[0][0] += nx; v[0][1] += ny; |
| v[1][0] += nx; v[1][1] += ny; |
| v[2][0] -= nx; v[2][1] -= ny; |
| v[3][0] -= nx; v[3][1] -= ny; |
| aapolyx(con, v[0], 4); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Rect |
| #endif |
| |
| void recti_validate(void *con, GGLint l, GGLint t, GGLint r, GGLint b) |
| { |
| GGL_CONTEXT(c, con); |
| ggl_pick(c); |
| c->procs.recti = recti; |
| c->procs.recti(con, l, t, r, b); |
| } |
| |
| void recti(void* con, GGLint l, GGLint t, GGLint r, GGLint b) |
| { |
| GGL_CONTEXT(c, con); |
| |
| // scissor... |
| if (l < GGLint(c->state.scissor.left)) |
| l = GGLint(c->state.scissor.left); |
| if (t < GGLint(c->state.scissor.top)) |
| t = GGLint(c->state.scissor.top); |
| if (r > GGLint(c->state.scissor.right)) |
| r = GGLint(c->state.scissor.right); |
| if (b > GGLint(c->state.scissor.bottom)) |
| b = GGLint(c->state.scissor.bottom); |
| |
| int xc = r - l; |
| int yc = b - t; |
| if (xc>0 && yc>0) { |
| c->iterators.xl = l; |
| c->iterators.xr = r; |
| c->init_y(c, t); |
| c->rect(c, yc); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Triangle / Debugging |
| #endif |
| |
| static void scanline_set(context_t* c) |
| { |
| int32_t x = c->iterators.xl; |
| size_t ct = c->iterators.xr - x; |
| int32_t y = c->iterators.y; |
| surface_t* cb = &(c->state.buffers.color); |
| const GGLFormat* fp = &(c->formats[cb->format]); |
| uint8_t* dst = reinterpret_cast<uint8_t*>(cb->data) + |
| (x + (cb->stride * y)) * fp->size; |
| const size_t size = ct * fp->size; |
| memset(dst, 0xFF, size); |
| } |
| |
| static void trianglex_debug(void* con, |
| const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) |
| { |
| GGL_CONTEXT(c, con); |
| if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { |
| aa_trianglex(con,v0,v1,v2); |
| } else { |
| trianglex_big(con,v0,v1,v2); |
| } |
| void (*save_scanline)(context_t*) = c->scanline; |
| c->scanline = scanline_set; |
| linex(con, v0, v1, TRI_ONE); |
| linex(con, v1, v2, TRI_ONE); |
| linex(con, v2, v0, TRI_ONE); |
| c->scanline = save_scanline; |
| } |
| |
| static void trianglex_xor(void* con, |
| const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) |
| { |
| trianglex_big(con,v0,v1,v2); |
| trianglex_small(con,v0,v1,v2); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #pragma mark Triangle |
| #endif |
| |
| void trianglex_validate(void *con, |
| const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) |
| { |
| GGL_CONTEXT(c, con); |
| ggl_pick(c); |
| if (c->state.needs.p & GGL_NEED_MASK(P_AA)) { |
| c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : aa_trianglex; |
| } else { |
| c->procs.trianglex = DEBUG_TRANGLES ? trianglex_debug : trianglex_big; |
| } |
| c->procs.trianglex(con, v0, v1, v2); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| void trianglex_small(void* con, |
| const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) |
| { |
| GGL_CONTEXT(c, con); |
| |
| // vertices are in 28.4 fixed point, which allows |
| // us to use 32 bits multiplies below. |
| int32_t x0 = v0[0]; |
| int32_t y0 = v0[1]; |
| int32_t x1 = v1[0]; |
| int32_t y1 = v1[1]; |
| int32_t x2 = v2[0]; |
| int32_t y2 = v2[1]; |
| |
| int32_t dx01 = x0 - x1; |
| int32_t dy20 = y2 - y0; |
| int32_t dy01 = y0 - y1; |
| int32_t dx20 = x2 - x0; |
| |
| // The code below works only with CCW triangles |
| // so if we get a CW triangle, we need to swap two of its vertices |
| if (dx01*dy20 < dy01*dx20) { |
| swap(x0, x1); |
| swap(y0, y1); |
| dx01 = x0 - x1; |
| dy01 = y0 - y1; |
| dx20 = x2 - x0; |
| dy20 = y2 - y0; |
| } |
| int32_t dx12 = x1 - x2; |
| int32_t dy12 = y1 - y2; |
| |
| // bounding box & scissor |
| const int32_t bminx = TRI_FLOOR(min(x0, x1, x2)) >> TRI_FRACTION_BITS; |
| const int32_t bminy = TRI_FLOOR(min(y0, y1, y2)) >> TRI_FRACTION_BITS; |
| const int32_t bmaxx = TRI_CEIL( max(x0, x1, x2)) >> TRI_FRACTION_BITS; |
| const int32_t bmaxy = TRI_CEIL( max(y0, y1, y2)) >> TRI_FRACTION_BITS; |
| const int32_t minx = max(bminx, c->state.scissor.left); |
| const int32_t miny = max(bminy, c->state.scissor.top); |
| const int32_t maxx = min(bmaxx, c->state.scissor.right); |
| const int32_t maxy = min(bmaxy, c->state.scissor.bottom); |
| if ((minx >= maxx) || (miny >= maxy)) |
| return; // too small or clipped out... |
| |
| // step equations to the bounding box and snap to pixel center |
| const int32_t my = (miny << TRI_FRACTION_BITS) + TRI_HALF; |
| const int32_t mx = (minx << TRI_FRACTION_BITS) + TRI_HALF; |
| int32_t ey0 = dy01 * (x0 - mx) - dx01 * (y0 - my); |
| int32_t ey1 = dy12 * (x1 - mx) - dx12 * (y1 - my); |
| int32_t ey2 = dy20 * (x2 - mx) - dx20 * (y2 - my); |
| |
| // right-exclusive fill rule, to avoid rare cases |
| // of over drawing |
| if (dy01<0 || (dy01 == 0 && dx01>0)) ey0++; |
| if (dy12<0 || (dy12 == 0 && dx12>0)) ey1++; |
| if (dy20<0 || (dy20 == 0 && dx20>0)) ey2++; |
| |
| c->init_y(c, miny); |
| for (int32_t y = miny; y < maxy; y++) { |
| int32_t ex0 = ey0; |
| int32_t ex1 = ey1; |
| int32_t ex2 = ey2; |
| int32_t xl, xr; |
| for (xl=minx ; xl<maxx ; xl++) { |
| if (ex0>0 && ex1>0 && ex2>0) |
| break; // all strictly positive |
| ex0 -= dy01 << TRI_FRACTION_BITS; |
| ex1 -= dy12 << TRI_FRACTION_BITS; |
| ex2 -= dy20 << TRI_FRACTION_BITS; |
| } |
| xr = xl; |
| for ( ; xr<maxx ; xr++) { |
| if (!(ex0>0 && ex1>0 && ex2>0)) |
| break; // not all strictly positive |
| ex0 -= dy01 << TRI_FRACTION_BITS; |
| ex1 -= dy12 << TRI_FRACTION_BITS; |
| ex2 -= dy20 << TRI_FRACTION_BITS; |
| } |
| |
| if (xl < xr) { |
| c->iterators.xl = xl; |
| c->iterators.xr = xr; |
| c->scanline(c); |
| } |
| c->step_y(c); |
| |
| ey0 += dx01 << TRI_FRACTION_BITS; |
| ey1 += dx12 << TRI_FRACTION_BITS; |
| ey2 += dx20 << TRI_FRACTION_BITS; |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #endif |
| |
| // the following routine fills a triangle via edge stepping, which |
| // unfortunately requires divisions in the setup phase to get right, |
| // it should probably only be used for relatively large trianges |
| |
| |
| // x = y*DX/DY (ou DX and DY are constants, DY > 0, et y >= 0) |
| // |
| // for an equation of the type: |
| // x' = y*K/2^p (with K and p constants "carefully chosen") |
| // |
| // We can now do a DDA without precision loss. We define 'e' by: |
| // x' - x = y*(DX/DY - K/2^p) = y*e |
| // |
| // If we choose K = round(DX*2^p/DY) then, |
| // abs(e) <= 1/2^(p+1) by construction |
| // |
| // therefore abs(x'-x) = y*abs(e) <= y/2^(p+1) <= DY/2^(p+1) <= DMAX/2^(p+1) |
| // |
| // which means that if DMAX <= 2^p, therefore abs(x-x') <= 1/2, including |
| // at the last line. In fact, it's even a strict inequality except in one |
| // extrem case (DY == DMAX et e = +/- 1/2) |
| // |
| // Applying that to our coordinates, we need 2^p >= 4096*16 = 65536 |
| // so p = 16 is enough, we're so lucky! |
| |
| const int TRI_ITERATORS_BITS = 16; |
| |
| struct Edge |
| { |
| int32_t x; // edge position in 16.16 coordinates |
| int32_t x_incr; // on each step, increment x by that amount |
| int32_t y_top; // starting scanline, 16.4 format |
| int32_t y_bot; |
| }; |
| |
| static void |
| edge_dump( Edge* edge ) |
| { |
| ALOGI( " top=%d (%.3f) bot=%d (%.3f) x=%d (%.3f) ix=%d (%.3f)", |
| edge->y_top, edge->y_top/float(TRI_ONE), |
| edge->y_bot, edge->y_bot/float(TRI_ONE), |
| edge->x, edge->x/float(FIXED_ONE), |
| edge->x_incr, edge->x_incr/float(FIXED_ONE) ); |
| } |
| |
| static void |
| triangle_dump_edges( Edge* edges, |
| int count ) |
| { |
| ALOGI( "%d edge%s:\n", count, count == 1 ? "" : "s" ); |
| for ( ; count > 0; count--, edges++ ) |
| edge_dump( edges ); |
| } |
| |
| // the following function sets up an edge, it assumes |
| // that ymin and ymax are in already in the 'reduced' |
| // format |
| static __attribute__((noinline)) |
| void edge_setup( |
| Edge* edges, |
| int* pcount, |
| const GGLcoord* p1, |
| const GGLcoord* p2, |
| int32_t ymin, |
| int32_t ymax ) |
| { |
| const GGLfixed* top = p1; |
| const GGLfixed* bot = p2; |
| Edge* edge = edges + *pcount; |
| |
| if (top[1] > bot[1]) { |
| swap(top, bot); |
| } |
| |
| int y1 = top[1] | 1; |
| int y2 = bot[1] | 1; |
| int dy = y2 - y1; |
| |
| if ( dy == 0 || y1 > ymax || y2 < ymin ) |
| return; |
| |
| if ( y1 > ymin ) |
| ymin = TRI_SNAP_NEXT_HALF(y1); |
| |
| if ( y2 < ymax ) |
| ymax = TRI_SNAP_PREV_HALF(y2); |
| |
| if ( ymin > ymax ) // when the edge doesn't cross any scanline |
| return; |
| |
| const int x1 = top[0]; |
| const int dx = bot[0] - x1; |
| const int shift = TRI_ITERATORS_BITS - TRI_FRACTION_BITS; |
| |
| // setup edge fields |
| // We add 0.5 to edge->x here because it simplifies the rounding |
| // in triangle_sweep_edges() -- this doesn't change the ordering of 'x' |
| edge->x = (x1 << shift) + (1LU << (TRI_ITERATORS_BITS-1)); |
| edge->x_incr = 0; |
| edge->y_top = ymin; |
| edge->y_bot = ymax; |
| |
| if (ggl_likely(ymin <= ymax && dx)) { |
| edge->x_incr = gglDivQ16(dx, dy); |
| } |
| if (ggl_likely(y1 < ymin)) { |
| int32_t xadjust = (edge->x_incr * (ymin-y1)) >> TRI_FRACTION_BITS; |
| edge->x += xadjust; |
| } |
| |
| ++*pcount; |
| } |
| |
| |
| static void |
| triangle_sweep_edges( Edge* left, |
| Edge* right, |
| int ytop, |
| int ybot, |
| context_t* c ) |
| { |
| int count = ((ybot - ytop)>>TRI_FRACTION_BITS) + 1; |
| if (count<=0) return; |
| |
| // sort the edges horizontally |
| if ((left->x > right->x) || |
| ((left->x == right->x) && (left->x_incr > right->x_incr))) { |
| swap(left, right); |
| } |
| |
| int left_x = left->x; |
| int right_x = right->x; |
| const int left_xi = left->x_incr; |
| const int right_xi = right->x_incr; |
| left->x += left_xi * count; |
| right->x += right_xi * count; |
| |
| const int xmin = c->state.scissor.left; |
| const int xmax = c->state.scissor.right; |
| do { |
| // horizontal scissoring |
| const int32_t xl = max(left_x >> TRI_ITERATORS_BITS, xmin); |
| const int32_t xr = min(right_x >> TRI_ITERATORS_BITS, xmax); |
| left_x += left_xi; |
| right_x += right_xi; |
| // invoke the scanline rasterizer |
| if (ggl_likely(xl < xr)) { |
| c->iterators.xl = xl; |
| c->iterators.xr = xr; |
| c->scanline(c); |
| } |
| c->step_y(c); |
| } while (--count); |
| } |
| |
| |
| void trianglex_big(void* con, |
| const GGLcoord* v0, const GGLcoord* v1, const GGLcoord* v2) |
| { |
| GGL_CONTEXT(c, con); |
| |
| Edge edges[3]; |
| int num_edges = 0; |
| int32_t ymin = TRI_FROM_INT(c->state.scissor.top) + TRI_HALF; |
| int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom) - TRI_HALF; |
| |
| edge_setup( edges, &num_edges, v0, v1, ymin, ymax ); |
| edge_setup( edges, &num_edges, v0, v2, ymin, ymax ); |
| edge_setup( edges, &num_edges, v1, v2, ymin, ymax ); |
| |
| if (ggl_unlikely(num_edges<2)) // for really tiny triangles that don't |
| return; // cross any scanline centers |
| |
| Edge* left = &edges[0]; |
| Edge* right = &edges[1]; |
| Edge* other = &edges[2]; |
| int32_t y_top = min(left->y_top, right->y_top); |
| int32_t y_bot = max(left->y_bot, right->y_bot); |
| |
| if (ggl_likely(num_edges==3)) { |
| y_top = min(y_top, edges[2].y_top); |
| y_bot = max(y_bot, edges[2].y_bot); |
| if (edges[0].y_top > y_top) { |
| other = &edges[0]; |
| left = &edges[2]; |
| } else if (edges[1].y_top > y_top) { |
| other = &edges[1]; |
| right = &edges[2]; |
| } |
| } |
| |
| c->init_y(c, y_top >> TRI_FRACTION_BITS); |
| |
| int32_t y_mid = min(left->y_bot, right->y_bot); |
| triangle_sweep_edges( left, right, y_top, y_mid, c ); |
| |
| // second scanline sweep loop, if necessary |
| y_mid += TRI_ONE; |
| if (y_mid <= y_bot) { |
| ((left->y_bot == y_bot) ? right : left) = other; |
| if (other->y_top < y_mid) { |
| other->x += other->x_incr; |
| } |
| triangle_sweep_edges( left, right, y_mid, y_bot, c ); |
| } |
| } |
| |
| void aa_trianglex(void* con, |
| const GGLcoord* a, const GGLcoord* b, const GGLcoord* c) |
| { |
| GGLcoord pts[6] = { a[0], a[1], b[0], b[1], c[0], c[1] }; |
| aapolyx(con, pts, 3); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| #if 0 |
| #pragma mark - |
| #endif |
| |
| struct AAEdge |
| { |
| GGLfixed x; // edge position in 12.16 coordinates |
| GGLfixed x_incr; // on each y step, increment x by that amount |
| GGLfixed y_incr; // on each x step, increment y by that amount |
| int16_t y_top; // starting scanline, 12.4 format |
| int16_t y_bot; // starting scanline, 12.4 format |
| void dump(); |
| }; |
| |
| void AAEdge::dump() |
| { |
| float tri = 1.0f / TRI_ONE; |
| float iter = 1.0f / (1<<TRI_ITERATORS_BITS); |
| float fix = 1.0f / FIXED_ONE; |
| ALOGD( "x=%08x (%.3f), " |
| "x_incr=%08x (%.3f), y_incr=%08x (%.3f), " |
| "y_top=%08x (%.3f), y_bot=%08x (%.3f) ", |
| x, x*fix, |
| x_incr, x_incr*iter, |
| y_incr, y_incr*iter, |
| y_top, y_top*tri, |
| y_bot, y_bot*tri ); |
| } |
| |
| // the following function sets up an edge, it assumes |
| // that ymin and ymax are in already in the 'reduced' |
| // format |
| static __attribute__((noinline)) |
| void aa_edge_setup( |
| AAEdge* edges, |
| int* pcount, |
| const GGLcoord* p1, |
| const GGLcoord* p2, |
| int32_t ymin, |
| int32_t ymax ) |
| { |
| const GGLfixed* top = p1; |
| const GGLfixed* bot = p2; |
| AAEdge* edge = edges + *pcount; |
| |
| if (top[1] > bot[1]) |
| swap(top, bot); |
| |
| int y1 = top[1]; |
| int y2 = bot[1]; |
| int dy = y2 - y1; |
| |
| if (dy==0 || y1>ymax || y2<ymin) |
| return; |
| |
| if (y1 > ymin) |
| ymin = y1; |
| |
| if (y2 < ymax) |
| ymax = y2; |
| |
| const int x1 = top[0]; |
| const int dx = bot[0] - x1; |
| const int shift = FIXED_BITS - TRI_FRACTION_BITS; |
| |
| // setup edge fields |
| edge->x = x1 << shift; |
| edge->x_incr = 0; |
| edge->y_top = ymin; |
| edge->y_bot = ymax; |
| edge->y_incr = 0x7FFFFFFF; |
| |
| if (ggl_likely(ymin <= ymax && dx)) { |
| edge->x_incr = gglDivQ16(dx, dy); |
| if (dx != 0) { |
| edge->y_incr = abs(gglDivQ16(dy, dx)); |
| } |
| } |
| if (ggl_likely(y1 < ymin)) { |
| int32_t xadjust = (edge->x_incr * (ymin-y1)) |
| >> (TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS); |
| edge->x += xadjust; |
| } |
| |
| ++*pcount; |
| } |
| |
| |
| typedef int (*compar_t)(const void*, const void*); |
| static int compare_edges(const AAEdge *e0, const AAEdge *e1) { |
| if (e0->y_top > e1->y_top) return 1; |
| if (e0->y_top < e1->y_top) return -1; |
| if (e0->x > e1->x) return 1; |
| if (e0->x < e1->x) return -1; |
| if (e0->x_incr > e1->x_incr) return 1; |
| if (e0->x_incr < e1->x_incr) return -1; |
| return 0; // same edges, should never happen |
| } |
| |
| static inline |
| void SET_COVERAGE(int16_t*& p, int32_t value, ssize_t n) |
| { |
| android_memset16((uint16_t*)p, value, n*2); |
| p += n; |
| } |
| |
| static inline |
| void ADD_COVERAGE(int16_t*& p, int32_t value) |
| { |
| value = *p + value; |
| if (value >= 0x8000) |
| value = 0x7FFF; |
| *p++ = value; |
| } |
| |
| static inline |
| void SUB_COVERAGE(int16_t*& p, int32_t value) |
| { |
| value = *p - value; |
| value &= ~(value>>31); |
| *p++ = value; |
| } |
| |
| void aapolyx(void* con, |
| const GGLcoord* pts, int count) |
| { |
| /* |
| * NOTE: This routine assumes that the polygon has been clipped to the |
| * viewport already, that is, no vertex lies outside of the framebuffer. |
| * If this happens, the code below won't corrupt memory but the |
| * coverage values may not be correct. |
| */ |
| |
| GGL_CONTEXT(c, con); |
| |
| // we do only quads for now (it's used for thick lines) |
| if ((count>4) || (count<2)) return; |
| |
| // take scissor into account |
| const int xmin = c->state.scissor.left; |
| const int xmax = c->state.scissor.right; |
| if (xmin >= xmax) return; |
| |
| // generate edges from the vertices |
| int32_t ymin = TRI_FROM_INT(c->state.scissor.top); |
| int32_t ymax = TRI_FROM_INT(c->state.scissor.bottom); |
| if (ymin >= ymax) return; |
| |
| AAEdge edges[4]; |
| int num_edges = 0; |
| GGLcoord const * p = pts; |
| for (int i=0 ; i<count-1 ; i++, p+=2) { |
| aa_edge_setup(edges, &num_edges, p, p+2, ymin, ymax); |
| } |
| aa_edge_setup(edges, &num_edges, p, pts, ymin, ymax ); |
| if (ggl_unlikely(num_edges<2)) |
| return; |
| |
| // sort the edge list top to bottom, left to right. |
| qsort(edges, num_edges, sizeof(AAEdge), (compar_t)compare_edges); |
| |
| int16_t* const covPtr = c->state.buffers.coverage; |
| memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr)); |
| |
| // now, sweep all edges in order |
| // start with the 2 first edges. We know that they share their top |
| // vertex, by construction. |
| int i = 2; |
| AAEdge* left = &edges[0]; |
| AAEdge* right = &edges[1]; |
| int32_t yt = left->y_top; |
| GGLfixed l = left->x; |
| GGLfixed r = right->x; |
| int retire = 0; |
| int16_t* coverage; |
| |
| // at this point we can initialize the rasterizer |
| c->init_y(c, yt>>TRI_FRACTION_BITS); |
| c->iterators.xl = xmax; |
| c->iterators.xr = xmin; |
| |
| do { |
| int32_t y = min(min(left->y_bot, right->y_bot), TRI_FLOOR(yt + TRI_ONE)); |
| const int32_t shift = TRI_FRACTION_BITS + TRI_ITERATORS_BITS - FIXED_BITS; |
| const int cf_shift = (1 + TRI_FRACTION_BITS*2 + TRI_ITERATORS_BITS - 15); |
| |
| // compute xmin and xmax for the left edge |
| GGLfixed l_min = gglMulAddx(left->x_incr, y - left->y_top, left->x, shift); |
| GGLfixed l_max = l; |
| l = l_min; |
| if (l_min > l_max) |
| swap(l_min, l_max); |
| |
| // compute xmin and xmax for the right edge |
| GGLfixed r_min = gglMulAddx(right->x_incr, y - right->y_top, right->x, shift); |
| GGLfixed r_max = r; |
| r = r_min; |
| if (r_min > r_max) |
| swap(r_min, r_max); |
| |
| // make sure we're not touching coverage values outside of the |
| // framebuffer |
| l_min &= ~(l_min>>31); |
| r_min &= ~(r_min>>31); |
| l_max &= ~(l_max>>31); |
| r_max &= ~(r_max>>31); |
| if (gglFixedToIntFloor(l_min) >= xmax) l_min = gglIntToFixed(xmax)-1; |
| if (gglFixedToIntFloor(r_min) >= xmax) r_min = gglIntToFixed(xmax)-1; |
| if (gglFixedToIntCeil(l_max) >= xmax) l_max = gglIntToFixed(xmax)-1; |
| if (gglFixedToIntCeil(r_max) >= xmax) r_max = gglIntToFixed(xmax)-1; |
| |
| // compute the integer versions of the above |
| const GGLfixed l_min_i = gglFloorx(l_min); |
| const GGLfixed l_max_i = gglCeilx (l_max); |
| const GGLfixed r_min_i = gglFloorx(r_min); |
| const GGLfixed r_max_i = gglCeilx (r_max); |
| |
| // clip horizontally using the scissor |
| const int xml = max(xmin, gglFixedToIntFloor(l_min_i)); |
| const int xmr = min(xmax, gglFixedToIntFloor(r_max_i)); |
| |
| // if we just stepped to a new scanline, render the previous one. |
| // and clear the coverage buffer |
| if (retire) { |
| if (c->iterators.xl < c->iterators.xr) |
| c->scanline(c); |
| c->step_y(c); |
| memset(covPtr+xmin, 0, (xmax-xmin)*sizeof(*covPtr)); |
| c->iterators.xl = xml; |
| c->iterators.xr = xmr; |
| } else { |
| // update the horizontal range of this scanline |
| c->iterators.xl = min(c->iterators.xl, xml); |
| c->iterators.xr = max(c->iterators.xr, xmr); |
| } |
| |
| coverage = covPtr + gglFixedToIntFloor(l_min_i); |
| if (l_min_i == gglFloorx(l_max)) { |
| |
| /* |
| * fully traverse this pixel vertically |
| * l_max |
| * +-----/--+ yt |
| * | / | |
| * | / | |
| * | / | |
| * +-/------+ y |
| * l_min (l_min_i + TRI_ONE) |
| */ |
| |
| GGLfixed dx = l_max - l_min; |
| int32_t dy = y - yt; |
| int cf = gglMulx((dx >> 1) + (l_min_i + FIXED_ONE - l_max), dy, |
| FIXED_BITS + TRI_FRACTION_BITS - 15); |
| ADD_COVERAGE(coverage, cf); |
| // all pixels on the right have cf = 1.0 |
| } else { |
| /* |
| * spans several pixels in one scanline |
| * l_max |
| * +--------+--/-----+ yt |
| * | |/ | |
| * | /| | |
| * | / | | |
| * +---/----+--------+ y |
| * l_min (l_min_i + TRI_ONE) |
| */ |
| |
| // handle the first pixel separately... |
| const int32_t y_incr = left->y_incr; |
| int32_t dx = TRI_FROM_FIXED(l_min_i - l_min) + TRI_ONE; |
| int32_t cf = (dx * dx * y_incr) >> cf_shift; |
| ADD_COVERAGE(coverage, cf); |
| |
| // following pixels get covered by y_incr, but we need |
| // to fix-up the cf to account for previous partial pixel |
| dx = TRI_FROM_FIXED(l_min - l_min_i); |
| cf -= (dx * dx * y_incr) >> cf_shift; |
| for (int x = l_min_i+FIXED_ONE ; x < l_max_i-FIXED_ONE ; x += FIXED_ONE) { |
| cf += y_incr >> (TRI_ITERATORS_BITS-15); |
| ADD_COVERAGE(coverage, cf); |
| } |
| |
| // and the last pixel |
| dx = TRI_FROM_FIXED(l_max - l_max_i) - TRI_ONE; |
| cf += (dx * dx * y_incr) >> cf_shift; |
| ADD_COVERAGE(coverage, cf); |
| } |
| |
| // now, fill up all fully covered pixels |
| coverage = covPtr + gglFixedToIntFloor(l_max_i); |
| int cf = ((y - yt) << (15 - TRI_FRACTION_BITS)); |
| if (ggl_likely(cf >= 0x8000)) { |
| SET_COVERAGE(coverage, 0x7FFF, ((r_max - l_max_i)>>FIXED_BITS)+1); |
| } else { |
| for (int x=l_max_i ; x<r_max ; x+=FIXED_ONE) { |
| ADD_COVERAGE(coverage, cf); |
| } |
| } |
| |
| // subtract the coverage of the right edge |
| coverage = covPtr + gglFixedToIntFloor(r_min_i); |
| if (r_min_i == gglFloorx(r_max)) { |
| GGLfixed dx = r_max - r_min; |
| int32_t dy = y - yt; |
| int cf = gglMulx((dx >> 1) + (r_min_i + FIXED_ONE - r_max), dy, |
| FIXED_BITS + TRI_FRACTION_BITS - 15); |
| SUB_COVERAGE(coverage, cf); |
| // all pixels on the right have cf = 1.0 |
| } else { |
| // handle the first pixel separately... |
| const int32_t y_incr = right->y_incr; |
| int32_t dx = TRI_FROM_FIXED(r_min_i - r_min) + TRI_ONE; |
| int32_t cf = (dx * dx * y_incr) >> cf_shift; |
| SUB_COVERAGE(coverage, cf); |
| |
| // following pixels get covered by y_incr, but we need |
| // to fix-up the cf to account for previous partial pixel |
| dx = TRI_FROM_FIXED(r_min - r_min_i); |
| cf -= (dx * dx * y_incr) >> cf_shift; |
| for (int x = r_min_i+FIXED_ONE ; x < r_max_i-FIXED_ONE ; x += FIXED_ONE) { |
| cf += y_incr >> (TRI_ITERATORS_BITS-15); |
| SUB_COVERAGE(coverage, cf); |
| } |
| |
| // and the last pixel |
| dx = TRI_FROM_FIXED(r_max - r_max_i) - TRI_ONE; |
| cf += (dx * dx * y_incr) >> cf_shift; |
| SUB_COVERAGE(coverage, cf); |
| } |
| |
| // did we reach the end of an edge? if so, get a new one. |
| if (y == left->y_bot || y == right->y_bot) { |
| // bail out if we're done |
| if (i>=num_edges) |
| break; |
| if (y == left->y_bot) |
| left = &edges[i++]; |
| if (y == right->y_bot) |
| right = &edges[i++]; |
| } |
| |
| // next scanline |
| yt = y; |
| |
| // did we just finish a scanline? |
| retire = (y << (32-TRI_FRACTION_BITS)) == 0; |
| } while (true); |
| |
| // render the last scanline |
| if (c->iterators.xl < c->iterators.xr) |
| c->scanline(c); |
| } |
| |
| }; // namespace android |