/* 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. */ #define LOG_TAG "pixelflinger-trap" #include #include #include #include #include #include "trap.h" #include "picker.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 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 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 ; iscanline(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 ; iscanline(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(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 ; xl0 && 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 ( ; xr0 && 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< bot[1]) swap(top, bot); int y1 = top[1]; int y2 = bot[1]; int dy = y2 - y1; if (dy==0 || y1>ymax || y2 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 ; istate.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> 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