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