more work on overlay renderer:

- moved text rendering to overlay
- added grid overlay
This commit is contained in:
Hannes Janetzek 2012-10-13 04:57:27 +02:00
parent 33d8865d7b
commit 4a06553ddc
33 changed files with 2050 additions and 1089 deletions

View File

@ -38,11 +38,10 @@ public class JobQueue {
* the job to be added to this queue. * the job to be added to this queue.
*/ */
public synchronized void setJobs(ArrayList<JobTile> tiles) { public synchronized void setJobs(ArrayList<JobTile> tiles) {
mPriorityQueue.clear(); // mPriorityQueue.clear();
// mPriorityQueue.addAll(tiles);
for (int i = 0, n = tiles.size(); i < n; i++) { for (int i = 0, n = tiles.size(); i < n; i++) {
JobTile tile = tiles.get(i); JobTile tile = tiles.get(i);
// tile.state = LOADING;
tile.isLoading = true; tile.isLoading = true;
mPriorityQueue.offer(tile); mPriorityQueue.offer(tile);
} }
@ -52,11 +51,10 @@ public class JobQueue {
* Removes all jobs from this queue. * Removes all jobs from this queue.
*/ */
public synchronized void clear() { public synchronized void clear() {
for (int i = 0, n = mPriorityQueue.size(); i < n; i++) { for (JobTile tile : mPriorityQueue) {
JobTile tile = mPriorityQueue.poll();
// tile.state = 0;
tile.isLoading = false; tile.isLoading = false;
} }
mPriorityQueue.clear(); mPriorityQueue.clear();
} }
@ -71,8 +69,6 @@ public class JobQueue {
* @return the most important job from this queue or null, if empty. * @return the most important job from this queue or null, if empty.
*/ */
public synchronized JobTile poll() { public synchronized JobTile poll() {
JobTile tile = mPriorityQueue.poll(); return mPriorityQueue.poll();
return tile;
} }
} }

View File

@ -61,9 +61,11 @@ public class MapWorker extends PausableThread {
protected void doWork() { protected void doWork() {
JobTile tile = mJobQueue.poll(); JobTile tile = mJobQueue.poll();
if (mMapGenerator == null || tile == null) if (tile == null)
return; return;
// Log.d("...", "load: " + tile);
boolean success = mMapGenerator.executeJob(tile); boolean success = mMapGenerator.executeJob(tile);
if (!isInterrupted() && success) { if (!isInterrupted() && success) {

View File

@ -19,15 +19,15 @@ import android.opengl.GLES20;
class BufferObject { class BufferObject {
private static BufferObject pool; private static BufferObject pool;
static int counter;
static synchronized BufferObject get() { static synchronized BufferObject get() {
BufferObject bo;
if (pool == null) { if (pool == null)
return null; return null;
} counter--;
bo = pool; BufferObject bo = pool;
pool = pool.next; pool = pool.next;
bo.next = null; bo.next = null;
return bo; return bo;
@ -64,6 +64,7 @@ class BufferObject {
static synchronized void release(BufferObject bo) { static synchronized void release(BufferObject bo) {
bo.next = pool; bo.next = pool;
pool = bo; pool = bo;
counter++;
} }
// Note: only call from GL-Thread // Note: only call from GL-Thread
@ -106,6 +107,7 @@ class BufferObject {
bo.next = pool; bo.next = pool;
pool = bo; pool = bo;
} }
counter = num;
} }
int id; int id;

View File

@ -37,6 +37,7 @@ import org.oscim.renderer.MapRenderer.TilesData;
import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers; 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.MapView; import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition; import org.oscim.view.MapViewPosition;
@ -44,7 +45,6 @@ import android.opengl.GLES20;
import android.opengl.GLSurfaceView; import android.opengl.GLSurfaceView;
import android.opengl.Matrix; import android.opengl.Matrix;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.FloatMath;
import android.util.Log; import android.util.Log;
public class GLRenderer implements GLSurfaceView.Renderer { public class GLRenderer implements GLSurfaceView.Renderer {
@ -61,14 +61,11 @@ public class GLRenderer implements GLSurfaceView.Renderer {
static int CACHE_TILES = CACHE_TILES_MAX; static int CACHE_TILES = CACHE_TILES_MAX;
private final MapView mMapView; private final MapView mMapView;
private final MapViewPosition mMapViewPosition;
private static MapPosition mMapPosition;
// private static ArrayList<BufferObject> mVBOs;
static int mWidth, mHeight; static int mWidth, mHeight;
private static MapViewPosition mMapViewPosition;
private static MapPosition mMapPosition;
private static int rotateBuffers = 2; private static int rotateBuffers = 2;
private static ShortBuffer shortBuffer[]; private static ShortBuffer shortBuffer[];
private static short[] mFillCoords; private static short[] mFillCoords;
@ -82,16 +79,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static float[] mTileCoords = new float[8]; private static float[] mTileCoords = new float[8];
private static float[] mDebugCoords = new float[8]; private static float[] mDebugCoords = new float[8];
// mNextTiles is set by TileLoader and swapped with private static float[] mClearColor = null;
// mDrawTiles in onDrawFrame in GL thread.
private static TilesData mNextTiles;
/* package */static TilesData mDrawTiles;
// flag set by updateVisibleList when current visible tiles
// changed. used in onDrawFrame to flip mNextTiles/mDrawTiles
private static boolean mUpdateTiles;
private float[] mClearColor = null;
// number of tiles drawn in one frame // number of tiles drawn in one frame
private static short mDrawCount = 0; private static short mDrawCount = 0;
@ -99,7 +87,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static boolean mUpdateColor = false; private static boolean mUpdateColor = false;
// drawlock to synchronize Main- and GL-Thread // drawlock to synchronize Main- and GL-Thread
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 // Add additional tiles that serve as placeholer when flipping
@ -108,7 +96,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// the other option would be to run scanbox each time for upload, // the other option would be to run scanbox each time for upload,
// drawing, proxies and text layer. needing to add placeholder only // drawing, proxies and text layer. needing to add placeholder only
// happens rarely, unless you live on Fidschi // happens rarely, unless you live on Fidschi
/* package */static int mHolderCount; /* package */static int mHolderCount;
/* package */static TilesData mDrawTiles;
static boolean[] vertexArray = { false, false }; static boolean[] vertexArray = { false, false };
@ -222,58 +212,18 @@ public class GLRenderer implements GLSurfaceView.Renderer {
shortBuffer[i].put(mFillCoords, 0, 8); shortBuffer[i].put(mFillCoords, 0, 8);
} }
mUpdateTiles = false; mOverlays = new ArrayList<Overlay>();
// mOverlays.add(new OverlayGrid(mapView));
// mOverlays.add(new OverlayTest(mapView));
mOverlays.add(new OverlayText(mapView));
} }
private static ArrayList<Overlay> mOverlays; private static ArrayList<Overlay> mOverlays;
/** public static void setRenderTheme(RenderTheme t) {
* Called by TileLoader when list of active tiles changed. the list is mClearColor = GlUtils.colorToFloat(t.getMapBackground());
* copied to mNextTiles to be used in next call to onDrawFrame
*
* @param tiles
* active tiles
* @param overlays
* ...
*/
static void updateTiles(TilesData tiles, ArrayList<Overlay> overlays) {
MapTile[] newTiles = tiles.tiles;
// lock tiles (and their proxies) to not be removed from cache
for (int i = 0, n = tiles.cnt; i < n; i++)
newTiles[i].lock();
mOverlays = overlays;
// dont flip next/drawTiles while copying
GLRenderer.tilelock.lock();
MapTile[] nextTiles = mNextTiles.tiles;
// unlock previously active tiles
for (int i = 0, n = mNextTiles.cnt; i < n; i++)
nextTiles[i].unlock();
// copy newTiles to nextTiles
System.arraycopy(newTiles, 0, nextTiles, 0, tiles.cnt);
mNextTiles.cnt = tiles.cnt;
// flip next/drawTiles in next onDrawFrame
mUpdateTiles = true;
GLRenderer.tilelock.unlock();
}
void setRenderTheme(RenderTheme t) {
int bg = t.getMapBackground();
float[] c = new float[4];
c[3] = (bg >> 24 & 0xff) / 255.0f;
c[0] = (bg >> 16 & 0xff) / 255.0f;
c[1] = (bg >> 8 & 0xff) / 255.0f;
c[2] = (bg >> 0 & 0xff) / 255.0f;
mClearColor = c;
mUpdateColor = true; mUpdateColor = true;
} }
@ -282,19 +232,20 @@ public class GLRenderer implements GLSurfaceView.Renderer {
private static boolean uploadLayers(Layers layers, BufferObject vbo, boolean addFill) { private static boolean uploadLayers(Layers layers, BufferObject vbo, boolean addFill) {
int newSize = layers.getSize(); int newSize = layers.getSize();
if (newSize == 0) if (newSize == 0) {
return false; Log.d(TAG, "empty");
return true;
}
GLES20.glBindBuffer(GL_ARRAY_BUFFER, 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) { int curBuffer = uploadCnt % rotateBuffers;
uploadCnt = 0; uploadCnt++;
}
ShortBuffer sbuf = shortBuffer[uploadCnt]; ShortBuffer sbuf = shortBuffer[curBuffer];
// add fill coordinates // add fill coordinates
if (addFill) if (addFill)
@ -307,7 +258,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
.order(ByteOrder.nativeOrder()) .order(ByteOrder.nativeOrder())
.asShortBuffer(); .asShortBuffer();
shortBuffer[uploadCnt] = sbuf; shortBuffer[curBuffer] = sbuf;
if (addFill) if (addFill)
sbuf.put(mFillCoords, 0, 8); sbuf.put(mFillCoords, 0, 8);
} else { } else {
@ -345,18 +296,21 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mBufferMemoryUsage += vbo.size; mBufferMemoryUsage += vbo.size;
} }
uploadCnt++;
return true; return true;
} }
private static boolean uploadTileData(MapTile tile) { private static boolean uploadTileData(MapTile tile) {
// synchronized (tile) {
if (uploadLayers(tile.layers, tile.vbo, true)) tile.isReady = uploadLayers(tile.layers, tile.vbo, true);
tile.isReady = true; if (!tile.isReady) {
tile.layers.clear();
tile.layers = null;
}
tile.newData = false; tile.newData = false;
// Log.d(TAG, "uploaded " + tile.isReady + " " + tile);
// }
return tile.isReady; return tile.isReady;
} }
@ -427,11 +381,19 @@ public class GLRenderer implements GLSurfaceView.Renderer {
@Override @Override
public void onDrawFrame(GL10 glUnused) { public void onDrawFrame(GL10 glUnused) {
long start = 0;
// prevent main thread recreating all tiles (updateMap) // prevent main thread recreating all tiles (updateMap)
// while rendering is going. not have seen this happen // while rendering is going.
// yet though drawlock.lock();
GLRenderer.drawlock.lock(); try {
draw();
} finally {
drawlock.unlock();
}
}
static void draw() {
long start = 0;
if (MapView.debugFrameTime) if (MapView.debugFrameTime)
start = SystemClock.uptimeMillis(); start = SystemClock.uptimeMillis();
@ -451,24 +413,22 @@ 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);
if (mUpdateTiles) { int serial = 0;
// get current tiles to draw if (mDrawTiles != null)
GLRenderer.tilelock.lock(); serial = mDrawTiles.serial;
mUpdateTiles = false;
TilesData tmp = mDrawTiles; // get current tiles to draw
mDrawTiles = mNextTiles; mDrawTiles = MapRenderer.getActiveTiles(mDrawTiles);
mNextTiles = tmp;
GLRenderer.tilelock.unlock();
// force update of mapPosition
mMapPosition.zoomLevel = -1;
}
if (mDrawTiles == null || mDrawTiles.cnt == 0) { if (mDrawTiles == null || mDrawTiles.cnt == 0) {
GLRenderer.drawlock.unlock();
return; return;
} }
boolean tilesChanged = false;
// check if the tiles have changed...
if (serial != mDrawTiles.serial) {
mMapPosition.zoomLevel = -1;
tilesChanged = true;
}
// get current MapPosition, set mTileCoords (mapping of screen to model // get current MapPosition, set mTileCoords (mapping of screen to model
// coordinates) // coordinates)
@ -476,10 +436,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
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;
@ -492,6 +448,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// zoom-level changed. // zoom-level changed.
float div = scaleDiv(tiles[0]); float div = scaleDiv(tiles[0]);
// transform screen coordinates to tile coordinates
float s = Tile.TILE_SIZE; float s = Tile.TILE_SIZE;
float scale = mapPosition.scale / div; float scale = mapPosition.scale / div;
float px = (float) mapPosition.x * div; float px = (float) mapPosition.x * div;
@ -511,9 +468,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
// Log.d(TAG, "visible: " + tileCnt); // Log.d(TAG, "visible: " + tileCnt);
uploadCnt = 0; uploadCnt = 0;
int updateTextures = 0; // int updateTextures = 0;
// check visible tiles, upload new vertex data // compile data and upload to VBOsi
for (int i = 0; i < tileCnt; i++) { for (int i = 0; i < tileCnt; i++) {
MapTile tile = tiles[i]; MapTile tile = tiles[i];
@ -522,10 +479,10 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (!tile.isVisible) if (!tile.isVisible)
continue; continue;
if (MapView.staticLabeling) { // if (MapView.staticLabeling) {
if (tile.texture == null && TextRenderer.drawToTexture(tile)) // if (tile.texture == null && TextRenderer.drawToTexture(tile))
updateTextures++; // updateTextures++;
} // }
if (tile.newData) { if (tile.newData) {
uploadTileData(tile); uploadTileData(tile);
@ -562,10 +519,18 @@ public class GLRenderer implements GLSurfaceView.Renderer {
if (uploadCnt > 0) if (uploadCnt > 0)
checkBufferUsage(); checkBufferUsage();
if (MapView.staticLabeling) { tilesChanged |= (uploadCnt > 0);
if (updateTextures > 0)
TextRenderer.compileTextures(); // if (changed || tilesChanged) {
for (Overlay overlay : mOverlays) {
overlay.update(changed, tilesChanged);
} }
// }
// if (MapView.staticLabeling) {
// if (updateTextures > 0)
// TextRenderer.compileTextures();
// }
GLES20.glEnable(GL_DEPTH_TEST); GLES20.glEnable(GL_DEPTH_TEST);
GLES20.glEnable(GL_POLYGON_OFFSET_FILL); GLES20.glEnable(GL_POLYGON_OFFSET_FILL);
@ -593,41 +558,41 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mDrawCount = 0; mDrawCount = 0;
mDrawSerial++; mDrawSerial++;
if (MapView.staticLabeling) { GLES20.glEnable(GL_BLEND);
GLES20.glEnable(GL_BLEND);
int z = mapPosition.zoomLevel;
float s = mapPosition.scale;
int zoomLevelDiff = Math.max(z - TileGenerator.STROKE_MAX_ZOOM_LEVEL, // if (MapView.staticLabeling) {
0); // int z = mapPosition.zoomLevel;
float scale = (float) Math.pow(1.4, zoomLevelDiff); // float s = mapPosition.scale;
if (scale < 1) // int zoomLevelDiff = Math.max(z - TileGenerator.STROKE_MAX_ZOOM_LEVEL,
scale = 1; // 0);
// float scale = (float) Math.pow(1.4, zoomLevelDiff);
if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL) // if (scale < 1)
TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix); // scale = 1;
else //
TextRenderer.beginDraw(1f / s, mProjMatrix); // if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL)
// TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix);
for (int i = 0; i < tileCnt; i++) { // else
MapTile t = tiles[i]; // TextRenderer.beginDraw(1f / s, mProjMatrix);
if (!t.isVisible) //
continue; // for (int i = 0; i < tileCnt; i++) {
// MapTile t = tiles[i];
if (t.holder == null) { // if (!t.isVisible)
if (t.texture != null) { // continue;
setMatrix(mMVPMatrix, t, 1, false); //
TextRenderer.drawTile(t, mMVPMatrix); // if (t.holder == null) {
} // if (t.texture != null) {
} else { // setMatrix(mMVPMatrix, t, 1, false);
if (t.holder.texture != null) { // TextRenderer.drawTile(t, mMVPMatrix);
setMatrix(mMVPMatrix, t, 1, false); // }
TextRenderer.drawTile(t.holder, mMVPMatrix); // } else {
} // if (t.holder.texture != null) {
} // setMatrix(mMVPMatrix, t, 1, false);
} // TextRenderer.drawTile(t.holder, mMVPMatrix);
TextRenderer.endDraw(); // }
} // }
// }
// TextRenderer.endDraw();
// }
// call overlay renderer // call overlay renderer
for (Overlay overlay : mOverlays) { for (Overlay overlay : mOverlays) {
@ -675,8 +640,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mapPosition.viewMatrix, 0); mapPosition.viewMatrix, 0);
PolygonRenderer.debugDraw(mMVPMatrix, mDebugCoords, 1); PolygonRenderer.debugDraw(mMVPMatrix, mDebugCoords, 1);
} }
GLRenderer.drawlock.unlock();
} }
// used to not draw a tile twice per frame. // used to not draw a tile twice per frame.
@ -728,12 +691,12 @@ public class GLRenderer implements GLSurfaceView.Renderer {
} }
} }
if (tile.layers.symbolLayers != null) { if (tile.layers.textureLayers != null) {
setMatrix(mvp, tile, div, false); setMatrix(mvp, tile, div, false);
for (Layer l = tile.layers.symbolLayers; l != null;) { for (Layer l = tile.layers.textureLayers; l != null;) {
l = TextureRenderer.draw(l, 1, mProjMatrix, mvp, l = TextureRenderer.draw(l, 1, mProjMatrix, mvp,
tile.layers.symbolOffset); tile.layers.texOffset);
} }
} }
} }
@ -798,6 +761,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
public void onSurfaceChanged(GL10 glUnused, int width, int height) { public void onSurfaceChanged(GL10 glUnused, int width, int height) {
Log.d(TAG, "SurfaceChanged:" + width + " " + height); Log.d(TAG, "SurfaceChanged:" + width + " " + height);
mDrawTiles = null;
if (width <= 0 || height <= 0) if (width <= 0 || height <= 0)
return; return;
@ -849,7 +814,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
BufferObject.init(numVBO); BufferObject.init(numVBO);
// Set up textures // Set up textures
TextRenderer.setup(numTiles); // TextRenderer.setup(numTiles);
if (mClearColor != null) if (mClearColor != null)
mUpdateColor = true; mUpdateColor = true;
@ -861,13 +826,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
mMapView.redrawMap(); mMapView.redrawMap();
} }
// FIXME this is a bit too spaghetti
void clearTiles(int numTiles) {
mDrawTiles = 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);
@ -875,7 +833,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
LineRenderer.init(); LineRenderer.init();
PolygonRenderer.init(); PolygonRenderer.init();
TextRenderer.init(); // TextRenderer.init();
TextureRenderer.init(); TextureRenderer.init();
TextureObject.init(10); TextureObject.init(10);
@ -884,7 +842,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
GLES20.glClearStencil(0); GLES20.glClearStencil(0);
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; mNewSurface = true;
} }

View File

@ -16,12 +16,13 @@ package org.oscim.renderer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.locks.ReentrantLock;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.generator.JobTile; import org.oscim.generator.JobTile;
import org.oscim.renderer.layer.TextItem;
import org.oscim.renderer.layer.VertexPool; import org.oscim.renderer.layer.VertexPool;
import org.oscim.theme.RenderTheme;
import org.oscim.utils.GlConfigChooser; import org.oscim.utils.GlConfigChooser;
import org.oscim.view.MapView; import org.oscim.view.MapView;
import org.oscim.view.MapViewPosition; import org.oscim.view.MapViewPosition;
@ -75,22 +76,31 @@ public class MapRenderer extends GLSurfaceView {
// GLThread // GLThread
static final class TilesData { static final class TilesData {
int cnt = 0; int cnt = 0;
final MapTile[] tiles; int serial;
MapTile[] tiles;
TilesData() {
}
TilesData(int numTiles) { TilesData(int numTiles) {
tiles = new MapTile[numTiles]; tiles = new MapTile[numTiles];
} }
} }
/* package */static TilesData mCurrentTiles; static int mUpdateCnt;
static ReentrantLock tilelock = new ReentrantLock();
static TilesData mCurrentTiles;
/* package */static TilesData mNewTiles;
static int tileCounter;
private static ScanBox mScanBox = new ScanBox() { private static ScanBox mScanBox = new ScanBox() {
@Override @Override
void setVisible(int y, int x1, int x2) { void setVisible(int y, int x1, int x2) {
MapTile[] tiles = mCurrentTiles.tiles; MapTile[] tiles = mNewTiles.tiles;
int cnt = mCurrentTiles.cnt; int cnt = mNewTiles.cnt;
int max = mCurrentTiles.tiles.length; int max = tiles.length;
int xmax = 1 << mZoom; int xmax = 1 << mZoom;
for (int x = x1; x < x2; x++) { for (int x = x1; x < x2; x++) {
@ -101,7 +111,7 @@ public class MapRenderer extends GLSurfaceView {
break; break;
} }
// NOTE to myself: do not modify x, argh !!! // NOTE to myself: do not modify x!
int xx = x; int xx = x;
if (x < 0 || x >= xmax) { if (x < 0 || x >= xmax) {
@ -127,13 +137,15 @@ public class MapRenderer extends GLSurfaceView {
tiles[cnt++] = tile; tiles[cnt++] = tile;
} }
} }
mCurrentTiles.cnt = cnt; mNewTiles.cnt = cnt;
} }
}; };
// why not try a pattern every now and then? // why not try a pattern every now and then?
// but should do the same for GLRenderer // but should do the same for GLRenderer.
private static MapRenderer SINGLETON; // findbugs found that volatile thingy, though this class
// is created before any other thread starts
private static volatile MapRenderer SINGLETON;
public static MapRenderer create(Context context, MapView mapView) { public static MapRenderer create(Context context, MapView mapView) {
if (SINGLETON != null) if (SINGLETON != null)
@ -177,7 +189,10 @@ public class MapRenderer extends GLSurfaceView {
VertexPool.init(); VertexPool.init();
QuadTree.init(); QuadTree.init();
mUpdateCnt = 0;
mInitial = true; mInitial = true;
tileCounter = 0;
} }
/** /**
@ -188,7 +203,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(final boolean clear) { public synchronized void updateMap(final boolean clear) {
boolean changedPos = false; boolean changedPos = false;
if (mMapView == null) if (mMapView == null)
@ -214,7 +229,7 @@ public class MapRenderer extends GLSurfaceView {
// TODO clear overlay items data // TODO clear overlay items data
mOverlays.clear(); mOverlays.clear();
mOverlays.add(new Overlay()); // 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;
@ -225,9 +240,9 @@ public class MapRenderer extends GLSurfaceView {
int numTiles = (num * num) / (size * size) * 4; int numTiles = (num * num) / (size * size) * 4;
mRenderer.clearTiles(numTiles); // mRenderer.clearTiles(numTiles);
mNewTiles = new TilesData(numTiles);
mCurrentTiles = new TilesData(numTiles); mCurrentTiles = new TilesData(numTiles);
// MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo(); // MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo();
// if (mapInfo != null) // if (mapInfo != null)
// mZoomLevels = mapInfo.zoomLevel; // mZoomLevels = mapInfo.zoomLevel;
@ -243,8 +258,10 @@ public class MapRenderer extends GLSurfaceView {
float[] coords = mTileCoords; float[] coords = mTileCoords;
changedPos = mMapViewPosition.getMapPosition(mapPosition, coords); changedPos = mMapViewPosition.getMapPosition(mapPosition, coords);
if (!changedPos) if (!changedPos) {
requestRender();
return; 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
@ -259,16 +276,6 @@ public class MapRenderer extends GLSurfaceView {
coords[i + 1] = (py + coords[i + 1] / scale) / s; coords[i + 1] = (py + coords[i + 1] / scale) / s;
} }
// this does not work reloably with tilt and rotation
// changedPos = false;
// for (int i = 0; i < 8; i++)
// if (mBoundaryTiles[i] != (int) coords[i]) {
// changedPos = true;
// break;
// }
// 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.
@ -279,6 +286,7 @@ public class MapRenderer extends GLSurfaceView {
if (changed) { 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);
@ -286,9 +294,51 @@ public class MapRenderer extends GLSurfaceView {
} }
} }
public static TilesData getActiveTiles(TilesData td) {
if (td != null && td.serial == mUpdateCnt)
return td;
// dont flip new/currentTiles while copying
MapRenderer.tilelock.lock();
try {
MapTile[] newTiles = mCurrentTiles.tiles;
int cnt = mCurrentTiles.cnt;
// lock tiles (and their proxies) to not be removed from cache
for (int i = 0; i < cnt; i++)
newTiles[i].lock();
MapTile[] nextTiles;
if (td == null)
td = new TilesData(newTiles.length);
nextTiles = td.tiles;
// unlock previously active tiles
for (int i = 0, n = td.cnt; i < n; i++)
nextTiles[i].unlock();
// copy newTiles to nextTiles
System.arraycopy(newTiles, 0, nextTiles, 0, cnt);
td.serial = mUpdateCnt;
td.cnt = cnt;
} finally {
MapRenderer.tilelock.unlock();
}
return td;
}
// public void releaseTiles(TilesData tiles) {
//
// }
/** /**
* set mCurrentTiles for the visible tiles and pass it to GLRenderer, add * set mNewTiles for the visible tiles and pass it to GLRenderer, add jobs
* jobs for not yet loaded tiles * for not yet loaded tiles
* *
* @param mapPosition * @param mapPosition
* the current MapPosition * the current MapPosition
@ -298,44 +348,97 @@ public class MapRenderer extends GLSurfaceView {
*/ */
private static boolean updateVisibleList(MapPosition mapPosition, int zdir) { private static boolean updateVisibleList(MapPosition mapPosition, int zdir) {
// TODO keep mJobList and JobQueue in sync, no need to clear
mJobList.clear(); mJobList.clear();
// set non processed tiles to isLoading == false
mMapView.addJobs(null);
mCurrentTiles.cnt = 0;
mScanBox.scan(mTileCoords, mapPosition.zoomLevel);
// Log.d(TAG, "visible: " + mCurrentTiles.cnt + "/" +
// mCurrentTiles.tiles.length);
GLRenderer.updateTiles(mCurrentTiles, mOverlays);
// note: this sets isLoading == true for all job tiles // sets non processed tiles to isLoading = false
// and clear job queue
mMapView.addJobs(null);
mNewTiles.cnt = 0;
mScanBox.scan(mTileCoords, mapPosition.zoomLevel);
MapTile[] newTiles = mNewTiles.tiles;
MapTile[] curTiles = mCurrentTiles.tiles;
boolean changed = false;
for (int i = 0, n = mNewTiles.cnt; i < n && !changed; i++) {
MapTile t = newTiles[i];
boolean found = false;
for (int j = 0, m = mCurrentTiles.cnt; j < m; j++) {
if (t == curTiles[j]) {
found = true;
break;
}
}
if (!found)
changed = true;
}
if (changed) {
MapRenderer.tilelock.lock();
try {
for (int i = 0, n = mNewTiles.cnt; i < n; i++)
newTiles[i].lock();
for (int i = 0, n = mCurrentTiles.cnt; i < n; i++)
curTiles[i].unlock();
TilesData tmp = mCurrentTiles;
mCurrentTiles = mNewTiles;
mNewTiles = tmp;
mUpdateCnt++;
} finally {
MapRenderer.tilelock.unlock();
}
// Log.d(TAG, "tiles: " + tileCounter + " " + BufferObject.counter
// + " sum:" + (tileCounter + BufferObject.counter));
}
if (mJobList.size() > 0) { if (mJobList.size() > 0) {
updateTileDistances(mJobList, mapPosition); updateTileDistances(mJobList, mapPosition);
Collections.sort(mJobList); Collections.sort(mJobList);
// sets tiles to isLoading = true
mMapView.addJobs(mJobList); mMapView.addJobs(mJobList);
return true; return true;
} }
return false; return false;
} }
/* package */ /**
static MapTile addTile(int x, int y, byte zoomLevel, int zdir) { * @param x
* ...
* @param y
* ...
* @param zoomLevel
* ...
* @param zdir
* ...
* @return ...
*/
/* package */static MapTile addTile(int x, int y, byte zoomLevel, int zdir) {
MapTile tile; MapTile tile;
tile = QuadTree.getTile(x, y, zoomLevel); tile = QuadTree.getTile(x, y, zoomLevel);
if (tile == null) { if (tile == null) {
tile = new MapTile(x, y, zoomLevel); tile = new MapTile(x, y, zoomLevel);
QuadTree.add(tile); QuadTree.add(tile);
mTiles.add(tile);
}
// if (!fetchProxy && !tile.isActive()) { mTiles.add(tile);
if (!tile.isActive()) { mJobList.add(tile);
tileCounter++;
} else if (!tile.isActive()) {
mJobList.add(tile); mJobList.add(tile);
} }
// mNewTiles.tiles[tiles++] = tile;
// mCurrentTiles.tiles[tiles++] = tile;
// if (fetchChildren) { // if (fetchChildren) {
// byte z = (byte) (zoomLevel + 1); // byte z = (byte) (zoomLevel + 1);
@ -359,22 +462,23 @@ public class MapRenderer extends GLSurfaceView {
// } // }
// if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) { // if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) {
if (zdir > 0 && zoomLevel > 0) {
// prefetch parent
MapTile p = tile.rel.parent.tile;
if (p == null) { // if (zdir > 0 && zoomLevel > 0) {
p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1)); // // prefetch parent
// MapTile p = tile.rel.parent.tile;
QuadTree.add(p); //
mTiles.add(p); // if (p == null) {
mJobList.add(p); // p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1));
//
} else if (!p.isActive()) { // QuadTree.add(p);
if (!mJobList.contains(p)) // mTiles.add(p);
mJobList.add(p); // mJobList.add(p);
} //
} // } else if (!p.isActive()) {
// if (!mJobList.contains(p))
// mJobList.add(p);
// }
// }
return tile; return tile;
} }
@ -389,15 +493,16 @@ public class MapRenderer extends GLSurfaceView {
t.layers = null; t.layers = null;
} }
t.labels = null; TextItem.release(t.labels);
if (t.vbo != null) { if (t.vbo != null) {
BufferObject.release(t.vbo); BufferObject.release(t.vbo);
t.vbo = null; t.vbo = null;
} }
if (t.texture != null) // if (t.texture != null)
t.texture.tile = null; // t.texture.tile = null;
tileCounter--;
QuadTree.remove(t); QuadTree.remove(t);
} }
@ -479,15 +584,13 @@ public class MapRenderer extends GLSurfaceView {
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 GLRenderer // dont remove tile used by GLRenderer
Log.d(TAG, "X not removing " + t + " " + t.distance); Log.d(TAG, "X not removing " + t + " " + t.distance);
mTiles.add(t); mTiles.add(t);
continue; } else if (t.isLoading) {
}
if (t.isLoading) {
// NOTE: if we add tile back and set loading=false, on next // NOTE: if we add tile back and set loading=false, on next
// limitCache the tile will be removed. clearTile could // limitCache the tile will be removed. clearTile could
// interfere with TileGenerator. so clear in passTile() // interfere with TileGenerator. so clear in passTile()
@ -498,10 +601,10 @@ public class MapRenderer extends GLSurfaceView {
// t.isLoading = false; // t.isLoading = false;
mTiles.add(t); mTiles.add(t);
Log.d(TAG, "X cancel loading " + t + " " + t.distance); Log.d(TAG, "X cancel loading " + t + " " + t.distance);
continue; } else {
clearTile(t);
} }
// }
clearTile(t);
} }
} }
@ -516,8 +619,8 @@ public class MapRenderer extends GLSurfaceView {
// remove tiles already 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);
// t.rel == null means tile was removed in limitCache
if (!t.newData) { if (!t.newData || t.rel == null) {
mTilesLoaded.remove(i); mTilesLoaded.remove(i);
size--; size--;
continue; continue;
@ -532,7 +635,7 @@ public class MapRenderer extends GLSurfaceView {
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.isLocked()) { if (t.isLocked()) {
// Log.d(TAG, "keep unused tile data: " + t + " " + // Log.d(TAG, "keep unused tile data: " + t + " " +
// t.isActive); // t.isActive);
@ -545,6 +648,7 @@ public class MapRenderer extends GLSurfaceView {
mTiles.remove(t); mTiles.remove(t);
clearTile(t); clearTile(t);
} }
// }
} }
} }
@ -558,42 +662,47 @@ public class MapRenderer extends GLSurfaceView {
public synchronized boolean passTile(JobTile jobTile) { public synchronized boolean passTile(JobTile jobTile) {
MapTile tile = (MapTile) jobTile; MapTile tile = (MapTile) jobTile;
if (!tile.isLoading) { // XXX this is concurrent with updateVisiblelist which set loading false
// no one should be able to use this tile now, TileGenerator passed // temporarily
// it, GL-Thread does nothing until newdata is set. // if (!tile.isLoading) {
Log.d(TAG, "passTile: canceled " + tile); // // no one should be able to use this tile now, TileGenerator passed
synchronized (mTilesLoaded) { // // it, GL-Thread does nothing until newdata is set.
mTilesLoaded.add(tile); // Log.d(TAG, "passTile: canceled " + tile);
} // return true;
return true; // }
}
tile.vbo = BufferObject.get();
// synchronized (tile) {
if (tile.vbo == null) { if (tile.vbo == null) {
Log.d(TAG, "no VBOs left for " + tile); tile.vbo = BufferObject.get();
tile.isLoading = false;
return false; if (tile.vbo == null) {
} Log.d(TAG, "no VBOs left for " + tile);
tile.isLoading = false;
return false;
}
} else
Log.d(TAG, "tile loaded before " + tile);
tile.newData = true; tile.newData = true;
tile.isLoading = false; tile.isLoading = false;
// }
if (!MapView.debugFrameTime) if (!MapView.debugFrameTime)
requestRender(); requestRender();
synchronized (mTilesLoaded) { synchronized (mTilesLoaded) {
mTilesLoaded.add(tile); if (!mTilesLoaded.contains(tile))
mTilesLoaded.add(tile);
} }
return true; return true;
} }
public void setRenderTheme(RenderTheme t) { // public void setRenderTheme(RenderTheme t) {
if (mRenderer != null) // if (mRenderer != null)
mRenderer.setRenderTheme(t); // mRenderer.setRenderTheme(t);
//
} // }
@Override @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) { protected void onSizeChanged(int w, int h, int oldw, int oldh) {

View File

@ -16,6 +16,7 @@ package org.oscim.renderer;
import org.oscim.generator.JobTile; import org.oscim.generator.JobTile;
import org.oscim.renderer.layer.Layers; import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.TextItem;
class MapTile extends JobTile { class MapTile extends JobTile {
@ -25,7 +26,7 @@ class MapTile extends JobTile {
*/ */
BufferObject vbo; BufferObject vbo;
TextTexture texture; // TextTexture texture;
/** /**
* Tile data set by TileGenerator: * Tile data set by TileGenerator:
@ -56,10 +57,14 @@ class MapTile extends JobTile {
int lastDraw = 0; int lastDraw = 0;
// keep track which tiles are locked as proxy for this tile // keep track which tiles are locked as proxy for this tile
final static int PROXY_PARENT = 16; final static int PROXY_CHILD1 = 1 << 0;
final static int PROXY_GRAMPA = 32; final static int PROXY_CHILD2 = 1 << 1;
final static int PROXY_HOLDER = 64; final static int PROXY_CHILD3 = 1 << 2;
// 1-8: children final static int PROXY_CHILD4 = 1 << 3;
final static int PROXY_PARENT = 1 << 4;
final static int PROXY_GRAMPA = 1 << 5;
final static int PROXY_HOLDER = 1 << 6;
byte proxies; byte proxies;
// counting the tiles that use this tile as proxy // counting the tiles that use this tile as proxy
@ -83,8 +88,6 @@ class MapTile extends JobTile {
} }
void lock() { void lock() {
if (holder != null)
return;
locked++; locked++;
@ -116,27 +119,21 @@ class MapTile extends JobTile {
} }
void unlock() { void unlock() {
if (holder != null)
return;
locked--; locked--;
if (locked > 0 || proxies == 0) if (locked > 0 || proxies == 0)
return; return;
if ((proxies & (1 << 4)) != 0) { if ((proxies & PROXY_PARENT) != 0)
MapTile p = rel.parent.tile; rel.parent.tile.refs--;
p.refs--;
} if ((proxies & PROXY_GRAMPA) != 0)
if ((proxies & (1 << 5)) != 0) { rel.parent.parent.tile.refs--;
MapTile p = rel.parent.parent.tile;
p.refs--;
}
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if ((proxies & (1 << i)) != 0) { if ((proxies & (1 << i)) != 0)
rel.child[i].tile.refs--; rel.child[i].tile.refs--;
}
} }
proxies = 0; proxies = 0;
} }

View File

@ -14,121 +14,79 @@
*/ */
package org.oscim.renderer; package org.oscim.renderer;
import java.io.IOException;
import org.oscim.core.MapPosition; import org.oscim.core.MapPosition;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.Layers; import org.oscim.renderer.layer.Layers;
import org.oscim.renderer.layer.LineLayer; import org.oscim.utils.FastMath;
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 org.oscim.view.MapView;
import android.graphics.Color;
import android.graphics.Paint.Cap;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.opengl.Matrix; import android.opengl.Matrix;
import android.util.Log;
public class Overlay { public abstract class Overlay {
protected final MapView mMapView;
protected MapPosition mMapPosition;
protected final Layers layers;
BufferObject vbo;
Layers layers;
TextItem labels;
TextTexture texture;
// flag to set when data is ready for (re)compilation. // flag to set when data is ready for (re)compilation.
boolean newData; protected boolean newData;
boolean isReady;
MapPosition mMapPosition;
float drawScale; // flag set by GLRenderer when data is compiled
protected boolean isReady;
private boolean first = true; protected BufferObject vbo;
Overlay() { Overlay(MapView mapView) {
mMapView = mapView;
mMapPosition = new MapPosition(); mMapPosition = new MapPosition();
layers = new Layers(); 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() { synchronized boolean onTouch(boolean down) {
Log.d("...", "Overlay handle onTouch " + down);
return true; return true;
} }
/**
* update mMapPosition
*
* @return true if position has changed
*/
protected boolean updateMapPosition() {
return mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
}
// /////////////// called from GLRender Thread //////////////////////// // /////////////// called from GLRender Thread ////////////////////////
// use synchronized (this){} when updating 'layers' from another 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) * @param positionChanged
// mapView.getMapViewPosition().getMapPosition(mMapPosition, null); * true when MapPosition has changed
* @param tilesChanged
if (first) { * true when loaded tiles changed
// fix at initial position */
mapView.getMapViewPosition().getMapPosition(mMapPosition, null); synchronized void update(boolean positionChanged, boolean tilesChanged) {
first = false; // // keep position constant (or update layer relative to new position)
// mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
// pass layers to be uploaded and drawn to GL Thread //
// afterwards never modify 'layers' outside of this function! // if (first) {
newData = true; // // 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]; float[] mvp = new float[16];
synchronized void render(MapPosition pos, float[] mv, float[] proj) { synchronized void render(MapPosition pos, float[] mv, float[] proj) {
float div = 1; float div = setMatrix(pos, mv);
setMatrix(pos, mv);
Matrix.multiplyMM(mvp, 0, proj, 0, mv, 0); Matrix.multiplyMM(mvp, 0, proj, 0, mv, 0);
@ -140,29 +98,32 @@ public class Overlay {
l = PolygonRenderer.draw(pos, l, mvp, true, false); l = PolygonRenderer.draw(pos, l, mvp, true, false);
} else { } else {
GLES20.glEnable(GLES20.GL_BLEND); GLES20.glEnable(GLES20.GL_BLEND);
l = LineRenderer.draw(pos, l, mvp, div, 0, layers.lineOffset); l = LineRenderer.draw(pos, l, mvp, 1 / div, 0, layers.lineOffset);
} }
} }
for (Layer l = layers.symbolLayers; l != null;) { // float scale = curPos.scale / div;
l = TextureRenderer.draw(l, 1, proj, mv, layers.symbolOffset);
for (Layer l = layers.textureLayers; l != null;) {
l = TextureRenderer.draw(l, (mMapPosition.scale / pos.scale) * div, proj, mv,
layers.texOffset);
} }
} }
private void setMatrix(MapPosition mPos, float[] matrix) { private float setMatrix(MapPosition curPos, float[] matrix) {
MapPosition oPos = mMapPosition; MapPosition oPos = mMapPosition;
float div = 1;
byte z = oPos.zoomLevel; byte z = oPos.zoomLevel;
int diff = mPos.zoomLevel - z; // int diff = curPos.zoomLevel - z;
float div = FastMath.pow(z - curPos.zoomLevel);
// if (diff < 0)
// div = (1 << -diff);
// else if (diff > 0)
// div = (1.0f / (1 << diff));
if (diff < 0) float x = (float) (oPos.x - curPos.x * div);
div = (1 << -diff); float y = (float) (oPos.y - curPos.y * div);
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 // flip around date-line
float max = (Tile.TILE_SIZE << z); float max = (Tile.TILE_SIZE << z);
@ -171,7 +132,7 @@ public class Overlay {
else if (x > max / 2) else if (x > max / 2)
x = x - max; x = x - max;
float scale = mPos.scale / div; float scale = curPos.scale / div;
Matrix.setIdentityM(matrix, 0); Matrix.setIdentityM(matrix, 0);
@ -179,12 +140,14 @@ public class Overlay {
matrix[12] = x * scale; matrix[12] = x * scale;
matrix[13] = y * scale; matrix[13] = y * scale;
scale = (mPos.scale / oPos.scale) / div; scale = (curPos.scale / oPos.scale) / div;
// scale to tile to world coordinates // scale to tile to world coordinates
scale /= GLRenderer.COORD_MULTIPLIER; scale /= GLRenderer.COORD_MULTIPLIER;
matrix[0] = scale; matrix[0] = scale;
matrix[5] = scale; matrix[5] = scale;
Matrix.multiplyMM(matrix, 0, mPos.viewMatrix, 0, matrix, 0); Matrix.multiplyMM(matrix, 0, curPos.viewMatrix, 0, matrix, 0);
return div;
} }
} }

View File

@ -0,0 +1,143 @@
/*
* 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 org.oscim.core.Tile;
import org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.TextItem;
import org.oscim.renderer.layer.TextLayer;
import org.oscim.theme.renderinstruction.Line;
import org.oscim.theme.renderinstruction.Text;
import org.oscim.view.MapView;
import android.graphics.Color;
import android.graphics.Paint.Cap;
import android.util.Log;
public class OverlayGrid extends Overlay {
private float[] mPoints;
private short[] mIndex;
private Text mText;
OverlayGrid(MapView mapView) {
super(mapView);
int size = Tile.TILE_SIZE;
float[] points = new float[64];
short[] index = new short[16];
float pos = -size * 4;
// vertical lines
for (int i = 0; i < 8; i++) {
index[i] = 4;
// x1,y1,x2,y2
points[i * 4] = pos + i * size;
points[i * 4 + 1] = pos + 0;
points[i * 4 + 2] = pos + i * size;
points[i * 4 + 3] = pos + size * 8;
}
// horizontal lines
for (int j = 8; j < 16; j++) {
index[j] = 4;
points[j * 4] = pos + 0;
points[j * 4 + 1] = pos + (j - 8) * size;
points[j * 4 + 2] = pos + size * 8;
points[j * 4 + 3] = pos + (j - 8) * size;
}
mIndex = index;
mPoints = points;
// mText = Text.createText(20, 3, Color.BLACK, Color.RED, false);
mText = Text.createText(22, 0, Color.RED, 0, false);
// mText = Text.createText(22, 0, Color.RED, 0, true);
}
private void addLabels(int x, int y, int z) {
int size = Tile.TILE_SIZE;
TextLayer tl = new TextLayer();
for (int i = -2; i < 2; i++) {
for (int j = -2; j < 2; j++) {
TextItem ti = TextItem.get().set(size * j + size / 2, size * i + size / 2,
(x + j) + " / " + (y + i) + " / " + z, mText);
// TextItem ti = new TextItem(size * j + size / 2, size * i +
// size / 2,
// (x + j) + " / " + (y + i) + " / " + z, mText);
// rotation, TODO could also be used for slide range
ti.x1 = 0;
ti.y1 = 1; // (short) (size / 2);
ti.x2 = 1; // (short) size;
ti.y2 = 1; // (short) (size / 2);
tl.addText(ti);
}
}
layers.textureLayers = tl;
}
private int mCurX = -1;
private int mCurY = -1;
private byte mCurZ = -1;
private boolean finished;
void timerFinished() {
Log.d("...", "timer finish!");
finished = true;
mMapView.redrawMap();
}
@Override
synchronized void update(boolean positionChanged, boolean tilesChanged) {
updateMapPosition();
// fix map position to tile coordinates
float size = Tile.TILE_SIZE;
int x = (int) (mMapPosition.x / size);
int y = (int) (mMapPosition.y / size);
mMapPosition.x = x * size;
mMapPosition.y = y * size;
if (!finished)
mMapPosition.scale = 1;
// update layers when map moved by at least one tile
if (x != mCurX || y != mCurY || mMapPosition.zoomLevel != mCurZ) {
mCurX = x;
mCurY = y;
mCurZ = mMapPosition.zoomLevel;
layers.clear();
LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT);
ll.width = 1.5f;
ll.addLine(mPoints, mIndex, false);
addLabels(x, y, mCurZ);
newData = true;
finished = false;
}
}
}

View File

@ -0,0 +1,110 @@
/*
* 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 org.oscim.renderer.layer.Layer;
import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.renderinstruction.Line;
import org.oscim.view.MapView;
import android.graphics.Color;
import android.graphics.Paint.Cap;
public class OverlayTest extends Overlay {
TextItem labels;
float drawScale;
private boolean first = true;
OverlayTest(MapView mapView) {
super(mapView);
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);
// TextLayer tl = new TextLayer();
// Text t = Text.createText(20, 2, Color.WHITE, Color.BLACK, false);
// TextItem ti = new TextItem(0, 0, "check one, check two", t);
// ti.x1 = 0;
// ti.y1 = 0;
// ti.x2 = (short) Tile.TILE_SIZE;
// ti.y2 = (short) Tile.TILE_SIZE;
//
// tl.addText(ti);
//
// layers.textureLayers = tl;
}
@Override
synchronized void update(boolean positionChanged, boolean tilesChanged) {
// keep position constant (or update layer relative to new position)
mMapView.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;
}
}
}

View File

@ -0,0 +1,164 @@
/*
* 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 org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.renderer.MapRenderer.TilesData;
import org.oscim.renderer.layer.TextItem;
import org.oscim.renderer.layer.TextLayer;
import org.oscim.utils.FastMath;
import org.oscim.utils.PausableThread;
import org.oscim.view.MapView;
import android.os.SystemClock;
import android.util.FloatMath;
public class OverlayText extends Overlay {
private TilesData tiles;
private LabelThread mThread;
/* package */boolean mRun;
/* package */boolean mRerun;
private MapPosition mWorkPos;
private TextLayer mWorkLayer;
class LabelThread extends PausableThread {
@Override
protected void doWork() {
SystemClock.sleep(250);
mRun = false;
updateLabels();
mMapView.redrawMap();
}
@Override
protected String getThreadName() {
return "Labeling";
}
@Override
protected boolean hasWork() {
return mRun || mRerun;
}
}
OverlayText(MapView mapView) {
super(mapView);
mWorkPos = new MapPosition();
mThread = new LabelThread();
mThread.start();
}
void updateLabels() {
tiles = MapRenderer.getActiveTiles(tiles);
if (tiles.cnt == 0)
return;
mMapView.getMapViewPosition().getMapPosition(mWorkPos, null);
// TODO tiles might be from another zoomlevel than the current:
// this scales MapPosition to the zoomlevel of tiles...
int diff = tiles.tiles[0].zoomLevel - mWorkPos.zoomLevel;
float div = FastMath.pow(diff);
// fix map position to tile coordinates
float size = Tile.TILE_SIZE;
int x = (int) (mWorkPos.x / div / size);
int y = (int) (mWorkPos.y / div / size);
mWorkPos.x = x * size;
mWorkPos.y = y * size;
mWorkPos.zoomLevel += diff;
mWorkPos.scale = div;
// Log.d("...", "relabel " + mRerun + " " + x + " " + y);
TextLayer tl = new TextLayer();
float angle = (float) Math.toRadians(mWorkPos.angle);
float cos = FloatMath.cos(angle);
float sin = FloatMath.sin(angle);
for (int i = 0, n = tiles.cnt; i < n; i++) {
MapTile t = tiles.tiles[i];
if (!t.isVisible)
continue;
int dx = (t.tileX - x) * Tile.TILE_SIZE;
int dy = (t.tileY - y) * Tile.TILE_SIZE;
// Log.d("...", "update tiles " + dx + " " + dy);
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
TextItem ti2 = TextItem.get().move(ti, dx, dy);
if (!ti.text.caption) {
if (cos * (ti.x2 - ti.x1) - sin * (ti.y2 - ti.y1) < 0) {
// flip label upside-down
ti2.x1 = ti.x2;
ti2.y1 = ti.y2;
ti2.x2 = ti.x1;
ti2.y2 = ti.y1;
} else {
ti2.x1 = ti.x1;
ti2.y1 = ti.y1;
ti2.x2 = ti.x2;
ti2.y2 = ti.y2;
}
}
tl.addText(ti2);
}
}
// everything synchronized?
synchronized (this) {
mWorkLayer = tl;
}
}
@Override
synchronized void update(boolean positionChanged, boolean tilesChanged) {
// Log.d("...", "update " + tilesChanged + " " + positionChanged);
if (mWorkLayer != null) {
layers.clear();
layers.textureLayers = mWorkLayer;
mWorkLayer = null;
// make the 'labeled' MapPosition current
MapPosition tmp = mMapPosition;
mMapPosition = mWorkPos;
mWorkPos = tmp;
// TODO should return true instead
newData = true;
}
if (tilesChanged || positionChanged) {
if (!mRun) {
mRun = true;
synchronized (mThread) {
mThread.notify();
}
}
}
}
}

View File

@ -14,442 +14,445 @@
*/ */
package org.oscim.renderer; package org.oscim.renderer;
import java.nio.ByteBuffer; //import java.nio.ByteBuffer;
import java.nio.ByteOrder; //import java.nio.ByteOrder;
import java.nio.ShortBuffer; //import java.nio.ShortBuffer;
//
import org.oscim.utils.GlUtils; //import org.oscim.renderer.layer.TextItem;
//import org.oscim.utils.GlUtils;
import android.graphics.Bitmap; //
import android.graphics.Canvas; //import android.graphics.Bitmap;
import android.graphics.Color; //import android.graphics.Canvas;
import android.graphics.Paint; //import android.graphics.Color;
import android.opengl.GLES20; //import android.graphics.Paint;
import android.opengl.GLUtils; //import android.opengl.GLES20;
import android.util.FloatMath; //import android.opengl.GLUtils;
import android.util.Log; //import android.util.FloatMath;
//import android.util.Log;
public class TextRenderer { //
private static String TAG = "TextRenderer"; //public class TextRenderer {
// private static String TAG = "TextRenderer";
private final static int TEXTURE_WIDTH = 256; //
private final static int TEXTURE_HEIGHT = 256; // private final static int TEXTURE_WIDTH = 256;
private final static float SCALE = 8.0f; // private final static int TEXTURE_HEIGHT = 256;
private final static int LBIT_MASK = 0xfffffffe; // private final static float SCALE = 8.0f;
// private final static int L2BIT_MASK = 0xfffffffc; // private final static int LBIT_MASK = 0xfffffffe;
// // private final static int L2BIT_MASK = 0xfffffffc;
final static int INDICES_PER_SPRITE = 6; //
final static int VERTICES_PER_SPRITE = 4; // final static int INDICES_PER_SPRITE = 6;
final static int SHORTS_PER_VERTICE = 6; // final static int VERTICES_PER_SPRITE = 4;
final static int MAX_LABELS = 35; // final static int SHORTS_PER_VERTICE = 6;
// final static int MAX_LABELS = 35;
private static Bitmap mBitmap; //
private static Canvas mCanvas; // private static Bitmap mBitmap;
private static int mFontPadX = 1; // private static Canvas mCanvas;
private static int mFontPadY = 1; // private static int mFontPadX = 1;
private static int mBitmapFormat; // private static int mFontPadY = 1;
private static int mBitmapType; // private static int mBitmapFormat;
private static ShortBuffer mShortBuffer; // private static int mBitmapType;
private static TextTexture[] mTextures; // private static ShortBuffer mShortBuffer;
// private static TextTexture[] mTextures;
private static int mIndicesVBO; //
private static int mVerticesVBO; // private static int mIndicesVBO;
// private static int mVerticesVBO;
private static int mTextProgram; //
private static int hTextMVMatrix; // private static int mTextProgram;
private static int hTextProjectionMatrix; // private static int hTextMVMatrix;
private static int hTextVertex; // private static int hTextProjectionMatrix;
private static int hTextScale; // private static int hTextVertex;
private static int hTextScreenScale; // private static int hTextScale;
private static int hTextTextureCoord; // private static int hTextScreenScale;
// private static int hTextTextureCoord;
private static Paint mPaint = new Paint(Color.BLACK); //
// private static Paint mPaint = new Paint(Color.BLACK);
private static boolean debug = false; //
private static short[] debugVertices = { // private static boolean debug = false;
// private static short[] debugVertices = {
0, 0, //
0, TEXTURE_HEIGHT * 4, // 0, 0,
// 0, TEXTURE_HEIGHT * 4,
0, TEXTURE_HEIGHT - 1, //
0, 0, // 0, TEXTURE_HEIGHT - 1,
// 0, 0,
TEXTURE_WIDTH - 1, 0, //
TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4, // TEXTURE_WIDTH - 1, 0,
// TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4,
TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1, //
TEXTURE_WIDTH * 4, 0, // TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1,
// TEXTURE_WIDTH * 4, 0,
}; //
// };
static void init() { //
mTextProgram = GlUtils.createProgram(Shaders.textVertexShader, // static void init() {
Shaders.textFragmentShader); // mTextProgram = GlUtils.createProgram(Shaders.textVertexShader,
// Shaders.textFragmentShader);
hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv"); //
hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj"); // hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv");
hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale"); // hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj");
hTextScreenScale = GLES20.glGetUniformLocation(mTextProgram, "u_swidth"); // hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale");
hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex"); // hTextScreenScale = GLES20.glGetUniformLocation(mTextProgram, "u_swidth");
hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord"); // hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex");
// hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord");
} //
// }
static boolean setup(int numTextures) { //
int bufferSize = numTextures // static boolean setup(int numTextures) {
* MAX_LABELS * VERTICES_PER_SPRITE // int bufferSize = numTextures
* SHORTS_PER_VERTICE * (Short.SIZE / 8); // * MAX_LABELS * VERTICES_PER_SPRITE
// * SHORTS_PER_VERTICE * (Short.SIZE / 8);
// if (mBitmap == null) { //
mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, // // if (mBitmap == null) {
Bitmap.Config.ARGB_8888); // mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
mCanvas = new Canvas(mBitmap); // Bitmap.Config.ARGB_8888);
// mCanvas = new Canvas(mBitmap);
mBitmapFormat = GLUtils.getInternalFormat(mBitmap); //
mBitmapType = GLUtils.getType(mBitmap); // mBitmapFormat = GLUtils.getInternalFormat(mBitmap);
// mBitmapType = GLUtils.getType(mBitmap);
ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize) //
.order(ByteOrder.nativeOrder()); // ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize)
// .order(ByteOrder.nativeOrder());
mShortBuffer = buf.asShortBuffer(); //
// } // mShortBuffer = buf.asShortBuffer();
// // }
int[] textureIds = new int[numTextures]; //
TextTexture[] textures = new TextTexture[numTextures]; // int[] textureIds = new int[numTextures];
GLES20.glGenTextures(numTextures, textureIds, 0); // TextTexture[] textures = new TextTexture[numTextures];
// GLES20.glGenTextures(numTextures, textureIds, 0);
for (int i = 0; i < numTextures; i++) { //
// setup filters for texture // for (int i = 0; i < numTextures; i++) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]); // // setup filters for texture
// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, //
GLES20.GL_LINEAR); // GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, // GLES20.GL_LINEAR);
GLES20.GL_LINEAR); // GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, // GLES20.GL_LINEAR);
GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping // GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, // GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping
GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping // GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
// GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping
// load the generated bitmap onto the texture //
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap, // // load the generated bitmap onto the texture
mBitmapType, 0); // GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap,
// mBitmapType, 0);
textures[i] = new TextTexture(textureIds[i]); //
} // textures[i] = new TextTexture(textureIds[i]);
// }
GlUtils.checkGlError("init textures"); //
// GlUtils.checkGlError("init textures");
mTextures = textures; //
// mTextures = textures;
// Setup triangle indices //
short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE]; // // Setup triangle indices
int len = indices.length; // short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE];
short j = 0; // int len = indices.length;
for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) { // short j = 0;
indices[i + 0] = (short) (j + 0); // for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) {
indices[i + 1] = (short) (j + 1); // indices[i + 0] = (short) (j + 0);
indices[i + 2] = (short) (j + 2); // indices[i + 1] = (short) (j + 1);
indices[i + 3] = (short) (j + 2); // indices[i + 2] = (short) (j + 2);
indices[i + 4] = (short) (j + 3); // indices[i + 3] = (short) (j + 2);
indices[i + 5] = (short) (j + 0); // indices[i + 4] = (short) (j + 3);
} // indices[i + 5] = (short) (j + 0);
// }
mShortBuffer.clear(); //
mShortBuffer.put(indices, 0, len); // mShortBuffer.clear();
mShortBuffer.flip(); // mShortBuffer.put(indices, 0, len);
// mShortBuffer.flip();
int[] mVboIds = new int[2]; //
GLES20.glGenBuffers(2, mVboIds, 0); // int[] mVboIds = new int[2];
mIndicesVBO = mVboIds[0]; // GLES20.glGenBuffers(2, mVboIds, 0);
mVerticesVBO = mVboIds[1]; // mIndicesVBO = mVboIds[0];
// mVerticesVBO = mVboIds[1];
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); //
GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8), // GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
mShortBuffer, GLES20.GL_STATIC_DRAW); // GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, len * (Short.SIZE / 8),
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); // mShortBuffer, GLES20.GL_STATIC_DRAW);
// GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
mShortBuffer.clear(); //
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); // mShortBuffer.clear();
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize, // GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
mShortBuffer, GLES20.GL_DYNAMIC_DRAW); // GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize,
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); // mShortBuffer, GLES20.GL_DYNAMIC_DRAW);
// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
return true; //
} // return true;
// }
static boolean drawToTexture(MapTile tile) { //
TextTexture tex = null; // static boolean drawToTexture(MapTile tile) {
// TextTexture tex = null;
if (tile.labels == null) //
return false; // if (tile.labels == null)
// return false;
for (int i = 0; i < mTextures.length; i++) { //
tex = mTextures[i]; // for (int i = 0; i < mTextures.length; i++) {
if (tex.tile == null) // tex = mTextures[i];
break; // if (tex.tile == null)
// break;
if (!tex.tile.isLocked()) //
break; // if (!tex.tile.isLocked())
// break;
tex = null; //
} // tex = null;
// }
if (tex == null) { //
for (int i = 0; i < mTextures.length; i++) { // if (tex == null) {
tex = mTextures[i]; // for (int i = 0; i < mTextures.length; i++) {
if (!tex.tile.isVisible) // tex = mTextures[i];
break; // if (!tex.tile.isVisible)
// break;
tex = null; //
} // tex = null;
} // }
// }
if (tex == null) { //
Log.d(TAG, "no textures left"); // if (tex == null) {
return false; // Log.d(TAG, "no textures left");
} // return false;
if (tex.tile != null) // }
tex.tile.texture = null; // if (tex.tile != null)
// tex.tile.texture = null;
mBitmap.eraseColor(Color.TRANSPARENT); //
// mBitmap.eraseColor(Color.TRANSPARENT);
int pos = 0; //
short[] buf = tex.vertices; // int pos = 0;
// short[] buf = tex.vertices;
float y = 0; //
float x = mFontPadX; // float y = 0;
float width, height; // float x = mFontPadX;
// float width, height;
int max = MAX_LABELS; //
// int max = MAX_LABELS;
if (debug) { //
mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4], // if (debug) {
debugVertices[5], mPaint); // mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4],
mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8], // debugVertices[5], mPaint);
debugVertices[9], mPaint); // mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8],
// debugVertices[9], mPaint);
mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4], //
debugVertices[5], mPaint); // mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4],
mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8], // debugVertices[5], mPaint);
debugVertices[9], mPaint); // mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8],
} // debugVertices[9], mPaint);
// }
int advanceY = 0; //
// int advanceY = 0;
TextItem t = tile.labels; //
float yy; // TextItem t = tile.labels;
// float yy;
for (int i = 0; t != null && i < max; t = t.next, i++) { //
// for (int i = 0; t != null && i < max; t = t.next, i++) {
height = (int) (t.text.fontHeight) + 2 * mFontPadY; // if (t.text.caption)
width = t.width + 2 * mFontPadX; // continue;
//
if (height > advanceY) // height = (int) (t.text.fontHeight) + 2 * mFontPadY;
advanceY = (int) height; // width = t.width + 2 * mFontPadX;
//
if (x + width > TEXTURE_WIDTH) { // if (height > advanceY)
x = mFontPadX; // advanceY = (int) height;
y += advanceY; //
advanceY = (int) height; // if (x + width > TEXTURE_WIDTH) {
} // x = mFontPadX;
// y += advanceY;
yy = y + (height - 1) - t.text.fontDescent - mFontPadY; // advanceY = (int) height;
if (yy > TEXTURE_HEIGHT) { // }
Log.d(TAG, "reached max labels"); //
break; // yy = y + (height - 1) - t.text.fontDescent - mFontPadY;
// continue; // if (yy > TEXTURE_HEIGHT) {
} // Log.d(TAG, "reached max labels");
// break;
if (t.text.stroke != null) // // continue;
mCanvas.drawText(t.string, x + t.width / 2, yy, t.text.stroke); // }
//
mCanvas.drawText(t.string, x + t.width / 2, yy, t.text.paint); // if (t.text.stroke != null)
// mCanvas.drawText(t.string, x + t.width / 2, yy, t.text.stroke);
if (width > TEXTURE_WIDTH) //
width = TEXTURE_WIDTH; // mCanvas.drawText(t.string, x + t.width / 2, yy, t.text.paint);
//
float hw = width / 2.0f; // if (width > TEXTURE_WIDTH)
float hh = height / 2.0f; // width = TEXTURE_WIDTH;
short x1, x2, x3, x4, y1, y2, y3, y4; //
// float hw = width / 2.0f;
if (t.text.caption) { // float hh = height / 2.0f;
x1 = x3 = (short) (SCALE * (-hw)); // short x1, x2, x3, x4, y1, y2, y3, y4;
y1 = y3 = (short) (SCALE * (hh)); //
x2 = x4 = (short) (SCALE * (hw)); // if (t.text.caption) {
y2 = y4 = (short) (SCALE * (-hh)); // x1 = x3 = (short) (SCALE * (-hw));
} else { // y1 = y3 = (short) (SCALE * (hh));
float vx = t.x1 - t.x2; // x2 = x4 = (short) (SCALE * (hw));
float vy = t.y1 - t.y2; // y2 = y4 = (short) (SCALE * (-hh));
float a = FloatMath.sqrt(vx * vx + vy * vy); // } else {
vx = vx / a; // float vx = t.x1 - t.x2;
vy = vy / a; // float vy = t.y1 - t.y2;
// float a = FloatMath.sqrt(vx * vx + vy * vy);
float ux = -vy; // vx = vx / a;
float uy = vx; // vy = vy / a;
//
// int dx = (int) (vx * SCALE) & L2BIT_MASK; // float ux = -vy;
// int dy = (int) (vy * SCALE) & L2BIT_MASK; // float uy = vx;
// //
// x1 = (short) dx; // // int dx = (int) (vx * SCALE) & L2BIT_MASK;
// y1 = (short) dy; // // int dy = (int) (vy * SCALE) & L2BIT_MASK;
// // //
// x2 = (short) (dx | 1); // // x1 = (short) dx;
// y3 = (short) (dy | 1); // // y1 = (short) dy;
// // //
// x4 = (short) (dx | 3); // // x2 = (short) (dx | 1);
// y4 = (short) (dy | 3); // // y3 = (short) (dy | 1);
// // //
// x3 = (short) (dx | 2); // // x4 = (short) (dx | 3);
// y2 = (short) (dy | 2); // // y4 = (short) (dy | 3);
// //
x1 = (short) (SCALE * (vx * hw - ux * hh)); // // x3 = (short) (dx | 2);
y1 = (short) (SCALE * (vy * hw - uy * hh)); // // y2 = (short) (dy | 2);
x2 = (short) (SCALE * (-vx * hw - ux * hh)); //
y3 = (short) (SCALE * (-vy * hw - uy * hh)); // x1 = (short) (SCALE * (vx * hw - ux * hh));
x4 = (short) (SCALE * (-vx * hw + ux * hh)); // y1 = (short) (SCALE * (vy * hw - uy * hh));
y4 = (short) (SCALE * (-vy * hw + uy * hh)); // x2 = (short) (SCALE * (-vx * hw - ux * hh));
x3 = (short) (SCALE * (vx * hw + ux * hh)); // y3 = (short) (SCALE * (-vy * hw - uy * hh));
y2 = (short) (SCALE * (vy * hw + uy * hh)); // x4 = (short) (SCALE * (-vx * hw + ux * hh));
// y4 = (short) (SCALE * (-vy * hw + uy * hh));
} // x3 = (short) (SCALE * (vx * hw + ux * hh));
short u1 = (short) (SCALE * x); // y2 = (short) (SCALE * (vy * hw + uy * hh));
short v1 = (short) (SCALE * y); //
short u2 = (short) (SCALE * (x + width)); // }
short v2 = (short) (SCALE * (y + height)); // short u1 = (short) (SCALE * x);
// short v1 = (short) (SCALE * y);
// pack caption/way-text info in lowest bit // short u2 = (short) (SCALE * (x + width));
int tmp = (int) (SCALE * t.x) & LBIT_MASK; // short v2 = (short) (SCALE * (y + height));
short tx = (short) (tmp | (t.text.caption ? 1 : 0)); //
// // pack caption/way-text info in lowest bit
short ty = (short) (SCALE * t.y); // int tmp = (int) (SCALE * t.x) & LBIT_MASK;
// short tx = (short) (tmp | (t.text.caption ? 1 : 0));
// top-left //
buf[pos++] = tx; // short ty = (short) (SCALE * t.y);
buf[pos++] = ty; //
buf[pos++] = x1; // // top-left
buf[pos++] = y1; // buf[pos++] = tx;
buf[pos++] = u1; // buf[pos++] = ty;
buf[pos++] = v2; // buf[pos++] = x1;
// buf[pos++] = y1;
// top-right // buf[pos++] = u1;
buf[pos++] = tx; // buf[pos++] = v2;
buf[pos++] = ty; //
buf[pos++] = x2; // // top-right
buf[pos++] = y3; // buf[pos++] = tx;
buf[pos++] = u2; // buf[pos++] = ty;
buf[pos++] = v2; // buf[pos++] = x2;
// buf[pos++] = y3;
// bot-right // buf[pos++] = u2;
buf[pos++] = tx; // buf[pos++] = v2;
buf[pos++] = ty; //
buf[pos++] = x4; // // bot-right
buf[pos++] = y4; // buf[pos++] = tx;
buf[pos++] = u2; // buf[pos++] = ty;
buf[pos++] = v1; // buf[pos++] = x4;
// buf[pos++] = y4;
// bot-left // buf[pos++] = u2;
buf[pos++] = tx; // buf[pos++] = v1;
buf[pos++] = ty; //
buf[pos++] = x3; // // bot-left
buf[pos++] = y2; // buf[pos++] = tx;
buf[pos++] = u1; // buf[pos++] = ty;
buf[pos++] = v1; // buf[pos++] = x3;
// buf[pos++] = y2;
x += width; // buf[pos++] = u1;
// buf[pos++] = v1;
if (y > TEXTURE_HEIGHT) { //
Log.d(TAG, "reached max labels: texture is full"); // x += width;
break; //
} // if (y > TEXTURE_HEIGHT) {
} // Log.d(TAG, "reached max labels: texture is full");
// break;
tex.length = pos; // }
tile.texture = tex; // }
tex.tile = tile; //
// tex.length = pos;
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id); // tile.texture = tex;
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap, // tex.tile = tile;
mBitmapFormat, mBitmapType); //
// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id);
// FIXME shouldnt be needed here, still looking for sometimes corrupted // GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap,
// labels.. // mBitmapFormat, mBitmapType);
GLES20.glFlush(); //
// // FIXME shouldnt be needed here, still looking for sometimes corrupted
return true; // // labels..
} // GLES20.glFlush();
//
static void compileTextures() { // return true;
int offset = 0; // }
TextTexture tex; //
// static void compileTextures() {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); // int offset = 0;
// TextTexture tex;
mShortBuffer.clear(); //
// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
for (int i = 0; i < mTextures.length; i++) { //
tex = mTextures[i]; // mShortBuffer.clear();
if (tex.tile == null) // || !tex.tile.isLocked) //
continue; // for (int i = 0; i < mTextures.length; i++) {
// tex = mTextures[i];
mShortBuffer.put(tex.vertices, 0, tex.length); // if (tex.tile == null) // || !tex.tile.isLocked)
tex.offset = offset; // continue;
offset += tex.length; //
} // mShortBuffer.put(tex.vertices, 0, tex.length);
// tex.offset = offset;
mShortBuffer.flip(); // offset += tex.length;
// }
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8), //
mShortBuffer); // mShortBuffer.flip();
} //
// GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8),
static void beginDraw(float scale, float[] projection) { // mShortBuffer);
GLES20.glUseProgram(mTextProgram); // }
//
// GLES20.glEnableVertexAttribArray(hTextTextureCoord); // static void beginDraw(float scale, float[] projection) {
// GLES20.glEnableVertexAttribArray(hTextVertex); // GLES20.glUseProgram(mTextProgram);
//
int va = hTextTextureCoord; // // GLES20.glEnableVertexAttribArray(hTextTextureCoord);
if (!GLRenderer.vertexArray[va]) { // // GLES20.glEnableVertexAttribArray(hTextVertex);
GLES20.glEnableVertexAttribArray(va); //
GLRenderer.vertexArray[va] = true; // int va = hTextTextureCoord;
} // if (!GLRenderer.vertexArray[va]) {
// GLES20.glEnableVertexAttribArray(va);
va = hTextVertex; // GLRenderer.vertexArray[va] = true;
if (!GLRenderer.vertexArray[va]) { // }
GLES20.glEnableVertexAttribArray(va); //
GLRenderer.vertexArray[va] = true; // va = hTextVertex;
} // if (!GLRenderer.vertexArray[va]) {
// GLES20.glEnableVertexAttribArray(va);
GLES20.glUniform1f(hTextScale, scale); // GLRenderer.vertexArray[va] = true;
GLES20.glUniform1f(hTextScreenScale, 1f / GLRenderer.mWidth); // }
GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0); //
// GLES20.glUniform1f(hTextScale, scale);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); // GLES20.glUniform1f(hTextScreenScale, 1f / GLRenderer.mWidth);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); // GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0);
} //
// GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
static void endDraw() { // GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); // }
//
// GLES20.glDisableVertexAttribArray(hTextTextureCoord); // static void endDraw() {
// GLES20.glDisableVertexAttribArray(hTextVertex); // GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
} //
// // GLES20.glDisableVertexAttribArray(hTextTextureCoord);
static void drawTile(MapTile tile, float[] matrix) { // // GLES20.glDisableVertexAttribArray(hTextVertex);
// }
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id); //
// static void drawTile(MapTile tile, float[] matrix) {
GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0); //
// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id);
GLES20.glVertexAttribPointer(hTextVertex, 4, //
GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)); // GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0);
//
GLES20.glVertexAttribPointer(hTextTextureCoord, 2, // 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));
+ 8); //
// GLES20.glVertexAttribPointer(hTextTextureCoord, 2,
GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) * // GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)
INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0); // + 8);
} //
} // GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) *
// INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0);
// }
// }

View File

@ -14,20 +14,20 @@
*/ */
package org.oscim.renderer; package org.oscim.renderer;
public class TextTexture { //public class TextTexture {
//
final short[] vertices; // final short[] vertices;
final int id; // final int id;
int length; // int length;
int offset; // int offset;
MapTile tile; // MapTile tile;
//
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

@ -15,6 +15,8 @@
package org.oscim.renderer; package org.oscim.renderer;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.opengl.GLES20; import android.opengl.GLES20;
import android.opengl.GLUtils; import android.opengl.GLUtils;
import android.util.Log; import android.util.Log;
@ -22,11 +24,22 @@ import android.util.Log;
public class TextureObject { public class TextureObject {
private static TextureObject pool; private static TextureObject pool;
// shared bitmap and canvas for default texture size
public final static int TEXTURE_WIDTH = 256;
public final static int TEXTURE_HEIGHT = 256;
private static Bitmap mBitmap;
private static Canvas mCanvas;
private static int mBitmapFormat;
private static int mBitmapType;
private static int objectCount = 10;
public static synchronized TextureObject get() { public static synchronized TextureObject get() {
TextureObject to; TextureObject to;
if (pool == null) { if (pool == null) {
init(10); init(10);
objectCount += 10;
Log.d("...", "textures: " + objectCount);
} }
to = pool; to = pool;
@ -36,8 +49,15 @@ public class TextureObject {
} }
public static synchronized void release(TextureObject to) { public static synchronized void release(TextureObject to) {
to.next = pool;
pool = to; while (to != null) {
TextureObject next = to.next;
to.next = pool;
pool = to;
to = next;
}
} }
public static void uploadTexture(TextureObject to, Bitmap bitmap, public static void uploadTexture(TextureObject to, Bitmap bitmap,
@ -79,6 +99,30 @@ public class TextureObject {
to.next = pool; to.next = pool;
pool = to; pool = to;
} }
mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mBitmapFormat = GLUtils.getInternalFormat(mBitmap);
mBitmapType = GLUtils.getType(mBitmap);
}
public static Canvas getCanvas() {
mBitmap.eraseColor(Color.TRANSPARENT);
return mCanvas;
}
public static TextureObject uploadCanvas(short offset, short indices) {
TextureObject to = get();
uploadTexture(to, mBitmap,
mBitmapFormat, mBitmapType,
TEXTURE_WIDTH, TEXTURE_HEIGHT);
to.offset = offset;
to.vertices = (short) (indices - offset);
return to;
} }
public TextureObject next; public TextureObject next;
@ -89,7 +133,8 @@ public class TextureObject {
// vertex offset from which this texture is referenced // vertex offset from which this texture is referenced
// or store texture id with vertex? // or store texture id with vertex?
int offset; short offset;
short vertices;
TextureObject(int id) { TextureObject(int id) {
this.id = id; this.id = id;

View File

@ -39,7 +39,7 @@ public class TextureRenderer {
final static int VERTICES_PER_SPRITE = 4; final static int VERTICES_PER_SPRITE = 4;
final static int SHORTS_PER_VERTICE = 6; final static int SHORTS_PER_VERTICE = 6;
// per texture // per texture
public final static int MAX_ITEMS = 40; public final static int MAX_ITEMS = 50;
static void init() { static void init() {
mTextureProgram = GlUtils.createProgram(Shaders.textVertexShader, mTextureProgram = GlUtils.createProgram(Shaders.textVertexShader,
@ -89,6 +89,7 @@ public class TextureRenderer {
static Layer draw(Layer layer, float scale, float[] projection, static Layer draw(Layer layer, float scale, float[] projection,
float matrix[], int offset) { float matrix[], int offset) {
GLES20.glUseProgram(mTextureProgram); GLES20.glUseProgram(mTextureProgram);
GlUtils.checkGlError("draw texture1"); GlUtils.checkGlError("draw texture1");
@ -116,20 +117,25 @@ public class TextureRenderer {
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
GlUtils.checkGlError("draw texture3"); GlUtils.checkGlError("draw texture3");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tl.textures.id); for (TextureObject to = tl.textures; to != null; to = to.next) {
GlUtils.checkGlError("draw texture4");
GlUtils.checkGlError("draw texture5"); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, to.id);
GLES20.glVertexAttribPointer(hTextureVertex, 4, GlUtils.checkGlError("draw texture4");
GLES20.GL_SHORT, false, 12, offset);
GlUtils.checkGlError("draw texture..");
GLES20.glVertexAttribPointer(hTextureTexCoord, 2, GlUtils.checkGlError("draw texture5");
GLES20.GL_SHORT, false, 12, offset + 8);
GlUtils.checkGlError("draw texture...");
GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tl.verticesCnt / 4) // to.offset * 24(shorts) * 2(short-bytes) / 6(indices)
* INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0); GLES20.glVertexAttribPointer(hTextureVertex, 4,
GLES20.GL_SHORT, false, 12, to.offset * 8 + offset);
GlUtils.checkGlError("draw texture..");
GLES20.glVertexAttribPointer(hTextureTexCoord, 2,
GLES20.GL_SHORT, false, 12, to.offset * 8 + offset + 8);
GlUtils.checkGlError("draw texture...");
GLES20.glDrawElements(GLES20.GL_TRIANGLES, to.vertices,
GLES20.GL_UNSIGNED_SHORT, 0);
}
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
GlUtils.checkGlError("draw texture"); GlUtils.checkGlError("draw texture");

View File

@ -28,6 +28,7 @@ import org.oscim.renderer.layer.LineLayer;
import org.oscim.renderer.layer.PolygonLayer; import org.oscim.renderer.layer.PolygonLayer;
import org.oscim.renderer.layer.SymbolItem; import org.oscim.renderer.layer.SymbolItem;
import org.oscim.renderer.layer.SymbolLayer; import org.oscim.renderer.layer.SymbolLayer;
import org.oscim.renderer.layer.TextItem;
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;
@ -39,6 +40,7 @@ import org.oscim.view.MapView;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Paint; import android.graphics.Paint;
import android.util.FloatMath;
import android.util.Log; import android.util.Log;
/** /**
@ -97,6 +99,10 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
private float mProjectionScaleFactor; private float mProjectionScaleFactor;
public static void setRenderTheme(RenderTheme theme) {
renderTheme = theme;
}
/** /**
* @param mapView * @param mapView
* the MapView * the MapView
@ -243,7 +249,9 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
if (text.textKey == mTagEmptyName.key) { if (text.textKey == mTagEmptyName.key) {
TextItem t = new TextItem(mCoords[0], mCoords[1], mTagName.value, text); // TextItem t = new TextItem(mCoords[0], mCoords[1], mTagName.value,
// text);
TextItem t = TextItem.get().set(mCoords[0], mCoords[1], mTagName.value, text);
t.next = mLabels; t.next = mLabels;
mLabels = t; mLabels = t;
} }
@ -258,7 +266,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
return; return;
if (text.textKey == mTagEmptyName.key) { if (text.textKey == mTagEmptyName.key) {
TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, text); TextItem t = TextItem.get().set(mPoiX, mPoiY, mTagName.value, text);
// TextItem t = new TextItem(mPoiX, mPoiY, mTagName.value, text);
t.next = mLabels; t.next = mLabels;
mLabels = t; mLabels = t;
} }
@ -294,10 +303,10 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
public void renderPointOfInterestSymbol(Bitmap bitmap) { public void renderPointOfInterestSymbol(Bitmap bitmap) {
// Log.d(TAG, "add symbol"); // Log.d(TAG, "add symbol");
if (mLayers.symbolLayers == null) if (mLayers.textureLayers == null)
mLayers.symbolLayers = new SymbolLayer(); mLayers.textureLayers = new SymbolLayer();
SymbolLayer sl = (SymbolLayer) mLayers.symbolLayers; SymbolLayer sl = (SymbolLayer) mLayers.textureLayers;
SymbolItem it = new SymbolItem(); SymbolItem it = new SymbolItem();
it.x = mPoiX; it.x = mPoiX;
@ -388,12 +397,13 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
mDebugDrawPolygons = !debugSettings.mDisablePolygons; mDebugDrawPolygons = !debugSettings.mDisablePolygons;
mDebugDrawUnmatched = debugSettings.mDrawUnmatchted; mDebugDrawUnmatched = debugSettings.mDrawUnmatchted;
if (tile.newData || tile.isReady) { if (tile.newData || tile.isReady || tile.layers != null) {
// should be 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 + " "
+ tile.isReady + " "); + tile.isReady + " "
+ tile.isLoading);
return false; return false;
} }
@ -406,9 +416,9 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL); setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL);
// acount for area changes with latitude // acount for area changes with latitude
mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection mProjectionScaleFactor = 0.5f + 0.5f * (
.pixelYToLatitude(tile.pixelY, tile.zoomLevel) FloatMath.sin((float) (Math.abs(MercatorProjection
* (Math.PI / 180))); .pixelYToLatitude(tile.pixelY, tile.zoomLevel)) * (Math.PI / 180))));
mLayers = new Layers(); mLayers = new Layers();
@ -482,10 +492,6 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
return mMapDatabase; return mMapDatabase;
} }
public void setRenderTheme(RenderTheme theme) {
TileGenerator.renderTheme = theme;
}
@Override @Override
public boolean checkWay(Tag[] tags, boolean closed) { public boolean checkWay(Tag[] tags, boolean closed) {

View File

@ -14,12 +14,13 @@
*/ */
package org.oscim.renderer; package org.oscim.renderer;
import org.oscim.renderer.layer.TextItem;
import org.oscim.theme.renderinstruction.Text; import org.oscim.theme.renderinstruction.Text;
import org.oscim.utils.GeometryUtils; import org.oscim.utils.GeometryUtils;
import android.util.FloatMath; import android.util.FloatMath;
final class WayDecorator { public final class WayDecorator {
// /** // /**
// * Minimum distance in pixels before the symbol is repeated. // * Minimum distance in pixels before the symbol is repeated.
// */ // */
@ -99,7 +100,7 @@ final class WayDecorator {
// } // }
// } // }
static TextItem renderText(float[] coordinates, String string, Text text, public 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;
@ -256,9 +257,16 @@ final class WayDecorator {
} }
// if (t == null) // if (t == null)
t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, string, t = TextItem.get();
text, wayNameWidth); // t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2,
// string,
// text, wayNameWidth);
t.x = x1 + (x2 - x1) / 2f;
t.y = y1 + (y2 - y1) / 2f;
t.string = string;
t.text = text;
t.width = wayNameWidth;
t.x1 = (short) x1; t.x1 = (short) x1;
t.y1 = (short) y1; t.y1 = (short) y1;
t.x2 = (short) x2; t.x2 = (short) x2;

View File

@ -14,7 +14,7 @@
*/ */
package org.oscim.renderer.layer; package org.oscim.renderer.layer;
public class Layer { public abstract class Layer {
public final static byte LINE = 0; public final static byte LINE = 0;
public final static byte POLYGON = 1; public final static byte POLYGON = 1;
public final static byte WAYTEXT = 2; public final static byte WAYTEXT = 2;
@ -34,4 +34,7 @@ public class Layer {
VertexPoolItem pool; VertexPoolItem pool;
protected VertexPoolItem curItem; protected VertexPoolItem curItem;
protected void clear() {
}
} }

View File

@ -16,8 +16,6 @@ package org.oscim.renderer.layer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject;
import android.util.Log; import android.util.Log;
public class Layers { public class Layers {
@ -25,8 +23,8 @@ public class Layers {
public Layer layers; public Layer layers;
public int lineOffset; public int lineOffset;
public Layer symbolLayers; public Layer textureLayers;
public int symbolOffset; public int texOffset;
private Layer mCurLayer; private Layer mCurLayer;
@ -57,10 +55,13 @@ public class Layers {
if (ret == null) { if (ret == null) {
if (type == Layer.LINE) if (type == Layer.LINE)
ret = new LineLayer(level); ret = new LineLayer(level);
else else if (type == Layer.POLYGON)
ret = new PolygonLayer(level); ret = new PolygonLayer(level);
else
return null;
if (l == null) { if (l == null) {
// insert at start
ret.next = layers; ret.next = layers;
layers = ret; layers = ret;
} else { } else {
@ -81,7 +82,9 @@ public class Layers {
private static int TEXTURE_VERTEX_SHORTS = 6; private static int TEXTURE_VERTEX_SHORTS = 6;
public int getSize() { public int getSize() {
int size = 0; int size = 0;
for (Layer l = layers; l != null; l = l.next) { for (Layer l = layers; l != null; l = l.next) {
if (l.type == Layer.LINE) if (l.type == Layer.LINE)
size += l.verticesCnt * LINE_VERTEX_SHORTS; size += l.verticesCnt * LINE_VERTEX_SHORTS;
@ -90,7 +93,7 @@ public class Layers {
} }
for (Layer l = symbolLayers; l != null; l = l.next) { for (Layer l = textureLayers; l != null; l = l.next) {
size += l.verticesCnt * TEXTURE_VERTEX_SHORTS; size += l.verticesCnt * TEXTURE_VERTEX_SHORTS;
} }
@ -109,10 +112,10 @@ public class Layers {
lineOffset = sbuf.position() * 2; // * short-bytes lineOffset = sbuf.position() * 2; // * short-bytes
addLayerItems(sbuf, layers, Layer.LINE, 0); addLayerItems(sbuf, layers, Layer.LINE, 0);
symbolOffset = sbuf.position() * 2; // * short-bytes texOffset = sbuf.position() * 2; // * short-bytes
for (Layer l = symbolLayers; l != null; l = l.next) { for (Layer l = textureLayers; l != null; l = l.next) {
SymbolLayer sl = (SymbolLayer) l; TextureLayer sl = (TextureLayer) l;
sl.compile(sbuf); sl.compile(sbuf);
} }
} }
@ -148,19 +151,25 @@ public class Layers {
} }
public void clear() { public void clear() {
// FIXME collect pool and add as a whole
for (Layer l = layers; l != null; l = l.next) { while (layers != null) {
Layer l = layers;
if (l.pool != null) { if (l.pool != null) {
VertexPool.release(l.pool); VertexPool.release(l.pool);
l.pool = null; l.pool = null;
l.curItem = null; l.curItem = null;
} }
layers = layers.next;
} }
for (Layer l = symbolLayers; l != null; l = l.next) { while (textureLayers != null) {
SymbolLayer sl = (SymbolLayer) l; textureLayers.clear();
if (sl.textures != null)
TextureObject.release(sl.textures); // TextureLayer sl = (TextureLayer) textureLayers;
// if (sl.textures != null)
// TextureObject.release(sl.textures);
textureLayers = textureLayers.next;
} }
} }
} }

View File

@ -35,20 +35,11 @@ public final class LineLayer extends Layer {
public Line line; public Line line;
public float width; public float width;
// boolean isOutline;
LineLayer(int layer) { LineLayer(int layer) {
this.layer = layer; this.layer = layer;
this.type = Layer.LINE; this.type = Layer.LINE;
} }
// 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) { 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)
@ -272,6 +263,7 @@ public final class LineLayer extends Layer {
prevY = y; prevY = y;
x = nextX; x = nextX;
y = nextY; y = nextY;
boolean flip = false;
for (;;) { for (;;) {
if (ipos < pos + length) { if (ipos < pos + length) {
@ -314,10 +306,15 @@ public final class LineLayer extends Layer {
ux = (ux / a); ux = (ux / a);
uy = (uy / a); uy = (uy / a);
// hack to avoid miter going to infinity // avoid miter going to infinity...
if (ux > 2.0f || ux < -2.0f || uy > 2.0f || uy < -2.0f) { if (ux > 4.0f || ux < -4.0f || uy > 4.0f || uy < -4.0f) {
ux = -wy; ux = vx - wx;
uy = wx; uy = vy - wy;
a = -wy * ux + wx * uy;
ux = (ux / a);
uy = (uy / a);
flip = !flip;
} }
} }
@ -327,6 +324,10 @@ public final class LineLayer extends Layer {
ddx = (int) (ux * DIR_SCALE); ddx = (int) (ux * DIR_SCALE);
ddy = (int) (uy * DIR_SCALE); ddy = (int) (uy * DIR_SCALE);
if (flip) {
ddx *= -1;
ddy *= -1;
}
if (opos == VertexPoolItem.SIZE) { if (opos == VertexPoolItem.SIZE) {
si = si.next = VertexPool.get(); si = si.next = VertexPool.get();
v = si.vertices; v = si.vertices;
@ -382,6 +383,11 @@ public final class LineLayer extends Layer {
ddx = (int) (ux * DIR_SCALE); ddx = (int) (ux * DIR_SCALE);
ddy = (int) (uy * DIR_SCALE); ddy = (int) (uy * DIR_SCALE);
if (flip) {
ddx *= -1;
ddy *= -1;
}
v[opos++] = ox; v[opos++] = ox;
v[opos++] = oy; v[opos++] = oy;
v[opos++] = (short) (0 | ddx & DIR_MASK); v[opos++] = (short) (0 | ddx & DIR_MASK);
@ -410,6 +416,11 @@ public final class LineLayer extends Layer {
dx = (short) (0 | ddx & DIR_MASK); dx = (short) (0 | ddx & DIR_MASK);
dy = (short) (0 | ddy & DIR_MASK); dy = (short) (0 | ddy & DIR_MASK);
if (flip) {
ddx *= -1;
ddy *= -1;
}
v[opos++] = ox; v[opos++] = ox;
v[opos++] = oy; v[opos++] = oy;
v[opos++] = dx; v[opos++] = dx;
@ -427,6 +438,11 @@ public final class LineLayer extends Layer {
dx = (short) (2 | ddx & DIR_MASK); dx = (short) (2 | ddx & DIR_MASK);
dy = (short) (0 | ddy & DIR_MASK); dy = (short) (0 | ddy & DIR_MASK);
if (flip) {
ddx *= -1;
ddy *= -1;
}
v[opos++] = ox; v[opos++] = ox;
v[opos++] = oy; v[opos++] = oy;
v[opos++] = dx; v[opos++] = dx;
@ -457,7 +473,10 @@ public final class LineLayer extends Layer {
ddx = (int) ((ux - vx) * DIR_SCALE); ddx = (int) ((ux - vx) * DIR_SCALE);
ddy = (int) ((uy - vy) * DIR_SCALE); ddy = (int) ((uy - vy) * DIR_SCALE);
if (flip) {
ddx *= -1;
ddy *= -1;
}
v[opos++] = ox; v[opos++] = ox;
v[opos++] = oy; v[opos++] = oy;
v[opos++] = (short) (0 | ddx & DIR_MASK); v[opos++] = (short) (0 | ddx & DIR_MASK);
@ -472,6 +491,10 @@ public final class LineLayer extends Layer {
// add last vertex twice // add last vertex twice
ddx = (int) (-(ux + vx) * DIR_SCALE); ddx = (int) (-(ux + vx) * DIR_SCALE);
ddy = (int) (-(uy + vy) * DIR_SCALE); ddy = (int) (-(uy + vy) * DIR_SCALE);
if (flip) {
ddx *= -1;
ddy *= -1;
}
dx = (short) (2 | ddx & DIR_MASK); dx = (short) (2 | ddx & DIR_MASK);
dy = (short) (1 | ddy & DIR_MASK); dy = (short) (1 | ddy & DIR_MASK);

View File

@ -17,12 +17,9 @@ package org.oscim.renderer.layer;
import java.nio.ShortBuffer; import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject; import org.oscim.renderer.TextureObject;
import org.oscim.renderer.TextureRenderer;
import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.RectF;
import android.opengl.GLUtils;
import android.util.Log; import android.util.Log;
// TODO share one static texture for all poi map symabols // TODO share one static texture for all poi map symabols
@ -30,28 +27,17 @@ import android.util.Log;
public final class SymbolLayer extends TextureLayer { public final class SymbolLayer extends TextureLayer {
private static String TAG = SymbolLayer.class.getSimpleName(); private static String TAG = SymbolLayer.class.getSimpleName();
private final static int TEXTURE_WIDTH = 256; private final static int TEXTURE_WIDTH = TextureObject.TEXTURE_WIDTH;
private final static int TEXTURE_HEIGHT = 256; private final static int TEXTURE_HEIGHT = TextureObject.TEXTURE_HEIGHT;
private final static float SCALE = 8.0f; private final static float SCALE = 8.0f;
private static short[] mVertices; private static short[] mVertices;
private static Bitmap mBitmap;
private static Canvas mCanvas;
private static int mBitmapFormat;
private static int mBitmapType;
SymbolItem symbols; SymbolItem symbols;
public SymbolLayer() { public SymbolLayer() {
if (mBitmap == null) { if (mVertices == null)
mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, mVertices = new short[TextureRenderer.MAX_ITEMS * 24];
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) { public void addSymbol(SymbolItem item) {
@ -73,19 +59,23 @@ public final class SymbolLayer extends TextureLayer {
} }
private final static int LBIT_MASK = 0xfffffffe; private final static int LBIT_MASK = 0xfffffffe;
private final RectF mRect = new RectF();
// TODO ... reuse texture when only symbol position changed // TODO ... reuse texture when only symbol position changed
@Override
public void compile(ShortBuffer sbuf) { public void compile(ShortBuffer sbuf) {
short numIndices = 0;
short offsetIndices = 0;
int pos = 0; int pos = 0;
short buf[] = mVertices; short buf[] = mVertices;
int bufLen = buf.length;
int advanceY = 0; int advanceY = 0;
float x = 0; float x = 0;
float y = 0; float y = 0;
mBitmap.eraseColor(Color.TRANSPARENT); Canvas canvas = TextureObject.getCanvas();
for (SymbolItem it = symbols; it != null;) { for (SymbolItem it = symbols; it != null;) {
@ -103,35 +93,31 @@ public final class SymbolLayer extends TextureLayer {
if (y + height > TEXTURE_HEIGHT) { if (y + height > TEXTURE_HEIGHT) {
Log.d(TAG, "reached max symbols"); Log.d(TAG, "reached max symbols");
// need to sync bitmap upload somehow???
TextureObject to = TextureObject.get(); TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
TextureObject.uploadTexture(to, mBitmap, offsetIndices = numIndices;
mBitmapFormat, mBitmapType,
TEXTURE_WIDTH, TEXTURE_HEIGHT);
to.next = textures; to.next = textures;
textures = to; textures = to;
sbuf.put(buf, 0, pos); sbuf.put(buf, 0, pos);
pos = 0; pos = 0;
x = 0;
y = 0;
advanceY = (int) height;
} }
} }
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); canvas.drawBitmap(it.bitmap, x, y, null);
// mCanvas.drawBitmap(it.bitmap, x, y, null);
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;
x1 = x3 = (short) (SCALE * (-hw));
x2 = x4 = (short) (SCALE * (hw));
y1 = y3 = (short) (SCALE * (hh)); short x1 = (short) (SCALE * (-hw));
y2 = y4 = (short) (SCALE * (-hh)); short x2 = (short) (SCALE * (hw));
short y1 = (short) (SCALE * (hh));
short y2 = (short) (SCALE * (-hh));
short u1 = (short) (SCALE * x); short u1 = (short) (SCALE * x);
short v1 = (short) (SCALE * y); short v1 = (short) (SCALE * y);
@ -157,40 +143,43 @@ public final class SymbolLayer extends TextureLayer {
buf[pos++] = y1; buf[pos++] = y1;
buf[pos++] = u1; buf[pos++] = u1;
buf[pos++] = v2; buf[pos++] = v2;
// top-right // top-right
buf[pos++] = tx; buf[pos++] = tx;
buf[pos++] = ty; buf[pos++] = ty;
buf[pos++] = x2; buf[pos++] = x2;
buf[pos++] = y3; buf[pos++] = y1;
buf[pos++] = u2; buf[pos++] = u2;
buf[pos++] = v2; buf[pos++] = v2;
// bot-right // bot-right
buf[pos++] = tx; buf[pos++] = tx;
buf[pos++] = ty; buf[pos++] = ty;
buf[pos++] = x4; buf[pos++] = x2;
buf[pos++] = y4; buf[pos++] = y2;
buf[pos++] = u2; buf[pos++] = u2;
buf[pos++] = v1; buf[pos++] = v1;
// bot-left // bot-left
buf[pos++] = tx; buf[pos++] = tx;
buf[pos++] = ty; buf[pos++] = ty;
buf[pos++] = x3; buf[pos++] = x1;
buf[pos++] = y2; buf[pos++] = y2;
buf[pos++] = u1; buf[pos++] = u1;
buf[pos++] = v1; buf[pos++] = v1;
x += width + 1; // six elements used to draw the four vertices
numIndices += 6;
// FIXME this does not work, need to draw bitmap on next
// texture...
if (pos == bufLen) {
sbuf.put(buf, 0, pos);
pos = 0;
}
x += width;
} }
} }
TextureObject to = TextureObject.get(); TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
TextureObject.uploadTexture(to, mBitmap,
mBitmapFormat, mBitmapType,
TEXTURE_WIDTH, TEXTURE_HEIGHT);
to.next = textures; to.next = textures;
textures = to; textures = to;

View File

@ -0,0 +1,77 @@
/*
* 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 org.oscim.theme.renderinstruction.Text;
public class TextItem {
private static Object lock = new Object();
private static TextItem pool;
public static TextItem get() {
synchronized (lock) {
if (pool == null)
return new TextItem();
TextItem ti = pool;
pool = pool.next;
ti.next = null;
return ti;
}
}
public static void release(TextItem ti) {
if (ti == null)
return;
synchronized (lock) {
while (ti != null) {
TextItem next = ti.next;
ti.next = pool;
pool = ti;
ti = next;
}
}
}
public TextItem set(float x, float y, String string, Text text) {
this.x = x;
this.y = y;
this.string = string;
this.text = text;
this.width = text.paint.measureText(string);
return this;
}
public TextItem move(TextItem ti, float dx, float dy) {
this.x = dx + ti.x;
this.y = dy + ti.y;
this.string = ti.string;
this.text = ti.text;
this.width = ti.width;
return this;
}
public TextItem next;
public float x, y;
public String string;
public Text text;
public float width;
public short x1, y1, x2, y2;
// public byte placement
}

View File

@ -14,9 +14,218 @@
*/ */
package org.oscim.renderer.layer; package org.oscim.renderer.layer;
import org.oscim.renderer.TextItem; import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject;
import org.oscim.renderer.TextureRenderer;
import android.graphics.Canvas;
import android.util.FloatMath;
public final class TextLayer extends TextureLayer { public final class TextLayer extends TextureLayer {
private static String TAG = SymbolLayer.class.getSimpleName();
private final static int TEXTURE_WIDTH = TextureObject.TEXTURE_WIDTH;
private final static int TEXTURE_HEIGHT = TextureObject.TEXTURE_HEIGHT;
private final static float SCALE = 8.0f;
private final static int LBIT_MASK = 0xfffffffe;
private static short[] mVertices;
private static int mFontPadX = 1;
private static int mFontPadY = 1;
TextItem labels; TextItem labels;
public TextItem getLabels() {
return labels;
}
public TextLayer() {
if (mVertices == null)
mVertices = new short[TextureRenderer.MAX_ITEMS * 24];
}
public void addText(TextItem item) {
verticesCnt += 4;
TextItem it = labels;
for (; it != null; it = it.next) {
if (it.text == item.text) {
item.next = it.next;
it.next = item;
return;
}
}
item.next = labels;
labels = item;
}
@Override
public void compile(ShortBuffer sbuf) {
// int numLabel = 0;
short numIndices = 0;
short offsetIndices = 0;
int pos = 0;
short buf[] = mVertices;
int bufLen = buf.length;
int advanceY = 0;
float x = 0;
float y = 0;
float yy;
Canvas canvas = TextureObject.getCanvas();
for (TextItem it = labels; it != null; it = it.next) {
// numLabel++;
float width = it.width + 2 * mFontPadX;
float height = (int) (it.text.fontHeight) + 2 * mFontPadY + 0.5f;
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 labels " + numLabel);
// need to sync bitmap upload somehow???
TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
offsetIndices = numIndices;
to.next = textures;
textures = to;
sbuf.put(buf, 0, pos);
pos = 0;
x = 0;
y = 0;
advanceY = (int) height;
// clear bitmap, TODO rotate two canvas to reduce the chance
// of having upload lock draing to the canvas?
canvas = TextureObject.getCanvas();
}
}
yy = y + (height - 1) - it.text.fontDescent - mFontPadY;
if (it.text.stroke != null)
canvas.drawText(it.string, x + it.width / 2, yy, it.text.stroke);
canvas.drawText(it.string, x + it.width / 2, yy, it.text.paint);
// FIXME !!!
if (width > TEXTURE_WIDTH)
width = TEXTURE_WIDTH;
float hw = width / 2.0f;
float hh = height / 2.0f;
short x1, x2, x3, x4, y1, y3, y2, y4;
if (it.text.caption) {
x1 = x3 = (short) (SCALE * -hw);
x2 = x4 = (short) (SCALE * hw);
y1 = y2 = (short) (SCALE * hh);
y3 = y4 = (short) (SCALE * -hh);
// x1 = x3 = (short) (0);
// x2 = x4 = (short) (SCALE * width);
} else {
float vx = it.x1 - it.x2;
float vy = it.y1 - it.y2;
float a = FloatMath.sqrt(vx * vx + vy * vy);
vx = vx / a;
vy = vy / a;
float ux = -vy;
float uy = vx;
float hh2 = hh + it.text.fontDescent / 2;
hh -= it.text.fontDescent / 2;
x1 = (short) (SCALE * (vx * hw - ux * hh));
y1 = (short) (SCALE * (vy * hw - uy * hh));
x2 = (short) (SCALE * (-vx * hw - ux * hh));
y2 = (short) (SCALE * (-vy * hw - uy * hh));
x4 = (short) (SCALE * (-vx * hw + ux * hh2));
y4 = (short) (SCALE * (-vy * hw + uy * hh2));
x3 = (short) (SCALE * (vx * hw + ux * hh2));
y3 = (short) (SCALE * (vy * hw + uy * hh2));
}
short u1 = (short) (SCALE * x);
short v1 = (short) (SCALE * y);
short u2 = (short) (SCALE * (x + width));
short v2 = (short) (SCALE * (y + height));
// add vertices
int tmp = (int) (SCALE * it.x) & LBIT_MASK;
short tx = (short) (tmp | (it.text.caption ? 1 : 0));
short ty = (short) (SCALE * it.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++] = y2;
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++] = y3;
buf[pos++] = u1;
buf[pos++] = v1;
// six indices to draw the four vertices
numIndices += 6;
// FIXME this does not work, need to draw bitmap on next
// texture...
if (pos == bufLen) {
sbuf.put(buf, 0, pos);
pos = 0;
}
x += width + 1;
}
TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
to.next = textures;
textures = to;
sbuf.put(buf, 0, pos);
// Log.d(TAG, "added labels " + numLabel);
}
@Override
protected void clear() {
TextureObject.release(textures);
TextItem.release(labels);
}
} }

View File

@ -14,9 +14,18 @@
*/ */
package org.oscim.renderer.layer; package org.oscim.renderer.layer;
import java.nio.ShortBuffer;
import org.oscim.renderer.TextureObject; import org.oscim.renderer.TextureObject;
public abstract class TextureLayer extends Layer { public abstract class TextureLayer extends Layer {
public TextureObject textures; public TextureObject textures;
/**
* @param sbuf
* buffer to add vertices
*/
void compile(ShortBuffer sbuf) {
}
} }

View File

@ -17,6 +17,7 @@ package org.oscim.theme.renderinstruction;
import org.oscim.core.Tag; import org.oscim.core.Tag;
import org.oscim.theme.IRenderCallback; import org.oscim.theme.IRenderCallback;
import org.oscim.theme.RenderThemeHandler; import org.oscim.theme.RenderThemeHandler;
import org.oscim.utils.GlUtils;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import android.graphics.Color; import android.graphics.Color;
@ -88,11 +89,7 @@ public final class Area extends RenderInstruction {
blend = -1; blend = -1;
strokeWidth = 0; strokeWidth = 0;
color = new float[4]; color = GlUtils.colorToFloatP(fill);
color[3] = (fill >> 24 & 0xff) / 255.0f;
color[0] = (fill >> 16 & 0xff) / 255.0f * color[3];
color[1] = (fill >> 8 & 0xff) / 255.0f * color[3];
color[2] = (fill >> 0 & 0xff) / 255.0f * color[3];
} }
/** /**
@ -128,41 +125,13 @@ public final class Area extends RenderInstruction {
// Shader shader = BitmapUtils.createBitmapShader(src); // Shader shader = BitmapUtils.createBitmapShader(src);
// paintFill.setShader(shader); // paintFill.setShader(shader);
// } // }
// paintFill.setStyle(Style.FILL);
// paintFill.setColor(fill);
// paintFill.setStrokeCap(Cap.ROUND);
// }
//
// if (stroke == Color.TRANSPARENT) {
// paintOutline = null;
// } else {
// paintOutline = new Paint(Paint.ANTI_ALIAS_FLAG);
// paintOutline.setStyle(Style.STROKE);
// paintOutline.setColor(stroke);
// paintOutline.setStrokeCap(Cap.ROUND);
// }
// if (stroke == Color.TRANSPARENT) { color = GlUtils.colorToFloatP(fill);
// stroke = null;
// } else{
// stroke = new Line()
// }
color = new float[4]; if (blend > 0)
color[3] = (fill >> 24 & 0xff) / 255.0f; blendColor = GlUtils.colorToFloatP(blendFill);
color[0] = (fill >> 16 & 0xff) / 255.0f * color[3]; else
color[1] = (fill >> 8 & 0xff) / 255.0f * color[3];
color[2] = (fill >> 0 & 0xff) / 255.0f * color[3];
if (blend > 0) {
blendColor = new float[4];
blendColor[3] = (blendFill >> 24 & 0xff) / 255.0f;
blendColor[0] = (blendFill >> 16 & 0xff) / 255.0f * blendColor[3];
blendColor[1] = (blendFill >> 8 & 0xff) / 255.0f * blendColor[3];
blendColor[2] = (blendFill >> 0 & 0xff) / 255.0f * blendColor[3];
} else {
blendColor = null; blendColor = null;
}
this.blend = blend; this.blend = blend;
this.strokeWidth = strokeWidth; this.strokeWidth = strokeWidth;
@ -183,32 +152,10 @@ public final class Area extends RenderInstruction {
// } // }
public String style; public String style;
/**
*
*/
private final int level; private final int level;
/**
*
*/
// public final Paint paintFill;
/**
*
*/
// public final Paint paintOutline;
/**
*
*/
public final float strokeWidth; public final float strokeWidth;
/**
*
*/
public final float color[]; public final float color[];
/**
*
*/
public final int fade; public final int fade;
public final float blendColor[]; public final float blendColor[];
public final int blend; public final int blend;
} }

View File

@ -20,6 +20,7 @@ import java.util.regex.Pattern;
import org.oscim.core.Tag; import org.oscim.core.Tag;
import org.oscim.theme.IRenderCallback; import org.oscim.theme.IRenderCallback;
import org.oscim.theme.RenderThemeHandler; import org.oscim.theme.RenderThemeHandler;
import org.oscim.utils.GlUtils;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import android.graphics.Color; import android.graphics.Color;
@ -121,11 +122,7 @@ public final class Line extends RenderInstruction {
this.fixed = true; this.fixed = true;
this.fade = -1; this.fade = -1;
this.stipple = 2; this.stipple = 2;
color = new float[4]; color = GlUtils.colorToFloatP(stroke);
color[3] = (stroke >> 24 & 0xff) / 255.0f;
color[0] = (stroke >> 16 & 0xff) / 255.0f * color[3];
color[1] = (stroke >> 8 & 0xff) / 255.0f * color[3];
color[2] = (stroke >> 0 & 0xff) / 255.0f * color[3];
} }
private static void validate(float strokeWidth) { private static void validate(float strokeWidth) {
@ -215,11 +212,7 @@ public final class Line extends RenderInstruction {
this.cap = strokeLinecap; this.cap = strokeLinecap;
color = new float[4]; color = GlUtils.colorToFloatP(stroke);
color[3] = (stroke >> 24 & 0xff) / 255.0f;
color[0] = (stroke >> 16 & 0xff) / 255.0f * color[3];
color[1] = (stroke >> 8 & 0xff) / 255.0f * color[3];
color[2] = (stroke >> 0 & 0xff) / 255.0f * color[3];
this.width = strokeWidth; this.width = strokeWidth;
this.level = level; this.level = level;

View File

@ -53,6 +53,7 @@ public final class Text extends RenderInstruction {
String style = null; String style = null;
// boolean caption = false; // boolean caption = false;
float dy = 0; float dy = 0;
for (int i = 0; i < attributes.getLength(); ++i) { for (int i = 0; i < attributes.getLength(); ++i) {
String name = attributes.getLocalName(i); String name = attributes.getLocalName(i);
String value = attributes.getValue(i); String value = attributes.getValue(i);
@ -82,10 +83,27 @@ public final class Text extends RenderInstruction {
} }
validate(elementName, textKey, fontSize, strokeWidth); validate(elementName, textKey, fontSize, strokeWidth);
Typeface typeface = Typeface.create(fontFamily.toTypeface(), fontStyle.toInt());
Typeface typeface = null;
if (fontFamily == FontFamily.DEFAULT) {
if (fontStyle == FontStyle.NORMAL)
typeface = typefaceNormal;
else if (fontStyle == FontStyle.BOLD)
typeface = typefaceBold;
}
if (typeface == null)
typeface = Typeface.create(fontFamily.toTypeface(), fontStyle.toInt());
return new Text(style, textKey, typeface, fontSize, fill, stroke, strokeWidth, dy, caption); return new Text(style, textKey, typeface, fontSize, fill, stroke, strokeWidth, dy, caption);
} }
private static Typeface typefaceNormal = Typeface.create(FontFamily.DEFAULT.toTypeface(),
FontStyle.NORMAL.toInt());
private static Typeface typefaceBold = Typeface.create(FontFamily.DEFAULT.toTypeface(),
FontStyle.BOLD.toInt());
private static void validate(String elementName, String textKey, float fontSize, private static void validate(String elementName, String textKey, float fontSize,
float strokeWidth) { float strokeWidth) {
if (textKey == null) { if (textKey == null) {
@ -110,9 +128,15 @@ public final class Text extends RenderInstruction {
public final boolean caption; public final boolean caption;
public final float dy; public final float dy;
public static Text createText(float fontSize, float strokeWidth, int fill, int outline,
boolean billboard) {
return new Text("", "", typefaceNormal, fontSize, fill, outline, strokeWidth, 0, billboard);
}
private Text(String style, String textKey, Typeface typeface, float fontSize, private Text(String style, String textKey, Typeface typeface, float fontSize,
int fill, int outline, float strokeWidth, float dy, boolean caption) { int fill, int outline, float strokeWidth, float dy, boolean caption) {
super(); // super();
this.style = style; this.style = style;
this.textKey = textKey; this.textKey = textKey;
@ -123,19 +147,21 @@ public final class Text extends RenderInstruction {
paint.setTextAlign(Align.CENTER); paint.setTextAlign(Align.CENTER);
paint.setTypeface(typeface); paint.setTypeface(typeface);
paint.setColor(fill); paint.setColor(fill);
paint.setTextSize(fontSize);
stroke = new Paint(Paint.ANTI_ALIAS_FLAG); if (strokeWidth > 0) {
stroke.setStyle(Style.STROKE); stroke = new Paint(Paint.ANTI_ALIAS_FLAG);
stroke.setTextAlign(Align.CENTER); stroke.setStyle(Style.STROKE);
stroke.setTypeface(typeface); stroke.setTextAlign(Align.CENTER);
stroke.setColor(outline); stroke.setTypeface(typeface);
stroke.setStrokeWidth(strokeWidth); stroke.setColor(outline);
stroke.setStrokeWidth(strokeWidth);
stroke.setTextSize(fontSize);
} else
stroke = null;
this.fontSize = fontSize; this.fontSize = fontSize;
paint.setTextSize(fontSize);
stroke.setTextSize(fontSize);
FontMetrics fm = paint.getFontMetrics(); FontMetrics fm = paint.getFontMetrics();
fontHeight = FloatMath.ceil(Math.abs(fm.bottom) + Math.abs(fm.top)); fontHeight = FloatMath.ceil(Math.abs(fm.bottom) + Math.abs(fm.top));
fontDescent = FloatMath.ceil(Math.abs(fm.descent)); fontDescent = FloatMath.ceil(Math.abs(fm.descent));

View File

@ -47,4 +47,11 @@ public class FastMath {
} }
return r; return r;
} }
public static float pow(int pow) {
if (pow == 0)
return 1;
return (pow > 0 ? (1 << pow) : (1.0f / (1 << -pow)));
}
} }

View File

@ -157,6 +157,24 @@ public class GlUtils {
GLES20.glUniform4fv(handle, 1, c, 0); GLES20.glUniform4fv(handle, 1, c, 0);
else else
glUniform4f(handle, c[0] * alpha, c[1] * alpha, c[2] * alpha, c[3] * alpha); glUniform4f(handle, c[0] * alpha, c[1] * alpha, c[2] * alpha, c[3] * alpha);
}
public static float[] colorToFloat(int color) {
float[] c = new float[4];
c[3] = (color >> 24 & 0xff) / 255.0f;
c[0] = (color >> 16 & 0xff) / 255.0f;
c[1] = (color >> 8 & 0xff) / 255.0f;
c[2] = (color >> 0 & 0xff) / 255.0f;
return c;
}
// premultiply alpha
public static float[] colorToFloatP(int color) {
float[] c = new float[4];
c[3] = (color >> 24 & 0xff) / 255.0f;
c[0] = (color >> 16 & 0xff) / 255.0f * c[3];
c[1] = (color >> 8 & 0xff) / 255.0f * c[3];
c[2] = (color >> 0 & 0xff) / 255.0f * c[3];
return c;
} }
} }

View File

@ -1,5 +1,6 @@
/* /*
* Copyright 2010, 2011, 2012 mapsforge.org * 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
@ -33,6 +34,7 @@ import org.oscim.database.OpenResult;
import org.oscim.generator.JobQueue; import org.oscim.generator.JobQueue;
import org.oscim.generator.JobTile; import org.oscim.generator.JobTile;
import org.oscim.generator.MapWorker; import org.oscim.generator.MapWorker;
import org.oscim.renderer.GLRenderer;
import org.oscim.renderer.MapRenderer; import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.TileGenerator; import org.oscim.renderer.TileGenerator;
import org.oscim.theme.ExternalRenderTheme; import org.oscim.theme.ExternalRenderTheme;
@ -58,7 +60,7 @@ public class MapView extends FrameLayout {
public static final boolean debugFrameTime = false; public static final boolean debugFrameTime = false;
public static final boolean testRegionZoom = false; public static final boolean testRegionZoom = false;
public static final boolean staticLabeling = true; // public static final boolean staticLabeling = false;
private static final boolean debugDatabase = false; private static final boolean debugDatabase = false;
@ -438,8 +440,9 @@ public class MapView extends FrameLayout {
try { try {
inputStream = theme.getRenderThemeAsStream(); inputStream = theme.getRenderThemeAsStream();
RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream); RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream);
mMapRenderer.setRenderTheme(t); // FIXME somehow...
mMapWorkers[0].getMapGenerator().setRenderTheme(t); GLRenderer.setRenderTheme(t);
TileGenerator.setRenderTheme(t);
return true; return true;
} catch (ParserConfigurationException e) { } catch (ParserConfigurationException e) {
Log.e(TAG, e.getMessage()); Log.e(TAG, e.getMessage());

View File

@ -523,7 +523,7 @@ public class MapViewPosition {
mMapScale = newScale; mMapScale = newScale;
} }
if (pivotY != 0 || pivotY != 0) if (pivotX != 0 || pivotY != 0)
moveMap(pivotX * (1.0f - scale), moveMap(pivotX * (1.0f - scale),
pivotY * (1.0f - scale)); pivotY * (1.0f - scale));

View File

@ -116,9 +116,10 @@ public class MapZoomControls {
private final Handler mZoomControlsHideHandler; private final Handler mZoomControlsHideHandler;
private byte mZoomLevelMax; private byte mZoomLevelMax;
private byte mZoomLevelMin; private byte mZoomLevelMin;
private MapView mMapView; private final MapView mMapView;
MapZoomControls(Context context, final MapView mapView) { MapZoomControls(Context context, final MapView mapView) {
mMapView = mapView;
mZoomControls = new ZoomControls(context); mZoomControls = new ZoomControls(context);
mShowMapZoomControls = true; mShowMapZoomControls = true;
mZoomLevelMax = DEFAULT_ZOOM_LEVEL_MAX; mZoomLevelMax = DEFAULT_ZOOM_LEVEL_MAX;

View File

@ -12,34 +12,8 @@
* 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.renderer; package org.oscim.view;
import org.oscim.theme.renderinstruction.Text; public class OverlayManager {
public class TextItem {
TextItem next;
final float x, y;
final String string;
final Text text;
final float width;
short x1, y1, x2, y2;
public TextItem(float x, float y, String string, Text text) {
this.x = x;
this.y = y;
this.string = string;
this.text = text;
this.width = text.paint.measureText(string);
}
public TextItem(float x, float y, String string, Text text, float width) {
this.x = x;
this.y = y;
this.string = string;
this.text = text;
this.width = width;
}
} }

View File

@ -18,6 +18,8 @@ import org.oscim.core.Tile;
import android.content.Context; import android.content.Context;
import android.os.CountDownTimer; import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
@ -53,6 +55,9 @@ final class TouchHandler
private boolean mMoveStart; private boolean mMoveStart;
private boolean mBeginRotate; private boolean mBeginRotate;
private boolean mBeginTilt; private boolean mBeginTilt;
private boolean mLongPress;
private long mLongPressTime;
private float mPosX; private float mPosX;
private float mPosY; private float mPosY;
private double mAngle; private double mAngle;
@ -124,6 +129,7 @@ final class TouchHandler
private boolean onActionCancel() { private boolean onActionCancel() {
mActivePointerId = INVALID_POINTER_ID; mActivePointerId = INVALID_POINTER_ID;
mLongPress = true;
return true; return true;
} }
@ -174,6 +180,12 @@ final class TouchHandler
mPosX = event.getX(id); mPosX = event.getX(id);
mPosY = event.getY(id); mPosY = event.getY(id);
if (mLongPress) {
mMapPosition.scaleMap(1 - moveY / 100, 0, 0);
mMapView.redrawMap();
return true;
}
if (!scaling) { if (!scaling) {
mMapPosition.moveMap(moveX, moveY); mMapPosition.moveMap(moveX, moveY);
mMapView.redrawMap(); mMapView.redrawMap();
@ -207,7 +219,16 @@ final class TouchHandler
if (!mBeginRotate && !mBeginScale && !mBeginTilt) { if (!mBeginRotate && !mBeginScale && !mBeginTilt) {
if (Math.abs(r) > 0.03) if (Math.abs(r) > 0.03)
mBeginRotate = true; mBeginRotate = true;
} else if (mBeginRotate) { }
// quick way to prevent flipping...
// Log.d("", "rotation " + rad + " " + r);
if (Math.abs(r) > 0.1) {
rad = mAngle;
r = 0;
}
if (mBeginRotate) {
double rsin = Math.sin(r); double rsin = Math.sin(r);
double rcos = Math.cos(r); double rcos = Math.cos(r);
@ -260,6 +281,8 @@ final class TouchHandler
} }
multi--; multi--;
mLongPress = false;
return true; return true;
} }
@ -272,6 +295,28 @@ final class TouchHandler
mActivePointerId = INVALID_POINTER_ID; mActivePointerId = INVALID_POINTER_ID;
mScaling = false; mScaling = false;
multi = 0; multi = 0;
// if (mLongPress && SystemClock.uptimeMillis() - mLongPressTime < 150)
// {
// mScrollX = (mPosX - (mMapView.getWidth() >> 1)) * 2f;
// mScrollY = (mPosY - (mMapView.getHeight() >> 1)) * 2f;
// mPrevScale = 0;
//
// mTimer = new CountDownTimer((int) SCALE_DURATION, 30) {
// @Override
// public void onTick(long tick) {
// scale2(tick);
// }
//
// @Override
// public void onFinish() {
// scale2(0);
// }
// }.start();
// }
mLongPress = false;
return true; return true;
} }
@ -292,7 +337,6 @@ final class TouchHandler
} }
fling = false; fling = false;
} }
// Log.d("mapsforge", "onDown");
return true; return true;
} }
@ -353,6 +397,7 @@ final class TouchHandler
Log.d("mapsforge", "long press"); Log.d("mapsforge", "long press");
mMapView.mRegionLookup.updateRegion(-1, null); mMapView.mRegionLookup.updateRegion(-1, null);
} }
mLongPress = true;
} }
boolean scale2(long tick) { boolean scale2(long tick) {
@ -360,11 +405,12 @@ final class TouchHandler
fling = true; fling = true;
if (mPrevScale >= 1) if (mPrevScale >= 1)
return false; return false;
float adv = (SCALE_DURATION - tick) / SCALE_DURATION; float adv = (SCALE_DURATION - tick) / SCALE_DURATION;
adv = mInterpolator.getInterpolation(adv); adv = mInterpolator.getInterpolation(adv);
float scale = adv - mPrevScale; float scale = adv - mPrevScale;
mPrevScale += scale; mPrevScale += scale;
scale *= 0.75;
scale += 1; scale += 1;
adv += 1; adv += 1;
@ -379,26 +425,26 @@ final class TouchHandler
@Override @Override
public boolean onDoubleTap(MotionEvent e) { public boolean onDoubleTap(MotionEvent e) {
if (MapView.testRegionZoom) { if (MapView.testRegionZoom) {
Log.d("mapsforge", "double tap");
mMapView.mRegionLookup.updateRegion(1, mMapView.mRegionLookup.updateRegion(1,
mMapPosition.getOffsetPoint(mPosX, mPosY)); mMapPosition.getOffsetPoint(mPosX, mPosY));
} else { } else {
mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f; mLongPress = true;
mScrollY = (e.getY(0) - (mMapView.getHeight() >> 1)) * 2f; mLongPressTime = SystemClock.uptimeMillis();
mPrevScale = 0; // mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f;
// mScrollY = (e.getY(0) - (mMapView.getHeight() >> 1)) * 2f;
mTimer = new CountDownTimer((int) SCALE_DURATION, 30) { // mPrevScale = 0;
@Override //
public void onTick(long tick) { // mTimer = new CountDownTimer((int) SCALE_DURATION, 30) {
scale2(tick); // @Override
} // public void onTick(long tick) {
// scale2(tick);
@Override // }
public void onFinish() { //
scale(0); // @Override
} // public void onFinish() {
}.start(); // scale(0);
// }
// }.start();
} }
return true; return true;
} }
@ -513,4 +559,120 @@ final class TouchHandler
return true; return true;
} }
/*
* from CountDownTimer.java: Copyright (C) 2008 The Android Open Source
* Project Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may
* obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
* law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*/
final static class Timer {
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
final long mCountdownInterval;
long mStopTimeInFuture;
/**
* @param millisInFuture
* The number of millis in the future from the call to
* {@link #start()} until the countdown is done and
* {@link #onFinish()} is called.
* @param countDownInterval
* The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
public Timer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*/
public final void cancel() {
mHandler.removeMessages(MSG);
}
/**
* Start the countdown.
*
* @return ...
*/
public synchronized final Timer start() {
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* Callback fired on regular interval.
*
* @param millisUntilFinished
* The amount of time until finished.
*/
public void onTick(long millisUntilFinished) {
}
/**
* Callback fired when the time is up.
*/
public void onFinish() {
}
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (Timer.this) {
final long millisLeft = mStopTimeInFuture
- SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to
// execute
long delay = lastTickStart + mCountdownInterval
- SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval
// to
// complete, skip to next interval
while (delay < 0)
delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
} }