From: Glenn Adams Date: Fri, 18 Jan 2013 16:42:03 +0000 (+0000) Subject: FOP-2191: cache matched lookups, assembled lookup spec uses; reduce glyph processing... X-Git-Tag: fop-2_0~242 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0327e0137383bc897b45faee65f0f21f5b820803;p=xmlgraphics-fop.git FOP-2191: cache matched lookups, assembled lookup spec uses; reduce glyph processing state allocation git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1435241 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java index a865449ac..fafcf5614 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningState.java @@ -43,6 +43,12 @@ public class GlyphPositioningState extends GlyphProcessingState { /** if true, then some adjustment was applied */ private boolean adjusted; + /** + * Construct default (reset) glyph positioning state. + */ + public GlyphPositioningState() { + } + /** * Construct glyph positioning state. * @param gs input glyph sequence @@ -73,6 +79,26 @@ public class GlyphPositioningState extends GlyphProcessingState { this.adjustments = ps.adjustments; } + /** + * Reset glyph positioning state. + * @param gs input glyph sequence + * @param script script identifier + * @param language language identifier + * @param feature feature identifier + * @param fontSize font size (in micropoints) + * @param widths array of design advancements (in glyph index order) + * @param adjustments positioning adjustments to which positioning is applied + * @param sct script context tester (or null) + */ + public GlyphPositioningState reset ( GlyphSequence gs, String script, String language, String feature, int fontSize, int[] widths, int[][] adjustments, ScriptContextTester sct ) { + super.reset ( gs, script, language, feature, sct ); + this.fontSize = fontSize; + this.widths = widths; + this.adjustments = adjustments; + this.adjusted = false; + return this; + } + /** * Obtain design advancement (width) of glyph at specified index. * @param gi glyph index diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java index a2e3a7fe7..5bf073bf4 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphPositioningSubtable.java @@ -34,6 +34,8 @@ import org.apache.fop.complexscripts.util.ScriptContextTester; */ public abstract class GlyphPositioningSubtable extends GlyphSubtable implements GlyphPositioning { + private static final GlyphPositioningState state = new GlyphPositioningState(); + /** * Instantiate a GlyphPositioningSubtable. * @param id subtable identifier @@ -122,7 +124,9 @@ public abstract class GlyphPositioningSubtable extends GlyphSubtable implements * @return true if a non-zero adjustment occurred */ public static final boolean position ( GlyphSequence gs, String script, String language, String feature, int fontSize, GlyphPositioningSubtable[] sta, int[] widths, int[][] adjustments, ScriptContextTester sct ) { - return position ( new GlyphPositioningState ( gs, script, language, feature, fontSize, widths, adjustments, sct ), sta, -1 ); + synchronized ( state ) { + return position ( state.reset ( gs, script, language, feature, fontSize, widths, adjustments, sct ), sta, -1 ); + } } } diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java index c003ebbf9..6916e2742 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphProcessingState.java @@ -75,6 +75,12 @@ public class GlyphProcessingState { /** current subtable */ private GlyphSubtable subtable; + /** + * Construct default (reset) glyph processing state. + */ + public GlyphProcessingState() { + } + /** * Construct glyph processing state. * @param gs input glyph sequence @@ -106,6 +112,35 @@ public class GlyphProcessingState { setPosition ( s.index ); } + /** + * Reset glyph processing state. + * @param gs input glyph sequence + * @param script script identifier + * @param language language identifier + * @param feature feature identifier + * @param sct script context tester (or null) + */ + protected GlyphProcessingState reset ( GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct ) { + this.gdef = null; + this.script = script; + this.language = language; + this.feature = feature; + this.igs = gs; + this.index = 0; + this.indexLast = gs.getGlyphCount(); + this.consumed = 0; + this.lookupFlags = 0; + this.classMatchSet = 0; + this.sct = sct; + this.gct = ( sct != null ) ? sct.getTester ( feature ) : null; + this.ignoreBase = new GlyphTester() { public boolean test(int gi, int flags) { return isIgnoredBase(gi, flags); } }; + this.ignoreLigature = new GlyphTester() { public boolean test(int gi, int flags) { return isIgnoredLigature(gi, flags); } }; + this.ignoreMark = new GlyphTester() { public boolean test(int gi, int flags) { return isIgnoredMark(gi, flags); } }; + this.ignoreDefault = null; + this.subtable = null; + return this; + } + /** * Set governing glyph definition table. * @param gdef glyph definition table (or null, to unset) diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java index c6217e394..5ff3394a2 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionState.java @@ -47,6 +47,12 @@ public class GlyphSubstitutionState extends GlyphProcessingState { /** character association predications */ private boolean predications; + /** + * Construct default (reset) glyph substitution state. + */ + public GlyphSubstitutionState() { + } + /** * Construct glyph substitution state. * @param gs input glyph sequence @@ -73,6 +79,23 @@ public class GlyphSubstitutionState extends GlyphProcessingState { this.oal = new ArrayList ( indexLast ); } + /** + * Reset glyph substitution state. + * @param gs input glyph sequence + * @param script script identifier + * @param language language identifier + * @param feature feature identifier + * @param sct script context tester (or null) + */ + public GlyphSubstitutionState reset ( GlyphSequence gs, String script, String language, String feature, ScriptContextTester sct ) { + super.reset ( gs, script, language, feature, sct ); + this.alternatesIndex = null; + this.ogb = IntBuffer.allocate ( gs.getGlyphCount() ); + this.oal = new ArrayList ( gs.getGlyphCount() ); + this.predications = gs.getPredications(); + return this; + } + /** * Set alternates indices. * @param alternates array of alternates indices ordered by coverage index diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java index 2f337bc6a..446854566 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphSubstitutionSubtable.java @@ -33,6 +33,8 @@ import org.apache.fop.complexscripts.util.ScriptContextTester; */ public abstract class GlyphSubstitutionSubtable extends GlyphSubtable implements GlyphSubstitution { + private static final GlyphSubstitutionState state = new GlyphSubstitutionState(); + /** * Instantiate a GlyphSubstitutionSubtable. * @param id subtable identifier @@ -119,7 +121,9 @@ public abstract class GlyphSubstitutionSubtable extends GlyphSubtable implements * @return output glyph sequence */ public static final GlyphSequence substitute ( GlyphSequence gs, String script, String language, String feature, GlyphSubstitutionSubtable[] sta, ScriptContextTester sct ) { - return substitute ( new GlyphSubstitutionState ( gs, script, language, feature, sct ), sta, -1 ); + synchronized ( state ) { + return substitute ( state.reset ( gs, script, language, feature, sct ), sta, -1 ); + } } } diff --git a/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java b/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java index d7bf938cb..1dd9d8b77 100644 --- a/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java +++ b/src/java/org/apache/fop/complexscripts/fonts/GlyphTable.java @@ -21,6 +21,7 @@ package org.apache.fop.complexscripts.fonts; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -73,6 +74,9 @@ public class GlyphTable { // map from lookup identifiers to lookup tables private Map/**/ lookupTables; + // cache for lookups matching + private Map/*>>*/ matchedLookups; + // if true, then prevent further subtable addition private boolean frozen; @@ -90,6 +94,7 @@ public class GlyphTable { this.gdef = gdef; this.lookups = lookups; this.lookupTables = new LinkedHashMap/*>*/(); + this.matchedLookups = new HashMap/*>>*/(); } } @@ -212,11 +217,16 @@ public class GlyphTable { * @return a (possibly empty) map from matching lookup specifications to lists of corresponding lookup tables */ public Map/*>*/ matchLookups ( String script, String language, String feature ) { - List/**/ lsl = matchLookupSpecs ( script, language, feature ); - Map lm = new LinkedHashMap(); - for ( Iterator it = lsl.iterator(); it.hasNext(); ) { - LookupSpec ls = (LookupSpec) it.next(); - lm.put ( ls, findLookupTables ( ls ) ); + LookupSpec lsm = new LookupSpec ( script, language, feature, true, true ); + Map/*>*/ lm = (Map/*>*/) matchedLookups.get ( lsm ); + if ( lm == null ) { + lm = new LinkedHashMap(); + List/**/ lsl = matchLookupSpecs ( script, language, feature ); + for ( Iterator it = lsl.iterator(); it.hasNext(); ) { + LookupSpec ls = (LookupSpec) it.next(); + lm.put ( ls, findLookupTables ( ls ) ); + } + matchedLookups.put ( lsm, lm ); } return lm; } @@ -337,23 +347,34 @@ public class GlyphTable { * @param feature a feature identifier */ public LookupSpec ( String script, String language, String feature ) { - if ( ( script == null ) || ( script.length() == 0 ) ) { + this ( script, language, feature, false, false ); + } + + /** + * Instantiate lookup spec. + * @param script a script identifier + * @param language a language identifier + * @param feature a feature identifier + * @param permitEmpty if true the permit empty script, language, or feature + * @param permitWildcard if true the permit wildcard script, language, or feature + */ + LookupSpec ( String script, String language, String feature, boolean permitEmpty, boolean permitWildcard ) { + if ( ( script == null ) || ( ! permitEmpty && ( script.length() == 0 ) ) ) { throw new AdvancedTypographicTableFormatException ( "script must be non-empty string" ); - } else if ( ( language == null ) || ( language.length() == 0 ) ) { + } else if ( ( language == null ) || ( ! permitEmpty && ( language.length() == 0 ) ) ) { throw new AdvancedTypographicTableFormatException ( "language must be non-empty string" ); - } else if ( ( feature == null ) || ( feature.length() == 0 ) ) { + } else if ( ( feature == null ) || ( ! permitEmpty && ( feature.length() == 0 ) ) ) { throw new AdvancedTypographicTableFormatException ( "feature must be non-empty string" ); - } else if ( script.equals("*") ) { + } else if ( ! permitWildcard && script.equals("*") ) { throw new AdvancedTypographicTableFormatException ( "script must not be wildcard" ); - } else if ( language.equals("*") ) { + } else if ( ! permitWildcard && language.equals("*") ) { throw new AdvancedTypographicTableFormatException ( "language must not be wildcard" ); - } else if ( feature.equals("*") ) { + } else if ( ! permitWildcard && feature.equals("*") ) { throw new AdvancedTypographicTableFormatException ( "feature must not be wildcard" ); - } else { - this.script = script.trim(); - this.language = language.trim(); - this.feature = feature.trim(); } + this.script = script.trim(); + this.language = language.trim(); + this.feature = feature.trim(); } /** @return script identifier */ diff --git a/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java b/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java index e9397a710..95d9a4e00 100644 --- a/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java +++ b/src/java/org/apache/fop/complexscripts/scripts/ScriptProcessor.java @@ -19,6 +19,7 @@ package org.apache.fop.complexscripts.scripts; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -44,6 +45,8 @@ public abstract class ScriptProcessor { private final String script; + private final Map/**/ assembledLookups; + private static Map processors = new HashMap(); /** @@ -55,6 +58,7 @@ public abstract class ScriptProcessor { throw new IllegalArgumentException ( "script must be non-empty string" ); } else { this.script = script; + this.assembledLookups = new HashMap/**/(); } } @@ -201,7 +205,22 @@ public abstract class ScriptProcessor { * @return ordered array of assembled lookup table use specifications */ public final GlyphTable.UseSpec[] assembleLookups ( GlyphTable table, String[] features, Map/*>*/ lookups ) { - return table.assembleLookups ( features, lookups ); + AssembledLookupsKey key = new AssembledLookupsKey ( table, features, lookups ); + GlyphTable.UseSpec[] usa; + if ( ( usa = assembledLookupsGet ( key ) ) != null ) { + return usa; + } else { + return assembledLookupsPut ( key, table.assembleLookups ( features, lookups ) ); + } + } + + private GlyphTable.UseSpec[] assembledLookupsGet ( AssembledLookupsKey key ) { + return (GlyphTable.UseSpec[]) assembledLookups.get ( key ); + } + + private GlyphTable.UseSpec[] assembledLookupsPut ( AssembledLookupsKey key, GlyphTable.UseSpec[] usa ) { + assembledLookups.put ( key, usa ); + return usa; } /** @@ -232,4 +251,45 @@ public abstract class ScriptProcessor { return sp; } + private static class AssembledLookupsKey { + + private final GlyphTable table; + private final String[] features; + private final Map/*>*/ lookups; + + AssembledLookupsKey ( GlyphTable table, String[] features, Map/*>*/ lookups ) { + this.table = table; + this.features = features; + this.lookups = lookups; + } + + /** {@inheritDoc} */ + public int hashCode() { + int hc = 0; + hc = 7 * hc + ( hc ^ table.hashCode() ); + hc = 11 * hc + ( hc ^ Arrays.hashCode ( features ) ); + hc = 17 * hc + ( hc ^ lookups.hashCode() ); + return hc; + } + + /** {@inheritDoc} */ + public boolean equals ( Object o ) { + if ( o instanceof AssembledLookupsKey ) { + AssembledLookupsKey k = (AssembledLookupsKey) o; + if ( ! table.equals ( k.table ) ) { + return false; + } else if ( ! Arrays.equals ( features, k.features ) ) { + return false; + } else if ( ! lookups.equals ( k.lookups ) ) { + return false; + } else { + return true; + } + } else { + return false; + } + } + + } + } diff --git a/status.xml b/status.xml index a566da0c3..caec3a862 100644 --- a/status.xml +++ b/status.xml @@ -59,6 +59,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Cache matched lookups, assembled lookup spec uses; reduce glyph processing state allocation. + Optimize string allocation in PDF output processing.