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