From 7f573f0ac4a453cc4b713db33f457f7b77109e79 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Fri, 17 Aug 2012 04:38:16 +0200 Subject: [PATCH] started work on labeling --- res/values/strings.xml | 3 +- res/xml/preferences.xml | 4 +- src/org/mapsforge/android/DebugSettings.java | 47 +- src/org/mapsforge/android/MapView.java | 77 +- .../android/glrenderer/GLMapTile.java | 10 +- .../mapsforge/android/glrenderer/Layer.java | 22 +- .../android/glrenderer/LineLayer.java | 98 +- .../android/glrenderer/LineLayers.java | 94 +- .../android/glrenderer/MapGenerator.java | 399 ++++++-- .../android/glrenderer/MapRenderer.java | 490 ++++----- .../android/glrenderer/PolygonLayer.java | 16 +- .../android/glrenderer/PolygonLayers.java | 99 +- .../android/glrenderer/PoolItem.java | 3 +- .../mapsforge/android/glrenderer/Shaders.java | 2 + .../android/glrenderer/TextItem.java | 35 + .../android/glrenderer/TextLayer.java | 28 + .../android/glrenderer/TextRenderer.java | 405 ++++++++ .../android/glrenderer/TextTexture.java | 32 + .../{LayerPool.java => VertexPool.java} | 52 +- .../mapsforge/android/input/TouchHandler.java | 54 +- .../android/rendertheme/IRenderCallback.java | 17 +- .../android/rendertheme/RenderTheme.java | 26 +- .../rendertheme/osmarender/osmarender.xml | 169 ++- .../rendertheme/renderinstruction/Area.java | 25 +- .../renderinstruction/Caption.java | 69 +- .../rendertheme/renderinstruction/Line.java | 21 +- .../android/swrenderer/MapGenerator.java | 18 +- src/org/mapsforge/android/utils/GlUtils.java | 1 + src/org/mapsforge/app/TileMap.java | 4 +- src/org/mapsforge/core/Tag.java | 23 + src/org/mapsforge/core/Tile.java | 2 +- src/org/mapsforge/database/IMapDatabase.java | 4 +- .../database/IMapDatabaseCallback.java | 8 +- src/org/mapsforge/database/QueryResult.java | 22 + .../mapsforge/database/json/MapDatabase.java | 18 +- .../database/mapfile/MapDatabase.java | 30 +- .../mapsforge/database/pbmap/MapDatabase.java | 283 ++++-- src/org/mapsforge/database/pbmap/Tags.java | 958 ++++++++++++++++++ .../database/postgis/MapDatabase.java | 78 +- 39 files changed, 2839 insertions(+), 907 deletions(-) create mode 100644 src/org/mapsforge/android/glrenderer/TextItem.java create mode 100644 src/org/mapsforge/android/glrenderer/TextLayer.java create mode 100644 src/org/mapsforge/android/glrenderer/TextRenderer.java create mode 100644 src/org/mapsforge/android/glrenderer/TextTexture.java rename src/org/mapsforge/android/glrenderer/{LayerPool.java => VertexPool.java} (53%) create mode 100644 src/org/mapsforge/database/QueryResult.java create mode 100644 src/org/mapsforge/database/pbmap/Tags.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 1353acbe..264fa792 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -88,7 +88,8 @@ Show the scale of the map Tile coordinates Show coordinates on tiles - Tile boundaries + Draw unmatched ways + Tile boundaries Draw tile boundaries Disable Polygon rendering Highlight tiles which have the water flag set diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index e633f96c..2dad5457 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -32,8 +32,8 @@ android:key="showFpsCounter" /> --> - + diff --git a/src/org/mapsforge/android/DebugSettings.java b/src/org/mapsforge/android/DebugSettings.java index faa04aab..729e5c1c 100644 --- a/src/org/mapsforge/android/DebugSettings.java +++ b/src/org/mapsforge/android/DebugSettings.java @@ -29,13 +29,13 @@ public class DebugSettings { */ public final boolean mDrawTileFrames; + public final boolean mDrawUnmatchted; + /** * True if highlighting of water tiles is enabled, false otherwise. */ public final boolean mDisablePolygons; - private final int mHashCodeValue; - /** * @param drawTileCoordinates * if drawing of tile coordinates is enabled. @@ -43,49 +43,14 @@ public class DebugSettings { * if drawing of tile frames is enabled. * @param disablePolygons * if highlighting of water tiles is enabled. + * @param drawUnmatched + * ... */ public DebugSettings(boolean drawTileCoordinates, boolean drawTileFrames, - boolean disablePolygons) { + boolean disablePolygons, boolean drawUnmatched) { mDrawTileCoordinates = drawTileCoordinates; mDrawTileFrames = drawTileFrames; + mDrawUnmatchted = drawUnmatched; mDisablePolygons = disablePolygons; - mHashCodeValue = calculateHashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof DebugSettings)) { - return false; - } - DebugSettings other = (DebugSettings) obj; - if (mDrawTileCoordinates != other.mDrawTileCoordinates) { - return false; - } - if (mDrawTileFrames != other.mDrawTileFrames) { - return false; - } - if (mDisablePolygons != other.mDisablePolygons) { - return false; - } - return true; - } - - @Override - public int hashCode() { - return mHashCodeValue; - } - - /** - * @return the hash code of this object. - */ - private int calculateHashCode() { - int result = 1; - result = 31 * result + (mDrawTileCoordinates ? 1231 : 1237); - result = 31 * result + (mDrawTileFrames ? 1231 : 1237); - result = 31 * result + (mDisablePolygons ? 1231 : 1237); - return result; } } diff --git a/src/org/mapsforge/android/MapView.java b/src/org/mapsforge/android/MapView.java index 8f6fcf84..ef5b1b62 100644 --- a/src/org/mapsforge/android/MapView.java +++ b/src/org/mapsforge/android/MapView.java @@ -40,6 +40,7 @@ import org.mapsforge.android.rendertheme.RenderThemeHandler; import org.mapsforge.android.utils.GlConfigChooser; import org.mapsforge.core.GeoPoint; import org.mapsforge.core.MapPosition; +import org.mapsforge.core.Tile; import org.mapsforge.database.FileOpenResult; import org.mapsforge.database.IMapDatabase; import org.mapsforge.database.MapFileInfo; @@ -93,9 +94,9 @@ public class MapView extends GLSurfaceView { private IMapRenderer mMapRenderer; private JobQueue mJobQueue; private MapWorker mMapWorkers[]; - private int mNumMapWorkers = 4; + private int mNumMapWorkers = 6; private JobParameters mJobParameters; - private DebugSettings mDebugSettings; + public DebugSettings debugSettings; private String mMapFile; /** @@ -122,6 +123,8 @@ public class MapView extends GLSurfaceView { MapDatabaseFactory.getMapDatabase(attributeSet)); } + private boolean mDebugDatabase = false; + private MapView(Context context, AttributeSet attributeSet, MapRenderers mapGeneratorType, MapDatabases mapDatabaseType) { @@ -131,13 +134,17 @@ public class MapView extends GLSurfaceView { throw new IllegalArgumentException( "context is not an instance of MapActivity"); } + Log.d(TAG, "create MapView: " + mapDatabaseType.name()); - setWillNotDraw(true); - setWillNotCacheDrawing(true); + // TODO make this dpi dependent + Tile.TILE_SIZE = 400; + + // setWillNotDraw(true); + // setWillNotCacheDrawing(true); MapActivity mapActivity = (MapActivity) context; - mDebugSettings = new DebugSettings(false, false, false); + debugSettings = new DebugSettings(false, false, false, false); mJobParameters = new JobParameters(DEFAULT_RENDER_THEME, DEFAULT_TEXT_SCALE); mMapController = new MapController(this); @@ -161,8 +168,14 @@ public class MapView extends GLSurfaceView { mMapWorkers = new MapWorker[mNumMapWorkers]; for (int i = 0; i < mNumMapWorkers; i++) { - IMapDatabase mapDatabase = MapDatabaseFactory - .createMapDatabase(mapDatabaseType); + IMapDatabase mapDatabase; + if (mDebugDatabase) { + mapDatabase = MapDatabaseFactory + .createMapDatabase(MapDatabases.JSON_READER); + + } else { + mapDatabase = MapDatabaseFactory.createMapDatabase(mapDatabaseType); + } IMapGenerator mapGenerator = mMapRenderer.createMapGenerator(); mapGenerator.setMapDatabase(mapDatabase); @@ -170,8 +183,6 @@ public class MapView extends GLSurfaceView { if (i == 0) { mMapDatabase = mapDatabase; initMapStartPosition(); - - // mapGenerator.setRendertheme(DEFAULT_RENDER_THEME); } mMapWorkers[i] = new MapWorker(i, this, mapGenerator, mMapRenderer); mMapWorkers[i].start(); @@ -179,13 +190,13 @@ public class MapView extends GLSurfaceView { setRenderTheme(InternalRenderTheme.OSMARENDER); - mapActivity.registerMapView(this); - setEGLConfigChooser(new GlConfigChooser()); setEGLContextClientVersion(2); setRenderer(mMapRenderer); setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + + mapActivity.registerMapView(this); } private void initMapStartPosition() { @@ -212,7 +223,7 @@ public class MapView extends GLSurfaceView { * @return the debug settings which are used in this MapView. */ public DebugSettings getDebugSettings() { - return mDebugSettings; + return debugSettings; } /** @@ -302,14 +313,14 @@ public class MapView extends GLSurfaceView { * Calculates all necessary tiles and adds jobs accordingly. */ public void redrawTiles() { - if (getWidth() > 0 && getHeight() > 0) + if (getWidth() <= 0 || getHeight() <= 0) return; mMapRenderer.redrawTiles(false); } - void clearAndRedrawMapView() { - if (getWidth() > 0 && getHeight() > 0) + private void clearAndRedrawMapView() { + if (getWidth() <= 0 || getHeight() <= 0) return; mMapRenderer.redrawTiles(true); @@ -343,7 +354,7 @@ public class MapView extends GLSurfaceView { * the new DebugSettings for this MapView. */ public void setDebugSettings(DebugSettings debugSettings) { - mDebugSettings = debugSettings; + this.debugSettings = debugSettings; clearAndRedrawMapView(); } @@ -377,7 +388,7 @@ public class MapView extends GLSurfaceView { mJobQueue.clear(); - mapWorkersPause(); + mapWorkersPause(true); for (MapWorker mapWorker : mMapWorkers) { @@ -445,6 +456,9 @@ public class MapView extends GLSurfaceView { */ public void setMapDatabase(MapDatabases mapDatabaseType) { + if (mDebugDatabase) + return; + IMapGenerator mapGenerator; Log.d(TAG, "setMapDatabase " + mapDatabaseType.name()); @@ -452,7 +466,9 @@ public class MapView extends GLSurfaceView { if (mMapDatabaseType == mapDatabaseType) return; - mapWorkersPause(); + mMapDatabaseType = mapDatabaseType; + + mapWorkersPause(true); for (MapWorker mapWorker : mMapWorkers) { mapGenerator = mapWorker.getMapGenerator(); @@ -468,6 +484,8 @@ public class MapView extends GLSurfaceView { setMapFile(mapFile); mapWorkersProceed(); + + Log.d(TAG, ">>>"); } /** @@ -510,7 +528,7 @@ public class MapView extends GLSurfaceView { private boolean setRenderTheme(Theme theme) { - mapWorkersPause(); + mapWorkersPause(true); InputStream inputStream = null; try { @@ -607,7 +625,7 @@ public class MapView extends GLSurfaceView { int oldHeight) { mJobQueue.clear(); - mapWorkersPause(); + mapWorkersPause(true); super.onSizeChanged(width, height, oldWidth, oldHeight); @@ -668,8 +686,7 @@ public class MapView extends GLSurfaceView { @Override public void onPause() { super.onPause(); - for (MapWorker mapWorker : mMapWorkers) - mapWorker.pause(); + mapWorkersPause(false); // mMapMover.pause(); // mZoomAnimator.pause(); @@ -678,8 +695,7 @@ public class MapView extends GLSurfaceView { @Override public void onResume() { super.onResume(); - for (MapWorker mapWorker : mMapWorkers) - mapWorker.proceed(); + mapWorkersProceed(); // mMapMover.proceed(); // mZoomAnimator.proceed(); @@ -747,21 +763,24 @@ public class MapView extends GLSurfaceView { public void addJobs(ArrayList jobs) { mJobQueue.setJobs(jobs); - for (MapWorker m : mMapWorkers) { + for (int i = 0; i < mNumMapWorkers; i++) { + MapWorker m = mMapWorkers[i]; synchronized (m) { m.notify(); } } } - private void mapWorkersPause() { + private void mapWorkersPause(boolean wait) { for (MapWorker mapWorker : mMapWorkers) { if (!mapWorker.isPausing()) mapWorker.pause(); } - for (MapWorker mapWorker : mMapWorkers) { - if (!mapWorker.isPausing()) - mapWorker.awaitPausing(); + if (wait) { + for (MapWorker mapWorker : mMapWorkers) { + if (!mapWorker.isPausing()) + mapWorker.awaitPausing(); + } } } diff --git a/src/org/mapsforge/android/glrenderer/GLMapTile.java b/src/org/mapsforge/android/glrenderer/GLMapTile.java index f8837803..9abc5783 100644 --- a/src/org/mapsforge/android/glrenderer/GLMapTile.java +++ b/src/org/mapsforge/android/glrenderer/GLMapTile.java @@ -14,6 +14,8 @@ */ package org.mapsforge.android.glrenderer; +import java.util.ArrayList; + import org.mapsforge.android.mapgenerator.MapTile; import org.mapsforge.core.Tile; @@ -21,10 +23,12 @@ class GLMapTile extends MapTile { VertexBufferObject lineVBO; VertexBufferObject polygonVBO; + TextTexture texture; - LineLayers lineLayers; - PolygonLayers polygonLayers; - // MeshLayers meshLayers; + LineLayer lineLayers; + PolygonLayer polygonLayers; + + ArrayList labels; boolean newData; boolean loading; diff --git a/src/org/mapsforge/android/glrenderer/Layer.java b/src/org/mapsforge/android/glrenderer/Layer.java index e7d3de3b..ca93b35e 100644 --- a/src/org/mapsforge/android/glrenderer/Layer.java +++ b/src/org/mapsforge/android/glrenderer/Layer.java @@ -14,35 +14,27 @@ */ package org.mapsforge.android.glrenderer; -import java.util.LinkedList; - class Layer { - LinkedList pool; + PoolItem pool; + protected PoolItem curItem; int verticesCnt; int offset; final int layer; - // final int color; - final float[] colors; - Layer(int l, int color) { + Layer(int l) { layer = l; verticesCnt = 0; - - colors = new float[4]; - - colors[0] = (color >> 16 & 0xff) / 255.0f; - colors[1] = (color >> 8 & 0xff) / 255.0f; - colors[2] = (color >> 0 & 0xff) / 255.0f; - colors[3] = (color >> 24 & 0xff) / 255.0f; } float[] getNextPoolItem() { curItem.used = PoolItem.SIZE; - curItem = LayerPool.get(); - pool.add(curItem); + + curItem.next = VertexPool.get(); + curItem = curItem.next; + return curItem.vertices; } } diff --git a/src/org/mapsforge/android/glrenderer/LineLayer.java b/src/org/mapsforge/android/glrenderer/LineLayer.java index 085a3e95..f8524ddc 100644 --- a/src/org/mapsforge/android/glrenderer/LineLayer.java +++ b/src/org/mapsforge/android/glrenderer/LineLayer.java @@ -14,34 +14,39 @@ */ package org.mapsforge.android.glrenderer; -import java.util.ArrayList; -import java.util.LinkedList; - +import org.mapsforge.android.rendertheme.renderinstruction.Line; import org.mapsforge.core.Tile; +import android.util.FloatMath; + class LineLayer extends Layer { - ArrayList outlines; - boolean isOutline; - boolean isFixed; + Line line; + + LineLayer next; + LineLayer outlines; + float width; + boolean isOutline; - LineLayer(int layer, int color, boolean outline, boolean fixed) { - super(layer, color); - isOutline = outline; - isFixed = fixed; - if (outline) { - outlines = new ArrayList(); - } else { - curItem = LayerPool.get(); + LineLayer(int layer, Line line, boolean outline) { + super(layer); - pool = new LinkedList(); - pool.add(curItem); + this.line = line; + this.isOutline = outline; + + if (!outline) { + curItem = VertexPool.get(); + pool = curItem; } } void addOutline(LineLayer link) { - if (!outlines.contains(link)) - outlines.add(link); + for (LineLayer l = outlines; l != null; l = l.outlines) + if (link == l) + return; + + link.outlines = outlines; + outlines = link; } /* @@ -49,7 +54,7 @@ class LineLayer extends Layer { */ void addLine(float[] pointArray, int pos, int length, float w, boolean capRound) { float x, y, nextX, nextY, prevX, prevY, ux, uy, vx, vy, wx, wy; - double a; + float a; int pointPos = pos; boolean rounded = capRound; width = w; @@ -59,10 +64,12 @@ class LineLayer extends Layer { // amount of vertices used verticesCnt += length + (rounded ? 6 : 2); + int MAX = PoolItem.SIZE; + float[] curVertices = curItem.vertices; int vertexPos = curItem.used; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -77,7 +84,7 @@ class LineLayer extends Layer { vx = nextX - x; vy = nextY - y; - a = Math.sqrt(vx * vx + vy * vy); + a = FloatMath.sqrt(vx * vx + vy * vy); vx = (float) (vx / a); vy = (float) (vy / a); @@ -103,7 +110,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 1.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -113,7 +120,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 1.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -123,7 +130,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = 1.0f; curVertices[vertexPos++] = 1.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -134,7 +141,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -161,7 +168,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -171,7 +178,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -195,14 +202,14 @@ class LineLayer extends Layer { // Unit vector pointing back to previous node vx = prevX - x; vy = prevY - y; - a = Math.sqrt(vx * vx + vy * vy); + a = FloatMath.sqrt(vx * vx + vy * vy); vx = (float) (vx / a); vy = (float) (vy / a); // Unit vector pointing forward to next node wx = nextX - x; wy = nextY - y; - a = Math.sqrt(wx * wx + wy * wy); + a = FloatMath.sqrt(wx * wx + wy * wy); wx = (float) (wx / a); wy = (float) (wy / a); @@ -242,7 +249,7 @@ class LineLayer extends Layer { uxw = ux * w; uyw = uy * w; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -252,7 +259,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -271,7 +278,7 @@ class LineLayer extends Layer { vx = prevX - x; vy = prevY - y; - a = Math.sqrt(vx * vx + vy * vy); + a = FloatMath.sqrt(vx * vx + vy * vy); vx = (float) (vx / a); vy = (float) (vy / a); @@ -288,11 +295,16 @@ class LineLayer extends Layer { outside = (x <= 0 || x >= Tile.TILE_SIZE || y <= 0 || y >= Tile.TILE_SIZE) && (x - vxw <= 0 || x - vxw >= Tile.TILE_SIZE || y - vyw <= 0 || y - vyw >= Tile.TILE_SIZE); - if (vertexPos == PoolItem.SIZE) { - curItem.used = vertexPos; - curItem = LayerPool.get(); - pool.add(curItem); - curVertices = curItem.vertices; + // if (vertexPos == MAX) { + // curItem.used = vertexPos; + // curItem = LayerPool.get(); + // pool.add(curItem); + // curVertices = curItem.vertices; + // vertexPos = 0; + // } + + if (vertexPos == MAX) { + curVertices = getNextPoolItem(); vertexPos = 0; } @@ -302,7 +314,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -312,7 +324,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = 1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -323,7 +335,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = -1.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -334,7 +346,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = 1.0f; curVertices[vertexPos++] = -1.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -358,7 +370,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = -1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } @@ -369,7 +381,7 @@ class LineLayer extends Layer { curVertices[vertexPos++] = 1.0f; curVertices[vertexPos++] = 0.0f; - if (vertexPos == PoolItem.SIZE) { + if (vertexPos == MAX) { curVertices = getNextPoolItem(); vertexPos = 0; } diff --git a/src/org/mapsforge/android/glrenderer/LineLayers.java b/src/org/mapsforge/android/glrenderer/LineLayers.java index 3299bb8d..97ebd51a 100644 --- a/src/org/mapsforge/android/glrenderer/LineLayers.java +++ b/src/org/mapsforge/android/glrenderer/LineLayers.java @@ -19,42 +19,17 @@ import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; -import android.util.SparseArray; - class LineLayers { private static int NUM_VERTEX_FLOATS = 4; - private SparseArray layers; - - LineLayer[] array = null; - int size = 0; - - LineLayers() { - layers = new SparseArray(10); - } - - LineLayer getLayer(int layer, int color, boolean outline, boolean fixed) { - LineLayer l = layers.get(layer); - if (l != null) { - return l; - } - - l = new LineLayer(layer, color, outline, fixed); - layers.put(layer, l); - - return l; - } - - FloatBuffer compileLayerData(FloatBuffer buf) { + static FloatBuffer compileLayerData(LineLayer layers, FloatBuffer buf) { FloatBuffer fbuf = buf; + int size = 0; - array = new LineLayer[layers.size()]; + for (LineLayer l = layers; l != null; l = l.next) + size += l.verticesCnt; - for (int i = 0, n = layers.size(); i < n; i++) { - LineLayer l = layers.valueAt(i); - array[i] = l; - size += l.verticesCnt * NUM_VERTEX_FLOATS; - } + size *= NUM_VERTEX_FLOATS; if (buf == null || buf.capacity() < size) { ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 4).order( @@ -65,40 +40,42 @@ class LineLayers { } int pos = 0; - for (int i = 0, n = array.length; i < n; i++) { - LineLayer l = array[i]; + PoolItem last = null, items = null; + + for (LineLayer l = layers; l != null; l = l.next) { if (l.isOutline) continue; - for (PoolItem item : l.pool) { + for (PoolItem item = l.pool; item != null; item = item.next) { fbuf.put(item.vertices, 0, item.used); + last = item; } - l.offset = pos; pos += l.verticesCnt; - LayerPool.add(l.pool); + if (last != null) { + last.next = items; + items = l.pool; + } + l.pool = null; } - fbuf.flip(); + VertexPool.add(items); - // not needed for drawing - layers = null; + fbuf.flip(); return fbuf; } - ShortBuffer compileLayerData(ShortBuffer buf) { + static ShortBuffer compileLayerData(LineLayer layers, ShortBuffer buf) { + int size = 0; ShortBuffer sbuf = buf; - array = new LineLayer[layers.size()]; + for (LineLayer l = layers; l != null; l = l.next) + size += l.verticesCnt; - for (int i = 0, n = layers.size(); i < n; i++) { - LineLayer l = layers.valueAt(i); - array[i] = l; - size += l.verticesCnt * NUM_VERTEX_FLOATS; - } + size *= NUM_VERTEX_FLOATS; if (buf == null || buf.capacity() < size) { ByteBuffer bbuf = ByteBuffer.allocateDirect(size * 2).order( @@ -111,29 +88,40 @@ class LineLayers { short[] data = new short[PoolItem.SIZE]; - for (int i = 0, n = array.length; i < n; i++) { - LineLayer l = array[i]; + PoolItem last = null, items = null; + + for (LineLayer l = layers; l != null; l = l.next) { if (l.isOutline) continue; - for (int k = 0, m = l.pool.size(); k < m; k++) { - PoolItem item = l.pool.get(k); + for (PoolItem item = l.pool; item != null; item = item.next) { PoolItem.toHalfFloat(item, data); sbuf.put(data, 0, item.used); + last = item; } l.offset = pos; pos += l.verticesCnt; - LayerPool.add(l.pool); + if (last != null) { + last.next = items; + items = l.pool; + } + l.pool = null; } - sbuf.flip(); + VertexPool.add(items); - // not needed for drawing - layers = null; + sbuf.flip(); return sbuf; } + + static void clear(LineLayer layer) { + for (LineLayer l = layer; l != null; l = l.next) { + if (l.pool != null) + VertexPool.add(l.pool); + } + } } diff --git a/src/org/mapsforge/android/glrenderer/MapGenerator.java b/src/org/mapsforge/android/glrenderer/MapGenerator.java index 4ed8a11b..35e756e6 100644 --- a/src/org/mapsforge/android/glrenderer/MapGenerator.java +++ b/src/org/mapsforge/android/glrenderer/MapGenerator.java @@ -14,11 +14,14 @@ */ package org.mapsforge.android.glrenderer; +import java.util.ArrayList; + import org.mapsforge.android.mapgenerator.IMapGenerator; import org.mapsforge.android.mapgenerator.MapGeneratorJob; import org.mapsforge.android.rendertheme.IRenderCallback; import org.mapsforge.android.rendertheme.RenderTheme; import org.mapsforge.android.rendertheme.renderinstruction.Area; +import org.mapsforge.android.rendertheme.renderinstruction.Caption; import org.mapsforge.android.rendertheme.renderinstruction.Line; import org.mapsforge.android.rendertheme.renderinstruction.RenderInstruction; import org.mapsforge.core.MercatorProjection; @@ -27,9 +30,9 @@ import org.mapsforge.core.Tile; import org.mapsforge.core.WebMercator; import org.mapsforge.database.IMapDatabase; import org.mapsforge.database.IMapDatabaseCallback; +import org.mapsforge.database.QueryResult; import android.graphics.Bitmap; -import android.graphics.Color; import android.graphics.Paint; import android.util.Log; @@ -54,11 +57,14 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas private GLMapTile mCurrentTile; private float[] mWayNodes; - private int[] mWays; + private short[] mWays; - private LineLayers mLineLayers; - private PolygonLayers mPolyLayers; - // private MeshLayers mMeshLayers; + private LineLayer mLineLayers; + private PolygonLayer mPolyLayers; + private LineLayer mCurLineLayer; + private PolygonLayer mCurPolyLayer; + + private ArrayList mLabels; private int mDrawingLayer; private int mLevels; @@ -71,13 +77,60 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas */ public MapGenerator() { Log.d(TAG, "init DatabaseRenderer"); - LayerPool.init(); + VertexPool.init(); + } + + private float mPoiX = 256; + private float mPoiY = 256; + + private Tag mTagEmptyName = new Tag("name", ""); + private Tag mTagName; + + private void filterTags(Tag[] tags) { + for (int i = 0; i < tags.length; i++) { + // Log.d(TAG, "check tag: " + tags[i]); + if (tags[i].key == mTagEmptyName.key && tags[i].value != null) { + mTagName = tags[i]; + tags[i] = mTagEmptyName; + } + } } @Override - public void renderPointOfInterest(byte layer, int latitude, int longitude, Tag[] tags) { - // TODO Auto-generated method stub + public void renderPointOfInterest(byte layer, float latitude, float longitude, + Tag[] tags) { + mTagName = null; + + long x = mCurrentTile.x; + long y = mCurrentTile.y; + long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel; + + double divx, divy; + long dx = (x - (z >> 1)); + long dy = (y - (z >> 1)); + + if (useSphericalMercator) { + divx = f900913 / (z >> 1); + divy = f900913 / (z >> 1); + mPoiX = (float) (longitude / divx - dx); + mPoiY = (float) (latitude / divy + dy); + } else { + divx = 180000000.0 / (z >> 1); + divy = z / PIx4; + mPoiX = (float) (longitude / divx - dx); + double sinLat = Math.sin(latitude * PI180); + mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy); + if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10 + || mPoiY > Tile.TILE_SIZE + 10) + return; + } + + // remove tags that should not be cached in Rendertheme + filterTags(tags); + // Log.d(TAG, "renderPointOfInterest: " + mTagName); + + MapGenerator.renderTheme.matchNode(this, tags, mCurrentTile.zoomLevel); } @Override @@ -94,7 +147,7 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas if (mProjected) return mProjectedResult; - float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, maxx = Float.MIN_VALUE, maxy = Float.MIN_VALUE; + // float minx = Float.MAX_VALUE, miny = Float.MAX_VALUE, maxx = Float.MIN_VALUE, maxy = Float.MIN_VALUE; float[] coords = mWayNodes; @@ -133,16 +186,16 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas lat = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy); } - if (area && i == 0) { - if (lon < minx) - minx = lon; - if (lon > maxx) - maxx = lon; - if (lat < miny) - miny = lat; - if (lat > maxy) - maxy = lat; - } + // if (area && i == 0) { + // if (lon < minx) + // minx = lon; + // if (lon > maxx) + // maxx = lon; + // if (lat < miny) + // miny = lat; + // if (lat > maxy) + // maxy = lat; + // } if (cnt != 0) { // drop small distance intermediate nodes @@ -161,38 +214,44 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas cnt += 2; } - if (area) { - // Log.d(TAG, "area:" + (maxx - minx) * (maxy - miny)); - if ((maxx - minx) * (maxy - miny) < 2000 / mCurrentTile.zoomLevel) { - mProjected = true; - mProjectedResult = false; - return false; - } - } + // if (area) { + // // Log.d(TAG, "area:" + (maxx - minx) * (maxy - miny)); + // if ((maxx - minx) * (maxy - miny) < 2000 / mCurrentTile.zoomLevel) { + // mProjected = true; + // mProjectedResult = false; + // return false; + // } + // } - mWays[i] = cnt; + mWays[i] = (short) cnt; } mProjected = true; mProjectedResult = true; return true; } - private boolean firstMatch; - private boolean prevClosed; + // private boolean firstMatch; + // private boolean prevClosed; private RenderInstruction[] mRenderInstructions = null; + private final String TAG_WATER = "water".intern(); + @Override - public void renderWay(byte layer, Tag[] tags, float[] wayNodes, int[] wayLength, + public void renderWay(byte layer, Tag[] tags, float[] wayNodes, short[] wayLength, boolean changed) { + // Log.d(TAG, "render way: " + layer); + mTagName = null; + mProjected = false; mDrawingLayer = getValidLayer(layer) * mLevels; - int len = wayLength[0]; - boolean closed = (wayNodes[0] == wayNodes[len - 2] && - wayNodes[1] == wayNodes[len - 1]); + // int len = wayLength[0]; + // boolean closed = (wayNodes[0] == wayNodes[len - 2] && + // wayNodes[1] == wayNodes[len - 1]); + boolean closed = changed; mSimplify = 0.5f; if (closed) { @@ -201,36 +260,76 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas else mSimplify = 0.2f; - if (tags.length == 1 && "water".equals(tags[0].value)) + if (tags.length == 1 && TAG_WATER == (tags[0].value)) mSimplify = 0; } mWayNodes = wayNodes; mWays = wayLength; - if (!firstMatch && prevClosed == closed && !changed) { - if (mRenderInstructions != null) { - for (int i = 0, n = mRenderInstructions.length; i < n; i++) - mRenderInstructions[i].renderWay(this, tags); - } - // MapGenerator.renderTheme.matchWay(this, tags, - // (byte) (mCurrentTile.zoomLevel + 0), - // closed, false); + // if (mRenderInstructions != null) { + // for (int i = 0, n = mRenderInstructions.length; i < n; i++) + // mRenderInstructions[i].renderWay(this, tags); + // } + + // prevClosed = closed; + mRenderInstructions = MapGenerator.renderTheme.matchWay(this, tags, + (byte) (mCurrentTile.zoomLevel + 0), + closed, true); + + if (mRenderInstructions == null && mDebugDrawUnmatched) + debugUnmatched(closed, tags); + + // firstMatch = false; + } + + private void debugUnmatched(boolean closed, Tag[] tags) { + + Log.d(TAG, "way not matched: " + tags[0] + " " + + (tags.length > 1 ? tags[1] : "") + " " + closed); + + mTagName = new Tag("name", tags[0].key + ":" + tags[0].value, false); + + if (closed) { + mRenderInstructions = MapGenerator.renderTheme.matchWay(this, debugTagArea, + (byte) 0, true, true); } else { - prevClosed = closed; - mRenderInstructions = MapGenerator.renderTheme.matchWay(this, tags, - (byte) (mCurrentTile.zoomLevel + 0), - closed, true); + mRenderInstructions = MapGenerator.renderTheme.matchWay(this, debugTagWay, + (byte) 0, true, true); } - firstMatch = false; } @Override - public void renderAreaCaption(String caption, float verticalOffset, Paint paint, - Paint stroke) { - // TODO Auto-generated method stub + public void renderAreaCaption(Caption caption) { + // Log.d(TAG, "renderAreaCaption: " + mTagName); + if (mTagName == null) + return; + + if (caption.textKey == mTagEmptyName.key) { + if (mLabels == null) + mLabels = new ArrayList(); + mLabels.add(new TextItem(mWayNodes[0], mWayNodes[1], mTagName.value, caption)); + } + + // if (caption.textKey == mTagEmptyName.key) + // mLabels.add(new TextItem(mPoiX, mPoiY, mTagName.value, caption)); + } + + @Override + public void renderPointOfInterestCaption(Caption caption) { + // Log.d(TAG, "renderPointOfInterestCaption: " + mPoiX + " " + mPoiY + " " + // + mTagName); + + if (mTagName == null) + return; + + if (caption.textKey == mTagEmptyName.key) { + if (mLabels == null) + mLabels = new ArrayList(); + mLabels.add(new TextItem(mPoiX, mPoiY, mTagName.value, caption)); + } } @Override @@ -239,13 +338,6 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas } - @Override - public void renderPointOfInterestCaption(String caption, float verticalOffset, - Paint paint, Paint stroke) { - // TODO Auto-generated method stub - - } - @Override public void renderPointOfInterestCircle(float radius, Paint fill, int level) { // TODO Auto-generated method stub @@ -260,12 +352,60 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas @Override public void renderWay(Line line) { - projectToTile(false); LineLayer outlineLayer = null; - LineLayer l = mLineLayers.getLayer(mDrawingLayer + line.level, line.color, false, - line.fixed); + LineLayer lineLayer = null; + + int numLayer = mDrawingLayer + line.level; + + // LineLayer l = mLineLayers; + // + // for (; l != null; l = l.next) + // if (l.next == null || l.next.layer > numLayer) + // break; + // + // if (l == null || l == mLineLayers) { + // // insert at start + // lineLayer = new LineLayer(numLayer, line, false); + // lineLayer.next = mLineLayers; + // mLineLayers = lineLayer; + // } else if (l.layer == numLayer) { + // lineLayer = l; + // } else { + // // insert between current and next layer + // lineLayer = new LineLayer(numLayer, line, false); + // lineLayer.next = l.next; + // l.next = lineLayer; + // } + + // FIXME simplify this... + if (mCurLineLayer != null && mCurLineLayer.layer == numLayer) { + lineLayer = mCurLineLayer; + } else if (mLineLayers == null || mLineLayers.layer > numLayer) { + // insert new layer at start + lineLayer = new LineLayer(numLayer, line, false); + lineLayer.next = mLineLayers; + mLineLayers = lineLayer; + } else { + for (LineLayer l = mLineLayers; l != null; l = l.next) { + if (l.layer == numLayer) { + lineLayer = l; + break; + } + if (l.next == null || l.next.layer > numLayer) { + lineLayer = new LineLayer(numLayer, line, false); + // insert new layer between current and next layer + lineLayer.next = l.next; + l.next = lineLayer; + } + } + } + + if (lineLayer == null) + return; + + mCurLineLayer = lineLayer; float w = line.strokeWidth; @@ -273,13 +413,8 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas w *= mStrokeScale; w *= mProjectionScaleFactor; } - if (line.outline != -1) { - Line outline = MapGenerator.renderTheme.getOutline(line.outline); - if (outline != null) { - outlineLayer = mLineLayers.getLayer(mDrawingLayer + outline.level, - outline.color, true, false); - outlineLayer.addOutline(l); - } + else { + w *= 1.2; // TODO make this dependent on dpi } for (int i = 0, pos = 0, n = mWays.length; i < n; i++) { @@ -287,10 +422,44 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas // need at least two points if (length >= 4) - l.addLine(mWayNodes, pos, length, w, line.round); + lineLayer.addLine(mWayNodes, pos, length, w, line.round); pos += length; } + + if (line.outline < 0) + return; + + Line outline = MapGenerator.renderTheme.getOutline(line.outline); + + if (outline == null) + return; + + numLayer = mDrawingLayer + outline.level; + + if (mLineLayers == null || mLineLayers.layer > numLayer) { + // insert new layer at start + outlineLayer = new LineLayer(numLayer, outline, true); + outlineLayer.next = mLineLayers; + mLineLayers = outlineLayer; + } else { + for (LineLayer l = mLineLayers; l != null; l = l.next) { + if (l.layer == numLayer) { + outlineLayer = l; + break; + } + if (l.next == null || l.next.layer > numLayer) { + outlineLayer = new LineLayer(numLayer, outline, true); + // insert new layer between current and next layer + outlineLayer.next = l.next; + l.next = outlineLayer; + } + } + } + + if (outlineLayer != null) + outlineLayer.addOutline(lineLayer); + } @Override @@ -302,17 +471,41 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas if (!projectToTile(false)) return; - PolygonLayer l = mPolyLayers.getLayer(mDrawingLayer + area.level, area.color, - area.fade); + int numLayer = mDrawingLayer + area.level; + PolygonLayer layer = null; - // MeshLayer l = mMeshLayers.getLayer(mDrawingLayer + area.level, area.color, - // area.fade); + if (mCurPolyLayer != null && mCurPolyLayer.layer == numLayer) { + layer = mCurPolyLayer; + } else if (mPolyLayers == null || mPolyLayers.layer > numLayer) { + // insert new layer at start + layer = new PolygonLayer(numLayer, area); + layer.next = mPolyLayers; + mPolyLayers = layer; + } else { + for (PolygonLayer l = mPolyLayers; l != null; l = l.next) { + if (l.layer >= numLayer) { + layer = l; + break; + } + + if (l.next == null || l.next.layer > numLayer) { + layer = new PolygonLayer(numLayer, area); + // insert new layer between current and next layer + layer.next = l.next; + l.next = layer; + } + } + } + if (layer == null) + return; + + mCurPolyLayer = layer; for (int i = 0, pos = 0, n = mWays.length; i < n; i++) { int length = mWays[i]; // need at least three points if (length >= 6) - l.addPolygon(mWayNodes, pos, length); + layer.addPolygon(mWayNodes, pos, length); pos += length; } @@ -338,10 +531,10 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas } private boolean mDebugDrawPolygons; + boolean mDebugDrawUnmatched; @Override public boolean executeJob(MapGeneratorJob mapGeneratorJob) { - // Log.d(TAG, "load " + mCurrentTile); if (!(mapGeneratorJob.tile instanceof GLMapTile)) return false; @@ -353,7 +546,7 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas mCurrentTile = (GLMapTile) mapGeneratorJob.tile; mDebugDrawPolygons = !mapGeneratorJob.debugSettings.mDisablePolygons; - + mDebugDrawUnmatched = mapGeneratorJob.debugSettings.mDrawUnmatchted; if (mCurrentTile.isLoading || mCurrentTile.isDrawn) return false; @@ -363,32 +556,49 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas setScaleStrokeWidth(mCurrentTile.zoomLevel); - mLineLayers = new LineLayers(); - mPolyLayers = new PolygonLayers(); - // mMeshLayers = new MeshLayers(); - mCurrentTile.lineLayers = mLineLayers; - mCurrentTile.polygonLayers = mPolyLayers; - // mCurrentTile.meshLayers = mMeshLayers; + mLineLayers = null; + mPolyLayers = null; + mLabels = null; - firstMatch = true; + // firstMatch = true; mProjectionScaleFactor = (float) (1.0 / Math.cos(MercatorProjection .pixelYToLatitude(mCurrentTile.pixelY, mCurrentTile.zoomLevel) - * (Math.PI / 180))) / 1.5f; - mMapDatabase.executeQuery(mCurrentTile, this); + * (Math.PI / 180))); // / 1.5f; + + if (mMapDatabase.executeQuery(mCurrentTile, this) != QueryResult.SUCCESS) { + LineLayers.clear(mLineLayers); + PolygonLayers.clear(mPolyLayers); + mLineLayers = null; + mPolyLayers = null; + mCurrentTile.isLoading = false; + return false; + } if (mapGeneratorJob.debugSettings.mDrawTileFrames) { - float[] coords = { 0, 0, 0, Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, - Tile.TILE_SIZE, 0, 0, 0 }; - LineLayer ll = mLineLayers.getLayer(Integer.MAX_VALUE, Color.BLACK, false, - true); - ll.addLine(coords, 0, coords.length, 1.5f, false); + mTagName = new Tag("name", mCurrentTile.toString(), false); + mPoiX = 10; + mPoiY = 10; + MapGenerator.renderTheme.matchNode(this, debugTagWay, (byte) 0); + // float[] coords = { 0, 0, 0, Tile.TILE_SIZE, Tile.TILE_SIZE, Tile.TILE_SIZE, + // Tile.TILE_SIZE, 0, 0, 0 }; + // LineLayer ll = mLineLayers.getLayer(Integer.MAX_VALUE, Color.BLACK, false, + // true, -1); + // ll.addLine(coords, 0, coords.length, 1.5f, false); } + mCurrentTile.lineLayers = mLineLayers; + mCurrentTile.polygonLayers = mPolyLayers; + mCurrentTile.labels = mLabels; + mCurPolyLayer = null; + mCurLineLayer = null; mCurrentTile.newData = true; return true; } + private Tag[] debugTagWay = { new Tag("debug", "way") }; + private Tag[] debugTagArea = { new Tag("debug", "area") }; + private float mProjectionScaleFactor; private static byte getValidLayer(byte layer) { @@ -431,4 +641,13 @@ public class MapGenerator implements IMapGenerator, IRenderCallback, IMapDatabas public void setRenderTheme(RenderTheme theme) { MapGenerator.renderTheme = theme; } + + @Override + public boolean checkWay(Tag[] tags, boolean closed) { + + mRenderInstructions = MapGenerator.renderTheme.matchWay(this, tags, + (byte) (mCurrentTile.zoomLevel + 0), closed, false); + + return mRenderInstructions != null; + } } diff --git a/src/org/mapsforge/android/glrenderer/MapRenderer.java b/src/org/mapsforge/android/glrenderer/MapRenderer.java index f7c0988c..da15fb87 100644 --- a/src/org/mapsforge/android/glrenderer/MapRenderer.java +++ b/src/org/mapsforge/android/glrenderer/MapRenderer.java @@ -25,6 +25,7 @@ import static android.opengl.GLES20.GL_EXTENSIONS; import static android.opengl.GLES20.GL_FLOAT; import static android.opengl.GLES20.GL_INVERT; import static android.opengl.GLES20.GL_NEVER; +import static android.opengl.GLES20.GL_ONE; import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA; import static android.opengl.GLES20.GL_SCISSOR_TEST; import static android.opengl.GLES20.GL_SRC_ALPHA; @@ -80,6 +81,7 @@ import org.mapsforge.android.mapgenerator.JobParameters; import org.mapsforge.android.mapgenerator.MapGeneratorJob; import org.mapsforge.android.mapgenerator.TileCacheKey; import org.mapsforge.android.mapgenerator.TileDistanceSort; +import org.mapsforge.android.rendertheme.renderinstruction.Line; import org.mapsforge.android.utils.GlConfigChooser; import org.mapsforge.android.utils.GlUtils; import org.mapsforge.core.MapPosition; @@ -102,7 +104,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { // private boolean mTriangulate = false; - private static int CACHE_TILES_MAX = 400; + private static int CACHE_TILES_MAX = 250; private static int CACHE_TILES = CACHE_TILES_MAX; private static int LIMIT_BUFFERS = 20 * MB; @@ -127,28 +129,29 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { private JobParameters mJobParameter; private MapPosition mMapPosition, mPrevMapPosition; - private int mWidth, mHeight; - private float mAspect; + private static int mWidth, mHeight; + private static float mAspect; // draw position is updated from current position in onDrawFrame // keeping the position consistent while drawing - private double mDrawX, mDrawY, mDrawZ, mCurX, mCurY, mCurZ; - private float mDrawScale, mCurScale; + private static double mDrawX, mDrawY, mDrawZ, mCurX, mCurY, mCurZ; + private static float mDrawScale, mCurScale; // current center tile - private long mTileX, mTileY; + private static long mTileX, mTileY; - private FloatBuffer floatBuffer[]; - private ShortBuffer shortBuffer[]; + private static int rotateBuffers = 2; + private static FloatBuffer floatBuffer[]; + private static ShortBuffer shortBuffer[]; - boolean useHalfFloat = false; + static boolean useHalfFloat = false; // bytes currently loaded in VBOs - private int mBufferMemoryUsage; + private static int mBufferMemoryUsage; // flag set by updateVisibleList when current visible tiles changed. // used in onDrawFrame to nextTiles to curTiles - private boolean mUpdateTiles; + private static boolean mUpdateTiles; class TilesData { int cnt = 0; @@ -159,30 +162,30 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { } } - private float[] mMVPMatrix = new float[16]; + private static float[] mMVPMatrix = new float[16]; // private float[] mMMatrix = new float[16]; // private float[] mRMatrix = new float[16]; // newTiles is set in updateVisibleList and synchronized swapped // with nextTiles on main thread. // nextTiles is swapped with curTiles in onDrawFrame in GL thread. - private TilesData newTiles, nextTiles, curTiles; + private static TilesData newTiles, nextTiles, curTiles; private boolean mInitial; // shader handles - private int gLineProgram; - private int gLineVertexPositionHandle; - private int gLineTexturePositionHandle; - private int gLineColorHandle; - private int gLineMatrixHandle; - private int gLineModeHandle; - private int gLineWidthHandle; + private static int lineProgram; + private static int hLineVertexPosition; + private static int hLineTexturePosition; + private static int hLineColor; + private static int hLineMatrix; + private static int hLineMode; + private static int hLineWidth; - private int gPolygonProgram; - private int gPolygonVertexPositionHandle; - private int gPolygonMatrixHandle; - private int gPolygonColorHandle; + private static int polygonProgram; + private static int hPolygonVertexPosition; + private static int hPolygonMatrix; + private static int hPolygonColor; /** * @@ -261,7 +264,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { GLMapTile t = mTileList.remove(j); if (t.isActive) { - // Log.d(TAG, "EEEK removing active tile"); + Log.d(TAG, "EEEK removing active tile"); mTileList.add(t); continue; } @@ -441,18 +444,14 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { updateTileDistances(); - // scramble tile draw order, might help to make draw calls independent... just a guess :) + // scramble tile draw order: might help to make gl + // pipelines more independent... just a guess :) for (int i = 1; i < tiles / 2; i += 2) { GLMapTile tmp = newTiles.tiles[i]; newTiles.tiles[i] = newTiles.tiles[tiles - i]; newTiles.tiles[tiles - i] = tmp; } - int removes = mTiles.size() - CACHE_TILES; - - if (removes > 0) - Collections.sort(mTileList, mTileDistanceSort); - // pass new tile list to glThread synchronized (this) { for (int i = 0; i < nextTiles.cnt; i++) @@ -472,7 +471,12 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { mUpdateTiles = true; } - limitCache(removes); + int removes = mTiles.size() - CACHE_TILES; + + if (removes > 0) { + Collections.sort(mTileList, mTileDistanceSort); + limitCache(removes); + } if (mJobList.size() > 0) mMapView.addJobs(mJobList); @@ -593,7 +597,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { float alpha = 1.0f; - if (l.fadeLevel >= mDrawZ) { + if (l.area.fade >= mDrawZ) { alpha = (mDrawScale > 1.3f ? mDrawScale : 1.3f) - alpha; if (alpha > 1.0f) @@ -608,8 +612,8 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { blend = false; } - glUniform4f(gPolygonColorHandle, - l.colors[0], l.colors[1], l.colors[2], alpha); + glUniform4f(hPolygonColor, + l.area.color[0], l.area.color[1], l.area.color[2], alpha); // set stencil buffer mask used to draw this layer glStencilFunc(GL_EQUAL, 0xff, 1 << c); @@ -625,7 +629,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { private boolean drawPolygons(GLMapTile tile, int diff) { int cnt = 0; - if (tile.polygonLayers == null || tile.polygonLayers.array == null) + if (tile.polygonLayers == null) return true; glScissor(tile.sx, tile.sy, tile.sw, tile.sh); @@ -635,27 +639,23 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { glBindBuffer(GL_ARRAY_BUFFER, tile.polygonVBO.id); if (useHalfFloat) { - glVertexAttribPointer(gPolygonVertexPositionHandle, 2, + glVertexAttribPointer(hPolygonVertexPosition, 2, OES_HALF_FLOAT, false, 0, POLYGON_VERTICES_DATA_POS_OFFSET); } else { - glVertexAttribPointer(gPolygonVertexPositionHandle, 2, + glVertexAttribPointer(hPolygonVertexPosition, 2, GL_FLOAT, false, 0, POLYGON_VERTICES_DATA_POS_OFFSET); } - - // glBindBuffer(GL_ARRAY_BUFFER, 0); } setMatrix(tile, diff); - glUniformMatrix4fv(gPolygonMatrixHandle, 1, false, mMVPMatrix, 0); + glUniformMatrix4fv(hPolygonMatrix, 1, false, mMVPMatrix, 0); boolean firstPass = true; - for (int i = 0, n = tile.polygonLayers.array.length; i < n; i++) { - PolygonLayer l = tile.polygonLayers.array[i]; - + for (PolygonLayer l = tile.polygonLayers; l != null; l = l.next) { // fade out polygon layers (set in RederTheme) - if (l.fadeLevel > 0 && l.fadeLevel > mDrawZ) + if (l.area.fade > 0 && l.area.fade > mDrawZ) continue; if (cnt == 0) { @@ -668,8 +668,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { if (firstPass) firstPass = false; else { - // eeek, nexus! - cant do old-school polygons - // glFinish(); + GLES20.glFlush(); // clear stencilbuffer glStencilMask(0xFF); @@ -703,56 +702,16 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { // eeek, nexus! - cant do old-school polygons // glFinish(); } + GLES20.glFlush(); return true; } - private int mLastBoundVBO; - - // private boolean drawTriangles(GLMapTile tile, int diff) { - // - // if (tile.meshLayers == null || tile.meshLayers.array == null) - // return true; - // - // glScissor(tile.sx, tile.sy, tile.sw, tile.sh); - // - // if (mLastBoundVBO != tile.polygonVBO.id) { - // mLastBoundVBO = tile.polygonVBO.id; - // glBindBuffer(GL_ARRAY_BUFFER, tile.polygonVBO.id); - // - // if (useHalfFloat) { - // glVertexAttribPointer(gPolygonVertexPositionHandle, 2, - // OES_HALF_FLOAT, false, 0, - // POLYGON_VERTICES_DATA_POS_OFFSET); - // } else { - // glVertexAttribPointer(gPolygonVertexPositionHandle, 2, - // GL_FLOAT, false, 0, - // POLYGON_VERTICES_DATA_POS_OFFSET); - // } - // - // // glBindBuffer(GL_ARRAY_BUFFER, 0); - // } - // setMatrix(tile, diff); - // glUniformMatrix4fv(gPolygonMatrixHandle, 1, false, mMVPMatrix, 0); - // - // MeshLayer[] layers = tile.meshLayers.array; - // - // for (int i = 0, n = layers.length; i < n; i++) { - // MeshLayer l = layers[i]; - // glUniform4fv(gPolygonColorHandle, 1, l.colors, 0); - // - // // glUniform4f(gPolygonColorHandle, 1, 0, 0, 1); - // - // // System.out.println("draw: " + l.offset + " " + l.verticesCnt); - // glDrawArrays(GL_TRIANGLES, l.offset, l.verticesCnt); - // } - // - // return true; - // } + private static int mLastBoundVBO; private boolean drawLines(GLMapTile tile, int diff) { float z = 1; - if (tile.lineLayers == null || tile.lineLayers.array == null) + if (tile.lineLayers == null) return false; glScissor(tile.sx, tile.sy, tile.sw, tile.sh); @@ -762,27 +721,24 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { glBindBuffer(GL_ARRAY_BUFFER, tile.lineVBO.id); if (useHalfFloat) { - glVertexAttribPointer(gLineVertexPositionHandle, 2, OES_HALF_FLOAT, + glVertexAttribPointer(hLineVertexPosition, 2, OES_HALF_FLOAT, false, 8, LINE_VERTICES_DATA_POS_OFFSET); - glVertexAttribPointer(gLineTexturePositionHandle, 2, OES_HALF_FLOAT, + glVertexAttribPointer(hLineTexturePosition, 2, OES_HALF_FLOAT, false, 8, LINE_VERTICES_DATA_TEX_OFFSET >> 1); } else { - glVertexAttribPointer(gLineVertexPositionHandle, 2, GL_FLOAT, + glVertexAttribPointer(hLineVertexPosition, 2, GL_FLOAT, false, 16, LINE_VERTICES_DATA_POS_OFFSET); - glVertexAttribPointer(gLineTexturePositionHandle, 2, GL_FLOAT, + glVertexAttribPointer(hLineTexturePosition, 2, GL_FLOAT, false, 16, LINE_VERTICES_DATA_TEX_OFFSET); } - // glBindBuffer(GL_ARRAY_BUFFER, 0); } if (diff != 0) z = (diff > 0) ? (1 << diff) : 1.0f / (1 << -diff); setMatrix(tile, diff); - glUniformMatrix4fv(gLineMatrixHandle, 1, false, mMVPMatrix, 0); - - LineLayer[] layers = tile.lineLayers.array; + glUniformMatrix4fv(hLineMatrix, 1, false, mMVPMatrix, 0); boolean drawOutlines = false; boolean drawFixed = false; @@ -792,52 +748,67 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { // linear scale for fixed lines float fdiv = 0.9f / (mDrawScale / z); - // int cnt = 0; - for (int i = 0, n = layers.length; i < n; i++) { - LineLayer l = layers[i]; + boolean first = true; + + for (LineLayer l = tile.lineLayers; l != null; l = l.next) { + Line line = l.line; + if (line.fade > 0 && line.fade > mDrawZ) + continue; // set line width and mode - if ((i == 0) || (l.isOutline != drawOutlines) || (l.isFixed != drawFixed)) { + if (first || (l.isOutline != drawOutlines) + || (line.fixed != drawFixed)) { + first = false; drawOutlines = l.isOutline; - drawFixed = l.isFixed; + drawFixed = line.fixed; if (drawOutlines) { - glUniform2f(gLineModeHandle, 0, wdiv); + glUniform2f(hLineMode, 0, wdiv); } else if (!drawFixed) { - glUniform2f(gLineModeHandle, 0, wdiv * 0.98f); + glUniform2f(hLineMode, 0, wdiv * 0.98f); } } if (drawFixed) { if (l.width < 1.0) - glUniform2f(gLineModeHandle, 0.4f, fdiv); + glUniform2f(hLineMode, 0.4f, fdiv); else - glUniform2f(gLineModeHandle, 0, fdiv); + glUniform2f(hLineMode, 0, fdiv); + } + + if (line.fade >= mDrawZ) { + float alpha = 1.0f; + + alpha = (mDrawScale > 1.3f ? mDrawScale : 1.3f) - alpha; + if (alpha > 1.0f) + alpha = 1.0f; + glUniform4f(hLineColor, + line.color[0], line.color[1], line.color[2], alpha); + } else { + glUniform4fv(hLineColor, 1, line.color, 0); } - glUniform4fv(gLineColorHandle, 1, l.colors, 0); if (drawOutlines) { - for (int j = 0, m = l.outlines.size(); j < m; j++) { - LineLayer o = l.outlines.get(j); + for (LineLayer o = l.outlines; o != null; o = o.outlines) { if (mSimpleLines) - glUniform1f(gLineWidthHandle, o.width); + glUniform1f(hLineWidth, o.width); glDrawArrays(GL_TRIANGLE_STRIP, o.offset, o.verticesCnt); } } else { if (mSimpleLines) - glUniform1f(gLineWidthHandle, l.width); + glUniform1f(hLineWidth, l.width); glDrawArrays(GL_TRIANGLE_STRIP, l.offset, l.verticesCnt); } } - + GLES20.glFlush(); return true; } - private void setMatrix(GLMapTile tile, int diff) { + private static void setMatrix(GLMapTile tile, int diff) { float x, y, scale; float z = 1; @@ -858,7 +829,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { mMVPMatrix[5] = (scale); } - private boolean setTileScissor(GLMapTile tile, float div) { + private static boolean setTileScissor(GLMapTile tile, float div) { double dx, dy, scale; @@ -916,12 +887,14 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { private int uploadCnt = 0; private boolean uploadTileData(GLMapTile tile) { + ShortBuffer sbuf = null; + FloatBuffer fbuf = null; // double start = SystemClock.uptimeMillis(); - // use multiple buffers to avoid overwriting buffer while current data is uploaded - // (or rather the blocking which is required to avoid this) - if (uploadCnt >= 10) { + // use multiple buffers to avoid overwriting buffer while current + // data is uploaded (or rather the blocking which is required to avoid this) + if (uploadCnt >= rotateBuffers) { // mMapView.requestRender(); // return false; uploadCnt = 0; @@ -939,27 +912,34 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { tile.polygonVBO = mVBOs.remove(mVBOs.size() - 1); } } - if (useHalfFloat) - shortBuffer[uploadCnt * 2] = tile.lineLayers - .compileLayerData(shortBuffer[uploadCnt * 2]); - else - floatBuffer[uploadCnt * 2] = tile.lineLayers - .compileLayerData(floatBuffer[uploadCnt * 2]); + int size = 0; - if (tile.lineLayers.size > 0) { + if (useHalfFloat) { + sbuf = LineLayers.compileLayerData(tile.lineLayers, + shortBuffer[uploadCnt * 2]); + size = sbuf.remaining(); + shortBuffer[uploadCnt * 2] = sbuf; + } else { + fbuf = LineLayers.compileLayerData(tile.lineLayers, + floatBuffer[uploadCnt * 2]); + size = fbuf.remaining(); + floatBuffer[uploadCnt * 2] = fbuf; + } + + if (size > 0) { mBufferMemoryUsage -= tile.lineVBO.size; glBindBuffer(GL_ARRAY_BUFFER, tile.lineVBO.id); // glBufferData(GL_ARRAY_BUFFER, 0, null, GL_DYNAMIC_DRAW); if (useHalfFloat) { - tile.lineVBO.size = tile.lineLayers.size * SHORT_BYTES; + tile.lineVBO.size = size * SHORT_BYTES; glBufferData(GL_ARRAY_BUFFER, tile.lineVBO.size, - shortBuffer[uploadCnt * 2], GL_DYNAMIC_DRAW); + sbuf, GL_DYNAMIC_DRAW); } else { - tile.lineVBO.size = tile.lineLayers.size * FLOAT_BYTES; + tile.lineVBO.size = size * FLOAT_BYTES; glBufferData(GL_ARRAY_BUFFER, tile.lineVBO.size, - floatBuffer[uploadCnt * 2], GL_DYNAMIC_DRAW); + fbuf, GL_DYNAMIC_DRAW); } mBufferMemoryUsage += tile.lineVBO.size; @@ -968,16 +948,19 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { tile.lineLayers = null; } - // if (!mTriangulate) { - if (useHalfFloat) - shortBuffer[uploadCnt * 2 + 1] = tile.polygonLayers - .compileLayerData(shortBuffer[uploadCnt * 2 + 1]); - else - floatBuffer[uploadCnt * 2 + 1] = tile.polygonLayers - .compileLayerData(floatBuffer[uploadCnt * 2 + 1]); - + if (useHalfFloat) { + sbuf = PolygonLayers.compileLayerData(tile.polygonLayers, + shortBuffer[uploadCnt * 2 + 1]); + size = sbuf.remaining(); + shortBuffer[uploadCnt * 2 + 1] = sbuf; + } else { + fbuf = PolygonLayers.compileLayerData(tile.polygonLayers, + floatBuffer[uploadCnt * 2 + 1]); + size = fbuf.remaining(); + floatBuffer[uploadCnt * 2 + 1] = fbuf; + } // Upload polygon data to vertex buffer object - if (tile.polygonLayers.size > 0) { + if (size > 0) { mBufferMemoryUsage -= tile.polygonVBO.size; glBindBuffer(GL_ARRAY_BUFFER, tile.polygonVBO.id); @@ -985,54 +968,24 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { // GL_DYNAMIC_DRAW); if (useHalfFloat) { - tile.polygonVBO.size = tile.polygonLayers.size * SHORT_BYTES; + tile.polygonVBO.size = size * SHORT_BYTES; glBufferData(GL_ARRAY_BUFFER, tile.polygonVBO.size, - shortBuffer[uploadCnt * 2 + 1], GL_DYNAMIC_DRAW); + sbuf, GL_DYNAMIC_DRAW); } else { - tile.polygonVBO.size = tile.polygonLayers.size * FLOAT_BYTES; + tile.polygonVBO.size = size * FLOAT_BYTES; glBufferData(GL_ARRAY_BUFFER, tile.polygonVBO.size, - floatBuffer[uploadCnt * 2 + 1], GL_DYNAMIC_DRAW); + fbuf, GL_DYNAMIC_DRAW); } mBufferMemoryUsage += tile.polygonVBO.size; } else { tile.polygonLayers = null; } - // } - // else { - // if (useHalfFloat) - // shortBuffer[uploadCnt * 2 + 1] = tile.meshLayers - // .compileLayerData(shortBuffer[uploadCnt * 2 + 1]); - // else - // floatBuffer[uploadCnt * 2 + 1] = tile.meshLayers - // .compileLayerData(floatBuffer[uploadCnt * 2 + 1]); - // - // // Upload triangle data to vertex buffer object - // if (tile.meshLayers.size > 0) { - // mBufferMemoryUsage -= tile.polygonVBO.size; - // - // glBindBuffer(GL_ARRAY_BUFFER, tile.polygonVBO.id); - // // glBufferData(GL_ARRAY_BUFFER, 0, null, - // // GL_DYNAMIC_DRAW); - // - // if (useHalfFloat) { - // tile.polygonVBO.size = tile.meshLayers.size * SHORT_BYTES; - // glBufferData(GL_ARRAY_BUFFER, tile.polygonVBO.size, - // shortBuffer[uploadCnt * 2 + 1], GL_DYNAMIC_DRAW); - // } else { - // tile.polygonVBO.size = tile.meshLayers.size * FLOAT_BYTES; - // glBufferData(GL_ARRAY_BUFFER, tile.polygonVBO.size, - // floatBuffer[uploadCnt * 2 + 1], GL_DYNAMIC_DRAW); - // } - // mBufferMemoryUsage += tile.polygonVBO.size; - // - // } else { - // tile.meshLayers = null; - // } - // } + tile.newData = false; tile.isDrawn = true; tile.isLoading = false; + // double compile = SystemClock.uptimeMillis(); // glFinish(); // double now = SystemClock.uptimeMillis(); @@ -1044,8 +997,6 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { return true; } - // private long startTime = SystemClock.uptimeMillis(); - @Override public void onDrawFrame(GL10 glUnused) { long start = 0, poly_time = 0, clear_time = 0; @@ -1057,20 +1008,8 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { start = SystemClock.uptimeMillis(); glStencilMask(0xFF); - glDisable(GL_SCISSOR_TEST); glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - // long endTime = SystemClock.uptimeMillis(); - // long dt = endTime - startTime; - // if (dt < 33) - // try { - // Thread.sleep(33 - dt); - // } catch (InterruptedException e) { - // Log.d(TAG, "interrupt"); - // return; - // } - // startTime = SystemClock.uptimeMillis(); - synchronized (this) { mDrawX = mCurX; mDrawY = mCurY; @@ -1114,13 +1053,20 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { uploadCnt = 0; mLastBoundVBO = -1; + int updateTextures = 0; + // check visible tiles, set tile clip scissors, upload new vertex data + for (int i = 0; i < tileCnt; i++) { GLMapTile tile = tiles[i]; if (!setTileScissor(tile, 1)) continue; + if (tile.texture == null && tile.labels != null && + mTextRenderer.drawToTexture(tile)) + updateTextures++; + if (tile.newData) { uploadTileData(tile); continue; @@ -1143,25 +1089,25 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { } } - if (GlUtils.checkGlOutOfMemory("upload: " + mBufferMemoryUsage) - && LIMIT_BUFFERS > MB) - LIMIT_BUFFERS -= MB; + if (updateTextures > 0) + mTextRenderer.compileTextures(); + + // if (GlUtils.checkGlOutOfMemory("upload: " + mBufferMemoryUsage) + // && LIMIT_BUFFERS > MB) + // LIMIT_BUFFERS -= MB; if (timing) clear_time = (SystemClock.uptimeMillis() - start); glEnable(GL_SCISSOR_TEST); - glUseProgram(gPolygonProgram); - glEnableVertexAttribArray(gPolygonVertexPositionHandle); + glUseProgram(polygonProgram); + glEnableVertexAttribArray(hPolygonVertexPosition); - // if (!mTriangulate) { glDisable(GL_BLEND); // Draw Polygons glEnable(GL_STENCIL_TEST); - // glEnableVertexAttribArray(gPolygonVertexPositionHandle); - for (int i = 0; i < tileCnt; i++) { if (tiles[i].isVisible) { GLMapTile tile = tiles[i]; @@ -1172,24 +1118,11 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { drawProxyPolygons(tile); } } - // GlUtils.checkGlError("polygons"); + glDisable(GL_STENCIL_TEST); - // } else { - // // Draw Triangles - // for (int i = 0; i < tileCnt; i++) { - // if (tiles[i].isVisible) { - // GLMapTile tile = tiles[i]; - // - // if (tile.isDrawn) - // drawTriangles(tile, 0); - // else - // drawProxyTriangles(tile); - // } - // } - // // GlUtils.checkGlError("triangles"); - // } + // required on GalaxyII, Android 2.3.3 (cant just VAA enable once...) - glDisableVertexAttribArray(gPolygonVertexPositionHandle); + glDisableVertexAttribArray(hPolygonVertexPosition); if (timing) { glFinish(); @@ -1198,10 +1131,10 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { // Draw lines glEnable(GL_BLEND); - glUseProgram(gLineProgram); + glUseProgram(lineProgram); - glEnableVertexAttribArray(gLineVertexPositionHandle); - glEnableVertexAttribArray(gLineTexturePositionHandle); + glEnableVertexAttribArray(hLineVertexPosition); + glEnableVertexAttribArray(hLineTexturePosition); for (int i = 0; i < tileCnt; i++) { if (tiles[i].isVisible) { @@ -1214,20 +1147,37 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { } } + glDisableVertexAttribArray(hLineVertexPosition); + glDisableVertexAttribArray(hLineTexturePosition); + + glDisable(GL_SCISSOR_TEST); + + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + mTextRenderer.beginDraw(); + for (int i = 0; i < tileCnt; i++) { + if (!tiles[i].isVisible || tiles[i].texture == null) + continue; + + setMatrix(tiles[i], 0); + + mTextRenderer.drawTile(tiles[i], mMVPMatrix); + } + mTextRenderer.endDraw(); + if (timing) { glFinish(); Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start) + " " + clear_time + " " + poly_time); } - glDisableVertexAttribArray(gLineVertexPositionHandle); - glDisableVertexAttribArray(gLineTexturePositionHandle); - // GlUtils.checkGlError("lines"); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } - private int[] mVboIds; + private static TextRenderer mTextRenderer; @Override public void onSurfaceChanged(GL10 glUnused, int width, int height) { + GlUtils.checkGlError("onSurfaceChanged"); + mVBOs.clear(); mTiles.clear(); mTileList.clear(); @@ -1246,20 +1196,27 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { mAspect = (float) height / width; glViewport(0, 0, width, height); + GlUtils.checkGlError("glViewport"); - int tiles = (mWidth / Tile.TILE_SIZE + 4) * (mHeight / Tile.TILE_SIZE + 4); - curTiles = new TilesData(tiles); - newTiles = new TilesData(tiles); - nextTiles = new TilesData(tiles); + int numTiles = (mWidth / (Tile.TILE_SIZE / 2) + 2) + * (mHeight / (Tile.TILE_SIZE / 2) + 2); + + curTiles = new TilesData(numTiles); + newTiles = new TilesData(numTiles); + nextTiles = new TilesData(numTiles); // Set up vertex buffer objects - int numVBO = (CACHE_TILES + tiles) * 2; - mVboIds = new int[numVBO]; + int numVBO = (CACHE_TILES + numTiles) * 2; + int[] mVboIds = new int[numVBO]; glGenBuffers(numVBO, mVboIds, 0); + GlUtils.checkGlError("glGenBuffers"); for (int i = 0; i < numVBO; i++) mVBOs.add(new VertexBufferObject(mVboIds[i])); + // Set up textures + mTextRenderer = new TextRenderer(numTiles * 2); + mDebugSettings = mMapView.getDebugSettings(); mJobParameter = mMapView.getJobParameters(); @@ -1273,14 +1230,14 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Set up the program for rendering lines - gLineProgram = GlUtils.createProgram(Shaders.gLineVertexShader, + lineProgram = GlUtils.createProgram(Shaders.gLineVertexShader, Shaders.gLineFragmentShader); - if (gLineProgram == 0) { + if (lineProgram == 0) { mSimpleLines = true; Log.e(TAG, "trying simple line program."); - gLineProgram = GlUtils.createProgram(Shaders.gLineVertexShader, + lineProgram = GlUtils.createProgram(Shaders.gLineVertexShader, Shaders.gLineFragmentShaderSimple); - if (gLineProgram == 0) { + if (lineProgram == 0) { Log.e(TAG, "Could not create line program."); return; } @@ -1290,48 +1247,51 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { if (ext.indexOf("GL_OES_vertex_half_float") >= 0) { useHalfFloat = true; - shortBuffer = new ShortBuffer[20]; + shortBuffer = new ShortBuffer[rotateBuffers * 2]; } else { - floatBuffer = new FloatBuffer[20]; + floatBuffer = new FloatBuffer[rotateBuffers * 2]; } Log.d(TAG, "Extensions: " + ext); - gLineMatrixHandle = glGetUniformLocation(gLineProgram, "u_center"); - gLineModeHandle = glGetUniformLocation(gLineProgram, "u_mode"); - gLineColorHandle = glGetUniformLocation(gLineProgram, "u_color"); - gLineVertexPositionHandle = GLES20 - .glGetAttribLocation(gLineProgram, "a_position"); - gLineTexturePositionHandle = glGetAttribLocation(gLineProgram, "a_st"); + hLineMatrix = glGetUniformLocation(lineProgram, "u_center"); + hLineMode = glGetUniformLocation(lineProgram, "u_mode"); + hLineColor = glGetUniformLocation(lineProgram, "u_color"); + hLineVertexPosition = GLES20.glGetAttribLocation(lineProgram, "a_position"); + hLineTexturePosition = glGetAttribLocation(lineProgram, "a_st"); if (mSimpleLines) - gLineWidthHandle = glGetUniformLocation(gLineProgram, "u_width"); + hLineWidth = glGetUniformLocation(lineProgram, "u_width"); // Set up the program for rendering polygons - gPolygonProgram = GlUtils.createProgram(Shaders.gPolygonVertexShader, + polygonProgram = GlUtils.createProgram(Shaders.gPolygonVertexShader, Shaders.gPolygonFragmentShader); - if (gPolygonProgram == 0) { + if (polygonProgram == 0) { Log.e(TAG, "Could not create polygon program."); return; } - gPolygonMatrixHandle = glGetUniformLocation(gPolygonProgram, "u_center"); - gPolygonVertexPositionHandle = glGetAttribLocation(gPolygonProgram, - "a_position"); - gPolygonColorHandle = glGetUniformLocation(gPolygonProgram, "u_color"); + hPolygonMatrix = glGetUniformLocation(polygonProgram, "u_center"); + hPolygonVertexPosition = glGetAttribLocation(polygonProgram, "a_position"); + hPolygonColor = glGetUniformLocation(polygonProgram, "u_color"); - // glUseProgram(gPolygonProgram); - // glEnableVertexAttribArray(gPolygonVertexPositionHandle); + // glUseProgram(polygonProgram); + // glEnableVertexAttribArray(hPolygonVertexPosition); // - // glUseProgram(gLineProgram); - // glEnableVertexAttribArray(gLineVertexPositionHandle); - // glEnableVertexAttribArray(gLineTexturePositionHandle); + // glUseProgram(lineProgram); + // glEnableVertexAttribArray(hLineVertexPosition); + // glEnableVertexAttribArray(hLineTexturePosition); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + // glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); glDepthMask(false); glDisable(GL_DITHER); glClearColor(0.98f, 0.98f, 0.97f, 1.0f); glClearStencil(0); + + GlUtils.checkGlError("onSurfaceCreated"); + } private void drawProxyLines(GLMapTile tile) { @@ -1397,38 +1357,6 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { } } - // private void drawProxyTriangles(GLMapTile tile) { - // if (tile.parent != null && tile.parent.isDrawn) { - // tile.parent.sx = tile.sx; - // tile.parent.sy = tile.sy; - // tile.parent.sw = tile.sw; - // tile.parent.sh = tile.sh; - // drawTriangles(tile.parent, -1); - // } else { - // int drawn = 0; - // - // for (int i = 0; i < 4; i++) { - // GLMapTile c = tile.child[i]; - // - // if (c != null && c.isDrawn && setTileScissor(c, 2)) { - // drawTriangles(c, 1); - // drawn++; - // } - // } - // - // if (drawn < 4 && tile.parent != null) { - // GLMapTile p = tile.parent.parent; - // if (p != null && p.isDrawn) { - // p.sx = tile.sx; - // p.sy = tile.sy; - // p.sw = tile.sw; - // p.sh = tile.sh; - // drawTriangles(p, -2); - // } - // } - // } - // } - @Override public boolean processedTile() { return true; diff --git a/src/org/mapsforge/android/glrenderer/PolygonLayer.java b/src/org/mapsforge/android/glrenderer/PolygonLayer.java index d8890fb3..bb8025d8 100644 --- a/src/org/mapsforge/android/glrenderer/PolygonLayer.java +++ b/src/org/mapsforge/android/glrenderer/PolygonLayer.java @@ -14,21 +14,21 @@ */ package org.mapsforge.android.glrenderer; -import java.util.LinkedList; +import org.mapsforge.android.rendertheme.renderinstruction.Area; class PolygonLayer extends Layer { - int fadeLevel; + PolygonLayer next; + Area area; private boolean first = true; private float originX; private float originY; - PolygonLayer(int layer, int color, int fade) { - super(layer, color); - fadeLevel = fade; - curItem = LayerPool.get(); - pool = new LinkedList(); - pool.add(curItem); + PolygonLayer(int layer, Area area) { + super(layer); + this.area = area; + curItem = VertexPool.get(); + pool = curItem; } void addPolygon(float[] points, int pos, int length) { diff --git a/src/org/mapsforge/android/glrenderer/PolygonLayers.java b/src/org/mapsforge/android/glrenderer/PolygonLayers.java index ebb35401..98de333c 100644 --- a/src/org/mapsforge/android/glrenderer/PolygonLayers.java +++ b/src/org/mapsforge/android/glrenderer/PolygonLayers.java @@ -22,50 +22,20 @@ import java.nio.ShortBuffer; import org.mapsforge.android.utils.FastMath; import org.mapsforge.core.Tile; -import android.util.SparseArray; - class PolygonLayers { private static final int NUM_VERTEX_FLOATS = 2; - private static final float[] mFillCoords = { -2, Tile.TILE_SIZE + 1, + static final float[] mFillCoords = { -2, Tile.TILE_SIZE + 1, Tile.TILE_SIZE + 1, Tile.TILE_SIZE + 1, -2, -2, Tile.TILE_SIZE + 1, -2 }; private static short[] mByteFillCoords = null; - private SparseArray layers; - - PolygonLayer[] array = null; - int size; - - PolygonLayers() { - layers = new SparseArray(10); - size = 4; - } - - PolygonLayer getLayer(int layer, int color, int fade) { - PolygonLayer l = layers.get(layer); - if (l != null) { - // if (color == l.color) - return l; - - // return getLayer(layer + 1, color, fade); - } - - l = new PolygonLayer(layer, color, fade); - layers.put(layer, l); - return l; - } - - FloatBuffer compileLayerData(FloatBuffer buf) { + static FloatBuffer compileLayerData(PolygonLayer layers, FloatBuffer buf) { FloatBuffer fbuf = buf; + int size = 4; - array = new PolygonLayer[layers.size()]; - - for (int i = 0, n = layers.size(); i < n; i++) { - PolygonLayer l = layers.valueAt(i); - array[i] = l; + for (PolygonLayer l = layers; l != null; l = l.next) size += l.verticesCnt; - } size *= NUM_VERTEX_FLOATS; @@ -81,38 +51,40 @@ class PolygonLayers { fbuf.put(mFillCoords, 0, 8); int pos = 4; - for (int i = 0, n = array.length; i < n; i++) { - PolygonLayer l = array[i]; + PoolItem last = null, items = null; - for (PoolItem item : l.pool) { + for (PolygonLayer l = layers; l != null; l = l.next) { + + for (PoolItem item = l.pool; item != null; item = item.next) { fbuf.put(item.vertices, 0, item.used); + last = item; } - l.offset = pos; pos += l.verticesCnt; - LayerPool.add(l.pool); + if (last != null) { + last.next = items; + items = l.pool; + } + l.pool = null; } - fbuf.flip(); + VertexPool.add(items); - // not needed for drawing - layers = null; + fbuf.flip(); return fbuf; } - ShortBuffer compileLayerData(ShortBuffer buf) { + static final short[] tmpItem = new short[PoolItem.SIZE]; + + static ShortBuffer compileLayerData(PolygonLayer layers, ShortBuffer buf) { ShortBuffer sbuf = buf; + int size = 4; - array = new PolygonLayer[layers.size()]; - - for (int i = 0, n = layers.size(); i < n; i++) { - PolygonLayer l = layers.valueAt(i); - array[i] = l; + for (PolygonLayer l = layers; l != null; l = l.next) size += l.verticesCnt; - } size *= NUM_VERTEX_FLOATS; @@ -124,7 +96,7 @@ class PolygonLayers { sbuf.clear(); } - short[] data = new short[PoolItem.SIZE]; + short[] data = tmpItem; if (mByteFillCoords == null) { mByteFillCoords = new short[8]; @@ -141,27 +113,38 @@ class PolygonLayers { sbuf.put(mByteFillCoords, 0, 8); int pos = 4; - for (int i = 0, n = array.length; i < n; i++) { - PolygonLayer l = array[i]; + PoolItem last = null, items = null; - for (int k = 0, m = l.pool.size(); k < m; k++) { - PoolItem item = l.pool.get(k); + for (PolygonLayer l = layers; l != null; l = l.next) { + + for (PoolItem item = l.pool; item != null; item = item.next) { PoolItem.toHalfFloat(item, data); sbuf.put(data, 0, item.used); + last = item; } l.offset = pos; pos += l.verticesCnt; - LayerPool.add(l.pool); + if (last != null) { + last.next = items; + items = l.pool; + } + l.pool = null; } - sbuf.flip(); + VertexPool.add(items); - // not needed for drawing - layers = null; + sbuf.flip(); return sbuf; } + + static void clear(PolygonLayer layers) { + for (PolygonLayer l = layers; l != null; l = l.next) { + if (l.pool != null) + VertexPool.add(l.pool); + } + } } diff --git a/src/org/mapsforge/android/glrenderer/PoolItem.java b/src/org/mapsforge/android/glrenderer/PoolItem.java index 2f92db25..1be769c1 100644 --- a/src/org/mapsforge/android/glrenderer/PoolItem.java +++ b/src/org/mapsforge/android/glrenderer/PoolItem.java @@ -24,13 +24,14 @@ import java.nio.IntBuffer; class PoolItem { final float[] vertices; int used; + PoolItem next; PoolItem() { vertices = new float[SIZE]; used = 0; } - static int SIZE = 256; + static int SIZE = 128; private static final float FLOAT_HALF_PREC = 5.96046E-8f; private static final float FLOAT_HALF_MAX = 65504f; diff --git a/src/org/mapsforge/android/glrenderer/Shaders.java b/src/org/mapsforge/android/glrenderer/Shaders.java index 1e9696f5..f4e30903 100644 --- a/src/org/mapsforge/android/glrenderer/Shaders.java +++ b/src/org/mapsforge/android/glrenderer/Shaders.java @@ -23,6 +23,8 @@ class Shaders { + "attribute vec2 a_st;" + "varying vec2 v_st;" + "void main() {" + // + " gl_Position = u_center * vec4(a_position.x, a_position.y, 0.0, 1.0);" + // + " v_st = a_position.zw;" + " gl_Position = u_center * a_position;" + " v_st = a_st;" + "}"; diff --git a/src/org/mapsforge/android/glrenderer/TextItem.java b/src/org/mapsforge/android/glrenderer/TextItem.java new file mode 100644 index 00000000..ee129cc9 --- /dev/null +++ b/src/org/mapsforge/android/glrenderer/TextItem.java @@ -0,0 +1,35 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.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.mapsforge.android.glrenderer; + +import org.mapsforge.android.rendertheme.renderinstruction.Caption; + +public class TextItem { + TextItem next; + + final float x, y; + final String text; + final Caption caption; + final float width; + + public TextItem(float x, float y, String text, Caption caption) { + this.x = x; + this.y = y; + this.text = text; + this.caption = caption; + this.width = caption.paint.measureText(text); + } + +} diff --git a/src/org/mapsforge/android/glrenderer/TextLayer.java b/src/org/mapsforge/android/glrenderer/TextLayer.java new file mode 100644 index 00000000..a833b601 --- /dev/null +++ b/src/org/mapsforge/android/glrenderer/TextLayer.java @@ -0,0 +1,28 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.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.mapsforge.android.glrenderer; + +import java.util.ArrayList; + +import org.mapsforge.android.rendertheme.renderinstruction.Caption; + +public class TextLayer { + public ArrayList labels; + + void addLabel(float x, float y, String text, Caption caption) { + + } + +} diff --git a/src/org/mapsforge/android/glrenderer/TextRenderer.java b/src/org/mapsforge/android/glrenderer/TextRenderer.java new file mode 100644 index 00000000..e339cf7a --- /dev/null +++ b/src/org/mapsforge/android/glrenderer/TextRenderer.java @@ -0,0 +1,405 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.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.mapsforge.android.glrenderer; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.ShortBuffer; + +import org.mapsforge.android.utils.GlUtils; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.opengl.GLES20; +import android.opengl.GLUtils; +import android.util.Log; + +public class TextRenderer { + private final static int TEXTURE_WIDTH = 512; + private final static int TEXTURE_HEIGHT = 256; + + final static int MAX_LABELS = 30; + + private final Bitmap mBitmap; + private final Canvas mCanvas; + private int mFontPadX = 1; + private int mFontPadY = 1; + private int mBitmapFormat; + private int mBitmapType; + private ByteBuffer mByteBuffer; + private FloatBuffer mFloatBuffer; + private TextTexture[] mTextures; + + private int mIndicesVBO; + private int mVerticesVBO; + + final static int INDICES_PER_SPRITE = 6; // Indices Per Sprite + final static int VERTICES_PER_SPRITE = 4; // Vertices Per Sprite + final static int FLOATS_PER_VERTICE = 4; + + private static int mTextProgram; + static int mTextUVPMatrixLocation; + static int mTextVertexLocation; + static int mTextTextureCoordLocation; + static int mTextUColorLocation; + + static Paint mPaint = new Paint(Color.BLACK); + + boolean debug = false; + float[] debugVertices = { + + 0, 0, + 0, 1, + + 0, TEXTURE_HEIGHT - 1, + 0, 0, + + TEXTURE_WIDTH - 1, 0, + 1, 1, + + TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1, + 1, 0, + + }; + + TextRenderer(int numTextures) { + mBitmap = Bitmap + .createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888); + mCanvas = new Canvas(mBitmap); + + mBitmapFormat = GLUtils.getInternalFormat(mBitmap); + mBitmapType = GLUtils.getType(mBitmap); + + mTextProgram = GlUtils.createProgram(textVertexShader, textFragmentShader); + + mTextUVPMatrixLocation = GLES20.glGetUniformLocation(mTextProgram, "mvp"); + mTextUColorLocation = GLES20.glGetUniformLocation(mTextProgram, "col"); + mTextVertexLocation = GLES20.glGetAttribLocation(mTextProgram, "vertex"); + mTextTextureCoordLocation = GLES20.glGetAttribLocation(mTextProgram, "tex_coord"); + + // mVertexBuffer = new float[]; + int bufferSize = numTextures + * MAX_LABELS * VERTICES_PER_SPRITE + * FLOATS_PER_VERTICE * (Float.SIZE / 8); + + mByteBuffer = ByteBuffer.allocateDirect(bufferSize) + .order(ByteOrder.nativeOrder()); + + mFloatBuffer = mByteBuffer.asFloatBuffer(); + + int[] textureIds = new int[numTextures]; + TextTexture[] textures = new TextTexture[numTextures]; + GLES20.glGenTextures(numTextures, textureIds, 0); + + for (int i = 0; i < numTextures; i++) { + // setup filters for texture + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]); + + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, + GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, + GLES20.GL_LINEAR); + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, + GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping + GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, + GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping + + // load the generated bitmap onto the texture + // GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmap, 0); + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap, + mBitmapType, 0); + + textures[i] = new TextTexture(textureIds[i]); + } + GlUtils.checkGlError("init textures"); + + mTextures = textures; + + // Setup triangle indices + short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE]; + int len = indices.length; + short j = 0; + for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) { + indices[i + 0] = (short) (j + 0); + indices[i + 1] = (short) (j + 1); + indices[i + 2] = (short) (j + 2); + indices[i + 3] = (short) (j + 2); + indices[i + 4] = (short) (j + 3); + indices[i + 5] = (short) (j + 0); + } + + ShortBuffer tmpIndices = mByteBuffer.asShortBuffer(); + + tmpIndices.put(indices, 0, len); + tmpIndices.flip(); + + int[] mVboIds = new int[2]; + GLES20.glGenBuffers(2, mVboIds, 0); + + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mVboIds[0]); + GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8), + tmpIndices, + GLES20.GL_STATIC_DRAW); + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); + + mIndicesVBO = mVboIds[0]; + mVerticesVBO = mVboIds[1]; + } + + boolean drawToTexture(GLMapTile tile) { + TextTexture tex = null; + + if (tile.labels.size() == 0) + return false; + + for (int i = 0; i < mTextures.length; i++) { + tex = mTextures[i]; + if (tex.tile == null) + break; + if (!tex.tile.isActive) + break; + + tex = null; + } + + if (tex == null) { + Log.d(TAG, "no textures left"); + return false; + } + if (tex.tile != null) + tex.tile.texture = null; + + // if (debug) + // mBitmap.eraseColor(0xaa0000aa); + // else + mBitmap.eraseColor(Color.TRANSPARENT); + + int pos = 0; + float[] buf = tex.vertices; + + float xx = mFontPadX; + float yy = 0; + float width, height; + + float y = 0; + float x = 0; + + int max = tile.labels.size(); + if (max > MAX_LABELS) + max = MAX_LABELS; + + if (debug) { + mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4], + debugVertices[5], mPaint); + mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8], + debugVertices[9], mPaint); + + mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4], + debugVertices[5], mPaint); + mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8], + debugVertices[9], mPaint); + } + int advanceY = 0; + // int advanceX = 0; + for (int i = 0; i < max; i++) { + TextItem t = tile.labels.get(i); + + height = (int) (t.caption.fontHeight) + 2 * mFontPadY; + width = t.width + 2 * mFontPadX; + + if (height > advanceY) + advanceY = (int) height; + + if (xx + width > TEXTURE_WIDTH) { + xx = mFontPadX; + y += advanceY; + advanceY = (int) height; + } + + yy = y + (height - 1) - t.caption.fontDescent - mFontPadY; + + if (t.caption.stroke != null) + mCanvas.drawText(t.text, xx + t.width / 2, yy, t.caption.stroke); + + mCanvas.drawText(t.text, xx + t.width / 2, yy, t.caption.paint); + + // Log.d(TAG, "draw: " + t.text + " at:" + (xx + t.width / 2) + " " + yy + " w:" + // + t.width + " " + cellHeight); + + if (width > TEXTURE_WIDTH) + width = TEXTURE_WIDTH; + + float halfWidth = width / 2.0f; + float halfHeight = height / 2.0f; + float x1 = t.x - halfWidth; + float y1 = t.y - halfHeight; + float x2 = t.x + halfWidth; + float y2 = t.y + halfHeight; + + float u1 = xx / TEXTURE_WIDTH; + float v1 = y / TEXTURE_HEIGHT; + float u2 = u1 + (width / TEXTURE_WIDTH); + float v2 = v1 + (height / TEXTURE_HEIGHT); + + buf[pos++] = x1; + buf[pos++] = y1; + buf[pos++] = u1; + buf[pos++] = v2; + + buf[pos++] = x2; + buf[pos++] = y1; + buf[pos++] = u2; + buf[pos++] = v2; + + buf[pos++] = x2; + buf[pos++] = y2; + buf[pos++] = u2; + buf[pos++] = v1; + + buf[pos++] = x1; + buf[pos++] = y2; + buf[pos++] = u1; + buf[pos++] = v1; + + // yy += cellHeight; + // x += width; + + xx += width; + + // y += cellHeight; + if (y > TEXTURE_HEIGHT) { + Log.d(TAG, "reached max labels"); + break; + } + } + + tex.length = pos; + tile.texture = tex; + tex.tile = tile; + // GlUtils.checkGlError("0"); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id); + // GlUtils.checkGlError("1"); + GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap, mBitmapFormat, + mBitmapType); + // GlUtils.checkGlError("2"); + + return true; + } + + private static String TAG = "TextRenderer"; + + void compileTextures() { + int offset = 0; + TextTexture tex; + + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); + + mFloatBuffer.clear(); + + for (int i = 0; i < mTextures.length; i++) { + tex = mTextures[i]; + if (tex.tile == null || !tex.tile.isActive) + continue; + + mFloatBuffer.put(tex.vertices, 0, tex.length); + tex.offset = offset; + offset += tex.length; + } + + mFloatBuffer.flip(); + // Log.d(TAG, "compileTextures" + mFloatBuffer.remaining() + " " + offset); + + // TODO use sub-bufferdata function + GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, offset * (Float.SIZE / 8), + mFloatBuffer, GLES20.GL_DYNAMIC_DRAW); + } + + void beginDraw() { + GLES20.glUseProgram(mTextProgram); + + GLES20.glEnableVertexAttribArray(mTextTextureCoordLocation); + GLES20.glEnableVertexAttribArray(mTextVertexLocation); + + if (debug) { + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); + mFloatBuffer.clear(); + mFloatBuffer.put(debugVertices, 0, 16); + mFloatBuffer.flip(); + GLES20.glVertexAttribPointer(mTextVertexLocation, 2, + GLES20.GL_FLOAT, false, 16, mFloatBuffer); + mFloatBuffer.position(2); + GLES20.glVertexAttribPointer(mTextTextureCoordLocation, 2, + GLES20.GL_FLOAT, false, 16, mFloatBuffer); + } else { + GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); + GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); + } + } + + void endDraw() { + + GLES20.glDisableVertexAttribArray(mTextTextureCoordLocation); + GLES20.glDisableVertexAttribArray(mTextVertexLocation); + } + + void drawTile(GLMapTile tile, float[] matrix) { + + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id); + GlUtils.checkGlError("bind"); + + GLES20.glUniformMatrix4fv(mTextUVPMatrixLocation, 1, false, matrix, 0); + GlUtils.checkGlError("matrix"); + + if (debug) { + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } else { + + GLES20.glVertexAttribPointer(mTextVertexLocation, 2, + GLES20.GL_FLOAT, false, 16, tile.texture.offset * 4); + + GLES20.glVertexAttribPointer(mTextTextureCoordLocation, 2, + GLES20.GL_FLOAT, false, 16, tile.texture.offset * 4 + 8); + + GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 16) * + INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0); + + } + + } + + private static String textVertexShader = "" + + "precision highp float; " + + "attribute vec4 vertex;" + + "attribute vec2 tex_coord;" + + "uniform mat4 mvp;" + + "varying vec2 tex_c;" + + "void main() {" + + " gl_Position = mvp * vertex;" + + " tex_c = tex_coord;" + + "}"; + + private static String textFragmentShader = "" + + "precision highp float;" + + "uniform sampler2D tex;" + + "uniform vec4 col;" + + "varying vec2 tex_c;" + + "void main() {" + + " gl_FragColor = texture2D(tex, tex_c);" + + "}"; + +} diff --git a/src/org/mapsforge/android/glrenderer/TextTexture.java b/src/org/mapsforge/android/glrenderer/TextTexture.java new file mode 100644 index 00000000..2ef358e3 --- /dev/null +++ b/src/org/mapsforge/android/glrenderer/TextTexture.java @@ -0,0 +1,32 @@ +/* + * Copyright 2010, 2011, 2012 mapsforge.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.mapsforge.android.glrenderer; + +public class TextTexture { + + final float[] vertices; + final int id; + int length; + int offset; + GLMapTile tile; + + String[] text; + + TextTexture(int textureID) { + vertices = new float[TextRenderer.MAX_LABELS * 16]; + id = textureID; + } + +} diff --git a/src/org/mapsforge/android/glrenderer/LayerPool.java b/src/org/mapsforge/android/glrenderer/VertexPool.java similarity index 53% rename from src/org/mapsforge/android/glrenderer/LayerPool.java rename to src/org/mapsforge/android/glrenderer/VertexPool.java index 21215a21..63200b8f 100644 --- a/src/org/mapsforge/android/glrenderer/LayerPool.java +++ b/src/org/mapsforge/android/glrenderer/VertexPool.java @@ -15,40 +15,62 @@ package org.mapsforge.android.glrenderer; -import java.util.LinkedList; +import android.annotation.SuppressLint; -class LayerPool { - static private LinkedList pool = null; - static private int count; +class VertexPool { + private static final int POOL_LIMIT = 8192; + + @SuppressLint("UseValueOf") + private static final Boolean lock = new Boolean(true); + + static private PoolItem pool = null; + static private int count = 0; static void init() { - if (pool == null) { - pool = new LinkedList(); - count = 0; - } } static PoolItem get() { - synchronized (pool) { + synchronized (lock) { if (count == 0) return new PoolItem(); count--; - PoolItem it = pool.pop(); + + PoolItem it = pool; + pool = pool.next; it.used = 0; + it.next = null; return it; } } - static void add(LinkedList items) { - synchronized (pool) { - int size = items.size(); + static void add(PoolItem items) { + if (items == null) + return; - while (count < 4096 && size-- > 0) { + synchronized (lock) { + PoolItem last = items; + + // limit pool items + while (count < POOL_LIMIT) { + if (last.next == null) { + break; + } + last = last.next; count++; - pool.add(items.pop()); } + + // clear references + PoolItem tmp2, tmp = last.next; + while (tmp != null) { + tmp2 = tmp; + tmp = tmp.next; + tmp2.next = null; + } + + last.next = pool; + pool = items; } } } diff --git a/src/org/mapsforge/android/input/TouchHandler.java b/src/org/mapsforge/android/input/TouchHandler.java index 21d8a265..75602a6a 100644 --- a/src/org/mapsforge/android/input/TouchHandler.java +++ b/src/org/mapsforge/android/input/TouchHandler.java @@ -19,6 +19,7 @@ import org.mapsforge.core.Tile; import android.content.Context; import android.os.CountDownTimer; +import android.os.SystemClock; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; @@ -59,7 +60,8 @@ public class TouchHandler { mMapView = mapView; mMapMoveDelta = viewConfiguration.getScaledTouchSlop(); mActivePointerId = INVALID_POINTER_ID; - mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener(mMapView)); + mScaleGestureDetector = new ScaleGestureDetector(context, new ScaleListener( + mMapView)); mGestureDetector = new GestureDetector(new MapGestureDetector(mMapView)); } @@ -90,7 +92,8 @@ public class TouchHandler { boolean scaling = mScaleGestureDetector.isInProgress(); if (!scaling && !mMoveThresholdReached) { - if (Math.abs(moveX) > 3 * mMapMoveDelta || Math.abs(moveY) > 3 * mMapMoveDelta) { + if (Math.abs(moveX) > 3 * mMapMoveDelta + || Math.abs(moveY) > 3 * mMapMoveDelta) { // the map movement threshold has been reached // longPressDetector.pressStop(); mMoveThresholdReached = true; @@ -179,15 +182,19 @@ public class TouchHandler { return true; } + private long lastRun = 0; + /** * @param motionEvent * ... * @return ... */ public boolean handleMotionEvent(MotionEvent motionEvent) { + // workaround for a bug in the ScaleGestureDetector, see Android issue // #12976 - if (motionEvent.getAction() != MotionEvent.ACTION_MOVE || motionEvent.getPointerCount() > 1) { + if (motionEvent.getAction() != MotionEvent.ACTION_MOVE + || motionEvent.getPointerCount() > 1) { mScaleGestureDetector.onTouchEvent(motionEvent); } @@ -197,23 +204,44 @@ public class TouchHandler { // // return true; // } int action = getAction(motionEvent); - + boolean ret = false; if (action == MotionEvent.ACTION_DOWN) { - return onActionDown(motionEvent); + ret = onActionDown(motionEvent); } else if (action == MotionEvent.ACTION_MOVE) { - return onActionMove(motionEvent); + ret = onActionMove(motionEvent); } else if (action == MotionEvent.ACTION_UP) { - return onActionUp(motionEvent); + ret = onActionUp(motionEvent); } else if (action == MotionEvent.ACTION_CANCEL) { - return onActionCancel(); + ret = onActionCancel(); // } else if (action == MotionEvent.ACTION_POINTER_DOWN) { // return onActionPointerDown(motionEvent); } else if (action == MotionEvent.ACTION_POINTER_UP) { - return onActionPointerUp(motionEvent); + ret = onActionPointerUp(motionEvent); } + // if (ret) { + // Log.d("", "" + ); + // + // // try { + // // + // // Thread.sleep(10); + // // } catch (InterruptedException e) { + // // // TODO Auto-generated catch block + // // // e.printStackTrace(); + // // } + // + // } + if (ret) { + // throttle input + long diff = SystemClock.uptimeMillis() - lastRun; + if (diff < 16) { + // Log.d("", "" + diff); + SystemClock.sleep(16 - diff); + } + lastRun = SystemClock.uptimeMillis(); + } // the event was not handled - return false; + return ret; } class MapGestureDetector extends SimpleOnGestureListener { @@ -254,7 +282,8 @@ public class TouchHandler { } @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, + float velocityY) { int w = Tile.TILE_SIZE * 20; int h = Tile.TILE_SIZE * 20; mPrevX = 0; @@ -265,7 +294,8 @@ public class TouchHandler { mTimer = null; } - mScroller.fling(0, 0, Math.round(velocityX) / 2, Math.round(velocityY) / 2, -w, w, -h, h); + mScroller.fling(0, 0, Math.round(velocityX) / 2, Math.round(velocityY) / 2, + -w, w, -h, h); // animate for two seconds mTimer = new CountDownTimer(2000, 20) { @Override diff --git a/src/org/mapsforge/android/rendertheme/IRenderCallback.java b/src/org/mapsforge/android/rendertheme/IRenderCallback.java index 19980f5f..bbaff643 100644 --- a/src/org/mapsforge/android/rendertheme/IRenderCallback.java +++ b/src/org/mapsforge/android/rendertheme/IRenderCallback.java @@ -15,6 +15,7 @@ package org.mapsforge.android.rendertheme; import org.mapsforge.android.rendertheme.renderinstruction.Area; +import org.mapsforge.android.rendertheme.renderinstruction.Caption; import org.mapsforge.android.rendertheme.renderinstruction.Line; import android.graphics.Bitmap; @@ -37,14 +38,8 @@ public interface IRenderCallback { * * @param caption * the text to be rendered. - * @param verticalOffset - * the vertical offset of the caption. - * @param paint - * the paint to be used for rendering the text. - * @param stroke - * an optional paint for the text casing (may be null). */ - void renderAreaCaption(String caption, float verticalOffset, Paint paint, Paint stroke); + void renderAreaCaption(Caption caption); /** * Renders an area symbol with the given bitmap. @@ -59,14 +54,8 @@ public interface IRenderCallback { * * @param caption * the text to be rendered. - * @param verticalOffset - * the vertical offset of the caption. - * @param paint - * the paint to be used for rendering the text. - * @param stroke - * an optional paint for the text casing (may be null). */ - void renderPointOfInterestCaption(String caption, float verticalOffset, Paint paint, Paint stroke); + void renderPointOfInterestCaption(Caption caption); /** * Renders a point of interest circle with the given parameters. diff --git a/src/org/mapsforge/android/rendertheme/RenderTheme.java b/src/org/mapsforge/android/rendertheme/RenderTheme.java index 74b7b39d..8324d31f 100644 --- a/src/org/mapsforge/android/rendertheme/RenderTheme.java +++ b/src/org/mapsforge/android/rendertheme/RenderTheme.java @@ -140,7 +140,7 @@ public class RenderTheme { public void matchNode(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel) { // List matchingList = matchingListNode; // MatchingCacheKey matchingCacheKey = matchingCacheKeyNode; - // + // if (!changed) { // if (matchingList != null) { // for (int i = 0, n = matchingList.size(); i < n; ++i) { @@ -158,11 +158,13 @@ public class RenderTheme { // matchingList.get(i).renderNode(renderCallback, tags); // } // } else { - // // cache miss + + // cache miss // matchingList = new ArrayList(); - // for (int i = 0, n = mRulesList.size(); i < n; ++i) { - // mRulesList.get(i).matchNode(renderCallback, tags, zoomLevel, matchingList); - // } + for (int i = 0, n = mRulesList.size(); i < n; ++i) { + mRulesList.get(i).matchNode(renderCallback, tags, zoomLevel); + // , matchingList + } // matchingCacheNodes.put(matchingCacheKey, matchingList); // } // @@ -214,7 +216,7 @@ public class RenderTheme { public synchronized RenderInstruction[] matchWay(IRenderCallback renderCallback, Tag[] tags, byte zoomLevel, - boolean closed, boolean changed) { + boolean closed, boolean render) { RenderInstruction[] renderInstructions = null; LRUCache matchingCache; @@ -240,12 +242,6 @@ public class RenderTheme { boolean found = matchingCache.containsKey(matchingCacheKey); if (found) { renderInstructions = matchingCache.get(matchingCacheKey); - - if (renderInstructions != null) { - for (int i = 0, n = renderInstructions.length; i < n; i++) - renderInstructions[i].renderWay(renderCallback, tags); - - } } else { // cache miss int c = (closed ? Closed.YES : Closed.NO); @@ -259,13 +255,17 @@ public class RenderTheme { renderInstructions = new RenderInstruction[matchingList.size()]; for (int i = 0, n = matchingList.size(); i < n; ++i) { RenderInstruction renderInstruction = matchingList.get(i); - renderInstruction.renderWay(renderCallback, tags); + renderInstructions[i] = renderInstruction; } } matchingCache.put(matchingCacheKey, renderInstructions); } + if (render && renderInstructions != null) { + for (int i = 0, n = renderInstructions.length; i < n; i++) + renderInstructions[i].renderWay(renderCallback, tags); + } // mRenderInstructions = renderInstructions; return renderInstructions; } diff --git a/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml b/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml index 12c70405..8f837911 100644 --- a/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml +++ b/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml @@ -5,29 +5,36 @@ version="1" map-background="#f0f0f0"> - + - + + + + + - + + + + + - - - + + + - + - + + - - @@ -99,15 +106,16 @@ + - + - - + + + + + + + @@ -132,19 +145,35 @@ - - - - - - - - - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + @@ -181,7 +210,7 @@ - + @@ -190,14 +219,23 @@ - + + > + + + - + - + + + + + - + - @@ -346,6 +384,8 @@ --> + + @@ -371,30 +411,30 @@ - + - + - + - + - + + fixed="true" fade="5"/> - + @@ -403,11 +443,11 @@ - - + @@ -426,7 +466,7 @@ - + @@ -434,10 +474,10 @@ - + - + @@ -558,11 +598,11 @@ - + - + @@ -579,6 +619,12 @@ + + + + + @@ -634,6 +680,15 @@ + + + + + + + + + @@ -642,6 +697,10 @@ + + + +