s_triangle.c revision 54c62ba5c36f3e2b279151f5df851d2ceee15319
1/*
2 * Mesa 3-D graphics library
3 * Version:  7.3
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/*
27 * When the device driver doesn't implement triangle rasterization it
28 * can hook in _swrast_Triangle, which eventually calls one of these
29 * functions to draw triangles.
30 */
31
32#include "main/glheader.h"
33#include "main/context.h"
34#include "main/colormac.h"
35#include "main/imports.h"
36#include "main/macros.h"
37#include "main/texformat.h"
38#include "shader/prog_instruction.h"
39
40#include "s_aatriangle.h"
41#include "s_context.h"
42#include "s_feedback.h"
43#include "s_span.h"
44#include "s_triangle.h"
45
46
47/*
48 * Just used for feedback mode.
49 */
50GLboolean
51_swrast_culltriangle( GLcontext *ctx,
52                      const SWvertex *v0,
53                      const SWvertex *v1,
54                      const SWvertex *v2 )
55{
56   GLfloat ex = v1->attrib[FRAG_ATTRIB_WPOS][0] - v0->attrib[FRAG_ATTRIB_WPOS][0];
57   GLfloat ey = v1->attrib[FRAG_ATTRIB_WPOS][1] - v0->attrib[FRAG_ATTRIB_WPOS][1];
58   GLfloat fx = v2->attrib[FRAG_ATTRIB_WPOS][0] - v0->attrib[FRAG_ATTRIB_WPOS][0];
59   GLfloat fy = v2->attrib[FRAG_ATTRIB_WPOS][1] - v0->attrib[FRAG_ATTRIB_WPOS][1];
60   GLfloat c = ex*fy-ey*fx;
61
62   if (c * SWRAST_CONTEXT(ctx)->_BackfaceCullSign > 0)
63      return 0;
64
65   return 1;
66}
67
68
69
70/*
71 * Render a smooth or flat-shaded color index triangle.
72 */
73#define NAME ci_triangle
74#define INTERP_Z 1
75#define INTERP_ATTRIBS 1  /* just for fog */
76#define INTERP_INDEX 1
77#define RENDER_SPAN( span )  _swrast_write_index_span(ctx, &span);
78#include "s_tritemp.h"
79
80
81
82/*
83 * Render a flat-shaded RGBA triangle.
84 */
85#define NAME flat_rgba_triangle
86#define INTERP_Z 1
87#define SETUP_CODE				\
88   ASSERT(ctx->Texture._EnabledCoordUnits == 0);\
89   ASSERT(ctx->Light.ShadeModel==GL_FLAT);	\
90   span.interpMask |= SPAN_RGBA;		\
91   span.red = ChanToFixed(v2->color[0]);	\
92   span.green = ChanToFixed(v2->color[1]);	\
93   span.blue = ChanToFixed(v2->color[2]);	\
94   span.alpha = ChanToFixed(v2->color[3]);	\
95   span.redStep = 0;				\
96   span.greenStep = 0;				\
97   span.blueStep = 0;				\
98   span.alphaStep = 0;
99#define RENDER_SPAN( span )  _swrast_write_rgba_span(ctx, &span);
100#include "s_tritemp.h"
101
102
103
104/*
105 * Render a smooth-shaded RGBA triangle.
106 */
107#define NAME smooth_rgba_triangle
108#define INTERP_Z 1
109#define INTERP_RGB 1
110#define INTERP_ALPHA 1
111#define SETUP_CODE				\
112   {						\
113      /* texturing must be off */		\
114      ASSERT(ctx->Texture._EnabledCoordUnits == 0);	\
115      ASSERT(ctx->Light.ShadeModel==GL_SMOOTH);	\
116   }
117#define RENDER_SPAN( span )  _swrast_write_rgba_span(ctx, &span);
118#include "s_tritemp.h"
119
120
121
122/*
123 * Render an RGB, GL_DECAL, textured triangle.
124 * Interpolate S,T only w/out mipmapping or perspective correction.
125 *
126 * No fog.  No depth testing.
127 */
128#define NAME simple_textured_triangle
129#define INTERP_INT_TEX 1
130#define S_SCALE twidth
131#define T_SCALE theight
132
133#define SETUP_CODE							\
134   struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0];	\
135   struct gl_texture_object *obj = ctx->Texture.Unit[0].Current2D;	\
136   const GLint b = obj->BaseLevel;					\
137   const GLfloat twidth = (GLfloat) obj->Image[0][b]->Width;		\
138   const GLfloat theight = (GLfloat) obj->Image[0][b]->Height;		\
139   const GLint twidth_log2 = obj->Image[0][b]->WidthLog2;		\
140   const GLchan *texture = (const GLchan *) obj->Image[0][b]->Data;	\
141   const GLint smask = obj->Image[0][b]->Width - 1;			\
142   const GLint tmask = obj->Image[0][b]->Height - 1;			\
143   if (!rb || !texture) {						\
144      return;								\
145   }
146
147#define RENDER_SPAN( span )						\
148   GLuint i;								\
149   GLchan rgb[MAX_WIDTH][3];						\
150   span.intTex[0] -= FIXED_HALF; /* off-by-one error? */		\
151   span.intTex[1] -= FIXED_HALF;					\
152   for (i = 0; i < span.end; i++) {					\
153      GLint s = FixedToInt(span.intTex[0]) & smask;			\
154      GLint t = FixedToInt(span.intTex[1]) & tmask;			\
155      GLint pos = (t << twidth_log2) + s;				\
156      pos = pos + pos + pos;  /* multiply by 3 */			\
157      rgb[i][RCOMP] = texture[pos];					\
158      rgb[i][GCOMP] = texture[pos+1];					\
159      rgb[i][BCOMP] = texture[pos+2];					\
160      span.intTex[0] += span.intTexStep[0];				\
161      span.intTex[1] += span.intTexStep[1];				\
162   }									\
163   rb->PutRowRGB(ctx, rb, span.end, span.x, span.y, rgb, NULL);
164
165#include "s_tritemp.h"
166
167
168
169/*
170 * Render an RGB, GL_DECAL, textured triangle.
171 * Interpolate S,T, GL_LESS depth test, w/out mipmapping or
172 * perspective correction.
173 * Depth buffer bits must be <= sizeof(DEFAULT_SOFTWARE_DEPTH_TYPE)
174 *
175 * No fog.
176 */
177#define NAME simple_z_textured_triangle
178#define INTERP_Z 1
179#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE
180#define INTERP_INT_TEX 1
181#define S_SCALE twidth
182#define T_SCALE theight
183
184#define SETUP_CODE							\
185   struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0];	\
186   struct gl_texture_object *obj = ctx->Texture.Unit[0].Current2D;	\
187   const GLint b = obj->BaseLevel;					\
188   const GLfloat twidth = (GLfloat) obj->Image[0][b]->Width;		\
189   const GLfloat theight = (GLfloat) obj->Image[0][b]->Height;		\
190   const GLint twidth_log2 = obj->Image[0][b]->WidthLog2;		\
191   const GLchan *texture = (const GLchan *) obj->Image[0][b]->Data;	\
192   const GLint smask = obj->Image[0][b]->Width - 1;			\
193   const GLint tmask = obj->Image[0][b]->Height - 1;			\
194   if (!rb || !texture) {						\
195      return;								\
196   }
197
198#define RENDER_SPAN( span )						\
199   GLuint i;				    				\
200   GLchan rgb[MAX_WIDTH][3];						\
201   span.intTex[0] -= FIXED_HALF; /* off-by-one error? */		\
202   span.intTex[1] -= FIXED_HALF;					\
203   for (i = 0; i < span.end; i++) {					\
204      const GLuint z = FixedToDepth(span.z);				\
205      if (z < zRow[i]) {						\
206         GLint s = FixedToInt(span.intTex[0]) & smask;			\
207         GLint t = FixedToInt(span.intTex[1]) & tmask;			\
208         GLint pos = (t << twidth_log2) + s;				\
209         pos = pos + pos + pos;  /* multiply by 3 */			\
210         rgb[i][RCOMP] = texture[pos];					\
211         rgb[i][GCOMP] = texture[pos+1];				\
212         rgb[i][BCOMP] = texture[pos+2];				\
213         zRow[i] = z;							\
214         span.array->mask[i] = 1;					\
215      }									\
216      else {								\
217         span.array->mask[i] = 0;					\
218      }									\
219      span.intTex[0] += span.intTexStep[0];				\
220      span.intTex[1] += span.intTexStep[1];				\
221      span.z += span.zStep;						\
222   }									\
223   rb->PutRowRGB(ctx, rb, span.end, span.x, span.y, rgb, span.array->mask);
224
225#include "s_tritemp.h"
226
227
228#if CHAN_TYPE != GL_FLOAT
229
230struct affine_info
231{
232   GLenum filter;
233   GLenum format;
234   GLenum envmode;
235   GLint smask, tmask;
236   GLint twidth_log2;
237   const GLchan *texture;
238   GLfixed er, eg, eb, ea;
239   GLint tbytesline, tsize;
240};
241
242
243static INLINE GLint
244ilerp(GLint t, GLint a, GLint b)
245{
246   return a + ((t * (b - a)) >> FIXED_SHIFT);
247}
248
249static INLINE GLint
250ilerp_2d(GLint ia, GLint ib, GLint v00, GLint v10, GLint v01, GLint v11)
251{
252   const GLint temp0 = ilerp(ia, v00, v10);
253   const GLint temp1 = ilerp(ia, v01, v11);
254   return ilerp(ib, temp0, temp1);
255}
256
257
258/* This function can handle GL_NEAREST or GL_LINEAR sampling of 2D RGB or RGBA
259 * textures with GL_REPLACE, GL_MODULATE, GL_BLEND, GL_DECAL or GL_ADD
260 * texture env modes.
261 */
262static INLINE void
263affine_span(GLcontext *ctx, SWspan *span,
264            struct affine_info *info)
265{
266   GLchan sample[4];  /* the filtered texture sample */
267   const GLuint texEnableSave = ctx->Texture._EnabledUnits;
268
269   /* Disable tex units so they're not re-applied in swrast_write_rgba_span */
270   ctx->Texture._EnabledUnits = 0x0;
271
272   /* Instead of defining a function for each mode, a test is done
273    * between the outer and inner loops. This is to reduce code size
274    * and complexity. Observe that an optimizing compiler kills
275    * unused variables (for instance tf,sf,ti,si in case of GL_NEAREST).
276    */
277
278#define NEAREST_RGB			\
279   sample[RCOMP] = tex00[RCOMP];	\
280   sample[GCOMP] = tex00[GCOMP];	\
281   sample[BCOMP] = tex00[BCOMP];	\
282   sample[ACOMP] = CHAN_MAX
283
284#define LINEAR_RGB							\
285   sample[RCOMP] = ilerp_2d(sf, tf, tex00[0], tex01[0], tex10[0], tex11[0]);\
286   sample[GCOMP] = ilerp_2d(sf, tf, tex00[1], tex01[1], tex10[1], tex11[1]);\
287   sample[BCOMP] = ilerp_2d(sf, tf, tex00[2], tex01[2], tex10[2], tex11[2]);\
288   sample[ACOMP] = CHAN_MAX;
289
290#define NEAREST_RGBA  COPY_CHAN4(sample, tex00)
291
292#define LINEAR_RGBA							\
293   sample[RCOMP] = ilerp_2d(sf, tf, tex00[0], tex01[0], tex10[0], tex11[0]);\
294   sample[GCOMP] = ilerp_2d(sf, tf, tex00[1], tex01[1], tex10[1], tex11[1]);\
295   sample[BCOMP] = ilerp_2d(sf, tf, tex00[2], tex01[2], tex10[2], tex11[2]);\
296   sample[ACOMP] = ilerp_2d(sf, tf, tex00[3], tex01[3], tex10[3], tex11[3])
297
298#define MODULATE							  \
299   dest[RCOMP] = span->red   * (sample[RCOMP] + 1u) >> (FIXED_SHIFT + 8); \
300   dest[GCOMP] = span->green * (sample[GCOMP] + 1u) >> (FIXED_SHIFT + 8); \
301   dest[BCOMP] = span->blue  * (sample[BCOMP] + 1u) >> (FIXED_SHIFT + 8); \
302   dest[ACOMP] = span->alpha * (sample[ACOMP] + 1u) >> (FIXED_SHIFT + 8)
303
304#define DECAL								\
305   dest[RCOMP] = ((CHAN_MAX - sample[ACOMP]) * span->red +		\
306               ((sample[ACOMP] + 1) * sample[RCOMP] << FIXED_SHIFT))	\
307               >> (FIXED_SHIFT + 8);					\
308   dest[GCOMP] = ((CHAN_MAX - sample[ACOMP]) * span->green +		\
309               ((sample[ACOMP] + 1) * sample[GCOMP] << FIXED_SHIFT))	\
310               >> (FIXED_SHIFT + 8);					\
311   dest[BCOMP] = ((CHAN_MAX - sample[ACOMP]) * span->blue +		\
312               ((sample[ACOMP] + 1) * sample[BCOMP] << FIXED_SHIFT))	\
313               >> (FIXED_SHIFT + 8);					\
314   dest[ACOMP] = FixedToInt(span->alpha)
315
316#define BLEND								\
317   dest[RCOMP] = ((CHAN_MAX - sample[RCOMP]) * span->red		\
318               + (sample[RCOMP] + 1) * info->er) >> (FIXED_SHIFT + 8);	\
319   dest[GCOMP] = ((CHAN_MAX - sample[GCOMP]) * span->green		\
320               + (sample[GCOMP] + 1) * info->eg) >> (FIXED_SHIFT + 8);	\
321   dest[BCOMP] = ((CHAN_MAX - sample[BCOMP]) * span->blue		\
322               + (sample[BCOMP] + 1) * info->eb) >> (FIXED_SHIFT + 8);	\
323   dest[ACOMP] = span->alpha * (sample[ACOMP] + 1) >> (FIXED_SHIFT + 8)
324
325#define REPLACE  COPY_CHAN4(dest, sample)
326
327#define ADD								\
328   {									\
329      GLint rSum = FixedToInt(span->red)   + (GLint) sample[RCOMP];	\
330      GLint gSum = FixedToInt(span->green) + (GLint) sample[GCOMP];	\
331      GLint bSum = FixedToInt(span->blue)  + (GLint) sample[BCOMP];	\
332      dest[RCOMP] = MIN2(rSum, CHAN_MAX);				\
333      dest[GCOMP] = MIN2(gSum, CHAN_MAX);				\
334      dest[BCOMP] = MIN2(bSum, CHAN_MAX);				\
335      dest[ACOMP] = span->alpha * (sample[ACOMP] + 1) >> (FIXED_SHIFT + 8); \
336  }
337
338/* shortcuts */
339
340#define NEAREST_RGB_REPLACE		\
341   NEAREST_RGB;				\
342   dest[0] = sample[0];			\
343   dest[1] = sample[1];			\
344   dest[2] = sample[2];			\
345   dest[3] = FixedToInt(span->alpha);
346
347#define NEAREST_RGBA_REPLACE  COPY_CHAN4(dest, tex00)
348
349#define SPAN_NEAREST(DO_TEX, COMPS)					\
350	for (i = 0; i < span->end; i++) {				\
351           /* Isn't it necessary to use FixedFloor below?? */		\
352           GLint s = FixedToInt(span->intTex[0]) & info->smask;		\
353           GLint t = FixedToInt(span->intTex[1]) & info->tmask;		\
354           GLint pos = (t << info->twidth_log2) + s;			\
355           const GLchan *tex00 = info->texture + COMPS * pos;		\
356           DO_TEX;							\
357           span->red += span->redStep;					\
358	   span->green += span->greenStep;				\
359           span->blue += span->blueStep;				\
360	   span->alpha += span->alphaStep;				\
361	   span->intTex[0] += span->intTexStep[0];			\
362	   span->intTex[1] += span->intTexStep[1];			\
363           dest += 4;							\
364	}
365
366#define SPAN_LINEAR(DO_TEX, COMPS)					\
367	for (i = 0; i < span->end; i++) {				\
368           /* Isn't it necessary to use FixedFloor below?? */		\
369           const GLint s = FixedToInt(span->intTex[0]) & info->smask;	\
370           const GLint t = FixedToInt(span->intTex[1]) & info->tmask;	\
371           const GLfixed sf = span->intTex[0] & FIXED_FRAC_MASK;	\
372           const GLfixed tf = span->intTex[1] & FIXED_FRAC_MASK;	\
373           const GLint pos = (t << info->twidth_log2) + s;		\
374           const GLchan *tex00 = info->texture + COMPS * pos;		\
375           const GLchan *tex10 = tex00 + info->tbytesline;		\
376           const GLchan *tex01 = tex00 + COMPS;				\
377           const GLchan *tex11 = tex10 + COMPS;				\
378           if (t == info->tmask) {					\
379              tex10 -= info->tsize;					\
380              tex11 -= info->tsize;					\
381           }								\
382           if (s == info->smask) {					\
383              tex01 -= info->tbytesline;				\
384              tex11 -= info->tbytesline;				\
385           }								\
386           DO_TEX;							\
387           span->red += span->redStep;					\
388	   span->green += span->greenStep;				\
389           span->blue += span->blueStep;				\
390	   span->alpha += span->alphaStep;				\
391	   span->intTex[0] += span->intTexStep[0];			\
392	   span->intTex[1] += span->intTexStep[1];			\
393           dest += 4;							\
394	}
395
396
397   GLuint i;
398   GLchan *dest = span->array->rgba[0];
399
400   span->intTex[0] -= FIXED_HALF;
401   span->intTex[1] -= FIXED_HALF;
402   switch (info->filter) {
403   case GL_NEAREST:
404      switch (info->format) {
405      case GL_RGB:
406         switch (info->envmode) {
407         case GL_MODULATE:
408            SPAN_NEAREST(NEAREST_RGB;MODULATE,3);
409            break;
410         case GL_DECAL:
411         case GL_REPLACE:
412            SPAN_NEAREST(NEAREST_RGB_REPLACE,3);
413            break;
414         case GL_BLEND:
415            SPAN_NEAREST(NEAREST_RGB;BLEND,3);
416            break;
417         case GL_ADD:
418            SPAN_NEAREST(NEAREST_RGB;ADD,3);
419            break;
420         default:
421            _mesa_problem(ctx, "bad tex env mode in SPAN_LINEAR");
422            return;
423         }
424         break;
425      case GL_RGBA:
426         switch(info->envmode) {
427         case GL_MODULATE:
428            SPAN_NEAREST(NEAREST_RGBA;MODULATE,4);
429            break;
430         case GL_DECAL:
431            SPAN_NEAREST(NEAREST_RGBA;DECAL,4);
432            break;
433         case GL_BLEND:
434            SPAN_NEAREST(NEAREST_RGBA;BLEND,4);
435            break;
436         case GL_ADD:
437            SPAN_NEAREST(NEAREST_RGBA;ADD,4);
438            break;
439         case GL_REPLACE:
440            SPAN_NEAREST(NEAREST_RGBA_REPLACE,4);
441            break;
442         default:
443            _mesa_problem(ctx, "bad tex env mode (2) in SPAN_LINEAR");
444            return;
445         }
446         break;
447      }
448      break;
449
450   case GL_LINEAR:
451      span->intTex[0] -= FIXED_HALF;
452      span->intTex[1] -= FIXED_HALF;
453      switch (info->format) {
454      case GL_RGB:
455         switch (info->envmode) {
456         case GL_MODULATE:
457            SPAN_LINEAR(LINEAR_RGB;MODULATE,3);
458            break;
459         case GL_DECAL:
460         case GL_REPLACE:
461            SPAN_LINEAR(LINEAR_RGB;REPLACE,3);
462            break;
463         case GL_BLEND:
464            SPAN_LINEAR(LINEAR_RGB;BLEND,3);
465            break;
466         case GL_ADD:
467            SPAN_LINEAR(LINEAR_RGB;ADD,3);
468            break;
469         default:
470            _mesa_problem(ctx, "bad tex env mode (3) in SPAN_LINEAR");
471            return;
472         }
473         break;
474      case GL_RGBA:
475         switch (info->envmode) {
476         case GL_MODULATE:
477            SPAN_LINEAR(LINEAR_RGBA;MODULATE,4);
478            break;
479         case GL_DECAL:
480            SPAN_LINEAR(LINEAR_RGBA;DECAL,4);
481            break;
482         case GL_BLEND:
483            SPAN_LINEAR(LINEAR_RGBA;BLEND,4);
484            break;
485         case GL_ADD:
486            SPAN_LINEAR(LINEAR_RGBA;ADD,4);
487            break;
488         case GL_REPLACE:
489            SPAN_LINEAR(LINEAR_RGBA;REPLACE,4);
490            break;
491         default:
492            _mesa_problem(ctx, "bad tex env mode (4) in SPAN_LINEAR");
493            return;
494         }
495         break;
496      }
497      break;
498   }
499   span->interpMask &= ~SPAN_RGBA;
500   ASSERT(span->arrayMask & SPAN_RGBA);
501
502   _swrast_write_rgba_span(ctx, span);
503
504   /* re-enable texture units */
505   ctx->Texture._EnabledUnits = texEnableSave;
506
507#undef SPAN_NEAREST
508#undef SPAN_LINEAR
509}
510
511
512
513/*
514 * Render an RGB/RGBA textured triangle without perspective correction.
515 */
516#define NAME affine_textured_triangle
517#define INTERP_Z 1
518#define INTERP_RGB 1
519#define INTERP_ALPHA 1
520#define INTERP_INT_TEX 1
521#define S_SCALE twidth
522#define T_SCALE theight
523
524#define SETUP_CODE							\
525   struct affine_info info;						\
526   struct gl_texture_unit *unit = ctx->Texture.Unit+0;			\
527   struct gl_texture_object *obj = unit->Current2D;			\
528   const GLint b = obj->BaseLevel;					\
529   const GLfloat twidth = (GLfloat) obj->Image[0][b]->Width;		\
530   const GLfloat theight = (GLfloat) obj->Image[0][b]->Height;		\
531   info.texture = (const GLchan *) obj->Image[0][b]->Data;		\
532   info.twidth_log2 = obj->Image[0][b]->WidthLog2;			\
533   info.smask = obj->Image[0][b]->Width - 1;				\
534   info.tmask = obj->Image[0][b]->Height - 1;				\
535   info.format = obj->Image[0][b]->_BaseFormat;				\
536   info.filter = obj->MinFilter;					\
537   info.envmode = unit->EnvMode;					\
538   span.arrayMask |= SPAN_RGBA;						\
539									\
540   if (info.envmode == GL_BLEND) {					\
541      /* potential off-by-one error here? (1.0f -> 2048 -> 0) */	\
542      info.er = FloatToFixed(unit->EnvColor[RCOMP] * CHAN_MAXF);	\
543      info.eg = FloatToFixed(unit->EnvColor[GCOMP] * CHAN_MAXF);	\
544      info.eb = FloatToFixed(unit->EnvColor[BCOMP] * CHAN_MAXF);	\
545      info.ea = FloatToFixed(unit->EnvColor[ACOMP] * CHAN_MAXF);	\
546   }									\
547   if (!info.texture) {							\
548      /* this shouldn't happen */					\
549      return;								\
550   }									\
551									\
552   switch (info.format) {						\
553   case GL_ALPHA:							\
554   case GL_LUMINANCE:							\
555   case GL_INTENSITY:							\
556      info.tbytesline = obj->Image[0][b]->Width;			\
557      break;								\
558   case GL_LUMINANCE_ALPHA:						\
559      info.tbytesline = obj->Image[0][b]->Width * 2;			\
560      break;								\
561   case GL_RGB:								\
562      info.tbytesline = obj->Image[0][b]->Width * 3;			\
563      break;								\
564   case GL_RGBA:							\
565      info.tbytesline = obj->Image[0][b]->Width * 4;			\
566      break;								\
567   default:								\
568      _mesa_problem(NULL, "Bad texture format in affine_texture_triangle");\
569      return;								\
570   }									\
571   info.tsize = obj->Image[0][b]->Height * info.tbytesline;
572
573#define RENDER_SPAN( span )   affine_span(ctx, &span, &info);
574
575#include "s_tritemp.h"
576
577
578
579struct persp_info
580{
581   GLenum filter;
582   GLenum format;
583   GLenum envmode;
584   GLint smask, tmask;
585   GLint twidth_log2;
586   const GLchan *texture;
587   GLfixed er, eg, eb, ea;   /* texture env color */
588   GLint tbytesline, tsize;
589};
590
591
592static INLINE void
593fast_persp_span(GLcontext *ctx, SWspan *span,
594		struct persp_info *info)
595{
596   GLchan sample[4];  /* the filtered texture sample */
597
598  /* Instead of defining a function for each mode, a test is done
599   * between the outer and inner loops. This is to reduce code size
600   * and complexity. Observe that an optimizing compiler kills
601   * unused variables (for instance tf,sf,ti,si in case of GL_NEAREST).
602   */
603#define SPAN_NEAREST(DO_TEX,COMP)					\
604	for (i = 0; i < span->end; i++) {				\
605           GLdouble invQ = tex_coord[2] ?				\
606                                 (1.0 / tex_coord[2]) : 1.0;            \
607           GLfloat s_tmp = (GLfloat) (tex_coord[0] * invQ);		\
608           GLfloat t_tmp = (GLfloat) (tex_coord[1] * invQ);		\
609           GLint s = IFLOOR(s_tmp) & info->smask;	        	\
610           GLint t = IFLOOR(t_tmp) & info->tmask;	        	\
611           GLint pos = (t << info->twidth_log2) + s;			\
612           const GLchan *tex00 = info->texture + COMP * pos;		\
613           DO_TEX;							\
614           span->red += span->redStep;					\
615	   span->green += span->greenStep;				\
616           span->blue += span->blueStep;				\
617	   span->alpha += span->alphaStep;				\
618	   tex_coord[0] += tex_step[0];					\
619	   tex_coord[1] += tex_step[1];					\
620	   tex_coord[2] += tex_step[2];					\
621           dest += 4;							\
622	}
623
624#define SPAN_LINEAR(DO_TEX,COMP)					\
625	for (i = 0; i < span->end; i++) {				\
626           GLdouble invQ = tex_coord[2] ?				\
627                                 (1.0 / tex_coord[2]) : 1.0;            \
628           const GLfloat s_tmp = (GLfloat) (tex_coord[0] * invQ);	\
629           const GLfloat t_tmp = (GLfloat) (tex_coord[1] * invQ);	\
630           const GLfixed s_fix = FloatToFixed(s_tmp) - FIXED_HALF;	\
631           const GLfixed t_fix = FloatToFixed(t_tmp) - FIXED_HALF;      \
632           const GLint s = FixedToInt(FixedFloor(s_fix)) & info->smask;	\
633           const GLint t = FixedToInt(FixedFloor(t_fix)) & info->tmask;	\
634           const GLfixed sf = s_fix & FIXED_FRAC_MASK;			\
635           const GLfixed tf = t_fix & FIXED_FRAC_MASK;			\
636           const GLint pos = (t << info->twidth_log2) + s;		\
637           const GLchan *tex00 = info->texture + COMP * pos;		\
638           const GLchan *tex10 = tex00 + info->tbytesline;		\
639           const GLchan *tex01 = tex00 + COMP;				\
640           const GLchan *tex11 = tex10 + COMP;				\
641           if (t == info->tmask) {					\
642              tex10 -= info->tsize;					\
643              tex11 -= info->tsize;					\
644           }								\
645           if (s == info->smask) {					\
646              tex01 -= info->tbytesline;				\
647              tex11 -= info->tbytesline;				\
648           }								\
649           DO_TEX;							\
650           span->red   += span->redStep;				\
651	   span->green += span->greenStep;				\
652           span->blue  += span->blueStep;				\
653	   span->alpha += span->alphaStep;				\
654	   tex_coord[0] += tex_step[0];					\
655	   tex_coord[1] += tex_step[1];					\
656	   tex_coord[2] += tex_step[2];					\
657           dest += 4;							\
658	}
659
660   GLuint i;
661   GLfloat tex_coord[3], tex_step[3];
662   GLchan *dest = span->array->rgba[0];
663
664   const GLuint savedTexEnable = ctx->Texture._EnabledUnits;
665   ctx->Texture._EnabledUnits = 0;
666
667   tex_coord[0] = span->attrStart[FRAG_ATTRIB_TEX0][0]  * (info->smask + 1);
668   tex_step[0] = span->attrStepX[FRAG_ATTRIB_TEX0][0] * (info->smask + 1);
669   tex_coord[1] = span->attrStart[FRAG_ATTRIB_TEX0][1] * (info->tmask + 1);
670   tex_step[1] = span->attrStepX[FRAG_ATTRIB_TEX0][1] * (info->tmask + 1);
671   /* span->attrStart[FRAG_ATTRIB_TEX0][2] only if 3D-texturing, here only 2D */
672   tex_coord[2] = span->attrStart[FRAG_ATTRIB_TEX0][3];
673   tex_step[2] = span->attrStepX[FRAG_ATTRIB_TEX0][3];
674
675   switch (info->filter) {
676   case GL_NEAREST:
677      switch (info->format) {
678      case GL_RGB:
679         switch (info->envmode) {
680         case GL_MODULATE:
681            SPAN_NEAREST(NEAREST_RGB;MODULATE,3);
682            break;
683         case GL_DECAL:
684         case GL_REPLACE:
685            SPAN_NEAREST(NEAREST_RGB_REPLACE,3);
686            break;
687         case GL_BLEND:
688            SPAN_NEAREST(NEAREST_RGB;BLEND,3);
689            break;
690         case GL_ADD:
691            SPAN_NEAREST(NEAREST_RGB;ADD,3);
692            break;
693         default:
694            _mesa_problem(ctx, "bad tex env mode (5) in SPAN_LINEAR");
695            return;
696         }
697         break;
698      case GL_RGBA:
699         switch(info->envmode) {
700         case GL_MODULATE:
701            SPAN_NEAREST(NEAREST_RGBA;MODULATE,4);
702            break;
703         case GL_DECAL:
704            SPAN_NEAREST(NEAREST_RGBA;DECAL,4);
705            break;
706         case GL_BLEND:
707            SPAN_NEAREST(NEAREST_RGBA;BLEND,4);
708            break;
709         case GL_ADD:
710            SPAN_NEAREST(NEAREST_RGBA;ADD,4);
711            break;
712         case GL_REPLACE:
713            SPAN_NEAREST(NEAREST_RGBA_REPLACE,4);
714            break;
715         default:
716            _mesa_problem(ctx, "bad tex env mode (6) in SPAN_LINEAR");
717            return;
718         }
719         break;
720      }
721      break;
722
723   case GL_LINEAR:
724      switch (info->format) {
725      case GL_RGB:
726         switch (info->envmode) {
727         case GL_MODULATE:
728            SPAN_LINEAR(LINEAR_RGB;MODULATE,3);
729            break;
730         case GL_DECAL:
731         case GL_REPLACE:
732            SPAN_LINEAR(LINEAR_RGB;REPLACE,3);
733            break;
734         case GL_BLEND:
735            SPAN_LINEAR(LINEAR_RGB;BLEND,3);
736            break;
737         case GL_ADD:
738            SPAN_LINEAR(LINEAR_RGB;ADD,3);
739            break;
740         default:
741            _mesa_problem(ctx, "bad tex env mode (7) in SPAN_LINEAR");
742            return;
743         }
744         break;
745      case GL_RGBA:
746         switch (info->envmode) {
747         case GL_MODULATE:
748            SPAN_LINEAR(LINEAR_RGBA;MODULATE,4);
749            break;
750         case GL_DECAL:
751            SPAN_LINEAR(LINEAR_RGBA;DECAL,4);
752            break;
753         case GL_BLEND:
754            SPAN_LINEAR(LINEAR_RGBA;BLEND,4);
755            break;
756         case GL_ADD:
757            SPAN_LINEAR(LINEAR_RGBA;ADD,4);
758            break;
759         case GL_REPLACE:
760            SPAN_LINEAR(LINEAR_RGBA;REPLACE,4);
761            break;
762         default:
763            _mesa_problem(ctx, "bad tex env mode (8) in SPAN_LINEAR");
764            return;
765         }
766         break;
767      }
768      break;
769   }
770
771   ASSERT(span->arrayMask & SPAN_RGBA);
772   _swrast_write_rgba_span(ctx, span);
773
774#undef SPAN_NEAREST
775#undef SPAN_LINEAR
776
777   /* restore state */
778   ctx->Texture._EnabledUnits = savedTexEnable;
779}
780
781
782/*
783 * Render an perspective corrected RGB/RGBA textured triangle.
784 * The Q (aka V in Mesa) coordinate must be zero such that the divide
785 * by interpolated Q/W comes out right.
786 *
787 */
788#define NAME persp_textured_triangle
789#define INTERP_Z 1
790#define INTERP_RGB 1
791#define INTERP_ALPHA 1
792#define INTERP_ATTRIBS 1
793
794#define SETUP_CODE							\
795   struct persp_info info;						\
796   const struct gl_texture_unit *unit = ctx->Texture.Unit+0;		\
797   const struct gl_texture_object *obj = unit->Current2D;		\
798   const GLint b = obj->BaseLevel;					\
799   info.texture = (const GLchan *) obj->Image[0][b]->Data;		\
800   info.twidth_log2 = obj->Image[0][b]->WidthLog2;			\
801   info.smask = obj->Image[0][b]->Width - 1;				\
802   info.tmask = obj->Image[0][b]->Height - 1;				\
803   info.format = obj->Image[0][b]->_BaseFormat;				\
804   info.filter = obj->MinFilter;					\
805   info.envmode = unit->EnvMode;					\
806									\
807   if (info.envmode == GL_BLEND) {					\
808      /* potential off-by-one error here? (1.0f -> 2048 -> 0) */	\
809      info.er = FloatToFixed(unit->EnvColor[RCOMP] * CHAN_MAXF);	\
810      info.eg = FloatToFixed(unit->EnvColor[GCOMP] * CHAN_MAXF);	\
811      info.eb = FloatToFixed(unit->EnvColor[BCOMP] * CHAN_MAXF);	\
812      info.ea = FloatToFixed(unit->EnvColor[ACOMP] * CHAN_MAXF);	\
813   }									\
814   if (!info.texture) {							\
815      /* this shouldn't happen */					\
816      return;								\
817   }									\
818									\
819   switch (info.format) {						\
820   case GL_ALPHA:							\
821   case GL_LUMINANCE:							\
822   case GL_INTENSITY:							\
823      info.tbytesline = obj->Image[0][b]->Width;			\
824      break;								\
825   case GL_LUMINANCE_ALPHA:						\
826      info.tbytesline = obj->Image[0][b]->Width * 2;			\
827      break;								\
828   case GL_RGB:								\
829      info.tbytesline = obj->Image[0][b]->Width * 3;			\
830      break;								\
831   case GL_RGBA:							\
832      info.tbytesline = obj->Image[0][b]->Width * 4;			\
833      break;								\
834   default:								\
835      _mesa_problem(NULL, "Bad texture format in persp_textured_triangle");\
836      return;								\
837   }									\
838   info.tsize = obj->Image[0][b]->Height * info.tbytesline;
839
840#define RENDER_SPAN( span )			\
841   span.interpMask &= ~SPAN_RGBA;		\
842   span.arrayMask |= SPAN_RGBA;			\
843   fast_persp_span(ctx, &span, &info);
844
845#include "s_tritemp.h"
846
847#endif /*CHAN_TYPE != GL_FLOAT*/
848
849
850
851/*
852 * Render an RGBA triangle with arbitrary attributes.
853 */
854#define NAME general_triangle
855#define INTERP_Z 1
856#define INTERP_RGB 1
857#define INTERP_ALPHA 1
858#define INTERP_ATTRIBS 1
859#define RENDER_SPAN( span )   _swrast_write_rgba_span(ctx, &span);
860#include "s_tritemp.h"
861
862
863
864
865/*
866 * Special tri function for occlusion testing
867 */
868#define NAME occlusion_zless_triangle
869#define INTERP_Z 1
870#define SETUP_CODE							\
871   struct gl_renderbuffer *rb = ctx->DrawBuffer->_DepthBuffer;		\
872   struct gl_query_object *q = ctx->Query.CurrentOcclusionObject;	\
873   ASSERT(ctx->Depth.Test);						\
874   ASSERT(!ctx->Depth.Mask);						\
875   ASSERT(ctx->Depth.Func == GL_LESS);					\
876   if (!q) {								\
877      return;								\
878   }
879#define RENDER_SPAN( span )						\
880   if (rb->DepthBits <= 16) {						\
881      GLuint i;								\
882      const GLushort *zRow = (const GLushort *)				\
883         rb->GetPointer(ctx, rb, span.x, span.y);			\
884      for (i = 0; i < span.end; i++) {					\
885         GLuint z = FixedToDepth(span.z);				\
886         if (z < zRow[i]) {						\
887            q->Result++;						\
888         }								\
889         span.z += span.zStep;						\
890      }									\
891   }									\
892   else {								\
893      GLuint i;								\
894      const GLuint *zRow = (const GLuint *)				\
895         rb->GetPointer(ctx, rb, span.x, span.y);			\
896      for (i = 0; i < span.end; i++) {					\
897         if ((GLuint)span.z < zRow[i]) {				\
898            q->Result++;						\
899         }								\
900         span.z += span.zStep;						\
901      }									\
902   }
903#include "s_tritemp.h"
904
905
906
907static void
908nodraw_triangle( GLcontext *ctx,
909                 const SWvertex *v0,
910                 const SWvertex *v1,
911                 const SWvertex *v2 )
912{
913   (void) (ctx && v0 && v1 && v2);
914}
915
916
917/*
918 * This is used when separate specular color is enabled, but not
919 * texturing.  We add the specular color to the primary color,
920 * draw the triangle, then restore the original primary color.
921 * Inefficient, but seldom needed.
922 */
923void
924_swrast_add_spec_terms_triangle(GLcontext *ctx, const SWvertex *v0,
925                                const SWvertex *v1, const SWvertex *v2)
926{
927   SWvertex *ncv0 = (SWvertex *)v0; /* drop const qualifier */
928   SWvertex *ncv1 = (SWvertex *)v1;
929   SWvertex *ncv2 = (SWvertex *)v2;
930   GLfloat rSum, gSum, bSum;
931   GLchan cSave[3][4];
932
933   /* save original colors */
934   COPY_CHAN4( cSave[0], ncv0->color );
935   COPY_CHAN4( cSave[1], ncv1->color );
936   COPY_CHAN4( cSave[2], ncv2->color );
937   /* sum v0 */
938   rSum = CHAN_TO_FLOAT(ncv0->color[0]) + ncv0->attrib[FRAG_ATTRIB_COL1][0];
939   gSum = CHAN_TO_FLOAT(ncv0->color[1]) + ncv0->attrib[FRAG_ATTRIB_COL1][1];
940   bSum = CHAN_TO_FLOAT(ncv0->color[2]) + ncv0->attrib[FRAG_ATTRIB_COL1][2];
941   UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[0], rSum);
942   UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[1], gSum);
943   UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[2], bSum);
944   /* sum v1 */
945   rSum = CHAN_TO_FLOAT(ncv1->color[0]) + ncv1->attrib[FRAG_ATTRIB_COL1][0];
946   gSum = CHAN_TO_FLOAT(ncv1->color[1]) + ncv1->attrib[FRAG_ATTRIB_COL1][1];
947   bSum = CHAN_TO_FLOAT(ncv1->color[2]) + ncv1->attrib[FRAG_ATTRIB_COL1][2];
948   UNCLAMPED_FLOAT_TO_CHAN(ncv1->color[0], rSum);
949   UNCLAMPED_FLOAT_TO_CHAN(ncv1->color[1], gSum);
950   UNCLAMPED_FLOAT_TO_CHAN(ncv1->color[2], bSum);
951   /* sum v2 */
952   rSum = CHAN_TO_FLOAT(ncv2->color[0]) + ncv2->attrib[FRAG_ATTRIB_COL1][0];
953   gSum = CHAN_TO_FLOAT(ncv2->color[1]) + ncv2->attrib[FRAG_ATTRIB_COL1][1];
954   bSum = CHAN_TO_FLOAT(ncv2->color[2]) + ncv2->attrib[FRAG_ATTRIB_COL1][2];
955   UNCLAMPED_FLOAT_TO_CHAN(ncv2->color[0], rSum);
956   UNCLAMPED_FLOAT_TO_CHAN(ncv2->color[1], gSum);
957   UNCLAMPED_FLOAT_TO_CHAN(ncv2->color[2], bSum);
958   /* draw */
959   SWRAST_CONTEXT(ctx)->SpecTriangle( ctx, ncv0, ncv1, ncv2 );
960   /* restore original colors */
961   COPY_CHAN4( ncv0->color, cSave[0] );
962   COPY_CHAN4( ncv1->color, cSave[1] );
963   COPY_CHAN4( ncv2->color, cSave[2] );
964}
965
966
967
968#ifdef DEBUG
969
970/* record the current triangle function name */
971const char *_mesa_triFuncName = NULL;
972
973#define USE(triFunc)				\
974do {						\
975    _mesa_triFuncName = #triFunc;		\
976    /*printf("%s\n", _mesa_triFuncName);*/	\
977    swrast->Triangle = triFunc;			\
978} while (0)
979
980#else
981
982#define USE(triFunc)  swrast->Triangle = triFunc;
983
984#endif
985
986
987
988
989/*
990 * Determine which triangle rendering function to use given the current
991 * rendering context.
992 *
993 * Please update the summary flag _SWRAST_NEW_TRIANGLE if you add or
994 * remove tests to this code.
995 */
996void
997_swrast_choose_triangle( GLcontext *ctx )
998{
999   SWcontext *swrast = SWRAST_CONTEXT(ctx);
1000   const GLboolean rgbmode = ctx->Visual.rgbMode;
1001
1002   if (ctx->Polygon.CullFlag &&
1003       ctx->Polygon.CullFaceMode == GL_FRONT_AND_BACK) {
1004      USE(nodraw_triangle);
1005      return;
1006   }
1007
1008   if (ctx->RenderMode==GL_RENDER) {
1009
1010      if (ctx->Polygon.SmoothFlag) {
1011         _swrast_set_aa_triangle_function(ctx);
1012         ASSERT(swrast->Triangle);
1013         return;
1014      }
1015
1016      /* special case for occlusion testing */
1017      if (ctx->Query.CurrentOcclusionObject &&
1018          ctx->Depth.Test &&
1019          ctx->Depth.Mask == GL_FALSE &&
1020          ctx->Depth.Func == GL_LESS &&
1021          !ctx->Stencil.Enabled) {
1022         if ((rgbmode &&
1023              ctx->Color.ColorMask[0] == 0 &&
1024              ctx->Color.ColorMask[1] == 0 &&
1025              ctx->Color.ColorMask[2] == 0 &&
1026              ctx->Color.ColorMask[3] == 0)
1027             ||
1028             (!rgbmode && ctx->Color.IndexMask == 0)) {
1029            USE(occlusion_zless_triangle);
1030            return;
1031         }
1032      }
1033
1034      if (!rgbmode) {
1035         USE(ci_triangle);
1036         return;
1037      }
1038
1039      /*
1040       * XXX should examine swrast->_ActiveAttribMask to determine what
1041       * needs to be interpolated.
1042       */
1043      if (ctx->Texture._EnabledCoordUnits ||
1044          ctx->FragmentProgram._Current ||
1045          ctx->ATIFragmentShader._Enabled ||
1046          NEED_SECONDARY_COLOR(ctx) ||
1047          swrast->_FogEnabled) {
1048         /* Ugh, we do a _lot_ of tests to pick the best textured tri func */
1049         const struct gl_texture_object *texObj2D;
1050         const struct gl_texture_image *texImg;
1051         GLenum minFilter, magFilter, envMode;
1052         GLint format;
1053         texObj2D = ctx->Texture.Unit[0].Current2D;
1054         texImg = texObj2D ? texObj2D->Image[0][texObj2D->BaseLevel] : NULL;
1055         format = texImg ? texImg->TexFormat->MesaFormat : -1;
1056         minFilter = texObj2D ? texObj2D->MinFilter : (GLenum) 0;
1057         magFilter = texObj2D ? texObj2D->MagFilter : (GLenum) 0;
1058         envMode = ctx->Texture.Unit[0].EnvMode;
1059
1060         /* First see if we can use an optimized 2-D texture function */
1061         if (ctx->Texture._EnabledCoordUnits == 0x1
1062             && !ctx->FragmentProgram._Current
1063             && !ctx->ATIFragmentShader._Enabled
1064             && ctx->Texture.Unit[0]._ReallyEnabled == TEXTURE_2D_BIT
1065             && texObj2D->WrapS == GL_REPEAT
1066             && texObj2D->WrapT == GL_REPEAT
1067             && texObj2D->_Swizzle == SWIZZLE_NOOP
1068             && texImg->_IsPowerOfTwo
1069             && texImg->Border == 0
1070             && texImg->Width == texImg->RowStride
1071             && (format == MESA_FORMAT_RGB || format == MESA_FORMAT_RGBA)
1072             && minFilter == magFilter
1073             && ctx->Light.Model.ColorControl == GL_SINGLE_COLOR
1074             && !swrast->_FogEnabled
1075             && ctx->Texture.Unit[0].EnvMode != GL_COMBINE_EXT
1076             && ctx->Texture.Unit[0].EnvMode != GL_COMBINE4_NV) {
1077	    if (ctx->Hint.PerspectiveCorrection==GL_FASTEST) {
1078	       if (minFilter == GL_NEAREST
1079		   && format == MESA_FORMAT_RGB
1080		   && (envMode == GL_REPLACE || envMode == GL_DECAL)
1081		   && ((swrast->_RasterMask == (DEPTH_BIT | TEXTURE_BIT)
1082			&& ctx->Depth.Func == GL_LESS
1083			&& ctx->Depth.Mask == GL_TRUE)
1084		       || swrast->_RasterMask == TEXTURE_BIT)
1085		   && ctx->Polygon.StippleFlag == GL_FALSE
1086                   && ctx->DrawBuffer->Visual.depthBits <= 16) {
1087		  if (swrast->_RasterMask == (DEPTH_BIT | TEXTURE_BIT)) {
1088		     USE(simple_z_textured_triangle);
1089		  }
1090		  else {
1091		     USE(simple_textured_triangle);
1092		  }
1093	       }
1094	       else {
1095#if CHAN_BITS != 8
1096                  USE(general_triangle);
1097#else
1098                  USE(affine_textured_triangle);
1099#endif
1100	       }
1101	    }
1102	    else {
1103#if CHAN_BITS != 8
1104               USE(general_triangle);
1105#else
1106               USE(persp_textured_triangle);
1107#endif
1108	    }
1109	 }
1110         else {
1111            /* general case textured triangles */
1112            USE(general_triangle);
1113         }
1114      }
1115      else {
1116         ASSERT(!swrast->_FogEnabled);
1117         ASSERT(!NEED_SECONDARY_COLOR(ctx));
1118	 if (ctx->Light.ShadeModel==GL_SMOOTH) {
1119	    /* smooth shaded, no texturing, stippled or some raster ops */
1120#if CHAN_BITS != 8
1121               USE(general_triangle);
1122#else
1123               USE(smooth_rgba_triangle);
1124#endif
1125	 }
1126	 else {
1127	    /* flat shaded, no texturing, stippled or some raster ops */
1128#if CHAN_BITS != 8
1129            USE(general_triangle);
1130#else
1131            USE(flat_rgba_triangle);
1132#endif
1133	 }
1134      }
1135   }
1136   else if (ctx->RenderMode==GL_FEEDBACK) {
1137      USE(_swrast_feedback_triangle);
1138   }
1139   else {
1140      /* GL_SELECT mode */
1141      USE(_swrast_select_triangle);
1142   }
1143}
1144