001/*
002
003*/
004
005package renderer.pipeline;
006import  renderer.scene.*;
007import  renderer.framebuffer.*;
008
009import java.awt.Color;
010
011/**
012   Rasterize a {@link LineSegment} into pixels in the
013   {@link FrameBuffer}'s viewport and anti-alias the
014   line at the same time.
015<p>
016   This is a fairly simplistic anti-aliasing algorithm.
017*/
018public class RasterizeAntialias
019{
020   public static boolean debug = false;
021
022   /**
023      Rasterize a {@link LineSegment} into antialiased pixels in the
024      {@link FrameBuffer}'s viewport.
025
026      @param ls  {@link LineSegment} to rasterize into the {@link FrameBuffer}
027      @param fb  {@link FrameBuffer} to hold rasterized pixels
028   */
029   public static void rasterize(LineSegment ls, FrameBuffer fb)
030   {
031      // Get the viewport's background color.
032      Color bg = fb.getBgColorVP();
033
034      // Make local copies of several values.
035      int w = fb.getWidthVP();
036      int h = fb.getHeightVP();
037
038      Vertex v0 = ls.model.vertexList.get( ls.index[0] );
039      Vertex v1 = ls.model.vertexList.get( ls.index[1] );
040
041      // Get the colors from the two vertices.
042      float r0 = v0.r,  g0 = v0.g,  b0 = v0.b;
043      float r1 = v1.r,  g1 = v1.g,  b1 = v1.b;
044
045      // Round the line segment's two endpoints to the nearest
046      // logical pixel. This makes the algorithm a lot simpler,
047      // but it can cause a slight, but noticable, shift of the
048      // line segment.
049      double x0 = Math.round(v0.x),  y0 = Math.round(v0.y);
050      double x1 = Math.round(v1.x),  y1 = Math.round(v1.y);
051
052      // Rasterize a degenerate line segment (a line segment
053      // that projected onto a point) as a single pixel.
054      if ( (x0 == x1) && (y0 == y1) )
055      {
056         // We don't know which endpoint of the line segment
057         // is in front, so just pick v0.
058         fb.setPixelVP((int)x0 - 1, h - (int)y0, new Color(r0, g0, b0));
059         return;
060      }
061
062      if (Math.abs(y1 - y0) <= Math.abs(x1 - x0)) // if abs(slope) <= 1
063      {
064         if (x1 < x0) // swap (x0, y0) with (x1, y1)
065         {
066            double tempX = x0;
067            double tempY = y0;
068            x0 = x1;
069            y0 = y1;
070            x1 = tempX;
071            y1 = tempY;
072            // swap the colors too
073            float tempR = r0;
074            float tempG = g0;
075            float tempB = b0;
076            r0 = r1;
077            g0 = g1;
078            b0 = b1;
079            r1 = tempR;
080            g1 = tempG;
081            b1 = tempB;
082         }
083
084         // Compute this line segment's slope.
085         double      m = (y1 - y0) / (x1 - x0);
086         double slopeR = (r1 - r0) / (x1 - x0);
087         double slopeG = (g1 - g0) / (x1 - x0);
088         double slopeB = (b1 - b0) / (x1 - x0);
089
090         // Rasterize this line segment.
091         // In the following loop, as x moves across the logical
092         // horizontal pixels, we will compute a y value for each x.
093         double y = y0;
094         for (int x = (int)x0; x < (int)x1; x++, y += m)
095         {
096            // Interpolate this pixel's color between the two endpoint's colors.
097            float r = (float)Math.abs(r0 + slopeR*(x - x0));
098            float g = (float)Math.abs(g0 + slopeG*(x - x0));
099            float b = (float)Math.abs(b0 + slopeB*(x - x0));
100            // We need the Math.abs() because otherwise, we sometimes get -0.0.
101
102            // Check if y is on the bottom or top edge of the pixel coordinate space.
103            if ( y <= 1 || h <= y )
104            {
105               // Set this pixel in the framebuffer.
106               fb.setPixelVP(x - 1, h - (int)Math.round(y), new Color(r, g, b));
107
108               // log interesting information to standard output
109               if (debug) logPixel(w, h, x, y, r, g, b);
110            }
111            else // y must be between two logical vertical pixel coordinates
112            {
113               // Let y_low and y_hi be the logical vertical pixel
114               // coordinates that bracket around y.
115               int y_low = (int)y;                // the integer part of y
116               int y_hi  = y_low + 1;
117               // Let weight be the fractional part of y. We will use
118               // weight to determine how much emphasis to place on
119               // each of the two pixels that bracket y.
120               float weight = (float)(y - y_low);
121
122               // Interpolate colors for the low and high pixels.
123               // The smaller weight is, the closer y is to the lower
124               // pixel, so we give the lower pixel more emphasis when
125               // weight is small.
126               float r_low = (float)((1 - weight) * r + weight * (bg.getRed()/255.0));
127               float g_low = (float)((1 - weight) * g + weight * (bg.getGreen()/255.0));
128               float b_low = (float)((1 - weight) * b + weight * (bg.getBlue()/255.0));
129               float r_hi  = (float)(weight * r + (1 - weight) * (bg.getRed()/255.0));
130               float g_hi  = (float)(weight * g + (1 - weight) * (bg.getGreen()/255.0));
131               float b_hi  = (float)(weight * b + (1 - weight) * (bg.getBlue()/255.0));
132
133               // Set this (antialiased) pixel in the framebuffer.
134               fb.setPixelVP(x - 1, h - y_low, new Color(r_low, g_low, b_low));
135               fb.setPixelVP(x - 1, h - y_hi,  new Color(r_hi,  g_hi,  b_hi));
136
137               // log interesting information to standard output
138               if (debug) logPixel(w, h, x, y, r_low, g_low, b_low, r_hi, g_hi, b_hi);
139            }
140            // Advance (x,y) to the next pixel (delta_x is 1, so delta_y is m).
141         }
142         // Set the pixel for the (x1,y1) endpoint
143         // (we do this separately to avoid roundoff errors).
144         fb.setPixelVP((int)x1 - 1, h - (int)y1, new Color(r1, g1, b1));
145      }
146      else  // abs(slope) > 1
147      {
148         if (y1 < y0) // swap (x0, y0) with (x1, y1)
149         {
150            double tempX = x0;
151            double tempY = y0;
152            x0 = x1;
153            y0 = y1;
154            x1 = tempX;
155            y1 = tempY;
156            // swap the colors too
157            float tempR = r0;
158            float tempG = g0;
159            float tempB = b0;
160            r0 = r1;
161            g0 = g1;
162            b0 = b1;
163            r1 = tempR;
164            g1 = tempG;
165            b1 = tempB;
166         }
167
168         // Compute this line segment's slope.
169         double      m = (x1 - x0) / (y1 - y0);
170         double slopeR = (r1 - r0) / (y1 - y0);
171         double slopeG = (g1 - g0) / (y1 - y0);
172         double slopeB = (b1 - b0) / (y1 - y0);
173
174         // Rasterize this line segment.
175         // In the following loop, as y moves across the logical
176         // vertical pixels, we will compute a x value for each y.
177         double x = x0;
178         for (int y = (int)y0; y < (int)y1; x += m, y++)
179         {
180            // Interpolate this pixel's color between the two endpoint's colors.
181            float r = (float)Math.abs(r0 + slopeR*(y - y0));
182            float g = (float)Math.abs(g0 + slopeG*(y - y0));
183            float b = (float)Math.abs(b0 + slopeB*(y - y0));
184            // We need the Math.abs() because otherwise, we sometimes get -0.0.
185
186            // Check if x is on the left or right edge of the pixel coordinate space.
187            if ( x <= 1 || w <= x )
188            {
189               // Set this pixel in the framebuffer.
190               fb.setPixelVP((int)Math.round(x) - 1, h - y, new Color(r, g, b));
191
192               // log interesting information to standard output
193               if (debug) logPixel(w, h, x, y, r, g, b);
194            }
195            else // x must be between two logical horizontal pixel coordinates
196            {
197               // x_low and x_hi will be pixel coordinates that bracket around x
198               int x_low = (int)x;                // the integer part of x
199               int x_hi  = x_low + 1;
200
201               float weight = (float)(x - x_low); // the fractional part of x
202
203               // Interpolate colors for the low and high pixels.
204               float r_low = (float)((1 - weight) * r + weight * (bg.getRed()/255.0));
205               float g_low = (float)((1 - weight) * g + weight * (bg.getGreen()/255.0));
206               float b_low = (float)((1 - weight) * b + weight * (bg.getBlue()/255.0));
207               float r_hi  = (float)(weight * r + (1 - weight) * (bg.getRed()/255.0));
208               float g_hi  = (float)(weight * g + (1 - weight) * (bg.getGreen()/255.0));
209               float b_hi  = (float)(weight * b + (1 - weight) * (bg.getBlue()/255.0));
210
211               // Set this (antialiased) pixel in the framebuffer.
212               fb.setPixelVP(x_low - 1, h - y, new Color(r_low, g_low, b_low));
213               fb.setPixelVP( x_hi - 1, h - y, new Color(r_hi,  g_hi,  b_hi));
214
215               // log interesting information to standard output
216               if (debug) logPixel(w, h, x, y, r_low, g_low, b_low, r_hi, g_hi, b_hi);
217            }
218            // Advance (x,y) to the next pixel (delta_y is 1, so delta_x is m).
219         }
220         // Set the pixel for the (x1,y1) endpoint
221         // (we do this separately to avoid roundoff errors).
222         fb.setPixelVP((int)x1 - 1, h - (int)y1, new Color(r1, g1, b1));
223      }
224   }
225
226
227   private static void logPixel(int w, int h, int x, double y,
228                                float r, float g, float b)
229   {
230      System.out.printf(
231         "[w=%d, h=%d]  x=%d, y=%f, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b);
232   }
233
234   private static void logPixel(int w, int h, double x, int y,
235                                float r, float g, float b)
236   {
237      System.out.printf(
238         "[w=%d, h=%d]  x=%f, y=%d, r=%f, g=%f, b=%f\n", w, h, x, y, r, g, b);
239   }
240
241   private static void logPixel(int w, int h, int x, double y,
242                                float r1, float g1, float b1,
243                                float r2, float g2, float b2)
244   {
245      System.out.printf(
246         "[w=%d, h=%d]  x=%d, y=%f, r=%f, g=%f, b=%f\n", w, h, x, y, r1, g1, b1);
247      System.out.printf(
248         "              x=%d, y=%f, r=%f, g=%f, b=%f\n",       x, y, r2, g2, b2);
249   }
250
251   private static void logPixel(int w, int h, double x, int y,
252                                float r1, float g1, float b1,
253                                float r2, float g2, float b2)
254   {
255      System.out.printf(
256         "[w=%d, h=%d]  x=%f, y=%d, r=%f, g=%f, b=%f\n", w, h, x, y, r1, g1, b1);
257      System.out.printf(
258         "              x=%f, y=%d, r=%f, g=%f, b=%f\n",       x, y, r2, g2, b2);
259   }
260}