1 /* 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /** 27 * Bresenham line-drawing implementation decomposing line segments 28 * into a series of rectangles. 29 * This is required, because xrender doesn't support line primitives directly. 30 * The code here is an almost 1:1 port of the existing C-source contained in 31 * sun/java2d/loop/DrawLine.c and sun/java2d/loop/LoopMacros.h 32 */ 33 package sun.java2d.xr; 34 35 public class XRDrawLine { 36 static final int BIG_MAX = ((1 << 29) - 1); 37 static final int BIG_MIN = (-(1 << 29)); 38 39 static final int OUTCODE_TOP = 1; 40 static final int OUTCODE_BOTTOM = 2; 41 static final int OUTCODE_LEFT = 4; 42 static final int OUTCODE_RIGHT = 8; 43 44 int x1, y1, x2, y2; 45 int ucX1, ucY1, ucX2, ucY2; 46 47 DirtyRegion region = new DirtyRegion(); 48 49 protected void rasterizeLine(GrowableRectArray rectBuffer, int _x1, 50 int _y1, int _x2, int _y2, int cxmin, int cymin, int cxmax, 51 int cymax, boolean clip, boolean overflowCheck) { 52 float diagF; 53 int error; 54 int steps; 55 int errminor, errmajor; 56 boolean xmajor; 57 int dx, dy, ax, ay; 58 59 initCoordinates(_x1, _y1, _x2, _y2, overflowCheck); 60 61 dx = x2 - x1; 62 dy = y2 - y1; 63 ax = Math.abs(dx); 64 ay = Math.abs(dy); 65 xmajor = (ax >= ay); 66 diagF = ((float) ax) / ay; 67 68 if (clip 69 && !clipCoordinates(cxmin, cymin, cxmax, cymax, xmajor, dx, dy, 70 ax, ay)) { 71 // whole line was clipped away 72 return; 73 } 74 75 region.setDirtyLineRegion(x1, y1, x2, y2); 76 int xDiff = region.x2 - region.x; 77 int yDiff = region.y2 - region.y; 78 79 if (xDiff == 0 || yDiff == 0) { 80 // horizontal / diagonal lines can be represented by a single 81 // rectangle 82 rectBuffer.pushRectValues(region.x, region.y, region.x2 - region.x 83 + 1, region.y2 - region.y + 1); 84 return; 85 } 86 87 // Setup bresenham 88 if (xmajor) { 89 errmajor = ay * 2; 90 errminor = ax * 2; 91 ax = -ax; /* For clipping adjustment below */ 92 steps = x2 - x1; 93 } else { 94 errmajor = ax * 2; 95 errminor = ay * 2; 96 ay = -ay; /* For clipping adjustment below */ 97 steps = y2 - y1; 98 } 99 100 if ((steps = (Math.abs(steps) + 1)) == 0) { 101 return; 102 } 103 104 error = -(errminor / 2); 105 106 if (y1 != ucY1) { 107 int ysteps = y1 - ucY1; 108 if (ysteps < 0) { 109 ysteps = -ysteps; 110 } 111 error += ysteps * ax * 2; 112 } 113 114 if (x1 != ucX1) { 115 int xsteps = x1 - ucX1; 116 if (xsteps < 0) { 117 xsteps = -xsteps; 118 } 119 error += xsteps * ay * 2; 120 } 121 error += errmajor; 122 errminor -= errmajor; 123 124 int xStep = (dx > 0 ? 1 : -1); 125 int yStep = (dy > 0 ? 1 : -1); 126 int orthogonalXStep = xmajor ? xStep : 0; 127 int orthogonalYStep = !xmajor ? yStep : 0; 128 129 /* 130 * For lines which proceed in one direction faster, we try to generate 131 * rectangles instead of points. Otherwise we try to avoid the extra 132 * work... 133 */ 134 if (diagF <= 0.9 || diagF >= 1.1) { 135 lineToRects(rectBuffer, steps, error, errmajor, errminor, xStep, 136 yStep, orthogonalXStep, orthogonalYStep); 137 } else { 138 lineToPoints(rectBuffer, steps, error, errmajor, errminor, xStep, 139 yStep, orthogonalXStep, orthogonalYStep); 140 } 141 } 142 143 private void lineToPoints(GrowableRectArray rectBuffer, int steps, 144 int error, int errmajor, int errminor, int xStep, int yStep, 145 int orthogonalXStep, int orthogonalYStep) { 146 int x = x1, y = y1; 147 148 do { 149 rectBuffer.pushRectValues(x, y, 1, 1); 150 151 // "Traditional" Bresenham line drawing 152 if (error < 0) { 153 error += errmajor; 154 x += orthogonalXStep; 155 y += orthogonalYStep; 156 } else { 157 error -= errminor; 158 x += xStep; 159 y += yStep; 160 } 161 } while (--steps > 0); 162 } 163 164 private void lineToRects(GrowableRectArray rectBuffer, int steps, 165 int error, int errmajor, int errminor, int xStep, int yStep, 166 int orthogonalXStep, int orthogonalYStep) { 167 int x = x1, y = y1; 168 int rectX = Integer.MIN_VALUE, rectY = 0; 169 int rectW = 0, rectH = 0; 170 171 do { 172 // Combine the resulting rectangles 173 // for steps performed in a single direction. 174 if (y == rectY) { 175 if (x == (rectX + rectW)) { 176 rectW++; 177 } else if (x == (rectX - 1)) { 178 rectX--; 179 rectW++; 180 } 181 } else if (x == rectX) { 182 if (y == (rectY + rectH)) { 183 rectH++; 184 } else if (y == (rectY - 1)) { 185 rectY--; 186 rectH++; 187 } 188 } else { 189 // Diagonal step: add the previous rectangle to the list, 190 // iff it was "real" (= not initialized before the first 191 // iteration) 192 if (rectX != Integer.MIN_VALUE) { 193 rectBuffer.pushRectValues(rectX, rectY, rectW, rectH); 194 } 195 rectX = x; 196 rectY = y; 197 rectW = rectH = 1; 198 } 199 200 // "Traditional" Bresenham line drawing 201 if (error < 0) { 202 error += errmajor; 203 x += orthogonalXStep; 204 y += orthogonalYStep; 205 } else { 206 error -= errminor; 207 x += xStep; 208 y += yStep; 209 } 210 } while (--steps > 0); 211 212 // Add last rectangle which isn't handled by the combination-code 213 // anymore 214 rectBuffer.pushRectValues(rectX, rectY, rectW, rectH); 215 } 216 217 private boolean clipCoordinates(int cxmin, int cymin, int cxmax, int cymax, 218 boolean xmajor, int dx, int dy, int ax, int ay) { 219 int outcode1, outcode2; 220 221 outcode1 = outcode(x1, y1, cxmin, cymin, cxmax, cymax); 222 outcode2 = outcode(x2, y2, cxmin, cymin, cxmax, cymax); 223 224 while ((outcode1 | outcode2) != 0) { 225 int xsteps = 0, ysteps = 0; 226 227 if ((outcode1 & outcode2) != 0) { 228 return false; 229 } 230 231 if (outcode1 != 0) { 232 if ((outcode1 & (OUTCODE_TOP | OUTCODE_BOTTOM)) != 0) { 233 if ((outcode1 & OUTCODE_TOP) != 0) { 234 y1 = cymin; 235 } else { 236 y1 = cymax; 237 } 238 ysteps = y1 - ucY1; 239 if (ysteps < 0) { 240 ysteps = -ysteps; 241 } 242 xsteps = 2 * ysteps * ax + ay; 243 if (xmajor) { 244 xsteps += ay - ax - 1; 245 } 246 xsteps = xsteps / (2 * ay); 247 if (dx < 0) { 248 xsteps = -xsteps; 249 } 250 x1 = ucX1 + xsteps; 251 } else if ((outcode1 & (OUTCODE_LEFT | OUTCODE_RIGHT)) != 0) { 252 if ((outcode1 & OUTCODE_LEFT) != 0) { 253 x1 = cxmin; 254 } else { 255 x1 = cxmax; 256 } 257 xsteps = x1 - ucX1; 258 if (xsteps < 0) { 259 xsteps = -xsteps; 260 } 261 ysteps = 2 * xsteps * ay + ax; 262 if (!xmajor) { 263 ysteps += ax - ay - 1; 264 } 265 ysteps = ysteps / (2 * ax); 266 if (dy < 0) { 267 ysteps = -ysteps; 268 } 269 y1 = ucY1 + ysteps; 270 } 271 outcode1 = outcode(x1, y1, cxmin, cymin, cxmax, cymax); 272 } else { 273 if ((outcode2 & (OUTCODE_TOP | OUTCODE_BOTTOM)) != 0) { 274 if ((outcode2 & OUTCODE_TOP) != 0) { 275 y2 = cymin; 276 } else { 277 y2 = cymax; 278 } 279 ysteps = y2 - ucY2; 280 if (ysteps < 0) { 281 ysteps = -ysteps; 282 } 283 xsteps = 2 * ysteps * ax + ay; 284 if (xmajor) { 285 xsteps += ay - ax; 286 } else { 287 xsteps -= 1; 288 } 289 xsteps = xsteps / (2 * ay); 290 if (dx > 0) { 291 xsteps = -xsteps; 292 } 293 x2 = ucX2 + xsteps; 294 } else if ((outcode2 & (OUTCODE_LEFT | OUTCODE_RIGHT)) != 0) { 295 if ((outcode2 & OUTCODE_LEFT) != 0) { 296 x2 = cxmin; 297 } else { 298 x2 = cxmax; 299 } 300 xsteps = x2 - ucX2; 301 if (xsteps < 0) { 302 xsteps = -xsteps; 303 } 304 ysteps = 2 * xsteps * ay + ax; 305 if (xmajor) { 306 ysteps -= 1; 307 } else { 308 ysteps += ax - ay; 309 } 310 ysteps = ysteps / (2 * ax); 311 if (dy > 0) { 312 ysteps = -ysteps; 313 } 314 y2 = ucY2 + ysteps; 315 } 316 outcode2 = outcode(x2, y2, cxmin, cymin, cxmax, cymax); 317 } 318 } 319 320 return true; 321 } 322 323 private void initCoordinates(int x1, int y1, int x2, int y2, 324 boolean checkOverflow) { 325 /* 326 * Part of calculating the Bresenham parameters for line stepping 327 * involves being able to store numbers that are twice the magnitude of 328 * the biggest absolute difference in coordinates. Since we want the 329 * stepping parameters to be stored in jints, we then need to avoid any 330 * absolute differences more than 30 bits. Thus, we need to preprocess 331 * the coordinates to reduce their range to 30 bits regardless of 332 * clipping. We need to cut their range back before we do the clipping 333 * because the Bresenham stepping values need to be calculated based on 334 * the "unclipped" coordinates. 335 * 336 * Thus, first we perform a "pre-clipping" stage to bring the 337 * coordinates within the 30-bit range and then we proceed to the 338 * regular clipping procedure, pretending that these were the original 339 * coordinates all along. Since this operation occurs based on a 340 * constant "pre-clip" rectangle of +/- 30 bits without any 341 * consideration for the final clip, the rounding errors that occur here 342 * will depend only on the line coordinates and be invariant with 343 * respect to the particular device/user clip rectangles in effect at 344 * the time. Thus, rendering a given large-range line will be consistent 345 * under a variety of clipping conditions. 346 */ 347 if (checkOverflow 348 && (OverflowsBig(x1) || OverflowsBig(y1) || OverflowsBig(x2) || OverflowsBig(y2))) { 349 /* 350 * Use doubles to get us into range for "Big" arithmetic. 351 * 352 * The math of adjusting an endpoint for clipping can involve an 353 * intermediate result with twice the number of bits as the original 354 * coordinate range. Since we want to maintain as much as 30 bits of 355 * precision in the resulting coordinates, we will get roundoff here 356 * even using IEEE double-precision arithmetic which cannot carry 60 357 * bits of mantissa. Since the rounding errors will be consistent 358 * for a given set of input coordinates the potential roundoff error 359 * should not affect the consistency of our rendering. 360 */ 361 double x1d = x1; 362 double y1d = y1; 363 double x2d = x2; 364 double y2d = y2; 365 double dxd = x2d - x1d; 366 double dyd = y2d - y1d; 367 368 if (x1 < BIG_MIN) { 369 y1d = y1 + (BIG_MIN - x1) * dyd / dxd; 370 x1d = BIG_MIN; 371 } else if (x1 > BIG_MAX) { 372 y1d = y1 - (x1 - BIG_MAX) * dyd / dxd; 373 x1d = BIG_MAX; 374 } 375 /* Use Y1d instead of _y1 for testing now as we may have modified it */ 376 if (y1d < BIG_MIN) { 377 x1d = x1 + (BIG_MIN - y1) * dxd / dyd; 378 y1d = BIG_MIN; 379 } else if (y1d > BIG_MAX) { 380 x1d = x1 - (y1 - BIG_MAX) * dxd / dyd; 381 y1d = BIG_MAX; 382 } 383 if (x2 < BIG_MIN) { 384 y2d = y2 + (BIG_MIN - x2) * dyd / dxd; 385 x2d = BIG_MIN; 386 } else if (x2 > BIG_MAX) { 387 y2d = y2 - (x2 - BIG_MAX) * dyd / dxd; 388 x2d = BIG_MAX; 389 } 390 /* Use Y2d instead of _y2 for testing now as we may have modified it */ 391 if (y2d < BIG_MIN) { 392 x2d = x2 + (BIG_MIN - y2) * dxd / dyd; 393 y2d = BIG_MIN; 394 } else if (y2d > BIG_MAX) { 395 x2d = x2 - (y2 - BIG_MAX) * dxd / dyd; 396 y2d = BIG_MAX; 397 } 398 399 x1 = (int) x1d; 400 y1 = (int) y1d; 401 x2 = (int) x2d; 402 y2 = (int) y2d; 403 } 404 405 this.x1 = ucX1 = x1; 406 this.y1 = ucY1 = y1; 407 this.x2 = ucX2 = x2; 408 this.y2 = ucY2 = y2; 409 } 410 411 private boolean OverflowsBig(int v) { 412 return ((v) != (((v) << 2) >> 2)); 413 } 414 415 private int out(int v, int vmin, int vmax, int cmin, int cmax) { 416 return ((v < vmin) ? cmin : ((v > vmax) ? cmax : 0)); 417 } 418 419 private int outcode(int x, int y, int xmin, int ymin, int xmax, int ymax) { 420 return out(y, ymin, ymax, OUTCODE_TOP, OUTCODE_BOTTOM) 421 | out(x, xmin, xmax, OUTCODE_LEFT, OUTCODE_RIGHT); 422 } 423 }