1 /* 2 * Copyright (c) 2005, 2016, 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.AlphaComposite; 29 import java.awt.Color; 30 import java.awt.Composite; 31 import java.awt.Paint; 32 import java.awt.geom.AffineTransform; 33 import sun.java2d.pipe.hw.AccelSurface; 34 import sun.java2d.InvalidPipeException; 35 import sun.java2d.SunGraphics2D; 36 import sun.java2d.loops.XORComposite; 37 import static sun.java2d.pipe.BufferedOpCodes.*; 38 import static sun.java2d.pipe.BufferedRenderPipe.BYTES_PER_SPAN; 39 40 import java.lang.annotation.Native; 41 import java.lang.ref.Reference; 42 import java.lang.ref.WeakReference; 43 44 /** 45 * Base context class for managing state in a single-threaded rendering 46 * environment. Each state-setting operation (e.g. SET_COLOR) is added to 47 * the provided RenderQueue, which will be processed at a later time by a 48 * single thread. Note that the RenderQueue lock must be acquired before 49 * calling the validate() method (or any other method in this class). See 50 * the RenderQueue class comments for a sample usage scenario. 51 * 52 * @see RenderQueue 53 */ 54 public abstract class BufferedContext { 55 56 /* 57 * The following flags help the internals of validate() determine 58 * the appropriate (meaning correct, or optimal) code path when 59 * setting up the current context. The flags can be bitwise OR'd 60 * together as needed. 61 */ 62 63 /** 64 * Indicates that no flags are needed; take all default code paths. 65 */ 66 @Native public static final int NO_CONTEXT_FLAGS = (0 << 0); 67 /** 68 * Indicates that the source surface (or color value, if it is a simple 69 * rendering operation) is opaque (has an alpha value of 1.0). If this 70 * flag is present, it allows us to disable blending in certain 71 * situations in order to improve performance. 72 */ 73 @Native public static final int SRC_IS_OPAQUE = (1 << 0); 74 /** 75 * Indicates that the operation uses an alpha mask, which may determine 76 * the code path that is used when setting up the current paint state. 77 */ 78 @Native public static final int USE_MASK = (1 << 1); 79 80 protected RenderQueue rq; 81 protected RenderBuffer buf; 82 83 /** 84 * This is a reference to the most recently validated BufferedContext. If 85 * this value is null, it means that there is no current context. It is 86 * provided here so that validate() only needs to do a quick reference 87 * check to see if the BufferedContext passed to that method is the same 88 * as the one we've cached here. 89 */ 90 protected static BufferedContext currentContext; 91 92 private Reference<AccelSurface> validSrcDataRef = new WeakReference<>(null); 93 private Reference<AccelSurface> validDstDataRef = new WeakReference<>(null); 94 private Reference<Region> validClipRef = new WeakReference<>(null); 95 private Reference<Composite> validCompRef = new WeakReference<>(null); 96 private Reference<Paint> validPaintRef = new WeakReference<>(null); 97 // renamed from isValidatedPaintAColor as part of a work around for 6764257 98 private boolean isValidatedPaintJustAColor; 99 private int validatedRGB; 100 private int validatedFlags; 101 private boolean xformInUse; 102 private AffineTransform transform; 103 BufferedContext(RenderQueue rq)104 protected BufferedContext(RenderQueue rq) { 105 this.rq = rq; 106 this.buf = rq.getBuffer(); 107 } 108 109 /** 110 * Fetches the BufferedContextContext associated with the dst. surface 111 * and validates the context using the given parameters. Most rendering 112 * operations will call this method first in order to set the necessary 113 * state before issuing rendering commands. 114 * 115 * Note: must be called while the RenderQueue lock is held. 116 * 117 * It's assumed that the type of surfaces has been checked by the Renderer 118 * 119 * @throws InvalidPipeException if either src or dest surface is not valid 120 * or lost 121 * @see RenderQueue#lock 122 * @see RenderQueue#unlock 123 */ validateContext(AccelSurface srcData, AccelSurface dstData, Region clip, Composite comp, AffineTransform xform, Paint paint, SunGraphics2D sg2d, int flags)124 public static void validateContext(AccelSurface srcData, 125 AccelSurface dstData, 126 Region clip, Composite comp, 127 AffineTransform xform, 128 Paint paint, SunGraphics2D sg2d, 129 int flags) 130 { 131 // assert rq.lock.isHeldByCurrentThread(); 132 BufferedContext context = dstData.getContext(); 133 context.validate(srcData, dstData, 134 clip, comp, xform, paint, sg2d, flags); 135 } 136 137 /** 138 * Fetches the BufferedContextassociated with the surface 139 * and disables all context state settings. 140 * 141 * Note: must be called while the RenderQueue lock is held. 142 * 143 * It's assumed that the type of surfaces has been checked by the Renderer 144 * 145 * @throws InvalidPipeException if the surface is not valid 146 * or lost 147 * @see RenderQueue#lock 148 * @see RenderQueue#unlock 149 */ validateContext(AccelSurface surface)150 public static void validateContext(AccelSurface surface) { 151 // assert rt.lock.isHeldByCurrentThread(); 152 validateContext(surface, surface, 153 null, null, null, null, null, NO_CONTEXT_FLAGS); 154 } 155 156 /** 157 * Validates the given parameters against the current state for this 158 * context. If this context is not current, it will be made current 159 * for the given source and destination surfaces, and the viewport will 160 * be updated. Then each part of the context state (clip, composite, 161 * etc.) is checked against the previous value. If the value has changed 162 * since the last call to validate(), it will be updated accordingly. 163 * 164 * Note that the SunGraphics2D parameter is only used for the purposes 165 * of validating a (non-null) Paint parameter. In all other cases it 166 * is safe to pass a null SunGraphics2D and it will be ignored. 167 * 168 * Note: must be called while the RenderQueue lock is held. 169 * 170 * It's assumed that the type of surfaces has been checked by the Renderer 171 * 172 * @throws InvalidPipeException if either src or dest surface is not valid 173 * or lost 174 */ validate(AccelSurface srcData, AccelSurface dstData, Region clip, Composite comp, AffineTransform xform, Paint paint, SunGraphics2D sg2d, int flags)175 public void validate(AccelSurface srcData, AccelSurface dstData, 176 Region clip, Composite comp, 177 AffineTransform xform, 178 Paint paint, SunGraphics2D sg2d, int flags) 179 { 180 // assert rq.lock.isHeldByCurrentThread(); 181 182 boolean updateClip = false; 183 boolean updatePaint = false; 184 185 if (!dstData.isValid() || 186 dstData.isSurfaceLost() || srcData.isSurfaceLost()) 187 { 188 invalidateContext(); 189 throw new InvalidPipeException("bounds changed or surface lost"); 190 } 191 192 if (paint instanceof Color) { 193 // REMIND: not 30-bit friendly 194 int newRGB = ((Color)paint).getRGB(); 195 if (isValidatedPaintJustAColor) { 196 if (newRGB != validatedRGB) { 197 validatedRGB = newRGB; 198 updatePaint = true; 199 } 200 } else { 201 validatedRGB = newRGB; 202 updatePaint = true; 203 isValidatedPaintJustAColor = true; 204 } 205 } else if (validPaintRef.get() != paint) { 206 updatePaint = true; 207 // this should be set when we are switching from paint to color 208 // in which case this condition will be true 209 isValidatedPaintJustAColor = false; 210 } 211 212 final AccelSurface validatedSrcData = validSrcDataRef.get(); 213 final AccelSurface validatedDstData = validDstDataRef.get(); 214 if ((currentContext != this) || 215 (srcData != validatedSrcData) || 216 (dstData != validatedDstData)) 217 { 218 if (dstData != validatedDstData) { 219 // the clip is dependent on the destination surface, so we 220 // need to update it if we have a new destination surface 221 updateClip = true; 222 } 223 224 if (paint == null) { 225 // make sure we update the color state (otherwise, it might 226 // not be updated if this is the first time the context 227 // is being validated) 228 updatePaint = true; 229 } 230 231 // update the current source and destination surfaces 232 setSurfaces(srcData, dstData); 233 234 currentContext = this; 235 validSrcDataRef = new WeakReference<>(srcData); 236 validDstDataRef = new WeakReference<>(dstData); 237 } 238 239 // validate clip 240 final Region validatedClip = validClipRef.get(); 241 if ((clip != validatedClip) || updateClip) { 242 if (clip != null) { 243 if (updateClip || 244 validatedClip == null || 245 !(validatedClip.isRectangular() && clip.isRectangular()) || 246 ((clip.getLoX() != validatedClip.getLoX() || 247 clip.getLoY() != validatedClip.getLoY() || 248 clip.getHiX() != validatedClip.getHiX() || 249 clip.getHiY() != validatedClip.getHiY()))) 250 { 251 setClip(clip); 252 } 253 } else { 254 resetClip(); 255 } 256 validClipRef = new WeakReference<>(clip); 257 } 258 259 // validate composite (note that a change in the context flags 260 // may require us to update the composite state, even if the 261 // composite has not changed) 262 if ((comp != validCompRef.get()) || (flags != validatedFlags)) { 263 if (comp != null) { 264 setComposite(comp, flags); 265 } else { 266 resetComposite(); 267 } 268 // the paint state is dependent on the composite state, so make 269 // sure we update the color below 270 updatePaint = true; 271 validCompRef = new WeakReference<>(comp); 272 validatedFlags = flags; 273 } 274 275 // validate transform 276 boolean txChanged = false; 277 if (xform == null) { 278 if (xformInUse) { 279 resetTransform(); 280 xformInUse = false; 281 txChanged = true; 282 } else if (sg2d != null && !sg2d.transform.equals(transform)) { 283 txChanged = true; 284 } 285 if (sg2d != null && txChanged) { 286 transform = new AffineTransform(sg2d.transform); 287 } 288 } else { 289 setTransform(xform); 290 xformInUse = true; 291 txChanged = true; 292 } 293 // non-Color paints may require paint revalidation 294 if (!isValidatedPaintJustAColor && txChanged) { 295 updatePaint = true; 296 } 297 298 // validate paint 299 if (updatePaint) { 300 if (paint != null) { 301 BufferedPaints.setPaint(rq, sg2d, paint, flags); 302 } else { 303 BufferedPaints.resetPaint(rq); 304 } 305 validPaintRef = new WeakReference<>(paint); 306 } 307 308 // mark dstData dirty 309 // REMIND: is this really needed now? we do it in SunGraphics2D.. 310 dstData.markDirty(); 311 } 312 313 /** 314 * Invalidates the surfaces associated with this context. This is 315 * useful when the context is no longer needed, and we want to break 316 * the chain caused by these surface references. 317 * 318 * Note: must be called while the RenderQueue lock is held. 319 * 320 * @see RenderQueue#lock 321 * @see RenderQueue#unlock 322 */ invalidateSurfaces()323 private void invalidateSurfaces() { 324 validSrcDataRef.clear(); 325 validDstDataRef.clear(); 326 } 327 setSurfaces(AccelSurface srcData, AccelSurface dstData)328 private void setSurfaces(AccelSurface srcData, 329 AccelSurface dstData) 330 { 331 // assert rq.lock.isHeldByCurrentThread(); 332 rq.ensureCapacityAndAlignment(20, 4); 333 buf.putInt(SET_SURFACES); 334 buf.putLong(srcData.getNativeOps()); 335 buf.putLong(dstData.getNativeOps()); 336 } 337 resetClip()338 private void resetClip() { 339 // assert rq.lock.isHeldByCurrentThread(); 340 rq.ensureCapacity(4); 341 buf.putInt(RESET_CLIP); 342 } 343 setClip(Region clip)344 private void setClip(Region clip) { 345 // assert rq.lock.isHeldByCurrentThread(); 346 if (clip.isRectangular()) { 347 rq.ensureCapacity(20); 348 buf.putInt(SET_RECT_CLIP); 349 buf.putInt(clip.getLoX()).putInt(clip.getLoY()); 350 buf.putInt(clip.getHiX()).putInt(clip.getHiY()); 351 } else { 352 rq.ensureCapacity(28); // so that we have room for at least a span 353 buf.putInt(BEGIN_SHAPE_CLIP); 354 buf.putInt(SET_SHAPE_CLIP_SPANS); 355 // include a placeholder for the span count 356 int countIndex = buf.position(); 357 buf.putInt(0); 358 int spanCount = 0; 359 int remainingSpans = buf.remaining() / BYTES_PER_SPAN; 360 int span[] = new int[4]; 361 SpanIterator si = clip.getSpanIterator(); 362 while (si.nextSpan(span)) { 363 if (remainingSpans == 0) { 364 buf.putInt(countIndex, spanCount); 365 rq.flushNow(); 366 buf.putInt(SET_SHAPE_CLIP_SPANS); 367 countIndex = buf.position(); 368 buf.putInt(0); 369 spanCount = 0; 370 remainingSpans = buf.remaining() / BYTES_PER_SPAN; 371 } 372 buf.putInt(span[0]); // x1 373 buf.putInt(span[1]); // y1 374 buf.putInt(span[2]); // x2 375 buf.putInt(span[3]); // y2 376 spanCount++; 377 remainingSpans--; 378 } 379 buf.putInt(countIndex, spanCount); 380 rq.ensureCapacity(4); 381 buf.putInt(END_SHAPE_CLIP); 382 } 383 } 384 resetComposite()385 private void resetComposite() { 386 // assert rq.lock.isHeldByCurrentThread(); 387 rq.ensureCapacity(4); 388 buf.putInt(RESET_COMPOSITE); 389 } 390 setComposite(Composite comp, int flags)391 private void setComposite(Composite comp, int flags) { 392 // assert rq.lock.isHeldByCurrentThread(); 393 if (comp instanceof AlphaComposite) { 394 AlphaComposite ac = (AlphaComposite)comp; 395 rq.ensureCapacity(16); 396 buf.putInt(SET_ALPHA_COMPOSITE); 397 buf.putInt(ac.getRule()); 398 buf.putFloat(ac.getAlpha()); 399 buf.putInt(flags); 400 } else if (comp instanceof XORComposite) { 401 int xorPixel = ((XORComposite)comp).getXorPixel(); 402 rq.ensureCapacity(8); 403 buf.putInt(SET_XOR_COMPOSITE); 404 buf.putInt(xorPixel); 405 } else { 406 throw new InternalError("not yet implemented"); 407 } 408 } 409 resetTransform()410 private void resetTransform() { 411 // assert rq.lock.isHeldByCurrentThread(); 412 rq.ensureCapacity(4); 413 buf.putInt(RESET_TRANSFORM); 414 } 415 setTransform(AffineTransform xform)416 private void setTransform(AffineTransform xform) { 417 // assert rq.lock.isHeldByCurrentThread(); 418 rq.ensureCapacityAndAlignment(52, 4); 419 buf.putInt(SET_TRANSFORM); 420 buf.putDouble(xform.getScaleX()); 421 buf.putDouble(xform.getShearY()); 422 buf.putDouble(xform.getShearX()); 423 buf.putDouble(xform.getScaleY()); 424 buf.putDouble(xform.getTranslateX()); 425 buf.putDouble(xform.getTranslateY()); 426 } 427 428 /** 429 * Resets this context's surfaces and all attributes. 430 * 431 * Note: must be called while the RenderQueue lock is held. 432 * 433 * @see RenderQueue#lock 434 * @see RenderQueue#unlock 435 */ invalidateContext()436 public void invalidateContext() { 437 resetTransform(); 438 resetComposite(); 439 resetClip(); 440 BufferedPaints.resetPaint(rq); 441 invalidateSurfaces(); 442 validCompRef.clear(); 443 validClipRef.clear(); 444 validPaintRef.clear(); 445 isValidatedPaintJustAColor = false; 446 xformInUse = false; 447 } 448 449 /** 450 * Returns a singleton {@code RenderQueue} object used by the rendering 451 * pipeline. 452 * 453 * @return a render queue 454 * @see RenderQueue 455 */ getRenderQueue()456 public abstract RenderQueue getRenderQueue(); 457 458 /** 459 * Saves the state of this context. 460 * It may reset the current context. 461 * 462 * Note: must be called while the RenderQueue lock is held. 463 * 464 * @see RenderQueue#lock 465 * @see RenderQueue#unlock 466 */ saveState()467 public abstract void saveState(); 468 469 /** 470 * Restores the native state of this context. 471 * It may reset the current context. 472 * 473 * Note: must be called while the RenderQueue lock is held. 474 * 475 * @see RenderQueue#lock 476 * @see RenderQueue#unlock 477 */ restoreState()478 public abstract void restoreState(); 479 } 480