- use only one RenderInstruction cache item when instructions are the same for multiple zoom-level
- do common checks in Rule instead of calling interface methods
This commit is contained in:
parent
b50481653a
commit
bf75d3bead
@ -19,26 +19,22 @@ import org.oscim.core.Tag;
|
||||
class MatchingCacheKey {
|
||||
int mHashCodeValue;
|
||||
Tag[] mTags;
|
||||
byte mZoomLevel;
|
||||
|
||||
MatchingCacheKey() {
|
||||
}
|
||||
|
||||
MatchingCacheKey(Tag[] tags, byte zoomLevel) {
|
||||
MatchingCacheKey(Tag[] tags) {
|
||||
mTags = tags;
|
||||
mZoomLevel = zoomLevel;
|
||||
mHashCodeValue = calculateHashCode();
|
||||
}
|
||||
|
||||
MatchingCacheKey(MatchingCacheKey key) {
|
||||
mTags = key.mTags;
|
||||
mZoomLevel = key.mZoomLevel;
|
||||
mHashCodeValue = key.mHashCodeValue;
|
||||
}
|
||||
|
||||
void set(Tag[] tags, byte zoomLevel) {
|
||||
void set(Tag[] tags) {
|
||||
mTags = tags;
|
||||
mZoomLevel = zoomLevel;
|
||||
|
||||
int result = 7;
|
||||
|
||||
@ -47,7 +43,7 @@ class MatchingCacheKey {
|
||||
break;
|
||||
result = 31 * result + mTags[i].hashCode();
|
||||
}
|
||||
result = 31 * result + mZoomLevel;
|
||||
result = 31 * result;
|
||||
|
||||
mHashCodeValue = result;
|
||||
}
|
||||
@ -57,15 +53,9 @@ class MatchingCacheKey {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
// else if (!(obj instanceof MatchingCacheKey)) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
MatchingCacheKey other = (MatchingCacheKey) obj;
|
||||
|
||||
if (mZoomLevel != other.mZoomLevel)
|
||||
return false;
|
||||
|
||||
if (mTags == null) {
|
||||
return (other.mTags == null);
|
||||
} else if (other.mTags == null)
|
||||
@ -76,10 +66,14 @@ class MatchingCacheKey {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
if (mTags[i] != other.mTags[i])
|
||||
return false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (mTags[i] == other.mTags[i])
|
||||
continue;
|
||||
if (mTags[i].key == other.mTags[i].key && mTags[i].value == other.mTags[i].value)
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -99,7 +93,7 @@ class MatchingCacheKey {
|
||||
break;
|
||||
result = 31 * result + mTags[i].hashCode();
|
||||
}
|
||||
result = 31 * result + mZoomLevel;
|
||||
result = 31 * result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -27,17 +27,12 @@ class NegativeRule extends Rule {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesNode(Tag[] tags, byte zoomLevel) {
|
||||
return mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
|
||||
&& (mElement != Element.WAY)
|
||||
&& mAttributeMatcher.matches(tags);
|
||||
boolean matchesNode(Tag[] tags) {
|
||||
return mAttributeMatcher.matches(tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesWay(Tag[] tags, byte zoomLevel, int closed) {
|
||||
return mZoomMin <= zoomLevel && mZoomMax >= zoomLevel
|
||||
&& (mElement != Element.NODE)
|
||||
&& (mClosed == closed || mClosed == Closed.ANY)
|
||||
&& mAttributeMatcher.matches(tags);
|
||||
boolean matchesWay(Tag[] tags) {
|
||||
return mAttributeMatcher.matches(tags);
|
||||
}
|
||||
}
|
||||
|
@ -36,21 +36,14 @@ class PositiveRule extends Rule {
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesNode(Tag[] tags, byte zoomLevel) {
|
||||
return (mElement != Element.WAY)
|
||||
&& mZoomMin <= zoomLevel
|
||||
&& mZoomMax >= zoomLevel
|
||||
&& (mKeyMatcher == null || mKeyMatcher.matches(tags))
|
||||
boolean matchesNode(Tag[] tags) {
|
||||
return (mKeyMatcher == null || mKeyMatcher.matches(tags))
|
||||
&& (mValueMatcher == null || mValueMatcher.matches(tags));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean matchesWay(Tag[] tags, byte zoomLevel, int closed) {
|
||||
return (mElement != Element.NODE)
|
||||
&& mZoomMin <= zoomLevel
|
||||
&& mZoomMax >= zoomLevel
|
||||
&& (mClosed == closed || mClosed == Closed.ANY)
|
||||
&& (mKeyMatcher == null || mKeyMatcher.matches(tags))
|
||||
&& (mValueMatcher == null || mValueMatcher.matches(tags));
|
||||
boolean matchesWay(Tag[] tags) {
|
||||
return (mKeyMatcher == null || mKeyMatcher.matches(tags)) &&
|
||||
(mValueMatcher == null || mValueMatcher.matches(tags));
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,9 @@ import android.graphics.Color;
|
||||
* A RenderTheme defines how ways and nodes are drawn.
|
||||
*/
|
||||
public class RenderTheme {
|
||||
private static final int MATCHING_CACHE_SIZE = 1024;
|
||||
private final static String TAG = RenderTheme.class.getName();
|
||||
|
||||
private static final int MATCHING_CACHE_SIZE = 512;
|
||||
private static final int RENDER_THEME_VERSION = 1;
|
||||
|
||||
private static void validate(String elementName, Integer version,
|
||||
@ -82,9 +84,15 @@ public class RenderTheme {
|
||||
private final int mMapBackground;
|
||||
private final ArrayList<Rule> mRulesList;
|
||||
|
||||
private final LRUCache<MatchingCacheKey, RenderInstruction[]> mMatchingCacheNodes;
|
||||
private final LRUCache<MatchingCacheKey, RenderInstruction[]> mMatchingCacheWay;
|
||||
private final LRUCache<MatchingCacheKey, RenderInstruction[]> mMatchingCacheArea;
|
||||
private final LRUCache<MatchingCacheKey, RenderInstructionItem> mMatchingCacheNodes;
|
||||
private final LRUCache<MatchingCacheKey, RenderInstructionItem> mMatchingCacheWay;
|
||||
private final LRUCache<MatchingCacheKey, RenderInstructionItem> mMatchingCacheArea;
|
||||
|
||||
class RenderInstructionItem {
|
||||
RenderInstructionItem next;
|
||||
int zoom;
|
||||
RenderInstruction[] list;
|
||||
}
|
||||
|
||||
RenderTheme(int mapBackground, float baseStrokeWidth, float baseTextSize) {
|
||||
mMapBackground = mapBackground;
|
||||
@ -92,11 +100,11 @@ public class RenderTheme {
|
||||
mBaseTextSize = baseTextSize;
|
||||
mRulesList = new ArrayList<Rule>();
|
||||
|
||||
mMatchingCacheNodes = new LRUCache<MatchingCacheKey, RenderInstruction[]>(
|
||||
mMatchingCacheNodes = new LRUCache<MatchingCacheKey, RenderInstructionItem>(
|
||||
MATCHING_CACHE_SIZE);
|
||||
mMatchingCacheWay = new LRUCache<MatchingCacheKey, RenderInstruction[]>(
|
||||
mMatchingCacheWay = new LRUCache<MatchingCacheKey, RenderInstructionItem>(
|
||||
MATCHING_CACHE_SIZE);
|
||||
mMatchingCacheArea = new LRUCache<MatchingCacheKey, RenderInstruction[]>(
|
||||
mMatchingCacheArea = new LRUCache<MatchingCacheKey, RenderInstructionItem>(
|
||||
MATCHING_CACHE_SIZE);
|
||||
}
|
||||
|
||||
@ -130,6 +138,12 @@ public class RenderTheme {
|
||||
return mMapBackground;
|
||||
}
|
||||
|
||||
private static void render(IRenderCallback renderCallback,
|
||||
RenderInstruction[] renderInstructions, Tag[] tags) {
|
||||
for (int i = 0, n = renderInstructions.length; i < n; i++)
|
||||
renderInstructions[i].renderNode(renderCallback, tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param renderCallback
|
||||
* ...
|
||||
@ -142,41 +156,92 @@ public class RenderTheme {
|
||||
public synchronized RenderInstruction[] matchNode(IRenderCallback renderCallback,
|
||||
Tag[] tags, byte zoomLevel) {
|
||||
|
||||
RenderInstruction[] renderInstructions = null;
|
||||
// list of renderinsctruction items in cache
|
||||
RenderInstructionItem ris = null;
|
||||
|
||||
MatchingCacheKey matchingCacheKey;
|
||||
// the item matching tags and zoomlevel
|
||||
RenderInstructionItem ri = null;
|
||||
|
||||
matchingCacheKey = new MatchingCacheKey(tags, zoomLevel);
|
||||
MatchingCacheKey matchingCacheKey = new MatchingCacheKey(tags);
|
||||
boolean found = mMatchingCacheNodes.containsKey(matchingCacheKey);
|
||||
int zoomMask = 1 << zoomLevel;
|
||||
|
||||
if (found) {
|
||||
renderInstructions = mMatchingCacheNodes.get(matchingCacheKey);
|
||||
} else {
|
||||
ris = mMatchingCacheNodes.get(matchingCacheKey);
|
||||
|
||||
for (ri = ris; ri != null; ri = ri.next)
|
||||
if ((ri.zoom & zoomMask) != 0)
|
||||
// cache hit
|
||||
break;
|
||||
}
|
||||
|
||||
if (ri == null) {
|
||||
// cache miss
|
||||
List<RenderInstruction> matchingList = new ArrayList<RenderInstruction>(4);
|
||||
List<RenderInstruction> matches = mMatchingList;
|
||||
matches.clear();
|
||||
for (int i = 0, n = mRulesList.size(); i < n; ++i)
|
||||
mRulesList.get(i)
|
||||
.matchNode(renderCallback, tags, zoomLevel, matchingList);
|
||||
mRulesList.get(i).matchNode(renderCallback, tags, zoomLevel, matches);
|
||||
|
||||
int size = matches.size();
|
||||
|
||||
// check if same instructions are used in another level
|
||||
for (ri = ris; ri != null; ri = ri.next) {
|
||||
if (size == 0) {
|
||||
if (ri.list != null)
|
||||
continue;
|
||||
|
||||
// both matchinglists are empty
|
||||
break;
|
||||
}
|
||||
|
||||
if (ri.list == null)
|
||||
continue;
|
||||
|
||||
if (ri.list.length != size)
|
||||
continue;
|
||||
|
||||
int i = 0;
|
||||
for (RenderInstruction r : ri.list) {
|
||||
if (r != matches.get(i))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
if (i == size)
|
||||
// both matching lists contain the same items
|
||||
break;
|
||||
}
|
||||
|
||||
if (ri != null) {
|
||||
// we found a same matchting list on another zoomlevel
|
||||
ri.zoom |= zoomMask;
|
||||
} else {
|
||||
|
||||
ri = new RenderInstructionItem();
|
||||
ri.zoom = zoomMask;
|
||||
|
||||
int size = matchingList.size();
|
||||
if (size > 0) {
|
||||
renderInstructions = new RenderInstruction[size];
|
||||
matchingList.toArray(renderInstructions);
|
||||
ri.list = new RenderInstruction[size];
|
||||
matches.toArray(ri.list);
|
||||
}
|
||||
// attach this list to the one found for MatchingKey
|
||||
if (ris != null)
|
||||
ris.next = ri;
|
||||
else
|
||||
mMatchingCacheNodes.put(matchingCacheKey, ri);
|
||||
}
|
||||
mMatchingCacheNodes.put(matchingCacheKey, renderInstructions);
|
||||
}
|
||||
|
||||
if (renderInstructions != null) {
|
||||
for (int i = 0, n = renderInstructions.length; i < n; i++)
|
||||
renderInstructions[i].renderNode(renderCallback, tags);
|
||||
}
|
||||
if (ri.list != null)
|
||||
render(renderCallback, ri.list, tags);
|
||||
|
||||
return renderInstructions;
|
||||
return ri.list;
|
||||
|
||||
}
|
||||
|
||||
// private int missCnt = 0;
|
||||
// private int hitCnt = 0;
|
||||
private int missCnt = 0;
|
||||
private int hitCnt = 0;
|
||||
private MatchingCacheKey mCacheKey = new MatchingCacheKey();
|
||||
private ArrayList<RenderInstruction> mMatchingList = new ArrayList<RenderInstruction>(4);
|
||||
|
||||
/**
|
||||
* Matches a way with the given parameters against this RenderTheme.
|
||||
@ -196,9 +261,13 @@ public class RenderTheme {
|
||||
public synchronized RenderInstruction[] matchWay(IRenderCallback renderCallback,
|
||||
Tag[] tags, byte zoomLevel, boolean closed, boolean render) {
|
||||
|
||||
RenderInstruction[] renderInstructions = null;
|
||||
// list of renderinsctruction items in cache
|
||||
RenderInstructionItem ris = null;
|
||||
|
||||
LRUCache<MatchingCacheKey, RenderInstruction[]> matchingCache;
|
||||
// the item matching tags and zoomlevel
|
||||
RenderInstructionItem ri = null;
|
||||
|
||||
LRUCache<MatchingCacheKey, RenderInstructionItem> matchingCache;
|
||||
MatchingCacheKey matchingCacheKey;
|
||||
|
||||
if (closed) {
|
||||
@ -206,38 +275,87 @@ public class RenderTheme {
|
||||
} else {
|
||||
matchingCache = mMatchingCacheWay;
|
||||
}
|
||||
int zoomMask = 1 << zoomLevel;
|
||||
|
||||
mCacheKey.set(tags, zoomLevel);
|
||||
renderInstructions = matchingCache.get(mCacheKey);
|
||||
mCacheKey.set(tags);
|
||||
ris = matchingCache.get(mCacheKey);
|
||||
|
||||
if (renderInstructions != null) {
|
||||
// Log.d("RenderTheme", hitCnt++ + "Cache Hit");
|
||||
} else if (!matchingCache.containsKey(mCacheKey)) {
|
||||
matchingCacheKey = new MatchingCacheKey(mCacheKey);
|
||||
for (ri = ris; ri != null; ri = ri.next)
|
||||
if ((ri.zoom & zoomMask) != 0)
|
||||
// cache hit
|
||||
break;
|
||||
|
||||
if (ri == null) {
|
||||
// cache miss
|
||||
// Log.d("RenderTheme", missCnt++ + " Cache Miss");
|
||||
//Log.d(TAG, missCnt++ + " / " + hitCnt + " Cache Miss");
|
||||
|
||||
int c = (closed ? Closed.YES : Closed.NO);
|
||||
List<RenderInstruction> matchingList = new ArrayList<RenderInstruction>(4);
|
||||
for (int i = 0, n = mRulesList.size(); i < n; ++i) {
|
||||
mRulesList.get(i).matchWay(renderCallback, tags, zoomLevel, c,
|
||||
matchingList);
|
||||
List<RenderInstruction> matches = mMatchingList;
|
||||
matches.clear();
|
||||
|
||||
for (int i = 0, n = mRulesList.size(); i < n; ++i)
|
||||
mRulesList.get(i).matchWay(renderCallback, tags, zoomLevel, c, matches);
|
||||
|
||||
int size = matches.size();
|
||||
// check if same instructions are used in another level
|
||||
for (ri = ris; ri != null; ri = ri.next) {
|
||||
if (size == 0) {
|
||||
if (ri.list != null)
|
||||
continue;
|
||||
|
||||
// both matchinglists are empty
|
||||
break;
|
||||
}
|
||||
int size = matchingList.size();
|
||||
|
||||
if (ri.list == null)
|
||||
continue;
|
||||
|
||||
if (ri.list.length != size)
|
||||
continue;
|
||||
|
||||
int i = 0;
|
||||
for (RenderInstruction r : ri.list) {
|
||||
if (r != matches.get(i))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
if (i == size)
|
||||
// both matching lists contain the same items
|
||||
break;
|
||||
}
|
||||
|
||||
if (ri != null) {
|
||||
// we found a same matchting list on another zoomlevel
|
||||
ri.zoom |= zoomMask;
|
||||
//Log.d(TAG, " same instructions " + size + " " + Arrays.deepToString(tags));
|
||||
|
||||
} else {
|
||||
//Log.d(TAG, " new instructions " + size + " " + Arrays.deepToString(tags));
|
||||
|
||||
ri = new RenderInstructionItem();
|
||||
ri.zoom = zoomMask;
|
||||
|
||||
if (size > 0) {
|
||||
renderInstructions = new RenderInstruction[size];
|
||||
matchingList.toArray(renderInstructions);
|
||||
}
|
||||
matchingCache.put(matchingCacheKey, renderInstructions);
|
||||
ri.list = new RenderInstruction[size];
|
||||
matches.toArray(ri.list);
|
||||
}
|
||||
|
||||
if (render && renderInstructions != null) {
|
||||
for (int i = 0, n = renderInstructions.length; i < n; i++)
|
||||
renderInstructions[i].renderWay(renderCallback, tags);
|
||||
// attach this list to the one found for MatchingKey
|
||||
if (ris != null) {
|
||||
ris.next = ri;
|
||||
}
|
||||
else {
|
||||
matchingCacheKey = new MatchingCacheKey(mCacheKey);
|
||||
matchingCache.put(matchingCacheKey, ri);
|
||||
}
|
||||
}
|
||||
|
||||
return renderInstructions;
|
||||
}
|
||||
if (render && ri.list != null) {
|
||||
for (int i = 0, n = ri.list.length; i < n; i++)
|
||||
ri.list[i].renderWay(renderCallback, tags);
|
||||
}
|
||||
return ri.list;
|
||||
}
|
||||
|
||||
void addRule(Rule rule) {
|
||||
|
@ -173,8 +173,7 @@ abstract class Rule {
|
||||
final int mElement;
|
||||
final int mClosed;
|
||||
|
||||
Rule(int element, int closed, byte zoomMin,
|
||||
byte zoomMax) {
|
||||
Rule(int element, int closed, byte zoomMin, byte zoomMax) {
|
||||
|
||||
mClosed = closed;
|
||||
mElement = element;
|
||||
@ -193,13 +192,17 @@ abstract class Rule {
|
||||
mSubRules.add(rule);
|
||||
}
|
||||
|
||||
abstract boolean matchesNode(Tag[] tags, byte zoomLevel);
|
||||
abstract boolean matchesNode(Tag[] tags);
|
||||
|
||||
abstract boolean matchesWay(Tag[] tags, byte zoomLevel, int closed);
|
||||
abstract boolean matchesWay(Tag[] tags);
|
||||
|
||||
void matchNode(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel,
|
||||
List<RenderInstruction> matchingList) {
|
||||
if (matchesNode(tags, zoomLevel)) {
|
||||
if ((mElement != Element.WAY)
|
||||
&& mZoomMin <= zoomLevel
|
||||
&& mZoomMax >= zoomLevel
|
||||
&& matchesNode(tags)) {
|
||||
|
||||
for (int i = 0, n = mRenderInstructionArray.length; i < n; i++)
|
||||
matchingList.add(mRenderInstructionArray[i]);
|
||||
|
||||
@ -210,13 +213,19 @@ abstract class Rule {
|
||||
}
|
||||
|
||||
void matchWay(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel,
|
||||
int closed,
|
||||
List<RenderInstruction> matchingList) {
|
||||
int closed, List<RenderInstruction> matchingList) {
|
||||
|
||||
if (matchesWay(tags, zoomLevel, closed)) {
|
||||
if ((mElement != Element.NODE)
|
||||
&& mZoomMin <= zoomLevel
|
||||
&& mZoomMax >= zoomLevel
|
||||
&& (mClosed == closed || mClosed == Closed.ANY)
|
||||
&& (matchesWay(tags))) {
|
||||
|
||||
// add instructions for this rule
|
||||
for (int i = 0, n = mRenderInstructionArray.length; i < n; i++)
|
||||
matchingList.add(mRenderInstructionArray[i]);
|
||||
|
||||
// check subrules
|
||||
for (int i = 0, n = mSubRuleArray.length; i < n; i++)
|
||||
mSubRuleArray[i].matchWay(renderCallback, tags, zoomLevel, closed,
|
||||
matchingList);
|
||||
@ -230,15 +239,10 @@ abstract class Rule {
|
||||
|
||||
mRenderInstructionArray = new RenderInstruction[mRenderInstructions.size()];
|
||||
mRenderInstructions.toArray(mRenderInstructionArray);
|
||||
// for (int i = 0, n = mRenderInstructions.size(); i < n; i++)
|
||||
// mRenderInstructionArray[i] = mRenderInstructions.get(i);
|
||||
|
||||
mSubRuleArray = new Rule[mSubRules.size()];
|
||||
mSubRules.toArray(mSubRuleArray);
|
||||
|
||||
// for (int i = 0, n = mSubRules.size(); i < n; i++)
|
||||
// mSubRuleArray[i] = mSubRules.get(i);
|
||||
|
||||
mRenderInstructions.clear();
|
||||
mRenderInstructions = null;
|
||||
mSubRules.clear();
|
||||
|
@ -17,7 +17,6 @@ package org.oscim.theme;
|
||||
import java.util.Stack;
|
||||
|
||||
final class RuleOptimizer {
|
||||
// private static final Logger LOG = Logger.getLogger(RuleOptimizer.class.getName());
|
||||
|
||||
private static AttributeMatcher optimizeKeyMatcher(AttributeMatcher attributeMatcher,
|
||||
Stack<Rule> ruleStack) {
|
||||
@ -26,7 +25,7 @@ final class RuleOptimizer {
|
||||
PositiveRule positiveRule = (PositiveRule) ruleStack.get(i);
|
||||
if (positiveRule.mKeyMatcher != null
|
||||
&& positiveRule.mKeyMatcher.isCoveredBy(attributeMatcher)) {
|
||||
return null; // AnyMatcher.getInstance();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,7 +41,7 @@ final class RuleOptimizer {
|
||||
|
||||
if (positiveRule.mValueMatcher != null
|
||||
&& positiveRule.mValueMatcher.isCoveredBy(attributeMatcher)) {
|
||||
return null; // AnyMatcher.getInstance();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user