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