/* * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @author Charlton Innovations, Inc. */ package sun.java2d.loops; import java.awt.image.BufferedImage; import java.awt.AlphaComposite; import java.awt.Rectangle; import sun.awt.image.BufImgSurfaceData; import sun.awt.util.ThreadGroupUtils; import sun.java2d.SurfaceData; import sun.java2d.pipe.Region; import java.lang.reflect.Field; import java.util.StringTokenizer; import java.util.Iterator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.io.PrintStream; import java.io.OutputStream; import java.io.FileOutputStream; import java.io.FileNotFoundException; import java.security.AccessController; import java.security.PrivilegedAction; import sun.security.action.GetPropertyAction; /** * defines interface for primitives which can be placed into * the graphic component manager framework */ public abstract class GraphicsPrimitive { protected static interface GeneralBinaryOp { /** * This method allows the setupGeneralBinaryOp method to set * the converters into the General version of the Primitive. */ public void setPrimitives(Blit srcconverter, Blit dstconverter, GraphicsPrimitive genericop, Blit resconverter); /** * These 4 methods are implemented automatically for any * GraphicsPrimitive. They are used by setupGeneralBinaryOp * to retrieve the information needed to find the right * converter primitives. */ public SurfaceType getSourceType(); public CompositeType getCompositeType(); public SurfaceType getDestType(); public String getSignature(); public int getPrimTypeID(); } protected static interface GeneralUnaryOp { /** * This method allows the setupGeneralUnaryOp method to set * the converters into the General version of the Primitive. */ public void setPrimitives(Blit dstconverter, GraphicsPrimitive genericop, Blit resconverter); /** * These 3 methods are implemented automatically for any * GraphicsPrimitive. They are used by setupGeneralUnaryOp * to retrieve the information needed to find the right * converter primitives. */ public CompositeType getCompositeType(); public SurfaceType getDestType(); public String getSignature(); public int getPrimTypeID(); } /** * INSTANCE DATA MEMBERS DESCRIBING CHARACTERISTICS OF THIS PRIMITIVE **/ // Making these be instance data members (instead of virtual methods // overridden by subclasses) is actually cheaper, since each class // is a singleton. As instance data members with final accessors, // accesses can be inlined. private String methodSignature; private int uniqueID; private static int unusedPrimID = 1; private SurfaceType sourceType; private CompositeType compositeType; private SurfaceType destType; private long pNativePrim; // Native blit loop info public static final synchronized int makePrimTypeID() { if (unusedPrimID > 255) { throw new InternalError("primitive id overflow"); } return unusedPrimID++; } public static final synchronized int makeUniqueID(int primTypeID, SurfaceType src, CompositeType cmp, SurfaceType dst) { return (primTypeID << 24) | (dst.getUniqueID() << 16) | (cmp.getUniqueID() << 8) | (src.getUniqueID()); } /** * Create a new GraphicsPrimitive with all of the required * descriptive information. */ protected GraphicsPrimitive(String methodSignature, int primTypeID, SurfaceType sourceType, CompositeType compositeType, SurfaceType destType) { this.methodSignature = methodSignature; this.sourceType = sourceType; this.compositeType = compositeType; this.destType = destType; if(sourceType == null || compositeType == null || destType == null) { this.uniqueID = primTypeID << 24; } else { this.uniqueID = GraphicsPrimitive.makeUniqueID(primTypeID, sourceType, compositeType, destType); } } /** * Create a new GraphicsPrimitive for native invocation * with all of the required descriptive information. */ protected GraphicsPrimitive(long pNativePrim, String methodSignature, int primTypeID, SurfaceType sourceType, CompositeType compositeType, SurfaceType destType) { this.pNativePrim = pNativePrim; this.methodSignature = methodSignature; this.sourceType = sourceType; this.compositeType = compositeType; this.destType = destType; if(sourceType == null || compositeType == null || destType == null) { this.uniqueID = primTypeID << 24; } else { this.uniqueID = GraphicsPrimitive.makeUniqueID(primTypeID, sourceType, compositeType, destType); } } /** * METHODS TO DESCRIBE THE SURFACES PRIMITIVES * CAN OPERATE ON AND THE FUNCTIONALITY THEY IMPLEMENT **/ /** * Gets instance ID of this graphics primitive. * * Instance ID is comprised of four distinct ids (ORed together) * that uniquely identify each instance of a GraphicsPrimitive * object. The four ids making up instance ID are: * 1. primitive id - identifier shared by all primitives of the * same type (eg. all Blits have the same primitive id) * 2. sourcetype id - identifies source surface type * 3. desttype id - identifies destination surface type * 4. compositetype id - identifies composite used * * @return instance ID */ public final int getUniqueID() { return uniqueID; } /** */ public final String getSignature() { return methodSignature; } /** * Gets unique id for this GraphicsPrimitive type. * * This id is used to identify the TYPE of primitive (Blit vs. BlitBg) * as opposed to INSTANCE of primitive. * * @return primitive ID */ public final int getPrimTypeID() { return uniqueID >>> 24; } /** */ public final long getNativePrim() { return pNativePrim; } /** */ public final SurfaceType getSourceType() { return sourceType; } /** */ public final CompositeType getCompositeType() { return compositeType; } /** */ public final SurfaceType getDestType() { return destType; } /** * Return true if this primitive can be used for the given signature * surfaces, and composite. * * @param signature The signature of the given operation. Must be * == (not just .equals) the signature string given by the * abstract class that declares the operation. * @param srctype The surface type for the source of the operation * @param comptype The composite type for the operation * @param dsttype The surface type for the destination of the operation */ public final boolean satisfies(String signature, SurfaceType srctype, CompositeType comptype, SurfaceType dsttype) { if (signature != methodSignature) { return false; } while (true) { if (srctype == null) { return false; } if (srctype.equals(sourceType)) { break; } srctype = srctype.getSuperType(); } while (true) { if (comptype == null) { return false; } if (comptype.equals(compositeType)) { break; } comptype = comptype.getSuperType(); } while (true) { if (dsttype == null) { return false; } if (dsttype.equals(destType)) { break; } dsttype = dsttype.getSuperType(); } return true; } // // A version of satisfies used for regression testing // final boolean satisfiesSameAs(GraphicsPrimitive other) { return (methodSignature == other.methodSignature && sourceType.equals(other.sourceType) && compositeType.equals(other.compositeType) && destType.equals(other.destType)); } public abstract GraphicsPrimitive makePrimitive(SurfaceType srctype, CompositeType comptype, SurfaceType dsttype); public abstract GraphicsPrimitive traceWrap(); static HashMap traceMap; static HashSet traceNotImplSet; public static int traceflags; public static String tracefile; public static String pname; public static PrintStream traceout; public static long treshold = 0; public static boolean verbose = false; public static final int TRACELOG = 1; public static final int TRACETIMESTAMP = 2; public static final int TRACECOUNTS = 4; public static final int TRACEPTIME = 8; public static final int TRACEPNAME = 16; public static final int TRACENOTIMPL = 32; static void showTraceUsage() { System.err.println("usage: -Dsun.java2d.trace="+ "[log[,timestamp]],[count],[ptime],[name:],"+ "[out:],[td=],[help],[verbose]"); } static { GetPropertyAction gpa = new GetPropertyAction("sun.java2d.trace"); String trace = AccessController.doPrivileged(gpa); if (trace != null) { int traceflags = 0; StringTokenizer st = new StringTokenizer(trace, ","); while (st.hasMoreTokens()) { String tok = st.nextToken(); if (tok.equalsIgnoreCase("count")) { traceflags |= GraphicsPrimitive.TRACECOUNTS; } else if (tok.equalsIgnoreCase("log")) { traceflags |= GraphicsPrimitive.TRACELOG; } else if (tok.equalsIgnoreCase("timestamp")) { traceflags |= GraphicsPrimitive.TRACETIMESTAMP; } else if (tok.equalsIgnoreCase("ptime")) { traceflags |=GraphicsPrimitive.TRACEPTIME; } else if (tok.equalsIgnoreCase("notimpl")) { traceflags |=GraphicsPrimitive.TRACENOTIMPL; } else if (tok.regionMatches(true, 0, "name:", 0, 5)) { traceflags |=GraphicsPrimitive.TRACEPNAME; pname = tok.substring(6); } else if (tok.equalsIgnoreCase("verbose")) { verbose = true; } else if (tok.regionMatches(true, 0, "out:", 0, 4)) { tracefile = tok.substring(4); } else if (tok.regionMatches(true, 0, "td=", 0, 3)) { try { treshold = Long.parseLong(tok.substring(3)); } catch (NumberFormatException e) { showTraceUsage(); } } else { if (!tok.equalsIgnoreCase("help")) { System.err.println("unrecognized token: "+tok); } showTraceUsage(); } } GraphicsPrimitiveMgr.setTraceFlags(traceflags); if (verbose) { System.err.print("GraphicsPrimitive logging "); if ((traceflags & GraphicsPrimitive.TRACELOG) != 0) { System.err.println("enabled"); System.err.print("GraphicsPrimitive timetamps "); if ((traceflags & GraphicsPrimitive.TRACETIMESTAMP) != 0) { System.err.println("enabled"); } else { System.err.println("disabled"); } } else { System.err.println("[and timestamps] disabled"); } System.err.print("GraphicsPrimitive invocation counts "); if ((traceflags & GraphicsPrimitive.TRACECOUNTS) != 0) { System.err.println("enabled"); } else { System.err.println("disabled"); } System.err.print("GraphicsPrimitive trace output to "); if (tracefile == null) { System.err.println("System.err"); } else { System.err.println("file '"+tracefile+"'"); } } GraphicsPrimitive.traceflags = traceflags; } } public static boolean tracingEnabled() { return (traceflags != 0); } private static PrintStream getTraceOutputFile() { if (traceout == null) { if (tracefile != null) { FileOutputStream o = AccessController.doPrivileged( new PrivilegedAction() { public FileOutputStream run() { try { return new FileOutputStream(tracefile); } catch (FileNotFoundException e) { return null; } } }); if (o != null) { traceout = new PrintStream(o); } else { traceout = System.err; } } else { traceout = System.err; } } return traceout; } public static class TraceReporter implements Runnable { private static boolean hookEnabled = false; public static synchronized void setShutdownHook() { if (hookEnabled) return; hookEnabled = true; AccessController.doPrivileged((PrivilegedAction) () -> { TraceReporter t = new TraceReporter(); Thread thread = new Thread( ThreadGroupUtils.getRootThreadGroup(), t, "TraceReporter", 0, false); thread.setContextClassLoader(null); Runtime.getRuntime().addShutdownHook(thread); return null; }); } public void run() { PrintStream ps = getTraceOutputFile(); if (traceMap != null) { Iterator> iterator = traceMap.entrySet().iterator(); long total = 0; int numprims = 0; while (iterator.hasNext()) { Map.Entry me = iterator.next(); Object prim = me.getKey(); int[] count = me.getValue(); if (count[0] == 1) { ps.print("1 call to "); } else { ps.print(count[0] + " calls to "); } ps.println(prim); numprims++; total += count[0]; } if (numprims == 0) { ps.println("No graphics primitives executed"); } else if (numprims > 1) { ps.println(total + " total calls to " + numprims + " different primitives"); } } if (traceNotImplSet != null) { ps.println("Not implemented graphics primitives:"); for (String name : traceNotImplSet) { ps.println(name); } } } } public synchronized static void tracePrimitive(Object prim) { if ((traceflags & TRACEPNAME) != 0) { if (!prim.toString().contains(pname)) return; } if ((traceflags & TRACECOUNTS) != 0) { if (traceMap == null) { traceMap = new HashMap<>(); TraceReporter.setShutdownHook(); } int[] o = traceMap.get(prim); if (o == null) { o = new int[1]; traceMap.put(prim, o); } o[0]++; } if ((traceflags & TRACELOG) != 0) { PrintStream ps = getTraceOutputFile(); if ((traceflags & TRACETIMESTAMP) != 0) { ps.print(System.currentTimeMillis()+": "); } ps.println(prim); } } public synchronized static void traceNotImplPrimitive(Object prim) { if ((traceflags & TRACEPNAME) != 0) { if (!prim.toString().contains(pname)) return; } if ((traceflags & TRACECOUNTS) != 0) { if (traceNotImplSet == null) { traceNotImplSet = new HashSet(); TraceReporter.setShutdownHook(); } traceNotImplSet.add(prim.toString()); } if ((traceflags & TRACELOG) != 0) { PrintStream ps = getTraceOutputFile(); if ((traceflags & TRACETIMESTAMP) != 0) { ps.print(System.currentTimeMillis()+":[NOT IMPL] "); } ps.println(prim); } } public synchronized static void tracePrimitiveTime(Object prim, long time) { if ((traceflags & TRACEPNAME) != 0) { if (!prim.toString().contains(pname)) return; } if (time > treshold && (traceflags & TRACEPTIME) != 0 && (traceflags & TRACELOG) != 0) { PrintStream ps = getTraceOutputFile(); ps.println(prim + " time: " + time); if (verbose) { final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); if (stackTrace.length > 3) { for (int i = 3; i < stackTrace.length; i++) { ps.println(" " + stackTrace[i].toString()); } } ps.println(); } } } protected void setupGeneralBinaryOp(GeneralBinaryOp gbo) { int primID = gbo.getPrimTypeID(); String methodSignature = gbo.getSignature(); SurfaceType srctype = gbo.getSourceType(); CompositeType comptype = gbo.getCompositeType(); SurfaceType dsttype = gbo.getDestType(); Blit convertsrc, convertdst, convertres; GraphicsPrimitive performop; convertsrc = createConverter(srctype, SurfaceType.IntArgb); performop = GraphicsPrimitiveMgr.locatePrim(primID, SurfaceType.IntArgb, comptype, dsttype); if (performop != null) { convertdst = null; convertres = null; } else { performop = getGeneralOp(primID, comptype); if (performop == null) { throw new InternalError("Cannot construct general op for "+ methodSignature+" "+comptype); } convertdst = createConverter(dsttype, SurfaceType.IntArgb); convertres = createConverter(SurfaceType.IntArgb, dsttype); } gbo.setPrimitives(convertsrc, convertdst, performop, convertres); } protected void setupGeneralUnaryOp(GeneralUnaryOp guo) { int primID = guo.getPrimTypeID(); String methodSignature = guo.getSignature(); CompositeType comptype = guo.getCompositeType(); SurfaceType dsttype = guo.getDestType(); Blit convertdst = createConverter(dsttype, SurfaceType.IntArgb); GraphicsPrimitive performop = getGeneralOp(primID, comptype); Blit convertres = createConverter(SurfaceType.IntArgb, dsttype); if (convertdst == null || performop == null || convertres == null) { throw new InternalError("Cannot construct binary op for "+ comptype+" "+dsttype); } guo.setPrimitives(convertdst, performop, convertres); } protected static Blit createConverter(SurfaceType srctype, SurfaceType dsttype) { if (srctype.equals(dsttype)) { return null; } Blit cv = Blit.getFromCache(srctype, CompositeType.SrcNoEa, dsttype); if (cv == null) { throw new InternalError("Cannot construct converter for "+ srctype+"=>"+dsttype); } return cv; } protected static SurfaceData convertFrom(Blit ob, SurfaceData srcData, int srcX, int srcY, int w, int h, SurfaceData dstData) { return convertFrom(ob, srcData, srcX, srcY, w, h, dstData, BufferedImage.TYPE_INT_ARGB); } protected static SurfaceData convertFrom(Blit ob, SurfaceData srcData, int srcX, int srcY, int w, int h, SurfaceData dstData, int type) { if (dstData != null) { Rectangle r = dstData.getBounds(); if (w > r.width || h > r.height) { dstData = null; } } if (dstData == null) { BufferedImage dstBI = new BufferedImage(w, h, type); dstData = BufImgSurfaceData.createData(dstBI); } ob.Blit(srcData, dstData, AlphaComposite.Src, null, srcX, srcY, 0, 0, w, h); return dstData; } protected static void convertTo(Blit ob, SurfaceData srcImg, SurfaceData dstImg, Region clip, int dstX, int dstY, int w, int h) { if (ob != null) { ob.Blit(srcImg, dstImg, AlphaComposite.Src, clip, 0, 0, dstX, dstY, w, h); } } protected static GraphicsPrimitive getGeneralOp(int primID, CompositeType comptype) { return GraphicsPrimitiveMgr.locatePrim(primID, SurfaceType.IntArgb, comptype, SurfaceType.IntArgb); } public static String simplename(Field[] fields, Object o) { for (int i = 0; i < fields.length; i++) { Field f = fields[i]; try { if (o == f.get(null)) { return f.getName(); } } catch (Exception e) { } } return "\""+o.toString()+"\""; } public static String simplename(SurfaceType st) { return simplename(SurfaceType.class.getDeclaredFields(), st); } public static String simplename(CompositeType ct) { return simplename(CompositeType.class.getDeclaredFields(), ct); } private String cachedname; public String toString() { if (cachedname == null) { String sig = methodSignature; int index = sig.indexOf('('); if (index >= 0) { sig = sig.substring(0, index); } cachedname = (getClass().getName()+"::"+ sig+"("+ simplename(sourceType)+", "+ simplename(compositeType)+", "+ simplename(destType)+")"); } return cachedname; } }