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