1 /* 2 * Copyright (c) 2010, 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.xr; 27 28 import java.awt.*; 29 import java.awt.geom.*; 30 import sun.awt.SunToolkit; 31 import sun.java2d.SunGraphics2D; 32 import sun.java2d.loops.*; 33 import sun.java2d.pipe.Region; 34 import sun.java2d.pipe.PixelDrawPipe; 35 import sun.java2d.pipe.PixelFillPipe; 36 import sun.java2d.pipe.ShapeDrawPipe; 37 import sun.java2d.pipe.SpanIterator; 38 import sun.java2d.pipe.ShapeSpanIterator; 39 import sun.java2d.pipe.LoopPipe; 40 41 import static sun.java2d.xr.XRUtils.clampToShort; 42 import static sun.java2d.xr.XRUtils.clampToUShort; 43 44 /** 45 * XRender provides only accalerated rectangles. To emulate higher "order" 46 * geometry we have to pass everything else to DoPath/FillSpans. 47 * 48 * TODO: DrawRect could be instrified 49 * 50 * @author Clemens Eisserer 51 */ 52 53 public class XRRenderer implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe { 54 XRDrawHandler drawHandler; 55 MaskTileManager tileManager; 56 XRDrawLine lineGen; 57 GrowableRectArray rectBuffer; 58 59 public XRRenderer(MaskTileManager tileManager) { 60 this.tileManager = tileManager; 61 this.rectBuffer = tileManager.getMainTile().getRects(); 62 63 this.drawHandler = new XRDrawHandler(); 64 this.lineGen = new XRDrawLine(); 65 } 66 67 /** 68 * Common validate method, used by all XRRender functions to validate the 69 * destination context. 70 */ 71 private final void validateSurface(SunGraphics2D sg2d) { 72 XRSurfaceData xrsd = (XRSurfaceData) sg2d.surfaceData; 73 xrsd.validateAsDestination(sg2d, sg2d.getCompClip()); 74 xrsd.maskBuffer.validateCompositeState(sg2d.composite, sg2d.transform, 75 sg2d.paint, sg2d); 76 } 77 78 public void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) { 79 Region compClip = sg2d.getCompClip(); 80 int transX1 = Region.clipAdd(x1, sg2d.transX); 81 int transY1 = Region.clipAdd(y1, sg2d.transY); 82 int transX2 = Region.clipAdd(x2, sg2d.transX); 83 int transY2 = Region.clipAdd(y2, sg2d.transY); 84 85 SunToolkit.awtLock(); 86 try { 87 validateSurface(sg2d); 88 lineGen.rasterizeLine(rectBuffer, transX1, transY1, 89 transX2, transY2, compClip.getLoX(), compClip.getLoY(), 90 compClip.getHiX(), compClip.getHiY(), true, true); 91 tileManager.fillMask((XRSurfaceData) sg2d.surfaceData); 92 } finally { 93 SunToolkit.awtUnlock(); 94 } 95 } 96 97 public void drawRect(SunGraphics2D sg2d, 98 int x, int y, int width, int height) { 99 draw(sg2d, new Rectangle2D.Float(x, y, width, height)); 100 } 101 102 public void drawPolyline(SunGraphics2D sg2d, 103 int xpoints[], int ypoints[], int npoints) { 104 Path2D.Float p2d = new Path2D.Float(); 105 if (npoints > 1) { 106 p2d.moveTo(xpoints[0], ypoints[0]); 107 for (int i = 1; i < npoints; i++) { 108 p2d.lineTo(xpoints[i], ypoints[i]); 109 } 110 } 111 112 draw(sg2d, p2d); 113 } 114 115 public void drawPolygon(SunGraphics2D sg2d, 116 int xpoints[], int ypoints[], int npoints) { 117 draw(sg2d, new Polygon(xpoints, ypoints, npoints)); 118 } 119 120 public void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) { 121 x = Region.clipAdd(x, sg2d.transX); 122 y = Region.clipAdd(y, sg2d.transY); 123 124 /* 125 * Limit x/y to signed short, width/height to unsigned short, 126 * to match the X11 coordinate limits for rectangles. 127 * Correct width/height in case x/y have been modified by clipping. 128 */ 129 if (x > Short.MAX_VALUE || y > Short.MAX_VALUE) { 130 return; 131 } 132 133 int x2 = Region.dimAdd(x, width); 134 int y2 = Region.dimAdd(y, height); 135 136 if (x2 < Short.MIN_VALUE || y2 < Short.MIN_VALUE) { 137 return; 138 } 139 140 x = clampToShort(x); 141 y = clampToShort(y); 142 width = clampToUShort(x2 - x); 143 height = clampToUShort(y2 - y); 144 145 if (width == 0 || height == 0) { 146 return; 147 } 148 149 SunToolkit.awtLock(); 150 try { 151 validateSurface(sg2d); 152 rectBuffer.pushRectValues(x, y, width, height); 153 tileManager.fillMask((XRSurfaceData) sg2d.surfaceData); 154 } finally { 155 SunToolkit.awtUnlock(); 156 } 157 } 158 159 public void fillPolygon(SunGraphics2D sg2d, 160 int xpoints[], int ypoints[], int npoints) { 161 fill(sg2d, new Polygon(xpoints, ypoints, npoints)); 162 } 163 164 public void drawRoundRect(SunGraphics2D sg2d, 165 int x, int y, int width, int height, 166 int arcWidth, int arcHeight) { 167 draw(sg2d, new RoundRectangle2D.Float(x, y, width, height, 168 arcWidth, arcHeight)); 169 } 170 171 public void fillRoundRect(SunGraphics2D sg2d, int x, int y, 172 int width, int height, 173 int arcWidth, int arcHeight) { 174 fill(sg2d, new RoundRectangle2D.Float(x, y, width, height, 175 arcWidth, arcHeight)); 176 } 177 178 public void drawOval(SunGraphics2D sg2d, 179 int x, int y, int width, int height) { 180 draw(sg2d, new Ellipse2D.Float(x, y, width, height)); 181 } 182 183 public void fillOval(SunGraphics2D sg2d, 184 int x, int y, int width, int height) { 185 fill(sg2d, new Ellipse2D.Float(x, y, width, height)); 186 } 187 188 public void drawArc(SunGraphics2D sg2d, 189 int x, int y, int width, int height, 190 int startAngle, int arcAngle) { 191 draw(sg2d, new Arc2D.Float(x, y, width, height, 192 startAngle, arcAngle, Arc2D.OPEN)); 193 } 194 195 public void fillArc(SunGraphics2D sg2d, 196 int x, int y, int width, int height, 197 int startAngle, int arcAngle) { 198 fill(sg2d, new Arc2D.Float(x, y, width, height, 199 startAngle, arcAngle, Arc2D.PIE)); 200 } 201 202 private class XRDrawHandler extends ProcessPath.DrawHandler { 203 DirtyRegion region; 204 205 XRDrawHandler() { 206 // these are bogus values; the caller will use validate() 207 // to ensure that they are set properly prior to each usage 208 super(0, 0, 0, 0); 209 this.region = new DirtyRegion(); 210 } 211 212 /** 213 * This method needs to be called prior to each draw/fillPath() 214 * operation to ensure the clip bounds are up to date. 215 */ 216 void validate(SunGraphics2D sg2d) { 217 Region clip = sg2d.getCompClip(); 218 setBounds(clip.getLoX(), clip.getLoY(), 219 clip.getHiX(), clip.getHiY(), sg2d.strokeHint); 220 validateSurface(sg2d); 221 } 222 223 public void drawLine(int x1, int y1, int x2, int y2) { 224 region.setDirtyLineRegion(x1, y1, x2, y2); 225 int xDiff = region.x2 - region.x; 226 int yDiff = region.y2 - region.y; 227 228 if (xDiff == 0 || yDiff == 0) { 229 // horizontal / diagonal lines can be represented by a single 230 // rectangle 231 rectBuffer.pushRectValues(region.x, region.y, region.x2 - region.x 232 + 1, region.y2 - region.y + 1); 233 } else if (xDiff == 1 && yDiff == 1) { 234 // fast path for pattern commonly generated by 235 // ProcessPath.DrawHandler 236 rectBuffer.pushRectValues(x1, y1, 1, 1); 237 rectBuffer.pushRectValues(x2, y2, 1, 1); 238 } else { 239 lineGen.rasterizeLine(rectBuffer, x1, y1, x2, y2, 0, 0, 240 0, 0, false, false); 241 } 242 } 243 244 public void drawPixel(int x, int y) { 245 rectBuffer.pushRectValues(x, y, 1, 1); 246 } 247 248 public void drawScanline(int x1, int x2, int y) { 249 rectBuffer.pushRectValues(x1, y, x2 - x1 + 1, 1); 250 } 251 } 252 253 protected void drawPath(SunGraphics2D sg2d, Path2D.Float p2df, 254 int transx, int transy) { 255 SunToolkit.awtLock(); 256 try { 257 validateSurface(sg2d); 258 drawHandler.validate(sg2d); 259 ProcessPath.drawPath(drawHandler, p2df, transx, transy); 260 tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData)); 261 } finally { 262 SunToolkit.awtUnlock(); 263 } 264 } 265 266 protected void fillPath(SunGraphics2D sg2d, Path2D.Float p2df, 267 int transx, int transy) { 268 SunToolkit.awtLock(); 269 try { 270 validateSurface(sg2d); 271 drawHandler.validate(sg2d); 272 ProcessPath.fillPath(drawHandler, p2df, transx, transy); 273 tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData)); 274 } finally { 275 SunToolkit.awtUnlock(); 276 } 277 } 278 279 protected void fillSpans(SunGraphics2D sg2d, SpanIterator si, 280 int transx, int transy) { 281 SunToolkit.awtLock(); 282 try { 283 validateSurface(sg2d); 284 int[] spanBox = new int[4]; 285 while (si.nextSpan(spanBox)) { 286 rectBuffer.pushRectValues(spanBox[0] + transx, 287 spanBox[1] + transy, 288 spanBox[2] - spanBox[0], 289 spanBox[3] - spanBox[1]); 290 } 291 tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData)); 292 } finally { 293 SunToolkit.awtUnlock(); 294 } 295 } 296 297 public void draw(SunGraphics2D sg2d, Shape s) { 298 if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) { 299 Path2D.Float p2df; 300 int transx, transy; 301 if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) { 302 if (s instanceof Path2D.Float) { 303 p2df = (Path2D.Float) s; 304 } else { 305 p2df = new Path2D.Float(s); 306 } 307 transx = sg2d.transX; 308 transy = sg2d.transY; 309 } else { 310 p2df = new Path2D.Float(s, sg2d.transform); 311 transx = 0; 312 transy = 0; 313 } 314 drawPath(sg2d, p2df, transx, transy); 315 } else if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) { 316 ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s); 317 try { 318 fillSpans(sg2d, si, 0, 0); 319 } finally { 320 si.dispose(); 321 } 322 } else { 323 fill(sg2d, sg2d.stroke.createStrokedShape(s)); 324 } 325 } 326 327 public void fill(SunGraphics2D sg2d, Shape s) { 328 int transx, transy; 329 330 if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) { 331 // Here we are able to use fillPath() for 332 // high-quality fills. 333 Path2D.Float p2df; 334 if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) { 335 if (s instanceof Path2D.Float) { 336 p2df = (Path2D.Float) s; 337 } else { 338 p2df = new Path2D.Float(s); 339 } 340 transx = sg2d.transX; 341 transy = sg2d.transY; 342 } else { 343 p2df = new Path2D.Float(s, sg2d.transform); 344 transx = 0; 345 transy = 0; 346 } 347 fillPath(sg2d, p2df, transx, transy); 348 return; 349 } 350 351 AffineTransform at; 352 if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) { 353 // Transform (translation) will be done by FillSpans 354 at = null; 355 transx = sg2d.transX; 356 transy = sg2d.transY; 357 } else { 358 // Transform will be done by the PathIterator 359 at = sg2d.transform; 360 transx = transy = 0; 361 } 362 363 ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d); 364 try { 365 // Subtract transx/y from the SSI clip to match the 366 // (potentially untranslated) geometry fed to it 367 Region clip = sg2d.getCompClip(); 368 ssi.setOutputAreaXYXY(clip.getLoX() - transx, 369 clip.getLoY() - transy, 370 clip.getHiX() - transx, 371 clip.getHiY() - transy); 372 ssi.appendPath(s.getPathIterator(at)); 373 fillSpans(sg2d, ssi, transx, transy); 374 } finally { 375 ssi.dispose(); 376 } 377 } 378 }