1/**************************************************************************
2 *
3 * Copyright 2007 VMware, Inc.
4 * 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
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/* Authors:  Keith Whitwell <keithw@vmware.com>
29 */
30
31#include "pipe/p_context.h"
32#include "pipe/p_defines.h"
33#include "pipe/p_shader_tokens.h"
34#include "util/u_math.h"
35#include "util/u_memory.h"
36#include "draw_private.h"
37#include "draw_pipe.h"
38
39
40struct wideline_stage {
41   struct draw_stage stage;
42};
43
44
45/**
46 * Draw a wide line by drawing a quad (two triangles).
47 */
48static void wideline_line( struct draw_stage *stage,
49                           struct prim_header *header )
50{
51   const unsigned pos = draw_current_shader_position_output(stage->draw);
52   const float half_width = 0.5f * stage->draw->rasterizer->line_width;
53
54   struct prim_header tri;
55
56   struct vertex_header *v0 = dup_vert(stage, header->v[0], 0);
57   struct vertex_header *v1 = dup_vert(stage, header->v[0], 1);
58   struct vertex_header *v2 = dup_vert(stage, header->v[1], 2);
59   struct vertex_header *v3 = dup_vert(stage, header->v[1], 3);
60
61   float *pos0 = v0->data[pos];
62   float *pos1 = v1->data[pos];
63   float *pos2 = v2->data[pos];
64   float *pos3 = v3->data[pos];
65
66   const float dx = fabsf(pos0[0] - pos2[0]);
67   const float dy = fabsf(pos0[1] - pos2[1]);
68
69   const boolean half_pixel_center =
70      stage->draw->rasterizer->half_pixel_center;
71
72   /* small tweak to meet GL specification */
73   const float bias = half_pixel_center ? 0.125f : 0.0f;
74
75   /*
76    * Draw wide line as a quad (two tris) by "stretching" the line along
77    * X or Y.
78    * We need to tweak coords in several ways to be conformant here.
79    */
80
81   if (dx > dy) {
82      /* x-major line */
83      pos0[1] = pos0[1] - half_width - bias;
84      pos1[1] = pos1[1] + half_width - bias;
85      pos2[1] = pos2[1] - half_width - bias;
86      pos3[1] = pos3[1] + half_width - bias;
87      if (half_pixel_center) {
88         if (pos0[0] < pos2[0]) {
89            /* left to right line */
90            pos0[0] -= 0.5f;
91            pos1[0] -= 0.5f;
92            pos2[0] -= 0.5f;
93            pos3[0] -= 0.5f;
94         }
95         else {
96            /* right to left line */
97            pos0[0] += 0.5f;
98            pos1[0] += 0.5f;
99            pos2[0] += 0.5f;
100            pos3[0] += 0.5f;
101         }
102      }
103   }
104   else {
105      /* y-major line */
106      pos0[0] = pos0[0] - half_width + bias;
107      pos1[0] = pos1[0] + half_width + bias;
108      pos2[0] = pos2[0] - half_width + bias;
109      pos3[0] = pos3[0] + half_width + bias;
110      if (half_pixel_center) {
111         if (pos0[1] < pos2[1]) {
112            /* top to bottom line */
113            pos0[1] -= 0.5f;
114            pos1[1] -= 0.5f;
115            pos2[1] -= 0.5f;
116            pos3[1] -= 0.5f;
117         }
118         else {
119            /* bottom to top line */
120            pos0[1] += 0.5f;
121            pos1[1] += 0.5f;
122            pos2[1] += 0.5f;
123            pos3[1] += 0.5f;
124         }
125      }
126   }
127
128   tri.det = header->det;  /* only the sign matters */
129   tri.v[0] = v0;
130   tri.v[1] = v2;
131   tri.v[2] = v3;
132   stage->next->tri( stage->next, &tri );
133
134   tri.v[0] = v0;
135   tri.v[1] = v3;
136   tri.v[2] = v1;
137   stage->next->tri( stage->next, &tri );
138}
139
140
141static void wideline_first_line( struct draw_stage *stage,
142                                 struct prim_header *header )
143{
144   struct draw_context *draw = stage->draw;
145   struct pipe_context *pipe = draw->pipe;
146   const struct pipe_rasterizer_state *rast = draw->rasterizer;
147   void *r;
148
149   /* Disable triangle culling, stippling, unfilled mode etc. */
150   r = draw_get_rasterizer_no_cull(draw, rast->scissor, rast->flatshade);
151   draw->suspend_flushing = TRUE;
152   pipe->bind_rasterizer_state(pipe, r);
153   draw->suspend_flushing = FALSE;
154
155   stage->line = wideline_line;
156
157   wideline_line(stage, header);
158}
159
160
161static void wideline_flush( struct draw_stage *stage, unsigned flags )
162{
163   struct draw_context *draw = stage->draw;
164   struct pipe_context *pipe = draw->pipe;
165
166   stage->line = wideline_first_line;
167   stage->next->flush( stage->next, flags );
168
169   /* restore original rasterizer state */
170   if (draw->rast_handle) {
171      draw->suspend_flushing = TRUE;
172      pipe->bind_rasterizer_state(pipe, draw->rast_handle);
173      draw->suspend_flushing = FALSE;
174   }
175}
176
177
178static void wideline_reset_stipple_counter( struct draw_stage *stage )
179{
180   stage->next->reset_stipple_counter( stage->next );
181}
182
183
184static void wideline_destroy( struct draw_stage *stage )
185{
186   draw_free_temp_verts( stage );
187   FREE( stage );
188}
189
190
191struct draw_stage *draw_wide_line_stage( struct draw_context *draw )
192{
193   struct wideline_stage *wide = CALLOC_STRUCT(wideline_stage);
194   if (!wide)
195      goto fail;
196
197   wide->stage.draw = draw;
198   wide->stage.name = "wide-line";
199   wide->stage.next = NULL;
200   wide->stage.point = draw_pipe_passthrough_point;
201   wide->stage.line = wideline_first_line;
202   wide->stage.tri = draw_pipe_passthrough_tri;
203   wide->stage.flush = wideline_flush;
204   wide->stage.reset_stipple_counter = wideline_reset_stipple_counter;
205   wide->stage.destroy = wideline_destroy;
206
207   if (!draw_alloc_temp_verts( &wide->stage, 4 ))
208      goto fail;
209
210   return &wide->stage;
211
212fail:
213   if (wide)
214      wide->stage.destroy( &wide->stage );
215
216   return NULL;
217}
218