llvmpipe: native line rasterization with correct pixel rasterization
authorHui Qi Tay <hqtay@vmware.com>
Wed, 4 Aug 2010 16:13:39 +0000 (17:13 +0100)
committerKeith Whitwell <keithw@vmware.com>
Fri, 27 Aug 2010 12:08:54 +0000 (13:08 +0100)
Line rasterization that follows diamond exit rule.
Can still optimize logic for start/endpoints.

src/gallium/drivers/llvmpipe/lp_setup_line.c

index 930207ae33a4bf13bc5c4dd677aee1be8f980499..15662031178091657586a189e50cb8b8a7f10fa6 100644 (file)
@@ -243,6 +243,11 @@ print_line(struct lp_setup_context *setup,
 }
 
 
+static INLINE boolean sign(float x){
+   return x >= 0;  
+}  
+
+
 static void
 lp_setup_line( struct lp_setup_context *setup,
                const float (*v1)[4],
@@ -251,7 +256,7 @@ lp_setup_line( struct lp_setup_context *setup,
    struct lp_scene *scene = lp_setup_get_current_scene(setup);
    struct lp_rast_triangle *line;
    float oneoverarea;
-   float half_width = setup->line_width / 2;
+   float width = MAX2(1.0, setup->line_width);
    int minx, maxx, miny, maxy;
    int ix0, ix1, iy0, iy1;
    unsigned tri_bytes;
@@ -260,7 +265,25 @@ lp_setup_line( struct lp_setup_context *setup,
    int i;
    int nr_planes = 4;
    boolean opaque;
-         
+   
+   /* linewidth should be interpreted as integer */
+   int fixed_width = subpixel_snap(round(width));
+
+   float xdiamond_offset=0;
+   float ydiamond_offset=0;
+   float xdiamond_offset_end=0;
+   float ydiamond_offset_end=0;
+      
+   float x1diff;
+   float y1diff;
+   float x2diff;
+   float y2diff;
+
+   boolean draw_start;
+   boolean draw_end;
+   boolean will_draw_start;
+   boolean will_draw_end;
+
    if (0)
       print_line(setup, v1, v2);
 
@@ -278,21 +301,76 @@ lp_setup_line( struct lp_setup_context *setup,
    if (!line)
       return;
 
-#ifndef DEBUG
+#ifdef DEBUG
    line->v[0][0] = v1[0][0];
    line->v[1][0] = v2[0][0];   
    line->v[0][1] = v1[0][1];
    line->v[1][1] = v2[0][1];
 #endif
 
-   /* pre-calculation(based on given vertices) to determine if line is
-    * more horizontal or more vertical
-    */
    line->dx = v1[0][0] - v2[0][0];
    line->dy = v1[0][1] - v2[0][1];
-   
-   /* x-major line */
+  
+/* X-MAJOR LINE */
    if (fabsf(line->dx) >= fabsf(line->dy)) {
+
+      x1diff = v1[0][0] - (float) floor(v1[0][0]) - 0.5;
+      y1diff = v1[0][1] - (float) floor(v1[0][1]) - 0.5;
+      x2diff = v2[0][0] - (float) floor(v2[0][0]) - 0.5;
+      y2diff = v2[0][1] - (float) floor(v2[0][1]) - 0.5;
+
+      if (y2diff==-0.5 && line->dy<0){
+         y2diff = 0.5;
+      }
+      
+      /* 
+       * Diamond exit rule test for starting point 
+       */    
+      if (fabsf(x1diff) + fabsf(y1diff) < 0.5) {
+         draw_start = TRUE;
+      }
+      else if (sign(x1diff) == sign(-line->dx)) {
+         draw_start = FALSE;
+      }
+      else if (sign(-y1diff) != sign(line->dy)) {
+         draw_start = TRUE;
+      }
+      else {
+         /* do intersection test */
+         float yintersect = v1[0][1] + x1diff*((float)line->dy/(float)line->dx);
+         if (yintersect < ceil(v1[0][1]) && yintersect > floor(v1[0][1])){
+            draw_start = TRUE;
+         }
+         else draw_start = FALSE;
+      }
+
+
+      /* 
+       * Diamond exit rule test for ending point 
+       */    
+      if (fabsf(x2diff) + fabsf(y2diff) < 0.5) {
+         draw_end = FALSE;
+      }
+      else if (sign(x2diff) != sign(-line->dx)) {
+         draw_end = FALSE;
+      }
+      else if (sign(-y2diff) == sign(line->dy)) {
+         draw_end = TRUE;
+      }
+      else {
+         /* do intersection test */
+         float yintersect = v2[0][1] + x2diff*((float)line->dy/(float)line->dx);
+         if (yintersect < ceil(v2[0][1]) && yintersect > floor(v2[0][1])){
+            draw_end = TRUE;
+         }
+         else draw_end = FALSE;
+      }
+
+      /* Are we already drawing start/end?
+       */
+      will_draw_start = sign(-x1diff) != sign(line->dx);
+      will_draw_end = (sign(x2diff) == sign(-line->dx)) || x2diff==0;
+
       if (line->dx < 0) {
          /* if v2 is to the right of v1, swap pointers */
          const float (*temp)[4] = v1;
@@ -300,21 +378,102 @@ lp_setup_line( struct lp_setup_context *setup,
          v2 = temp;
          line->dx = -line->dx;
          line->dy = -line->dy;
+         /* Otherwise shift planes appropriately */
+         if (will_draw_start != draw_start) {
+            xdiamond_offset_end = - x1diff - 0.5;
+            ydiamond_offset_end = xdiamond_offset_end*(float)line->dy/(float)line->dx;
+
+         }
+         if (will_draw_end != draw_end) {
+            xdiamond_offset = - x2diff - 0.5;
+            ydiamond_offset = xdiamond_offset*(float)line->dy/(float)line->dx;
+         }
+
       }
-      
+      else{
+         /* Otherwise shift planes appropriately */
+         if (will_draw_start != draw_start) {
+            xdiamond_offset = - x1diff + 0.5;
+            ydiamond_offset = xdiamond_offset*(float)line->dy/(float)line->dx;
+         }
+         if (will_draw_end != draw_end) {
+            xdiamond_offset_end = - x2diff + 0.5;
+            ydiamond_offset_end = xdiamond_offset_end*(float)line->dy/(float)line->dx;
+         }
+      }
+  
       /* x/y positions in fixed point */
-      x[0] = subpixel_snap(v1[0][0] - setup->pixel_offset);
-      x[1] = subpixel_snap(v2[0][0] - setup->pixel_offset);
-      x[2] = subpixel_snap(v2[0][0] - setup->pixel_offset);
-      x[3] = subpixel_snap(v1[0][0] - setup->pixel_offset);
+      x[0] = subpixel_snap(v1[0][0] + xdiamond_offset - setup->pixel_offset);
+      x[1] = subpixel_snap(v2[0][0] + xdiamond_offset_end - setup->pixel_offset);
+      x[2] = subpixel_snap(v2[0][0] + xdiamond_offset_end - setup->pixel_offset);
+      x[3] = subpixel_snap(v1[0][0] + xdiamond_offset - setup->pixel_offset);
+      
+      y[0] = subpixel_snap(v1[0][1] + ydiamond_offset - setup->pixel_offset) - fixed_width/2;
+      y[1] = subpixel_snap(v2[0][1] + ydiamond_offset_end - setup->pixel_offset) - fixed_width/2;
+      y[2] = subpixel_snap(v2[0][1] + ydiamond_offset_end - setup->pixel_offset) + fixed_width/2;
+      y[3] = subpixel_snap(v1[0][1] + ydiamond_offset - setup->pixel_offset) + fixed_width/2;
       
-      y[0] = subpixel_snap(v1[0][1] - half_width - setup->pixel_offset);
-      y[1] = subpixel_snap(v2[0][1] - half_width - setup->pixel_offset);
-      y[2] = subpixel_snap(v2[0][1] + half_width - setup->pixel_offset);
-      y[3] = subpixel_snap(v1[0][1] + half_width - setup->pixel_offset);
    }
+
+
    else{
-      /* y-major line */
+/* Y-MAJOR LINE */      
+      x1diff = v1[0][0] - (float) floor(v1[0][0]) - 0.5;
+      y1diff = v1[0][1] - (float) floor(v1[0][1]) - 0.5;
+      x2diff = v2[0][0] - (float) floor(v2[0][0]) - 0.5;
+      y2diff = v2[0][1] - (float) floor(v2[0][1]) - 0.5;
+
+      if (x2diff==-0.5 && line->dx<0){
+         x2diff = 0.5;
+      }
+
+/* 
+ * Diamond exit rule test for starting point 
+ */    
+      if (fabsf(x1diff) + fabsf(y1diff) < 0.5) {
+         draw_start = TRUE;
+      }
+      else if (sign(-y1diff) == sign(line->dy)) {
+         draw_start = FALSE;
+      }
+      else if (sign(x1diff) != sign(-line->dx)) {
+         draw_start = TRUE;
+      }
+      else {
+         /* do intersection test */
+         float xintersect = v1[0][0] + y1diff*((float)line->dx/(float)line->dy);
+         if (xintersect < ceil(v1[0][0]) && xintersect > floor(v1[0][0])){
+            draw_start = TRUE;
+         }
+         else draw_start = FALSE;
+      }
+
+      /* 
+       * Diamond exit rule test for ending point 
+       */    
+      if (fabsf(x2diff) + fabsf(y2diff) < 0.5) {
+         draw_end = FALSE;
+      }
+      else if (sign(-y2diff) != sign(line->dy) ) {
+         draw_end = FALSE;
+      }
+      else if (sign(x2diff) == sign(-line->dx) ) {
+         draw_end = TRUE;
+      }
+      else {
+         /* do intersection test */
+         float xintersect = v2[0][0] + y2diff*((float)line->dx/(float)line->dy);
+         if (xintersect < ceil(v2[0][0]) && xintersect > floor(v2[0][0])){
+            draw_end = TRUE;
+         }
+         else draw_end = FALSE;
+      }
+
+      /* Are we already drawing start/end?
+       */
+      will_draw_start = sign(y1diff) == sign(line->dy);
+      will_draw_end = (sign(-y2diff) == sign(line->dy)) || y2diff==0;
+
       if (line->dy > 0) {
          /* if v2 is on top of v1, swap pointers */
          const float (*temp)[4] = v1;
@@ -322,19 +481,44 @@ lp_setup_line( struct lp_setup_context *setup,
          v2 = temp; 
          line->dx = -line->dx;
          line->dy = -line->dy;
+
+         /* Otherwise shift planes appropriately */
+         if (will_draw_start != draw_start) {
+            ydiamond_offset_end = - y1diff + 0.5;
+            xdiamond_offset_end = ydiamond_offset_end*(float)line->dx/(float)line->dy;
+         }
+         if (will_draw_end != draw_end) {
+            ydiamond_offset = - y2diff + 0.5;
+            xdiamond_offset = ydiamond_offset*(float)line->dx/(float)line->dy;
+         }
+      }
+
+      else{
+         /* Otherwise shift planes appropriately */
+         if (will_draw_start != draw_start) {
+            ydiamond_offset = - y1diff - 0.5;
+            xdiamond_offset = ydiamond_offset*(float)line->dx/(float)line->dy;
+                     
+         }
+         if (will_draw_end != draw_end) {
+            ydiamond_offset_end = - y2diff - 0.5;
+            xdiamond_offset_end = ydiamond_offset_end*(float)line->dx/(float)line->dy;
+         }
       }
  
-      x[0] = subpixel_snap(v1[0][0] - half_width - setup->pixel_offset);
-      x[1] = subpixel_snap(v2[0][0] - half_width - setup->pixel_offset);
-      x[2] = subpixel_snap(v2[0][0] + half_width - setup->pixel_offset);
-      x[3] = subpixel_snap(v1[0][0] + half_width - setup->pixel_offset);
+      /* x/y positions in fixed point */
+      x[0] = subpixel_snap(v1[0][0] + xdiamond_offset - setup->pixel_offset) - fixed_width/2;
+      x[1] = subpixel_snap(v2[0][0] + xdiamond_offset_end - setup->pixel_offset) - fixed_width/2;
+      x[2] = subpixel_snap(v2[0][0] + xdiamond_offset_end - setup->pixel_offset) + fixed_width/2;
+      x[3] = subpixel_snap(v1[0][0] + xdiamond_offset - setup->pixel_offset) + fixed_width/2;
      
-      y[0] = subpixel_snap(v1[0][1] - setup->pixel_offset);
-      y[1] = subpixel_snap(v2[0][1] - setup->pixel_offset);
-      y[2] = subpixel_snap(v2[0][1] - setup->pixel_offset);
-      y[3] = subpixel_snap(v1[0][1] - setup->pixel_offset);
+      y[0] = subpixel_snap(v1[0][1] + ydiamond_offset - setup->pixel_offset); 
+      y[1] = subpixel_snap(v2[0][1] + ydiamond_offset_end - setup->pixel_offset);
+      y[2] = subpixel_snap(v2[0][1] + ydiamond_offset_end - setup->pixel_offset);
+      y[3] = subpixel_snap(v1[0][1] + ydiamond_offset - setup->pixel_offset);
    }
 
+
    /* calculate the deltas */
    line->plane[0].dcdy = x[0] - x[1];
    line->plane[1].dcdy = x[1] - x[2];
@@ -361,8 +545,8 @@ lp_setup_line( struct lp_setup_context *setup,
 
       minx = (MIN4(x[0], x[1], x[2], x[3]) + (FIXED_ONE-1)) >> FIXED_ORDER;
       maxx = (MAX4(x[0], x[1], x[2], x[3]) + (FIXED_ONE-1)) >> FIXED_ORDER;
-      miny = (MIN4(y[0], y[1], y[3], y[3]) + (FIXED_ONE-1) + adj) >> FIXED_ORDER;
-      maxy = (MAX4(y[0], y[1], y[3], y[3]) + (FIXED_ONE-1) + adj) >> FIXED_ORDER;
+      miny = (MIN4(y[0], y[1], y[2], y[3]) + (FIXED_ONE-1) + adj) >> FIXED_ORDER;
+      maxy = (MAX4(y[0], y[1], y[2], y[3]) + (FIXED_ONE-1) + adj) >> FIXED_ORDER;
    }
 
    if (setup->scissor_test) {
@@ -526,7 +710,7 @@ lp_setup_line( struct lp_setup_context *setup,
 
 
    /*
-    * All fields of 'tri' are now set.  The remaining code here is
+    * All fields of 'line' are now set.  The remaining code here is
     * concerned with binning.
     */