1 /*
2  * Copyright (c) 1997, 2015, 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 /*
27  * @author Charlton Innovations, Inc.
28  */
29 
30 package sun.java2d.loops;
31 
32 import java.awt.image.BufferedImage;
33 import java.awt.AlphaComposite;
34 import java.awt.Rectangle;
35 import sun.awt.image.BufImgSurfaceData;
36 import sun.awt.util.ThreadGroupUtils;
37 import sun.java2d.SurfaceData;
38 import sun.java2d.pipe.Region;
39 import java.lang.reflect.Field;
40 import java.util.StringTokenizer;
41 import java.util.Iterator;
42 import java.util.HashMap;
43 import java.util.Map;
44 import java.io.PrintStream;
45 import java.io.OutputStream;
46 import java.io.FileOutputStream;
47 import java.io.FileNotFoundException;
48 import java.security.AccessController;
49 import java.security.PrivilegedAction;
50 
51 import sun.security.action.GetPropertyAction;
52 
53 /**
54  * defines interface for primitives which can be placed into
55  * the graphic component manager framework
56  */
57 public abstract class GraphicsPrimitive {
58 
59     protected static interface GeneralBinaryOp {
60         /**
61          * This method allows the setupGeneralBinaryOp method to set
62          * the converters into the General version of the Primitive.
63          */
setPrimitives(Blit srcconverter, Blit dstconverter, GraphicsPrimitive genericop, Blit resconverter)64         public void setPrimitives(Blit srcconverter,
65                                   Blit dstconverter,
66                                   GraphicsPrimitive genericop,
67                                   Blit resconverter);
68 
69         /**
70          * These 4 methods are implemented automatically for any
71          * GraphicsPrimitive.  They are used by setupGeneralBinaryOp
72          * to retrieve the information needed to find the right
73          * converter primitives.
74          */
getSourceType()75         public SurfaceType getSourceType();
getCompositeType()76         public CompositeType getCompositeType();
getDestType()77         public SurfaceType getDestType();
getSignature()78         public String getSignature();
getPrimTypeID()79         public int getPrimTypeID();
80     }
81 
82     protected static interface GeneralUnaryOp {
83         /**
84          * This method allows the setupGeneralUnaryOp method to set
85          * the converters into the General version of the Primitive.
86          */
setPrimitives(Blit dstconverter, GraphicsPrimitive genericop, Blit resconverter)87         public void setPrimitives(Blit dstconverter,
88                                   GraphicsPrimitive genericop,
89                                   Blit resconverter);
90 
91         /**
92          * These 3 methods are implemented automatically for any
93          * GraphicsPrimitive.  They are used by setupGeneralUnaryOp
94          * to retrieve the information needed to find the right
95          * converter primitives.
96          */
getCompositeType()97         public CompositeType getCompositeType();
getDestType()98         public SurfaceType getDestType();
getSignature()99         public String getSignature();
getPrimTypeID()100         public int getPrimTypeID();
101     }
102 
103     /**
104     *  INSTANCE DATA MEMBERS DESCRIBING CHARACTERISTICS OF THIS PRIMITIVE
105     **/
106 
107     // Making these be instance data members (instead of virtual methods
108     // overridden by subclasses) is actually cheaper, since each class
109     // is a singleton.  As instance data members with final accessors,
110     // accesses can be inlined.
111     private String methodSignature;
112     private int uniqueID;
113     private static int unusedPrimID = 1;
114 
115     private SurfaceType sourceType;
116     private CompositeType compositeType;
117     private SurfaceType destType;
118 
119     private long pNativePrim;   // Native blit loop info
120 
makePrimTypeID()121     public static final synchronized int makePrimTypeID() {
122         if (unusedPrimID > 255) {
123             throw new InternalError("primitive id overflow");
124         }
125         return unusedPrimID++;
126     }
127 
makeUniqueID(int primTypeID, SurfaceType src, CompositeType cmp, SurfaceType dst)128     public static final synchronized int makeUniqueID(int primTypeID,
129                                                       SurfaceType src,
130                                                       CompositeType cmp,
131                                                       SurfaceType dst)
132     {
133         return (primTypeID << 24) |
134             (dst.getUniqueID() << 16) |
135             (cmp.getUniqueID() << 8)  |
136             (src.getUniqueID());
137     }
138 
139     /**
140      * Create a new GraphicsPrimitive with all of the required
141      * descriptive information.
142      */
GraphicsPrimitive(String methodSignature, int primTypeID, SurfaceType sourceType, CompositeType compositeType, SurfaceType destType)143     protected GraphicsPrimitive(String methodSignature,
144                                 int primTypeID,
145                                 SurfaceType sourceType,
146                                 CompositeType compositeType,
147                                 SurfaceType destType)
148     {
149         this.methodSignature = methodSignature;
150         this.sourceType = sourceType;
151         this.compositeType = compositeType;
152         this.destType = destType;
153 
154         if(sourceType == null || compositeType == null || destType == null) {
155             this.uniqueID = primTypeID << 24;
156         } else {
157             this.uniqueID = GraphicsPrimitive.makeUniqueID(primTypeID,
158                                                            sourceType,
159                                                            compositeType,
160                                                            destType);
161         }
162     }
163 
164     /**
165      * Create a new GraphicsPrimitive for native invocation
166      * with all of the required descriptive information.
167      */
GraphicsPrimitive(long pNativePrim, String methodSignature, int primTypeID, SurfaceType sourceType, CompositeType compositeType, SurfaceType destType)168     protected GraphicsPrimitive(long pNativePrim,
169                                 String methodSignature,
170                                 int primTypeID,
171                                 SurfaceType sourceType,
172                                 CompositeType compositeType,
173                                 SurfaceType destType)
174     {
175         this.pNativePrim = pNativePrim;
176         this.methodSignature = methodSignature;
177         this.sourceType = sourceType;
178         this.compositeType = compositeType;
179         this.destType = destType;
180 
181         if(sourceType == null || compositeType == null || destType == null) {
182             this.uniqueID = primTypeID << 24;
183         } else {
184             this.uniqueID = GraphicsPrimitive.makeUniqueID(primTypeID,
185                                                            sourceType,
186                                                            compositeType,
187                                                            destType);
188         }
189     }
190 
191     /**
192     *   METHODS TO DESCRIBE THE SURFACES PRIMITIVES
193     *   CAN OPERATE ON AND THE FUNCTIONALITY THEY IMPLEMENT
194     **/
195 
196     /**
197      * Gets instance ID of this graphics primitive.
198      *
199      * Instance ID is comprised of four distinct ids (ORed together)
200      * that uniquely identify each instance of a GraphicsPrimitive
201      * object. The four ids making up instance ID are:
202      * 1. primitive id - identifier shared by all primitives of the
203      * same type (eg. all Blits have the same primitive id)
204      * 2. sourcetype id - identifies source surface type
205      * 3. desttype id - identifies destination surface type
206      * 4. compositetype id - identifies composite used
207      *
208      * @return instance ID
209      */
getUniqueID()210     public final int getUniqueID() {
211         return uniqueID;
212     }
213 
214     /**
215      */
getSignature()216     public final String getSignature() {
217         return methodSignature;
218     }
219 
220     /**
221      * Gets unique id for this GraphicsPrimitive type.
222      *
223      * This id is used to identify the TYPE of primitive (Blit vs. BlitBg)
224      * as opposed to INSTANCE of primitive.
225      *
226      * @return primitive ID
227      */
getPrimTypeID()228     public final int getPrimTypeID() {
229         return uniqueID >>> 24;
230     }
231 
232     /**
233      */
getNativePrim()234     public final long getNativePrim() {
235         return pNativePrim;
236     }
237 
238     /**
239      */
getSourceType()240     public final SurfaceType getSourceType() {
241         return sourceType;
242     }
243 
244     /**
245      */
getCompositeType()246     public final CompositeType getCompositeType() {
247         return compositeType;
248     }
249 
250     /**
251      */
getDestType()252     public final SurfaceType getDestType() {
253         return destType;
254     }
255 
256     /**
257      * Return true if this primitive can be used for the given signature
258      * surfaces, and composite.
259      *
260      * @param signature The signature of the given operation.  Must be
261      *          == (not just .equals) the signature string given by the
262      *          abstract class that declares the operation.
263      * @param srctype The surface type for the source of the operation
264      * @param comptype The composite type for the operation
265      * @param dsttype The surface type for the destination of the operation
266      */
satisfies(String signature, SurfaceType srctype, CompositeType comptype, SurfaceType dsttype)267     public final boolean satisfies(String signature,
268                                    SurfaceType srctype,
269                                    CompositeType comptype,
270                                    SurfaceType dsttype)
271     {
272         if (signature != methodSignature) {
273             return false;
274         }
275         while (true) {
276             if (srctype == null) {
277                 return false;
278             }
279             if (srctype.equals(sourceType)) {
280                 break;
281             }
282             srctype = srctype.getSuperType();
283         }
284         while (true) {
285             if (comptype == null) {
286                 return false;
287             }
288             if (comptype.equals(compositeType)) {
289                 break;
290             }
291             comptype = comptype.getSuperType();
292         }
293         while (true) {
294             if (dsttype == null) {
295                 return false;
296             }
297             if (dsttype.equals(destType)) {
298                 break;
299             }
300             dsttype = dsttype.getSuperType();
301         }
302         return true;
303     }
304 
305     //
306     // A version of satisfies used for regression testing
307     //
satisfiesSameAs(GraphicsPrimitive other)308     final boolean satisfiesSameAs(GraphicsPrimitive other) {
309         return (methodSignature == other.methodSignature &&
310                 sourceType.equals(other.sourceType) &&
311                 compositeType.equals(other.compositeType) &&
312                 destType.equals(other.destType));
313     }
314 
makePrimitive(SurfaceType srctype, CompositeType comptype, SurfaceType dsttype)315     public abstract GraphicsPrimitive makePrimitive(SurfaceType srctype,
316                                                     CompositeType comptype,
317                                                     SurfaceType dsttype);
318 
traceWrap()319     public abstract GraphicsPrimitive traceWrap();
320 
321     static HashMap<Object, int[]> traceMap;
322 
323     public static int traceflags;
324     public static String tracefile;
325     public static PrintStream traceout;
326 
327     public static final int TRACELOG = 1;
328     public static final int TRACETIMESTAMP = 2;
329     public static final int TRACECOUNTS = 4;
330 
331     static {
332         GetPropertyAction gpa = new GetPropertyAction("sun.java2d.trace");
333         String trace = AccessController.doPrivileged(gpa);
334         if (trace != null) {
335             boolean verbose = false;
336             int traceflags = 0;
337             StringTokenizer st = new StringTokenizer(trace, ",");
338             while (st.hasMoreTokens()) {
339                 String tok = st.nextToken();
340                 if (tok.equalsIgnoreCase("count")) {
341                     traceflags |= GraphicsPrimitive.TRACECOUNTS;
342                 } else if (tok.equalsIgnoreCase("log")) {
343                     traceflags |= GraphicsPrimitive.TRACELOG;
344                 } else if (tok.equalsIgnoreCase("timestamp")) {
345                     traceflags |= GraphicsPrimitive.TRACETIMESTAMP;
346                 } else if (tok.equalsIgnoreCase("verbose")) {
347                     verbose = true;
348                 } else if (tok.regionMatches(true, 0, "out:", 0, 4)) {
349                     tracefile = tok.substring(4);
350                 } else {
351                     if (!tok.equalsIgnoreCase("help")) {
352                         System.err.println("unrecognized token: "+tok);
353                     }
354                     System.err.println("usage: -Dsun.java2d.trace="+
355                                        "[log[,timestamp]],[count],"+
356                                        "[out:<filename>],[help],[verbose]");
357                 }
358             }
359             if (verbose) {
360                 System.err.print("GraphicsPrimitive logging ");
361                 if ((traceflags & GraphicsPrimitive.TRACELOG) != 0) {
362                     System.err.println("enabled");
363                     System.err.print("GraphicsPrimitive timetamps ");
364                     if ((traceflags & GraphicsPrimitive.TRACETIMESTAMP) != 0) {
365                         System.err.println("enabled");
366                     } else {
367                         System.err.println("disabled");
368                     }
369                 } else {
370                     System.err.println("[and timestamps] disabled");
371                 }
372                 System.err.print("GraphicsPrimitive invocation counts ");
373                 if ((traceflags & GraphicsPrimitive.TRACECOUNTS) != 0) {
374                     System.err.println("enabled");
375                 } else {
376                     System.err.println("disabled");
377                 }
378                 System.err.print("GraphicsPrimitive trace output to ");
379                 if (tracefile == null) {
380                     System.err.println("System.err");
381                 } else {
382                     System.err.println("file '"+tracefile+"'");
383                 }
384             }
385             GraphicsPrimitive.traceflags = traceflags;
386         }
387     }
388 
tracingEnabled()389     public static boolean tracingEnabled() {
390         return (traceflags != 0);
391     }
392 
getTraceOutputFile()393     private static PrintStream getTraceOutputFile() {
394         if (traceout == null) {
395             if (tracefile != null) {
396                 FileOutputStream o = AccessController.doPrivileged(
397                     new PrivilegedAction<FileOutputStream>() {
398                         public FileOutputStream run() {
399                             try {
400                                 return new FileOutputStream(tracefile);
401                             } catch (FileNotFoundException e) {
402                                 return null;
403                             }
404                         }
405                     });
406                 if (o != null) {
407                     traceout = new PrintStream(o);
408                 } else {
409                     traceout = System.err;
410                 }
411             } else {
412                 traceout = System.err;
413             }
414         }
415         return traceout;
416     }
417 
418     public static class TraceReporter implements Runnable {
setShutdownHook()419         public static void setShutdownHook() {
420             AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
421                 TraceReporter t = new TraceReporter();
422                 Thread thread = new Thread(
423                         ThreadGroupUtils.getRootThreadGroup(), t,
424                         "TraceReporter", 0, false);
425                 thread.setContextClassLoader(null);
426                 Runtime.getRuntime().addShutdownHook(thread);
427                 return null;
428             });
429         }
430 
run()431         public void run() {
432             PrintStream ps = getTraceOutputFile();
433             Iterator<Map.Entry<Object, int[]>> iterator =
434                 traceMap.entrySet().iterator();
435             long total = 0;
436             int numprims = 0;
437             while (iterator.hasNext()) {
438                 Map.Entry<Object, int[]> me = iterator.next();
439                 Object prim = me.getKey();
440                 int[] count = me.getValue();
441                 if (count[0] == 1) {
442                     ps.print("1 call to ");
443                 } else {
444                     ps.print(count[0]+" calls to ");
445                 }
446                 ps.println(prim);
447                 numprims++;
448                 total += count[0];
449             }
450             if (numprims == 0) {
451                 ps.println("No graphics primitives executed");
452             } else if (numprims > 1) {
453                 ps.println(total+" total calls to "+
454                            numprims+" different primitives");
455             }
456         }
457     }
458 
tracePrimitive(Object prim)459     public static synchronized void tracePrimitive(Object prim) {
460         if ((traceflags & TRACECOUNTS) != 0) {
461             if (traceMap == null) {
462                 traceMap = new HashMap<>();
463                 TraceReporter.setShutdownHook();
464             }
465             int[] o = traceMap.get(prim);
466             if (o == null) {
467                 o = new int[1];
468                 traceMap.put(prim, o);
469             }
470             o[0]++;
471         }
472         if ((traceflags & TRACELOG) != 0) {
473             PrintStream ps = getTraceOutputFile();
474             if ((traceflags & TRACETIMESTAMP) != 0) {
475                 ps.print(System.currentTimeMillis()+": ");
476             }
477             ps.println(prim);
478         }
479     }
480 
setupGeneralBinaryOp(GeneralBinaryOp gbo)481     protected void setupGeneralBinaryOp(GeneralBinaryOp gbo) {
482         int primID = gbo.getPrimTypeID();
483         String methodSignature = gbo.getSignature();
484         SurfaceType srctype = gbo.getSourceType();
485         CompositeType comptype = gbo.getCompositeType();
486         SurfaceType dsttype = gbo.getDestType();
487         Blit convertsrc, convertdst, convertres;
488         GraphicsPrimitive performop;
489 
490         convertsrc = createConverter(srctype, SurfaceType.IntArgb);
491         performop = GraphicsPrimitiveMgr.locatePrim(primID,
492                                                     SurfaceType.IntArgb,
493                                                     comptype, dsttype);
494         if (performop != null) {
495             convertdst = null;
496             convertres = null;
497         } else {
498             performop = getGeneralOp(primID, comptype);
499             if (performop == null) {
500                 throw new InternalError("Cannot construct general op for "+
501                                         methodSignature+" "+comptype);
502             }
503             convertdst = createConverter(dsttype, SurfaceType.IntArgb);
504             convertres = createConverter(SurfaceType.IntArgb, dsttype);
505         }
506 
507         gbo.setPrimitives(convertsrc, convertdst, performop, convertres);
508     }
509 
setupGeneralUnaryOp(GeneralUnaryOp guo)510     protected void setupGeneralUnaryOp(GeneralUnaryOp guo) {
511         int primID = guo.getPrimTypeID();
512         String methodSignature = guo.getSignature();
513         CompositeType comptype = guo.getCompositeType();
514         SurfaceType dsttype = guo.getDestType();
515 
516         Blit convertdst = createConverter(dsttype, SurfaceType.IntArgb);
517         GraphicsPrimitive performop = getGeneralOp(primID, comptype);
518         Blit convertres = createConverter(SurfaceType.IntArgb, dsttype);
519         if (convertdst == null || performop == null || convertres == null) {
520             throw new InternalError("Cannot construct binary op for "+
521                                     comptype+" "+dsttype);
522         }
523 
524         guo.setPrimitives(convertdst, performop, convertres);
525     }
526 
createConverter(SurfaceType srctype, SurfaceType dsttype)527     protected static Blit createConverter(SurfaceType srctype,
528                                           SurfaceType dsttype)
529     {
530         if (srctype.equals(dsttype)) {
531             return null;
532         }
533         Blit cv = Blit.getFromCache(srctype, CompositeType.SrcNoEa, dsttype);
534         if (cv == null) {
535             throw new InternalError("Cannot construct converter for "+
536                                     srctype+"=>"+dsttype);
537         }
538         return cv;
539     }
540 
convertFrom(Blit ob, SurfaceData srcData, int srcX, int srcY, int w, int h, SurfaceData dstData)541     protected static SurfaceData convertFrom(Blit ob, SurfaceData srcData,
542                                              int srcX, int srcY, int w, int h,
543                                              SurfaceData dstData)
544     {
545         return convertFrom(ob, srcData,
546                            srcX, srcY, w, h, dstData,
547                            BufferedImage.TYPE_INT_ARGB);
548     }
549 
convertFrom(Blit ob, SurfaceData srcData, int srcX, int srcY, int w, int h, SurfaceData dstData, int type)550     protected static SurfaceData convertFrom(Blit ob, SurfaceData srcData,
551                                              int srcX, int srcY, int w, int h,
552                                              SurfaceData dstData, int type)
553     {
554         if (dstData != null) {
555             Rectangle r = dstData.getBounds();
556             if (w > r.width || h > r.height) {
557                 dstData = null;
558             }
559         }
560         if (dstData == null) {
561             BufferedImage dstBI = new BufferedImage(w, h, type);
562             dstData = BufImgSurfaceData.createData(dstBI);
563         }
564         ob.Blit(srcData, dstData, AlphaComposite.Src, null,
565                 srcX, srcY, 0, 0, w, h);
566         return dstData;
567     }
568 
convertTo(Blit ob, SurfaceData srcImg, SurfaceData dstImg, Region clip, int dstX, int dstY, int w, int h)569     protected static void convertTo(Blit ob,
570                                     SurfaceData srcImg, SurfaceData dstImg,
571                                     Region clip,
572                                     int dstX, int dstY, int w, int h)
573     {
574         if (ob != null) {
575             ob.Blit(srcImg, dstImg, AlphaComposite.Src, clip,
576                     0, 0, dstX, dstY, w, h);
577         }
578     }
579 
getGeneralOp(int primID, CompositeType comptype)580     protected static GraphicsPrimitive getGeneralOp(int primID,
581                                                     CompositeType comptype)
582     {
583         return GraphicsPrimitiveMgr.locatePrim(primID,
584                                                SurfaceType.IntArgb,
585                                                comptype,
586                                                SurfaceType.IntArgb);
587     }
588 
simplename(Field[] fields, Object o)589     public static String simplename(Field[] fields, Object o) {
590         for (int i = 0; i < fields.length; i++) {
591             Field f = fields[i];
592             try {
593                 if (o == f.get(null)) {
594                     return f.getName();
595                 }
596             } catch (Exception e) {
597             }
598         }
599         return "\""+o.toString()+"\"";
600     }
601 
simplename(SurfaceType st)602     public static String simplename(SurfaceType st) {
603         return simplename(SurfaceType.class.getDeclaredFields(), st);
604     }
605 
simplename(CompositeType ct)606     public static String simplename(CompositeType ct) {
607         return simplename(CompositeType.class.getDeclaredFields(), ct);
608     }
609 
610     private String cachedname;
611 
toString()612     public String toString() {
613         if (cachedname == null) {
614             String sig = methodSignature;
615             int index = sig.indexOf('(');
616             if (index >= 0) {
617                 sig = sig.substring(0, index);
618             }
619             cachedname = (getClass().getName()+"::"+
620                           sig+"("+
621                           simplename(sourceType)+", "+
622                           simplename(compositeType)+", "+
623                           simplename(destType)+")");
624         }
625         return cachedname;
626     }
627 }
628