From e76b0a374064c0a1095dc813a0d1b48fbd47ca28 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Sun, 19 Jan 2014 22:01:51 +0100 Subject: [PATCH] refactor TextRenderer, sync MapTile in Label-Thread --- .../tile/vector/labeling/LabelLayer.java | 28 +- .../tile/vector/labeling/TextRenderer.java | 403 ++++++++---------- .../org/oscim/utils/async/ContinuousTask.java | 91 ++++ 3 files changed, 284 insertions(+), 238 deletions(-) create mode 100644 vtm/src/org/oscim/utils/async/ContinuousTask.java diff --git a/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java b/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java index eb3ecaac..db6b189e 100644 --- a/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java +++ b/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java @@ -28,7 +28,7 @@ public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateLi static final Logger log = LoggerFactory.getLogger(LabelLayer.class); private final TextRenderer mTextRenderer; - private int multi; + //private int multi; public LabelLayer(Map map, TileRenderer tileRenderLayer) { super(map); @@ -49,19 +49,19 @@ public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateLi @Override public void onMotionEvent(MotionEvent e) { - int action = e.getAction() & MotionEvent.ACTION_MASK; - if (action == MotionEvent.ACTION_POINTER_DOWN) { - multi++; - mTextRenderer.hold(true); - } else if (action == MotionEvent.ACTION_POINTER_UP) { - multi--; - if (multi == 0) - mTextRenderer.hold(false); - } else if (action == MotionEvent.ACTION_CANCEL) { - multi = 0; - log.debug("cancel " + multi); - mTextRenderer.hold(false); - } + // int action = e.getAction() & MotionEvent.ACTION_MASK; + // if (action == MotionEvent.ACTION_POINTER_DOWN) { + // multi++; + // mTextRenderer.hold(true); + // } else if (action == MotionEvent.ACTION_POINTER_UP) { + // multi--; + // if (multi == 0) + // mTextRenderer.hold(false); + // } else if (action == MotionEvent.ACTION_CANCEL) { + // multi = 0; + // log.debug("cancel " + multi); + // mTextRenderer.hold(false); + // } } @Override diff --git a/vtm/src/org/oscim/layers/tile/vector/labeling/TextRenderer.java b/vtm/src/org/oscim/layers/tile/vector/labeling/TextRenderer.java index a80383d2..4e4278f5 100644 --- a/vtm/src/org/oscim/layers/tile/vector/labeling/TextRenderer.java +++ b/vtm/src/org/oscim/layers/tile/vector/labeling/TextRenderer.java @@ -51,6 +51,7 @@ import org.oscim.tiling.TileRenderer; import org.oscim.tiling.TileSet; import org.oscim.utils.FastMath; import org.oscim.utils.OBB2D; +import org.oscim.utils.async.ContinuousTask; import org.oscim.utils.pool.Pool; class TextRenderer extends ElementRenderer { @@ -143,6 +144,8 @@ class TextRenderer extends ElementRenderer { //mActiveTiles = new HashMap(); mRelabelCnt = 0; + + mLabelTask = new LabelTask(map); } // remove Label l from mLabels and return l.next @@ -274,6 +277,108 @@ class TextRenderer extends ElementRenderer { return dx; } + private Label updateWayLabels(MapTile t, Label l, float dx, float dy, double scale, + ElementLayers dbg) { + + for (TextItem ti = t.labels; ti != null; ti = ti.next) { + if (ti.text.caption) + continue; + + // acquire a TextItem to add to TextLayer + if (l == null) + l = getLabel(); + + // check if path at current scale is long enough for text + if (dbg == null && ti.width > ti.length * scale) + continue; + + l.clone(ti); + l.move(ti, dx, dy, (float) scale); + + // set line endpoints relative to view to be able to + // check intersections with label from other tiles + float w = (ti.x2 - ti.x1) / 2f; + float h = (ti.y2 - ti.y1) / 2f; + l.bbox = null; + l.x1 = l.x - w; + l.y1 = l.y - h; + l.x2 = l.x + w; + l.y2 = l.y + h; + + if (!wayIsVisible(l)) + continue; + + byte overlaps = -1; + + if (l.bbox == null) + l.bbox = new OBB2D(l.x, l.y, l.x1, l.y1, + l.width + MIN_WAY_DIST, + l.text.fontHeight + MIN_WAY_DIST); + else + l.bbox.set(l.x, l.y, l.x1, l.y1, + l.width + MIN_WAY_DIST, + l.text.fontHeight + MIN_WAY_DIST); + + if (dbg == null || ti.width < ti.length * scale) + overlaps = checkOverlap(l); + + if (dbg != null) + Debug.addDebugBox(dbg, l, ti, overlaps, false, (float) scale); + + if (overlaps == 0) { + addLabel(l); + l.item = TextItem.copy(ti); + l.tile = t; + l.active = mRelabelCnt; + l = null; + } + } + return l; + } + + private Label updateNodeLabels(MapTile t, Label l, float dx, float dy, double scale, float cos, + float sin) { + O: for (TextItem ti = t.labels; ti != null; ti = ti.next) { + if (!ti.text.caption) + continue; + + // acquire a TextItem to add to TextLayer + if (l == null) + l = getLabel(); + + l.clone(ti); + l.move(ti, dx, dy, (float) scale); + if (!nodeIsVisible(l)) + continue; + + if (l.bbox == null) + l.bbox = new OBB2D(); + + l.bbox.setNormalized(l.x, l.y, cos, -sin, + l.width + MIN_CAPTION_DIST, + l.text.fontHeight + MIN_CAPTION_DIST, + l.text.dy); + + for (Label lp = mLabels; lp != null;) { + if (l.bbox.overlaps(lp.bbox)) { + if (l.text.priority < lp.text.priority) { + lp = removeLabel(lp); + continue; + } + continue O; + } + lp = (Label) lp.next; + } + + addLabel(l); + l.item = TextItem.copy(ti); + l.tile = t; + l.active = mRelabelCnt; + l = null; + } + return l; + } + boolean updateLabels() { // nextLayer is not loaded yet if (mNextLayer.ready) @@ -400,118 +505,30 @@ class TextRenderer extends ElementRenderer { /* add way labels */ for (int i = 0, n = mTileSet.cnt; i < n; i++) { MapTile t = tiles[i]; - if (!t.state(MapTile.STATE_READY)) - continue; - - float dx = (float) (t.tileX * Tile.SIZE - tileX); - float dy = (float) (t.tileY * Tile.SIZE - tileY); - dx = flipLongitude(dx, maxx); - - for (TextItem ti = t.labels; ti != null; ti = ti.next) { - - if (ti.text.caption) + synchronized (t) { + if (!t.state(MapTile.STATE_READY)) continue; - // acquire a TextItem to add to TextLayer - if (l == null) - l = getLabel(); + float dx = (float) (t.tileX * Tile.SIZE - tileX); + float dy = (float) (t.tileY * Tile.SIZE - tileY); + dx = flipLongitude(dx, maxx); - // check if path at current scale is long enough for text - if (dbg == null && ti.width > ti.length * scale) - continue; - - l.clone(ti); - l.move(ti, dx, dy, (float) scale); - - // set line endpoints relative to view to be able to - // check intersections with label from other tiles - float w = (ti.x2 - ti.x1) / 2f; - float h = (ti.y2 - ti.y1) / 2f; - l.bbox = null; - l.x1 = l.x - w; - l.y1 = l.y - h; - l.x2 = l.x + w; - l.y2 = l.y + h; - - if (!wayIsVisible(l)) - continue; - - byte overlaps = -1; - - if (l.bbox == null) - l.bbox = new OBB2D(l.x, l.y, l.x1, l.y1, - l.width + MIN_WAY_DIST, - l.text.fontHeight + MIN_WAY_DIST); - else - l.bbox.set(l.x, l.y, l.x1, l.y1, - l.width + MIN_WAY_DIST, - l.text.fontHeight + MIN_WAY_DIST); - - if (dbg == null || ti.width < ti.length * scale) - overlaps = checkOverlap(l); - - if (dbg != null) - Debug.addDebugBox(dbg, l, ti, overlaps, false, (float) scale); - - if (overlaps == 0) { - addLabel(l); - l.item = TextItem.copy(ti); - l.tile = t; - l.active = mRelabelCnt; - l = null; - } + l = updateWayLabels(t, l, dx, dy, scale, dbg); } } /* add caption */ for (int i = 0, n = mTileSet.cnt; i < n; i++) { MapTile t = tiles[i]; - if (!t.state(MapTile.STATE_READY)) - continue; - - float dx = (float) (t.tileX * Tile.SIZE - tileX); - float dy = (float) (t.tileY * Tile.SIZE - tileY); - dx = flipLongitude(dx, maxx); - - O: for (TextItem ti = t.labels; ti != null; ti = ti.next) { - if (!ti.text.caption) + synchronized (t) { + if (!t.state(MapTile.STATE_READY)) continue; - // acquire a TextItem to add to TextLayer - if (l == null) - l = getLabel(); + float dx = (float) (t.tileX * Tile.SIZE - tileX); + float dy = (float) (t.tileY * Tile.SIZE - tileY); + dx = flipLongitude(dx, maxx); - l.clone(ti); - l.move(ti, dx, dy, (float) scale); - if (!nodeIsVisible(l)) - continue; - - //l.setAxisAlignedBBox(); - - if (l.bbox == null) - l.bbox = new OBB2D(); - - l.bbox.setNormalized(l.x, l.y, cos, -sin, - l.width + MIN_CAPTION_DIST, - l.text.fontHeight + MIN_CAPTION_DIST, - l.text.dy); - - for (Label lp = mLabels; lp != null;) { - if (l.bbox.overlaps(lp.bbox)) { - if (l.text.priority < lp.text.priority) { - lp = removeLabel(lp); - continue; - } - continue O; - } - lp = (Label) lp.next; - } - - addLabel(l); - l.item = TextItem.copy(ti); - l.tile = t; - l.active = mRelabelCnt; - l = null; + l = updateNodeLabels(t, l, dx, dy, scale, cos, sin); } } @@ -541,25 +558,27 @@ class TextRenderer extends ElementRenderer { for (int i = 0, n = mTileSet.cnt; i < n; i++) { MapTile t = tiles[i]; - if (!t.state(MapTile.STATE_READY)) - continue; - - float dx = (float) (t.tileX * Tile.SIZE - tileX); - float dy = (float) (t.tileY * Tile.SIZE - tileY); - dx = flipLongitude(dx, maxx); - - for (SymbolItem ti = t.symbols; ti != null; ti = ti.next) { - if (ti.texRegion == null) + synchronized (t) { + if (!t.state(MapTile.STATE_READY)) continue; - SymbolItem s = SymbolItem.pool.get(); + float dx = (float) (t.tileX * Tile.SIZE - tileX); + float dy = (float) (t.tileY * Tile.SIZE - tileY); + dx = flipLongitude(dx, maxx); - s.texRegion = ti.texRegion; - s.x = (float) ((dx + ti.x) * scale); - s.y = (float) ((dy + ti.y) * scale); - s.billboard = true; + for (SymbolItem ti = t.symbols; ti != null; ti = ti.next) { + if (ti.texRegion == null) + continue; - sl.addSymbol(s); + SymbolItem s = SymbolItem.pool.get(); + + s.texRegion = ti.texRegion; + s.x = (float) ((dx + ti.x) * scale); + s.y = (float) ((dy + ti.y) * scale); + s.billboard = true; + + sl.addSymbol(s); + } } } @@ -588,11 +607,6 @@ class TextRenderer extends ElementRenderer { public synchronized void update(MapPosition pos, boolean changed, Matrices matrices) { - //if (System.currentTimeMillis() - lastDraw > 1000){ - // updateLabels(); - // lastDraw = System.currentTimeMillis(); - // - //} if (mNextLayer.ready) { // exchange current with next layers TextureLayers tmp = mCurLayer; @@ -615,127 +629,68 @@ class TextRenderer extends ElementRenderer { compile(); } - if (mRequestClear) - cleanup(); - - //if (!mHolding) - postLabelTask(); - } - - /* private */LabelTask mLabelTask; - /* private */long mLastRun; - /* private */boolean mRequestRun; - /* private */boolean mRequestClear; - /* private */boolean mRelabel; - - class LabelTask implements Runnable { - private boolean isCancelled; - - @Override - public void run() { - boolean labelsChanged = false; - if (isCancelled) { - return; - } - long now = System.currentTimeMillis(); - //log.debug("relabel after " + (now - mLastRun)); - mLastRun = now; - - labelsChanged = updateLabels(); - - if (!isCancelled && labelsChanged) - mMap.render(); - - mLabelTask = null; - mRequestRun = false; - isCancelled = false; - - if (mRelabel) { - mRelabel = false; - postLabelTask(); - } - } - - public void cancel() { - isCancelled = true; - } - } - - /* private */void cleanup() { - mLabels = (Label) mPool.releaseAll(mLabels); - mTileSet.releaseTiles(); - mLabelTask = null; - mRequestClear = false; - } - - private final Runnable mLabelUpdate = new Runnable() { - @Override - public void run() { - if (mLabelTask == null) { - mLabelTask = new LabelTask(); - mMap.addTask(mLabelTask); - } - } - }; - - /* private */void postLabelTask() { - synchronized (mLabelUpdate) { - if (mRequestRun) { - mRelabel = true; - } else { - mRequestRun = true; - long delay = (mLastRun + MAX_RELABEL_DELAY) - System.currentTimeMillis(); - //log.debug("relabel in: " + delay); - mMap.postDelayed(mLabelUpdate, Math.max(delay, 0)); - } - } + mLabelTask.submit((mLastRun + MAX_RELABEL_DELAY) - System.currentTimeMillis()); } @Override public synchronized void render(MapPosition pos, Matrices m) { - - layers.vbo.bind(); GLState.test(false, false); float scale = (float) (mMapPosition.scale / pos.scale); setMatrix(pos, m, true); - if (layers.baseLayers != null) { - for (RenderElement l = layers.baseLayers; l != null;) { - if (l.type == RenderElement.POLYGON) { - l = PolygonLayer.Renderer.draw(pos, l, m, true, 1, false); - } else { - float div = scale * (float) (pos.scale / (1 << pos.zoomLevel)); - l = LineLayer.Renderer.draw(layers, l, pos, m, div, 0); + synchronized (layers) { + layers.vbo.bind(); + if (layers.baseLayers != null) { + for (RenderElement l = layers.baseLayers; l != null;) { + if (l.type == RenderElement.POLYGON) { + l = PolygonLayer.Renderer.draw(pos, l, m, true, 1, false); + } else { + float div = scale * (float) (pos.scale / (1 << pos.zoomLevel)); + l = LineLayer.Renderer.draw(layers, l, pos, m, div, 0); + } } } + + setMatrix(pos, m, false); + + for (RenderElement l = layers.textureLayers; l != null;) + l = TextureLayer.Renderer.draw(l, scale, m); } - - setMatrix(pos, m, false); - - for (RenderElement l = layers.textureLayers; l != null;) - l = TextureLayer.Renderer.draw(l, scale, m); } - //private boolean mHolding; + final class LabelTask extends ContinuousTask { - /** - * @param enable layer updates - */ - public synchronized void hold(boolean enable) { - // mHolding = enable; - // if (!enable) - // runLabelTask(); + public LabelTask(Map map) { + super(map, 10); + } + + @Override + public void doWork() { + + if (updateLabels()) + mMap.render(); + + mLastRun = System.currentTimeMillis(); + } + + @Override + public void cleanup() { + clearLabelsInternal(); + } } - public synchronized void clearLabels() { - if (mRequestRun) { - mRequestClear = true; - //mRelabel = true; - } else { - cleanup(); - //postLabelTask(); - } + private final LabelTask mLabelTask; + + /* private */long mLastRun; + + /* private */void clearLabelsInternal() { + mLabels = (Label) mPool.releaseAll(mLabels); + mTileSet.releaseTiles(); + } + + public void clearLabels() { + mLabelTask.cancel(); } } diff --git a/vtm/src/org/oscim/utils/async/ContinuousTask.java b/vtm/src/org/oscim/utils/async/ContinuousTask.java new file mode 100644 index 00000000..a8584b53 --- /dev/null +++ b/vtm/src/org/oscim/utils/async/ContinuousTask.java @@ -0,0 +1,91 @@ +package org.oscim.utils.async; + +import org.oscim.map.Map; + +public abstract class ContinuousTask implements Runnable { + private final Map mMap; + + protected boolean mRunning; + protected boolean mWait; + protected boolean mCancel; + protected boolean mDelayed; + + protected long mMinDelay; + + public ContinuousTask(Map map, long minDelay) { + mMap = map; + mMinDelay = minDelay; + } + + @Override + public void run() { + + synchronized (this) { + //System.out.println("run " + mRunning + " " + // + mCancel + " " + mDelayed + " " + mWait); + + if (mCancel) { + mCancel = false; + mRunning = false; + mDelayed = false; + mWait = false; + cleanup(); + return; + } + if (mDelayed) { + // entered on main-loop + mDelayed = false; + mWait = false; + // unset running temporarily + mRunning = false; + submit(0); + return; + } + } + + doWork(); + + synchronized (this) { + mRunning = false; + + if (mCancel) + cleanup(); + else if (mWait) + submit(mMinDelay); + + mCancel = false; + mWait = false; + } + } + + public abstract void doWork(); + + public abstract void cleanup(); + + public synchronized void submit(long delay) { + //System.out.println("submit " + mRunning + " " + mCancel + " " + delay); + + if (mRunning) { + mWait = true; + return; + } + mRunning = true; + if (delay <= 0) { + mMap.addTask(this); + return; + } + + mDelayed = true; + mMap.postDelayed(this, delay); + + } + + public synchronized void cancel() { + if (mRunning) { + mCancel = true; + return; + } + + cleanup(); + } +}