From 8626aa2f53621b2f74f1bc421653e3aa8cd69a26 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Fri, 24 Oct 2014 22:20:11 +0200 Subject: [PATCH 01/20] api: no more sync'ed Viewport methods - remove synchronized viewport methods: viewport MUST only be changed on main-thread - when calling Map.setPosition() from another thread the position will be applied on the next main-loop iteration --- .../src/org/oscim/android/AndroidMap.java | 60 +++++--- vtm-gdx/src/org/oscim/gdx/GdxMap.java | 77 +++++----- .../gdx/emu/org/oscim/utils/ThreadUtils.java | 16 ++ vtm/src/org/oscim/map/Animator.java | 140 ++++++++++-------- vtm/src/org/oscim/map/Map.java | 47 ++++-- vtm/src/org/oscim/map/ViewController.java | 58 +++++++- vtm/src/org/oscim/map/Viewport.java | 31 ++-- vtm/src/org/oscim/renderer/GLViewport.java | 5 +- vtm/src/org/oscim/renderer/MapRenderer.java | 23 ++- vtm/src/org/oscim/utils/ThreadUtils.java | 20 +++ 10 files changed, 320 insertions(+), 157 deletions(-) create mode 100644 vtm-web/src/org/oscim/gdx/emu/org/oscim/utils/ThreadUtils.java create mode 100644 vtm/src/org/oscim/utils/ThreadUtils.java diff --git a/vtm-android/src/org/oscim/android/AndroidMap.java b/vtm-android/src/org/oscim/android/AndroidMap.java index 4801da947..0a130a6f0 100644 --- a/vtm-android/src/org/oscim/android/AndroidMap.java +++ b/vtm-android/src/org/oscim/android/AndroidMap.java @@ -18,16 +18,20 @@ import org.oscim.android.gl.GLView; import org.oscim.map.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import android.widget.RelativeLayout.LayoutParams; public class AndroidMap extends Map { + static final Logger log = LoggerFactory.getLogger(AndroidMap.class); private final MapView mMapView; final GLView mGLView; - private volatile boolean mWaitRedraw; - private volatile boolean mPausing; + private boolean mRenderRequest; + private boolean mRenderWait; + private boolean mPausing; public AndroidMap(MapView mapView) { super(); @@ -52,14 +56,24 @@ public int getHeight() { return mMapView.getHeight(); } + private final Runnable mRedrawCb = new Runnable() { + @Override + public void run() { + prepareFrame(); + mGLView.requestRender(); + } + }; + @Override public void updateMap(boolean redraw) { - //if (redraw && !mClearMap && !mPausing) - // mGLView.requestRender(); + if (mPausing) + return; - if (!mWaitRedraw) { - mWaitRedraw = true; - mMapView.post(mRedrawRequest); + if (!mRenderRequest) { + mRenderRequest = true; + mMapView.post(mRedrawCb); + } else { + mRenderWait = true; } } @@ -68,25 +82,24 @@ public void render() { if (mPausing) return; - if (mClearMap) - updateMap(false); - else - mGLView.requestRender(); + updateMap(true); } - private final Runnable mRedrawRequest = new Runnable() { - @Override - public void run() { - redrawMapInternal(); - } - }; - - void redrawMapInternal() { - mWaitRedraw = false; - - updateLayers(); + @Override + public void beginFrame() { + } - mGLView.requestRender(); + @Override + public void doneFrame() { + mRenderRequest = false; + + if (mRenderWait) { + //log.debug("redraw"); + mRenderWait = false; + updateMap(true); + //prepareFrame(); + //mGLView.requestRender(); + } } @Override @@ -100,6 +113,7 @@ public boolean postDelayed(Runnable action, long delay) { } public void pause(boolean pause) { + log.debug("pause... {}", pause); mPausing = pause; } } diff --git a/vtm-gdx/src/org/oscim/gdx/GdxMap.java b/vtm-gdx/src/org/oscim/gdx/GdxMap.java index dda616199..20fa383a5 100644 --- a/vtm-gdx/src/org/oscim/gdx/GdxMap.java +++ b/vtm-gdx/src/org/oscim/gdx/GdxMap.java @@ -38,15 +38,12 @@ public abstract class GdxMap implements ApplicationListener { final static Logger log = LoggerFactory.getLogger(GdxMap.class); - protected final Map mMap; - private final MapAdapter mMapAdapter; + protected Map mMap; VectorTileLayer mMapLayer; - private final MapRenderer mMapRenderer; + private MapRenderer mMapRenderer; public GdxMap() { - mMap = mMapAdapter = new MapAdapter(); - mMapRenderer = new MapRenderer(mMap); } protected void initDefaultLayers(TileSource tileSource, boolean tileGrid, boolean labels, @@ -70,6 +67,8 @@ protected void initDefaultLayers(TileSource tileSource, boolean tileGrid, boolea @Override public void create() { + mMap = new MapAdapter(); + mMapRenderer = new MapRenderer(mMap); Gdx.graphics.setContinuousRendering(false); Gdx.app.setLogLevel(Application.LOG_DEBUG); @@ -101,9 +100,13 @@ public void dispose() { } + /* private */boolean mRenderWait; + /* private */boolean mRenderRequest; + /* private */boolean mUpdateRequest; + @Override public void render() { - if (!mMapAdapter.needsRedraw()) + if (!mRenderRequest) return; mMapRenderer.onDrawFrame(); @@ -132,8 +135,7 @@ public Map getMap() { return mMap; } - static class MapAdapter extends Map { - boolean mRenderRequest; + class MapAdapter extends Map { @Override public int getWidth() { @@ -145,21 +147,35 @@ public int getHeight() { return Gdx.graphics.getHeight(); } + private final Runnable mRedrawCb = new Runnable() { + @Override + public void run() { + prepareFrame(); + Gdx.graphics.requestRendering(); + } + }; + @Override public void updateMap(boolean forceRender) { - if (!mWaitRedraw) { - mWaitRedraw = true; - Gdx.app.postRunnable(mRedrawRequest); + + if (!mRenderRequest) { + mRenderRequest = true; + Gdx.app.postRunnable(mRedrawCb); + } else { + mRenderWait = true; } } @Override public void render() { + //updateMap(true); + mRenderRequest = true; if (mClearMap) updateMap(false); - else + else { Gdx.graphics.requestRendering(); + } } @Override @@ -179,35 +195,22 @@ public void run() { return true; } - /** - * Update all Layers on Main thread. - * - * @param forceRedraw - * also render frame FIXME (does nothing atm) - */ - private void redrawMapInternal(boolean forceRedraw) { - updateLayers(); - - mRenderRequest = true; - Gdx.graphics.requestRendering(); + @Override + public void beginFrame() { } - /* private */boolean mWaitRedraw; - private final Runnable mRedrawRequest = new Runnable() { - @Override - public void run() { - mWaitRedraw = false; - redrawMapInternal(false); - } - }; + @Override + public void doneFrame() { + mRenderRequest = false; - public boolean needsRedraw() { - if (!mRenderRequest) - return false; + if (mRenderWait) { + mRenderWait = false; - mRenderRequest = false; - return true; - } + mRenderRequest = true; + prepareFrame(); + Gdx.graphics.requestRendering(); + } + } } } diff --git a/vtm-web/src/org/oscim/gdx/emu/org/oscim/utils/ThreadUtils.java b/vtm-web/src/org/oscim/gdx/emu/org/oscim/utils/ThreadUtils.java new file mode 100644 index 000000000..56ee2076f --- /dev/null +++ b/vtm-web/src/org/oscim/gdx/emu/org/oscim/utils/ThreadUtils.java @@ -0,0 +1,16 @@ +package org.oscim.utils; + +public class ThreadUtils { + + public static void assertMainThread() { + } + + public static boolean isMainThread() { + return true; + } + + public static void init() { + + } + +} diff --git a/vtm/src/org/oscim/map/Animator.java b/vtm/src/org/oscim/map/Animator.java index 0bf182b2c..40b5ec04a 100644 --- a/vtm/src/org/oscim/map/Animator.java +++ b/vtm/src/org/oscim/map/Animator.java @@ -27,14 +27,14 @@ import org.oscim.core.Point; import org.oscim.core.Tile; import org.oscim.renderer.MapRenderer; +import org.oscim.utils.ThreadUtils; +import org.oscim.utils.async.Task; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Animator { static final Logger log = LoggerFactory.getLogger(Animator.class); - //static final Logger log = LoggerFactory.getLogger(MapAnimator.class); - private final static int ANIM_NONE = 0; private final static int ANIM_MOVE = 1 << 0; private final static int ANIM_SCALE = 1 << 1; @@ -62,6 +62,8 @@ public Animator(Map map) { } public synchronized void animateTo(long duration, BoundingBox bbox) { + ThreadUtils.assertMainThread(); + mMap.getMapPosition(mStartPos); /* TODO for large distance first scale out, then in * calculate the maximum scale at which the BoundingBox @@ -89,12 +91,22 @@ public synchronized void animateTo(long duration, BoundingBox bbox) { animStart(duration, ANIM_MOVE | ANIM_SCALE | ANIM_ROTATE | ANIM_TILT); } - public synchronized void animateTo(BoundingBox bbox) { + public void animateTo(BoundingBox bbox) { animateTo(1000, bbox); } - public synchronized void animateTo(long duration, GeoPoint geoPoint, + /** + * Animate to GeoPoint + * + * @param duration in ms + * @param geoPoint + * @param scale + * @param relative alter scale relative to current scale + */ + public void animateTo(long duration, GeoPoint geoPoint, double scale, boolean relative) { + ThreadUtils.assertMainThread(); + mMap.getMapPosition(mStartPos); if (relative) @@ -110,11 +122,13 @@ public synchronized void animateTo(long duration, GeoPoint geoPoint, animStart(duration, ANIM_MOVE | ANIM_SCALE); } - public synchronized void animateTo(GeoPoint p) { + public void animateTo(GeoPoint p) { animateTo(500, p, 1, true); } - public synchronized void animateTo(long duration, MapPosition pos) { + public void animateTo(long duration, MapPosition pos) { + ThreadUtils.assertMainThread(); + mMap.getMapPosition(mStartPos); pos.scale = clamp(pos.scale, @@ -130,8 +144,10 @@ public synchronized void animateTo(long duration, MapPosition pos) { animStart(duration, ANIM_MOVE | ANIM_SCALE | ANIM_ROTATE | ANIM_TILT); } - public synchronized void animateZoom(long duration, double scaleBy, + public void animateZoom(long duration, double scaleBy, float pivotX, float pivotY) { + ThreadUtils.assertMainThread(); + mMap.getMapPosition(mCurPos); if (mState == ANIM_SCALE) @@ -150,9 +166,11 @@ public synchronized void animateZoom(long duration, double scaleBy, animStart(duration, ANIM_SCALE); } - public synchronized void animateFling(float velocityX, float velocityY, + public void animateFling(float velocityX, float velocityY, int xmin, int xmax, int ymin, int ymax) { + ThreadUtils.assertMainThread(); + if (velocityX * velocityX + velocityY * velocityY < 2048) return; @@ -177,87 +195,90 @@ public synchronized void animateFling(float velocityX, float velocityY, } private void animStart(float duration, int state) { - mState = state; mCurPos.copy(mStartPos); + mState = state; mDuration = duration; mAnimEnd = System.currentTimeMillis() + (long) duration; mMap.render(); } - private void animCancel() { - mState = ANIM_NONE; - mPivot.x = 0; - mPivot.y = 0; - } - /** * called by MapRenderer at begin of each frame. */ - public synchronized void updateAnimation() { + void updateAnimation() { if (mState == ANIM_NONE) return; long millisLeft = mAnimEnd - MapRenderer.frametime; - boolean changed = false; + //boolean changed = false; ViewController v = mMap.viewport(); - synchronized (v) { - /* cancel animation when position was changed since last - * update, i.e. when it was modified outside the animator. */ - if (v.getMapPosition(mCurPos)) { - animCancel(); - return; - } - - float adv = clamp(1.0f - millisLeft / mDuration, 0, 1); + //synchronized (v) { + /* cancel animation when position was changed since last + * update, i.e. when it was modified outside the animator. */ + if (v.getMapPosition(mCurPos)) { + log.debug("cancel anim - changed"); + cancel(); + return; + } - double scaleAdv = 1; - if ((mState & ANIM_SCALE) != 0) { - scaleAdv = doScale(v, adv); - } + float adv = clamp(1.0f - millisLeft / mDuration, 0, 1); - if ((mState & ANIM_MOVE) != 0) { - v.moveTo(mStartPos.x + mDeltaPos.x * (adv / scaleAdv), - mStartPos.y + mDeltaPos.y * (adv / scaleAdv)); - } + double scaleAdv = 1; + if ((mState & ANIM_SCALE) != 0) { + scaleAdv = doScale(v, adv); + } - if ((mState & ANIM_FLING) != 0) { - adv = (float) Math.sqrt(adv); - double dx = mVelocity.x * adv; - double dy = mVelocity.y * adv; - if ((dx - mScroll.x) != 0 || (dy - mScroll.y) != 0) { - v.moveMap((float) (dx - mScroll.x), - (float) (dy - mScroll.y)); - mScroll.x = dx; - mScroll.y = dy; - } - } - if ((mState & ANIM_ROTATE) != 0) { - v.setRotation(mStartPos.bearing + mDeltaPos.bearing * adv); - } + if ((mState & ANIM_MOVE) != 0) { + v.moveTo(mStartPos.x + mDeltaPos.x * (adv / scaleAdv), + mStartPos.y + mDeltaPos.y * (adv / scaleAdv)); + } - if ((mState & ANIM_TILT) != 0) { - v.setTilt(mStartPos.tilt + mDeltaPos.tilt * adv); + if ((mState & ANIM_FLING) != 0) { + adv = (float) Math.sqrt(adv); + double dx = mVelocity.x * adv; + double dy = mVelocity.y * adv; + if ((dx - mScroll.x) != 0 || (dy - mScroll.y) != 0) { + v.moveMap((float) (dx - mScroll.x), + (float) (dy - mScroll.y)); + mScroll.x = dx; + mScroll.y = dy; } + } + if ((mState & ANIM_ROTATE) != 0) { + v.setRotation(mStartPos.bearing + mDeltaPos.bearing * adv); + } - if (millisLeft <= 0) - animCancel(); + if ((mState & ANIM_TILT) != 0) { + v.setTilt(mStartPos.tilt + mDeltaPos.tilt * adv); + } - /* remember current map position */ - changed = v.getMapPosition(mCurPos); + if (millisLeft <= 0) { + //log.debug("animate END"); + cancel(); } + /* remember current map position */ + final boolean changed = v.getMapPosition(mCurPos); + if (changed) { - /* render and inform layers that position has changed */ mMap.updateMap(true); } else { - /* just render next frame */ - mMap.render(); + mMap.postDelayed(updateTask, 10); } } + private Task updateTask = new Task() { + @Override + public int go(boolean canceled) { + if (!canceled) + updateAnimation(); + return Task.DONE; + } + }; + private double doScale(ViewController v, float adv) { double newScale = mStartPos.scale + mDeltaPos.scale * Math.sqrt(adv); @@ -267,7 +288,10 @@ private double doScale(ViewController v, float adv) { return newScale / (mStartPos.scale + mDeltaPos.scale); } - public synchronized void cancel() { + public void cancel() { + //ThreadUtils.assertMainThread(); mState = ANIM_NONE; + mPivot.x = 0; + mPivot.y = 0; } } diff --git a/vtm/src/org/oscim/map/Map.java b/vtm/src/org/oscim/map/Map.java index 8d0270997..8d2a15b0a 100644 --- a/vtm/src/org/oscim/map/Map.java +++ b/vtm/src/org/oscim/map/Map.java @@ -32,6 +32,7 @@ import org.oscim.theme.ThemeFile; import org.oscim.theme.ThemeLoader; import org.oscim.tiling.TileSource; +import org.oscim.utils.ThreadUtils; import org.oscim.utils.async.AsyncExecutor; import org.oscim.utils.async.TaskQueue; import org.slf4j.Logger; @@ -85,7 +86,7 @@ public interface InputListener extends EventListener { private final Layers mLayers; private final ViewController mViewport; - private final Animator mAnimator; + protected final Animator mAnimator; private final MapPosition mMapPosition; private final AsyncExecutor mAsyncExecutor; @@ -97,6 +98,8 @@ public interface InputListener extends EventListener { protected boolean mClearMap = true; public Map() { + ThreadUtils.init(); + mViewport = new ViewController(); mAnimator = new Animator(this); mLayers = new Layers(this); @@ -237,9 +240,19 @@ public void clearMap() { /** * Set {@link MapPosition} of {@link Viewport} and trigger a redraw. */ - public void setMapPosition(MapPosition mapPosition) { - mViewport.setMapPosition(mapPosition); - updateMap(true); + public void setMapPosition(final MapPosition mapPosition) { + if (!ThreadUtils.isMainThread()) + post(new Runnable() { + @Override + public void run() { + mViewport.setMapPosition(mapPosition); + updateMap(true); + } + }); + else { + mViewport.setMapPosition(mapPosition); + updateMap(true); + } } public void setMapPosition(double latitude, double longitude, double scale) { @@ -253,6 +266,10 @@ public void setMapPosition(double latitude, double longitude, double scale) { * @return true when MapPosition was updated (has changed) */ public boolean getMapPosition(MapPosition mapPosition) { + if (!ThreadUtils.isMainThread()) { + return mViewport.getSyncMapPosition(mapPosition); + } + return mViewport.getMapPosition(mapPosition); } @@ -289,14 +306,18 @@ public Animator animator() { } /** - * This function is run on main-loop before rendering a frame. - * Caution: Do not call directly! + * This function is run on main-thread before rendering a frame. + * + * For internal use only. Do not call! */ - protected void updateLayers() { - boolean changed = false; + protected void prepareFrame() { + ThreadUtils.assertMainThread(); + MapPosition pos = mMapPosition; - changed |= mViewport.getMapPosition(pos); + mAnimator.updateAnimation(); + + boolean changed = mViewport.getMapPosition(pos); if (mClearMap) events.fire(CLEAR_EVENT, pos); @@ -306,9 +327,17 @@ else if (changed) events.fire(UPDATE_EVENT, pos); mClearMap = false; + + mAnimator.updateAnimation(); + + mViewport.syncViewport(); } public boolean handleGesture(Gesture g, MotionEvent e) { return mLayers.handleGesture(g, e); } + + public abstract void beginFrame(); + + public abstract void doneFrame(); } diff --git a/vtm/src/org/oscim/map/ViewController.java b/vtm/src/org/oscim/map/ViewController.java index c7b9fabc9..df144b448 100644 --- a/vtm/src/org/oscim/map/ViewController.java +++ b/vtm/src/org/oscim/map/ViewController.java @@ -5,10 +5,13 @@ import org.oscim.core.Tile; import org.oscim.renderer.GLMatrix; import org.oscim.utils.FastMath; +import org.oscim.utils.ThreadUtils; public class ViewController extends Viewport { - public synchronized void setScreenSize(int width, int height) { + public void setScreenSize(int width, int height) { + ThreadUtils.assertMainThread(); + mHeight = height; mWidth = width; @@ -50,7 +53,9 @@ public synchronized void setScreenSize(int width, int height) { * @param mx the amount of pixels to move the map horizontally. * @param my the amount of pixels to move the map vertically. */ - public synchronized void moveMap(float mx, float my) { + public void moveMap(float mx, float my) { + ThreadUtils.assertMainThread(); + Point p = applyRotation(mx, my); double tileScale = mPos.scale * Tile.SIZE; moveTo(mPos.x - p.x / tileScale, mPos.y - p.y / tileScale); @@ -94,7 +99,9 @@ private Point applyRotation(double mx, double my) { * @param pivotY * @return true if scale was changed */ - public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) { + public boolean scaleMap(float scale, float pivotX, float pivotY) { + ThreadUtils.assertMainThread(); + // just sanitize input //scale = FastMath.clamp(scale, 0.5f, 2); if (scale < 0.000001) @@ -126,7 +133,8 @@ public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) { * @param pivotX * @param pivotY */ - public synchronized void rotateMap(double radians, float pivotX, float pivotY) { + public void rotateMap(double radians, float pivotX, float pivotY) { + ThreadUtils.assertMainThread(); double rsin = Math.sin(radians); double rcos = Math.cos(radians); @@ -139,7 +147,9 @@ public synchronized void rotateMap(double radians, float pivotX, float pivotY) { setRotation(mPos.bearing + Math.toDegrees(radians)); } - public synchronized void setRotation(double degree) { + public void setRotation(double degree) { + ThreadUtils.assertMainThread(); + while (degree > 180) degree -= 360; while (degree < -180) @@ -149,11 +159,15 @@ public synchronized void setRotation(double degree) { updateMatrices(); } - public synchronized boolean tiltMap(float move) { + public boolean tiltMap(float move) { + ThreadUtils.assertMainThread(); + return setTilt(mPos.tilt + move); } - public synchronized boolean setTilt(float tilt) { + public boolean setTilt(float tilt) { + ThreadUtils.assertMainThread(); + tilt = FastMath.clamp(tilt, 0, MAX_TILT); if (tilt == mPos.tilt) return false; @@ -162,7 +176,9 @@ public synchronized boolean setTilt(float tilt) { return true; } - public synchronized void setMapPosition(MapPosition mapPosition) { + public void setMapPosition(MapPosition mapPosition) { + ThreadUtils.assertMainThread(); + mPos.scale = FastMath.clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE); mPos.x = mapPosition.x; mPos.y = mapPosition.y; @@ -199,4 +215,30 @@ private void updateMatrices() { /* (AB)^-1 = B^-1*A^-1, invert projection */ mUnprojMatrix.multiplyMM(mTmpMatrix, mProjMatrixInverse); } + + public final Viewport mNextFrame = new Viewport(); + + /** synchronize on this object when doing multiple calls on it */ + public final Viewport getSyncViewport() { + return mNextFrame; + } + + void syncViewport() { + synchronized (mNextFrame) { + mNextFrame.copy(this); + } + } + + public boolean getSyncViewport(Viewport v) { + synchronized (mNextFrame) { + return v.copy(mNextFrame); + } + } + + public boolean getSyncMapPosition(MapPosition mapPosition) { + synchronized (mNextFrame) { + return mNextFrame.getMapPosition(mapPosition); + } + } + } diff --git a/vtm/src/org/oscim/map/Viewport.java b/vtm/src/org/oscim/map/Viewport.java index fd07da6d6..ac7b2cec7 100644 --- a/vtm/src/org/oscim/map/Viewport.java +++ b/vtm/src/org/oscim/map/Viewport.java @@ -87,7 +87,7 @@ protected Viewport() { * @return true iff current position is different from * passed position. */ - public synchronized boolean getMapPosition(MapPosition pos) { + public boolean getMapPosition(MapPosition pos) { boolean changed = (pos.scale != mPos.scale || pos.x != mPos.x @@ -114,7 +114,7 @@ public synchronized boolean getMapPosition(MapPosition pos) { * @param box float[8] will be set. * @param add increase extents of box */ - public synchronized void getMapExtents(float[] box, float add) { + public void getMapExtents(float[] box, float add) { float t = getDepth(1); float t2 = getDepth(-1); @@ -143,6 +143,8 @@ public synchronized void getMapExtents(float[] box, float add) { * Get Z-value of the map-plane for a point on screen - * calculate the intersection of a ray from camera origin * and the map plane + * TODO use + * www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf */ protected float getDepth(float y) { // origin is moved by VIEW_DISTANCE @@ -244,7 +246,7 @@ public synchronized void getBBox(Box box, int expand) { * @param y screen coordinate * @return the corresponding GeoPoint */ - public synchronized GeoPoint fromScreenPoint(float x, float y) { + public GeoPoint fromScreenPoint(float x, float y) { fromScreenPoint(x, y, mMovePoint); return new GeoPoint( MercatorProjection.toLatitude(mMovePoint.y), @@ -257,7 +259,7 @@ public synchronized GeoPoint fromScreenPoint(float x, float y) { * @param x screen coordinate * @param y screen coordinate */ - public synchronized void fromScreenPoint(double x, double y, Point out) { + public void fromScreenPoint(double x, double y, Point out) { // scale to -1..1 float mx = (float) (1 - (x / mWidth * 2)); float my = (float) (1 - (y / mHeight * 2)); @@ -294,7 +296,7 @@ else if (dy < 0) * @param geoPoint the GeoPoint * @param out Point projected to screen pixel relative to center */ - public synchronized void toScreenPoint(GeoPoint geoPoint, Point out) { + public void toScreenPoint(GeoPoint geoPoint, Point out) { MercatorProjection.project(geoPoint, out); toScreenPoint(out.x, out.y, out); } @@ -304,7 +306,7 @@ public synchronized void toScreenPoint(GeoPoint geoPoint, Point out) { * * @param out Point projected to screen coordinate */ - public synchronized void toScreenPoint(double x, double y, Point out) { + public void toScreenPoint(double x, double y, Point out) { double cs = mPos.scale * Tile.SIZE; double cx = mPos.x * cs; @@ -322,20 +324,17 @@ public synchronized void toScreenPoint(double x, double y, Point out) { out.y = -(mv[1] * (mHeight / 2)); } - public synchronized boolean copy(Viewport viewport) { + protected boolean copy(Viewport viewport) { + mHeight = viewport.mHeight; + mWidth = viewport.mWidth; + mProjMatrix.copy(viewport.mProjMatrix); + mProjMatrixUnscaled.copy(viewport.mProjMatrixUnscaled); + mProjMatrixInverse.copy(viewport.mProjMatrixInverse); + mUnprojMatrix.copy(viewport.mUnprojMatrix); mRotationMatrix.copy(viewport.mRotationMatrix); mViewMatrix.copy(viewport.mViewMatrix); mViewProjMatrix.copy(viewport.mViewProjMatrix); return viewport.getMapPosition(mPos); } - - public synchronized void initFrom(Viewport viewport) { - mProjMatrix.copy(viewport.mProjMatrix); - mProjMatrixUnscaled.copy(viewport.mProjMatrixUnscaled); - mProjMatrixInverse.copy(viewport.mProjMatrixInverse); - - mHeight = viewport.mHeight; - mWidth = viewport.mWidth; - } } diff --git a/vtm/src/org/oscim/renderer/GLViewport.java b/vtm/src/org/oscim/renderer/GLViewport.java index e735f7c2e..c6f25a2df 100644 --- a/vtm/src/org/oscim/renderer/GLViewport.java +++ b/vtm/src/org/oscim/renderer/GLViewport.java @@ -1,6 +1,7 @@ package org.oscim.renderer; import org.oscim.core.MapPosition; +import org.oscim.map.Map; import org.oscim.map.Viewport; public class GLViewport extends Viewport { @@ -40,8 +41,8 @@ public boolean changed() { return changed; } - void setFrom(Viewport viewport) { - changed = super.copy(viewport); + void setFrom(Map map) { + changed = map.viewport().getSyncViewport(this); getMapExtents(plane, 0); } diff --git a/vtm/src/org/oscim/renderer/MapRenderer.java b/vtm/src/org/oscim/renderer/MapRenderer.java index 81d40e93b..63fe71529 100644 --- a/vtm/src/org/oscim/renderer/MapRenderer.java +++ b/vtm/src/org/oscim/renderer/MapRenderer.java @@ -70,10 +70,28 @@ public static void setBackgroundColor(int color) { mClearColor = GLUtils.colorToFloat(color); } + private final Runnable renderBegin = new Runnable() { + @Override + public void run() { + mMap.beginFrame(); + } + }; + private final Runnable renderDone = new Runnable() { + @Override + public void run() { + mMap.doneFrame(); + } + }; + public void onDrawFrame() { frametime = System.currentTimeMillis(); + + mMap.post(renderBegin); + draw(); + mMap.post(renderDone); + mBufferPool.releaseBuffers(); TextureItem.disposeTextures(); } @@ -98,8 +116,7 @@ private void draw() { GLState.bindElementBuffer(-1); GLState.bindVertexBuffer(-1); - mMap.animator().updateAnimation(); - mViewport.setFrom(mMap.viewport()); + mViewport.setFrom(mMap); if (GLAdapter.debugView) { /* modify this to scale only the view, to see @@ -145,8 +162,6 @@ public void onSurfaceChanged(int width, int height) { if (width <= 0 || height <= 0) return; - //mMap.viewport().getMatrix(null, mMatrices.proj, null); - mViewport.initFrom(mMap.viewport()); gl.viewport(0, 0, width, height); //GL.scissor(0, 0, width, height); diff --git a/vtm/src/org/oscim/utils/ThreadUtils.java b/vtm/src/org/oscim/utils/ThreadUtils.java new file mode 100644 index 000000000..2f7d53449 --- /dev/null +++ b/vtm/src/org/oscim/utils/ThreadUtils.java @@ -0,0 +1,20 @@ +package org.oscim.utils; + +public class ThreadUtils { + + private static Thread MAIN_THREAD; + + public static void assertMainThread() { + if (MAIN_THREAD != Thread.currentThread()) + throw new RuntimeException("Access from non-main thread!"); + } + + public static boolean isMainThread() { + return MAIN_THREAD == Thread.currentThread(); + } + + public static void init() { + MAIN_THREAD = Thread.currentThread(); + } + +} From dfc1aeb98f867b786dfd2a04750934eede465d45 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Mon, 13 Oct 2014 22:20:06 +0200 Subject: [PATCH 02/20] Animator: animation end event --- vtm/src/org/oscim/map/Animator.java | 8 +++++--- vtm/src/org/oscim/map/Map.java | 13 ++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/vtm/src/org/oscim/map/Animator.java b/vtm/src/org/oscim/map/Animator.java index 40b5ec04a..ff9d3433b 100644 --- a/vtm/src/org/oscim/map/Animator.java +++ b/vtm/src/org/oscim/map/Animator.java @@ -211,11 +211,8 @@ void updateAnimation() { long millisLeft = mAnimEnd - MapRenderer.frametime; - //boolean changed = false; - ViewController v = mMap.viewport(); - //synchronized (v) { /* cancel animation when position was changed since last * update, i.e. when it was modified outside the animator. */ if (v.getMapPosition(mCurPos)) { @@ -293,5 +290,10 @@ public void cancel() { mState = ANIM_NONE; mPivot.x = 0; mPivot.y = 0; + mMap.events.fire(Map.ANIM_END, mMap.mMapPosition); + } + + public boolean isActive() { + return mState != ANIM_NONE; } } diff --git a/vtm/src/org/oscim/map/Map.java b/vtm/src/org/oscim/map/Map.java index 8d2a15b0a..9737ae8d7 100644 --- a/vtm/src/org/oscim/map/Map.java +++ b/vtm/src/org/oscim/map/Map.java @@ -66,30 +66,33 @@ public interface InputListener extends EventListener { /** * UpdateListener event. Map position has changed. */ - public static Event POSITION_EVENT = new Event(); + public static final Event POSITION_EVENT = new Event(); /** * UpdateLister event. Delivered on main-thread when updateMap() was called * and no CLEAR_EVENT or POSITION_EVENT was triggered. */ - public static Event UPDATE_EVENT = new Event(); + public static final Event UPDATE_EVENT = new Event(); /** * UpdateListerner event. Map state has changed in a way that all layers * should clear their state e.g. the theme or the TilesSource has changed. * TODO should have an event-source to only clear affected layers. */ - public static Event CLEAR_EVENT = new Event(); + public static final Event CLEAR_EVENT = new Event(); + + public static final Event ANIM_END = new Event(); public final EventDispatcher input; public final EventDispatcher events; private final Layers mLayers; private final ViewController mViewport; - protected final Animator mAnimator; - private final MapPosition mMapPosition; private final AsyncExecutor mAsyncExecutor; + protected final Animator mAnimator; + protected final MapPosition mMapPosition; + protected final MapEventLayer mEventLayer; protected GestureDetector mGestureDetector; From e1c1a200740e9225b46188523d2e408048405f6a Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Mon, 13 Oct 2014 22:20:35 +0200 Subject: [PATCH 03/20] ViewController: clamp tilt in setPosition --- vtm/src/org/oscim/map/ViewController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vtm/src/org/oscim/map/ViewController.java b/vtm/src/org/oscim/map/ViewController.java index df144b448..e3eacda74 100644 --- a/vtm/src/org/oscim/map/ViewController.java +++ b/vtm/src/org/oscim/map/ViewController.java @@ -1,5 +1,7 @@ package org.oscim.map; +import static org.oscim.utils.FastMath.clamp; + import org.oscim.core.MapPosition; import org.oscim.core.Point; import org.oscim.core.Tile; @@ -179,10 +181,10 @@ public boolean setTilt(float tilt) { public void setMapPosition(MapPosition mapPosition) { ThreadUtils.assertMainThread(); - mPos.scale = FastMath.clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE); + mPos.scale = clamp(mapPosition.scale, MIN_SCALE, MAX_SCALE); mPos.x = mapPosition.x; mPos.y = mapPosition.y; - mPos.tilt = mapPosition.tilt; + mPos.tilt = clamp(mapPosition.tilt, 0, MAX_TILT); mPos.bearing = mapPosition.bearing; updateMatrices(); } From 0c5a7e7aa6faa2adff20068ce835301486c5f311 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Mon, 20 Oct 2014 05:12:21 +0200 Subject: [PATCH 04/20] android: refactor MapView - merge AndroidMap + GLView into Mapview - let MapView directly extends GLSurfaceView --- .../src/org/oscim/android/AndroidMap.java | 119 -------------- .../src/org/oscim/android/MapView.java | 150 +++++++++++++++++- .../src/org/oscim/android/gl/GLView.java | 65 -------- 3 files changed, 144 insertions(+), 190 deletions(-) delete mode 100644 vtm-android/src/org/oscim/android/AndroidMap.java delete mode 100644 vtm-android/src/org/oscim/android/gl/GLView.java diff --git a/vtm-android/src/org/oscim/android/AndroidMap.java b/vtm-android/src/org/oscim/android/AndroidMap.java deleted file mode 100644 index 0a130a6f0..000000000 --- a/vtm-android/src/org/oscim/android/AndroidMap.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013 Hannes Janetzek - * - * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see . - */ -package org.oscim.android; - -import org.oscim.android.gl.GLView; -import org.oscim.map.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import android.widget.RelativeLayout.LayoutParams; - -public class AndroidMap extends Map { - static final Logger log = LoggerFactory.getLogger(AndroidMap.class); - - private final MapView mMapView; - final GLView mGLView; - - private boolean mRenderRequest; - private boolean mRenderWait; - private boolean mPausing; - - public AndroidMap(MapView mapView) { - super(); - - mMapView = mapView; - mGLView = new GLView(mapView.getContext(), this); - - LayoutParams params = - new LayoutParams(android.view.ViewGroup.LayoutParams.MATCH_PARENT, - android.view.ViewGroup.LayoutParams.MATCH_PARENT); - - mapView.addView(mGLView, params); - } - - @Override - public int getWidth() { - return mMapView.getWidth(); - } - - @Override - public int getHeight() { - return mMapView.getHeight(); - } - - private final Runnable mRedrawCb = new Runnable() { - @Override - public void run() { - prepareFrame(); - mGLView.requestRender(); - } - }; - - @Override - public void updateMap(boolean redraw) { - if (mPausing) - return; - - if (!mRenderRequest) { - mRenderRequest = true; - mMapView.post(mRedrawCb); - } else { - mRenderWait = true; - } - } - - @Override - public void render() { - if (mPausing) - return; - - updateMap(true); - } - - @Override - public void beginFrame() { - } - - @Override - public void doneFrame() { - mRenderRequest = false; - - if (mRenderWait) { - //log.debug("redraw"); - mRenderWait = false; - updateMap(true); - //prepareFrame(); - //mGLView.requestRender(); - } - } - - @Override - public boolean post(Runnable runnable) { - return mMapView.post(runnable); - } - - @Override - public boolean postDelayed(Runnable action, long delay) { - return mMapView.postDelayed(action, delay); - } - - public void pause(boolean pause) { - log.debug("pause... {}", pause); - mPausing = pause; - } -} diff --git a/vtm-android/src/org/oscim/android/MapView.java b/vtm-android/src/org/oscim/android/MapView.java index 2847a2ccc..5c603ad2f 100644 --- a/vtm-android/src/org/oscim/android/MapView.java +++ b/vtm-android/src/org/oscim/android/MapView.java @@ -16,8 +16,12 @@ */ package org.oscim.android; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + import org.oscim.android.canvas.AndroidGraphics; import org.oscim.android.gl.AndroidGL; +import org.oscim.android.gl.GlConfigChooser; import org.oscim.android.input.AndroidMotionEvent; import org.oscim.android.input.GestureHandler; import org.oscim.backend.CanvasAdapter; @@ -28,19 +32,26 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.opengl.GLSurfaceView; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.GestureDetector; -import android.widget.RelativeLayout; -public class MapView extends RelativeLayout { +/** + * The MapView, + * + * add it your App, have a map! + * + * Dont forget to call onPause / onResume! + */ +public class MapView extends GLSurfaceView { + + static final Logger log = LoggerFactory.getLogger(MapView.class); static { System.loadLibrary("vtm-jni"); } - static final Logger log = LoggerFactory.getLogger(MapView.class); - protected final AndroidMap mMap; protected final GestureDetector mGestureDetector; protected final AndroidMotionEvent mMotionEvent; @@ -49,12 +60,17 @@ public MapView(Context context) { this(context, null); } + @SuppressWarnings("deprecation") public MapView(Context context, AttributeSet attributeSet) { super(context, attributeSet); + + /* Not sure if this makes sense */ this.setWillNotDraw(true); this.setClickable(true); this.setFocusable(true); + this.setFocusableInTouchMode(true); + /* Setup android backedn */ AndroidGraphics.init(); AndroidAssets.init(context); GLAdapter.init(new AndroidGL()); @@ -62,8 +78,21 @@ public MapView(Context context, AttributeSet attributeSet) { DisplayMetrics metrics = getResources().getDisplayMetrics(); CanvasAdapter.dpi = (int) Math.max(metrics.xdpi, metrics.ydpi); + /* Initialize the Map */ mMap = new AndroidMap(this); + /* Initialize Renderer */ + setEGLConfigChooser(new GlConfigChooser()); + setEGLContextClientVersion(2); + + if (GLAdapter.debug) + setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR + | GLSurfaceView.DEBUG_LOG_GL_CALLS); + + setRenderer(new GLRenderer(mMap)); + setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + + /* to be removed */ if (context instanceof MapActivity) ((MapActivity) context).registerMapView(this); @@ -81,11 +110,11 @@ public void onStop() { } - void onPause() { + public void onPause() { mMap.pause(true); } - void onResume() { + public void onResume() { mMap.pause(false); } @@ -116,4 +145,113 @@ protected void onSizeChanged(int width, int height, public Map map() { return mMap; } + + static class AndroidMap extends Map { + + private final MapView mMapView; + + private boolean mRenderRequest; + private boolean mRenderWait; + private boolean mPausing; + + public AndroidMap(MapView mapView) { + super(); + mMapView = mapView; + } + + @Override + public int getWidth() { + return mMapView.getWidth(); + } + + @Override + public int getHeight() { + return mMapView.getHeight(); + } + + private final Runnable mRedrawCb = new Runnable() { + @Override + public void run() { + prepareFrame(); + mMapView.requestRender(); + } + }; + + @Override + public void updateMap(boolean redraw) { + if (mPausing) + return; + + if (!mRenderRequest) { + mRenderRequest = true; + mMapView.post(mRedrawCb); + } else { + mRenderWait = true; + } + } + + @Override + public void render() { + if (mPausing) + return; + + updateMap(true); + } + + @Override + public void beginFrame() { + } + + @Override + public void doneFrame() { + mRenderRequest = false; + + if (mRenderWait) { + //log.debug("redraw"); + mRenderWait = false; + updateMap(true); + //prepareFrame(); + //mGLView.requestRender(); + } + } + + @Override + public boolean post(Runnable runnable) { + return mMapView.post(runnable); + } + + @Override + public boolean postDelayed(Runnable action, long delay) { + return mMapView.postDelayed(action, delay); + } + + public void pause(boolean pause) { + log.debug("pause... {}", pause); + mPausing = pause; + } + } + + static class GLRenderer extends org.oscim.renderer.MapRenderer + implements GLSurfaceView.Renderer { + + public GLRenderer(Map map) { + super(map); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + super.onSurfaceCreated(); + } + + @Override + public void onSurfaceChanged(GL10 gl, int width, int height) { + super.onSurfaceChanged(width, height); + + } + + @Override + public void onDrawFrame(GL10 gl) { + super.onDrawFrame(); + } + } } diff --git a/vtm-android/src/org/oscim/android/gl/GLView.java b/vtm-android/src/org/oscim/android/gl/GLView.java deleted file mode 100644 index fcecb62d9..000000000 --- a/vtm-android/src/org/oscim/android/gl/GLView.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012 Hannes Janetzek - * - * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see . - */ -package org.oscim.android.gl; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -import org.oscim.backend.GLAdapter; -import org.oscim.map.Map; - -import android.content.Context; -import android.opengl.GLSurfaceView; - -public class GLView extends GLSurfaceView { - - class GLRenderer extends org.oscim.renderer.MapRenderer implements GLSurfaceView.Renderer { - - public GLRenderer(Map map) { - super(map); - } - - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - super.onSurfaceCreated(); - } - - @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { - super.onSurfaceChanged(width, height); - - } - - @Override - public void onDrawFrame(GL10 gl) { - super.onDrawFrame(); - } - } - - public GLView(Context context, Map map) { - super(context); - setEGLConfigChooser(new GlConfigChooser()); - setEGLContextClientVersion(2); - - if (GLAdapter.debug) - setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR | GLSurfaceView.DEBUG_LOG_GL_CALLS); - - setRenderer(new GLRenderer(map)); - - setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); - } -} From b1cfdfd454630814d0bc46d9c9c24e207c505fe0 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Mon, 20 Oct 2014 05:14:00 +0200 Subject: [PATCH 05/20] android: remove MapActivity - see vtm-android-start for an example use of MapView - add MapPreferences - update android-start - android-example: use appcompat actionbar --- vtm-android-example/AndroidManifest.xml | 4 +- .../res/layout/activity_map_styler.xml | 2 +- vtm-android-example/res/values/styles.xml | 2 +- .../oscim/android/test/BaseMapActivity.java | 15 +-- .../android/test/BitmapTileMapActivity.java | 35 ++--- .../org/oscim/android/test/MapActivity.java | 53 ++++++++ .../android/test/MapsforgeMapActivity.java | 7 +- .../android/test/MarkerOverlayActivity.java | 9 +- .../android/test/OsmJsonMapActivity.java | 5 - .../android/test/PathOverlayActivity.java | 10 +- .../oscim/android/test/SimpleMapActivity.java | 4 +- .../org/oscim/android/start/TestActivity.java | 30 ++++- .../src/org/oscim/android/MapActivity.java | 125 ------------------ .../src/org/oscim/android/MapPreferences.java | 74 +++++++++++ .../src/org/oscim/android/MapView.java | 5 - 15 files changed, 195 insertions(+), 185 deletions(-) create mode 100644 vtm-android-example/src/org/oscim/android/test/MapActivity.java delete mode 100644 vtm-android/src/org/oscim/android/MapActivity.java create mode 100644 vtm-android/src/org/oscim/android/MapPreferences.java diff --git a/vtm-android-example/AndroidManifest.xml b/vtm-android-example/AndroidManifest.xml index 5f2a9918a..a6424c93f 100644 --- a/vtm-android-example/AndroidManifest.xml +++ b/vtm-android-example/AndroidManifest.xml @@ -15,8 +15,8 @@ - + android:label="@string/app_name" + android:theme="@style/AppTheme" > diff --git a/vtm-android-example/res/layout/activity_map_styler.xml b/vtm-android-example/res/layout/activity_map_styler.xml index 1f622be23..875d1b801 100644 --- a/vtm-android-example/res/layout/activity_map_styler.xml +++ b/vtm-android-example/res/layout/activity_map_styler.xml @@ -26,7 +26,7 @@ android:id="@+id/controls" android:layout_width="fill_parent" android:layout_height="wrap_content" - android:background="#cc000000" + android:background="#ccdddddd" android:orientation="vertical" android:paddingLeft="10dp" android:paddingRight="10dp" diff --git a/vtm-android-example/res/values/styles.xml b/vtm-android-example/res/values/styles.xml index 8e0a2292e..76c49238d 100644 --- a/vtm-android-example/res/values/styles.xml +++ b/vtm-android-example/res/values/styles.xml @@ -1,5 +1,5 @@ -