1 /* 2 * Copyright (c) 2008, 2011, 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 package sun.java2d.pipe; 27 28 import java.awt.Shape; 29 import java.awt.BasicStroke; 30 import java.awt.geom.Line2D; 31 import java.awt.geom.Rectangle2D; 32 import java.awt.geom.AffineTransform; 33 import sun.java2d.SunGraphics2D; 34 import sun.awt.SunHints; 35 36 /** 37 * This class converts calls to the basic pixel rendering methods 38 * into calls to the methods on a ParallelogramPipe. 39 * Most calls are transformed into calls to the fill(Shape) method 40 * by the parent PixelToShapeConverter class, but some calls are 41 * transformed into calls to fill/drawParallelogram(). 42 */ 43 public class PixelToParallelogramConverter extends PixelToShapeConverter 44 implements ShapeDrawPipe 45 { 46 ParallelogramPipe outrenderer; 47 double minPenSize; 48 double normPosition; 49 double normRoundingBias; 50 boolean adjustfill; 51 52 /** 53 * @param shapepipe pipeline to forward shape calls to 54 * @param pgrampipe pipeline to forward parallelogram calls to 55 * (and drawLine calls if possible) 56 * @param minPenSize minimum pen size for dropout control 57 * @param normPosition sub-pixel location to normalize endpoints 58 * for STROKE_NORMALIZE cases 59 * @param adjustfill boolean to control whethere normalization 60 * constants are also applied to fill operations 61 * (normally true for non-AA, false for AA) 62 */ 63 public PixelToParallelogramConverter(ShapeDrawPipe shapepipe, 64 ParallelogramPipe pgrampipe, 65 double minPenSize, 66 double normPosition, 67 boolean adjustfill) 68 { 69 super(shapepipe); 70 outrenderer = pgrampipe; 71 this.minPenSize = minPenSize; 72 this.normPosition = normPosition; 73 this.normRoundingBias = 0.5 - normPosition; 74 this.adjustfill = adjustfill; 75 } 76 77 public void drawLine(SunGraphics2D sg2d, 78 int x1, int y1, int x2, int y2) 79 { 80 if (!drawGeneralLine(sg2d, x1, y1, x2, y2)) { 81 super.drawLine(sg2d, x1, y1, x2, y2); 82 } 83 } 84 85 public void drawRect(SunGraphics2D sg2d, 86 int x, int y, int w, int h) 87 { 88 if (w >= 0 && h >= 0) { 89 if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) { 90 BasicStroke bs = ((BasicStroke) sg2d.stroke); 91 if (w > 0 && h > 0) { 92 if (bs.getLineJoin() == BasicStroke.JOIN_MITER && 93 bs.getDashArray() == null) 94 { 95 double lw = bs.getLineWidth(); 96 drawRectangle(sg2d, x, y, w, h, lw); 97 return; 98 } 99 } else { 100 // Note: This calls the integer version which 101 // will verify that the local drawLine optimizations 102 // work and call super.drawLine(), if not. 103 drawLine(sg2d, x, y, x+w, y+h); 104 return; 105 } 106 } 107 super.drawRect(sg2d, x, y, w, h); 108 } 109 } 110 111 public void fillRect(SunGraphics2D sg2d, 112 int x, int y, int w, int h) 113 { 114 if (w > 0 && h > 0) { 115 fillRectangle(sg2d, x, y, w, h); 116 } 117 } 118 119 public void draw(SunGraphics2D sg2d, Shape s) { 120 if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) { 121 BasicStroke bs = ((BasicStroke) sg2d.stroke); 122 if (s instanceof Rectangle2D) { 123 if (bs.getLineJoin() == BasicStroke.JOIN_MITER && 124 bs.getDashArray() == null) 125 { 126 Rectangle2D r2d = (Rectangle2D) s; 127 double w = r2d.getWidth(); 128 double h = r2d.getHeight(); 129 double x = r2d.getX(); 130 double y = r2d.getY(); 131 if (w >= 0 && h >= 0) { 132 double lw = bs.getLineWidth(); 133 drawRectangle(sg2d, x, y, w, h, lw); 134 } 135 return; 136 } 137 } else if (s instanceof Line2D) { 138 Line2D l2d = (Line2D) s; 139 if (drawGeneralLine(sg2d, 140 l2d.getX1(), l2d.getY1(), 141 l2d.getX2(), l2d.getY2())) 142 { 143 return; 144 } 145 } 146 } 147 148 outpipe.draw(sg2d, s); 149 } 150 151 public void fill(SunGraphics2D sg2d, Shape s) { 152 if (s instanceof Rectangle2D) { 153 Rectangle2D r2d = (Rectangle2D) s; 154 double w = r2d.getWidth(); 155 double h = r2d.getHeight(); 156 if (w > 0 && h > 0) { 157 double x = r2d.getX(); 158 double y = r2d.getY(); 159 fillRectangle(sg2d, x, y, w, h); 160 } 161 return; 162 } 163 164 outpipe.fill(sg2d, s); 165 } 166 167 static double len(double x, double y) { 168 return ((x == 0) ? Math.abs(y) 169 : ((y == 0) ? Math.abs(x) 170 : Math.sqrt(x * x + y * y))); 171 } 172 173 double normalize(double v) { 174 return Math.floor(v + normRoundingBias) + normPosition; 175 } 176 177 public boolean drawGeneralLine(SunGraphics2D sg2d, 178 double ux1, double uy1, 179 double ux2, double uy2) 180 { 181 if (sg2d.strokeState == SunGraphics2D.STROKE_CUSTOM || 182 sg2d.strokeState == SunGraphics2D.STROKE_THINDASHED) 183 { 184 return false; 185 } 186 BasicStroke bs = (BasicStroke) sg2d.stroke; 187 int cap = bs.getEndCap(); 188 if (cap == BasicStroke.CAP_ROUND || bs.getDashArray() != null) { 189 // TODO: we could construct the GeneralPath directly 190 // for CAP_ROUND and save a lot of processing in that case... 191 // And again, we would need to deal with dropout control... 192 return false; 193 } 194 double lw = bs.getLineWidth(); 195 // Save the original dx, dy in case we need it to transform 196 // the linewidth as a perpendicular vector below 197 double dx = ux2 - ux1; 198 double dy = uy2 - uy1; 199 double x1, y1, x2, y2; 200 switch (sg2d.transformState) { 201 case SunGraphics2D.TRANSFORM_GENERIC: 202 case SunGraphics2D.TRANSFORM_TRANSLATESCALE: 203 { 204 double coords[] = {ux1, uy1, ux2, uy2}; 205 sg2d.transform.transform(coords, 0, coords, 0, 2); 206 x1 = coords[0]; 207 y1 = coords[1]; 208 x2 = coords[2]; 209 y2 = coords[3]; 210 } 211 break; 212 case SunGraphics2D.TRANSFORM_ANY_TRANSLATE: 213 case SunGraphics2D.TRANSFORM_INT_TRANSLATE: 214 { 215 double tx = sg2d.transform.getTranslateX(); 216 double ty = sg2d.transform.getTranslateY(); 217 x1 = ux1 + tx; 218 y1 = uy1 + ty; 219 x2 = ux2 + tx; 220 y2 = uy2 + ty; 221 } 222 break; 223 case SunGraphics2D.TRANSFORM_ISIDENT: 224 x1 = ux1; 225 y1 = uy1; 226 x2 = ux2; 227 y2 = uy2; 228 break; 229 default: 230 throw new InternalError("unknown TRANSFORM state..."); 231 } 232 if (sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE) { 233 if (sg2d.strokeState == SunGraphics2D.STROKE_THIN && 234 outrenderer instanceof PixelDrawPipe) 235 { 236 // PixelDrawPipes will add sg2d.transXY so we need to factor 237 // that out... 238 int ix1 = (int) Math.floor(x1 - sg2d.transX); 239 int iy1 = (int) Math.floor(y1 - sg2d.transY); 240 int ix2 = (int) Math.floor(x2 - sg2d.transX); 241 int iy2 = (int) Math.floor(y2 - sg2d.transY); 242 ((PixelDrawPipe)outrenderer).drawLine(sg2d, ix1, iy1, ix2, iy2); 243 return true; 244 } 245 x1 = normalize(x1); 246 y1 = normalize(y1); 247 x2 = normalize(x2); 248 y2 = normalize(y2); 249 } 250 if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { 251 // Transform the linewidth... 252 // calculate the scaling factor for a unit vector 253 // perpendicular to the original user space line. 254 double len = len(dx, dy); 255 if (len == 0) { 256 dx = len = 1; 257 // dy = 0; already 258 } 259 // delta transform the transposed (90 degree rotated) unit vector 260 double unitvector[] = {dy/len, -dx/len}; 261 sg2d.transform.deltaTransform(unitvector, 0, unitvector, 0, 1); 262 lw *= len(unitvector[0], unitvector[1]); 263 } 264 lw = Math.max(lw, minPenSize); 265 dx = x2 - x1; 266 dy = y2 - y1; 267 double len = len(dx, dy); 268 double udx, udy; 269 if (len == 0) { 270 if (cap == BasicStroke.CAP_BUTT) { 271 return true; 272 } 273 udx = lw; 274 udy = 0; 275 } else { 276 udx = lw * dx / len; 277 udy = lw * dy / len; 278 } 279 double px = x1 + udy / 2.0; 280 double py = y1 - udx / 2.0; 281 if (cap == BasicStroke.CAP_SQUARE) { 282 px -= udx / 2.0; 283 py -= udy / 2.0; 284 dx += udx; 285 dy += udy; 286 } 287 outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2, 288 px, py, -udy, udx, dx, dy); 289 return true; 290 } 291 292 public void fillRectangle(SunGraphics2D sg2d, 293 double rx, double ry, 294 double rw, double rh) 295 { 296 double px, py; 297 double dx1, dy1, dx2, dy2; 298 AffineTransform txform = sg2d.transform; 299 dx1 = txform.getScaleX(); 300 dy1 = txform.getShearY(); 301 dx2 = txform.getShearX(); 302 dy2 = txform.getScaleY(); 303 px = rx * dx1 + ry * dx2 + txform.getTranslateX(); 304 py = rx * dy1 + ry * dy2 + txform.getTranslateY(); 305 dx1 *= rw; 306 dy1 *= rw; 307 dx2 *= rh; 308 dy2 *= rh; 309 if (adjustfill && 310 sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM && 311 sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE) 312 { 313 double newx = normalize(px); 314 double newy = normalize(py); 315 dx1 = normalize(px + dx1) - newx; 316 dy1 = normalize(py + dy1) - newy; 317 dx2 = normalize(px + dx2) - newx; 318 dy2 = normalize(py + dy2) - newy; 319 px = newx; 320 py = newy; 321 } 322 outrenderer.fillParallelogram(sg2d, rx, ry, rx+rw, ry+rh, 323 px, py, dx1, dy1, dx2, dy2); 324 } 325 326 public void drawRectangle(SunGraphics2D sg2d, 327 double rx, double ry, 328 double rw, double rh, 329 double lw) 330 { 331 double px, py; 332 double dx1, dy1, dx2, dy2; 333 double lw1, lw2; 334 AffineTransform txform = sg2d.transform; 335 dx1 = txform.getScaleX(); 336 dy1 = txform.getShearY(); 337 dx2 = txform.getShearX(); 338 dy2 = txform.getScaleY(); 339 px = rx * dx1 + ry * dx2 + txform.getTranslateX(); 340 py = rx * dy1 + ry * dy2 + txform.getTranslateY(); 341 // lw along dx1,dy1 scale by transformed length of dx2,dy2 vectors 342 // and vice versa 343 lw1 = len(dx1, dy1) * lw; 344 lw2 = len(dx2, dy2) * lw; 345 dx1 *= rw; 346 dy1 *= rw; 347 dx2 *= rh; 348 dy2 *= rh; 349 if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM && 350 sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE) 351 { 352 double newx = normalize(px); 353 double newy = normalize(py); 354 dx1 = normalize(px + dx1) - newx; 355 dy1 = normalize(py + dy1) - newy; 356 dx2 = normalize(px + dx2) - newx; 357 dy2 = normalize(py + dy2) - newy; 358 px = newx; 359 py = newy; 360 } 361 lw1 = Math.max(lw1, minPenSize); 362 lw2 = Math.max(lw2, minPenSize); 363 double len1 = len(dx1, dy1); 364 double len2 = len(dx2, dy2); 365 if (lw1 >= len1 || lw2 >= len2) { 366 // The line widths are large enough to consume the 367 // entire hole in the middle of the parallelogram 368 // so we can just fill the outer parallelogram. 369 fillOuterParallelogram(sg2d, 370 rx, ry, rx+rw, ry+rh, 371 px, py, dx1, dy1, dx2, dy2, 372 len1, len2, lw1, lw2); 373 } else { 374 outrenderer.drawParallelogram(sg2d, 375 rx, ry, rx+rw, ry+rh, 376 px, py, dx1, dy1, dx2, dy2, 377 lw1 / len1, lw2 / len2); 378 } 379 } 380 381 /** 382 * This utility function handles the case where a drawRectangle 383 * operation discovered that the interior hole in the rectangle 384 * or parallelogram has been completely filled in by the stroke 385 * width. It calculates the outer parallelogram of the stroke 386 * and issues a single fillParallelogram request to fill it. 387 */ 388 public void fillOuterParallelogram(SunGraphics2D sg2d, 389 double ux1, double uy1, 390 double ux2, double uy2, 391 double px, double py, 392 double dx1, double dy1, 393 double dx2, double dy2, 394 double len1, double len2, 395 double lw1, double lw2) 396 { 397 double udx1 = dx1 / len1; 398 double udy1 = dy1 / len1; 399 double udx2 = dx2 / len2; 400 double udy2 = dy2 / len2; 401 if (len1 == 0) { 402 // len1 is 0, replace udxy1 with perpendicular of udxy2 403 if (len2 == 0) { 404 // both are 0, use a unit Y vector for udxy2 405 udx2 = 0; 406 udy2 = 1; 407 } 408 udx1 = udy2; 409 udy1 = -udx2; 410 } else if (len2 == 0) { 411 // len2 is 0, replace udxy2 with perpendicular of udxy1 412 udx2 = udy1; 413 udy2 = -udx1; 414 } 415 udx1 *= lw1; 416 udy1 *= lw1; 417 udx2 *= lw2; 418 udy2 *= lw2; 419 px -= (udx1 + udx2) / 2; 420 py -= (udy1 + udy2) / 2; 421 dx1 += udx1; 422 dy1 += udy1; 423 dx2 += udx2; 424 dy2 += udy2; 425 426 outrenderer.fillParallelogram(sg2d, ux1, uy1, ux2, uy2, 427 px, py, dx1, dy1, dx2, dy2); 428 } 429 }