1 /* 2 * Copyright (c) 2010, 2018, 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 MaskTileManager tileManager; 57 XRDrawLine lineGen; 58 GrowableRectArray rectBuffer; 59 XRRenderer(MaskTileManager tileManager)60 public XRRenderer(MaskTileManager 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 */ validateSurface(SunGraphics2D sg2d)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 drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2)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 drawRect(SunGraphics2D sg2d, int x, int y, int width, int height)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 drawPolyline(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints)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 drawPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints)121 public void drawPolygon(SunGraphics2D sg2d, 122 int[] xpoints, int[] ypoints, int npoints) { 123 draw(sg2d, new Polygon(xpoints, ypoints, npoints)); 124 } 125 fillRect(SunGraphics2D sg2d, int x, int y, int width, int height)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 fillPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints)165 public void fillPolygon(SunGraphics2D sg2d, 166 int[] xpoints, int[] ypoints, int npoints) { 167 fill(sg2d, new Polygon(xpoints, ypoints, npoints)); 168 } 169 drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight)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 fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight)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 drawOval(SunGraphics2D sg2d, int x, int y, int width, int height)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 fillOval(SunGraphics2D sg2d, int x, int y, int width, int height)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 drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle)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 fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle)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 XRDrawHandler()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 */ validate(SunGraphics2D sg2d)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 drawLine(int x1, int y1, int x2, int y2)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 drawPixel(int x, int y)250 public void drawPixel(int x, int y) { 251 rectBuffer.pushRectValues(x, y, 1, 1); 252 } 253 drawScanline(int x1, int x2, int y)254 public void drawScanline(int x1, int x2, int y) { 255 rectBuffer.pushRectValues(x1, y, x2 - x1 + 1, 1); 256 } 257 } 258 drawPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy)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 fillPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy)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 fillSpans(SunGraphics2D sg2d, SpanIterator si, int transx, int transy)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 draw(SunGraphics2D sg2d, Shape s)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 fill(SunGraphics2D sg2d, Shape s)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 } 385