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