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          */
  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          */
  75         public SurfaceType getSourceType();
  76         public CompositeType getCompositeType();
  77         public SurfaceType getDestType();
  78         public String getSignature();
  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          */
  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          */
  97         public CompositeType getCompositeType();
  98         public SurfaceType getDestType();
  99         public String getSignature();
 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 
 121     public static final synchronized int makePrimTypeID() {
 122         if (unusedPrimID > 255) {
 123             throw new InternalError("primitive id overflow");
 124         }
 125         return unusedPrimID++;
 126     }
 127 
 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      */
 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      */
 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      */
 210     public final int getUniqueID() {
 211         return uniqueID;
 212     }
 213 
 214     /**
 215      */
 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      */
 228     public final int getPrimTypeID() {
 229         return uniqueID >>> 24;
 230     }
 231 
 232     /**
 233      */
 234     public final long getNativePrim() {
 235         return pNativePrim;
 236     }
 237 
 238     /**
 239      */
 240     public final SurfaceType getSourceType() {
 241         return sourceType;
 242     }
 243 
 244     /**
 245      */
 246     public final CompositeType getCompositeType() {
 247         return compositeType;
 248     }
 249 
 250     /**
 251      */
 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      */
 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     //
 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 
 315     public abstract GraphicsPrimitive makePrimitive(SurfaceType srctype,
 316                                                     CompositeType comptype,
 317                                                     SurfaceType dsttype);
 318 
 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 
 389     public static boolean tracingEnabled() {
 390         return (traceflags != 0);
 391     }
 392 
 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 {
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 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 
 602     public static String simplename(SurfaceType st) {
 603         return simplename(SurfaceType.class.getDeclaredFields(), st);
 604     }
 605 
 606     public static String simplename(CompositeType ct) {
 607         return simplename(CompositeType.class.getDeclaredFields(), ct);
 608     }
 609 
 610     private String cachedname;
 611 
 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 }