1/*
2 * Mesa 3-D graphics library
3 * Version:  7.1
4 *
5 * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25
26#include "main/glheader.h"
27#include "main/colormac.h"
28#include "main/macros.h"
29#include "s_context.h"
30#include "s_feedback.h"
31#include "s_points.h"
32#include "s_span.h"
33
34
35/**
36 * Used to cull points with invalid coords
37 */
38#define CULL_INVALID(V)                              \
39   do {                                              \
40      float tmp = (V)->attrib[FRAG_ATTRIB_WPOS][0]   \
41                + (V)->attrib[FRAG_ATTRIB_WPOS][1];  \
42      if (IS_INF_OR_NAN(tmp))                        \
43	 return;                                     \
44   } while(0)
45
46
47
48/**
49 * Get/compute the point size.
50 * The size may come from a vertex shader, or computed with attentuation
51 * or just the glPointSize value.
52 * Must also clamp to user-defined range and implmentation limits.
53 */
54static inline GLfloat
55get_size(const struct gl_context *ctx, const SWvertex *vert, GLboolean smoothed)
56{
57   GLfloat size;
58
59   if (ctx->Point._Attenuated || ctx->VertexProgram.PointSizeEnabled) {
60      /* use vertex's point size */
61      size = vert->pointSize;
62   }
63   else {
64      /* use constant point size */
65      size = ctx->Point.Size;
66   }
67   /* always clamp to user-specified limits */
68   size = CLAMP(size, ctx->Point.MinSize, ctx->Point.MaxSize);
69   /* clamp to implementation limits */
70   if (smoothed)
71      size = CLAMP(size, ctx->Const.MinPointSizeAA, ctx->Const.MaxPointSizeAA);
72   else
73      size = CLAMP(size, ctx->Const.MinPointSize, ctx->Const.MaxPointSize);
74
75   return size;
76}
77
78
79/**
80 * Draw a point sprite
81 */
82static void
83sprite_point(struct gl_context *ctx, const SWvertex *vert)
84{
85   SWcontext *swrast = SWRAST_CONTEXT(ctx);
86   SWspan span;
87   GLfloat size;
88   GLuint tCoords[MAX_TEXTURE_COORD_UNITS + 1];
89   GLuint numTcoords = 0;
90   GLfloat t0, dtdy;
91
92   CULL_INVALID(vert);
93
94   /* z coord */
95   if (ctx->DrawBuffer->Visual.depthBits <= 16)
96      span.z = FloatToFixed(vert->attrib[FRAG_ATTRIB_WPOS][2] + 0.5F);
97   else
98      span.z = (GLuint) (vert->attrib[FRAG_ATTRIB_WPOS][2] + 0.5F);
99   span.zStep = 0;
100
101   size = get_size(ctx, vert, GL_FALSE);
102
103   /* span init */
104   INIT_SPAN(span, GL_POINT);
105   span.interpMask = SPAN_Z | SPAN_RGBA;
106
107   span.facing = swrast->PointLineFacing;
108
109   span.red   = ChanToFixed(vert->color[0]);
110   span.green = ChanToFixed(vert->color[1]);
111   span.blue  = ChanToFixed(vert->color[2]);
112   span.alpha = ChanToFixed(vert->color[3]);
113   span.redStep = 0;
114   span.greenStep = 0;
115   span.blueStep = 0;
116   span.alphaStep = 0;
117
118   /* need these for fragment programs */
119   span.attrStart[FRAG_ATTRIB_WPOS][3] = 1.0F;
120   span.attrStepX[FRAG_ATTRIB_WPOS][3] = 0.0F;
121   span.attrStepY[FRAG_ATTRIB_WPOS][3] = 0.0F;
122
123   {
124      GLfloat s, r, dsdx;
125
126      /* texcoord / pointcoord interpolants */
127      s = 0.0F;
128      dsdx = 1.0F / size;
129      if (ctx->Point.SpriteOrigin == GL_LOWER_LEFT) {
130         dtdy = 1.0F / size;
131         t0 = 0.5F * dtdy;
132      }
133      else {
134         /* GL_UPPER_LEFT */
135         dtdy = -1.0F / size;
136         t0 = 1.0F + 0.5F * dtdy;
137      }
138
139      ATTRIB_LOOP_BEGIN
140         if (attr >= FRAG_ATTRIB_TEX0 && attr <= FRAG_ATTRIB_TEX7) {
141            /* a texcoord attribute */
142            const GLuint u = attr - FRAG_ATTRIB_TEX0;
143            ASSERT(u < Elements(ctx->Point.CoordReplace));
144            if (ctx->Point.CoordReplace[u]) {
145               tCoords[numTcoords++] = attr;
146
147               if (ctx->Point.SpriteRMode == GL_ZERO)
148                  r = 0.0F;
149               else if (ctx->Point.SpriteRMode == GL_S)
150                  r = vert->attrib[attr][0];
151               else /* GL_R */
152                  r = vert->attrib[attr][2];
153
154               span.attrStart[attr][0] = s;
155               span.attrStart[attr][1] = 0.0; /* overwritten below */
156               span.attrStart[attr][2] = r;
157               span.attrStart[attr][3] = 1.0;
158
159               span.attrStepX[attr][0] = dsdx;
160               span.attrStepX[attr][1] = 0.0;
161               span.attrStepX[attr][2] = 0.0;
162               span.attrStepX[attr][3] = 0.0;
163
164               span.attrStepY[attr][0] = 0.0;
165               span.attrStepY[attr][1] = dtdy;
166               span.attrStepY[attr][2] = 0.0;
167               span.attrStepY[attr][3] = 0.0;
168
169               continue;
170            }
171         }
172         else if (attr == FRAG_ATTRIB_PNTC) {
173            /* GLSL gl_PointCoord.xy (.zw undefined) */
174            span.attrStart[FRAG_ATTRIB_PNTC][0] = 0.0;
175            span.attrStart[FRAG_ATTRIB_PNTC][1] = 0.0; /* t0 set below */
176            span.attrStepX[FRAG_ATTRIB_PNTC][0] = dsdx;
177            span.attrStepX[FRAG_ATTRIB_PNTC][1] = 0.0;
178            span.attrStepY[FRAG_ATTRIB_PNTC][0] = 0.0;
179            span.attrStepY[FRAG_ATTRIB_PNTC][1] = dtdy;
180            tCoords[numTcoords++] = FRAG_ATTRIB_PNTC;
181            continue;
182         }
183         /* use vertex's texcoord/attrib */
184         COPY_4V(span.attrStart[attr], vert->attrib[attr]);
185         ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0);
186         ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0);
187      ATTRIB_LOOP_END;
188   }
189
190   /* compute pos, bounds and render */
191   {
192      const GLfloat x = vert->attrib[FRAG_ATTRIB_WPOS][0];
193      const GLfloat y = vert->attrib[FRAG_ATTRIB_WPOS][1];
194      GLint iSize = (GLint) (size + 0.5F);
195      GLint xmin, xmax, ymin, ymax, iy;
196      GLint iRadius;
197      GLfloat tcoord = t0;
198
199      iSize = MAX2(1, iSize);
200      iRadius = iSize / 2;
201
202      if (iSize & 1) {
203         /* odd size */
204         xmin = (GLint) (x - iRadius);
205         xmax = (GLint) (x + iRadius);
206         ymin = (GLint) (y - iRadius);
207         ymax = (GLint) (y + iRadius);
208      }
209      else {
210         /* even size */
211         /* 0.501 factor allows conformance to pass */
212         xmin = (GLint) (x + 0.501) - iRadius;
213         xmax = xmin + iSize - 1;
214         ymin = (GLint) (y + 0.501) - iRadius;
215         ymax = ymin + iSize - 1;
216      }
217
218      /* render spans */
219      for (iy = ymin; iy <= ymax; iy++) {
220         GLuint i;
221         /* setup texcoord T for this row */
222         for (i = 0; i < numTcoords; i++) {
223            span.attrStart[tCoords[i]][1] = tcoord;
224         }
225
226         /* these might get changed by span clipping */
227         span.x = xmin;
228         span.y = iy;
229         span.end = xmax - xmin + 1;
230
231         _swrast_write_rgba_span(ctx, &span);
232
233         tcoord += dtdy;
234      }
235   }
236}
237
238
239/**
240 * Draw smooth/antialiased point.  RGB or CI mode.
241 */
242static void
243smooth_point(struct gl_context *ctx, const SWvertex *vert)
244{
245   SWcontext *swrast = SWRAST_CONTEXT(ctx);
246   SWspan span;
247   GLfloat size, alphaAtten;
248
249   CULL_INVALID(vert);
250
251   /* z coord */
252   if (ctx->DrawBuffer->Visual.depthBits <= 16)
253      span.z = FloatToFixed(vert->attrib[FRAG_ATTRIB_WPOS][2] + 0.5F);
254   else
255      span.z = (GLuint) (vert->attrib[FRAG_ATTRIB_WPOS][2] + 0.5F);
256   span.zStep = 0;
257
258   size = get_size(ctx, vert, GL_TRUE);
259
260   /* alpha attenuation / fade factor */
261   if (ctx->Multisample._Enabled) {
262      if (vert->pointSize >= ctx->Point.Threshold) {
263         alphaAtten = 1.0F;
264      }
265      else {
266         GLfloat dsize = vert->pointSize / ctx->Point.Threshold;
267         alphaAtten = dsize * dsize;
268      }
269   }
270   else {
271      alphaAtten = 1.0;
272   }
273   (void) alphaAtten; /* not used */
274
275   /* span init */
276   INIT_SPAN(span, GL_POINT);
277   span.interpMask = SPAN_Z | SPAN_RGBA;
278   span.arrayMask = SPAN_COVERAGE | SPAN_MASK;
279
280   span.facing = swrast->PointLineFacing;
281
282   span.red   = ChanToFixed(vert->color[0]);
283   span.green = ChanToFixed(vert->color[1]);
284   span.blue  = ChanToFixed(vert->color[2]);
285   span.alpha = ChanToFixed(vert->color[3]);
286   span.redStep = 0;
287   span.greenStep = 0;
288   span.blueStep = 0;
289   span.alphaStep = 0;
290
291   /* need these for fragment programs */
292   span.attrStart[FRAG_ATTRIB_WPOS][3] = 1.0F;
293   span.attrStepX[FRAG_ATTRIB_WPOS][3] = 0.0F;
294   span.attrStepY[FRAG_ATTRIB_WPOS][3] = 0.0F;
295
296   ATTRIB_LOOP_BEGIN
297      COPY_4V(span.attrStart[attr], vert->attrib[attr]);
298      ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0);
299      ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0);
300   ATTRIB_LOOP_END
301
302   /* compute pos, bounds and render */
303   {
304      const GLfloat x = vert->attrib[FRAG_ATTRIB_WPOS][0];
305      const GLfloat y = vert->attrib[FRAG_ATTRIB_WPOS][1];
306      const GLfloat radius = 0.5F * size;
307      const GLfloat rmin = radius - 0.7071F;  /* 0.7071 = sqrt(2)/2 */
308      const GLfloat rmax = radius + 0.7071F;
309      const GLfloat rmin2 = MAX2(0.0F, rmin * rmin);
310      const GLfloat rmax2 = rmax * rmax;
311      const GLfloat cscale = 1.0F / (rmax2 - rmin2);
312      const GLint xmin = (GLint) (x - radius);
313      const GLint xmax = (GLint) (x + radius);
314      const GLint ymin = (GLint) (y - radius);
315      const GLint ymax = (GLint) (y + radius);
316      GLint ix, iy;
317
318      for (iy = ymin; iy <= ymax; iy++) {
319
320         /* these might get changed by span clipping */
321         span.x = xmin;
322         span.y = iy;
323         span.end = xmax - xmin + 1;
324
325         /* compute coverage for each pixel in span */
326         for (ix = xmin; ix <= xmax; ix++) {
327            const GLfloat dx = ix - x + 0.5F;
328            const GLfloat dy = iy - y + 0.5F;
329            const GLfloat dist2 = dx * dx + dy * dy;
330            GLfloat coverage;
331
332            if (dist2 < rmax2) {
333               if (dist2 >= rmin2) {
334                  /* compute partial coverage */
335                  coverage = 1.0F - (dist2 - rmin2) * cscale;
336               }
337               else {
338                  /* full coverage */
339                  coverage = 1.0F;
340               }
341               span.array->mask[ix - xmin] = 1;
342            }
343            else {
344               /* zero coverage - fragment outside the radius */
345               coverage = 0.0;
346               span.array->mask[ix - xmin] = 0;
347            }
348            span.array->coverage[ix - xmin] = coverage;
349         }
350
351         /* render span */
352         _swrast_write_rgba_span(ctx, &span);
353
354      }
355   }
356}
357
358
359/**
360 * Draw large (size >= 1) non-AA point.  RGB or CI mode.
361 */
362static void
363large_point(struct gl_context *ctx, const SWvertex *vert)
364{
365   SWcontext *swrast = SWRAST_CONTEXT(ctx);
366   SWspan span;
367   GLfloat size;
368
369   CULL_INVALID(vert);
370
371   /* z coord */
372   if (ctx->DrawBuffer->Visual.depthBits <= 16)
373      span.z = FloatToFixed(vert->attrib[FRAG_ATTRIB_WPOS][2] + 0.5F);
374   else
375      span.z = (GLuint) (vert->attrib[FRAG_ATTRIB_WPOS][2] + 0.5F);
376   span.zStep = 0;
377
378   size = get_size(ctx, vert, GL_FALSE);
379
380   /* span init */
381   INIT_SPAN(span, GL_POINT);
382   span.arrayMask = SPAN_XY;
383   span.facing = swrast->PointLineFacing;
384
385   span.interpMask = SPAN_Z | SPAN_RGBA;
386   span.red   = ChanToFixed(vert->color[0]);
387   span.green = ChanToFixed(vert->color[1]);
388   span.blue  = ChanToFixed(vert->color[2]);
389   span.alpha = ChanToFixed(vert->color[3]);
390   span.redStep = 0;
391   span.greenStep = 0;
392   span.blueStep = 0;
393   span.alphaStep = 0;
394
395   /* need these for fragment programs */
396   span.attrStart[FRAG_ATTRIB_WPOS][3] = 1.0F;
397   span.attrStepX[FRAG_ATTRIB_WPOS][3] = 0.0F;
398   span.attrStepY[FRAG_ATTRIB_WPOS][3] = 0.0F;
399
400   ATTRIB_LOOP_BEGIN
401      COPY_4V(span.attrStart[attr], vert->attrib[attr]);
402      ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0);
403      ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0);
404   ATTRIB_LOOP_END
405
406   /* compute pos, bounds and render */
407   {
408      const GLfloat x = vert->attrib[FRAG_ATTRIB_WPOS][0];
409      const GLfloat y = vert->attrib[FRAG_ATTRIB_WPOS][1];
410      GLint iSize = (GLint) (size + 0.5F);
411      GLint xmin, xmax, ymin, ymax, ix, iy;
412      GLint iRadius;
413
414      iSize = MAX2(1, iSize);
415      iRadius = iSize / 2;
416
417      if (iSize & 1) {
418         /* odd size */
419         xmin = (GLint) (x - iRadius);
420         xmax = (GLint) (x + iRadius);
421         ymin = (GLint) (y - iRadius);
422         ymax = (GLint) (y + iRadius);
423      }
424      else {
425         /* even size */
426         /* 0.501 factor allows conformance to pass */
427         xmin = (GLint) (x + 0.501) - iRadius;
428         xmax = xmin + iSize - 1;
429         ymin = (GLint) (y + 0.501) - iRadius;
430         ymax = ymin + iSize - 1;
431      }
432
433      /* generate fragments */
434      span.end = 0;
435      for (iy = ymin; iy <= ymax; iy++) {
436         for (ix = xmin; ix <= xmax; ix++) {
437            span.array->x[span.end] = ix;
438            span.array->y[span.end] = iy;
439            span.end++;
440         }
441      }
442      assert(span.end <= SWRAST_MAX_WIDTH);
443      _swrast_write_rgba_span(ctx, &span);
444   }
445}
446
447
448/**
449 * Draw size=1, single-pixel point
450 */
451static void
452pixel_point(struct gl_context *ctx, const SWvertex *vert)
453{
454   SWcontext *swrast = SWRAST_CONTEXT(ctx);
455   /*
456    * Note that unlike the other functions, we put single-pixel points
457    * into a special span array in order to render as many points as
458    * possible with a single _swrast_write_rgba_span() call.
459    */
460   SWspan *span = &(swrast->PointSpan);
461   GLuint count;
462
463   CULL_INVALID(vert);
464
465   /* Span init */
466   span->interpMask = 0;
467   span->arrayMask = SPAN_XY | SPAN_Z;
468   span->arrayMask |= SPAN_RGBA;
469   /*span->arrayMask |= SPAN_LAMBDA;*/
470   span->arrayAttribs = swrast->_ActiveAttribMask; /* we'll produce these vals */
471
472   /* need these for fragment programs */
473   span->attrStart[FRAG_ATTRIB_WPOS][3] = 1.0F;
474   span->attrStepX[FRAG_ATTRIB_WPOS][3] = 0.0F;
475   span->attrStepY[FRAG_ATTRIB_WPOS][3] = 0.0F;
476
477   /* check if we need to flush */
478   if (span->end >= SWRAST_MAX_WIDTH ||
479       (swrast->_RasterMask & (BLEND_BIT | LOGIC_OP_BIT | MASKING_BIT)) ||
480       span->facing != swrast->PointLineFacing) {
481      if (span->end > 0) {
482	 _swrast_write_rgba_span(ctx, span);
483         span->end = 0;
484      }
485   }
486
487   count = span->end;
488
489   span->facing = swrast->PointLineFacing;
490
491   /* fragment attributes */
492   span->array->rgba[count][RCOMP] = vert->color[0];
493   span->array->rgba[count][GCOMP] = vert->color[1];
494   span->array->rgba[count][BCOMP] = vert->color[2];
495   span->array->rgba[count][ACOMP] = vert->color[3];
496
497   ATTRIB_LOOP_BEGIN
498      COPY_4V(span->array->attribs[attr][count], vert->attrib[attr]);
499   ATTRIB_LOOP_END
500
501   /* fragment position */
502   span->array->x[count] = (GLint) vert->attrib[FRAG_ATTRIB_WPOS][0];
503   span->array->y[count] = (GLint) vert->attrib[FRAG_ATTRIB_WPOS][1];
504   span->array->z[count] = (GLint) (vert->attrib[FRAG_ATTRIB_WPOS][2] + 0.5F);
505
506   span->end = count + 1;
507   ASSERT(span->end <= SWRAST_MAX_WIDTH);
508}
509
510
511/**
512 * Add specular color to primary color, draw point, restore original
513 * primary color.
514 */
515void
516_swrast_add_spec_terms_point(struct gl_context *ctx, const SWvertex *v0)
517{
518   SWvertex *ncv0 = (SWvertex *) v0; /* cast away const */
519   GLfloat rSum, gSum, bSum;
520   GLchan cSave[4];
521
522   /* save */
523   COPY_CHAN4(cSave, ncv0->color);
524   /* sum */
525   rSum = CHAN_TO_FLOAT(ncv0->color[0]) + ncv0->attrib[FRAG_ATTRIB_COL1][0];
526   gSum = CHAN_TO_FLOAT(ncv0->color[1]) + ncv0->attrib[FRAG_ATTRIB_COL1][1];
527   bSum = CHAN_TO_FLOAT(ncv0->color[2]) + ncv0->attrib[FRAG_ATTRIB_COL1][2];
528   UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[0], rSum);
529   UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[1], gSum);
530   UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[2], bSum);
531   /* draw */
532   SWRAST_CONTEXT(ctx)->SpecPoint(ctx, ncv0);
533   /* restore */
534   COPY_CHAN4(ncv0->color, cSave);
535}
536
537
538/**
539 * Examine current state to determine which point drawing function to use.
540 */
541void
542_swrast_choose_point(struct gl_context *ctx)
543{
544   SWcontext *swrast = SWRAST_CONTEXT(ctx);
545   const GLfloat size = CLAMP(ctx->Point.Size,
546                              ctx->Point.MinSize,
547                              ctx->Point.MaxSize);
548
549   if (ctx->RenderMode == GL_RENDER) {
550      if (ctx->Point.PointSprite) {
551         swrast->Point = sprite_point;
552      }
553      else if (ctx->Point.SmoothFlag) {
554         swrast->Point = smooth_point;
555      }
556      else if (size > 1.0 ||
557               ctx->Point._Attenuated ||
558               ctx->VertexProgram.PointSizeEnabled) {
559         swrast->Point = large_point;
560      }
561      else {
562         swrast->Point = pixel_point;
563      }
564   }
565   else if (ctx->RenderMode == GL_FEEDBACK) {
566      swrast->Point = _swrast_feedback_point;
567   }
568   else {
569      /* GL_SELECT mode */
570      swrast->Point = _swrast_select_point;
571   }
572}
573