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