/* ** Copyright 1991, Silicon Graphics, Inc. ** All Rights Reserved. ** ** This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.; ** the contents of this file may not be disclosed to third parties, copied or ** duplicated in any form, in whole or in part, without the prior written ** permission of Silicon Graphics, Inc. ** ** RESTRICTED RIGHTS LEGEND: ** Use, duplication or disclosure by the Government is subject to restrictions ** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data ** and Computer Software clause at DFARS 252.227-7013, and/or in similar or ** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - ** rights reserved under the Copyright Laws of the United States. ** ** $Revision: 1.10 $ ** $Date: 1993/06/18 00:30:15 $ */ #include "precomp.h" #pragma hdrstop #include /* ** This is a little wierd. What it does is to dither "comp" into the high ** n-4 bits, and add 16 * antiAliasPercent. Dithering of the low bits is ** left to the usual methods (the store and span procs, for example). */ __GLfloat __glBuildAntiAliasIndex(__GLfloat index, __GLfloat antiAliasPercent) { GLint newlowbits; newlowbits = (GLint)((__GL_CI_ANTI_ALIAS_DIVISOR - 1) * antiAliasPercent + (__GLfloat) 0.5); return (((int) index) & ~(__GL_CI_ANTI_ALIAS_DIVISOR - 1)) | newlowbits; } /************************************************************************/ /* ** To anti-alias points the below code operates a simple algrorithim that ** sub-samples the bounding box of the pixel area covered by the point. ** At each sub-sample the distance from the sample to the center of the ** point is computed and compared against the distance from the edge of ** the circle to the center. If the computed distance is <= the edge ** distance then the sample is inside the circle. All the samples for a ** particular pixel center are summed up and then the resulting value is ** divided by the total samples in the pixel. This gives us a coverage value ** to use to adjust the fragment alpha with before storing (there is ** an analagous affect when color index anti-aliasing is being done). ** ** The code below implements this simple algrorithim, but has been tuned ** so it might be difficult to translate. Basically, every possible operation ** that could be moved out of the Coverage code (i.e., invariants across ** the coverage test) has been done. Also, the minimal area is sampled ** over. */ /* Code below knows alot about these constants so beware */ #define __GL_FILTER_SIZE __glOne #define __GL_HALF_FILTER_SIZE __glHalf #define __GL_SAMPLES 4 #define __GL_SAMPLE_HIT ((__GLfloat) 0.0625) /* 1 / (4*4) */ #define __GL_SAMPLE_DELTA ((__GLfloat) 0.25) /* 1 / 4 */ #define __GL_HALF_SAMPLE_DELTA ((__GLfloat) 0.125) /* -halffilter + half delta */ #define __GL_COORD_ADJUST ((__GLfloat) -0.375) /* ** Return an estimate of the pixel coverage using sub-sampling. ** ** NOTE: The subtraction of xCenter,yCenter has been moved into the ** caller to save time. Consequently the starting coordinate may not be ** on a pixel center, but thats ok. */ static __GLfloat Coverage(__GLfloat xStart, __GLfloat yStart, __GLfloat radiusSquared) { GLint i; __GLfloat delta, yBottom, sampleX, sampleY; __GLfloat hits, hitsInc; /* ** Get starting sample x & y positions. We take our starting ** coordinate, back off half a filter size then add half a delta to ** it. This constrains the sampling to lie entirely within the ** pixel, never on the edges of the pixel. The constants above ** have this adjustment pre-computed. */ sampleX = xStart + __GL_COORD_ADJUST; yBottom = yStart + __GL_COORD_ADJUST; delta = __GL_SAMPLE_DELTA; hits = __glZero; hitsInc = __GL_SAMPLE_HIT; for (i = __GL_SAMPLES; --i >= 0; ) { __GLfloat check = radiusSquared - sampleX * sampleX; /* Unrolled inner loop - change this code if __GL_SAMPLES changes */ sampleY = yBottom; if (sampleY * sampleY <= check) { hits += hitsInc; } sampleY += delta; if (sampleY * sampleY <= check) { hits += hitsInc; } sampleY += delta; if (sampleY * sampleY <= check) { hits += hitsInc; } sampleY += delta; if (sampleY * sampleY <= check) { hits += hitsInc; } sampleX += delta; } return hits; } void FASTCALL __glRenderAntiAliasedRGBPoint(__GLcontext *gc, __GLvertex *vx) { __GLfloat xCenter, yCenter, radius, radiusSquared, coverage, x, y; __GLfloat zero, one, oldAlpha, xStart; __GLfloat tmp; __GLfragment frag; GLint w, width, height, ixLeft, iyBottom; GLuint modeFlags = gc->polygon.shader.modeFlags; /* ** Determine area to compute coverage over. The area is bloated by ** the filter's width & height implicitly. By truncating to integer ** (NOTE: the x,y coordinate is always positive here) we are ** guaranteed to find the lowest coordinate that needs examination ** because of the nature of circles. Similarly, by truncating the ** ending coordinate and adding one we get the pixel just past the ** upper/right edge of the circle. */ radius = gc->state.point.smoothSize * __glHalf; radiusSquared = radius * radius; xCenter = vx->window.x; yCenter = vx->window.y; /* Truncate down to get starting coordinate */ tmp = xCenter-radius; ixLeft = __GL_VERTEX_FLOAT_TO_INT(tmp); tmp = yCenter-radius; iyBottom = __GL_VERTEX_FLOAT_TO_INT(tmp); /* ** Truncate down and add 1 to get the ending coordinate, then subtract ** out the start to get the width & height. */ tmp = xCenter+radius; width = __GL_VERTEX_FLOAT_TO_INT(tmp) + 1 - ixLeft; tmp = yCenter+radius; height = __GL_VERTEX_FLOAT_TO_INT(tmp) + 1 - iyBottom; /* ** Setup fragment. The fragment base color will be constant ** (approximately) across the entire pixel. The only thing that will ** change is the alpha (for rgb) or the red component (for color ** index). */ frag.z = (__GLzValue)vx->window.z; frag.color = *vx->color; if (modeFlags & __GL_SHADE_TEXTURE) { (*gc->procs.texture)(gc, &frag.color, vx->texture.x, vx->texture.y, __glOne); } if (gc->polygon.shader.modeFlags & __GL_SHADE_COMPUTE_FOG) { (*gc->procs.fogPoint)(gc, &frag, vx->eyeZ); } else if ((gc->polygon.shader.modeFlags & __GL_SHADE_INTERP_FOG) || ((modeFlags & (__GL_SHADE_CHEAP_FOG | __GL_SHADE_SMOOTH_LIGHT)) == __GL_SHADE_CHEAP_FOG)) { (*gc->procs.fogColor)(gc, &frag.color, &frag.color, vx->fog); } /* ** Now render the circle centered on xCenter,yCenter. Move the ** subtraction of xCenter,yCenter outside of the loop to doing ** it up front in xStart and y. This way the coverage code can ** assume the incoming starting coordinate has been properly ** adjusted. */ zero = __glZero; one = __glOne; oldAlpha = frag.color.a; xStart = ixLeft + __glHalf - xCenter; y = iyBottom + __glHalf - yCenter; frag.y = iyBottom; while (--height >= 0) { x = xStart; frag.x = ixLeft; for (w = width; --w >= 0; ) { coverage = Coverage(x, y, radiusSquared); if (coverage > zero) { frag.color.a = oldAlpha * coverage; (*gc->procs.store)(gc->drawBuffer, &frag); } x += one; frag.x++; } y += one; frag.y++; } } void FASTCALL __glRenderAntiAliasedCIPoint(__GLcontext *gc, __GLvertex *vx) { __GLfloat xCenter, yCenter, radius, radiusSquared, coverage, x, y; __GLfloat zero, one, oldIndex, xStart; __GLfloat tmp; __GLfragment frag; GLint w, width, height, ixLeft, iyBottom; /* ** Determine area to compute coverage over. The area is bloated by ** the filter's width & height implicitly. By truncating to integer ** (NOTE: the x,y coordinate is always positive here) we are ** guaranteed to find the lowest coordinate that needs examination ** because of the nature of circles. Similarly, by truncating the ** ending coordinate and adding one we get the pixel just past the ** upper/right edge of the circle. */ radius = gc->state.point.smoothSize * __glHalf; radiusSquared = radius * radius; xCenter = vx->window.x; yCenter = vx->window.y; /* Truncate down to get starting coordinate */ tmp = xCenter-radius; ixLeft = __GL_VERTEX_FLOAT_TO_INT(tmp); tmp = yCenter-radius; iyBottom = __GL_VERTEX_FLOAT_TO_INT(tmp); /* ** Truncate down and add 1 to get the ending coordinate, then subtract ** out the start to get the width & height. */ tmp = xCenter+radius; width = __GL_VERTEX_FLOAT_TO_INT(tmp) + 1 - ixLeft; tmp = yCenter+radius; height = __GL_VERTEX_FLOAT_TO_INT(tmp) + 1 - iyBottom; /* ** Setup fragment. The fragment base color will be constant ** (approximately) across the entire pixel. The only thing that will ** change is the alpha (for rgb) or the red component (for color ** index). */ frag.z = (__GLzValue)vx->window.z; frag.color.r = vx->color->r; if (gc->polygon.shader.modeFlags & __GL_SHADE_COMPUTE_FOG) { (*gc->procs.fogPoint)(gc, &frag, vx->eyeZ); } else if ((gc->polygon.shader.modeFlags & __GL_SHADE_INTERP_FOG) || ((gc->polygon.shader.modeFlags & (__GL_SHADE_CHEAP_FOG | __GL_SHADE_SMOOTH_LIGHT)) == __GL_SHADE_CHEAP_FOG)) { (*gc->procs.fogColor)(gc, &frag.color, &frag.color, vx->fog); } /* ** Now render the circle centered on xCenter,yCenter. Move the ** subtraction of xCenter,yCenter outside of the loop to doing ** it up front in xStart and y. This way the coverage code can ** assume the incoming starting coordinate has been properly ** adjusted. */ zero = __glZero; one = __glOne; oldIndex = frag.color.r; xStart = ixLeft + __glHalf - xCenter; y = iyBottom + __glHalf - yCenter; frag.y = iyBottom; while (--height >= 0) { x = xStart; frag.x = ixLeft; for (w = width; --w >= 0; ) { coverage = Coverage(x, y, radiusSquared); if (coverage > zero) { frag.color.r = __glBuildAntiAliasIndex(oldIndex, coverage); (*gc->procs.store)(gc->drawBuffer, &frag); } x += one; frag.x++; } y += one; frag.y++; } }