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/* Implement line stipple by cutting lines up into smaller lines. 32 * There are hundreds of ways to implement line stipple, this is one 33 * choice that should work in all situations, requires no state 34 * manipulations, but with a penalty in terms of large amounts of 35 * generated geometry. 36 */ 37 38 39#include "pipe/p_defines.h" 40#include "pipe/p_shader_tokens.h" 41#include "util/u_math.h" 42#include "util/u_memory.h" 43 44#include "draw/draw_pipe.h" 45 46 47/** Subclass of draw_stage */ 48struct stipple_stage { 49 struct draw_stage stage; 50 unsigned counter; 51 ushort pattern; 52 ushort factor; 53 bool rectangular; 54}; 55 56 57static inline struct stipple_stage * 58stipple_stage(struct draw_stage *stage) 59{ 60 return (struct stipple_stage *) stage; 61} 62 63 64/** 65 * Compute interpolated vertex attributes for 'dst' at position 't' 66 * between 'v0' and 'v1'. 67 * XXX using linear interpolation for all attribs at this time. 68 */ 69static void 70screen_interp(struct draw_context *draw, 71 struct vertex_header *dst, 72 float t, 73 const struct vertex_header *v0, 74 const struct vertex_header *v1) 75{ 76 uint attr; 77 uint num_outputs = draw_current_shader_outputs(draw); 78 for (attr = 0; attr < num_outputs; attr++) { 79 const float *val0 = v0->data[attr]; 80 const float *val1 = v1->data[attr]; 81 float *newv = dst->data[attr]; 82 uint i; 83 for (i = 0; i < 4; i++) { 84 newv[i] = val0[i] + t * (val1[i] - val0[i]); 85 } 86 } 87} 88 89 90static void 91emit_segment(struct draw_stage *stage, struct prim_header *header, 92 float t0, float t1) 93{ 94 struct vertex_header *v0new = dup_vert(stage, header->v[0], 0); 95 struct vertex_header *v1new = dup_vert(stage, header->v[1], 1); 96 struct prim_header newprim = *header; 97 98 if (t0 > 0.0) { 99 screen_interp(stage->draw, v0new, t0, header->v[0], header->v[1]); 100 newprim.v[0] = v0new; 101 } 102 103 if (t1 < 1.0) { 104 screen_interp(stage->draw, v1new, t1, header->v[0], header->v[1]); 105 newprim.v[1] = v1new; 106 } 107 108 stage->next->line(stage->next, &newprim); 109} 110 111 112static inline bool 113stipple_test(unsigned counter, ushort pattern, ushort factor) 114{ 115 unsigned b = (counter / factor) & 0xf; 116 return !!((1 << b) & pattern); 117} 118 119 120static void 121stipple_line(struct draw_stage *stage, struct prim_header *header) 122{ 123 struct stipple_stage *stipple = stipple_stage(stage); 124 struct vertex_header *v0 = header->v[0]; 125 struct vertex_header *v1 = header->v[1]; 126 const unsigned pos = draw_current_shader_position_output(stage->draw); 127 const float *pos0 = v0->data[pos]; 128 const float *pos1 = v1->data[pos]; 129 float start = 0; 130 bool state = 0; 131 132 float x0 = pos0[0]; 133 float x1 = pos1[0]; 134 float y0 = pos0[1]; 135 float y1 = pos1[1]; 136 137 float length; 138 int i; 139 int intlength; 140 141 if (header->flags & DRAW_PIPE_RESET_STIPPLE) 142 stipple->counter = 0; 143 144 if (stipple->rectangular) { 145 float dx = x1 - x0; 146 float dy = y1 - y0; 147 length = sqrtf(dx*dx + dy*dy); 148 } else { 149 float dx = x0 > x1 ? x0 - x1 : x1 - x0; 150 float dy = y0 > y1 ? y0 - y1 : y1 - y0; 151 length = MAX2(dx, dy); 152 } 153 154 if (util_is_inf_or_nan(length)) 155 intlength = 0; 156 else 157 intlength = ceilf(length); 158 159 /* XXX ToDo: instead of iterating pixel-by-pixel, use a look-up table. 160 */ 161 for (i = 0; i < intlength; i++) { 162 bool result = stipple_test(stipple->counter + i, 163 stipple->pattern, stipple->factor); 164 if (result != state) { 165 /* changing from "off" to "on" or vice versa */ 166 if (state) { 167 /* finishing an "on" segment */ 168 emit_segment(stage, header, start / length, i / length); 169 } 170 else { 171 /* starting an "on" segment */ 172 start = (float)i; 173 } 174 state = result; 175 } 176 } 177 178 if (state && start < length) 179 emit_segment(stage, header, start / length, 1.0); 180 181 stipple->counter += intlength; 182} 183 184 185static void 186reset_stipple_counter(struct draw_stage *stage) 187{ 188 struct stipple_stage *stipple = stipple_stage(stage); 189 stipple->counter = 0; 190 stage->next->reset_stipple_counter(stage->next); 191} 192 193static void 194stipple_reset_point(struct draw_stage *stage, struct prim_header *header) 195{ 196 struct stipple_stage *stipple = stipple_stage(stage); 197 stipple->counter = 0; 198 stage->next->point(stage->next, header); 199} 200 201static void 202stipple_reset_tri(struct draw_stage *stage, struct prim_header *header) 203{ 204 struct stipple_stage *stipple = stipple_stage(stage); 205 stipple->counter = 0; 206 stage->next->tri(stage->next, header); 207} 208 209 210static void 211stipple_first_line(struct draw_stage *stage, 212 struct prim_header *header) 213{ 214 struct stipple_stage *stipple = stipple_stage(stage); 215 struct draw_context *draw = stage->draw; 216 217 stipple->pattern = draw->rasterizer->line_stipple_pattern; 218 stipple->factor = draw->rasterizer->line_stipple_factor + 1; 219 stipple->rectangular = draw->rasterizer->line_rectangular; 220 221 stage->line = stipple_line; 222 stage->line(stage, header); 223} 224 225 226static void 227stipple_flush(struct draw_stage *stage, unsigned flags) 228{ 229 stage->line = stipple_first_line; 230 stage->next->flush(stage->next, flags); 231} 232 233 234static void 235stipple_destroy(struct draw_stage *stage) 236{ 237 draw_free_temp_verts(stage); 238 FREE(stage); 239} 240 241 242/** 243 * Create line stippler stage 244 */ 245struct draw_stage * 246draw_stipple_stage(struct draw_context *draw) 247{ 248 struct stipple_stage *stipple = CALLOC_STRUCT(stipple_stage); 249 if (!stipple) 250 goto fail; 251 252 stipple->stage.draw = draw; 253 stipple->stage.name = "stipple"; 254 stipple->stage.next = NULL; 255 stipple->stage.point = stipple_reset_point; 256 stipple->stage.line = stipple_first_line; 257 stipple->stage.tri = stipple_reset_tri; 258 stipple->stage.reset_stipple_counter = reset_stipple_counter; 259 stipple->stage.flush = stipple_flush; 260 stipple->stage.destroy = stipple_destroy; 261 262 if (!draw_alloc_temp_verts(&stipple->stage, 2)) 263 goto fail; 264 265 return &stipple->stage; 266 267fail: 268 if (stipple) 269 stipple->stage.destroy(&stipple->stage); 270 271 return NULL; 272} 273