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