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