- started overlays

- started symbol layer
- move renderer and generator out of view package
  - hopefully the last big refactoring for a while...
- improve perspective, plane should be more far away to decrease foreshortening
This commit is contained in:
Hannes Janetzek 2012-10-09 13:23:15 +02:00
parent 2713f3bc6f
commit 33d8865d7b
128 changed files with 2360 additions and 1417 deletions

View File

@ -21,7 +21,7 @@
> >
<!-- android:theme="@style/Theme.TileMap" --> <!-- android:theme="@style/Theme.TileMap" -->
<activity <activity
android:name="org.oscim.app.TileMap" android:name="TileMap"
android:configChanges="orientation|screenSize" > android:configChanges="orientation|screenSize" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -15,4 +15,4 @@
# 'key.alias' for the name of the key to use. # 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target. # The password will be asked during the build when you use the 'release' target.
jar.libs.dir=lib #jar.libs.dir=

View File

@ -15,7 +15,7 @@
package org.oscim.app; package org.oscim.app;
import org.oscim.core.GeoPoint; import org.oscim.core.GeoPoint;
import org.oscim.view.MapPosition; import org.oscim.core.MapPosition;
import android.content.Context; import android.content.Context;
import android.location.Criteria; import android.location.Criteria;

View File

@ -4,21 +4,18 @@ import java.io.FileFilter;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import org.oscim.app.filefilter.FilterByFileExtension; import org.oscim.app.filefilter.FilterByFileExtension;
import org.oscim.app.filefilter.ValidMapFile;
import org.oscim.app.filefilter.ValidRenderTheme; import org.oscim.app.filefilter.ValidRenderTheme;
import org.oscim.app.filepicker.FilePicker; import org.oscim.app.filepicker.FilePicker;
import org.oscim.app.preferences.EditPreferences; import org.oscim.app.preferences.EditPreferences;
import org.oscim.core.GeoPoint; import org.oscim.core.GeoPoint;
import org.oscim.core.MapPosition;
import org.oscim.database.MapDatabases; import org.oscim.database.MapDatabases;
import org.oscim.theme.InternalRenderTheme; import org.oscim.theme.InternalRenderTheme;
import org.oscim.utils.AndroidUtils; import org.oscim.utils.AndroidUtils;
import org.oscim.view.DebugSettings; import org.oscim.view.DebugSettings;
import org.oscim.view.MapActivity; import org.oscim.view.MapActivity;
import org.oscim.view.MapPosition;
import org.oscim.view.MapView; import org.oscim.view.MapView;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
@ -59,11 +56,11 @@ public class TileMap extends MapActivity {
private static final int DIALOG_ENTER_COORDINATES = 0; private static final int DIALOG_ENTER_COORDINATES = 0;
private static final int DIALOG_INFO_MAP_FILE = 1; private static final int DIALOG_INFO_MAP_FILE = 1;
private static final int DIALOG_LOCATION_PROVIDER_DISABLED = 2; private static final int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
private static final FileFilter FILE_FILTER_EXTENSION_MAP = // private static final FileFilter FILE_FILTER_EXTENSION_MAP =
new FilterByFileExtension(".map"); // new FilterByFileExtension(".map");
private static final FileFilter FILE_FILTER_EXTENSION_XML = private static final FileFilter FILE_FILTER_EXTENSION_XML =
new FilterByFileExtension(".xml"); new FilterByFileExtension(".xml");
private static final int SELECT_MAP_FILE = 0; // private static final int SELECT_MAP_FILE = 0;
private static final int SELECT_RENDER_THEME_FILE = 1; private static final int SELECT_RENDER_THEME_FILE = 1;
LocationHandler mLocation; LocationHandler mLocation;
@ -232,11 +229,12 @@ public class TileMap extends MapActivity {
return mMapView.onTrackballEvent(event); return mMapView.onTrackballEvent(event);
} }
private void startMapFilePicker() { // private void startMapFilePicker() {
FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSION_MAP); // FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSION_MAP);
FilePicker.setFileSelectFilter(new ValidMapFile()); // FilePicker.setFileSelectFilter(new ValidMapFile());
startActivityForResult(new Intent(this, FilePicker.class), SELECT_MAP_FILE); // startActivityForResult(new Intent(this, FilePicker.class),
} // SELECT_MAP_FILE);
// }
private void startRenderThemePicker() { private void startRenderThemePicker() {
FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSION_XML); FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSION_XML);
@ -598,12 +596,12 @@ public class TileMap extends MapActivity {
// } // }
} }
static class VersionHelper { // static class VersionHelper {
@TargetApi(11) // @TargetApi(11)
static void refreshActionBarMenu(Activity activity) { // static void refreshActionBarMenu(Activity activity) {
activity.invalidateOptionsMenu(); // activity.invalidateOptionsMenu();
} // }
} // }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {

View File

@ -12,10 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view; package org.oscim.core;
import org.oscim.core.GeoPoint;
import org.oscim.core.MercatorProjection;
import android.opengl.Matrix; import android.opengl.Matrix;
@ -30,11 +27,13 @@ public class MapPosition {
public byte zoomLevel; public byte zoomLevel;
public float scale; public float scale;
public float angle; public float angle;
public float tilt;
public double x; public double x;
public double y; public double y;
public float[] rotation; public float[] viewMatrix;
public float[] rotateMatrix;
public MapPosition() { public MapPosition() {
this.zoomLevel = (byte) 1; this.zoomLevel = (byte) 1;
@ -46,9 +45,13 @@ public class MapPosition {
this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel); this.y = MercatorProjection.latitudeToPixelY(this.lat, zoomLevel);
} }
// FIXME remove this here
public void init() { public void init() {
rotation = new float[16]; viewMatrix = new float[16];
Matrix.setIdentityM(rotation, 0); Matrix.setIdentityM(viewMatrix, 0);
rotateMatrix = new float[16];
Matrix.setIdentityM(rotateMatrix, 0);
} }
/** /**
@ -60,7 +63,6 @@ public class MapPosition {
* ... * ...
*/ */
public MapPosition(GeoPoint geoPoint, byte zoomLevel, float scale) { public MapPosition(GeoPoint geoPoint, byte zoomLevel, float scale) {
// this.geoPoint = geoPoint;
this.zoomLevel = zoomLevel; this.zoomLevel = zoomLevel;
this.scale = scale; this.scale = scale;
this.lat = geoPoint.getLatitude(); this.lat = geoPoint.getLatitude();
@ -84,10 +86,10 @@ public class MapPosition {
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("MapPosition [geoPoint="); builder.append("MapPosition [");
builder.append("lat"); builder.append("lat=");
builder.append(this.lat); builder.append(this.lat);
builder.append("lon"); builder.append(", lon=");
builder.append(this.lon); builder.append(this.lon);
builder.append(", zoomLevel="); builder.append(", zoomLevel=");
builder.append(this.zoomLevel); builder.append(this.zoomLevel);

View File

@ -14,7 +14,6 @@
*/ */
package org.oscim.core; package org.oscim.core;
import org.oscim.view.MapPosition;
/** /**
* An implementation of the spherical Mercator projection. * An implementation of the spherical Mercator projection.

View File

@ -24,7 +24,8 @@ public class WebMercator {
*/ */
public static final String NAME = "SphericalMercator"; public static final String NAME = "SphericalMercator";
private static final double f900913 = 20037508.342789244; // earth radius * pi, roughly
public static final double f900913 = 20037508.342789244;
private static final double f900913_2 = 20037508.342789244 * 2; private static final double f900913_2 = 20037508.342789244 * 2;
/** /**

View File

@ -16,7 +16,7 @@ package org.oscim.database;
import java.util.Map; import java.util.Map;
import org.oscim.view.generator.JobTile; import org.oscim.generator.JobTile;
/** /**
* *

View File

@ -30,7 +30,7 @@ import org.oscim.database.QueryResult;
import org.oscim.database.mapfile.header.MapFileHeader; import org.oscim.database.mapfile.header.MapFileHeader;
import org.oscim.database.mapfile.header.MapFileInfo; import org.oscim.database.mapfile.header.MapFileInfo;
import org.oscim.database.mapfile.header.SubFileParameter; import org.oscim.database.mapfile.header.SubFileParameter;
import org.oscim.view.generator.JobTile; import org.oscim.generator.JobTile;
import android.os.Environment; import android.os.Environment;
@ -819,7 +819,7 @@ public class MapDatabase implements IMapDatabase {
Tag[] wayTags = sMapFileHeader.getMapFileInfo().wayTags; Tag[] wayTags = sMapFileHeader.getMapFileInfo().wayTags;
int[] textPos = new int[3]; int[] textPos = new int[3];
// float[] labelPosition; // float[] labelPosition;
boolean skippedWays = false; // boolean skippedWays = false;
int wayDataBlocks; int wayDataBlocks;
// skip string block // skip string block
@ -858,7 +858,7 @@ public class MapDatabase implements IMapDatabase {
if (tags == null) if (tags == null)
return false; return false;
skippedWays = true; // skippedWays = true;
mReadBuffer.setBufferPosition(pos); mReadBuffer.setBufferPosition(pos);
} }
@ -885,12 +885,12 @@ public class MapDatabase implements IMapDatabase {
// bit 5-8 represent the number of tag IDs // bit 5-8 represent the number of tag IDs
byte numberOfTags = (byte) (specialByte & WAY_NUMBER_OF_TAGS_BITMASK); byte numberOfTags = (byte) (specialByte & WAY_NUMBER_OF_TAGS_BITMASK);
boolean changed = skippedWays; // boolean changed = skippedWays;
skippedWays = false; // skippedWays = false;
if (numberOfTags != 0) { if (numberOfTags != 0) {
tags = mReadBuffer.readTags(wayTags, numberOfTags); tags = mReadBuffer.readTags(wayTags, numberOfTags);
changed = true; // changed = true;
} }
if (tags == null) if (tags == null)
return false; return false;
@ -904,8 +904,8 @@ public class MapDatabase implements IMapDatabase {
// check if the way has a name // check if the way has a name
if ((featureByte & WAY_FEATURE_NAME) != 0) { if ((featureByte & WAY_FEATURE_NAME) != 0) {
textPos[0] = mReadBuffer.readUnsignedInt(); textPos[0] = mReadBuffer.readUnsignedInt();
String str = mReadBuffer.readUTF8EncodedStringAt(stringOffset // String str =
+ textPos[0]); mReadBuffer.readUTF8EncodedStringAt(stringOffset + textPos[0]);
// if (changed) { // if (changed) {
// Tag[] tmp = tags; // Tag[] tmp = tags;
// tags = new Tag[tmp.length + 1]; // tags = new Tag[tmp.length + 1];

View File

@ -39,7 +39,7 @@ import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.MapInfo; import org.oscim.database.MapInfo;
import org.oscim.database.OpenResult; import org.oscim.database.OpenResult;
import org.oscim.database.QueryResult; import org.oscim.database.QueryResult;
import org.oscim.view.generator.JobTile; import org.oscim.generator.JobTile;
import android.os.Environment; import android.os.Environment;
import android.os.SystemClock; import android.os.SystemClock;
@ -65,8 +65,8 @@ public class MapDatabase implements IMapDatabase {
private static final String CACHE_FILE = "%d-%d-%d.tile"; private static final String CACHE_FILE = "%d-%d-%d.tile";
private static final String SERVER_ADDR = "city.informatik.uni-bremen.de"; private static final String SERVER_ADDR = "city.informatik.uni-bremen.de";
// private static final String URL = "/osci/map-live/"; private static final String URL = "/osci/map-live/";
private static final String URL = "/osci/oscim/"; // private static final String URL = "/osci/oscim/";
private final static float REF_TILE_SIZE = 4096.0f; private final static float REF_TILE_SIZE = 4096.0f;

View File

@ -62,7 +62,7 @@ import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.MapInfo; import org.oscim.database.MapInfo;
import org.oscim.database.OpenResult; import org.oscim.database.OpenResult;
import org.oscim.database.QueryResult; import org.oscim.database.QueryResult;
import org.oscim.view.generator.JobTile; import org.oscim.generator.JobTile;
import android.os.Environment; import android.os.Environment;
import android.os.SystemClock; import android.os.SystemClock;

View File

@ -33,7 +33,7 @@ import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.MapInfo; import org.oscim.database.MapInfo;
import org.oscim.database.OpenResult; import org.oscim.database.OpenResult;
import org.oscim.database.QueryResult; import org.oscim.database.QueryResult;
import org.oscim.view.generator.JobTile; import org.oscim.generator.JobTile;
import org.postgresql.PGConnection; import org.postgresql.PGConnection;
import android.util.Log; import android.util.Log;

View File

@ -24,7 +24,7 @@ import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.MapInfo; import org.oscim.database.MapInfo;
import org.oscim.database.OpenResult; import org.oscim.database.OpenResult;
import org.oscim.database.QueryResult; import org.oscim.database.QueryResult;
import org.oscim.view.generator.JobTile; import org.oscim.generator.JobTile;
/** /**
* *
@ -40,7 +40,7 @@ public class MapDatabase implements IMapDatabase {
private Tag[] mTags = { new Tag("natural", "water") }; private Tag[] mTags = { new Tag("natural", "water") };
private Tag[] mTagsWay = { new Tag("highway", "primary"), new Tag("name", "Highway Rd") }; private Tag[] mTagsWay = { new Tag("highway", "primary"), new Tag("name", "Highway Rd") };
private Tag[] mNameTags; // private Tag[] mNameTags;
private final MapInfo mMapInfo = private final MapInfo mMapInfo =
new MapInfo(new BoundingBox(-180, -90, 180, 90), new MapInfo(new BoundingBox(-180, -90, 180, 90),

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.generator; package org.oscim.generator;
//import static org.oscim.view.mapgenerator.JobTile.LOADING; //import static org.oscim.view.mapgenerator.JobTile.LOADING;

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.generator; package org.oscim.generator;
import org.oscim.core.Tile; import org.oscim.core.Tile;

View File

@ -12,11 +12,11 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.generator; package org.oscim.generator;
import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.TileGenerator;
import org.oscim.utils.PausableThread; import org.oscim.utils.PausableThread;
import org.oscim.view.renderer.MapRenderer;
import org.oscim.view.renderer.TileGenerator;
/** /**
* A MapWorker uses a {@link TileGenerator} to generate map tiles. It runs in a * A MapWorker uses a {@link TileGenerator} to generate map tiles. It runs in a

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.generator; package org.oscim.generator;
import java.util.Comparator; import java.util.Comparator;

View File

@ -0,0 +1,118 @@
/*
* Copyright 2012 Hannes Janetzek
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General 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 License for more details.
*
* You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
import android.opengl.GLES20;
class BufferObject {
private static BufferObject pool;
static synchronized BufferObject get() {
BufferObject bo;
if (pool == null) {
return null;
}
bo = pool;
pool = pool.next;
bo.next = null;
return bo;
}
// static synchronized BufferObject get(int size) {
// BufferObject bo, prev = null;
//
// if (pool == null) {
// return null;
// }
//
// int max = size * 4;
//
// for (bo = pool; bo != null; bo = bo.next) {
// if (bo.size > size && size < max)
// break;
//
// prev = bo;
// }
//
// if (prev != null && bo != null) {
// prev.next = bo.next;
// bo.next = null;
// return bo;
// }
//
// bo = pool;
// pool = pool.next;
// bo.next = null;
// return bo;
// }
static synchronized void release(BufferObject bo) {
bo.next = pool;
pool = bo;
}
// Note: only call from GL-Thread
static synchronized int limitUsage(int reduce) {
int vboIds[] = new int[10];
BufferObject[] tmp = new BufferObject[10];
int removed = 0;
int freed = 0;
for (BufferObject bo = pool; bo != null; bo = bo.next) {
if (bo.size > 0) {
freed += bo.size;
bo.size = 0;
vboIds[removed] = bo.id;
tmp[removed++] = bo;
if (removed == 10 || reduce < freed)
break;
}
}
if (removed > 0) {
GLES20.glDeleteBuffers(removed, vboIds, 0);
GLES20.glGenBuffers(removed, vboIds, 0);
for (int i = 0; i < removed; i++)
tmp[i].id = vboIds[i];
}
return freed;
}
static void init(int num) {
int[] mVboIds = new int[num];
GLES20.glGenBuffers(num, mVboIds, 0);
BufferObject bo;
for (int i = 1; i < num; i++) {
bo = new BufferObject(mVboIds[i]);
bo.next = pool;
pool = bo;
}
}
int id;
int size;
BufferObject next;
BufferObject(int id) {
this.id = id;
}
}

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General License along with * You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import static android.opengl.GLES20.GL_ARRAY_BUFFER; import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_BLEND; import static android.opengl.GLES20.GL_BLEND;
@ -31,13 +31,14 @@ import java.util.concurrent.locks.ReentrantLock;
import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.MapRenderer.TilesData;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers;
import org.oscim.theme.RenderTheme; import org.oscim.theme.RenderTheme;
import org.oscim.utils.GlUtils;
import org.oscim.view.MapPosition;
import org.oscim.view.MapView; import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition; import org.oscim.view.MapViewPosition;
import org.oscim.view.renderer.MapRenderer.TilesData;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
@ -55,7 +56,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static final int CACHE_TILES_MAX = 250; private static final int CACHE_TILES_MAX = 250;
private static final int LIMIT_BUFFERS = 16 * MB; private static final int LIMIT_BUFFERS = 16 * MB;
static final float COORD_MULTIPLIER = 8.0f; public static final float COORD_MULTIPLIER = 8.0f;
static int CACHE_TILES = CACHE_TILES_MAX; static int CACHE_TILES = CACHE_TILES_MAX;
@ -64,9 +65,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static MapPosition mMapPosition; private static MapPosition mMapPosition;
private static ArrayList<VertexBufferObject> mVBOs; // private static ArrayList<BufferObject> mVBOs;
private static int mWidth, mHeight; static int mWidth, mHeight;
private static int rotateBuffers = 2; private static int rotateBuffers = 2;
private static ShortBuffer shortBuffer[]; private static ShortBuffer shortBuffer[];
@ -77,7 +78,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static float[] mMVPMatrix = new float[16]; private static float[] mMVPMatrix = new float[16];
private static float[] mProjMatrix = new float[16]; private static float[] mProjMatrix = new float[16];
private static float[] mTmpMatrix = new float[16];
private static float[] mTileCoords = new float[8]; private static float[] mTileCoords = new float[8];
private static float[] mDebugCoords = new float[8];
// mNextTiles is set by TileLoader and swapped with // mNextTiles is set by TileLoader and swapped with
// mDrawTiles in onDrawFrame in GL thread. // mDrawTiles in onDrawFrame in GL thread.
@ -99,8 +102,22 @@ public class GLRenderer implements GLSurfaceView.Renderer {
static ReentrantLock tilelock = new ReentrantLock(); static ReentrantLock tilelock = new ReentrantLock();
static ReentrantLock drawlock = new ReentrantLock(); static ReentrantLock drawlock = new ReentrantLock();
// Add additional tiles that serve as placeholer when flipping
// over date-line.
// I dont really like this but cannot think of a better solution:
// the other option would be to run scanbox each time for upload,
// drawing, proxies and text layer. needing to add placeholder only
// happens rarely, unless you live on Fidschi
/* package */static int mHolderCount; /* package */static int mHolderCount;
static boolean[] vertexArray = { false, false };
// TODO
final class GLState {
boolean blend = false;
boolean depth = false;
}
// scanline fill class used to check tile visibility // scanline fill class used to check tile visibility
private static ScanBox mScanBox = new ScanBox() { private static ScanBox mScanBox = new ScanBox() {
@Override @Override
@ -120,33 +137,35 @@ public class GLRenderer implements GLSurfaceView.Renderer {
return; return;
// add placeholder tiles to show both sides // add placeholder tiles to show both sides
// of date line... // of date line. a little too complicated...
for (int x = x1; x < x2; x++) { for (int x = x1; x < x2; x++) {
MapTile holder = null; MapTile holder = null;
MapTile tile = null; MapTile tile = null;
boolean found = false; boolean found = false;
int xx = x;
if (x >= 0 && x < xmax) if (x >= 0 && x < xmax)
continue; continue;
int xx = x;
if (x < 0) if (x < 0)
xx = xmax + x; xx = xmax + x;
else else
xx = x - xmax; xx = x - xmax;
if (xx < 0 || xx >= xmax) if (xx < 0 || xx >= xmax) {
// Log.d(TAG, "out of bounds " + y + " " + x + "/" + xx);
continue; continue;
}
for (int i = cnt; i < cnt + mHolderCount; i++) for (int i = cnt; i < cnt + mHolderCount; i++)
if (tiles[i].tileX == x && tiles[i].tileY == y) { if (tiles[i].tileX == x && tiles[i].tileY == y) {
found = true; found = true;
break; break;
} }
if (found) if (found) {
// Log.d(TAG, "already added " + y + " " + x + "/" + xx);
continue; continue;
}
for (int i = 0; i < cnt; i++) for (int i = 0; i < cnt; i++)
if (tiles[i].tileX == xx && tiles[i].tileY == y) { if (tiles[i].tileX == xx && tiles[i].tileY == y) {
@ -154,11 +173,10 @@ public class GLRenderer implements GLSurfaceView.Renderer {
break; break;
} }
if (tile == null) if (tile == null) {
// Log.d(TAG, "not found " + y + " " + x + "/" + xx);
continue; continue;
}
// Log.d(TAG, "add placeholder " + y + " " + x + ">>" + xx + " "
// + tile);
holder = new MapTile(x, y, mZoom); holder = new MapTile(x, y, mZoom);
holder.isVisible = true; holder.isVisible = true;
@ -173,7 +191,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
* the MapView * the MapView
*/ */
public GLRenderer(MapView mapView) { public GLRenderer(MapView mapView) {
Log.d(TAG, "init MapRenderer");
mMapView = mapView; mMapView = mapView;
mMapViewPosition = mapView.getMapViewPosition(); mMapViewPosition = mapView.getMapViewPosition();
@ -208,14 +225,18 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mUpdateTiles = false; mUpdateTiles = false;
} }
private static ArrayList<Overlay> mOverlays;
/** /**
* Called by TileLoader when list of active tiles changed. the list is * Called by TileLoader when list of active tiles changed. the list is
* copied to mNextTiles to be used in next call to onDrawFrame * copied to mNextTiles to be used in next call to onDrawFrame
* *
* @param tiles * @param tiles
* active tiles * active tiles
* @param overlays
* ...
*/ */
static void updateTiles(TilesData tiles) { static void updateTiles(TilesData tiles, ArrayList<Overlay> overlays) {
MapTile[] newTiles = tiles.tiles; MapTile[] newTiles = tiles.tiles;
@ -223,6 +244,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
for (int i = 0, n = tiles.cnt; i < n; i++) for (int i = 0, n = tiles.cnt; i < n; i++)
newTiles[i].lock(); newTiles[i].lock();
mOverlays = overlays;
// dont flip next/drawTiles while copying // dont flip next/drawTiles while copying
GLRenderer.tilelock.lock(); GLRenderer.tilelock.lock();
@ -243,28 +266,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
GLRenderer.tilelock.unlock(); GLRenderer.tilelock.unlock();
} }
/**
* called by TileLoader. when tile is removed from cache reuse its vbo.
*
* @param vbo
* the VBO
*/
static void addVBO(VertexBufferObject vbo) {
synchronized (mVBOs) {
mVBOs.add(vbo);
}
}
void setVBO(MapTile tile) {
synchronized (mVBOs) {
int numVBOs = mVBOs.size();
if (numVBOs > 0 && tile.vbo == null) {
tile.vbo = mVBOs.remove(numVBOs - 1);
}
}
}
void setRenderTheme(RenderTheme t) { void setRenderTheme(RenderTheme t) {
int bg = t.getMapBackground(); int bg = t.getMapBackground();
float[] c = new float[4]; float[] c = new float[4];
@ -276,161 +277,121 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mUpdateColor = true; mUpdateColor = true;
} }
private int uploadCnt = 0; private static int uploadCnt = 0;
private boolean uploadTileData(MapTile tile) { private static boolean uploadLayers(Layers layers, BufferObject vbo, boolean addFill) {
// Upload line data to vertex buffer object
// Log.d(TAG, "uploadTileData, " + tile);
int lineSize = LineRenderer.sizeOf(tile.lineLayers); int newSize = layers.getSize();
int polySize = PolygonRenderer.sizeOf(tile.polygonLayers); if (newSize == 0)
int newSize = lineSize + polySize;
if (newSize == 0) {
LineRenderer.clear(tile.lineLayers);
PolygonRenderer.clear(tile.polygonLayers);
tile.lineLayers = null;
tile.polygonLayers = null;
tile.newData = false;
return false; return false;
}
GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); GLES20.glBindBuffer(GL_ARRAY_BUFFER, vbo.id);
// use multiple buffers to avoid overwriting buffer while current // use multiple buffers to avoid overwriting buffer while current
// data is uploaded (or rather the blocking which is probably done to // data is uploaded (or rather the blocking which is probably done to
// avoid overwriting) // avoid overwriting)
if (uploadCnt >= rotateBuffers) { if (uploadCnt >= rotateBuffers) {
uploadCnt = 0; uploadCnt = 0;
// GLES20.glFlush();
} }
ShortBuffer sbuf = shortBuffer[uploadCnt]; ShortBuffer sbuf = shortBuffer[uploadCnt];
// add fill coordinates // add fill coordinates
if (addFill)
newSize += 8; newSize += 8;
// probably not a good idea to do this in gl thread... // probably not a good idea to do this in gl thread...
if (sbuf.capacity() < newSize) { if (sbuf.capacity() < newSize) {
ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES) sbuf = ByteBuffer
.order(ByteOrder.nativeOrder()); .allocateDirect(newSize * SHORT_BYTES)
sbuf = bbuf.asShortBuffer(); .order(ByteOrder.nativeOrder())
.asShortBuffer();
shortBuffer[uploadCnt] = sbuf; shortBuffer[uploadCnt] = sbuf;
if (addFill)
sbuf.put(mFillCoords, 0, 8); sbuf.put(mFillCoords, 0, 8);
} else {
sbuf.clear();
if (addFill)
sbuf.put(mFillCoords, 0, 8);
// if (addFill)
// sbuf.position(8);
} }
sbuf.clear(); layers.compile(sbuf, addFill);
sbuf.position(8);
PolygonRenderer.compileLayerData(tile.polygonLayers, sbuf);
tile.lineOffset = (8 + polySize);
if (tile.lineOffset != sbuf.position())
Log.d(TAG, "tiles lineoffset is wrong: " + tile + " "
+ tile.lineOffset + " "
+ sbuf.position() + " "
+ sbuf.limit() + " "
+ sbuf.remaining() + " "
+ PolygonRenderer.sizeOf(tile.polygonLayers) + " "
+ tile.rel);
tile.lineOffset *= SHORT_BYTES;
LineRenderer.compileLayerData(tile.lineLayers, sbuf);
sbuf.flip(); sbuf.flip();
if (newSize != sbuf.remaining()) { if (newSize != sbuf.remaining()) {
Log.d(TAG, "tiles wrong: " + tile + " " Log.d(TAG, "wrong size: "
+ newSize + " " + newSize + " "
+ sbuf.position() + " " + sbuf.position() + " "
+ sbuf.limit() + " " + sbuf.limit() + " "
+ sbuf.remaining() + " " + sbuf.remaining());
+ LineRenderer.sizeOf(tile.lineLayers)
+ tile.isLoading + " "
+ tile.rel);
tile.newData = false; // tile.newData = false;
return false; return false;
} }
newSize *= SHORT_BYTES; newSize *= SHORT_BYTES;
// reuse memory allocated for vbo when possible and allocated // reuse memory allocated for vbo when possible and allocated
// memory is less then four times the new data // memory is less then four times the new data
if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4 if (vbo.size > newSize && vbo.size < newSize * 4
&& mBufferMemoryUsage < LIMIT_BUFFERS) { && mBufferMemoryUsage < LIMIT_BUFFERS) {
GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf); GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf);
} else { } else {
mBufferMemoryUsage -= tile.vbo.size; mBufferMemoryUsage -= vbo.size;
tile.vbo.size = newSize; vbo.size = newSize;
GLES20.glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW); GLES20.glBufferData(GL_ARRAY_BUFFER, vbo.size, sbuf, GL_DYNAMIC_DRAW);
mBufferMemoryUsage += tile.vbo.size; mBufferMemoryUsage += vbo.size;
} }
uploadCnt++; uploadCnt++;
tile.isReady = true;
tile.newData = false;
return true; return true;
} }
private static boolean uploadTileData(MapTile tile) {
if (uploadLayers(tile.layers, tile.vbo, true))
tile.isReady = true;
tile.newData = false;
return tile.isReady;
}
private static boolean uploadOverlayData(Overlay overlay) {
if (uploadLayers(overlay.layers, overlay.vbo, true))
overlay.isReady = true;
overlay.newData = false;
return overlay.isReady;
}
private static void checkBufferUsage() { private static void checkBufferUsage() {
// try to clear some unused vbo when exceding limit
if (mBufferMemoryUsage < LIMIT_BUFFERS) { if (mBufferMemoryUsage < LIMIT_BUFFERS) {
if (CACHE_TILES < CACHE_TILES_MAX) if (CACHE_TILES < CACHE_TILES_MAX)
CACHE_TILES += 50; CACHE_TILES += 50;
return; return;
} }
// try to clear some unused vbo when exceding limit
Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB"); Log.d(TAG, "buffer object usage: " + mBufferMemoryUsage / MB + "MB");
int vboIds[] = new int[10]; mBufferMemoryUsage -= BufferObject.limitUsage(2 * MB);
VertexBufferObject[] tmp = new VertexBufferObject[10];
int removed = 0;
synchronized (mVBOs) {
for (VertexBufferObject vbo : mVBOs) {
if (vbo.size == 0)
continue;
mBufferMemoryUsage -= vbo.size;
vbo.size = 0;
// this should free allocated memory but it does not.
// on HTC it causes OOM exception?!
// glBindBuffer(GL_ARRAY_BUFFER, vbo.id);
// glBufferData(GL_ARRAY_BUFFER, 0, null,
// GLES20.GL_STATIC_DRAW);
// recreate vbo instead
vboIds[removed] = vbo.id;
tmp[removed++] = vbo;
if (removed == 10)
break;
}
}
if (removed > 0) {
GLES20.glDeleteBuffers(removed, vboIds, 0);
GLES20.glGenBuffers(removed, vboIds, 0);
for (int i = 0; i < removed; i++)
tmp[i].id = vboIds[i];
Log.d(TAG, "now: " + mBufferMemoryUsage / MB + "MB"); Log.d(TAG, "now: " + mBufferMemoryUsage / MB + "MB");
}
if (mBufferMemoryUsage > LIMIT_BUFFERS && CACHE_TILES > 100) if (mBufferMemoryUsage > LIMIT_BUFFERS && CACHE_TILES > 100)
CACHE_TILES -= 50; CACHE_TILES -= 50;
} }
private static boolean mRotate = false;
private static void setMatrix(float[] matrix, MapTile tile, private static void setMatrix(float[] matrix, MapTile tile,
float div, boolean project) { float div, boolean project) {
MapPosition mapPosition = mMapPosition; MapPosition mapPosition = mMapPosition;
float x = (float) (tile.pixelX - mapPosition.x * div); float x = (float) (tile.pixelX - mapPosition.x * div);
@ -448,12 +409,10 @@ public class GLRenderer implements GLSurfaceView.Renderer {
matrix[0] = scale; matrix[0] = scale;
matrix[5] = scale; matrix[5] = scale;
if (mRotate) Matrix.multiplyMM(matrix, 0, mapPosition.viewMatrix, 0, matrix, 0);
Matrix.multiplyMM(matrix, 0, mapPosition.rotation, 0, matrix, 0);
if (project) if (project)
Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0); Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, matrix, 0);
} }
private static float scaleDiv(MapTile t) { private static float scaleDiv(MapTile t) {
@ -492,14 +451,18 @@ public class GLRenderer implements GLSurfaceView.Renderer {
| GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT
| GLES20.GL_STENCIL_BUFFER_BIT); | GLES20.GL_STENCIL_BUFFER_BIT);
// get current tiles to draw
if (mUpdateTiles) { if (mUpdateTiles) {
// get current tiles to draw
GLRenderer.tilelock.lock(); GLRenderer.tilelock.lock();
mUpdateTiles = false;
TilesData tmp = mDrawTiles; TilesData tmp = mDrawTiles;
mDrawTiles = mNextTiles; mDrawTiles = mNextTiles;
mNextTiles = tmp; mNextTiles = tmp;
mUpdateTiles = false;
GLRenderer.tilelock.unlock(); GLRenderer.tilelock.unlock();
// force update of mapPosition
mMapPosition.zoomLevel = -1;
} }
if (mDrawTiles == null || mDrawTiles.cnt == 0) { if (mDrawTiles == null || mDrawTiles.cnt == 0) {
@ -507,14 +470,16 @@ public class GLRenderer implements GLSurfaceView.Renderer {
return; return;
} }
mRotate = mMapView.enableRotation || mMapView.enableCompass;
// get current MapPosition, set mTileCoords (mapping of screen to model // get current MapPosition, set mTileCoords (mapping of screen to model
// coordinates) // coordinates)
MapPosition mapPosition = mMapPosition; MapPosition mapPosition = mMapPosition;
float[] coords = mTileCoords; float[] coords = mTileCoords;
boolean changed = mMapViewPosition.getMapPosition(mapPosition, coords); boolean changed = mMapViewPosition.getMapPosition(mapPosition, coords);
for (Overlay overlay : mOverlays) {
overlay.update(mMapView);
}
int tileCnt = mDrawTiles.cnt; int tileCnt = mDrawTiles.cnt;
MapTile[] tiles = mDrawTiles.tiles; MapTile[] tiles = mDrawTiles.tiles;
@ -539,24 +504,11 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mHolderCount = 0; mHolderCount = 0;
mScanBox.scan(coords, tiles[0].zoomLevel); mScanBox.scan(coords, tiles[0].zoomLevel);
}
tileCnt += mHolderCount; tileCnt += mHolderCount;
// // TODO get the right function: trying to accomodate for the // Log.d(TAG, "visible: " + tileCnt);
// y-stretching introduced by the perspective transformation...
// float sw, sh;
// sw = MapViewPosition.VIEW_SCALE;
// sh = MapViewPosition.VIEW_SCALE;
// sh += (mMapViewPosition.mTilt / 150);
//
// Matrix.frustumM(mProjMatrix, 0, -sw * mWidth, sw * mWidth,
// sh * mHeight, -sh * mHeight, 1, 2);
//
// Matrix.translateM(mProjMatrix, 0, 0, 0,
// -MapViewPosition.VIEW_DISTANCE);
//
// mProjMatrix[10] = 0;
// mProjMatrix[14] = 0;
}
uploadCnt = 0; uploadCnt = 0;
int updateTextures = 0; int updateTextures = 0;
@ -579,28 +531,33 @@ public class GLRenderer implements GLSurfaceView.Renderer {
uploadTileData(tile); uploadTileData(tile);
continue; continue;
} }
if (tile.holder != null) { if (tile.holder != null) {
if (tile.holder.newData) { // load tile that is referenced by this holder
if (tile.holder.newData)
uploadTileData(tile.holder); uploadTileData(tile.holder);
}
tile.isReady = tile.holder.isReady; tile.isReady = tile.holder.isReady;
} else if (!tile.isReady) { } else if (!tile.isReady) {
// check near relatives if they can serve as proxy // check near relatives than can serve as proxy
if ((tile.proxies & MapTile.PROXY_PARENT) != 0) {
MapTile rel = tile.rel.parent.tile; MapTile rel = tile.rel.parent.tile;
if (rel != null && rel.newData) { if (rel.newData)
uploadTileData(rel); uploadTileData(rel);
} else {
continue;
}
for (int c = 0; c < 4; c++) { for (int c = 0; c < 4; c++) {
if (tile.rel.child[c] == null) if ((tile.proxies & 1 << c) == 0)
continue; continue;
rel = tile.rel.child[c].tile; MapTile rel = tile.rel.child[c].tile;
if (rel != null && rel.newData) if (rel != null && rel.newData)
uploadTileData(rel); uploadTileData(rel);
} }
} }
} }
}
if (uploadCnt > 0) if (uploadCnt > 0)
checkBufferUsage(); checkBufferUsage();
@ -620,7 +577,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
// proxies are clipped to the region where nothing was drawn to depth // proxies are clipped to the region where nothing was drawn to depth
// buffer. TODO draw all parent before grandparent // buffer.
// TODO draw all parent before grandparent
// TODO draw proxies for placeholder...
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
if (t.isVisible && !t.isReady && (t.holder == null)) if (t.isVisible && !t.isReady && (t.holder == null))
@ -639,7 +598,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
int z = mapPosition.zoomLevel; int z = mapPosition.zoomLevel;
float s = mapPosition.scale; float s = mapPosition.scale;
int zoomLevelDiff = Math.max(z - TileGenerator.STROKE_MAX_ZOOM_LEVEL, 0); int zoomLevelDiff = Math.max(z - TileGenerator.STROKE_MAX_ZOOM_LEVEL,
0);
float scale = (float) Math.pow(1.4, zoomLevelDiff); float scale = (float) Math.pow(1.4, zoomLevelDiff);
if (scale < 1) if (scale < 1)
scale = 1; scale = 1;
@ -647,7 +607,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL) if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL)
TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix); TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix);
else else
TextRenderer.beginDraw(1 / s, mProjMatrix); TextRenderer.beginDraw(1f / s, mProjMatrix);
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
MapTile t = tiles[i]; MapTile t = tiles[i];
@ -669,12 +629,53 @@ public class GLRenderer implements GLSurfaceView.Renderer {
TextRenderer.endDraw(); TextRenderer.endDraw();
} }
// TODO call overlay renderer here // call overlay renderer
for (Overlay overlay : mOverlays) {
if (overlay.newData) {
if (overlay.vbo == null)
overlay.vbo = BufferObject.get();
if (overlay.vbo == null)
continue;
if (uploadOverlayData(overlay))
overlay.isReady = true;
}
if (!overlay.isReady)
continue;
// setMatrix(mMVPMatrix, overlay);
overlay.render(mMapPosition, mMVPMatrix, mProjMatrix);
}
if (MapView.debugFrameTime) { if (MapView.debugFrameTime) {
GLES20.glFinish(); GLES20.glFinish();
Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start)); Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start));
} }
if (debugView) {
float mm = 0.5f;
float min = -mm;
float max = mm;
float ymax = mm * mHeight / mWidth;
mDebugCoords[0] = min;
mDebugCoords[1] = ymax;
mDebugCoords[2] = max;
mDebugCoords[3] = ymax;
mDebugCoords[4] = min;
mDebugCoords[5] = -ymax;
mDebugCoords[6] = max;
mDebugCoords[7] = -ymax;
PolygonRenderer.debugDraw(mProjMatrix, mDebugCoords, 0);
mapPosition.zoomLevel = -1;
mMapViewPosition.getMapPosition(mapPosition, mDebugCoords);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0,
mapPosition.viewMatrix, 0);
PolygonRenderer.debugDraw(mMVPMatrix, mDebugCoords, 1);
}
GLRenderer.drawlock.unlock(); GLRenderer.drawlock.unlock();
} }
@ -701,54 +702,49 @@ public class GLRenderer implements GLSurfaceView.Renderer {
GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id);
LineLayer ll = tile.lineLayers;
PolygonLayer pl = tile.polygonLayers;
boolean clipped = false; boolean clipped = false;
int simpleShader = mRotate ? 0 : 1; int simpleShader = 0; // mRotate ? 0 : 1;
for (; pl != null || ll != null;) { for (Layer l = tile.layers.layers; l != null;) {
int lnext = Integer.MAX_VALUE;
int pnext = Integer.MAX_VALUE;
if (ll != null) switch (l.type) {
lnext = ll.layer; case Layer.POLYGON:
if (pl != null)
pnext = pl.layer;
if (pl != null && pnext < lnext) {
GLES20.glDisable(GL_BLEND); GLES20.glDisable(GL_BLEND);
pl = PolygonRenderer.drawPolygons(pos, pl, lnext, mvp, !clipped); l = PolygonRenderer.draw(pos, l, mvp, !clipped, true);
clipped = true; clipped = true;
} else { break;
// FIXME
case Layer.LINE:
if (!clipped) { if (!clipped) {
PolygonRenderer.drawPolygons(pos, null, 0, mvp, true); PolygonRenderer.draw(pos, null, mvp, true, true);
clipped = true; clipped = true;
} }
GLES20.glEnable(GL_BLEND); GLES20.glEnable(GL_BLEND);
ll = LineRenderer.drawLines(pos, ll, pnext, mvp, div, l = LineRenderer.draw(pos, l, mvp, div, simpleShader,
simpleShader, tile.lineOffset); tile.layers.lineOffset);
break;
}
}
if (tile.layers.symbolLayers != null) {
setMatrix(mvp, tile, div, false);
for (Layer l = tile.layers.symbolLayers; l != null;) {
l = TextureRenderer.draw(l, 1, mProjMatrix, mvp,
tile.layers.symbolOffset);
} }
} }
} }
// TODO should check tile.proxies here
private static boolean drawProxyChild(MapTile tile) { private static boolean drawProxyChild(MapTile tile) {
int drawn = 0; int drawn = 0;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (tile.rel.child[i] == null) if ((tile.proxies & 1 << i) == 0)
continue; continue;
MapTile c = tile.rel.child[i].tile; MapTile c = tile.rel.child[i].tile;
if (c == null)
continue;
// if (!isVisible(c)) {
// drawn++;
// continue;
// }
if (c.isReady) { if (c.isReady) {
drawTile(c); drawTile(c);
@ -812,13 +808,24 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mWidth = width; mWidth = width;
mHeight = height; mHeight = height;
// use this to scale only the view to see better which tiles are
// rendered
float s = MapViewPosition.VIEW_SCALE; float s = MapViewPosition.VIEW_SCALE;
Matrix.frustumM(mProjMatrix, 0, -s * width, s * width, float aspect = mHeight / (float) mWidth;
s * height, -s * height, 1, 2);
Matrix.translateM(mProjMatrix, 0, 0, 0, -MapViewPosition.VIEW_DISTANCE); Matrix.frustumM(mProjMatrix, 0, -1 * s, 1 * s,
aspect * s, -aspect * s, MapViewPosition.VIEW_NEAR,
MapViewPosition.VIEW_FAR);
Matrix.setIdentityM(mTmpMatrix, 0);
Matrix.translateM(mTmpMatrix, 0, 0, 0, -MapViewPosition.VIEW_DISTANCE);
Matrix.multiplyMM(mProjMatrix, 0, mProjMatrix, 0, mTmpMatrix, 0);
if (debugView) {
// modify this to scale only the view, to see better which tiles are
// rendered
Matrix.setIdentityM(mMVPMatrix, 0);
Matrix.scaleM(mMVPMatrix, 0, 0.5f, 0.5f, 1);
Matrix.multiplyMM(mProjMatrix, 0, mMVPMatrix, 0, mProjMatrix, 0);
}
// set to zero: we modify the z value with polygon-offset for clipping // set to zero: we modify the z value with polygon-offset for clipping
mProjMatrix[10] = 0; mProjMatrix[10] = 0;
@ -830,8 +837,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mMapView.redrawMap(); mMapView.redrawMap();
return; return;
} }
mNewSurface = false;
mNewSurface = false;
mBufferMemoryUsage = 0; mBufferMemoryUsage = 0;
int numTiles = (mWidth / (Tile.TILE_SIZE / 2) + 2) int numTiles = (mWidth / (Tile.TILE_SIZE / 2) + 2)
@ -839,14 +846,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// Set up vertex buffer objects // Set up vertex buffer objects
int numVBO = (CACHE_TILES + (numTiles * 2)); int numVBO = (CACHE_TILES + (numTiles * 2));
int[] mVboIds = new int[numVBO]; BufferObject.init(numVBO);
GLES20.glGenBuffers(numVBO, mVboIds, 0);
GlUtils.checkGlError("glGenBuffers");
mVBOs = new ArrayList<VertexBufferObject>(numVBO);
for (int i = 1; i < numVBO; i++)
mVBOs.add(new VertexBufferObject(mVboIds[i]));
// Set up textures // Set up textures
TextRenderer.setup(numTiles); TextRenderer.setup(numTiles);
@ -854,6 +854,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (mClearColor != null) if (mClearColor != null)
mUpdateColor = true; mUpdateColor = true;
vertexArray[0] = false;
vertexArray[1] = false;
// FIXME this should be synchronized // FIXME this should be synchronized
mMapView.redrawMap(); mMapView.redrawMap();
} }
@ -862,20 +865,19 @@ public class GLRenderer implements GLSurfaceView.Renderer {
void clearTiles(int numTiles) { void clearTiles(int numTiles) {
mDrawTiles = new TilesData(numTiles); mDrawTiles = new TilesData(numTiles);
mNextTiles = new TilesData(numTiles); mNextTiles = new TilesData(numTiles);
mMapPosition.zoomLevel = -1;
} }
@Override @Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) { public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS); // Log.d(TAG, "Extensions: " + ext);
Log.d(TAG, "Extensions: " + ext);
LineRenderer.init(); LineRenderer.init();
PolygonRenderer.init(); PolygonRenderer.init();
TextRenderer.init(); TextRenderer.init();
TextureRenderer.init();
mNewSurface = true; TextureObject.init(10);
// mUpdateColor = true;
// glEnable(GL_SCISSOR_TEST); // glEnable(GL_SCISSOR_TEST);
// glScissor(0, 0, mWidth, mHeight); // glScissor(0, 0, mWidth, mHeight);
@ -883,51 +885,10 @@ public class GLRenderer implements GLSurfaceView.Renderer {
GLES20.glDisable(GLES20.GL_CULL_FACE); GLES20.glDisable(GLES20.GL_CULL_FACE);
GLES20.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); GLES20.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
mNewSurface = true;
} }
private boolean mNewSurface; private boolean mNewSurface;
private static final boolean debugView = false;
} }
//
// private static boolean isVisible(MapTile tile) {
// float dx, dy, scale, div = 1;
// MapPosition mapPosition = mMapPosition;
// int diff = mapPosition.zoomLevel - tile.zoomLevel;
//
// if (diff < 0)
// div = (1 << -diff);
// else if (diff > 0)
// div = (1.0f / (1 << diff));
//
// scale = mapPosition.scale / div;
// dx = (float) (tile.pixelX - mapPosition.x * div);
// dy = (float) (tile.pixelY - mapPosition.y * div);
//
// int size = Tile.TILE_SIZE;
// int sx = (int) (dx * scale);
// int sy = (int) (dy * scale);
//
// // FIXME little hack, need to do scanline check or sth
// // this kindof works for typical screen aspect
// if (mRotate) {
// int ssize = mWidth > mHeight ? mWidth : mHeight;
// ssize += Tile.TILE_SIZE;
// if (sy > ssize / 2 || sx > ssize / 2
// || sx + size * scale < -ssize / 2
// || sy + size * scale < -ssize / 2) {
// tile.isVisible = false;
// return false;
// }
// } else {
// if (sy > mHeight / 2 || sx > mWidth / 2
// || sx + size * scale < -mWidth / 2
// || sy + size * scale < -mHeight / 2) {
// tile.isVisible = false;
// return false;
// }
// }
// tile.isVisible = true;
//
// return true;
// }

View File

@ -12,14 +12,15 @@
* You should have received a copy of the GNU Lesser General License along with * You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import java.nio.ShortBuffer;
import org.oscim.core.MapPosition;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
import org.oscim.utils.GlUtils; import org.oscim.utils.GlUtils;
import org.oscim.view.MapPosition;
import android.graphics.Paint.Cap;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.util.FloatMath; import android.util.FloatMath;
import android.util.Log; import android.util.Log;
@ -27,7 +28,7 @@ import android.util.Log;
class LineRenderer { class LineRenderer {
private final static String TAG = "LineRenderer"; private final static String TAG = "LineRenderer";
private static int NUM_VERTEX_SHORTS = 4; // private static int NUM_VERTEX_SHORTS = 4;
private static final int LINE_VERTICES_DATA_POS_OFFSET = 0; private static final int LINE_VERTICES_DATA_POS_OFFSET = 0;
private static final int LINE_VERTICES_DATA_TEX_OFFSET = 4; private static final int LINE_VERTICES_DATA_TEX_OFFSET = 4;
@ -40,6 +41,7 @@ class LineRenderer {
private static int[] hLineMatrix = new int[2]; private static int[] hLineMatrix = new int[2];
private static int[] hLineScale = new int[2]; private static int[] hLineScale = new int[2];
private static int[] hLineWidth = new int[2]; private static int[] hLineWidth = new int[2];
private static int[] hLineMode = new int[2];
static boolean init() { static boolean init() {
lineProgram[0] = GlUtils.createProgram(Shaders.lineVertexShader, lineProgram[0] = GlUtils.createProgram(Shaders.lineVertexShader,
@ -53,6 +55,7 @@ class LineRenderer {
hLineScale[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_wscale"); hLineScale[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_wscale");
hLineWidth[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_width"); hLineWidth[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_width");
hLineColor[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_color"); hLineColor[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_color");
hLineMode[0] = GLES20.glGetUniformLocation(lineProgram[0], "u_mode");
hLineVertexPosition[0] = GLES20.glGetAttribLocation(lineProgram[0], "a_position"); hLineVertexPosition[0] = GLES20.glGetAttribLocation(lineProgram[0], "a_position");
hLineTexturePosition[0] = GLES20.glGetAttribLocation(lineProgram[0], "a_st"); hLineTexturePosition[0] = GLES20.glGetAttribLocation(lineProgram[0], "a_st");
@ -68,6 +71,7 @@ class LineRenderer {
hLineScale[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_wscale"); hLineScale[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_wscale");
hLineWidth[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_width"); hLineWidth[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_width");
hLineColor[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_color"); hLineColor[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_color");
hLineMode[1] = GLES20.glGetUniformLocation(lineProgram[1], "u_mode");
hLineVertexPosition[1] = GLES20.glGetAttribLocation(lineProgram[1], "a_position"); hLineVertexPosition[1] = GLES20.glGetAttribLocation(lineProgram[1], "a_position");
hLineTexturePosition[1] = GLES20.glGetAttribLocation(lineProgram[1], "a_st"); hLineTexturePosition[1] = GLES20.glGetAttribLocation(lineProgram[1], "a_st");
@ -75,8 +79,8 @@ class LineRenderer {
return true; return true;
} }
static LineLayer drawLines(MapPosition pos, LineLayer layer, int next, static Layer draw(MapPosition pos, Layer layer, float[] matrix, float div,
float[] matrix, float div, int mode, int bufferOffset) { int mode, int bufferOffset) {
int zoom = pos.zoomLevel; int zoom = pos.zoomLevel;
float scale = pos.scale; float scale = pos.scale;
@ -86,8 +90,20 @@ class LineRenderer {
GLES20.glUseProgram(lineProgram[mode]); GLES20.glUseProgram(lineProgram[mode]);
GLES20.glEnableVertexAttribArray(hLineVertexPosition[mode]); int va = hLineVertexPosition[mode];
GLES20.glEnableVertexAttribArray(hLineTexturePosition[mode]); if (!GLRenderer.vertexArray[va]) {
GLES20.glEnableVertexAttribArray(va);
GLRenderer.vertexArray[va] = true;
}
va = hLineTexturePosition[mode];
if (!GLRenderer.vertexArray[va]) {
GLES20.glEnableVertexAttribArray(va);
GLRenderer.vertexArray[va] = true;
}
// GLES20.glEnableVertexAttribArray(hLineVertexPosition[mode]);
// GLES20.glEnableVertexAttribArray(hLineTexturePosition[mode]);
GLES20.glVertexAttribPointer(hLineVertexPosition[mode], 2, GLES20.GL_SHORT, GLES20.glVertexAttribPointer(hLineVertexPosition[mode], 2, GLES20.GL_SHORT,
false, 8, bufferOffset + LINE_VERTICES_DATA_POS_OFFSET); false, 8, bufferOffset + LINE_VERTICES_DATA_POS_OFFSET);
@ -100,12 +116,14 @@ class LineRenderer {
// scale factor to map one pixel on tile to one pixel on screen: // scale factor to map one pixel on tile to one pixel on screen:
// only works with orthographic projection // only works with orthographic projection
float s = scale / div; float s = scale / div;
float pixel = 2.0f / s; float pixel = 0;
if (mode == 0) if (mode == 1)
pixel = 0; pixel = 1.5f / s;
GLES20.glUniform1f(hLineScale[mode], pixel); GLES20.glUniform1f(hLineScale[mode], pixel);
int lineMode = 0;
GLES20.glUniform1i(hLineMode[mode], lineMode);
// line scale factor (for non fixed lines) // line scale factor (for non fixed lines)
float lineScale = FloatMath.sqrt(s); float lineScale = FloatMath.sqrt(s);
@ -115,10 +133,11 @@ class LineRenderer {
boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL; boolean strokeMaxZoom = zoom > TileGenerator.STROKE_MAX_ZOOM_LEVEL;
float width = 1; float width = 1;
LineLayer l = layer; Layer l = layer;
for (; l != null && l.layer < next; l = l.next) { for (; l != null && l.type == Layer.LINE; l = l.next) {
LineLayer ll = (LineLayer) l;
Line line = ll.line;
Line line = l.line;
if (line.fade != -1 && line.fade > zoom) if (line.fade != -1 && line.fade > zoom)
continue; continue;
@ -134,23 +153,33 @@ class LineRenderer {
blur = false; blur = false;
} }
if (l.isOutline) { if (line.outline) {
for (LineLayer o = l.outlines; o != null; o = o.outlines) { for (LineLayer o = ll.outlines; o != null; o = o.outlines) {
if (o.line.fixed || strokeMaxZoom) { if (o.line.fixed || strokeMaxZoom) {
width = (l.width + o.width) / s; width = (ll.width + o.width) / s;
} else { } else {
width = l.width / s + o.width / lineScale; width = ll.width / s + o.width / lineScale;
} }
GLES20.glUniform1f(hLineWidth[mode], width); GLES20.glUniform1f(hLineWidth[mode], width);
if (line.blur != 0) { if (line.blur != 0) {
blurScale = (l.width + o.width) / s - (line.blur / s); blurScale = (ll.width + o.width) / s - (line.blur / s);
GLES20.glUniform1f(hLineScale[mode], blurScale); GLES20.glUniform1f(hLineScale[mode], blurScale);
blur = true; blur = true;
} }
if (line.cap == Cap.ROUND) {
if (lineMode != 1) {
lineMode = 1;
GLES20.glUniform1i(hLineMode[mode], lineMode);
}
} else if (lineMode != 0) {
lineMode = 0;
GLES20.glUniform1i(hLineMode[mode], lineMode);
}
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, o.offset, o.verticesCnt); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, o.offset, o.verticesCnt);
} }
} else { } else {
@ -158,173 +187,37 @@ class LineRenderer {
if (line.fixed || strokeMaxZoom) { if (line.fixed || strokeMaxZoom) {
// invert scaling of extrusion vectors so that line width // invert scaling of extrusion vectors so that line width
// stays the same. // stays the same.
width = l.width / s; width = ll.width / s;
} else { } else {
width = l.width / lineScale; width = ll.width / lineScale;
} }
GLES20.glUniform1f(hLineWidth[mode], width); GLES20.glUniform1f(hLineWidth[mode], width);
if (line.blur != 0) { if (line.blur != 0) {
blurScale = (l.width / lineScale) * line.blur; blurScale = (ll.width / lineScale) * line.blur;
GLES20.glUniform1f(hLineScale[mode], blurScale); GLES20.glUniform1f(hLineScale[mode], blurScale);
blur = true; blur = true;
} }
if (line.cap == Cap.ROUND) {
if (lineMode != 1) {
lineMode = 1;
GLES20.glUniform1i(hLineMode[mode], lineMode);
}
} else if (lineMode != 0) {
lineMode = 0;
GLES20.glUniform1i(hLineMode[mode], lineMode);
}
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, l.offset, l.verticesCnt); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, l.offset, l.verticesCnt);
} }
} }
GLES20.glDisableVertexAttribArray(hLineVertexPosition[mode]); // GLES20.glDisableVertexAttribArray(hLineVertexPosition[mode]);
GLES20.glDisableVertexAttribArray(hLineTexturePosition[mode]); // GLES20.glDisableVertexAttribArray(hLineTexturePosition[mode]);
return l; return l;
} }
static int sizeOf(LineLayer layers) {
int size = 0;
for (LineLayer l = layers; l != null; l = l.next)
size += l.verticesCnt;
size *= NUM_VERTEX_SHORTS;
return size;
}
static void compileLayerData(LineLayer layers, ShortBuffer sbuf) {
int pos = 0;
VertexPoolItem last = null, items = null;
for (LineLayer l = layers; l != null; l = l.next) {
if (l.isOutline)
continue;
for (VertexPoolItem item = l.pool; item != null; item = item.next) {
if (item.next == null) {
sbuf.put(item.vertices, 0, item.used);
} else {
// item.used = VertexPoolItem.SIZE;
sbuf.put(item.vertices);
}
last = item;
}
l.offset = pos;
pos += l.verticesCnt;
if (last != null) {
last.next = items;
items = l.pool;
}
l.pool = null;
l.curItem = null;
}
VertexPool.add(items);
}
// @SuppressLint("UseValueOf")
// private static final Boolean lock = new Boolean(true);
// private static final int POOL_LIMIT = 1500;
//
// static private LineLayer pool = null;
// static private int count = 0;
// static private int countAll = 0;
//
// static void finish() {
// synchronized (lock) {
// count = 0;
// countAll = 0;
// pool = null;
// }
// }
//
// static LineLayer get(int layer, Line line, float width, boolean outline)
// {
// synchronized (lock) {
//
// if (count == 0 && pool == null) {
// countAll++;
// return new LineLayer(layer, line, width, outline);
// }
// if (count > 0) {
// count--;
// } else {
// int c = 0;
// LineLayer tmp = pool;
//
// while (tmp != null) {
// c++;
// tmp = tmp.next;
// }
//
// Log.d("LineLayersl", "eek wrong count: " + c + " left");
// }
//
// LineLayer it = pool;
// pool = pool.next;
// it.next = null;
// it.layer = layer;
// it.line = line;
// it.isOutline = outline;
// it.width = width;
// return it;
// }
// }
//
// static void add(LineLayer layers) {
// if (layers == null)
// return;
//
// synchronized (lock) {
//
// // limit pool items
// if (countAll < POOL_LIMIT) {
// LineLayer last = layers;
//
// while (true) {
// count++;
//
// if (last.next == null)
// break;
//
// last = last.next;
// }
//
// last.next = pool;
// pool = layers;
//
// } else {
// int cleared = 0;
// LineLayer prev, tmp = layers;
// while (tmp != null) {
// prev = tmp;
// tmp = tmp.next;
//
// countAll--;
// cleared++;
//
// prev.next = null;
//
// }
// Log.d("LineLayers", "sum: " + countAll + " free: " + count + " freed "
// + cleared);
// }
//
// }
// }
//
static void clear(LineLayer layer) {
for (LineLayer l = layer; l != null; l = l.next) {
if (l.pool != null) {
VertexPool.add(l.pool);
l.pool = null;
l.curItem = null;
}
}
// LineLayers.add(layer);
}
} }

View File

@ -12,18 +12,19 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.generator.JobTile;
import org.oscim.renderer.layer.VertexPool;
import org.oscim.theme.RenderTheme; import org.oscim.theme.RenderTheme;
import org.oscim.utils.GlConfigChooser; import org.oscim.utils.GlConfigChooser;
import org.oscim.view.MapPosition;
import org.oscim.view.MapView; import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition; import org.oscim.view.MapViewPosition;
import org.oscim.view.generator.JobTile;
import android.content.Context; import android.content.Context;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
@ -36,7 +37,7 @@ public class MapRenderer extends GLSurfaceView {
private GLRenderer mRenderer; private GLRenderer mRenderer;
private static final int MAX_TILES_IN_QUEUE = 40; private static final int MAX_TILES_IN_QUEUE = 40;
private static final int CACHE_THRESHOLD = 50; private static final int CACHE_THRESHOLD = 10;
private static MapView mMapView; private static MapView mMapView;
@ -52,6 +53,8 @@ public class MapRenderer extends GLSurfaceView {
// tiles that have new data to upload, see passTile() // tiles that have new data to upload, see passTile()
private static ArrayList<MapTile> mTilesLoaded; private static ArrayList<MapTile> mTilesLoaded;
private static ArrayList<Overlay> mOverlays;
// TODO current boundary tiles, values used to check if position has // TODO current boundary tiles, values used to check if position has
// changed for updating current tile list // changed for updating current tile list
@ -65,7 +68,8 @@ public class MapRenderer extends GLSurfaceView {
// private static int[] mZoomLevels; // private static int[] mZoomLevels;
private static float[] mTileCoords = new float[8]; private static float[] mTileCoords = new float[8];
private static int[] mBoundaryTiles = new int[8];
// private static int[] mBoundaryTiles = new int[8];
// used for passing tiles to be rendered from TileLoader(Main-Thread) to // used for passing tiles to be rendered from TileLoader(Main-Thread) to
// GLThread // GLThread
@ -90,14 +94,14 @@ public class MapRenderer extends GLSurfaceView {
int xmax = 1 << mZoom; int xmax = 1 << mZoom;
for (int x = x1; x < x2; x++) { for (int x = x1; x < x2; x++) {
// MapTile holder = null;
MapTile tile = null; MapTile tile = null;
// boolean found = false;
if (cnt == max) { if (cnt == max) {
Log.d(TAG, "reached max currentTiles " + max); Log.d(TAG, "reached maximum for currentTiles " + max);
break; break;
} }
// NOTE to myself: do not modify x, argh !!!
int xx = x; int xx = x;
if (x < 0 || x >= xmax) { if (x < 0 || x >= xmax) {
@ -107,12 +111,11 @@ public class MapRenderer extends GLSurfaceView {
else else
xx = x - xmax; xx = x - xmax;
if (xx < 0 || xx >= xmax) { if (xx < 0 || xx >= xmax)
// Log.d(TAG, "tile out of bounds " + y + " " + xx);
continue; continue;
} }
}
// check if tile is already added
for (int i = 0; i < cnt; i++) for (int i = 0; i < cnt; i++)
if (tiles[i].tileX == xx && tiles[i].tileY == y) { if (tiles[i].tileX == xx && tiles[i].tileY == y) {
tile = tiles[i]; tile = tiles[i];
@ -128,7 +131,28 @@ public class MapRenderer extends GLSurfaceView {
} }
}; };
public MapRenderer(Context context, MapView mapView) { // why not try a pattern every now and then?
// but should do the same for GLRenderer
private static MapRenderer SINGLETON;
public static MapRenderer create(Context context, MapView mapView) {
if (SINGLETON != null)
throw new IllegalStateException();
return SINGLETON = new MapRenderer(context, mapView);
}
public void destroy() {
SINGLETON = null;
// mRenderer = null;
// mTiles = null;
// mTilesLoaded = null;
// mJobList = null;
// mOverlays = null;
// ... free pools
}
private MapRenderer(Context context, MapView mapView) {
super(context); super(context);
mMapView = mapView; mMapView = mapView;
@ -148,6 +172,7 @@ public class MapRenderer extends GLSurfaceView {
mJobList = new ArrayList<JobTile>(); mJobList = new ArrayList<JobTile>();
mTiles = new ArrayList<MapTile>(); mTiles = new ArrayList<MapTile>();
mTilesLoaded = new ArrayList<MapTile>(30); mTilesLoaded = new ArrayList<MapTile>(30);
mOverlays = new ArrayList<Overlay>(5);
VertexPool.init(); VertexPool.init();
QuadTree.init(); QuadTree.init();
@ -163,7 +188,7 @@ public class MapRenderer extends GLSurfaceView {
* @param clear * @param clear
* whether to clear and reload all tiles * whether to clear and reload all tiles
*/ */
public void updateMap(boolean clear) { public void updateMap(final boolean clear) {
boolean changedPos = false; boolean changedPos = false;
if (mMapView == null) if (mMapView == null)
@ -173,14 +198,24 @@ public class MapRenderer extends GLSurfaceView {
// make sure onDrawFrame is not running // make sure onDrawFrame is not running
GLRenderer.drawlock.lock(); GLRenderer.drawlock.lock();
// remove all tiles references // remove all tiles references
Log.d(TAG, "CLEAR"); Log.d(TAG, "CLEAR " + mInitial);
if (clear) {
for (MapTile t : mTiles) for (MapTile t : mTiles)
clearTile(t); clearTile(t);
} else {
VertexPool.init();
}
mTiles.clear(); mTiles.clear();
mTilesLoaded.clear(); mTilesLoaded.clear();
QuadTree.init(); QuadTree.init();
// TODO clear overlay items data
mOverlays.clear();
mOverlays.add(new Overlay());
// set up TileData arrays that are passed to gl-thread // set up TileData arrays that are passed to gl-thread
int num = mWidth; int num = mWidth;
if (mWidth < mHeight) if (mWidth < mHeight)
@ -198,19 +233,25 @@ public class MapRenderer extends GLSurfaceView {
// mZoomLevels = mapInfo.zoomLevel; // mZoomLevels = mapInfo.zoomLevel;
GLRenderer.drawlock.unlock(); GLRenderer.drawlock.unlock();
changedPos = true; // .. make sure mMapPosition will be updated
mMapPosition.zoomLevel = -1;
mInitial = false; mInitial = false;
} }
MapPosition mapPosition = mMapPosition; MapPosition mapPosition = mMapPosition;
mMapViewPosition.getMapPosition(mapPosition, mTileCoords); float[] coords = mTileCoords;
changedPos = mMapViewPosition.getMapPosition(mapPosition, coords);
if (!changedPos)
return;
float s = Tile.TILE_SIZE; float s = Tile.TILE_SIZE;
// load some additional tiles more than currently visible // load some additional tiles more than currently visible
float scale = mapPosition.scale * 0.75f; float scale = mapPosition.scale * 0.75f;
float px = (float) mapPosition.x; float px = (float) mapPosition.x;
float py = (float) mapPosition.y; float py = (float) mapPosition.y;
float[] coords = mTileCoords;
int zdir = 0; int zdir = 0;
for (int i = 0; i < 8; i += 2) { for (int i = 0; i < 8; i += 2) {
@ -218,32 +259,30 @@ public class MapRenderer extends GLSurfaceView {
coords[i + 1] = (py + coords[i + 1] / scale) / s; coords[i + 1] = (py + coords[i + 1] / scale) / s;
} }
for (int i = 0; i < 8; i++) // this does not work reloably with tilt and rotation
if (mBoundaryTiles[i] != (int) coords[i]) { // changedPos = false;
changedPos = true; // for (int i = 0; i < 8; i++)
break; // if (mBoundaryTiles[i] != (int) coords[i]) {
} // changedPos = true;
// break;
for (int i = 0; i < 8; i++) // }
mBoundaryTiles[i] = (int) coords[i]; // for (int i = 0; i < 8; i++)
// mBoundaryTiles[i] = (int) coords[i];
// TODO all following should probably be done in an idler instead // TODO all following should probably be done in an idler instead
// to drain queued events. need to check how android handles things. // to drain queued events. need to check how android handles things.
if (changedPos) { boolean changed = updateVisibleList(mapPosition, zdir);
updateVisibleList(mapPosition, zdir);
if (!MapView.debugFrameTime) if (!MapView.debugFrameTime)
requestRender(); requestRender();
if (changed) {
int remove = mTiles.size() - GLRenderer.CACHE_TILES; int remove = mTiles.size() - GLRenderer.CACHE_TILES;
if (remove > CACHE_THRESHOLD) if (remove > CACHE_THRESHOLD)
limitCache(mapPosition, remove); limitCache(mapPosition, remove);
limitLoadQueue(); limitLoadQueue();
} else {
if (!MapView.debugFrameTime)
requestRender();
} }
} }
@ -255,8 +294,9 @@ public class MapRenderer extends GLSurfaceView {
* the current MapPosition * the current MapPosition
* @param zdir * @param zdir
* zoom direction * zoom direction
* @return true if new tiles were loaded
*/ */
private static void updateVisibleList(MapPosition mapPosition, int zdir) { private static boolean updateVisibleList(MapPosition mapPosition, int zdir) {
mJobList.clear(); mJobList.clear();
// set non processed tiles to isLoading == false // set non processed tiles to isLoading == false
@ -265,14 +305,16 @@ public class MapRenderer extends GLSurfaceView {
mScanBox.scan(mTileCoords, mapPosition.zoomLevel); mScanBox.scan(mTileCoords, mapPosition.zoomLevel);
// Log.d(TAG, "visible: " + mCurrentTiles.cnt + "/" + // Log.d(TAG, "visible: " + mCurrentTiles.cnt + "/" +
// mCurrentTiles.tiles.length); // mCurrentTiles.tiles.length);
GLRenderer.updateTiles(mCurrentTiles); GLRenderer.updateTiles(mCurrentTiles, mOverlays);
// note: this sets isLoading == true for all job tiles // note: this sets isLoading == true for all job tiles
if (mJobList.size() > 0) { if (mJobList.size() > 0) {
updateTileDistances(mJobList, mapPosition); updateTileDistances(mJobList, mapPosition);
Collections.sort(mJobList); Collections.sort(mJobList);
mMapView.addJobs(mJobList); mMapView.addJobs(mJobList);
return true;
} }
return false;
} }
/* package */ /* package */
@ -342,15 +384,15 @@ public class MapRenderer extends GLSurfaceView {
t.isLoading = false; t.isLoading = false;
t.isReady = false; t.isReady = false;
LineRenderer.clear(t.lineLayers); if (t.layers != null) {
PolygonRenderer.clear(t.polygonLayers); t.layers.clear();
t.layers = null;
}
t.labels = null; t.labels = null;
t.lineLayers = null;
t.polygonLayers = null;
if (t.vbo != null) { if (t.vbo != null) {
GLRenderer.addVBO(t.vbo); BufferObject.release(t.vbo);
t.vbo = null; t.vbo = null;
} }
if (t.texture != null) if (t.texture != null)
@ -364,7 +406,7 @@ public class MapRenderer extends GLSurfaceView {
byte zoom = mapPosition.zoomLevel; byte zoom = mapPosition.zoomLevel;
long x = (long) mapPosition.x; long x = (long) mapPosition.x;
long y = (long) mapPosition.y; long y = (long) mapPosition.y;
// long center = Tile.TILE_SIZE << (zoom - 1);
int diff; int diff;
long dx, dy; long dx, dy;
@ -380,6 +422,8 @@ public class MapRenderer extends GLSurfaceView {
dy = (t.pixelY + h) - y; dy = (t.pixelY + h) - y;
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * // t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) *
// 0.25f; // 0.25f;
// dx %= center;
// dy %= center;
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f; t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f;
} else if (diff > 0) { } else if (diff > 0) {
// tile zoom level is child of current // tile zoom level is child of current
@ -392,6 +436,8 @@ public class MapRenderer extends GLSurfaceView {
dx = ((t.pixelX + h) >> (diff >> 1)) - x; dx = ((t.pixelX + h) >> (diff >> 1)) - x;
dy = ((t.pixelY + h) >> (diff >> 1)) - y; dy = ((t.pixelY + h) >> (diff >> 1)) - y;
} }
// dx %= center;
// dy %= center;
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)); // t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy));
t.distance = FloatMath.sqrt((dx * dx + dy * dy)); t.distance = FloatMath.sqrt((dx * dx + dy * dy));
@ -399,7 +445,8 @@ public class MapRenderer extends GLSurfaceView {
// tile zoom level is parent of current // tile zoom level is parent of current
dx = ((t.pixelX + h) << -diff) - x; dx = ((t.pixelX + h) << -diff) - x;
dy = ((t.pixelY + h) << -diff) - y; dy = ((t.pixelY + h) << -diff) - y;
// dx %= center;
// dy %= center;
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * // t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) *
// (-diff * 0.5f); // (-diff * 0.5f);
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f); t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f);
@ -410,14 +457,13 @@ public class MapRenderer extends GLSurfaceView {
private static void limitCache(MapPosition mapPosition, int remove) { private static void limitCache(MapPosition mapPosition, int remove) {
int size = mTiles.size(); int size = mTiles.size();
// remove orphaned tiles // remove tiles that were never loaded
for (int i = 0; i < size;) { for (int i = 0; i < size;) {
MapTile t = mTiles.get(i); MapTile t = mTiles.get(i);
// make sure tile cannot be used by GL or MapWorker Thread // make sure tile cannot be used by GL or MapWorker Thread
if (t.isLocked() || t.isActive()) { if (t.isLocked() || t.isActive()) {
i++; i++;
} else { } else {
// Log.d(TAG, "remove empty tile" + t);
clearTile(t); clearTile(t);
mTiles.remove(i); mTiles.remove(i);
remove--; remove--;
@ -432,26 +478,25 @@ public class MapRenderer extends GLSurfaceView {
Collections.sort(mTiles); Collections.sort(mTiles);
for (int i = 1; i < remove; i++) { for (int i = 1; i < remove; i++) {
MapTile t = mTiles.remove(size - i); MapTile t = mTiles.remove(size - i);
synchronized (t) {
if (t.isLocked()) { if (t.isLocked()) {
// dont remove tile used by renderthread // dont remove tile used by GLRenderer
Log.d(TAG, "X not removing " + t Log.d(TAG, "X not removing " + t + " " + t.distance);
// + " " + t.isLocked
+ " " + t.distance);
mTiles.add(t); mTiles.add(t);
continue; continue;
} }
if (t.isLoading) { if (t.isLoading) {
// NOTE: if we add tile back then on next limitCache // NOTE: if we add tile back and set loading=false, on next
// the tile will be removed. clearTile could interfere with // limitCache the tile will be removed. clearTile could
// MapGenerator. so clear in passTile() instead. // interfere with TileGenerator. so clear in passTile()
// mTiles.add(t); // instead.
t.isLoading = false; // ... no, this does not work either: when set loading to
// false tile could be added to load queue while still
// processed in TileGenerator => need tile.cancel flag.
// t.isLoading = false;
mTiles.add(t);
Log.d(TAG, "X cancel loading " + t + " " + t.distance); Log.d(TAG, "X cancel loading " + t + " " + t.distance);
continue; continue;
} }
@ -459,7 +504,6 @@ public class MapRenderer extends GLSurfaceView {
clearTile(t); clearTile(t);
} }
} }
}
private static void limitLoadQueue() { private static void limitLoadQueue() {
int size = mTilesLoaded.size(); int size = mTilesLoaded.size();
@ -469,11 +513,11 @@ public class MapRenderer extends GLSurfaceView {
synchronized (mTilesLoaded) { synchronized (mTilesLoaded) {
// remove tiles uploaded to vbo // remove tiles already uploaded to vbo
for (int i = 0; i < size;) { for (int i = 0; i < size;) {
MapTile t = mTilesLoaded.get(i); MapTile t = mTilesLoaded.get(i);
// rel == null means tile is already removed by limitCache
if (!t.newData || t.rel == null) { if (!t.newData) {
mTilesLoaded.remove(i); mTilesLoaded.remove(i);
size--; size--;
continue; continue;
@ -484,20 +528,11 @@ public class MapRenderer extends GLSurfaceView {
if (size < MAX_TILES_IN_QUEUE) if (size < MAX_TILES_IN_QUEUE)
return; return;
// Log.d(TAG, "queue: " + mTilesLoaded.size() + " " + size + " "
// + (size - MAX_TILES_IN_QUEUE / 2));
// clear loaded but not used tiles // clear loaded but not used tiles
for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) { for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) {
MapTile t = mTilesLoaded.get(i); MapTile t = mTilesLoaded.get(i);
synchronized (t) {
if (t.rel == null) {
mTilesLoaded.remove(i);
continue;
}
if (t.isLocked()) { if (t.isLocked()) {
// Log.d(TAG, "keep unused tile data: " + t + " " + // Log.d(TAG, "keep unused tile data: " + t + " " +
// t.isActive); // t.isActive);
@ -512,7 +547,6 @@ public class MapRenderer extends GLSurfaceView {
} }
} }
} }
}
/** /**
* called from MapWorker Thread when tile is loaded by TileGenerator * called from MapWorker Thread when tile is loaded by TileGenerator
@ -534,7 +568,7 @@ public class MapRenderer extends GLSurfaceView {
return true; return true;
} }
mRenderer.setVBO(tile); tile.vbo = BufferObject.get();
if (tile.vbo == null) { if (tile.vbo == null) {
Log.d(TAG, "no VBOs left for " + tile); Log.d(TAG, "no VBOs left for " + tile);
@ -573,79 +607,4 @@ public class MapRenderer extends GLSurfaceView {
super.onSizeChanged(w, h, oldw, oldh); super.onSizeChanged(w, h, oldw, oldh);
} }
// private static void updateVisibleList(MapPosition mapPosition, int zdir)
// {
// double x = mapPosition.x;
// double y = mapPosition.y;
// byte zoomLevel = mapPosition.zoomLevel;
// float scale = mapPosition.scale;
//
// double add = 1.0f / scale;
// int offsetX = (int) ((mWidth >> 1) * add) + Tile.TILE_SIZE;
// int offsetY = (int) ((mHeight >> 1) * add) + Tile.TILE_SIZE;
//
// long pixelRight = (long) x + offsetX;
// long pixelBottom = (long) y + offsetY;
// long pixelLeft = (long) x - offsetX;
// long pixelTop = (long) y - offsetY;
//
// int tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel);
// int tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel);
// int tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel);
// int tileBottom = MercatorProjection.pixelYToTileY(pixelBottom,
// zoomLevel);
//
// mJobList.clear();
//
// // set non processed tiles to isLoading == false
// mMapView.addJobs(null);
//
// int tiles = 0;
// int max = mCurrentTiles.tiles.length - 1;
//
// // boolean fetchChildren = false;
// // boolean fetchParent = false;
// // boolean fetchProxy = false;
// // if (mZoomLevels != null) {
// // // check MapDatabase zoom-level-mapping
// // if (mZoomLevels[zoomLevel] == 0) {
// // mCurrentTiles.cnt = 0;
// // mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles);
// // return;
// // }
// //
// // if (mZoomLevels[zoomLevel] > zoomLevel) {
// // fetchChildren = true;
// // fetchProxy = true;
// //
// // } else if (mZoomLevels[zoomLevel] < zoomLevel) {
// // fetchParent = true;
// // fetchProxy = true;
// // }
// // }
//
// for (int yy = tileTop; yy <= tileBottom; yy++) {
// for (int xx = tileLeft; xx <= tileRight; xx++) {
//
// if (tiles == max)
// break;
//
// // MapTile tile =
// addTile(xx, yy, zoomLevel, zdir);
// // mCurrentTiles.tiles[tiles++] = tile;
// }
// }
//
// // pass new tile list to glThread
// mCurrentTiles.cnt = tiles;
// mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles);
//
// // note: this sets isLoading == true for all job tiles
// if (mJobList.size() > 0) {
// updateTileDistances(mJobList, mapPosition);
// Collections.sort(mJobList);
// mMapView.addJobs(mJobList);
// }
// }
} }

View File

@ -12,9 +12,10 @@
* You should have received a copy of the GNU Lesser General License along with * You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import org.oscim.view.generator.JobTile; import org.oscim.generator.JobTile;
import org.oscim.renderer.layer.Layers;
class MapTile extends JobTile { class MapTile extends JobTile {
@ -22,26 +23,15 @@ class MapTile extends JobTile {
* VBO layout: - 16 bytes fill coordinates, n bytes polygon vertices, m * VBO layout: - 16 bytes fill coordinates, n bytes polygon vertices, m
* bytes lines vertices * bytes lines vertices
*/ */
VertexBufferObject vbo; BufferObject vbo;
/**
* polygonOffset in vbo is always 16 bytes,
*/
int lineOffset;
TextTexture texture; TextTexture texture;
/** /**
* Tile data set by TileGenerator: * Tile data set by TileGenerator:
*/ */
LineLayer lineLayers;
PolygonLayer polygonLayers;
TextItem labels; TextItem labels;
Layers layers;
/**
* tile is used by render thread. set by updateVisibleList (main thread).
*/
// boolean isLocked;
/** /**
* tile has new data to upload to gl * tile has new data to upload to gl
@ -59,7 +49,7 @@ class MapTile extends JobTile {
boolean isVisible; boolean isVisible;
/** /**
* pointer to access relatives in TileTree * pointer to access relatives in QuadTree
*/ */
QuadTree rel; QuadTree rel;
@ -74,12 +64,16 @@ class MapTile extends JobTile {
// counting the tiles that use this tile as proxy // counting the tiles that use this tile as proxy
byte refs; byte refs;
byte locked; byte locked;
// this tile sits in fo another tile. e.g. x:-1,y:0,z:1 for x:1,y:0 // used when this tile sits in fo another tile.
// e.g. x:-1,y:0,z:1 for x:1,y:0
MapTile holder; MapTile holder;
MapTile(int tileX, int tileY, byte zoomLevel) {
super(tileX, tileY, zoomLevel);
}
boolean isActive() { boolean isActive() {
return isLoading || newData || isReady; return isLoading || newData || isReady;
} }
@ -146,9 +140,4 @@ class MapTile extends JobTile {
} }
proxies = 0; proxies = 0;
} }
MapTile(int tileX, int tileY, byte zoomLevel) {
super(tileX, tileY, zoomLevel);
}
} }

View File

@ -0,0 +1,190 @@
/*
* Copyright 2012, Hannes Janetzek
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
import java.io.IOException;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.SymbolItem;
import org.oscim.renderer.layer.SymbolLayer;
import org.oscim.theme.renderinstruction.BitmapUtils;
import org.oscim.theme.renderinstruction.Line;
import org.oscim.view.MapView;
import android.graphics.Color;
import android.graphics.Paint.Cap;
import android.opengl.GLES20;
import android.opengl.Matrix;
public class Overlay {
BufferObject vbo;
Layers layers;
TextItem labels;
TextTexture texture;
// flag to set when data is ready for (re)compilation.
boolean newData;
boolean isReady;
MapPosition mMapPosition;
float drawScale;
private boolean first = true;
Overlay() {
mMapPosition = new MapPosition();
layers = new Layers();
LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT);
ll.width = 2;
float[] points = { -100, -100, 100, -100, 100, 100, -100, 100, -100,
-100 };
short[] index = { (short) points.length };
ll.addLine(points, index, false);
//
// PolygonLayer pl = (PolygonLayer) layers.getLayer(0, Layer.POLYGON);
// pl.area = new Area(Color.argb(128, 255, 0, 0));
//
// float[] ppoints = {
// 0, 256,
// 0, 0,
// 256, 0,
// 256, 256,
// };
// short[] pindex = { (short) ppoints.length };
// pl.addPolygon(ppoints, pindex);
SymbolLayer sl = new SymbolLayer();
SymbolItem it = new SymbolItem();
it.x = 0;
it.y = 0;
// billboard always faces camera
it.billboard = true;
try {
it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sl.addSymbol(it);
SymbolItem it2 = new SymbolItem();
it2.bitmap = it.bitmap;
it2.x = 0;
it2.y = 0;
// billboard always faces camera
it2.billboard = false;
sl.addSymbol(it2);
layers.symbolLayers = sl;
}
synchronized boolean onTouch() {
return true;
}
// /////////////// called from GLRender Thread ////////////////////////
// use synchronized (this){} when updating 'layers' from another thread
synchronized void update(MapView mapView) {
// keep position constant (or update layer relative to new position)
// mapView.getMapViewPosition().getMapPosition(mMapPosition, null);
if (first) {
// fix at initial position
mapView.getMapViewPosition().getMapPosition(mMapPosition, null);
first = false;
// pass layers to be uploaded and drawn to GL Thread
// afterwards never modify 'layers' outside of this function!
newData = true;
}
}
float[] mvp = new float[16];
synchronized void render(MapPosition pos, float[] mv, float[] proj) {
float div = 1;
setMatrix(pos, mv);
Matrix.multiplyMM(mvp, 0, proj, 0, mv, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo.id);
for (Layer l = layers.layers; l != null;) {
if (l.type == Layer.POLYGON) {
GLES20.glDisable(GLES20.GL_BLEND);
l = PolygonRenderer.draw(pos, l, mvp, true, false);
} else {
GLES20.glEnable(GLES20.GL_BLEND);
l = LineRenderer.draw(pos, l, mvp, div, 0, layers.lineOffset);
}
}
for (Layer l = layers.symbolLayers; l != null;) {
l = TextureRenderer.draw(l, 1, proj, mv, layers.symbolOffset);
}
}
private void setMatrix(MapPosition mPos, float[] matrix) {
MapPosition oPos = mMapPosition;
float div = 1;
byte z = oPos.zoomLevel;
int diff = mPos.zoomLevel - z;
if (diff < 0)
div = (1 << -diff);
else if (diff > 0)
div = (1.0f / (1 << diff));
float x = (float) (oPos.x - mPos.x * div);
float y = (float) (oPos.y - mPos.y * div);
// flip around date-line
float max = (Tile.TILE_SIZE << z);
if (x < -max / 2)
x = max + x;
else if (x > max / 2)
x = x - max;
float scale = mPos.scale / div;
Matrix.setIdentityM(matrix, 0);
// translate relative to map center
matrix[12] = x * scale;
matrix[13] = y * scale;
scale = (mPos.scale / oPos.scale) / div;
// scale to tile to world coordinates
scale /= GLRenderer.COORD_MULTIPLIER;
matrix[0] = scale;
matrix[5] = scale;
Matrix.multiplyMM(matrix, 0, mPos.viewMatrix, 0, matrix, 0);
}
}

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General License along with * You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import static android.opengl.GLES20.GL_BLEND; import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_EQUAL; import static android.opengl.GLES20.GL_EQUAL;
@ -38,17 +38,18 @@ import static android.opengl.GLES20.glVertexAttribPointer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import org.oscim.core.MapPosition;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.PolygonLayer;
import org.oscim.utils.GlUtils; import org.oscim.utils.GlUtils;
import org.oscim.view.MapPosition;
import android.opengl.GLES20; import android.opengl.GLES20;
class PolygonRenderer { class PolygonRenderer {
// private static final String TAG = "PolygonRenderer"; // private static final String TAG = "PolygonRenderer";
private static final int NUM_VERTEX_SHORTS = 2; // private static final int NUM_VERTEX_SHORTS = 2;
private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0; private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0;
private static final int STENCIL_BITS = 8; private static final int STENCIL_BITS = 8;
@ -88,9 +89,6 @@ class PolygonRenderer {
/* do not modify stencil buffer */ /* do not modify stencil buffer */
glStencilMask(0); glStencilMask(0);
/* only draw where nothing was drawn yet */
glEnable(GLES20.GL_DEPTH_TEST);
for (int c = mStart; c < mCount; c++) { for (int c = mStart; c < mCount; c++) {
PolygonLayer l = mFillPolys[c]; PolygonLayer l = mFillPolys[c];
@ -162,14 +160,25 @@ class PolygonRenderer {
// stencil buffer index to start fill // stencil buffer index to start fill
private static int mStart; private static int mStart;
static PolygonLayer drawPolygons(MapPosition pos, PolygonLayer layer, int next, static Layer draw(MapPosition pos, Layer layer,
float[] matrix, boolean first) { float[] matrix, boolean first, boolean clip) {
int zoom = pos.zoomLevel; int zoom = pos.zoomLevel;
float scale = pos.scale; float scale = pos.scale;
glUseProgram(polygonProgram); glUseProgram(polygonProgram);
GLES20.glEnableVertexAttribArray(hPolygonVertexPosition);
int va = hPolygonVertexPosition;
if (!GLRenderer.vertexArray[va]) {
GLES20.glEnableVertexAttribArray(va);
GLRenderer.vertexArray[va] = true;
}
va = va == 0 ? 1 : 0;
if (GLRenderer.vertexArray[va]) {
GLES20.glDisableVertexAttribArray(va);
GLRenderer.vertexArray[va] = false;
}
// GLES20.glEnableVertexAttribArray(hPolygonVertexPosition);
glVertexAttribPointer(hPolygonVertexPosition, 2, GLES20.GL_SHORT, glVertexAttribPointer(hPolygonVertexPosition, 2, GLES20.GL_SHORT,
false, 0, POLYGON_VERTICES_DATA_POS_OFFSET); false, 0, POLYGON_VERTICES_DATA_POS_OFFSET);
@ -179,8 +188,6 @@ class PolygonRenderer {
// use stencilbuffer method for polygon drawing // use stencilbuffer method for polygon drawing
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
PolygonLayer l = layer;
if (first) { if (first) {
mCount = 0; mCount = 0;
mStart = 0; mStart = 0;
@ -188,12 +195,15 @@ class PolygonRenderer {
mStart = mCount; mStart = mCount;
} }
for (; l != null && l.layer < next; l = l.next) { Layer l = layer;
for (; l != null && l.type == Layer.POLYGON; l = l.next) {
PolygonLayer pl = (PolygonLayer) l;
// fade out polygon layers (set in RederTheme) // fade out polygon layers (set in RederTheme)
if (l.area.fade > 0 && l.area.fade > zoom) if (pl.area.fade > 0 && pl.area.fade > zoom)
continue; continue;
if (mCount == 0) { if (mCount == mStart) {
// clear stencilbuffer (tile region) // clear stencilbuffer (tile region)
// disable drawing to framebuffer // disable drawing to framebuffer
@ -205,7 +215,7 @@ class PolygonRenderer {
glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);
if (first) { if (clip) {
// draw clip-region into depth buffer: // draw clip-region into depth buffer:
// this is used for lines and polygons // this is used for lines and polygons
@ -214,14 +224,15 @@ class PolygonRenderer {
// to prevent overdraw gl_less restricts // to prevent overdraw gl_less restricts
// the clip to the area where no other // the clip to the area where no other
// tile was drawn // tile has drawn
GLES20.glDepthFunc(GLES20.GL_LESS); GLES20.glDepthFunc(GLES20.GL_LESS);
} }
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
if (first) { if (clip) {
first = false; first = false;
clip = false;
// dont modify depth buffer // dont modify depth buffer
GLES20.glDepthMask(false); GLES20.glDepthMask(false);
// only draw to this tile // only draw to this tile
@ -232,23 +243,26 @@ class PolygonRenderer {
glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT); glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
// no need for depth test while drawing stencil // no need for depth test while drawing stencil
if (clip)
glDisable(GLES20.GL_DEPTH_TEST); glDisable(GLES20.GL_DEPTH_TEST);
} else if (mCount == mStart) {
// disable drawing to framebuffer
glColorMask(false, false, false, false);
// never pass the test: always apply fail op
glStencilFunc(GLES20.GL_ALWAYS, 0, 0xFF);
// stencil op for stencil method polygon drawing
glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
// no need for depth test while drawing stencil
glDisable(GLES20.GL_DEPTH_TEST);
} }
// else if (mCount == mStart) {
// // disable drawing to framebuffer
// glColorMask(false, false, false, false);
//
// // never pass the test: always apply fail op
// glStencilFunc(GLES20.GL_ALWAYS, 0, 0xFF);
//
// // stencil op for stencil method polygon drawing
// glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT);
//
// // no need for depth test while drawing stencil
// if (clip)
// glDisable(GLES20.GL_DEPTH_TEST);
// }
mFillPolys[mCount] = l; mFillPolys[mCount] = pl;
// set stencil mask to draw to // set stencil mask to draw to
glStencilMask(1 << mCount++); glStencilMask(1 << mCount++);
@ -257,15 +271,23 @@ class PolygonRenderer {
// draw up to 8 layers into stencil buffer // draw up to 8 layers into stencil buffer
if (mCount == STENCIL_BITS) { if (mCount == STENCIL_BITS) {
/* only draw where nothing was drawn yet */
if (clip)
glEnable(GLES20.GL_DEPTH_TEST);
fillPolygons(zoom, scale); fillPolygons(zoom, scale);
mCount = 0; mCount = 0;
mStart = 0; mStart = 0;
} }
} }
if (mCount > 0) if (mCount > 0) {
fillPolygons(zoom, scale); /* only draw where nothing was drawn yet */
if (clip)
glEnable(GLES20.GL_DEPTH_TEST);
fillPolygons(zoom, scale);
}
// maybe reset start when only few layers left in stencil buffer // maybe reset start when only few layers left in stencil buffer
// if (mCount > 5){ // if (mCount > 5){
// mCount = 0; // mCount = 0;
@ -274,19 +296,20 @@ class PolygonRenderer {
glDisable(GL_STENCIL_TEST); glDisable(GL_STENCIL_TEST);
if (first) if (clip && first)
drawDepthClip(); drawDepthClip();
GLES20.glDisableVertexAttribArray(hPolygonVertexPosition); // GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);
return l; return l;
} }
private static float[] debugFillColor = { 0.3f, 0.0f, 0.0f, 0.3f }; private static float[] debugFillColor = { 0.3f, 0.0f, 0.0f, 0.3f };
private static float[] debugFillColor2 = { 0.0f, 0.3f, 0.0f, 0.3f };
private static ByteBuffer mDebugFill; private static ByteBuffer mDebugFill;
static void debugDraw(float[] matrix, float[] coords) { static void debugDraw(float[] matrix, float[] coords, int color) {
mDebugFill = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder()); mDebugFill = ByteBuffer.allocateDirect(32).order(ByteOrder.nativeOrder());
FloatBuffer buf = mDebugFill.asFloatBuffer(); FloatBuffer buf = mDebugFill.asFloatBuffer();
@ -303,12 +326,15 @@ class PolygonRenderer {
glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0); glUniformMatrix4fv(hPolygonMatrix, 1, false, matrix, 0);
if (color == 0)
glUniform4fv(hPolygonColor, 1, debugFillColor, 0); glUniform4fv(hPolygonColor, 1, debugFillColor, 0);
else
glUniform4fv(hPolygonColor, 1, debugFillColor2, 0);
glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GlUtils.checkGlError("draw debug"); GlUtils.checkGlError("draw debug");
GLES20.glDisableVertexAttribArray(hPolygonVertexPosition); // GLES20.glDisableVertexAttribArray(hPolygonVertexPosition);
} }
static void drawDepthClip() { static void drawDepthClip() {
@ -322,55 +348,4 @@ class PolygonRenderer {
glColorMask(true, true, true, true); glColorMask(true, true, true, true);
GLES20.glDepthFunc(GLES20.GL_EQUAL); GLES20.glDepthFunc(GLES20.GL_EQUAL);
} }
static int sizeOf(PolygonLayer layers) {
int size = 0;
for (PolygonLayer l = layers; l != null; l = l.next)
size += l.verticesCnt;
size *= NUM_VERTEX_SHORTS;
return size;
}
static void compileLayerData(PolygonLayer layers, ShortBuffer sbuf) {
int pos = 4;
VertexPoolItem last = null, items = null;
for (PolygonLayer l = layers; l != null; l = l.next) {
for (VertexPoolItem item = l.pool; item != null; item = item.next) {
if (item.next == null) {
sbuf.put(item.vertices, 0, item.used);
} else {
// item.used = VertexPoolItem.SIZE;
sbuf.put(item.vertices);
}
last = item;
}
l.offset = pos;
pos += l.verticesCnt;
if (last != null) {
last.next = items;
items = l.pool;
}
l.pool = null;
}
VertexPool.add(items);
}
static void clear(PolygonLayer layers) {
for (PolygonLayer l = layers; l != null; l = l.next) {
if (l.pool != null)
VertexPool.add(l.pool);
}
}
} }

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import android.util.Log; import android.util.Log;
@ -37,17 +37,9 @@ class QuadTree {
MapTile tile; MapTile tile;
static void init() { static void init() {
pool = null; pool = null;
root = new QuadTree(); root = new QuadTree();
root.parent = root; root.parent = root;
// QuadTree t;
// for (int i = 0; i < 200; i++) {
// t = new QuadTree();
// t.parent = pool;
// pool = t;
// }
} }
static boolean remove(MapTile t) { static boolean remove(MapTile t) {
@ -90,8 +82,6 @@ class QuadTree {
int y = tile.tileY; int y = tile.tileY;
int z = tile.zoomLevel; int z = tile.zoomLevel;
QuadTree cur;
// if (x < 0 || x >= 1 << z) { // if (x < 0 || x >= 1 << z) {
// Log.d(TAG, "invalid position"); // Log.d(TAG, "invalid position");
// return null; // return null;
@ -109,7 +99,7 @@ class QuadTree {
leaf.refs++; leaf.refs++;
cur = leaf.child[id]; QuadTree cur = leaf.child[id];
if (cur != null) { if (cur != null) {
leaf = cur; leaf = cur;

View File

@ -15,7 +15,7 @@
/* ported from Polymaps: Layer.js Copyright (c) 2010, SimpleGeo and Stamen Design */ /* ported from Polymaps: Layer.js Copyright (c) 2010, SimpleGeo and Stamen Design */
package org.oscim.view.renderer; package org.oscim.renderer;
import android.util.FloatMath; import android.util.FloatMath;
import android.util.Log; import android.util.Log;
@ -103,6 +103,7 @@ public abstract class ScanBox {
scanSpans(ca, bc); scanSpans(ca, bc);
} }
// FIXME
private static final int MAX_SLOPE = 4; private static final int MAX_SLOPE = 4;
private void scanSpans(Edge e0, Edge e1) { private void scanSpans(Edge e0, Edge e1) {

View File

@ -13,9 +13,9 @@
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
class Shaders { public final class Shaders {
final static String lineVertexShader = "" final static String lineVertexShader = ""
+ "precision mediump float;" + "precision mediump float;"
@ -54,40 +54,119 @@ class Shaders {
+ "precision mediump float;" + "precision mediump float;"
+ "uniform float u_wscale;" + "uniform float u_wscale;"
+ "uniform float u_width;" + "uniform float u_width;"
+ "uniform int u_mode;"
+ "uniform vec4 u_color;" + "uniform vec4 u_color;"
+ "varying vec2 v_st;" + "varying vec2 v_st;"
+ "const float zero = 0.0;" + "const float zero = 0.0;"
+ "void main() {" + "void main() {"
+ " float len;" + " float len;"
+ " if (v_st.t == zero)" + " if (u_mode == 0)"
+ " len = abs(v_st.s);" + " len = u_width - abs(v_st.s);"
+ " else " + " else "
+ " len = length(v_st);" + " len = u_width - length(v_st);"
// fade to alpha. u_wscale is the width in pixel which should be // fade to alpha. u_wscale is the width in pixel which should be
// faded, u_width - len the position of this fragment on the // faded, u_width - len the position of this fragment on the
// perpendicular to this line segment // perpendicular to this line segment
+ " gl_FragColor = smoothstep(zero, u_wscale, u_width - len) * u_color;" + " vec4 color = u_color;"
+ " if (len < u_wscale)"
+ " color *= len / u_wscale;"
+ " gl_FragColor = color;"
// smoothstep(zero, u_wscale, u_width - len) * u_color;"
+ "}"; + "}";
// final static String lineFragmentShader = ""
// + "#extension GL_OES_standard_derivatives : enable\n"
// + "precision mediump float;\n"
// + "uniform float u_wscale;"
// + "uniform float u_width;"
// + "uniform vec4 u_color;"
// + "varying vec2 v_st;"
// + "const float zero = 0.0;"
// + "const vec4 col1 = vec4(0.5,0.0,0.0,0.5);"
// + "const vec4 col2 = vec4(0.0,0.0,0.5,0.5);"
// + "void main() {"
// + " vec4 color = u_color;"
// + " float width = u_width;"
// + " float len;"
// + " if (v_st.t == zero)"
// + " len = abs(v_st.s);"
// + " else "
// + " len = length(v_st);"
// + " vec2 st_width = fwidth(v_st);"
// + " float fuzz = max(st_width.s, st_width.t);"
// // + " if (v_st.s > 0.0){"
// // + " color = col1;"
// + " color *= (1.0 - len) / (fuzz + u_wscale);"
// // + " }else{"
// // + " color = col2;"
// // + " color *= 1.0 - (fuzz + u_wscale) / (len - 1.0);"
// // + " }"
// + " gl_FragColor = color;"
// + "}";
// final static String lineFragmentShader = ""
// + "#extension GL_OES_standard_derivatives : enable\n"
// + "precision mediump float;\n"
// + "uniform float u_wscale;"
// + "uniform float u_width;"
// + "uniform vec4 u_color;"
// + "varying vec2 v_st;"
// + "const float zero = 0.0;"
// + "const vec4 col1 = vec4(0.5,0.0,0.0,0.5);"
// + "const vec4 col2 = vec4(0.0,0.0,0.5,0.5);"
// + "void main() {"
// + " vec4 color = u_color;"
// + " float width = u_width;"
// + " float len;"
// + " if (v_st.t == zero)"
// + " len = abs(v_st.s);"
// + " else "
// + " len = length(v_st);"
// + " vec2 st_width = fwidth(v_st);"
// + " float fuzz = max(st_width.s, st_width.t);"
// + " if (u_width > 1.0) fuzz *= 1.2;"
// + " color *= (u_width - len) / (fuzz + u_wscale);"
// + " if (v_st.s > 0.0){"
// + " if (u_width - len < fuzz + u_wscale)"
// + " color = col1 * (u_width - len) / (fuzz + u_wscale);"
// + " }else{"
// + " if (u_width - len < fuzz + u_wscale)"
// + " color = col2 * (u_width - len) / (fuzz + u_wscale);"
// + "}"
// // + " color *= (fuzz + u_wscale);"
// // " color *= smoothstep(zero, fuzz + u_wscale, u_width - len);"
// + " gl_FragColor = color;"
// + "}";
final static String lineFragmentShader = "" final static String lineFragmentShader = ""
+ "#extension GL_OES_standard_derivatives : enable\n" + "#extension GL_OES_standard_derivatives : enable\n"
+ "precision mediump float;\n" + "precision mediump float;\n"
+ "uniform float u_wscale;" + "uniform float u_wscale;"
+ "uniform float u_width;" + "uniform float u_width;"
+ "uniform int u_mode;"
+ "uniform vec4 u_color;" + "uniform vec4 u_color;"
+ "varying vec2 v_st;" + "varying vec2 v_st;"
// + "const vec4 col1 = vec4(0.5,0.0,0.0,0.5);"
// + "const vec4 col2 = vec4(0.0,0.0,0.5,0.5);"
+ "const float zero = 0.0;" + "const float zero = 0.0;"
+ "void main() {" + "void main() {"
+ " vec4 color = u_color;" + " vec4 color = u_color;"
+ " float width = u_width;" + " float width = u_width;"
+ " float len;" + " float len;"
+ " if (v_st.t == zero)" + " if (u_mode == 0)"
+ " len = abs(v_st.s);" + " len = u_width - abs(v_st.s);"
+ " else " + " else "
+ " len = length(v_st);" + " len = u_width - length(v_st);"
+ " vec2 st_width = fwidth(v_st);" + " vec2 st_width = fwidth(v_st);"
+ " float fuzz = max(st_width.s, st_width.t) * 1.5;" + " float fuzz = max(st_width.s, st_width.t);"
+ " color *= smoothstep(zero, fuzz + u_wscale, u_width - len);" // + " if (u_width > 1.0) fuzz *= 1.2;"
+ " fuzz += u_wscale;"
+ " if (len < fuzz){"
// + " if (v_st.s > zero)"
+ " color *= len / fuzz;"
// + " else"
// + " color = col2 * (u_width - len) / fuzz;"
+ " }"
+ " gl_FragColor = color;" + " gl_FragColor = color;"
+ "}"; + "}";
@ -113,17 +192,18 @@ class Shaders {
+ "uniform mat4 u_mv;" + "uniform mat4 u_mv;"
+ "uniform mat4 u_proj;" + "uniform mat4 u_proj;"
+ "uniform float u_scale;" + "uniform float u_scale;"
+ "uniform float u_swidth;"
+ "varying vec2 tex_c;" + "varying vec2 tex_c;"
+ "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);" + "const vec2 div = vec2(1.0/2048.0,1.0/2048.0);"
+ "const float coord_scale = 0.125;" + "const float coord_scale = 0.125;"
+ "void main() {" + "void main() {"
+ " vec4 pos;" + " vec4 pos;"
+ " if (mod(vertex.x, 2.0) == 0.0){" + " if (mod(vertex.x, 2.0) == 0.0){"
+ " pos = u_proj * (u_mv * vec4(vertex.xy + vertex.zw * u_scale, 0.0, 1.0));" + " pos = u_proj * (u_mv * vec4(vertex.xy + vertex.zw * u_scale, 0.0, 1.0));"
+ " } else {" + " } else {"
// place as billboard // // place as billboard
+ " vec4 dir = u_mv * vec4(vertex.xy, 0.0, 1.0);" + " vec4 dir = u_mv * vec4(vertex.xy, 0.0, 1.0);"
+ " pos = u_proj * (dir + vec4(vertex.zw * coord_scale, 0.0, 0.0));" + " pos = u_proj * (dir + vec4(vertex.zw * (coord_scale * u_swidth), 0.0, 0.0));"
+ " }" + " }"
+ " gl_Position = pos;" + " gl_Position = pos;"
+ " tex_c = tex_coord * div;" + " tex_c = tex_coord * div;"
@ -134,7 +214,7 @@ class Shaders {
// + "attribute vec4 vertex;" // + "attribute vec4 vertex;"
// + "attribute vec2 tex_coord;" // + "attribute vec2 tex_coord;"
// + "uniform mat4 mvp;" // + "uniform mat4 mvp;"
// + "uniform mat4 rotation;" // + "uniform mat4 viewMatrix;"
// + "uniform float scale;" // + "uniform float scale;"
// + "varying vec2 tex_c;" // + "varying vec2 tex_c;"
// + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);" // + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
@ -143,7 +223,7 @@ class Shaders {
// + " if (mod(vertex.x, 2.0) == 0.0){" // + " if (mod(vertex.x, 2.0) == 0.0){"
// + " pos = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);" // + " pos = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);"
// + " } else {" // + " } else {"
// + " vec4 dir = rotation * vec4(vertex.zw / scale, 0.0, 1.0);" // + " vec4 dir = viewMatrix * vec4(vertex.zw / scale, 0.0, 1.0);"
// + " pos = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);" // + " pos = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);"
// + " }" // + " }"
// + " pos.z = 0.0;" // + " pos.z = 0.0;"
@ -156,7 +236,7 @@ class Shaders {
// + "attribute vec4 vertex;" // + "attribute vec4 vertex;"
// + "attribute vec2 tex_coord;" // + "attribute vec2 tex_coord;"
// + "uniform mat4 mvp;" // + "uniform mat4 mvp;"
// + "uniform mat4 rotation;" // + "uniform mat4 viewMatrix;"
// + "uniform float scale;" // + "uniform float scale;"
// + "varying vec2 tex_c;" // + "varying vec2 tex_c;"
// + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);" // + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);"
@ -165,7 +245,7 @@ class Shaders {
// + // +
// " gl_Position = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);" // " gl_Position = mvp * vec4(vertex.xy + vertex.zw / scale, 0.0, 1.0);"
// + " } else {" // + " } else {"
// + " vec4 dir = rotation * vec4(vertex.zw / scale, 0.0, 1.0);" // + " vec4 dir = viewMatrix * vec4(vertex.zw / scale, 0.0, 1.0);"
// + " gl_Position = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);" // + " gl_Position = mvp * vec4(vertex.xy + dir.xy, 0.0, 1.0);"
// + " }" // + " }"
// + " tex_c = tex_coord * div;" // + " tex_c = tex_coord * div;"

View File

@ -12,37 +12,33 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import org.oscim.theme.renderinstruction.Caption; import org.oscim.theme.renderinstruction.Text;
import org.oscim.theme.renderinstruction.PathText;
public class TextItem { public class TextItem {
TextItem next; TextItem next;
final float x, y; final float x, y;
final String text; final String string;
final Caption caption; final Text text;
final PathText path;
final float width; final float width;
short x1, y1, x2, y2; short x1, y1, x2, y2;
public TextItem(float x, float y, String text, Caption caption) { public TextItem(float x, float y, String string, Text text) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.string = string;
this.text = text; this.text = text;
this.caption = caption; this.width = text.paint.measureText(string);
this.width = caption.paint.measureText(text);
this.path = null;
} }
public TextItem(float x, float y, String text, PathText pathText, float width) { public TextItem(float x, float y, String string, Text text, float width) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.string = string;
this.text = text; this.text = text;
this.path = pathText;
this.caption = null;
this.width = width; this.width = width;
} }

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
@ -30,7 +30,9 @@ import android.util.FloatMath;
import android.util.Log; import android.util.Log;
public class TextRenderer { public class TextRenderer {
private final static int TEXTURE_WIDTH = 512; private static String TAG = "TextRenderer";
private final static int TEXTURE_WIDTH = 256;
private final static int TEXTURE_HEIGHT = 256; private final static int TEXTURE_HEIGHT = 256;
private final static float SCALE = 8.0f; private final static float SCALE = 8.0f;
private final static int LBIT_MASK = 0xfffffffe; private final static int LBIT_MASK = 0xfffffffe;
@ -58,6 +60,7 @@ public class TextRenderer {
private static int hTextProjectionMatrix; private static int hTextProjectionMatrix;
private static int hTextVertex; private static int hTextVertex;
private static int hTextScale; private static int hTextScale;
private static int hTextScreenScale;
private static int hTextTextureCoord; private static int hTextTextureCoord;
private static Paint mPaint = new Paint(Color.BLACK); private static Paint mPaint = new Paint(Color.BLACK);
@ -86,6 +89,7 @@ public class TextRenderer {
hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv"); hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv");
hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj"); hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj");
hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale"); hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale");
hTextScreenScale = GLES20.glGetUniformLocation(mTextProgram, "u_swidth");
hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex"); hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex");
hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord"); hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord");
@ -235,16 +239,10 @@ public class TextRenderer {
TextItem t = tile.labels; TextItem t = tile.labels;
float yy; float yy;
short x1, x2, x3, x4, y1, y2, y3, y4;
for (int i = 0; t != null && i < max; t = t.next, i++) { for (int i = 0; t != null && i < max; t = t.next, i++) {
if (t.caption != null) { height = (int) (t.text.fontHeight) + 2 * mFontPadY;
height = (int) (t.caption.fontHeight) + 2 * mFontPadY;
} else {
height = (int) (t.path.fontHeight) + 2 * mFontPadY;
}
width = t.width + 2 * mFontPadX; width = t.width + 2 * mFontPadX;
if (height > advanceY) if (height > advanceY)
@ -256,35 +254,26 @@ public class TextRenderer {
advanceY = (int) height; advanceY = (int) height;
} }
if (t.caption != null) { yy = y + (height - 1) - t.text.fontDescent - mFontPadY;
yy = y + (height - 1) - t.caption.fontDescent - mFontPadY;
} else {
yy = y + (height - 1) - t.path.fontDescent - mFontPadY;
}
if (yy > TEXTURE_HEIGHT) { if (yy > TEXTURE_HEIGHT) {
Log.d(TAG, "reached max labels"); Log.d(TAG, "reached max labels");
continue; break;
// continue;
} }
if (t.caption != null) { if (t.text.stroke != null)
if (t.caption.stroke != null) mCanvas.drawText(t.string, x + t.width / 2, yy, t.text.stroke);
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.stroke);
mCanvas.drawText(t.text, x + t.width / 2, yy, t.caption.paint); mCanvas.drawText(t.string, x + t.width / 2, yy, t.text.paint);
} else {
if (t.path.stroke != null)
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.stroke);
mCanvas.drawText(t.text, x + t.width / 2, yy, t.path.paint);
}
if (width > TEXTURE_WIDTH) if (width > TEXTURE_WIDTH)
width = TEXTURE_WIDTH; width = TEXTURE_WIDTH;
float hw = width / 2.0f; float hw = width / 2.0f;
float hh = height / 2.0f; float hh = height / 2.0f;
short x1, x2, x3, x4, y1, y2, y3, y4;
if (t.caption != null) { if (t.text.caption) {
x1 = x3 = (short) (SCALE * (-hw)); x1 = x3 = (short) (SCALE * (-hw));
y1 = y3 = (short) (SCALE * (hh)); y1 = y3 = (short) (SCALE * (hh));
x2 = x4 = (short) (SCALE * (hw)); x2 = x4 = (short) (SCALE * (hw));
@ -330,11 +319,8 @@ public class TextRenderer {
short v2 = (short) (SCALE * (y + height)); short v2 = (short) (SCALE * (y + height));
// pack caption/way-text info in lowest bit // pack caption/way-text info in lowest bit
short tx; int tmp = (int) (SCALE * t.x) & LBIT_MASK;
if (t.caption == null) short tx = (short) (tmp | (t.text.caption ? 1 : 0));
tx = (short) ((int) (SCALE * t.x) & LBIT_MASK | 0);
else
tx = (short) ((int) (SCALE * t.x) & LBIT_MASK | 1);
short ty = (short) (SCALE * t.y); short ty = (short) (SCALE * t.y);
@ -393,8 +379,6 @@ public class TextRenderer {
return true; return true;
} }
private static String TAG = "TextRenderer";
static void compileTextures() { static void compileTextures() {
int offset = 0; int offset = 0;
TextTexture tex; TextTexture tex;
@ -422,32 +406,34 @@ public class TextRenderer {
static void beginDraw(float scale, float[] projection) { static void beginDraw(float scale, float[] projection) {
GLES20.glUseProgram(mTextProgram); GLES20.glUseProgram(mTextProgram);
GLES20.glEnableVertexAttribArray(hTextTextureCoord); // GLES20.glEnableVertexAttribArray(hTextTextureCoord);
GLES20.glEnableVertexAttribArray(hTextVertex); // GLES20.glEnableVertexAttribArray(hTextVertex);
int va = hTextTextureCoord;
if (!GLRenderer.vertexArray[va]) {
GLES20.glEnableVertexAttribArray(va);
GLRenderer.vertexArray[va] = true;
}
va = hTextVertex;
if (!GLRenderer.vertexArray[va]) {
GLES20.glEnableVertexAttribArray(va);
GLRenderer.vertexArray[va] = true;
}
GLES20.glUniform1f(hTextScale, scale); GLES20.glUniform1f(hTextScale, scale);
GLES20.glUniform1f(hTextScreenScale, 1f / GLRenderer.mWidth);
GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0); GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0);
if (debug) {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
mShortBuffer.clear();
mShortBuffer.put(debugVertices, 0, 16);
mShortBuffer.flip();
GLES20.glVertexAttribPointer(hTextVertex, 2,
GLES20.GL_SHORT, false, 8, mShortBuffer);
mShortBuffer.position(2);
GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
GLES20.GL_SHORT, false, 8, mShortBuffer);
} else {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
} }
}
static void endDraw() { static void endDraw() {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GLES20.glDisableVertexAttribArray(hTextTextureCoord); // GLES20.glDisableVertexAttribArray(hTextTextureCoord);
GLES20.glDisableVertexAttribArray(hTextVertex); // GLES20.glDisableVertexAttribArray(hTextVertex);
} }
static void drawTile(MapTile tile, float[] matrix) { static void drawTile(MapTile tile, float[] matrix) {
@ -456,10 +442,6 @@ public class TextRenderer {
GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0); GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0);
if (debug) {
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
} else {
GLES20.glVertexAttribPointer(hTextVertex, 4, GLES20.glVertexAttribPointer(hTextVertex, 4,
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)); GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8));
@ -470,5 +452,4 @@ public class TextRenderer {
GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) * GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) *
INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0); INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0);
} }
}
} }

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
public class TextTexture { public class TextTexture {
@ -22,12 +22,11 @@ public class TextTexture {
int offset; int offset;
MapTile tile; MapTile tile;
String[] text;
TextTexture(int textureID) { TextTexture(int textureID) {
vertices = new short[TextRenderer.MAX_LABELS * vertices = new short[TextRenderer.MAX_LABELS *
TextRenderer.VERTICES_PER_SPRITE * TextRenderer.VERTICES_PER_SPRITE *
TextRenderer.SHORTS_PER_VERTICE]; TextRenderer.SHORTS_PER_VERTICE];
id = textureID; id = textureID;
} }

View File

@ -0,0 +1,97 @@
/*
* Copyright 2012 Hannes Janetzek
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
import android.graphics.Bitmap;
import android.opengl.GLES20;
import android.opengl.GLUtils;
import android.util.Log;
public class TextureObject {
private static TextureObject pool;
public static synchronized TextureObject get() {
TextureObject to;
if (pool == null) {
init(10);
}
to = pool;
pool = pool.next;
to.next = null;
return to;
}
public static synchronized void release(TextureObject to) {
to.next = pool;
pool = to;
}
public static void uploadTexture(TextureObject to, Bitmap bitmap,
int format, int type, int w, int h) {
if (to == null) {
Log.d("...", "no fckn texture!");
return;
}
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, to.id);
if (to.width == w && to.height == h)
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, bitmap, format, type);
else {
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, format, bitmap, type, 0);
to.width = w;
to.height = h;
}
}
static void init(int num) {
TextureObject to;
int[] textureIds = new int[num];
GLES20.glGenTextures(num, textureIds, 0);
for (int i = 1; i < num; i++) {
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
to = new TextureObject(textureIds[i]);
to.next = pool;
pool = to;
}
}
public TextureObject next;
int id;
int width;
int height;
// vertex offset from which this texture is referenced
// or store texture id with vertex?
int offset;
TextureObject(int id) {
this.id = id;
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright 2012 Hannes Janetzek
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.TextureLayer;
import org.oscim.utils.GlUtils;
import android.opengl.GLES20;
public class TextureRenderer {
private static int mTextureProgram;
private static int hTextureMVMatrix;
private static int hTextureProjMatrix;
private static int hTextureVertex;
private static int hTextureScale;
private static int hTextureScreenScale;
private static int hTextureTexCoord;
private static int mIndicesVBO;
final static int INDICES_PER_SPRITE = 6;
final static int VERTICES_PER_SPRITE = 4;
final static int SHORTS_PER_VERTICE = 6;
// per texture
public final static int MAX_ITEMS = 40;
static void init() {
mTextureProgram = GlUtils.createProgram(Shaders.textVertexShader,
Shaders.textFragmentShader);
hTextureMVMatrix = GLES20.glGetUniformLocation(mTextureProgram, "u_mv");
hTextureProjMatrix = GLES20.glGetUniformLocation(mTextureProgram, "u_proj");
hTextureScale = GLES20.glGetUniformLocation(mTextureProgram, "u_scale");
hTextureScreenScale = GLES20.glGetUniformLocation(mTextureProgram, "u_swidth");
hTextureVertex = GLES20.glGetAttribLocation(mTextureProgram, "vertex");
hTextureTexCoord = GLES20.glGetAttribLocation(mTextureProgram, "tex_coord");
int bufferSize = MAX_ITEMS * VERTICES_PER_SPRITE
* SHORTS_PER_VERTICE * (Short.SIZE / 8);
ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize)
.order(ByteOrder.nativeOrder());
ShortBuffer mShortBuffer = buf.asShortBuffer();
// Setup triangle indices
short[] indices = new short[MAX_ITEMS * 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);
}
mShortBuffer.clear();
mShortBuffer.put(indices, 0, len);
mShortBuffer.flip();
int[] mVboIds = new int[1];
GLES20.glGenBuffers(1, mVboIds, 0);
mIndicesVBO = mVboIds[0];
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8),
mShortBuffer, GLES20.GL_STATIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}
static Layer draw(Layer layer, float scale, float[] projection,
float matrix[], int offset) {
GLES20.glUseProgram(mTextureProgram);
GlUtils.checkGlError("draw texture1");
int va = hTextureTexCoord;
if (!GLRenderer.vertexArray[va]) {
GLES20.glEnableVertexAttribArray(va);
GLRenderer.vertexArray[va] = true;
}
va = hTextureVertex;
if (!GLRenderer.vertexArray[va]) {
GLES20.glEnableVertexAttribArray(va);
GLRenderer.vertexArray[va] = true;
}
TextureLayer tl = (TextureLayer) layer;
GlUtils.checkGlError("draw texture2.");
GLES20.glUniform1f(hTextureScale, scale);
GLES20.glUniform1f(hTextureScreenScale, 1f / GLRenderer.mWidth);
GLES20.glUniformMatrix4fv(hTextureProjMatrix, 1, false, projection, 0);
GLES20.glUniformMatrix4fv(hTextureMVMatrix, 1, false, matrix, 0);
GlUtils.checkGlError("draw texture2");
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
GlUtils.checkGlError("draw texture3");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tl.textures.id);
GlUtils.checkGlError("draw texture4");
GlUtils.checkGlError("draw texture5");
GLES20.glVertexAttribPointer(hTextureVertex, 4,
GLES20.GL_SHORT, false, 12, offset);
GlUtils.checkGlError("draw texture..");
GLES20.glVertexAttribPointer(hTextureTexCoord, 2,
GLES20.GL_SHORT, false, 12, offset + 8);
GlUtils.checkGlError("draw texture...");
GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tl.verticesCnt / 4)
* INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GlUtils.checkGlError("draw texture");
return layer.next;
}
}

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import org.oscim.core.MercatorProjection; import org.oscim.core.MercatorProjection;
import org.oscim.core.Tag; import org.oscim.core.Tag;
@ -21,16 +21,21 @@ import org.oscim.core.WebMercator;
import org.oscim.database.IMapDatabase; import org.oscim.database.IMapDatabase;
import org.oscim.database.IMapDatabaseCallback; import org.oscim.database.IMapDatabaseCallback;
import org.oscim.database.QueryResult; import org.oscim.database.QueryResult;
import org.oscim.generator.JobTile;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.PolygonLayer;
import org.oscim.renderer.layer.SymbolItem;
import org.oscim.renderer.layer.SymbolLayer;
import org.oscim.theme.IRenderCallback; import org.oscim.theme.IRenderCallback;
import org.oscim.theme.RenderTheme; import org.oscim.theme.RenderTheme;
import org.oscim.theme.renderinstruction.Area; import org.oscim.theme.renderinstruction.Area;
import org.oscim.theme.renderinstruction.Caption;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
import org.oscim.theme.renderinstruction.PathText;
import org.oscim.theme.renderinstruction.RenderInstruction; import org.oscim.theme.renderinstruction.RenderInstruction;
import org.oscim.theme.renderinstruction.Text;
import org.oscim.view.DebugSettings; import org.oscim.view.DebugSettings;
import org.oscim.view.MapView; import org.oscim.view.MapView;
import org.oscim.view.generator.JobTile;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Paint; import android.graphics.Paint;
@ -45,9 +50,9 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private static final double PI180 = (Math.PI / 180) / 1000000.0; private static final double PI180 = (Math.PI / 180) / 1000000.0;
private static final double PIx4 = Math.PI * 4; private static final double PIx4 = Math.PI * 4;
private static final double STROKE_INCREASE = Math.sqrt(2); private static final double STROKE_INCREASE = Math.sqrt(2);
private static final byte LAYERS = 11; private static final byte LAYERS = 11;
private static final double f900913 = 20037508.342789244;
static final byte STROKE_MIN_ZOOM_LEVEL = 12; static final byte STROKE_MIN_ZOOM_LEVEL = 12;
static final byte STROKE_MAX_ZOOM_LEVEL = 17; static final byte STROKE_MAX_ZOOM_LEVEL = 17;
@ -58,13 +63,15 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private MapTile mCurrentTile; private MapTile mCurrentTile;
// coordinates of the currently processed way
private float[] mCoords; private float[] mCoords;
private short[] mIndices; private short[] mIndices;
private LineLayer mLineLayers; // current line layer, will be added to outline layers
private PolygonLayer mPolyLayers;
private LineLayer mCurLineLayer; private LineLayer mCurLineLayer;
private PolygonLayer mCurPolyLayer;
// layer data prepared for rendering
private Layers mLayers;
private TextItem mLabels; private TextItem mLabels;
@ -74,10 +81,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private float mStrokeScale = 1.0f; private float mStrokeScale = 1.0f;
private boolean mProjected; private boolean mProjected;
// private boolean mProjectedResult;
private float mSimplify; private float mSimplify;
// private boolean firstMatch;
// private boolean prevClosed;
private RenderInstruction[] mRenderInstructions = null; private RenderInstruction[] mRenderInstructions = null;
@ -126,8 +130,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
mTagName = null; mTagName = null;
if (mMapProjection != null) if (mMapProjection != null) {
{
long x = mCurrentTile.pixelX; long x = mCurrentTile.pixelX;
long y = mCurrentTile.pixelY + Tile.TILE_SIZE; long y = mCurrentTile.pixelY + Tile.TILE_SIZE;
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel; long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
@ -137,7 +140,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
long dy = (y - (z >> 1)); long dy = (y - (z >> 1));
if (mMapProjection == WebMercator.NAME) { if (mMapProjection == WebMercator.NAME) {
double div = f900913 / (z >> 1); double div = WebMercator.f900913 / (z >> 1);
// divy = f900913 / (z >> 1); // divy = f900913 / (z >> 1);
mPoiX = (float) (longitude / div - dx); mPoiX = (float) (longitude / div - dx);
mPoiY = (float) (latitude / div + dy); mPoiY = (float) (latitude / div + dy);
@ -147,6 +150,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
mPoiX = (float) (longitude / divx - dx); mPoiX = (float) (longitude / divx - dx);
double sinLat = Math.sin(latitude * PI180); double sinLat = Math.sin(latitude * PI180);
mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy); mPoiY = (float) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy);
// TODO remove this, only used for mapsforge maps
if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10 if (mPoiX < -10 || mPoiX > Tile.TILE_SIZE + 10 || mPoiY < -10
|| mPoiY > Tile.TILE_SIZE + 10) || mPoiY > Tile.TILE_SIZE + 10)
return; return;
@ -230,45 +235,45 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
} }
@Override @Override
public void renderAreaCaption(Caption caption) { public void renderAreaCaption(Text text) {
// Log.d(TAG, "renderAreaCaption: " + mTagName); // Log.d(TAG, "renderAreaCaption: " + mTagName);
if (mTagName == null) if (mTagName == null)
return; return;
if (caption.textKey == mTagEmptyName.key) { if (text.textKey == mTagEmptyName.key) {
TextItem t = new TextItem(mCoords[0], mCoords[1], mTagName.value, caption); TextItem t = new TextItem(mCoords[0], mCoords[1], mTagName.value, text);
t.next = mLabels; t.next = mLabels;
mLabels = t; mLabels = t;
} }
} }
@Override @Override
public void renderPointOfInterestCaption(Caption caption) { public void renderPointOfInterestCaption(Text text) {
// Log.d(TAG, "renderPointOfInterestCaption: " + mPoiX + " " + mPoiY + // Log.d(TAG, "renderPointOfInterestCaption: " + mPoiX + " " + mPoiY +
// " " + mTagName); // " " + mTagName);
if (mTagName == null) if (mTagName == null)
return; return;
if (caption.textKey == mTagEmptyName.key) { if (text.textKey == mTagEmptyName.key) {
TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, caption); TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, text);
t.next = mLabels; t.next = mLabels;
mLabels = t; mLabels = t;
} }
} }
@Override @Override
public void renderWayText(PathText pathText) { public void renderWayText(Text text) {
// Log.d(TAG, "renderWayText: " + mTagName); // Log.d(TAG, "renderWayText: " + mTagName);
if (mTagName == null) if (mTagName == null)
return; return;
if (pathText.textKey == mTagEmptyName.key && mTagName.value != null) { if (text.textKey == mTagEmptyName.key && mTagName.value != null) {
mLabels = WayDecorator.renderText(mCoords, mTagName.value, pathText, 0, mLabels = WayDecorator.renderText(mCoords, mTagName.value, text, 0,
mIndices[0], mLabels); mIndices[0], mLabels);
} }
} }
@ -286,67 +291,47 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
} }
@Override @Override
public void renderPointOfInterestSymbol(Bitmap symbol) { public void renderPointOfInterestSymbol(Bitmap bitmap) {
// TODO Auto-generated method stub // Log.d(TAG, "add symbol");
if (mLayers.symbolLayers == null)
mLayers.symbolLayers = new SymbolLayer();
SymbolLayer sl = (SymbolLayer) mLayers.symbolLayers;
SymbolItem it = new SymbolItem();
it.x = mPoiX;
it.y = mPoiY;
it.bitmap = bitmap;
it.billboard = true;
sl.addSymbol(it);
} }
private int countLines;
private int countNodes;
@Override @Override
public void renderWay(Line line, int level) { public void renderWay(Line line, int level) {
projectToTile(); projectToTile();
if (line.outline && mCurLineLayer == null) if (line.outline && mCurLineLayer == null)
return; return;
float w = line.width; int numLayer = (mDrawingLayer * 2) + level;
LineLayer lineLayer = (LineLayer) mLayers.getLayer(numLayer, Layer.LINE);
if (lineLayer == null)
return;
if (lineLayer.line == null) {
lineLayer.line = line;
float w = line.width;
if (!line.fixed) { if (!line.fixed) {
w *= mStrokeScale; w *= mStrokeScale;
w *= mProjectionScaleFactor; w *= mProjectionScaleFactor;
} }
lineLayer.width = w;
LineLayer lineLayer = null;
int numLayer = mDrawingLayer + level;
LineLayer l = mLineLayers;
if (mCurLineLayer != null && mCurLineLayer.layer == numLayer) {
lineLayer = mCurLineLayer;
} else if (l == null || l.layer > numLayer) {
// insert new layer at start
lineLayer = new LineLayer(numLayer, line, w, line.outline);
// lineLayer = LineLayers.get(numLayer, line, w, false);
lineLayer.next = l;
mLineLayers = lineLayer;
} else {
while (l != null) {
// found layer
if (l.layer == numLayer) {
lineLayer = l;
break;
}
// insert new layer between current and next layer
if (l.next == null || l.next.layer > numLayer) {
lineLayer = new LineLayer(numLayer, line, w, line.outline);
// lineLayer = LineLayers.get(numLayer, line, w, false);
lineLayer.next = l.next;
l.next = lineLayer;
break;
}
l = l.next;
}
} }
if (lineLayer == null) {
mCurLineLayer = null;
return;
}
if (line.outline) { if (line.outline) {
lineLayer.addOutline(mCurLineLayer); lineLayer.addOutline(mCurLineLayer);
return; return;
@ -367,38 +352,12 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
int numLayer = mDrawingLayer + level; int numLayer = mDrawingLayer + level;
PolygonLayer layer = null; PolygonLayer layer = (PolygonLayer) mLayers.getLayer(numLayer, Layer.POLYGON);
PolygonLayer l = mPolyLayers;
if (mCurPolyLayer != null && mCurPolyLayer.layer == numLayer) {
layer = mCurPolyLayer;
} else if (l == null || l.layer > numLayer) {
// insert new layer at start
layer = new PolygonLayer(numLayer, area);
layer.next = l;
mPolyLayers = layer;
} else {
while (l != null) {
if (l.layer == numLayer) {
layer = l;
break;
}
// insert new layer between current and next layer
if (l.next == null || l.next.layer > numLayer) {
layer = new PolygonLayer(numLayer, area);
layer.next = l.next;
l.next = layer;
break;
}
l = l.next;
}
}
if (layer == null) if (layer == null)
return; return;
mCurPolyLayer = layer; if (layer.area == null)
layer.area = area;
layer.addPolygon(mCoords, mIndices); layer.addPolygon(mCoords, mIndices);
} }
@ -430,7 +389,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
mDebugDrawUnmatched = debugSettings.mDrawUnmatchted; mDebugDrawUnmatched = debugSettings.mDrawUnmatchted;
if (tile.newData || tile.isReady) { if (tile.newData || tile.isReady) {
// fixed now.... // should be fixed now.
Log.d(TAG, "XXX tile already loaded " Log.d(TAG, "XXX tile already loaded "
+ tile + " " + tile + " "
+ tile.newData + " " + tile.newData + " "
@ -446,22 +405,19 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
else else
setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL); setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL);
// firstMatch = true;
countLines = 0;
countNodes = 0;
// acount for area changes with latitude // acount for area changes with latitude
mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection
.pixelYToLatitude(tile.pixelY, tile.zoomLevel) .pixelYToLatitude(tile.pixelY, tile.zoomLevel)
* (Math.PI / 180))); * (Math.PI / 180)));
mLayers = new Layers();
if (mMapDatabase.executeQuery(tile, this) != QueryResult.SUCCESS) { if (mMapDatabase.executeQuery(tile, this) != QueryResult.SUCCESS) {
Log.d(TAG, "Failed loading: " + tile); Log.d(TAG, "Failed loading: " + tile);
LineRenderer.clear(mLineLayers); mLayers.clear();
PolygonRenderer.clear(mPolyLayers); mLayers = null;
mLineLayers = null;
mPolyLayers = null;
mLabels = null; mLabels = null;
mCurLineLayer = null;
tile.isLoading = false; tile.isLoading = false;
return false; return false;
} }
@ -478,15 +434,12 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
TileGenerator.renderTheme.matchWay(this, debugTagBox, (byte) 0, false, true); TileGenerator.renderTheme.matchWay(this, debugTagBox, (byte) 0, false, true);
} }
tile.lineLayers = mLineLayers; tile.layers = mLayers;
tile.polygonLayers = mPolyLayers;
tile.labels = mLabels; tile.labels = mLabels;
mCurPolyLayer = null; mLayers = null;
mCurLineLayer = null;
mLineLayers = null;
mPolyLayers = null;
mLabels = null; mLabels = null;
mCurLineLayer = null;
return true; return true;
} }
@ -542,6 +495,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
return mRenderInstructions != null; return mRenderInstructions != null;
} }
// TODO move this to Projection classes
private boolean projectToTile() { private boolean projectToTile() {
if (mProjected || mMapProjection == null) if (mProjected || mMapProjection == null)
return true; return true;
@ -558,13 +512,12 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel; long z = Tile.TILE_SIZE << mCurrentTile.zoomLevel;
float min = mSimplify; float min = mSimplify;
double divx, divy; double divx, divy = 0;
long dx = (x - (z >> 1)); long dx = (x - (z >> 1));
long dy = (y - (z >> 1)); long dy = (y - (z >> 1));
if (useWebMercator) { if (useWebMercator) {
divx = f900913 / (z >> 1); divx = WebMercator.f900913 / (z >> 1);
divy = f900913 / (z >> 1);
} else { } else {
divx = 180000000.0 / (z >> 1); divx = 180000000.0 / (z >> 1);
divy = z / PIx4; divy = z / PIx4;
@ -584,7 +537,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
if (useWebMercator) { if (useWebMercator) {
lon = (float) (coords[pos] / divx - dx); lon = (float) (coords[pos] / divx - dx);
lat = (float) (coords[pos + 1] / divy + dy); lat = (float) (coords[pos + 1] / divx + dy);
} else { } else {
lon = (float) ((coords[pos]) / divx - dx); lon = (float) ((coords[pos]) / divx - dx);
double sinLat = Math.sin(coords[pos + 1] * PI180); double sinLat = Math.sin(coords[pos + 1] * PI180);

View File

@ -12,9 +12,9 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer;
import org.oscim.theme.renderinstruction.PathText; import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.GeometryUtils; import org.oscim.utils.GeometryUtils;
import android.util.FloatMath; import android.util.FloatMath;
@ -99,7 +99,7 @@ final class WayDecorator {
// } // }
// } // }
static TextItem renderText(float[] coordinates, String text, PathText pathText, static TextItem renderText(float[] coordinates, String string, Text text,
int pos, int len, TextItem textItems) { int pos, int len, TextItem textItems) {
TextItem items = textItems; TextItem items = textItems;
TextItem t = null; TextItem t = null;
@ -175,7 +175,7 @@ final class WayDecorator {
} else if (segmentLengthInPixel > minWidth) { } else if (segmentLengthInPixel > minWidth) {
if (wayNameWidth < 0) { if (wayNameWidth < 0) {
wayNameWidth = pathText.paint.measureText(text); wayNameWidth = text.paint.measureText(string);
} }
if (segmentLengthInPixel > wayNameWidth + 25) { if (segmentLengthInPixel > wayNameWidth + 25) {
@ -227,7 +227,7 @@ final class WayDecorator {
if (x1 - 10 < t2.x2 && t2.x1 - 10 < x2 && top - 10 < bot2 if (x1 - 10 < t2.x2 && t2.x1 - 10 < x2 && top - 10 < bot2
&& top2 - 10 < bot) { && top2 - 10 < bot) {
if (t2.text.equals(text)) { if (t2.string.equals(string)) {
intersects = true; intersects = true;
break; break;
} }
@ -256,8 +256,8 @@ final class WayDecorator {
} }
// if (t == null) // if (t == null)
t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, text, t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, string,
pathText, wayNameWidth); text, wayNameWidth);
t.x1 = (short) x1; t.x1 = (short) x1;
t.y1 = (short) y1; t.y1 = (short) y1;

View File

@ -0,0 +1,37 @@
/*
* Copyright 2012 Hannes Janetzek
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer.layer;
public class Layer {
public final static byte LINE = 0;
public final static byte POLYGON = 1;
public final static byte WAYTEXT = 2;
public final static byte POITEXT = 3;
public final static byte SYMBOL = 4;
public final static byte BITMAP = 5;
public byte type;
public Layer next;
int layer;
// number of vertices this layer holds
public int verticesCnt;
// vertices offset of this layer in VBO
public int offset;
VertexPoolItem pool;
protected VertexPoolItem curItem;
}

View File

@ -0,0 +1,166 @@
/*
* Copyright 2012 Hannes Janetzek
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer.layer;
import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject;
import android.util.Log;
public class Layers {
public Layer layers;
public int lineOffset;
public Layer symbolLayers;
public int symbolOffset;
private Layer mCurLayer;
public Layer getLayer(int level, byte type) {
Layer l = layers;
Layer ret = null;
if (mCurLayer != null && mCurLayer.layer == level) {
ret = mCurLayer;
} else if (l == null || l.layer > level) {
// insert new layer at start
l = null;
} else {
while (true) {
if (l.layer == level) {
// found layer
ret = l;
break;
}
if (l.next == null || l.next.layer > level) {
// insert new layer between current and next layer
break;
}
l = l.next;
}
}
if (ret == null) {
if (type == Layer.LINE)
ret = new LineLayer(level);
else
ret = new PolygonLayer(level);
if (l == null) {
ret.next = layers;
layers = ret;
} else {
ret.next = l.next;
l.next = ret;
}
} else if (ret.type != type) {
Log.d("...", "wrong layer type " + ret.type + " " + type);
// FIXME thorw exception
return null;
}
return ret;
}
private static int LINE_VERTEX_SHORTS = 4;
private static int POLY_VERTEX_SHORTS = 2;
private static int TEXTURE_VERTEX_SHORTS = 6;
public int getSize() {
int size = 0;
for (Layer l = layers; l != null; l = l.next) {
if (l.type == Layer.LINE)
size += l.verticesCnt * LINE_VERTEX_SHORTS;
else
size += l.verticesCnt * POLY_VERTEX_SHORTS;
}
for (Layer l = symbolLayers; l != null; l = l.next) {
size += l.verticesCnt * TEXTURE_VERTEX_SHORTS;
}
return size;
}
public void compile(ShortBuffer sbuf, boolean addFill) {
// offset from fill coordinates
int pos = 0;
if (addFill)
pos = 4;
// add polygons first, needed to get the offsets right...
addLayerItems(sbuf, layers, Layer.POLYGON, pos);
lineOffset = sbuf.position() * 2; // * short-bytes
addLayerItems(sbuf, layers, Layer.LINE, 0);
symbolOffset = sbuf.position() * 2; // * short-bytes
for (Layer l = symbolLayers; l != null; l = l.next) {
SymbolLayer sl = (SymbolLayer) l;
sl.compile(sbuf);
}
}
private static void addLayerItems(ShortBuffer sbuf, Layer l, byte type, int pos) {
VertexPoolItem last = null, items = null;
for (; l != null; l = l.next) {
if (l.type != type)
continue;
for (VertexPoolItem it = l.pool; it != null; it = it.next) {
if (it.next == null)
sbuf.put(it.vertices, 0, it.used);
else
sbuf.put(it.vertices);
last = it;
}
if (last == null)
continue;
l.offset = pos;
pos += l.verticesCnt;
last.next = items;
items = l.pool;
last = null;
l.pool = null;
l.curItem = null;
}
VertexPool.release(items);
}
public void clear() {
// FIXME collect pool and add as a whole
for (Layer l = layers; l != null; l = l.next) {
if (l.pool != null) {
VertexPool.release(l.pool);
l.pool = null;
l.curItem = null;
}
}
for (Layer l = symbolLayers; l != null; l = l.next) {
SymbolLayer sl = (SymbolLayer) l;
if (sl.textures != null)
TextureObject.release(sl.textures);
}
}
}

View File

@ -12,15 +12,16 @@
* You should have received a copy of the GNU Lesser General License along with * You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer.layer;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.GLRenderer;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
import android.graphics.Paint.Cap; import android.graphics.Paint.Cap;
import android.util.FloatMath; import android.util.FloatMath;
class LineLayer { public final class LineLayer extends Layer {
private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER; private static final float COORD_SCALE = GLRenderer.COORD_MULTIPLIER;
// scale factor mapping extrusion vector to short values // scale factor mapping extrusion vector to short values
@ -29,33 +30,26 @@ class LineLayer {
// coordinates // coordinates
private static final int DIR_MASK = 0xFFFFFFFC; private static final int DIR_MASK = 0xFFFFFFFC;
// next layer
LineLayer next;
// lines referenced by this outline layer // lines referenced by this outline layer
LineLayer outlines; public LineLayer outlines;
public Line line;
public float width;
Line line; // boolean isOutline;
float width;
boolean isOutline;
int layer;
VertexPoolItem pool; LineLayer(int layer) {
protected VertexPoolItem curItem;
// number of vertices this layer holds
int verticesCnt;
// vertices offset of this layer in VBO
int offset;
LineLayer(int layer, Line line, float width, boolean outline) {
this.layer = layer; this.layer = layer;
this.width = width; this.type = Layer.LINE;
this.line = line;
this.isOutline = outline;
} }
void addOutline(LineLayer link) { // LineLayer(int layer, Line line, float width, boolean outline) {
// this.layer = layer;
// this.width = width;
// this.line = line;
// // this.isOutline = outline;
// }
public void addOutline(LineLayer link) {
for (LineLayer l = outlines; l != null; l = l.outlines) for (LineLayer l = outlines; l != null; l = l.outlines)
if (link == l) if (link == l)
return; return;
@ -69,7 +63,7 @@ class LineLayer {
* (https://github.com/olofsj/GLMap/) by olofsj * (https://github.com/olofsj/GLMap/) by olofsj
*/ */
void addLine(float[] points, short[] index, boolean closed) { public void addLine(float[] points, short[] index, boolean closed) {
float x, y, nextX, nextY, prevX, prevY; float x, y, nextX, nextY, prevX, prevY;
float a, ux, uy, vx, vy, wx, wy; float a, ux, uy, vx, vy, wx, wy;
@ -108,7 +102,6 @@ class LineLayer {
continue; continue;
} }
//
closed = false; closed = false;
// amount of vertices used // amount of vertices used

View File

@ -12,32 +12,25 @@
* You should have received a copy of the GNU Lesser General License along with * You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer.layer;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.GLRenderer;
import org.oscim.theme.renderinstruction.Area; import org.oscim.theme.renderinstruction.Area;
class PolygonLayer { public final class PolygonLayer extends Layer {
private static final float S = GLRenderer.COORD_MULTIPLIER; private static final float S = GLRenderer.COORD_MULTIPLIER;
PolygonLayer next; public Area area;
Area area;
VertexPoolItem pool; PolygonLayer(int layer) {
protected VertexPoolItem curItem;
int verticesCnt;
int offset;
final int layer;
PolygonLayer(int layer, Area area) {
this.layer = layer; this.layer = layer;
this.area = area; this.type = Layer.POLYGON;
curItem = VertexPool.get(); curItem = VertexPool.get();
pool = curItem; pool = curItem;
} }
void addPolygon(float[] points, short[] index) { public void addPolygon(float[] points, short[] index) {
short center = (short) ((Tile.TILE_SIZE >> 1) * S); short center = (short) ((Tile.TILE_SIZE >> 1) * S);
VertexPoolItem si = curItem; VertexPoolItem si = curItem;

View File

@ -0,0 +1,29 @@
/*
* Copyright 2012 Hannes Janetzek
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer.layer;
import android.graphics.Bitmap;
public class SymbolItem {
SymbolItem next;
public Bitmap bitmap;
public float x;
public float y;
public boolean billboard;
// center, top, bottom, left, right, top-left...
byte placement;
}

View File

@ -0,0 +1,200 @@
/*
* Copyright 2012 Hannes Janetzek
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer.layer;
import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.RectF;
import android.opengl.GLUtils;
import android.util.Log;
// TODO share one static texture for all poi map symabols
public final class SymbolLayer extends TextureLayer {
private static String TAG = SymbolLayer.class.getSimpleName();
private final static int TEXTURE_WIDTH = 256;
private final static int TEXTURE_HEIGHT = 256;
private final static float SCALE = 8.0f;
private static short[] mVertices;
private static Bitmap mBitmap;
private static Canvas mCanvas;
private static int mBitmapFormat;
private static int mBitmapType;
SymbolItem symbols;
public SymbolLayer() {
if (mBitmap == null) {
mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mBitmapFormat = GLUtils.getInternalFormat(mBitmap);
mBitmapType = GLUtils.getType(mBitmap);
//
mVertices = new short[40 * 24];
}
}
public void addSymbol(SymbolItem item) {
verticesCnt += 4;
SymbolItem it = symbols;
for (; it != null; it = it.next) {
if (it.bitmap == item.bitmap) {
item.next = it.next;
it.next = item;
return;
}
}
item.next = symbols;
symbols = item;
}
private final static int LBIT_MASK = 0xfffffffe;
private final RectF mRect = new RectF();
// TODO ... reuse texture when only symbol position changed
public void compile(ShortBuffer sbuf) {
int pos = 0;
short buf[] = mVertices;
int advanceY = 0;
float x = 0;
float y = 0;
mBitmap.eraseColor(Color.TRANSPARENT);
for (SymbolItem it = symbols; it != null;) {
// add bitmap
float width = it.bitmap.getWidth();
float height = it.bitmap.getHeight();
if (height > advanceY)
advanceY = (int) height;
if (x + width > TEXTURE_WIDTH) {
x = 0;
y += advanceY;
advanceY = (int) (height + 0.5f);
if (y + height > TEXTURE_HEIGHT) {
Log.d(TAG, "reached max symbols");
// need to sync bitmap upload somehow???
TextureObject to = TextureObject.get();
TextureObject.uploadTexture(to, mBitmap,
mBitmapFormat, mBitmapType,
TEXTURE_WIDTH, TEXTURE_HEIGHT);
to.next = textures;
textures = to;
sbuf.put(buf, 0, pos);
pos = 0;
}
}
mRect.left = x;
mRect.top = y;
mRect.right = x + width;
mRect.bottom = y + height;
// Log.d("...", "draw " + x + " " + y + " " + width + " " + height);
mCanvas.drawBitmap(it.bitmap, null, mRect, null);
// mCanvas.drawBitmap(it.bitmap, x, y, null);
float hw = width / 2.0f;
float hh = height / 2.0f;
short x1, x2, x3, x4, y1, y2, y3, y4;
x1 = x3 = (short) (SCALE * (-hw));
x2 = x4 = (short) (SCALE * (hw));
y1 = y3 = (short) (SCALE * (hh));
y2 = y4 = (short) (SCALE * (-hh));
short u1 = (short) (SCALE * x);
short v1 = (short) (SCALE * y);
short u2 = (short) (SCALE * (x + width));
short v2 = (short) (SCALE * (y + height));
// add symbol items referencing the same bitmap
for (SymbolItem it2 = it;; it2 = it2.next) {
if (it2 == null || it2.bitmap != it.bitmap) {
it = it2;
break;
}
// add vertices
short tx = (short) ((int) (SCALE * it2.x) & LBIT_MASK | (it2.billboard ? 1 : 0));
short ty = (short) (SCALE * it2.y);
// top-left
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x1;
buf[pos++] = y1;
buf[pos++] = u1;
buf[pos++] = v2;
// top-right
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x2;
buf[pos++] = y3;
buf[pos++] = u2;
buf[pos++] = v2;
// bot-right
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x4;
buf[pos++] = y4;
buf[pos++] = u2;
buf[pos++] = v1;
// bot-left
buf[pos++] = tx;
buf[pos++] = ty;
buf[pos++] = x3;
buf[pos++] = y2;
buf[pos++] = u1;
buf[pos++] = v1;
x += width + 1;
}
}
TextureObject to = TextureObject.get();
TextureObject.uploadTexture(to, mBitmap,
mBitmapFormat, mBitmapType,
TEXTURE_WIDTH, TEXTURE_HEIGHT);
to.next = textures;
textures = to;
sbuf.put(buf, 0, pos);
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2010, 2011, 2012 mapsforge.org * Copyright 2012 Hannes Janetzek
* *
* This program is free software: you can redistribute it and/or modify it under the * 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 * terms of the GNU Lesser General Public License as published by the Free Software
@ -12,8 +12,11 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer.layer;
public class SymbolLayer { import org.oscim.renderer.TextItem;
public final class TextLayer extends TextureLayer {
TextItem labels;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2010, 2011, 2012 mapsforge.org * Copyright 2012 Hannes Janetzek
* *
* This program is free software: you can redistribute it and/or modify it under the * 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 * terms of the GNU Lesser General Public License as published by the Free Software
@ -12,8 +12,11 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer.layer;
public class TextureLayer { import org.oscim.renderer.TextureObject;
public abstract class TextureLayer extends Layer {
public TextureObject textures;
} }

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer.layer;
import android.util.Log; import android.util.Log;
@ -23,7 +23,7 @@ public class VertexPool {
static private int count = 0; static private int count = 0;
static private int countAll = 0; static private int countAll = 0;
static synchronized void init() { public static synchronized void init() {
count = 0; count = 0;
countAll = 0; countAll = 0;
pool = null; pool = null;
@ -61,7 +61,7 @@ public class VertexPool {
// private static float load = 1.0f; // private static float load = 1.0f;
// private static int loadCount = 0; // private static int loadCount = 0;
static synchronized void add(VertexPoolItem items) { static synchronized void release(VertexPoolItem items) {
if (items == null) if (items == null)
return; return;
@ -86,7 +86,8 @@ public class VertexPool {
last.next = pool; last.next = pool;
pool = items; pool = items;
// Log.d("Pool", "added: " + (count - pcnt) + " " + count + " " + countAll // Log.d("Pool", "added: " + (count - pcnt) + " " + count + " " +
// countAll
// + " load: " + (load / loadCount)); // + " load: " + (load / loadCount));
} else { } else {

View File

@ -12,7 +12,7 @@
* You should have received a copy of the GNU Lesser General Public License along with * You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>. * this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.oscim.view.renderer; package org.oscim.renderer.layer;
public class VertexPoolItem { public class VertexPoolItem {
final short[] vertices; final short[] vertices;

View File

@ -15,9 +15,8 @@
package org.oscim.theme; package org.oscim.theme;
import org.oscim.theme.renderinstruction.Area; import org.oscim.theme.renderinstruction.Area;
import org.oscim.theme.renderinstruction.Caption;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
import org.oscim.theme.renderinstruction.PathText; import org.oscim.theme.renderinstruction.Text;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Paint; import android.graphics.Paint;
@ -36,14 +35,6 @@ public interface IRenderCallback {
*/ */
void renderArea(Area area, int level); void renderArea(Area area, int level);
/**
* Renders an area caption with the given text.
*
* @param caption
* the text to be rendered.
*/
void renderAreaCaption(Caption caption);
/** /**
* Renders an area symbol with the given bitmap. * Renders an area symbol with the given bitmap.
* *
@ -52,14 +43,6 @@ public interface IRenderCallback {
*/ */
void renderAreaSymbol(Bitmap symbol); void renderAreaSymbol(Bitmap symbol);
/**
* Renders a point of interest caption with the given text.
*
* @param caption
* the text to be rendered.
*/
void renderPointOfInterestCaption(Caption caption);
/** /**
* Renders a point of interest circle with the given parameters. * Renders a point of interest circle with the given parameters.
* *
@ -105,8 +88,25 @@ public interface IRenderCallback {
/** /**
* Renders a way with the given text along the way path. * Renders a way with the given text along the way path.
* *
* @param pathText * @param text
* ... * ...
*/ */
void renderWayText(PathText pathText); void renderWayText(Text text);
/**
* Renders an area caption with the given text.
*
* @param text
* the text to be rendered.
*/
void renderAreaCaption(Text text);
/**
* Renders a point of interest caption with the given text.
*
* @param text
* the text to be rendered.
*/
void renderPointOfInterestCaption(Text text);
} }

View File

@ -26,13 +26,12 @@ import javax.xml.parsers.SAXParserFactory;
import org.oscim.theme.renderinstruction.Area; import org.oscim.theme.renderinstruction.Area;
import org.oscim.theme.renderinstruction.AreaLevel; import org.oscim.theme.renderinstruction.AreaLevel;
import org.oscim.theme.renderinstruction.Caption;
import org.oscim.theme.renderinstruction.Circle; import org.oscim.theme.renderinstruction.Circle;
import org.oscim.theme.renderinstruction.Line; import org.oscim.theme.renderinstruction.Line;
import org.oscim.theme.renderinstruction.LineSymbol; import org.oscim.theme.renderinstruction.LineSymbol;
import org.oscim.theme.renderinstruction.PathText;
import org.oscim.theme.renderinstruction.RenderInstruction; import org.oscim.theme.renderinstruction.RenderInstruction;
import org.oscim.theme.renderinstruction.Symbol; import org.oscim.theme.renderinstruction.Symbol;
import org.oscim.theme.renderinstruction.Text;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -55,7 +54,7 @@ public class RenderThemeHandler extends DefaultHandler {
private static final String ELEMENT_NAME_RENDER_THEME = "rendertheme"; private static final String ELEMENT_NAME_RENDER_THEME = "rendertheme";
private static final String ELEMENT_NAME_RULE = "rule"; private static final String ELEMENT_NAME_RULE = "rule";
private static final String ELEMENT_NAME_STYPE_PATH_TEXT = "style-pathtext"; private static final String ELEMENT_NAME_STYLE_TEXT = "style-text";
private static final String ELEMENT_NAME_STYLE_AREA = "style-area"; private static final String ELEMENT_NAME_STYLE_AREA = "style-area";
private static final String ELEMENT_NAME_STYLE_LINE = "style-line"; private static final String ELEMENT_NAME_STYLE_LINE = "style-line";
private static final String ELEMENT_NAME_STYLE_OUTLINE = "style-outline"; private static final String ELEMENT_NAME_STYLE_OUTLINE = "style-outline";
@ -68,7 +67,8 @@ public class RenderThemeHandler extends DefaultHandler {
/** /**
* @param inputStream * @param inputStream
* an input stream containing valid render theme XML data. * an input stream containing valid render theme XML data.
* @return a new RenderTheme which is created by parsing the XML data from the input stream. * @return a new RenderTheme which is created by parsing the XML data from
* the input stream.
* @throws SAXException * @throws SAXException
* if an error occurs while parsing the render theme XML. * if an error occurs while parsing the render theme XML.
* @throws ParserConfigurationException * @throws ParserConfigurationException
@ -171,11 +171,11 @@ public class RenderThemeHandler extends DefaultHandler {
mRuleStack.push(mCurrentRule); mRuleStack.push(mCurrentRule);
} }
else if (ELEMENT_NAME_STYPE_PATH_TEXT.equals(localName)) { else if (ELEMENT_NAME_STYLE_TEXT.equals(localName)) {
checkState(localName, Element.STYLE); checkState(localName, Element.STYLE);
PathText pathText = PathText.create(localName, attributes); Text text = Text.create(localName, attributes, false);
tmpStyleHash.put("t" + pathText.style, pathText); tmpStyleHash.put("t" + text.style, text);
// System.out.println("add style: " + pathText.style); // System.out.println("add style: " + text.style);
} }
else if (ELEMENT_NAME_STYLE_AREA.equals(localName)) { else if (ELEMENT_NAME_STYLE_AREA.equals(localName)) {
@ -194,7 +194,8 @@ public class RenderThemeHandler extends DefaultHandler {
Line line = Line.create((Line) ri, localName, attributes, 0, Line line = Line.create((Line) ri, localName, attributes, 0,
false); false);
tmpStyleHash.put("l" + line.style, line); tmpStyleHash.put("l" + line.style, line);
// System.out.println("add style: " + line.style + " from " + style); // System.out.println("add style: " + line.style +
// " from " + style);
} }
else { else {
Log.d("...", "this aint no style! " + style); Log.d("...", "this aint no style! " + style);
@ -222,8 +223,11 @@ public class RenderThemeHandler extends DefaultHandler {
else if ("caption".equals(localName)) { else if ("caption".equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION); checkState(localName, Element.RENDERING_INSTRUCTION);
Caption caption = Caption.create(localName, attributes); Text text = Text.create(localName, attributes, true);
mCurrentRule.addRenderingInstruction(caption); mCurrentRule.addRenderingInstruction(text);
// Caption caption = Caption.create(localName, attributes);
// mCurrentRule.addRenderingInstruction(caption);
} }
else if ("circle".equals(localName)) { else if ("circle".equals(localName)) {
@ -244,10 +248,10 @@ public class RenderThemeHandler extends DefaultHandler {
mCurrentRule.addRenderingInstruction(lineSymbol); mCurrentRule.addRenderingInstruction(lineSymbol);
} }
else if ("pathText".equals(localName)) { else if ("text".equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION); checkState(localName, Element.RENDERING_INSTRUCTION);
PathText pathText = PathText.create(localName, attributes); Text text = Text.create(localName, attributes, false);
mCurrentRule.addRenderingInstruction(pathText); mCurrentRule.addRenderingInstruction(text);
} }
else if ("symbol".equals(localName)) { else if ("symbol".equals(localName)) {
@ -262,7 +266,8 @@ public class RenderThemeHandler extends DefaultHandler {
if (style != null) { if (style != null) {
Line line = (Line) tmpStyleHash.get("l" + style); Line line = (Line) tmpStyleHash.get("l" + style);
if (line != null) { if (line != null) {
// System.out.println("found style line : " + line.style); // System.out.println("found style line : " +
// line.style);
Line newLine = Line.create(line, localName, attributes, Line newLine = Line.create(line, localName, attributes,
mLevel++, false); mLevel++, false);
@ -290,13 +295,13 @@ public class RenderThemeHandler extends DefaultHandler {
mCurrentRule.addRenderingInstruction(new AreaLevel(area, mCurrentRule.addRenderingInstruction(new AreaLevel(area,
mLevel++)); mLevel++));
else else
Log.d("...", "this aint no style inna di area! " + style); Log.d("...", "this aint no style inna d'area! " + style);
} }
} else if (ELEMENT_NAME_USE_STYLE_PATH_TEXT.equals(localName)) { } else if (ELEMENT_NAME_USE_STYLE_PATH_TEXT.equals(localName)) {
checkState(localName, Element.RENDERING_INSTRUCTION); checkState(localName, Element.RENDERING_INSTRUCTION);
String style = attributes.getValue("name"); String style = attributes.getValue("name");
if (style != null) { if (style != null) {
PathText pt = (PathText) tmpStyleHash.get("t" + style); Text pt = (Text) tmpStyleHash.get("t" + style);
if (pt != null) if (pt != null)
mCurrentRule.addRenderingInstruction(pt); mCurrentRule.addRenderingInstruction(pt);
else else

View File

@ -4,8 +4,8 @@
xsi:schemaLocation="http://mapsforge.org/renderTheme ../renderTheme.xsd" xsi:schemaLocation="http://mapsforge.org/renderTheme ../renderTheme.xsd"
version="1" map-background="#fcfcfa"> version="1" map-background="#fcfcfa">
<style-pathtext name="road" k="name" font-size="16" fill="#101010" stroke="#ffffff" stroke-width="2.0" /> <style-text name="road" k="name" font-size="16" fill="#101010" stroke="#ffffff" stroke-width="2.0" />
<style-pathtext name="major-road" k="name" font-style="bold" font-size="16" fill="#101010" stroke="#ffffff" stroke-width="2.0" /> <style-text name="major-road" k="name" font-style="bold" font-size="16" fill="#101010" stroke="#ffffff" stroke-width="2.0" />
<style-area name="residential" fill="#f0eee8" fade="10"/> <style-area name="residential" fill="#f0eee8" fade="10"/>
@ -65,14 +65,14 @@
<style-line name="steps" stroke="#f1f0f4" width="0.25" cap="butt" /> <style-line name="steps" stroke="#f1f0f4" width="0.25" cap="butt" />
<style-line name="water:outline" stroke="#a4bbcc" width="1.0" fixed="true" cap="butt" /> <style-line name="water:outline" stroke="#a4bbcc" width="1.0" fixed="true" cap="butt" />
<style-line name="water" stroke="#b4cbdc" width="1.0" cap="butt" /> <style-line name="water" stroke="#afcbf3" width="1.0" cap="butt" />
<style-area name="water" fill="#afcbf3" /> <style-area name="water" fill="#afcbf3" />
<!-- no-go area boundary --> <!-- no-go area boundary -->
<style-line name="fence" stroke="#444444" width="1.2" fixed="true" cap="butt"/> <style-line name="fence" stroke="#444444" width="1.2" fixed="true" cap="butt"/>
<style-line name="aeroway:runway" stroke="#c8ccbe" width="1.8" cap="butt" /> <style-line name="aeroway:runway" stroke="#c8ccbe" width="1.8" cap="butt" />
<style-line name="building" stroke="#c9c3c1" width="1.2" fixed="true" cap="butt" fade="15"/> <style-line name="building" stroke="#c9c3c1" width="1.0" fixed="true" cap="butt" fade="15"/>
<style-area name="building" fill="#e9e6e3" fade="15"/> <style-area name="building" fill="#e9e6e3" fade="15"/>
<!-- ways --> <!-- ways -->
@ -487,11 +487,11 @@
</rule> </rule>
<!-- outline 1 - 4 --> <!-- outline 1 - 4 -->
<style-outline name="1" stroke="#ee605020" width="0.02"/> <style-outline name="1" stroke="#aa807040" width="0.1"/>
<!-- <style-outline name="1" stroke="#404030"/> --> <!-- <style-outline name="1" stroke="#404030"/> -->
<style-outline name="2" stroke="#c0c0c0" /> <style-outline name="2" stroke="#c0c0c0" />
<style-outline name="primary" stroke="#7f7700" width="0.025"/> <style-outline name="primary" stroke="#aa7f7700" width="0.1"/>
<style-outline name="motorway" stroke="#805f2e" width="0.025"/> <style-outline name="motorway" stroke="#aa805f2e" width="0.1"/>
<!-- highway --> <!-- highway -->
@ -499,23 +499,23 @@
<rule e="way" k="*" v="*" zoom-min="5" zoom-max="10"> <rule e="way" k="*" v="*" zoom-min="5" zoom-max="10">
<rule e="way" k="area" v="~|no|false"> <rule e="way" k="area" v="~|no|false">
<rule e="way" k="*" v="secondary|primary_link" zoom-min="9"> <rule e="way" k="*" v="secondary|primary_link" zoom-min="9">
<line stroke="#f2df6d" width="1.8" cap="butt" fixed="true" fade="9"/> <line stroke="#f2df6d" width="1.3" cap="butt" fixed="true" fade="9"/>
</rule> </rule>
<rule e="way" k="*" v="trunk_link|motorway_link" zoom-min="8"> <rule e="way" k="*" v="trunk_link|motorway_link" zoom-min="8">
<line stroke="#fed6a3" width="1.8" cap="butt" fixed="true" fade="8"/> <line stroke="#fed6a3" width="1.4" cap="butt" fixed="true" fade="8"/>
</rule> </rule>
<rule e="way" k="*" v="primary" zoom-min="5"> <rule e="way" k="*" v="primary" zoom-min="5">
<line stroke="#f2df6d" width="1.8" cap="butt" fixed="true" fade="5"/> <line stroke="#f2df6d" width="1.4" cap="butt" fixed="true" fade="5"/>
</rule> </rule>
<rule e="way" k="*" v="trunk" zoom-min="5"> <rule e="way" k="*" v="trunk" zoom-min="5">
<line stroke="#fed6a3" width="2.0" cap="butt" fixed="true" fade="5"/> <line stroke="#fed6a3" width="1.4" cap="butt" fixed="true" fade="5"/>
</rule> </rule>
<rule e="way" k="*" v="motorway"> <rule e="way" k="*" v="motorway">
<line stroke="#eec693" width="2.2" cap="butt" fixed="true" fade="5"/> <line stroke="#eec693" width="1.5" cap="butt" fixed="true" fade="5"/>
</rule> </rule>
</rule> </rule>
</rule> </rule>
@ -790,7 +790,7 @@
src="jar:/org/mapsforge/android/maps/rendertheme/osmarender/symbols/cable_car.png" src="jar:/org/mapsforge/android/maps/rendertheme/osmarender/symbols/cable_car.png"
/> </rule> <rule e="way" k="aerialway" v="chair_lift"> <lineSymbol src="jar:/org/mapsforge/android/maps/rendertheme/osmarender/symbols/chair_lift_2.png" /> </rule> <rule e="way" k="aerialway" v="chair_lift"> <lineSymbol src="jar:/org/mapsforge/android/maps/rendertheme/osmarender/symbols/chair_lift_2.png"
/> </rule> <rule e="way" k="aerialway" v="gondola"> <lineSymbol src="jar:/org/mapsforge/android/maps/rendertheme/osmarender/symbols/gondola.png" /> </rule> <rule e="way" k="aerialway" v="gondola"> <lineSymbol src="jar:/org/mapsforge/android/maps/rendertheme/osmarender/symbols/gondola.png"
/> </rule> <rule e="way" k="*" v="*" zoom-min="14"> <pathText k="name" font-style="bold" /> </rule> <rule e="way" k="*" v="*" zoom-min="14"> <text k="name" font-style="bold"
font-size="10" fill="#606060" stroke="#ffffff" width="2.0" /> </rule> font-size="10" fill="#606060" stroke="#ffffff" width="2.0" /> </rule>
</rule> --> </rule> -->
@ -831,7 +831,7 @@
</rule> </rule>
<rule e="way" k="railway" v="rail|turntable" > <rule e="way" k="railway" v="rail|turntable" >
<line stroke="#887766" width="0.8" cap="butt" fixed="true" fade="12"/> <line stroke="#ccaa9988" width="1.0" cap="butt" fixed="true" fade="12"/>
</rule> </rule>
<!-- <rule e="way" k="railway" v="rail" zoom-max="14" zoom-min="13"> <!-- <rule e="way" k="railway" v="rail" zoom-max="14" zoom-min="13">
<line stroke="#8888aa" width="0.6" cap="butt" <line stroke="#8888aa" width="0.6" cap="butt"
@ -969,6 +969,165 @@
stroke="#ffffff" stroke-width="2.0" /> --> stroke="#ffffff" stroke-width="2.0" /> -->
</rule> </rule>
</rule> </rule>
<!-- aeroway -->
<rule e="node" k="aeroway" v="*">
<rule e="node" k="aeroway" v="helipad" zoom-min="17">
<symbol src="jar:symbols/helipad.png" />
</rule>
<rule e="node" k="aeroway" v="aerodrome|airport" zoom-max="13">
<symbol src="jar:symbols/airport.png" />
</rule>
</rule>
<!-- amenity -->
<rule e="node" k="amenity" v="*">
<rule e="node" k="amenity" v="atm" zoom-min="17">
<symbol src="jar:symbols/atm.png" />
</rule>
<rule e="node" k="amenity" v="bank" zoom-min="17">
<symbol src="jar:symbols/bank.png" />
</rule>
<rule e="node" k="amenity" v="bench" zoom-min="17">
<symbol src="jar:symbols/bench.png" />
</rule>
<rule e="node" k="amenity" v="bicycle_rental" zoom-min="17">
<symbol src="jar:symbols/bicycle_rental.png" />
</rule>
<rule e="node" k="amenity" v="bus_station" zoom-min="17">
<symbol src="jar:symbols/bus_sta.png" />
</rule>
<rule e="node" k="amenity" v="cafe" zoom-min="17">
<symbol src="jar:symbols/cafe.png" />
</rule>
<rule e="node" k="amenity" v="cinema" zoom-min="17">
<symbol src="jar:symbols/cinema.png" />
</rule>
<rule e="node" k="amenity" v="drinking_water" zoom-min="17">
<symbol src="jar:symbols/drinking_water.png" />
</rule>
<rule e="node" k="amenity" v="fast_food" zoom-min="17">
<symbol src="jar:symbols/fastfood.png" />
</rule>
<rule e="node" k="amenity" v="fire_station" zoom-min="17">
<symbol src="jar:symbols/firebrigade.png" />
</rule>
<rule e="node" k="amenity" v="fountain" zoom-min="17">
<symbol src="jar:symbols/fountain.png" />
</rule>
<rule e="node" k="amenity" v="fuel" zoom-min="17">
<symbol src="jar:symbols/petrolStation.png" />
</rule>
<rule e="node" k="amenity" v="hospital" zoom-min="15">
<symbol src="jar:symbols/hospital.png" />
</rule>
<!-- <rule e="node" k="amenity" v="kindergarten" zoom-min="17">
<symbol src="jar:symbols/kindergarten.png" />
</rule>-->
<rule e="node" k="amenity" v="library" zoom-min="17">
<symbol src="jar:symbols/library.png" />
</rule>
<rule e="node" k="amenity" v="parking" zoom-min="17">
<symbol src="jar:symbols/parking.png" />
</rule>
<rule e="node" k="amenity" v="pharmacy" zoom-min="17">
<symbol src="jar:symbols/pharmacy.png" />
</rule>
<rule e="node" k="amenity" v="place_of_worship" zoom-min="17">
<rule e="node" k="denomination|religion" v="jewish">
<symbol src="jar:symbols/synagogue.png" />
</rule>
<rule e="node" k="denomination|religion" v="muslim|moslem">
<symbol src="jar:symbols/mosque.png" />
</rule>
<rule e="node" k="denomination|religion" v="christian">
<symbol src="jar:symbols/church.png" />
</rule>
</rule>
<rule e="node" k="amenity" v="post_box" zoom-min="17">
<symbol src="jar:symbols/postbox.png" />
</rule>
<rule e="node" k="amenity" v="post_office" zoom-min="17">
<symbol src="jar:symbols/postoffice.png" />
</rule>
<rule e="node" k="amenity" v="pub|bar" zoom-min="17">
<symbol src="jar:symbols/pub.png" />
</rule>
<!-- <rule e="node" k="amenity" v="recycling" zoom-min="17">
<symbol src="jar:symbols/recycling.png" />
</rule> -->
<rule e="node" k="amenity" v="restaurant" zoom-min="17">
<symbol src="jar:symbols/restaurant.png" />
</rule>
<rule e="node" k="amenity" v="school" zoom-min="17">
<symbol src="jar:symbols/school.png" />
</rule>
<rule e="node" k="amenity" v="shelter" zoom-min="17">
<symbol src="jar:symbols/shelter.png" />
</rule>
<!-- <rule e="node" k="amenity" v="telephone" zoom-min="17">
<symbol src="jar:symbols/telephone.png" />
</rule> -->
<rule e="node" k="amenity" v="theatre" zoom-min="17">
<symbol src="jar:symbols/theatre.png" />
</rule>
<rule e="node" k="amenity" v="toilets" zoom-min="17">
<symbol src="jar:symbols/toilets.png" />
</rule>
<rule e="node" k="amenity" v="university|college" zoom-min="17">
<symbol src="jar:symbols/university.png" />
</rule>
<!-- <rule e="node" k="*" v="*" zoom-min="17">
<caption k="name" font-style="bold" font-size="10" fill="#4040ff" stroke="#ffffff" stroke-width="2.0" />
</rule>-->
</rule>
<!-- shop -->
<rule e="node" k="shop" v="*">
<rule e="node" k="shop" v="bakery" zoom-min="17">
<symbol src="jar:symbols/bakery.png" />
</rule>
<rule e="node" k="shop" v="florist" zoom-min="17">
<symbol src="jar:symbols/florist.png" />
</rule>
<rule e="node" k="shop" v="supermarket|organic" zoom-min="17">
<symbol src="jar:symbols/supermarket.png" />
</rule>
<rule e="node" k="*" v="*" zoom-min="17">
<caption k="name" font-style="bold" font-size="10" fill="#4040ff" stroke="#ffffff" stroke-width="2.0" />
</rule>
</rule>
<!-- tourism -->
<rule e="node" k="tourism" v="*">
<rule e="node" k="tourism" v="alpine_hut" zoom-min="16">
<symbol src="jar:symbols/alpine_hut.png" />
</rule>
<rule e="node" k="tourism" v="camp_site" zoom-min="17">
<symbol src="jar:symbols/campSite.png" />
</rule>
<rule e="node" k="tourism" v="hostel" zoom-min="17">
<symbol src="jar:symbols/hostel.png" />
</rule>
<rule e="node" k="tourism" v="hotel" zoom-min="17">
<symbol src="jar:symbols/hotel.png" />
</rule>
<rule e="node" k="tourism" v="information" zoom-min="17">
<symbol src="jar:symbols/information.png" />
</rule>
<rule e="node" k="tourism" v="viewpoint" zoom-min="15">
<symbol src="jar:symbols/viewpoint.png" />
</rule>
<rule e="node" k="*" v="*" zoom-min="17">
<caption k="name" font-style="bold" font-size="10" fill="#4040ff" stroke="#ffffff" stroke-width="2.0" />
</rule>
</rule>
</rule> </rule>
</rendertheme> </rendertheme>

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Some files were not shown because too many files have changed in this diff Show More