diff --git a/src/org/oscim/generator/JobQueue.java b/src/org/oscim/generator/JobQueue.java index 33c754e4..0911ec62 100644 --- a/src/org/oscim/generator/JobQueue.java +++ b/src/org/oscim/generator/JobQueue.java @@ -38,11 +38,10 @@ public class JobQueue { * the job to be added to this queue. */ public synchronized void setJobs(ArrayList tiles) { - mPriorityQueue.clear(); - // mPriorityQueue.addAll(tiles); + // mPriorityQueue.clear(); + for (int i = 0, n = tiles.size(); i < n; i++) { JobTile tile = tiles.get(i); - // tile.state = LOADING; tile.isLoading = true; mPriorityQueue.offer(tile); } @@ -52,11 +51,10 @@ public class JobQueue { * Removes all jobs from this queue. */ public synchronized void clear() { - for (int i = 0, n = mPriorityQueue.size(); i < n; i++) { - JobTile tile = mPriorityQueue.poll(); - // tile.state = 0; + for (JobTile tile : mPriorityQueue) { tile.isLoading = false; } + mPriorityQueue.clear(); } @@ -71,8 +69,6 @@ public class JobQueue { * @return the most important job from this queue or null, if empty. */ public synchronized JobTile poll() { - JobTile tile = mPriorityQueue.poll(); - - return tile; + return mPriorityQueue.poll(); } } diff --git a/src/org/oscim/generator/MapWorker.java b/src/org/oscim/generator/MapWorker.java index 8e3c94da..6983307e 100644 --- a/src/org/oscim/generator/MapWorker.java +++ b/src/org/oscim/generator/MapWorker.java @@ -61,9 +61,11 @@ public class MapWorker extends PausableThread { protected void doWork() { JobTile tile = mJobQueue.poll(); - if (mMapGenerator == null || tile == null) + if (tile == null) return; + // Log.d("...", "load: " + tile); + boolean success = mMapGenerator.executeJob(tile); if (!isInterrupted() && success) { diff --git a/src/org/oscim/renderer/BufferObject.java b/src/org/oscim/renderer/BufferObject.java index 5de24643..454dc015 100644 --- a/src/org/oscim/renderer/BufferObject.java +++ b/src/org/oscim/renderer/BufferObject.java @@ -19,15 +19,15 @@ import android.opengl.GLES20; class BufferObject { private static BufferObject pool; + static int counter; static synchronized BufferObject get() { - BufferObject bo; - if (pool == null) { + if (pool == null) return null; - } + counter--; - bo = pool; + BufferObject bo = pool; pool = pool.next; bo.next = null; return bo; @@ -64,6 +64,7 @@ class BufferObject { static synchronized void release(BufferObject bo) { bo.next = pool; pool = bo; + counter++; } // Note: only call from GL-Thread @@ -106,6 +107,7 @@ class BufferObject { bo.next = pool; pool = bo; } + counter = num; } int id; diff --git a/src/org/oscim/renderer/GLRenderer.java b/src/org/oscim/renderer/GLRenderer.java index bb44f6f6..da716945 100644 --- a/src/org/oscim/renderer/GLRenderer.java +++ b/src/org/oscim/renderer/GLRenderer.java @@ -37,6 +37,7 @@ import org.oscim.renderer.MapRenderer.TilesData; import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layers; import org.oscim.theme.RenderTheme; +import org.oscim.utils.GlUtils; import org.oscim.view.MapView; import org.oscim.view.MapViewPosition; @@ -44,7 +45,6 @@ import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import android.os.SystemClock; -import android.util.FloatMath; import android.util.Log; public class GLRenderer implements GLSurfaceView.Renderer { @@ -61,14 +61,11 @@ public class GLRenderer implements GLSurfaceView.Renderer { static int CACHE_TILES = CACHE_TILES_MAX; private final MapView mMapView; - private final MapViewPosition mMapViewPosition; - - private static MapPosition mMapPosition; - - // private static ArrayList mVBOs; - static int mWidth, mHeight; + private static MapViewPosition mMapViewPosition; + private static MapPosition mMapPosition; + private static int rotateBuffers = 2; private static ShortBuffer shortBuffer[]; private static short[] mFillCoords; @@ -82,16 +79,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { private static float[] mTileCoords = new float[8]; private static float[] mDebugCoords = new float[8]; - // mNextTiles is set by TileLoader and swapped with - // 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; + private static float[] mClearColor = null; // number of tiles drawn in one frame private static short mDrawCount = 0; @@ -99,7 +87,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { private static boolean mUpdateColor = false; // drawlock to synchronize Main- and GL-Thread - static ReentrantLock tilelock = new ReentrantLock(); + // static ReentrantLock tilelock = new ReentrantLock(); static ReentrantLock drawlock = new ReentrantLock(); // 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, // drawing, proxies and text layer. needing to add placeholder only // happens rarely, unless you live on Fidschi + /* package */static int mHolderCount; + /* package */static TilesData mDrawTiles; static boolean[] vertexArray = { false, false }; @@ -222,58 +212,18 @@ public class GLRenderer implements GLSurfaceView.Renderer { shortBuffer[i].put(mFillCoords, 0, 8); } - mUpdateTiles = false; + mOverlays = new ArrayList(); + + // mOverlays.add(new OverlayGrid(mapView)); + // mOverlays.add(new OverlayTest(mapView)); + mOverlays.add(new OverlayText(mapView)); + } private static ArrayList mOverlays; - /** - * Called by TileLoader when list of active tiles changed. the list is - * copied to mNextTiles to be used in next call to onDrawFrame - * - * @param tiles - * active tiles - * @param overlays - * ... - */ - static void updateTiles(TilesData tiles, ArrayList 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; + public static void setRenderTheme(RenderTheme t) { + mClearColor = GlUtils.colorToFloat(t.getMapBackground()); mUpdateColor = true; } @@ -282,19 +232,20 @@ public class GLRenderer implements GLSurfaceView.Renderer { private static boolean uploadLayers(Layers layers, BufferObject vbo, boolean addFill) { int newSize = layers.getSize(); - if (newSize == 0) - return false; + if (newSize == 0) { + Log.d(TAG, "empty"); + return true; + } GLES20.glBindBuffer(GL_ARRAY_BUFFER, vbo.id); // use multiple buffers to avoid overwriting buffer while current // data is uploaded (or rather the blocking which is probably done to // avoid overwriting) - if (uploadCnt >= rotateBuffers) { - uploadCnt = 0; - } + int curBuffer = uploadCnt % rotateBuffers; + uploadCnt++; - ShortBuffer sbuf = shortBuffer[uploadCnt]; + ShortBuffer sbuf = shortBuffer[curBuffer]; // add fill coordinates if (addFill) @@ -307,7 +258,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { .order(ByteOrder.nativeOrder()) .asShortBuffer(); - shortBuffer[uploadCnt] = sbuf; + shortBuffer[curBuffer] = sbuf; if (addFill) sbuf.put(mFillCoords, 0, 8); } else { @@ -345,18 +296,21 @@ public class GLRenderer implements GLSurfaceView.Renderer { mBufferMemoryUsage += vbo.size; } - uploadCnt++; - return true; } private static boolean uploadTileData(MapTile tile) { - - if (uploadLayers(tile.layers, tile.vbo, true)) - tile.isReady = true; + // synchronized (tile) { + tile.isReady = uploadLayers(tile.layers, tile.vbo, true); + if (!tile.isReady) { + tile.layers.clear(); + tile.layers = null; + } tile.newData = false; + // Log.d(TAG, "uploaded " + tile.isReady + " " + tile); + // } return tile.isReady; } @@ -427,11 +381,19 @@ public class GLRenderer implements GLSurfaceView.Renderer { @Override public void onDrawFrame(GL10 glUnused) { - long start = 0; + // prevent main thread recreating all tiles (updateMap) - // while rendering is going. not have seen this happen - // yet though - GLRenderer.drawlock.lock(); + // while rendering is going. + drawlock.lock(); + try { + draw(); + } finally { + drawlock.unlock(); + } + } + + static void draw() { + long start = 0; if (MapView.debugFrameTime) start = SystemClock.uptimeMillis(); @@ -451,24 +413,22 @@ public class GLRenderer implements GLSurfaceView.Renderer { | GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT); - if (mUpdateTiles) { - // get current tiles to draw - GLRenderer.tilelock.lock(); - mUpdateTiles = false; + int serial = 0; + if (mDrawTiles != null) + serial = mDrawTiles.serial; - TilesData tmp = mDrawTiles; - mDrawTiles = mNextTiles; - mNextTiles = tmp; - GLRenderer.tilelock.unlock(); - - // force update of mapPosition - mMapPosition.zoomLevel = -1; - } + // get current tiles to draw + mDrawTiles = MapRenderer.getActiveTiles(mDrawTiles); if (mDrawTiles == null || mDrawTiles.cnt == 0) { - GLRenderer.drawlock.unlock(); 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 // coordinates) @@ -476,10 +436,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { float[] coords = mTileCoords; boolean changed = mMapViewPosition.getMapPosition(mapPosition, coords); - for (Overlay overlay : mOverlays) { - overlay.update(mMapView); - } - int tileCnt = mDrawTiles.cnt; MapTile[] tiles = mDrawTiles.tiles; @@ -492,6 +448,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { // zoom-level changed. float div = scaleDiv(tiles[0]); + // transform screen coordinates to tile coordinates float s = Tile.TILE_SIZE; float scale = mapPosition.scale / div; float px = (float) mapPosition.x * div; @@ -511,9 +468,9 @@ public class GLRenderer implements GLSurfaceView.Renderer { // Log.d(TAG, "visible: " + tileCnt); 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++) { MapTile tile = tiles[i]; @@ -522,10 +479,10 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (!tile.isVisible) continue; - if (MapView.staticLabeling) { - if (tile.texture == null && TextRenderer.drawToTexture(tile)) - updateTextures++; - } + // if (MapView.staticLabeling) { + // if (tile.texture == null && TextRenderer.drawToTexture(tile)) + // updateTextures++; + // } if (tile.newData) { uploadTileData(tile); @@ -562,10 +519,18 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (uploadCnt > 0) checkBufferUsage(); - if (MapView.staticLabeling) { - if (updateTextures > 0) - TextRenderer.compileTextures(); + tilesChanged |= (uploadCnt > 0); + + // 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_POLYGON_OFFSET_FILL); @@ -593,41 +558,41 @@ public class GLRenderer implements GLSurfaceView.Renderer { mDrawCount = 0; mDrawSerial++; - if (MapView.staticLabeling) { - GLES20.glEnable(GL_BLEND); - int z = mapPosition.zoomLevel; - float s = mapPosition.scale; + GLES20.glEnable(GL_BLEND); - int zoomLevelDiff = Math.max(z - TileGenerator.STROKE_MAX_ZOOM_LEVEL, - 0); - float scale = (float) Math.pow(1.4, zoomLevelDiff); - if (scale < 1) - scale = 1; - - if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL) - TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix); - else - TextRenderer.beginDraw(1f / s, mProjMatrix); - - for (int i = 0; i < tileCnt; i++) { - MapTile t = tiles[i]; - if (!t.isVisible) - continue; - - if (t.holder == null) { - if (t.texture != null) { - setMatrix(mMVPMatrix, t, 1, false); - TextRenderer.drawTile(t, mMVPMatrix); - } - } else { - if (t.holder.texture != null) { - setMatrix(mMVPMatrix, t, 1, false); - TextRenderer.drawTile(t.holder, mMVPMatrix); - } - } - } - TextRenderer.endDraw(); - } + // if (MapView.staticLabeling) { + // int z = mapPosition.zoomLevel; + // float s = mapPosition.scale; + // int zoomLevelDiff = Math.max(z - TileGenerator.STROKE_MAX_ZOOM_LEVEL, + // 0); + // float scale = (float) Math.pow(1.4, zoomLevelDiff); + // if (scale < 1) + // scale = 1; + // + // if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL) + // TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix); + // else + // TextRenderer.beginDraw(1f / s, mProjMatrix); + // + // for (int i = 0; i < tileCnt; i++) { + // MapTile t = tiles[i]; + // if (!t.isVisible) + // continue; + // + // if (t.holder == null) { + // if (t.texture != null) { + // setMatrix(mMVPMatrix, t, 1, false); + // TextRenderer.drawTile(t, mMVPMatrix); + // } + // } else { + // if (t.holder.texture != null) { + // setMatrix(mMVPMatrix, t, 1, false); + // TextRenderer.drawTile(t.holder, mMVPMatrix); + // } + // } + // } + // TextRenderer.endDraw(); + // } // call overlay renderer for (Overlay overlay : mOverlays) { @@ -675,8 +640,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { mapPosition.viewMatrix, 0); PolygonRenderer.debugDraw(mMVPMatrix, mDebugCoords, 1); } - - GLRenderer.drawlock.unlock(); } // 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); - for (Layer l = tile.layers.symbolLayers; l != null;) { + for (Layer l = tile.layers.textureLayers; l != null;) { 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) { Log.d(TAG, "SurfaceChanged:" + width + " " + height); + mDrawTiles = null; + if (width <= 0 || height <= 0) return; @@ -849,7 +814,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { BufferObject.init(numVBO); // Set up textures - TextRenderer.setup(numTiles); + // TextRenderer.setup(numTiles); if (mClearColor != null) mUpdateColor = true; @@ -861,13 +826,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { 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 public void onSurfaceCreated(GL10 gl, EGLConfig config) { // String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS); @@ -875,7 +833,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { LineRenderer.init(); PolygonRenderer.init(); - TextRenderer.init(); + // TextRenderer.init(); TextureRenderer.init(); TextureObject.init(10); @@ -884,7 +842,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { GLES20.glClearStencil(0); GLES20.glDisable(GLES20.GL_CULL_FACE); GLES20.glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - mNewSurface = true; } diff --git a/src/org/oscim/renderer/MapRenderer.java b/src/org/oscim/renderer/MapRenderer.java index 2ebb8375..d30623ae 100644 --- a/src/org/oscim/renderer/MapRenderer.java +++ b/src/org/oscim/renderer/MapRenderer.java @@ -16,12 +16,13 @@ package org.oscim.renderer; import java.util.ArrayList; import java.util.Collections; +import java.util.concurrent.locks.ReentrantLock; import org.oscim.core.MapPosition; import org.oscim.core.Tile; import org.oscim.generator.JobTile; +import org.oscim.renderer.layer.TextItem; import org.oscim.renderer.layer.VertexPool; -import org.oscim.theme.RenderTheme; import org.oscim.utils.GlConfigChooser; import org.oscim.view.MapView; import org.oscim.view.MapViewPosition; @@ -75,22 +76,31 @@ public class MapRenderer extends GLSurfaceView { // GLThread static final class TilesData { int cnt = 0; - final MapTile[] tiles; + int serial; + MapTile[] tiles; + + TilesData() { + } TilesData(int 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() { @Override void setVisible(int y, int x1, int x2) { - MapTile[] tiles = mCurrentTiles.tiles; - int cnt = mCurrentTiles.cnt; - int max = mCurrentTiles.tiles.length; + MapTile[] tiles = mNewTiles.tiles; + int cnt = mNewTiles.cnt; + int max = tiles.length; int xmax = 1 << mZoom; for (int x = x1; x < x2; x++) { @@ -101,7 +111,7 @@ public class MapRenderer extends GLSurfaceView { break; } - // NOTE to myself: do not modify x, argh !!! + // NOTE to myself: do not modify x! int xx = x; if (x < 0 || x >= xmax) { @@ -127,13 +137,15 @@ public class MapRenderer extends GLSurfaceView { tiles[cnt++] = tile; } } - mCurrentTiles.cnt = cnt; + mNewTiles.cnt = cnt; } }; // why not try a pattern every now and then? - // but should do the same for GLRenderer - private static MapRenderer SINGLETON; + // but should do the same for GLRenderer. + // 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) { if (SINGLETON != null) @@ -177,7 +189,10 @@ public class MapRenderer extends GLSurfaceView { VertexPool.init(); QuadTree.init(); + mUpdateCnt = 0; mInitial = true; + + tileCounter = 0; } /** @@ -188,7 +203,7 @@ public class MapRenderer extends GLSurfaceView { * @param clear * whether to clear and reload all tiles */ - public void updateMap(final boolean clear) { + public synchronized void updateMap(final boolean clear) { boolean changedPos = false; if (mMapView == null) @@ -214,7 +229,7 @@ public class MapRenderer extends GLSurfaceView { // TODO clear overlay items data mOverlays.clear(); - mOverlays.add(new Overlay()); + // mOverlays.add(new Overlay()); // set up TileData arrays that are passed to gl-thread int num = mWidth; @@ -225,9 +240,9 @@ public class MapRenderer extends GLSurfaceView { int numTiles = (num * num) / (size * size) * 4; - mRenderer.clearTiles(numTiles); + // mRenderer.clearTiles(numTiles); + mNewTiles = new TilesData(numTiles); mCurrentTiles = new TilesData(numTiles); - // MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo(); // if (mapInfo != null) // mZoomLevels = mapInfo.zoomLevel; @@ -243,8 +258,10 @@ public class MapRenderer extends GLSurfaceView { float[] coords = mTileCoords; changedPos = mMapViewPosition.getMapPosition(mapPosition, coords); - if (!changedPos) + if (!changedPos) { + requestRender(); return; + } float s = Tile.TILE_SIZE; // 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; } - // 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 // to drain queued events. need to check how android handles things. @@ -279,6 +286,7 @@ public class MapRenderer extends GLSurfaceView { if (changed) { int remove = mTiles.size() - GLRenderer.CACHE_TILES; + if (remove > CACHE_THRESHOLD) 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 - * jobs for not yet loaded tiles + * set mNewTiles for the visible tiles and pass it to GLRenderer, add jobs + * for not yet loaded tiles * * @param mapPosition * the current MapPosition @@ -298,44 +348,97 @@ public class MapRenderer extends GLSurfaceView { */ private static boolean updateVisibleList(MapPosition mapPosition, int zdir) { + // TODO keep mJobList and JobQueue in sync, no need to 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) { updateTileDistances(mJobList, mapPosition); Collections.sort(mJobList); + + // sets tiles to isLoading = true mMapView.addJobs(mJobList); + return true; } 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; tile = QuadTree.getTile(x, y, zoomLevel); if (tile == null) { tile = new MapTile(x, y, zoomLevel); - QuadTree.add(tile); - mTiles.add(tile); - } - // if (!fetchProxy && !tile.isActive()) { - if (!tile.isActive()) { + mTiles.add(tile); + mJobList.add(tile); + tileCounter++; + } else if (!tile.isActive()) { mJobList.add(tile); } - - // mCurrentTiles.tiles[tiles++] = tile; + // mNewTiles.tiles[tiles++] = tile; // if (fetchChildren) { // byte z = (byte) (zoomLevel + 1); @@ -359,22 +462,23 @@ public class MapRenderer extends GLSurfaceView { // } // if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) { - if (zdir > 0 && zoomLevel > 0) { - // prefetch parent - MapTile p = tile.rel.parent.tile; - if (p == null) { - p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1)); - - QuadTree.add(p); - mTiles.add(p); - mJobList.add(p); - - } else if (!p.isActive()) { - if (!mJobList.contains(p)) - mJobList.add(p); - } - } + // if (zdir > 0 && zoomLevel > 0) { + // // prefetch parent + // MapTile p = tile.rel.parent.tile; + // + // if (p == null) { + // p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1)); + // + // QuadTree.add(p); + // mTiles.add(p); + // mJobList.add(p); + // + // } else if (!p.isActive()) { + // if (!mJobList.contains(p)) + // mJobList.add(p); + // } + // } return tile; } @@ -389,15 +493,16 @@ public class MapRenderer extends GLSurfaceView { t.layers = null; } - t.labels = null; + TextItem.release(t.labels); if (t.vbo != null) { BufferObject.release(t.vbo); t.vbo = null; } - if (t.texture != null) - t.texture.tile = null; + // if (t.texture != null) + // t.texture.tile = null; + tileCounter--; QuadTree.remove(t); } @@ -479,15 +584,13 @@ public class MapRenderer extends GLSurfaceView { for (int i = 1; i < remove; i++) { MapTile t = mTiles.remove(size - i); + // synchronized (t) { if (t.isLocked()) { // dont remove tile used by GLRenderer Log.d(TAG, "X not removing " + t + " " + t.distance); mTiles.add(t); - continue; - } - - if (t.isLoading) { + } else if (t.isLoading) { // NOTE: if we add tile back and set loading=false, on next // limitCache the tile will be removed. clearTile could // interfere with TileGenerator. so clear in passTile() @@ -498,10 +601,10 @@ public class MapRenderer extends GLSurfaceView { // t.isLoading = false; mTiles.add(t); 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 for (int i = 0; i < size;) { MapTile t = mTilesLoaded.get(i); - - if (!t.newData) { + // t.rel == null means tile was removed in limitCache + if (!t.newData || t.rel == null) { mTilesLoaded.remove(i); size--; continue; @@ -532,7 +635,7 @@ public class MapRenderer extends GLSurfaceView { for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) { MapTile t = mTilesLoaded.get(i); - + // synchronized (t) { if (t.isLocked()) { // Log.d(TAG, "keep unused tile data: " + t + " " + // t.isActive); @@ -545,6 +648,7 @@ public class MapRenderer extends GLSurfaceView { mTiles.remove(t); clearTile(t); } + // } } } @@ -558,42 +662,47 @@ public class MapRenderer extends GLSurfaceView { public synchronized boolean passTile(JobTile jobTile) { MapTile tile = (MapTile) jobTile; - if (!tile.isLoading) { - // no one should be able to use this tile now, TileGenerator passed - // it, GL-Thread does nothing until newdata is set. - Log.d(TAG, "passTile: canceled " + tile); - synchronized (mTilesLoaded) { - mTilesLoaded.add(tile); - } - return true; - } - - tile.vbo = BufferObject.get(); + // XXX this is concurrent with updateVisiblelist which set loading false + // temporarily + // if (!tile.isLoading) { + // // no one should be able to use this tile now, TileGenerator passed + // // it, GL-Thread does nothing until newdata is set. + // Log.d(TAG, "passTile: canceled " + tile); + // return true; + // } + // synchronized (tile) { if (tile.vbo == null) { - Log.d(TAG, "no VBOs left for " + tile); - tile.isLoading = false; - return false; - } + tile.vbo = BufferObject.get(); + + 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.isLoading = false; + // } if (!MapView.debugFrameTime) requestRender(); synchronized (mTilesLoaded) { - mTilesLoaded.add(tile); + if (!mTilesLoaded.contains(tile)) + mTilesLoaded.add(tile); } return true; } - public void setRenderTheme(RenderTheme t) { - if (mRenderer != null) - mRenderer.setRenderTheme(t); - - } + // public void setRenderTheme(RenderTheme t) { + // if (mRenderer != null) + // mRenderer.setRenderTheme(t); + // + // } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { diff --git a/src/org/oscim/renderer/MapTile.java b/src/org/oscim/renderer/MapTile.java index 32049988..98de5d96 100644 --- a/src/org/oscim/renderer/MapTile.java +++ b/src/org/oscim/renderer/MapTile.java @@ -16,6 +16,7 @@ package org.oscim.renderer; import org.oscim.generator.JobTile; import org.oscim.renderer.layer.Layers; +import org.oscim.renderer.layer.TextItem; class MapTile extends JobTile { @@ -25,7 +26,7 @@ class MapTile extends JobTile { */ BufferObject vbo; - TextTexture texture; + // TextTexture texture; /** * Tile data set by TileGenerator: @@ -56,10 +57,14 @@ class MapTile extends JobTile { int lastDraw = 0; // keep track which tiles are locked as proxy for this tile - final static int PROXY_PARENT = 16; - final static int PROXY_GRAMPA = 32; - final static int PROXY_HOLDER = 64; - // 1-8: children + final static int PROXY_CHILD1 = 1 << 0; + final static int PROXY_CHILD2 = 1 << 1; + final static int PROXY_CHILD3 = 1 << 2; + 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; // counting the tiles that use this tile as proxy @@ -83,8 +88,6 @@ class MapTile extends JobTile { } void lock() { - if (holder != null) - return; locked++; @@ -116,27 +119,21 @@ class MapTile extends JobTile { } void unlock() { - if (holder != null) - return; locked--; if (locked > 0 || proxies == 0) return; - if ((proxies & (1 << 4)) != 0) { - MapTile p = rel.parent.tile; - p.refs--; - } - if ((proxies & (1 << 5)) != 0) { - MapTile p = rel.parent.parent.tile; - p.refs--; - } + if ((proxies & PROXY_PARENT) != 0) + rel.parent.tile.refs--; + + if ((proxies & PROXY_GRAMPA) != 0) + rel.parent.parent.tile.refs--; for (int i = 0; i < 4; i++) { - if ((proxies & (1 << i)) != 0) { + if ((proxies & (1 << i)) != 0) rel.child[i].tile.refs--; - } } proxies = 0; } diff --git a/src/org/oscim/renderer/Overlay.java b/src/org/oscim/renderer/Overlay.java index 7d1cf6b5..febc779a 100644 --- a/src/org/oscim/renderer/Overlay.java +++ b/src/org/oscim/renderer/Overlay.java @@ -14,121 +14,79 @@ */ package org.oscim.renderer; -import java.io.IOException; - import org.oscim.core.MapPosition; import org.oscim.core.Tile; import org.oscim.renderer.layer.Layer; import org.oscim.renderer.layer.Layers; -import org.oscim.renderer.layer.LineLayer; -import org.oscim.renderer.layer.SymbolItem; -import org.oscim.renderer.layer.SymbolLayer; -import org.oscim.theme.renderinstruction.BitmapUtils; -import org.oscim.theme.renderinstruction.Line; +import org.oscim.utils.FastMath; import org.oscim.view.MapView; -import android.graphics.Color; -import android.graphics.Paint.Cap; import android.opengl.GLES20; 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. - boolean newData; - boolean isReady; - MapPosition mMapPosition; + protected boolean newData; - 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(); - 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; } + /** + * update mMapPosition + * + * @return true if position has changed + */ + protected boolean updateMapPosition() { + return mMapView.getMapViewPosition().getMapPosition(mMapPosition, null); + } + // /////////////// called from GLRender Thread //////////////////////// // use synchronized (this){} when updating 'layers' from another thread - synchronized void update(MapView mapView) { - // keep position constant (or update layer relative to new position) - // mapView.getMapViewPosition().getMapPosition(mMapPosition, null); - - if (first) { - // fix at initial position - mapView.getMapViewPosition().getMapPosition(mMapPosition, null); - first = false; - - // pass layers to be uploaded and drawn to GL Thread - // afterwards never modify 'layers' outside of this function! - newData = true; - } + /** + * @param positionChanged + * true when MapPosition has changed + * @param tilesChanged + * true when loaded tiles changed + */ + 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; + // } } float[] mvp = new float[16]; synchronized void render(MapPosition pos, float[] mv, float[] proj) { - float div = 1; - - setMatrix(pos, mv); + float div = setMatrix(pos, mv); Matrix.multiplyMM(mvp, 0, proj, 0, mv, 0); @@ -140,29 +98,32 @@ public class Overlay { l = PolygonRenderer.draw(pos, l, mvp, true, false); } else { 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;) { - l = TextureRenderer.draw(l, 1, proj, mv, layers.symbolOffset); + // float scale = curPos.scale / div; + + 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; - float div = 1; + 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) - div = (1 << -diff); - else if (diff > 0) - div = (1.0f / (1 << diff)); - - float x = (float) (oPos.x - mPos.x * div); - float y = (float) (oPos.y - mPos.y * div); + float x = (float) (oPos.x - curPos.x * div); + float y = (float) (oPos.y - curPos.y * div); // flip around date-line float max = (Tile.TILE_SIZE << z); @@ -171,7 +132,7 @@ public class Overlay { else if (x > max / 2) x = x - max; - float scale = mPos.scale / div; + float scale = curPos.scale / div; Matrix.setIdentityM(matrix, 0); @@ -179,12 +140,14 @@ public class Overlay { matrix[12] = x * scale; matrix[13] = y * scale; - scale = (mPos.scale / oPos.scale) / div; + scale = (curPos.scale / oPos.scale) / div; // scale to tile to world coordinates scale /= GLRenderer.COORD_MULTIPLIER; matrix[0] = scale; matrix[5] = scale; - Matrix.multiplyMM(matrix, 0, mPos.viewMatrix, 0, matrix, 0); + Matrix.multiplyMM(matrix, 0, curPos.viewMatrix, 0, matrix, 0); + + return div; } } diff --git a/src/org/oscim/renderer/OverlayGrid.java b/src/org/oscim/renderer/OverlayGrid.java new file mode 100644 index 00000000..cb848be4 --- /dev/null +++ b/src/org/oscim/renderer/OverlayGrid.java @@ -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 . + */ +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; + } + } +} diff --git a/src/org/oscim/renderer/OverlayTest.java b/src/org/oscim/renderer/OverlayTest.java new file mode 100644 index 00000000..6061cac2 --- /dev/null +++ b/src/org/oscim/renderer/OverlayTest.java @@ -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 . + */ +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; + } + } + +} diff --git a/src/org/oscim/renderer/OverlayText.java b/src/org/oscim/renderer/OverlayText.java new file mode 100644 index 00000000..f942dbc3 --- /dev/null +++ b/src/org/oscim/renderer/OverlayText.java @@ -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 . + */ + +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(); + } + } + } + } +} diff --git a/src/org/oscim/renderer/TextRenderer.java b/src/org/oscim/renderer/TextRenderer.java index 73117107..8f425174 100644 --- a/src/org/oscim/renderer/TextRenderer.java +++ b/src/org/oscim/renderer/TextRenderer.java @@ -14,442 +14,445 @@ */ package org.oscim.renderer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.ShortBuffer; - -import org.oscim.utils.GlUtils; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.opengl.GLES20; -import android.opengl.GLUtils; -import android.util.FloatMath; -import android.util.Log; - -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 float SCALE = 8.0f; - 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 SHORTS_PER_VERTICE = 6; - final static int MAX_LABELS = 35; - - private static Bitmap mBitmap; - private static Canvas mCanvas; - private static int mFontPadX = 1; - private static int mFontPadY = 1; - private static int mBitmapFormat; - private static int mBitmapType; - private static ShortBuffer mShortBuffer; - private static TextTexture[] mTextures; - - private static int mIndicesVBO; - private static int mVerticesVBO; - - private static int mTextProgram; - private static int hTextMVMatrix; - private static int hTextProjectionMatrix; - private static int hTextVertex; - private static int hTextScale; - private static int hTextScreenScale; - private static int hTextTextureCoord; - - private static Paint mPaint = new Paint(Color.BLACK); - - private static boolean debug = false; - private static short[] debugVertices = { - - 0, 0, - 0, TEXTURE_HEIGHT * 4, - - 0, TEXTURE_HEIGHT - 1, - 0, 0, - - TEXTURE_WIDTH - 1, 0, - TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4, - - TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1, - TEXTURE_WIDTH * 4, 0, - - }; - - static void init() { - mTextProgram = GlUtils.createProgram(Shaders.textVertexShader, - Shaders.textFragmentShader); - - hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv"); - hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj"); - hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale"); - hTextScreenScale = GLES20.glGetUniformLocation(mTextProgram, "u_swidth"); - hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex"); - hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord"); - - } - - static boolean setup(int numTextures) { - int bufferSize = numTextures - * MAX_LABELS * VERTICES_PER_SPRITE - * SHORTS_PER_VERTICE * (Short.SIZE / 8); - - // if (mBitmap == null) { - mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, - Bitmap.Config.ARGB_8888); - mCanvas = new Canvas(mBitmap); - - mBitmapFormat = GLUtils.getInternalFormat(mBitmap); - mBitmapType = GLUtils.getType(mBitmap); - - ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize) - .order(ByteOrder.nativeOrder()); - - mShortBuffer = buf.asShortBuffer(); - // } - - int[] textureIds = new int[numTextures]; - TextTexture[] textures = new TextTexture[numTextures]; - GLES20.glGenTextures(numTextures, textureIds, 0); - - for (int i = 0; i < numTextures; i++) { - // setup filters for texture - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]); - - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, - GLES20.GL_LINEAR); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, - GLES20.GL_LINEAR); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, - GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, - GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping - - // load the generated bitmap onto the texture - GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap, - mBitmapType, 0); - - textures[i] = new TextTexture(textureIds[i]); - } - - GlUtils.checkGlError("init textures"); - - mTextures = textures; - - // Setup triangle indices - short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE]; - int len = indices.length; - short j = 0; - for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) { - indices[i + 0] = (short) (j + 0); - indices[i + 1] = (short) (j + 1); - indices[i + 2] = (short) (j + 2); - indices[i + 3] = (short) (j + 2); - indices[i + 4] = (short) (j + 3); - indices[i + 5] = (short) (j + 0); - } - - mShortBuffer.clear(); - mShortBuffer.put(indices, 0, len); - mShortBuffer.flip(); - - int[] mVboIds = new int[2]; - GLES20.glGenBuffers(2, mVboIds, 0); - 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), - mShortBuffer, GLES20.GL_STATIC_DRAW); - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); - - mShortBuffer.clear(); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); - GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize, - mShortBuffer, GLES20.GL_DYNAMIC_DRAW); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); - - return true; - } - - static boolean drawToTexture(MapTile tile) { - TextTexture tex = null; - - if (tile.labels == null) - return false; - - for (int i = 0; i < mTextures.length; i++) { - tex = mTextures[i]; - if (tex.tile == null) - break; - - if (!tex.tile.isLocked()) - break; - - tex = null; - } - - if (tex == null) { - for (int i = 0; i < mTextures.length; i++) { - tex = mTextures[i]; - if (!tex.tile.isVisible) - break; - - tex = null; - } - } - - if (tex == null) { - Log.d(TAG, "no textures left"); - return false; - } - if (tex.tile != null) - tex.tile.texture = null; - - mBitmap.eraseColor(Color.TRANSPARENT); - - int pos = 0; - short[] buf = tex.vertices; - - float y = 0; - float x = mFontPadX; - float width, height; - - int max = MAX_LABELS; - - if (debug) { - mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4], - debugVertices[5], mPaint); - mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8], - debugVertices[9], mPaint); - - mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4], - debugVertices[5], mPaint); - mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8], - debugVertices[9], mPaint); - } - - int advanceY = 0; - - TextItem t = tile.labels; - float yy; - - for (int i = 0; t != null && i < max; t = t.next, i++) { - - height = (int) (t.text.fontHeight) + 2 * mFontPadY; - width = t.width + 2 * mFontPadX; - - if (height > advanceY) - advanceY = (int) height; - - if (x + width > TEXTURE_WIDTH) { - x = mFontPadX; - y += advanceY; - advanceY = (int) height; - } - - yy = y + (height - 1) - t.text.fontDescent - mFontPadY; - if (yy > TEXTURE_HEIGHT) { - Log.d(TAG, "reached max labels"); - break; - // continue; - } - - if (t.text.stroke != null) - 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 (width > TEXTURE_WIDTH) - width = TEXTURE_WIDTH; - - float hw = width / 2.0f; - float hh = height / 2.0f; - short x1, x2, x3, x4, y1, y2, y3, y4; - - if (t.text.caption) { - x1 = x3 = (short) (SCALE * (-hw)); - y1 = y3 = (short) (SCALE * (hh)); - x2 = x4 = (short) (SCALE * (hw)); - y2 = y4 = (short) (SCALE * (-hh)); - } else { - float vx = t.x1 - t.x2; - float vy = t.y1 - t.y2; - float a = FloatMath.sqrt(vx * vx + vy * vy); - vx = vx / a; - vy = vy / a; - - float ux = -vy; - float uy = vx; - - // int dx = (int) (vx * SCALE) & L2BIT_MASK; - // int dy = (int) (vy * SCALE) & L2BIT_MASK; - // - // x1 = (short) dx; - // y1 = (short) dy; - // - // x2 = (short) (dx | 1); - // y3 = (short) (dy | 1); - // - // x4 = (short) (dx | 3); - // y4 = (short) (dy | 3); - // - // x3 = (short) (dx | 2); - // y2 = (short) (dy | 2); - - x1 = (short) (SCALE * (vx * hw - ux * hh)); - y1 = (short) (SCALE * (vy * hw - uy * hh)); - x2 = (short) (SCALE * (-vx * hw - ux * hh)); - y3 = (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)); - y2 = (short) (SCALE * (vy * hw + uy * hh)); - - } - short u1 = (short) (SCALE * x); - short v1 = (short) (SCALE * y); - short u2 = (short) (SCALE * (x + width)); - short v2 = (short) (SCALE * (y + height)); - - // pack caption/way-text info in lowest bit - int tmp = (int) (SCALE * t.x) & LBIT_MASK; - short tx = (short) (tmp | (t.text.caption ? 1 : 0)); - - short ty = (short) (SCALE * t.y); - - // top-left - buf[pos++] = tx; - buf[pos++] = ty; - buf[pos++] = x1; - buf[pos++] = y1; - buf[pos++] = u1; - buf[pos++] = v2; - - // top-right - buf[pos++] = tx; - buf[pos++] = ty; - buf[pos++] = x2; - buf[pos++] = y3; - buf[pos++] = u2; - buf[pos++] = v2; - - // bot-right - buf[pos++] = tx; - buf[pos++] = ty; - buf[pos++] = x4; - buf[pos++] = y4; - buf[pos++] = u2; - buf[pos++] = v1; - - // bot-left - buf[pos++] = tx; - buf[pos++] = ty; - buf[pos++] = x3; - buf[pos++] = y2; - buf[pos++] = u1; - buf[pos++] = v1; - - x += width; - - if (y > TEXTURE_HEIGHT) { - Log.d(TAG, "reached max labels: texture is full"); - break; - } - } - - tex.length = pos; - tile.texture = tex; - tex.tile = tile; - - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id); - GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap, - mBitmapFormat, mBitmapType); - - // FIXME shouldnt be needed here, still looking for sometimes corrupted - // labels.. - GLES20.glFlush(); - - return true; - } - - static void compileTextures() { - int offset = 0; - TextTexture tex; - - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); - - mShortBuffer.clear(); - - for (int i = 0; i < mTextures.length; i++) { - tex = mTextures[i]; - if (tex.tile == null) // || !tex.tile.isLocked) - continue; - - mShortBuffer.put(tex.vertices, 0, tex.length); - tex.offset = offset; - offset += tex.length; - } - - mShortBuffer.flip(); - - GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8), - mShortBuffer); - } - - static void beginDraw(float scale, float[] projection) { - GLES20.glUseProgram(mTextProgram); - - // GLES20.glEnableVertexAttribArray(hTextTextureCoord); - // GLES20.glEnableVertexAttribArray(hTextVertex); - - int va = hTextTextureCoord; - if (!GLRenderer.vertexArray[va]) { - GLES20.glEnableVertexAttribArray(va); - GLRenderer.vertexArray[va] = true; - } - - va = hTextVertex; - if (!GLRenderer.vertexArray[va]) { - GLES20.glEnableVertexAttribArray(va); - GLRenderer.vertexArray[va] = true; - } - - GLES20.glUniform1f(hTextScale, scale); - GLES20.glUniform1f(hTextScreenScale, 1f / GLRenderer.mWidth); - GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0); - - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); - } - - static void endDraw() { - GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); - - // GLES20.glDisableVertexAttribArray(hTextTextureCoord); - // GLES20.glDisableVertexAttribArray(hTextVertex); - } - - static void drawTile(MapTile tile, float[] matrix) { - - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id); - - GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0); - - GLES20.glVertexAttribPointer(hTextVertex, 4, - GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)); - - GLES20.glVertexAttribPointer(hTextTextureCoord, 2, - GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8) - + 8); - - GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) * - INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0); - } -} +//import java.nio.ByteBuffer; +//import java.nio.ByteOrder; +//import java.nio.ShortBuffer; +// +//import org.oscim.renderer.layer.TextItem; +//import org.oscim.utils.GlUtils; +// +//import android.graphics.Bitmap; +//import android.graphics.Canvas; +//import android.graphics.Color; +//import android.graphics.Paint; +//import android.opengl.GLES20; +//import android.opengl.GLUtils; +//import android.util.FloatMath; +//import android.util.Log; +// +//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 float SCALE = 8.0f; +// 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 SHORTS_PER_VERTICE = 6; +// final static int MAX_LABELS = 35; +// +// private static Bitmap mBitmap; +// private static Canvas mCanvas; +// private static int mFontPadX = 1; +// private static int mFontPadY = 1; +// private static int mBitmapFormat; +// private static int mBitmapType; +// private static ShortBuffer mShortBuffer; +// private static TextTexture[] mTextures; +// +// private static int mIndicesVBO; +// private static int mVerticesVBO; +// +// private static int mTextProgram; +// private static int hTextMVMatrix; +// private static int hTextProjectionMatrix; +// private static int hTextVertex; +// private static int hTextScale; +// private static int hTextScreenScale; +// private static int hTextTextureCoord; +// +// private static Paint mPaint = new Paint(Color.BLACK); +// +// private static boolean debug = false; +// private static short[] debugVertices = { +// +// 0, 0, +// 0, TEXTURE_HEIGHT * 4, +// +// 0, TEXTURE_HEIGHT - 1, +// 0, 0, +// +// TEXTURE_WIDTH - 1, 0, +// TEXTURE_WIDTH * 4, TEXTURE_HEIGHT * 4, +// +// TEXTURE_WIDTH - 1, TEXTURE_HEIGHT - 1, +// TEXTURE_WIDTH * 4, 0, +// +// }; +// +// static void init() { +// mTextProgram = GlUtils.createProgram(Shaders.textVertexShader, +// Shaders.textFragmentShader); +// +// hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv"); +// hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj"); +// hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale"); +// hTextScreenScale = GLES20.glGetUniformLocation(mTextProgram, "u_swidth"); +// hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex"); +// hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord"); +// +// } +// +// static boolean setup(int numTextures) { +// int bufferSize = numTextures +// * MAX_LABELS * VERTICES_PER_SPRITE +// * SHORTS_PER_VERTICE * (Short.SIZE / 8); +// +// // if (mBitmap == null) { +// mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, +// Bitmap.Config.ARGB_8888); +// mCanvas = new Canvas(mBitmap); +// +// mBitmapFormat = GLUtils.getInternalFormat(mBitmap); +// mBitmapType = GLUtils.getType(mBitmap); +// +// ByteBuffer buf = ByteBuffer.allocateDirect(bufferSize) +// .order(ByteOrder.nativeOrder()); +// +// mShortBuffer = buf.asShortBuffer(); +// // } +// +// int[] textureIds = new int[numTextures]; +// TextTexture[] textures = new TextTexture[numTextures]; +// GLES20.glGenTextures(numTextures, textureIds, 0); +// +// for (int i = 0; i < numTextures; i++) { +// // setup filters for texture +// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]); +// +// GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, +// GLES20.GL_LINEAR); +// GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, +// GLES20.GL_LINEAR); +// GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, +// GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping +// GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, +// GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping +// +// // load the generated bitmap onto the texture +// GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, mBitmapFormat, mBitmap, +// mBitmapType, 0); +// +// textures[i] = new TextTexture(textureIds[i]); +// } +// +// GlUtils.checkGlError("init textures"); +// +// mTextures = textures; +// +// // Setup triangle indices +// short[] indices = new short[MAX_LABELS * INDICES_PER_SPRITE]; +// int len = indices.length; +// short j = 0; +// for (int i = 0; i < len; i += INDICES_PER_SPRITE, j += VERTICES_PER_SPRITE) { +// indices[i + 0] = (short) (j + 0); +// indices[i + 1] = (short) (j + 1); +// indices[i + 2] = (short) (j + 2); +// indices[i + 3] = (short) (j + 2); +// indices[i + 4] = (short) (j + 3); +// indices[i + 5] = (short) (j + 0); +// } +// +// mShortBuffer.clear(); +// mShortBuffer.put(indices, 0, len); +// mShortBuffer.flip(); +// +// int[] mVboIds = new int[2]; +// GLES20.glGenBuffers(2, mVboIds, 0); +// 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), +// mShortBuffer, GLES20.GL_STATIC_DRAW); +// GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); +// +// mShortBuffer.clear(); +// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); +// GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, bufferSize, +// mShortBuffer, GLES20.GL_DYNAMIC_DRAW); +// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); +// +// return true; +// } +// +// static boolean drawToTexture(MapTile tile) { +// TextTexture tex = null; +// +// if (tile.labels == null) +// return false; +// +// for (int i = 0; i < mTextures.length; i++) { +// tex = mTextures[i]; +// if (tex.tile == null) +// break; +// +// if (!tex.tile.isLocked()) +// break; +// +// tex = null; +// } +// +// if (tex == null) { +// for (int i = 0; i < mTextures.length; i++) { +// tex = mTextures[i]; +// if (!tex.tile.isVisible) +// break; +// +// tex = null; +// } +// } +// +// if (tex == null) { +// Log.d(TAG, "no textures left"); +// return false; +// } +// if (tex.tile != null) +// tex.tile.texture = null; +// +// mBitmap.eraseColor(Color.TRANSPARENT); +// +// int pos = 0; +// short[] buf = tex.vertices; +// +// float y = 0; +// float x = mFontPadX; +// float width, height; +// +// int max = MAX_LABELS; +// +// if (debug) { +// mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[4], +// debugVertices[5], mPaint); +// mCanvas.drawLine(debugVertices[0], debugVertices[1], debugVertices[8], +// debugVertices[9], mPaint); +// +// mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[4], +// debugVertices[5], mPaint); +// mCanvas.drawLine(debugVertices[12], debugVertices[13], debugVertices[8], +// debugVertices[9], mPaint); +// } +// +// int advanceY = 0; +// +// TextItem t = tile.labels; +// float yy; +// +// for (int i = 0; t != null && i < max; t = t.next, i++) { +// if (t.text.caption) +// continue; +// +// height = (int) (t.text.fontHeight) + 2 * mFontPadY; +// width = t.width + 2 * mFontPadX; +// +// if (height > advanceY) +// advanceY = (int) height; +// +// if (x + width > TEXTURE_WIDTH) { +// x = mFontPadX; +// y += advanceY; +// advanceY = (int) height; +// } +// +// yy = y + (height - 1) - t.text.fontDescent - mFontPadY; +// if (yy > TEXTURE_HEIGHT) { +// Log.d(TAG, "reached max labels"); +// break; +// // continue; +// } +// +// if (t.text.stroke != null) +// 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 (width > TEXTURE_WIDTH) +// width = TEXTURE_WIDTH; +// +// float hw = width / 2.0f; +// float hh = height / 2.0f; +// short x1, x2, x3, x4, y1, y2, y3, y4; +// +// if (t.text.caption) { +// x1 = x3 = (short) (SCALE * (-hw)); +// y1 = y3 = (short) (SCALE * (hh)); +// x2 = x4 = (short) (SCALE * (hw)); +// y2 = y4 = (short) (SCALE * (-hh)); +// } else { +// float vx = t.x1 - t.x2; +// float vy = t.y1 - t.y2; +// float a = FloatMath.sqrt(vx * vx + vy * vy); +// vx = vx / a; +// vy = vy / a; +// +// float ux = -vy; +// float uy = vx; +// +// // int dx = (int) (vx * SCALE) & L2BIT_MASK; +// // int dy = (int) (vy * SCALE) & L2BIT_MASK; +// // +// // x1 = (short) dx; +// // y1 = (short) dy; +// // +// // x2 = (short) (dx | 1); +// // y3 = (short) (dy | 1); +// // +// // x4 = (short) (dx | 3); +// // y4 = (short) (dy | 3); +// // +// // x3 = (short) (dx | 2); +// // y2 = (short) (dy | 2); +// +// x1 = (short) (SCALE * (vx * hw - ux * hh)); +// y1 = (short) (SCALE * (vy * hw - uy * hh)); +// x2 = (short) (SCALE * (-vx * hw - ux * hh)); +// y3 = (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)); +// y2 = (short) (SCALE * (vy * hw + uy * hh)); +// +// } +// short u1 = (short) (SCALE * x); +// short v1 = (short) (SCALE * y); +// short u2 = (short) (SCALE * (x + width)); +// short v2 = (short) (SCALE * (y + height)); +// +// // pack caption/way-text info in lowest bit +// int tmp = (int) (SCALE * t.x) & LBIT_MASK; +// short tx = (short) (tmp | (t.text.caption ? 1 : 0)); +// +// short ty = (short) (SCALE * t.y); +// +// // top-left +// buf[pos++] = tx; +// buf[pos++] = ty; +// buf[pos++] = x1; +// buf[pos++] = y1; +// buf[pos++] = u1; +// buf[pos++] = v2; +// +// // top-right +// buf[pos++] = tx; +// buf[pos++] = ty; +// buf[pos++] = x2; +// buf[pos++] = y3; +// buf[pos++] = u2; +// buf[pos++] = v2; +// +// // bot-right +// buf[pos++] = tx; +// buf[pos++] = ty; +// buf[pos++] = x4; +// buf[pos++] = y4; +// buf[pos++] = u2; +// buf[pos++] = v1; +// +// // bot-left +// buf[pos++] = tx; +// buf[pos++] = ty; +// buf[pos++] = x3; +// buf[pos++] = y2; +// buf[pos++] = u1; +// buf[pos++] = v1; +// +// x += width; +// +// if (y > TEXTURE_HEIGHT) { +// Log.d(TAG, "reached max labels: texture is full"); +// break; +// } +// } +// +// tex.length = pos; +// tile.texture = tex; +// tex.tile = tile; +// +// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tex.id); +// GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap, +// mBitmapFormat, mBitmapType); +// +// // FIXME shouldnt be needed here, still looking for sometimes corrupted +// // labels.. +// GLES20.glFlush(); +// +// return true; +// } +// +// static void compileTextures() { +// int offset = 0; +// TextTexture tex; +// +// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); +// +// mShortBuffer.clear(); +// +// for (int i = 0; i < mTextures.length; i++) { +// tex = mTextures[i]; +// if (tex.tile == null) // || !tex.tile.isLocked) +// continue; +// +// mShortBuffer.put(tex.vertices, 0, tex.length); +// tex.offset = offset; +// offset += tex.length; +// } +// +// mShortBuffer.flip(); +// +// GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, offset * (Short.SIZE / 8), +// mShortBuffer); +// } +// +// static void beginDraw(float scale, float[] projection) { +// GLES20.glUseProgram(mTextProgram); +// +// // GLES20.glEnableVertexAttribArray(hTextTextureCoord); +// // GLES20.glEnableVertexAttribArray(hTextVertex); +// +// int va = hTextTextureCoord; +// if (!GLRenderer.vertexArray[va]) { +// GLES20.glEnableVertexAttribArray(va); +// GLRenderer.vertexArray[va] = true; +// } +// +// va = hTextVertex; +// if (!GLRenderer.vertexArray[va]) { +// GLES20.glEnableVertexAttribArray(va); +// GLRenderer.vertexArray[va] = true; +// } +// +// GLES20.glUniform1f(hTextScale, scale); +// GLES20.glUniform1f(hTextScreenScale, 1f / GLRenderer.mWidth); +// GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0); +// +// GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); +// GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mVerticesVBO); +// } +// +// static void endDraw() { +// GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0); +// +// // GLES20.glDisableVertexAttribArray(hTextTextureCoord); +// // GLES20.glDisableVertexAttribArray(hTextVertex); +// } +// +// static void drawTile(MapTile tile, float[] matrix) { +// +// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id); +// +// GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0); +// +// GLES20.glVertexAttribPointer(hTextVertex, 4, +// GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8)); +// +// GLES20.glVertexAttribPointer(hTextTextureCoord, 2, +// GLES20.GL_SHORT, false, 12, tile.texture.offset * (Short.SIZE / 8) +// + 8); +// +// GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tile.texture.length / 24) * +// INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0); +// } +// } diff --git a/src/org/oscim/renderer/TextTexture.java b/src/org/oscim/renderer/TextTexture.java index c2f96e75..d33809bd 100644 --- a/src/org/oscim/renderer/TextTexture.java +++ b/src/org/oscim/renderer/TextTexture.java @@ -14,20 +14,20 @@ */ package org.oscim.renderer; -public class TextTexture { - - final short[] vertices; - final int id; - int length; - int offset; - MapTile tile; - - TextTexture(int textureID) { - vertices = new short[TextRenderer.MAX_LABELS * - TextRenderer.VERTICES_PER_SPRITE * - TextRenderer.SHORTS_PER_VERTICE]; - - id = textureID; - } - -} +//public class TextTexture { +// +// final short[] vertices; +// final int id; +// int length; +// int offset; +// MapTile tile; +// +// TextTexture(int textureID) { +// vertices = new short[TextRenderer.MAX_LABELS * +// TextRenderer.VERTICES_PER_SPRITE * +// TextRenderer.SHORTS_PER_VERTICE]; +// +// id = textureID; +// } +// +// } diff --git a/src/org/oscim/renderer/TextureObject.java b/src/org/oscim/renderer/TextureObject.java index 71c1d758..f1ed2b2b 100644 --- a/src/org/oscim/renderer/TextureObject.java +++ b/src/org/oscim/renderer/TextureObject.java @@ -15,6 +15,8 @@ package org.oscim.renderer; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; import android.opengl.GLES20; import android.opengl.GLUtils; import android.util.Log; @@ -22,11 +24,22 @@ import android.util.Log; public class TextureObject { 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() { TextureObject to; if (pool == null) { init(10); + objectCount += 10; + Log.d("...", "textures: " + objectCount); } to = pool; @@ -36,8 +49,15 @@ public class TextureObject { } 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, @@ -79,6 +99,30 @@ public class TextureObject { to.next = pool; 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; @@ -89,7 +133,8 @@ public class TextureObject { // vertex offset from which this texture is referenced // or store texture id with vertex? - int offset; + short offset; + short vertices; TextureObject(int id) { this.id = id; diff --git a/src/org/oscim/renderer/TextureRenderer.java b/src/org/oscim/renderer/TextureRenderer.java index a182bc1b..bf19a4d0 100644 --- a/src/org/oscim/renderer/TextureRenderer.java +++ b/src/org/oscim/renderer/TextureRenderer.java @@ -39,7 +39,7 @@ public class TextureRenderer { final static int VERTICES_PER_SPRITE = 4; final static int SHORTS_PER_VERTICE = 6; // per texture - public final static int MAX_ITEMS = 40; + public final static int MAX_ITEMS = 50; static void init() { mTextureProgram = GlUtils.createProgram(Shaders.textVertexShader, @@ -89,6 +89,7 @@ public class TextureRenderer { static Layer draw(Layer layer, float scale, float[] projection, float matrix[], int offset) { + GLES20.glUseProgram(mTextureProgram); GlUtils.checkGlError("draw texture1"); @@ -116,20 +117,25 @@ public class TextureRenderer { GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO); GlUtils.checkGlError("draw texture3"); - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tl.textures.id); - GlUtils.checkGlError("draw texture4"); + for (TextureObject to = tl.textures; to != null; to = to.next) { - GlUtils.checkGlError("draw texture5"); - GLES20.glVertexAttribPointer(hTextureVertex, 4, - GLES20.GL_SHORT, false, 12, offset); - GlUtils.checkGlError("draw texture.."); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, to.id); + GlUtils.checkGlError("draw texture4"); - GLES20.glVertexAttribPointer(hTextureTexCoord, 2, - GLES20.GL_SHORT, false, 12, offset + 8); - GlUtils.checkGlError("draw texture..."); + GlUtils.checkGlError("draw texture5"); - GLES20.glDrawElements(GLES20.GL_TRIANGLES, (tl.verticesCnt / 4) - * INDICES_PER_SPRITE, GLES20.GL_UNSIGNED_SHORT, 0); + // to.offset * 24(shorts) * 2(short-bytes) / 6(indices) + 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); GlUtils.checkGlError("draw texture"); diff --git a/src/org/oscim/renderer/TileGenerator.java b/src/org/oscim/renderer/TileGenerator.java index 18658509..69059f1d 100644 --- a/src/org/oscim/renderer/TileGenerator.java +++ b/src/org/oscim/renderer/TileGenerator.java @@ -28,6 +28,7 @@ import org.oscim.renderer.layer.LineLayer; import org.oscim.renderer.layer.PolygonLayer; import org.oscim.renderer.layer.SymbolItem; import org.oscim.renderer.layer.SymbolLayer; +import org.oscim.renderer.layer.TextItem; import org.oscim.theme.IRenderCallback; import org.oscim.theme.RenderTheme; import org.oscim.theme.renderinstruction.Area; @@ -39,6 +40,7 @@ import org.oscim.view.MapView; import android.graphics.Bitmap; import android.graphics.Paint; +import android.util.FloatMath; import android.util.Log; /** @@ -97,6 +99,10 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { private float mProjectionScaleFactor; + public static void setRenderTheme(RenderTheme theme) { + renderTheme = theme; + } + /** * @param mapView * the MapView @@ -243,7 +249,9 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { 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; mLabels = t; } @@ -258,7 +266,8 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { return; 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; mLabels = t; } @@ -294,10 +303,10 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { public void renderPointOfInterestSymbol(Bitmap bitmap) { // Log.d(TAG, "add symbol"); - if (mLayers.symbolLayers == null) - mLayers.symbolLayers = new SymbolLayer(); + if (mLayers.textureLayers == null) + mLayers.textureLayers = new SymbolLayer(); - SymbolLayer sl = (SymbolLayer) mLayers.symbolLayers; + SymbolLayer sl = (SymbolLayer) mLayers.textureLayers; SymbolItem it = new SymbolItem(); it.x = mPoiX; @@ -388,12 +397,13 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { mDebugDrawPolygons = !debugSettings.mDisablePolygons; mDebugDrawUnmatched = debugSettings.mDrawUnmatchted; - if (tile.newData || tile.isReady) { + if (tile.newData || tile.isReady || tile.layers != null) { // should be fixed now. Log.d(TAG, "XXX tile already loaded " + tile + " " + tile.newData + " " - + tile.isReady + " "); + + tile.isReady + " " + + tile.isLoading); return false; } @@ -406,9 +416,9 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { setScaleStrokeWidth(STROKE_MAX_ZOOM_LEVEL); // acount for area changes with latitude - mProjectionScaleFactor = 0.5f + (float) (0.5 / Math.cos(MercatorProjection - .pixelYToLatitude(tile.pixelY, tile.zoomLevel) - * (Math.PI / 180))); + mProjectionScaleFactor = 0.5f + 0.5f * ( + FloatMath.sin((float) (Math.abs(MercatorProjection + .pixelYToLatitude(tile.pixelY, tile.zoomLevel)) * (Math.PI / 180)))); mLayers = new Layers(); @@ -482,10 +492,6 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback { return mMapDatabase; } - public void setRenderTheme(RenderTheme theme) { - TileGenerator.renderTheme = theme; - } - @Override public boolean checkWay(Tag[] tags, boolean closed) { diff --git a/src/org/oscim/renderer/WayDecorator.java b/src/org/oscim/renderer/WayDecorator.java index 461a105b..26f801cb 100644 --- a/src/org/oscim/renderer/WayDecorator.java +++ b/src/org/oscim/renderer/WayDecorator.java @@ -14,12 +14,13 @@ */ package org.oscim.renderer; +import org.oscim.renderer.layer.TextItem; import org.oscim.theme.renderinstruction.Text; import org.oscim.utils.GeometryUtils; import android.util.FloatMath; -final class WayDecorator { +public final class WayDecorator { // /** // * 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) { TextItem items = textItems; TextItem t = null; @@ -256,9 +257,16 @@ final class WayDecorator { } // if (t == null) - t = new TextItem(x1 + (x2 - x1) / 2, y1 + (y2 - y1) / 2, string, - text, wayNameWidth); + t = TextItem.get(); + // 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.y1 = (short) y1; t.x2 = (short) x2; diff --git a/src/org/oscim/renderer/layer/Layer.java b/src/org/oscim/renderer/layer/Layer.java index 4f3967ee..7f4d941a 100644 --- a/src/org/oscim/renderer/layer/Layer.java +++ b/src/org/oscim/renderer/layer/Layer.java @@ -14,7 +14,7 @@ */ package org.oscim.renderer.layer; -public class Layer { +public abstract class Layer { public final static byte LINE = 0; public final static byte POLYGON = 1; public final static byte WAYTEXT = 2; @@ -34,4 +34,7 @@ public class Layer { VertexPoolItem pool; protected VertexPoolItem curItem; + + protected void clear() { + } } diff --git a/src/org/oscim/renderer/layer/Layers.java b/src/org/oscim/renderer/layer/Layers.java index c034d62f..d520581c 100644 --- a/src/org/oscim/renderer/layer/Layers.java +++ b/src/org/oscim/renderer/layer/Layers.java @@ -16,8 +16,6 @@ package org.oscim.renderer.layer; import java.nio.ShortBuffer; -import org.oscim.renderer.TextureObject; - import android.util.Log; public class Layers { @@ -25,8 +23,8 @@ public class Layers { public Layer layers; public int lineOffset; - public Layer symbolLayers; - public int symbolOffset; + public Layer textureLayers; + public int texOffset; private Layer mCurLayer; @@ -57,10 +55,13 @@ public class Layers { if (ret == null) { if (type == Layer.LINE) ret = new LineLayer(level); - else + else if (type == Layer.POLYGON) ret = new PolygonLayer(level); + else + return null; if (l == null) { + // insert at start ret.next = layers; layers = ret; } else { @@ -81,7 +82,9 @@ public class Layers { private static int TEXTURE_VERTEX_SHORTS = 6; public int getSize() { + int size = 0; + for (Layer l = layers; l != null; l = l.next) { if (l.type == Layer.LINE) size += l.verticesCnt * LINE_VERTEX_SHORTS; @@ -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; } @@ -109,10 +112,10 @@ public class Layers { lineOffset = sbuf.position() * 2; // * short-bytes 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) { - SymbolLayer sl = (SymbolLayer) l; + for (Layer l = textureLayers; l != null; l = l.next) { + TextureLayer sl = (TextureLayer) l; sl.compile(sbuf); } } @@ -148,19 +151,25 @@ public class Layers { } 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) { VertexPool.release(l.pool); l.pool = null; l.curItem = null; } + layers = layers.next; } - for (Layer l = symbolLayers; l != null; l = l.next) { - SymbolLayer sl = (SymbolLayer) l; - if (sl.textures != null) - TextureObject.release(sl.textures); + while (textureLayers != null) { + textureLayers.clear(); + + // TextureLayer sl = (TextureLayer) textureLayers; + // if (sl.textures != null) + // TextureObject.release(sl.textures); + + textureLayers = textureLayers.next; } } } diff --git a/src/org/oscim/renderer/layer/LineLayer.java b/src/org/oscim/renderer/layer/LineLayer.java index af344359..2846bf28 100644 --- a/src/org/oscim/renderer/layer/LineLayer.java +++ b/src/org/oscim/renderer/layer/LineLayer.java @@ -35,20 +35,11 @@ public final class LineLayer extends Layer { public Line line; public float width; - // boolean isOutline; - LineLayer(int layer) { this.layer = layer; 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) { for (LineLayer l = outlines; l != null; l = l.outlines) if (link == l) @@ -272,6 +263,7 @@ public final class LineLayer extends Layer { prevY = y; x = nextX; y = nextY; + boolean flip = false; for (;;) { if (ipos < pos + length) { @@ -314,10 +306,15 @@ public final class LineLayer extends Layer { ux = (ux / a); uy = (uy / a); - // hack to avoid miter going to infinity - if (ux > 2.0f || ux < -2.0f || uy > 2.0f || uy < -2.0f) { - ux = -wy; - uy = wx; + // avoid miter going to infinity... + if (ux > 4.0f || ux < -4.0f || uy > 4.0f || uy < -4.0f) { + ux = vx - 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); ddy = (int) (uy * DIR_SCALE); + if (flip) { + ddx *= -1; + ddy *= -1; + } if (opos == VertexPoolItem.SIZE) { si = si.next = VertexPool.get(); v = si.vertices; @@ -382,6 +383,11 @@ public final class LineLayer extends Layer { ddx = (int) (ux * DIR_SCALE); ddy = (int) (uy * DIR_SCALE); + if (flip) { + ddx *= -1; + ddy *= -1; + } + v[opos++] = ox; v[opos++] = oy; v[opos++] = (short) (0 | ddx & DIR_MASK); @@ -410,6 +416,11 @@ public final class LineLayer extends Layer { dx = (short) (0 | ddx & DIR_MASK); dy = (short) (0 | ddy & DIR_MASK); + if (flip) { + ddx *= -1; + ddy *= -1; + } + v[opos++] = ox; v[opos++] = oy; v[opos++] = dx; @@ -427,6 +438,11 @@ public final class LineLayer extends Layer { dx = (short) (2 | ddx & DIR_MASK); dy = (short) (0 | ddy & DIR_MASK); + if (flip) { + ddx *= -1; + ddy *= -1; + } + v[opos++] = ox; v[opos++] = oy; v[opos++] = dx; @@ -457,7 +473,10 @@ public final class LineLayer extends Layer { ddx = (int) ((ux - vx) * DIR_SCALE); ddy = (int) ((uy - vy) * DIR_SCALE); - + if (flip) { + ddx *= -1; + ddy *= -1; + } v[opos++] = ox; v[opos++] = oy; v[opos++] = (short) (0 | ddx & DIR_MASK); @@ -472,6 +491,10 @@ public final class LineLayer extends Layer { // add last vertex twice ddx = (int) (-(ux + vx) * DIR_SCALE); ddy = (int) (-(uy + vy) * DIR_SCALE); + if (flip) { + ddx *= -1; + ddy *= -1; + } dx = (short) (2 | ddx & DIR_MASK); dy = (short) (1 | ddy & DIR_MASK); diff --git a/src/org/oscim/renderer/layer/SymbolLayer.java b/src/org/oscim/renderer/layer/SymbolLayer.java index 1a04db57..54206de5 100644 --- a/src/org/oscim/renderer/layer/SymbolLayer.java +++ b/src/org/oscim/renderer/layer/SymbolLayer.java @@ -17,12 +17,9 @@ package org.oscim.renderer.layer; import java.nio.ShortBuffer; import org.oscim.renderer.TextureObject; +import org.oscim.renderer.TextureRenderer; -import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.RectF; -import android.opengl.GLUtils; import android.util.Log; // TODO share one static texture for all poi map symabols @@ -30,28 +27,17 @@ import android.util.Log; public final class SymbolLayer extends TextureLayer { private static String TAG = SymbolLayer.class.getSimpleName(); - private final static int TEXTURE_WIDTH = 256; - private final static int TEXTURE_HEIGHT = 256; + private final static int TEXTURE_WIDTH = TextureObject.TEXTURE_WIDTH; + private final static int TEXTURE_HEIGHT = TextureObject.TEXTURE_HEIGHT; private final static float SCALE = 8.0f; private static short[] mVertices; - private static Bitmap mBitmap; - private static Canvas mCanvas; - private static int mBitmapFormat; - private static int mBitmapType; SymbolItem symbols; public SymbolLayer() { - if (mBitmap == null) { - mBitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT, - Bitmap.Config.ARGB_8888); - mCanvas = new Canvas(mBitmap); - mBitmapFormat = GLUtils.getInternalFormat(mBitmap); - mBitmapType = GLUtils.getType(mBitmap); - // - mVertices = new short[40 * 24]; - } + if (mVertices == null) + mVertices = new short[TextureRenderer.MAX_ITEMS * 24]; } public void addSymbol(SymbolItem item) { @@ -73,19 +59,23 @@ public final class SymbolLayer extends TextureLayer { } private final static int LBIT_MASK = 0xfffffffe; - private final RectF mRect = new RectF(); // TODO ... reuse texture when only symbol position changed + @Override public void compile(ShortBuffer sbuf) { + 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; - mBitmap.eraseColor(Color.TRANSPARENT); + Canvas canvas = TextureObject.getCanvas(); for (SymbolItem it = symbols; it != null;) { @@ -103,35 +93,31 @@ public final class SymbolLayer extends TextureLayer { if (y + height > TEXTURE_HEIGHT) { Log.d(TAG, "reached max symbols"); - // need to sync bitmap upload somehow??? - TextureObject to = TextureObject.get(); - TextureObject.uploadTexture(to, mBitmap, - mBitmapFormat, mBitmapType, - TEXTURE_WIDTH, TEXTURE_HEIGHT); + + 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; } } - mRect.left = x; - mRect.top = y; - mRect.right = x + width; - mRect.bottom = y + height; - // Log.d("...", "draw " + x + " " + y + " " + width + " " + height); - mCanvas.drawBitmap(it.bitmap, null, mRect, null); - // mCanvas.drawBitmap(it.bitmap, x, y, null); + canvas.drawBitmap(it.bitmap, x, y, null); float hw = width / 2.0f; float hh = height / 2.0f; - short x1, x2, x3, x4, y1, y2, y3, y4; - x1 = x3 = (short) (SCALE * (-hw)); - x2 = x4 = (short) (SCALE * (hw)); - y1 = y3 = (short) (SCALE * (hh)); - y2 = y4 = (short) (SCALE * (-hh)); + short x1 = (short) (SCALE * (-hw)); + short x2 = (short) (SCALE * (hw)); + short y1 = (short) (SCALE * (hh)); + short y2 = (short) (SCALE * (-hh)); short u1 = (short) (SCALE * x); short v1 = (short) (SCALE * y); @@ -157,40 +143,43 @@ public final class SymbolLayer extends TextureLayer { buf[pos++] = y1; buf[pos++] = u1; buf[pos++] = v2; - // top-right buf[pos++] = tx; buf[pos++] = ty; buf[pos++] = x2; - buf[pos++] = y3; + buf[pos++] = y1; buf[pos++] = u2; buf[pos++] = v2; - // bot-right buf[pos++] = tx; buf[pos++] = ty; - buf[pos++] = x4; - buf[pos++] = y4; + buf[pos++] = x2; + buf[pos++] = y2; buf[pos++] = u2; buf[pos++] = v1; - // bot-left buf[pos++] = tx; buf[pos++] = ty; - buf[pos++] = x3; + buf[pos++] = x1; buf[pos++] = y2; buf[pos++] = u1; 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.uploadTexture(to, mBitmap, - mBitmapFormat, mBitmapType, - TEXTURE_WIDTH, TEXTURE_HEIGHT); + TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices); to.next = textures; textures = to; diff --git a/src/org/oscim/renderer/layer/TextItem.java b/src/org/oscim/renderer/layer/TextItem.java new file mode 100644 index 00000000..348dff3d --- /dev/null +++ b/src/org/oscim/renderer/layer/TextItem.java @@ -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 . + */ +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 +} diff --git a/src/org/oscim/renderer/layer/TextLayer.java b/src/org/oscim/renderer/layer/TextLayer.java index aa76f9a0..4755198e 100644 --- a/src/org/oscim/renderer/layer/TextLayer.java +++ b/src/org/oscim/renderer/layer/TextLayer.java @@ -14,9 +14,218 @@ */ 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 { + + 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; + 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); + } } diff --git a/src/org/oscim/renderer/layer/TextureLayer.java b/src/org/oscim/renderer/layer/TextureLayer.java index 8df7c6f2..fac6c6ff 100644 --- a/src/org/oscim/renderer/layer/TextureLayer.java +++ b/src/org/oscim/renderer/layer/TextureLayer.java @@ -14,9 +14,18 @@ */ package org.oscim.renderer.layer; +import java.nio.ShortBuffer; + import org.oscim.renderer.TextureObject; public abstract class TextureLayer extends Layer { public TextureObject textures; + /** + * @param sbuf + * buffer to add vertices + */ + void compile(ShortBuffer sbuf) { + } + } diff --git a/src/org/oscim/theme/renderinstruction/Area.java b/src/org/oscim/theme/renderinstruction/Area.java index ff91066c..d4fae587 100644 --- a/src/org/oscim/theme/renderinstruction/Area.java +++ b/src/org/oscim/theme/renderinstruction/Area.java @@ -17,6 +17,7 @@ package org.oscim.theme.renderinstruction; import org.oscim.core.Tag; import org.oscim.theme.IRenderCallback; import org.oscim.theme.RenderThemeHandler; +import org.oscim.utils.GlUtils; import org.xml.sax.Attributes; import android.graphics.Color; @@ -88,11 +89,7 @@ public final class Area extends RenderInstruction { blend = -1; strokeWidth = 0; - color = new float[4]; - 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]; + color = GlUtils.colorToFloatP(fill); } /** @@ -128,41 +125,13 @@ public final class Area extends RenderInstruction { // Shader shader = BitmapUtils.createBitmapShader(src); // 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) { - // stroke = null; - // } else{ - // stroke = new Line() - // } + color = GlUtils.colorToFloatP(fill); - color = new float[4]; - 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]; - - 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 { + if (blend > 0) + blendColor = GlUtils.colorToFloatP(blendFill); + else blendColor = null; - } this.blend = blend; this.strokeWidth = strokeWidth; @@ -183,32 +152,10 @@ public final class Area extends RenderInstruction { // } public String style; - /** - * - */ private final int level; - /** - * - */ - // public final Paint paintFill; - /** - * - */ - // public final Paint paintOutline; - /** - * - */ public final float strokeWidth; - /** - * - */ public final float color[]; - /** - * - */ public final int fade; - public final float blendColor[]; - public final int blend; } diff --git a/src/org/oscim/theme/renderinstruction/Line.java b/src/org/oscim/theme/renderinstruction/Line.java index c97e23c6..9888c626 100644 --- a/src/org/oscim/theme/renderinstruction/Line.java +++ b/src/org/oscim/theme/renderinstruction/Line.java @@ -20,6 +20,7 @@ import java.util.regex.Pattern; import org.oscim.core.Tag; import org.oscim.theme.IRenderCallback; import org.oscim.theme.RenderThemeHandler; +import org.oscim.utils.GlUtils; import org.xml.sax.Attributes; import android.graphics.Color; @@ -121,11 +122,7 @@ public final class Line extends RenderInstruction { this.fixed = true; this.fade = -1; this.stipple = 2; - color = new float[4]; - 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]; + color = GlUtils.colorToFloatP(stroke); } private static void validate(float strokeWidth) { @@ -215,11 +212,7 @@ public final class Line extends RenderInstruction { this.cap = strokeLinecap; - color = new float[4]; - 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]; + color = GlUtils.colorToFloatP(stroke); this.width = strokeWidth; this.level = level; diff --git a/src/org/oscim/theme/renderinstruction/Text.java b/src/org/oscim/theme/renderinstruction/Text.java index abb2b534..6553883f 100644 --- a/src/org/oscim/theme/renderinstruction/Text.java +++ b/src/org/oscim/theme/renderinstruction/Text.java @@ -53,6 +53,7 @@ public final class Text extends RenderInstruction { String style = null; // boolean caption = false; float dy = 0; + for (int i = 0; i < attributes.getLength(); ++i) { String name = attributes.getLocalName(i); String value = attributes.getValue(i); @@ -82,10 +83,27 @@ public final class Text extends RenderInstruction { } 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); } + 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, float strokeWidth) { if (textKey == null) { @@ -110,9 +128,15 @@ public final class Text extends RenderInstruction { public final boolean caption; 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, int fill, int outline, float strokeWidth, float dy, boolean caption) { - super(); + // super(); this.style = style; this.textKey = textKey; @@ -123,19 +147,21 @@ public final class Text extends RenderInstruction { paint.setTextAlign(Align.CENTER); paint.setTypeface(typeface); paint.setColor(fill); + paint.setTextSize(fontSize); - stroke = new Paint(Paint.ANTI_ALIAS_FLAG); - stroke.setStyle(Style.STROKE); - stroke.setTextAlign(Align.CENTER); - stroke.setTypeface(typeface); - stroke.setColor(outline); - stroke.setStrokeWidth(strokeWidth); + if (strokeWidth > 0) { + stroke = new Paint(Paint.ANTI_ALIAS_FLAG); + stroke.setStyle(Style.STROKE); + stroke.setTextAlign(Align.CENTER); + stroke.setTypeface(typeface); + stroke.setColor(outline); + stroke.setStrokeWidth(strokeWidth); + stroke.setTextSize(fontSize); + } else + stroke = null; this.fontSize = fontSize; - paint.setTextSize(fontSize); - stroke.setTextSize(fontSize); - FontMetrics fm = paint.getFontMetrics(); fontHeight = FloatMath.ceil(Math.abs(fm.bottom) + Math.abs(fm.top)); fontDescent = FloatMath.ceil(Math.abs(fm.descent)); diff --git a/src/org/oscim/utils/FastMath.java b/src/org/oscim/utils/FastMath.java index ae46db64..8135f0b3 100644 --- a/src/org/oscim/utils/FastMath.java +++ b/src/org/oscim/utils/FastMath.java @@ -47,4 +47,11 @@ public class FastMath { } return r; } + + public static float pow(int pow) { + if (pow == 0) + return 1; + + return (pow > 0 ? (1 << pow) : (1.0f / (1 << -pow))); + } } diff --git a/src/org/oscim/utils/GlUtils.java b/src/org/oscim/utils/GlUtils.java index 2ae1a832..022cae4c 100644 --- a/src/org/oscim/utils/GlUtils.java +++ b/src/org/oscim/utils/GlUtils.java @@ -157,6 +157,24 @@ public class GlUtils { GLES20.glUniform4fv(handle, 1, c, 0); else 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; } } diff --git a/src/org/oscim/view/MapView.java b/src/org/oscim/view/MapView.java index 6174f7dc..7cd7272a 100644 --- a/src/org/oscim/view/MapView.java +++ b/src/org/oscim/view/MapView.java @@ -1,5 +1,6 @@ /* * 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 * 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.JobTile; import org.oscim.generator.MapWorker; +import org.oscim.renderer.GLRenderer; import org.oscim.renderer.MapRenderer; import org.oscim.renderer.TileGenerator; import org.oscim.theme.ExternalRenderTheme; @@ -58,7 +60,7 @@ public class MapView extends FrameLayout { public static final boolean debugFrameTime = 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; @@ -438,8 +440,9 @@ public class MapView extends FrameLayout { try { inputStream = theme.getRenderThemeAsStream(); RenderTheme t = RenderThemeHandler.getRenderTheme(inputStream); - mMapRenderer.setRenderTheme(t); - mMapWorkers[0].getMapGenerator().setRenderTheme(t); + // FIXME somehow... + GLRenderer.setRenderTheme(t); + TileGenerator.setRenderTheme(t); return true; } catch (ParserConfigurationException e) { Log.e(TAG, e.getMessage()); diff --git a/src/org/oscim/view/MapViewPosition.java b/src/org/oscim/view/MapViewPosition.java index bfbc5d17..e0dec34b 100644 --- a/src/org/oscim/view/MapViewPosition.java +++ b/src/org/oscim/view/MapViewPosition.java @@ -523,7 +523,7 @@ public class MapViewPosition { mMapScale = newScale; } - if (pivotY != 0 || pivotY != 0) + if (pivotX != 0 || pivotY != 0) moveMap(pivotX * (1.0f - scale), pivotY * (1.0f - scale)); diff --git a/src/org/oscim/view/MapZoomControls.java b/src/org/oscim/view/MapZoomControls.java index d3ce2f82..83b1322f 100644 --- a/src/org/oscim/view/MapZoomControls.java +++ b/src/org/oscim/view/MapZoomControls.java @@ -116,9 +116,10 @@ public class MapZoomControls { private final Handler mZoomControlsHideHandler; private byte mZoomLevelMax; private byte mZoomLevelMin; - private MapView mMapView; + private final MapView mMapView; MapZoomControls(Context context, final MapView mapView) { + mMapView = mapView; mZoomControls = new ZoomControls(context); mShowMapZoomControls = true; mZoomLevelMax = DEFAULT_ZOOM_LEVEL_MAX; diff --git a/src/org/oscim/renderer/TextItem.java b/src/org/oscim/view/OverlayManager.java similarity index 54% rename from src/org/oscim/renderer/TextItem.java rename to src/org/oscim/view/OverlayManager.java index 7502c78c..a2fecf77 100644 --- a/src/org/oscim/renderer/TextItem.java +++ b/src/org/oscim/view/OverlayManager.java @@ -12,34 +12,8 @@ * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ -package org.oscim.renderer; +package org.oscim.view; -import org.oscim.theme.renderinstruction.Text; - -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; - } +public class OverlayManager { } diff --git a/src/org/oscim/view/TouchHandler.java b/src/org/oscim/view/TouchHandler.java index fef7a15c..546937a1 100644 --- a/src/org/oscim/view/TouchHandler.java +++ b/src/org/oscim/view/TouchHandler.java @@ -18,6 +18,8 @@ import org.oscim.core.Tile; import android.content.Context; import android.os.CountDownTimer; +import android.os.Handler; +import android.os.Message; import android.os.SystemClock; import android.util.Log; import android.view.GestureDetector; @@ -53,6 +55,9 @@ final class TouchHandler private boolean mMoveStart; private boolean mBeginRotate; private boolean mBeginTilt; + private boolean mLongPress; + private long mLongPressTime; + private float mPosX; private float mPosY; private double mAngle; @@ -124,6 +129,7 @@ final class TouchHandler private boolean onActionCancel() { mActivePointerId = INVALID_POINTER_ID; + mLongPress = true; return true; } @@ -174,6 +180,12 @@ final class TouchHandler mPosX = event.getX(id); mPosY = event.getY(id); + if (mLongPress) { + mMapPosition.scaleMap(1 - moveY / 100, 0, 0); + mMapView.redrawMap(); + return true; + } + if (!scaling) { mMapPosition.moveMap(moveX, moveY); mMapView.redrawMap(); @@ -207,7 +219,16 @@ final class TouchHandler if (!mBeginRotate && !mBeginScale && !mBeginTilt) { if (Math.abs(r) > 0.03) 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 rcos = Math.cos(r); @@ -260,6 +281,8 @@ final class TouchHandler } multi--; + mLongPress = false; + return true; } @@ -272,6 +295,28 @@ final class TouchHandler mActivePointerId = INVALID_POINTER_ID; mScaling = false; 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; } @@ -292,7 +337,6 @@ final class TouchHandler } fling = false; } - // Log.d("mapsforge", "onDown"); return true; } @@ -353,6 +397,7 @@ final class TouchHandler Log.d("mapsforge", "long press"); mMapView.mRegionLookup.updateRegion(-1, null); } + mLongPress = true; } boolean scale2(long tick) { @@ -360,11 +405,12 @@ final class TouchHandler fling = true; if (mPrevScale >= 1) return false; + float adv = (SCALE_DURATION - tick) / SCALE_DURATION; adv = mInterpolator.getInterpolation(adv); - float scale = adv - mPrevScale; mPrevScale += scale; + scale *= 0.75; scale += 1; adv += 1; @@ -379,26 +425,26 @@ final class TouchHandler @Override public boolean onDoubleTap(MotionEvent e) { if (MapView.testRegionZoom) { - Log.d("mapsforge", "double tap"); - mMapView.mRegionLookup.updateRegion(1, mMapPosition.getOffsetPoint(mPosX, mPosY)); } else { - mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f; - mScrollY = (e.getY(0) - (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() { - scale(0); - } - }.start(); + mLongPress = true; + mLongPressTime = SystemClock.uptimeMillis(); + // mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f; + // mScrollY = (e.getY(0) - (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() { + // scale(0); + // } + // }.start(); } return true; } @@ -513,4 +559,120 @@ final class TouchHandler 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); + } + } + } + }; + } }