From c89611837fbf02dc4995c94098562884dba5c6bd Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 28 Aug 2012 09:38:41 +0200 Subject: [PATCH 001/425] cleanup --- .../mapsforge/android/glrenderer/Layer.java | 41 -------- .../android/glrenderer/PoolItem.java | 95 ------------------- .../android/glrenderer/VertexPool.java | 73 -------------- 3 files changed, 209 deletions(-) delete mode 100644 VectorTileMap/src/org/mapsforge/android/glrenderer/Layer.java delete mode 100644 VectorTileMap/src/org/mapsforge/android/glrenderer/PoolItem.java delete mode 100644 VectorTileMap/src/org/mapsforge/android/glrenderer/VertexPool.java diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/Layer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/Layer.java deleted file mode 100644 index 7c40f63..0000000 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/Layer.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012 Hannes Janetzek - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General License for more details. - * - * You should have received a copy of the GNU Lesser General License along with - * this program. If not, see . - */ -package org.mapsforge.android.glrenderer; - -class Layer { - PoolItem pool; - - protected PoolItem curItem; - - int verticesCnt; - int offset; - - final int layer; - - Layer(int l) { - layer = l; - verticesCnt = 0; - } - - float[] getNextPoolItem() { - curItem.used = PoolItem.SIZE; - - curItem.next = VertexPool.get(); - curItem = curItem.next; - - return curItem.vertices; - } - -} diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/PoolItem.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/PoolItem.java deleted file mode 100644 index 1be769c..0000000 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/PoolItem.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2012 Hannes Janetzek - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General License for more details. - * - * You should have received a copy of the GNU Lesser General License along with - * this program. If not, see . - */ - -package org.mapsforge.android.glrenderer; - -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; - -// TODO use byte[] for half-float, not converting on compilation (in glThread) - -class PoolItem { - final float[] vertices; - int used; - PoolItem next; - - PoolItem() { - vertices = new float[SIZE]; - used = 0; - } - - static int SIZE = 128; - - private static final float FLOAT_HALF_PREC = 5.96046E-8f; - private static final float FLOAT_HALF_MAX = 65504f; - - private static ByteBuffer byteBuffer = ByteBuffer.allocate(SIZE * 4); - private static IntBuffer intBuffer = byteBuffer.asIntBuffer(); - private static FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); - private static int[] intArray = new int[SIZE]; - - static void toHalfFloat(PoolItem item, short[] data) { - floatBuffer.position(0); - floatBuffer.put(item.vertices, 0, item.used); - intBuffer.position(0); - intBuffer.get(intArray, 0, item.used); - - int out = 0; - for (int j = 0; j < item.used; j++) { - float flt = item.vertices[j]; - int f = intArray[j]; - - if (f == 0x0000000) { - // == 0 - data[out++] = (short) 0x0000; - } else if (f == 0x80000000) { - // == -0 - data[out++] = (short) 0x8000; - } else if (f == 0x3f800000) { - // == 1 - data[out++] = (short) 0x3c00; - } else if (f == 0xbf800000) { - // == -1 - data[out++] = (short) 0xbc00; - } else if (flt > FLOAT_HALF_MAX) { - if (flt == Float.POSITIVE_INFINITY) { - data[out++] = (short) 0x7c00; - } else { - data[out++] = (short) 0x7bff; - } - } else if (flt < -FLOAT_HALF_MAX) { - if (flt == Float.NEGATIVE_INFINITY) { - data[out++] = (short) 0xfc00; - } else { - data[out++] = (short) 0xfbff; - } - } else if (flt > 0f && flt < FLOAT_HALF_PREC) { - data[out++] = (short) 0x0001; - } else if (flt < 0f && flt > -FLOAT_HALF_PREC) { - data[out++] = (short) 0x8001; - } else { - // maybe just ignore and set 0? -- we'll see. when this happens - if (f == 0x7fc00000) - throw new UnsupportedOperationException( - "NaN to half conversion not supported!"); - - data[out++] = (short) (((f >> 16) & 0x8000) - | ((((f & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) - | ((f >> 13) & 0x03ff)); - } - } - } -} diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexPool.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexPool.java deleted file mode 100644 index e453ef0..0000000 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/VertexPool.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2012 Hannes Janetzek - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General License for more details. - * - * You should have received a copy of the GNU Lesser General License along with - * this program. If not, see . - */ - -package org.mapsforge.android.glrenderer; - -import android.annotation.SuppressLint; - -class VertexPool { - private static final int POOL_LIMIT = 8192; - - @SuppressLint("UseValueOf") - private static final Boolean lock = new Boolean(true); - - static private PoolItem pool = null; - static private int count = 0; - - static PoolItem get() { - synchronized (lock) { - - if (count == 0) - return new PoolItem(); - - count--; - - PoolItem it = pool; - pool = pool.next; - it.used = 0; - it.next = null; - return it; - } - } - - static void add(PoolItem items) { - if (items == null) - return; - - synchronized (lock) { - PoolItem last = items; - - // limit pool items - while (count < POOL_LIMIT) { - if (last.next == null) { - break; - } - last = last.next; - count++; - } - - // clear references - PoolItem tmp2, tmp = last.next; - while (tmp != null) { - tmp2 = tmp; - tmp = tmp.next; - tmp2.next = null; - } - - last.next = pool; - pool = items; - } - } -} From f39cea276a317e7f6ae260776953c00e76951db7 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 28 Aug 2012 09:39:17 +0200 Subject: [PATCH 002/425] use half the textures, take from non-visible tiles when needed --- .../org/mapsforge/android/glrenderer/MapRenderer.java | 4 +--- .../org/mapsforge/android/glrenderer/TextRenderer.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java index 7bbf219..67e4929 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java @@ -345,8 +345,6 @@ else if (t.isReady || t.newData) { // Log.d(TAG, "--------------------------------<<"); } - private Object lock = new Object(); - private boolean updateVisibleList(double x, double y, int zdir) { byte zoomLevel = mLastZoom; float scale = mLastScale; @@ -1144,7 +1142,7 @@ public void onSurfaceChanged(GL10 glUnused, int width, int height) { mVBOs.add(new VertexBufferObject(mVboIds[i])); // Set up textures - TextRenderer.init(numTiles * 2); + TextRenderer.init(numTiles); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GLES20.GL_DEPTH_TEST); diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextRenderer.java index b22100d..11e06c3 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextRenderer.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextRenderer.java @@ -188,6 +188,16 @@ static boolean drawToTexture(GLMapTile tile) { 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; From 2ca6a2dfcf650662bbe3043faafef47d4599e0c5 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 28 Aug 2012 09:39:26 +0200 Subject: [PATCH 003/425] fix --- .../src/org/mapsforge/database/pbmap/MapDatabase.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/database/pbmap/MapDatabase.java b/VectorTileMap/src/org/mapsforge/database/pbmap/MapDatabase.java index bf0de44..7a773b0 100644 --- a/VectorTileMap/src/org/mapsforge/database/pbmap/MapDatabase.java +++ b/VectorTileMap/src/org/mapsforge/database/pbmap/MapDatabase.java @@ -85,9 +85,9 @@ protected boolean removeEldestEntry(Entry e) { } }); - private final static int MAX_TILE_TAGS = 100; private final static float REF_TILE_SIZE = 4096.0f; + private int MAX_TILE_TAGS = 100; private Tag[] curTags = new Tag[MAX_TILE_TAGS]; private int mCurTagCnt; @@ -300,7 +300,8 @@ private boolean decodeTileTags() throws IOException { // FIXME ... if (mCurTagCnt >= MAX_TILE_TAGS) { - Tag[] tmp = new Tag[mCurTagCnt + 10]; + MAX_TILE_TAGS = mCurTagCnt + 10; + Tag[] tmp = new Tag[MAX_TILE_TAGS]; System.arraycopy(curTags, 0, tmp, 0, mCurTagCnt); curTags = tmp; } From 0f6aa997ac2384c22d3ccc99b0d5a2e3a6b4e038 Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 28 Aug 2012 10:10:30 +0200 Subject: [PATCH 004/425] fix regression, make sure not to upload a tile that will be cleared --- .../android/glrenderer/MapGenerator.java | 3 - .../android/glrenderer/MapRenderer.java | 73 +++++++++---------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java index 602c518..b0a1ff8 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java @@ -538,9 +538,6 @@ public boolean executeJob(MapGeneratorJob mapGeneratorJob) { mCurPolyLayer = null; mCurLineLayer = null; - tile.newData = true; - tile.isLoading = false; - return true; } diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java index 67e4929..4ecc293 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java @@ -208,7 +208,7 @@ private static int updateTileDistances() { if (diff == 0) { dx = (t.pixelX + h) - x; dy = (t.pixelY + h) - y; - t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * 0.5f; + t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * 0.25f; // t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f; } else if (diff > 0) { // tile zoom level is child of current @@ -225,7 +225,7 @@ private static int updateTileDistances() { dy = ((t.pixelY + h) << -diff) - y; t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) - * (-diff * 0.25f); + * (-diff * 0.5f); // t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f); } @@ -493,11 +493,10 @@ private boolean updateVisibleList(double x, double y, int zdir) { mUpdateTiles = true; } - int active = 0; // see FIXME in passTile - synchronized (mTiles) { - active = updateTileDistances(); - } + // synchronized (mTiles) { + updateTileDistances(); + // } if (mJobList.size() > 0) mMapView.addJobs(mJobList); @@ -505,9 +504,8 @@ private boolean updateVisibleList(double x, double y, int zdir) { int removes = mTiles.size() - CACHE_TILES; if (removes > 20) { - - Log.d(TAG, "---- remove " + removes + " on " + zoomLevel + " active:" - + active + "------"); + // Log.d(TAG, "---- remove " + removes + " on " + zoomLevel + " active:" + // + active + "------"); Collections.sort(mTileList, mTileDistanceSort); limitCache(removes); } @@ -611,8 +609,8 @@ else if (mLastScale - scale > 0.2 || mLastScale - scale < -0.2) { mTileY = tileY; mLastZoom = zoomLevel; - if (zdir > 0) - Log.d(TAG, "prefetch parent"); + // if (zdir > 0) + // Log.d(TAG, "prefetch parent"); if (changedZoom) { // need to update visible list first when zoom level changes @@ -651,9 +649,6 @@ public synchronized boolean passTile(MapGeneratorJob mapGeneratorJob) { return true; } - if (!timing && tile.isVisible) - mMapView.requestRender(); - int size = mTilesLoaded.size(); if (size > MAX_TILES_IN_QUEUE) { // remove uploaded tiles @@ -670,34 +665,38 @@ public synchronized boolean passTile(MapGeneratorJob mapGeneratorJob) { if (size > MAX_TILES_IN_QUEUE) { // FIXME pass tile via queue back to mainloop instead... - synchronized (mTiles) { - Collections.sort(mTilesLoaded, mTileDistanceSort); - } + // synchronized (mTiles) { + // Collections.sort(mTilesLoaded, mTileDistanceSort); + // } // clear loaded but not used tiles while (size-- > MAX_TILES_IN_QUEUE) { GLMapTile t = mTilesLoaded.get(size); // FIXME race condition: tile could be uploaded as proxy // therefore sync with upload tile data - synchronized (t) { - - // dont remove tile if currently used or is direct parent - // or child of currently active tile - if (t.isActive || childIsActive(t)) - // (t.parent != null && t.parent.isActive)) - { - // Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive); - continue; - } - mTilesLoaded.remove(size); - // Log.d(TAG, "remove unused tile data: " + t); - clearTile(t); - // TODO could also remove from mTileHash/List ? + // synchronized (t) { + // dont remove tile if currently used or is direct parent + // or child of currently active tile + if (t.isActive || childIsActive(t) || + (tile.parent != null && tile.parent.isActive)) { + // Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive); + continue; } + mTilesLoaded.remove(size); + // Log.d(TAG, "remove unused tile data: " + t); + clearTile(t); + // TODO could also remove from mTileHash/List ? + // } } } } + tile.newData = true; + tile.isLoading = false; + mTilesLoaded.add(0, tile); + if (!timing) + mMapView.requestRender(); + return true; } @@ -767,13 +766,13 @@ private boolean uploadTileData(GLMapTile tile) { } } - synchronized (tile) { - if (!tile.newData) - return false; + // synchronized (tile) { + if (!tile.newData) + return false; - tile.isReady = true; - tile.newData = false; - } + tile.isReady = true; + tile.newData = false; + // } int lineSize = LineLayers.sizeOf(tile.lineLayers); int polySize = PolygonLayers.sizeOf(tile.polygonLayers); From 4ad0e4f2ca582482834c8366561cf6f95559a64d Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 29 Aug 2012 10:45:19 +0200 Subject: [PATCH 005/425] - fix adding vbo id twice to pool. null tile.vbo in clear. phew - improve depth buffer clipping, no more overlapping of polygons and use it to clip proxy tiles too - simplify limitCache function - linelayers, dont use systemarraycopy for small arrays --- .../src/org/mapsforge/android/MapView.java | 2 +- .../android/glrenderer/LineLayer.java | 511 ++++++++++++------ .../android/glrenderer/MapGenerator.java | 1 + .../android/glrenderer/MapRenderer.java | 499 ++++++++--------- .../android/glrenderer/PolygonLayers.java | 48 +- .../android/glrenderer/ShortItem.java | 4 +- .../android/glrenderer/ShortPool.java | 24 +- 7 files changed, 635 insertions(+), 454 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/android/MapView.java b/VectorTileMap/src/org/mapsforge/android/MapView.java index af376cf..d443d2e 100644 --- a/VectorTileMap/src/org/mapsforge/android/MapView.java +++ b/VectorTileMap/src/org/mapsforge/android/MapView.java @@ -94,7 +94,7 @@ public class MapView extends GLSurfaceView { private IMapRenderer mMapRenderer; private JobQueue mJobQueue; private MapWorker mMapWorkers[]; - private int mNumMapWorkers = 6; + private int mNumMapWorkers = 4; private JobParameters mJobParameters; public DebugSettings debugSettings; private String mMapFile; diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayer.java index b0bf8d7..4b64bee 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayer.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/LineLayer.java @@ -35,9 +35,11 @@ class LineLayer { ShortItem pool; protected ShortItem curItem; + + // number of vertices this layer holds int verticesCnt; + // vertices offset of this layer in VBO int offset; - short[] mVertex; LineLayer(int layer, Line line, float width, boolean outline) { this.layer = layer; @@ -55,57 +57,14 @@ void addOutline(LineLayer link) { outlines = link; } - private static ShortItem addTwoVertex(short[] vertex, ShortItem item) { - ShortItem it = item; - - if (it.used + 6 >= ShortItem.SIZE) { - - if (it.used == ShortItem.SIZE) { - it.next = ShortPool.get(); - it = it.next; - - } else { - System.arraycopy(vertex, 0, it.vertices, it.used, 6); - it.used += 6; - - it.next = ShortPool.get(); - it = it.next; - - System.arraycopy(vertex, 6, it.vertices, it.used, 6); - it.used += 6; - - return it; - } - } - - System.arraycopy(vertex, 0, it.vertices, it.used, 12); - it.used += 12; - - return it; - } - - private static ShortItem addVertex(short[] vertex, ShortItem item) { - ShortItem it = item; - - if (it.used == ShortItem.SIZE) { - it.next = ShortPool.get(); - it = it.next; - } - - System.arraycopy(vertex, 0, it.vertices, it.used, 6); - it.used += 6; - - return it; - } - /* * line extrusion is based on code from GLMap (https://github.com/olofsj/GLMap/) by olofsj -- need some way to know * how the road connects to set the ending angles */ - void addLine(float[] pointArray, int pos, int length) { + void addLine(float[] points, int pos, int length) { float x, y, nextX, nextY, prevX, prevY, ux, uy, vx, vy, wx, wy; float a; - int pointPos = pos; + int ipos = pos; boolean rounded = false; boolean squared = false; @@ -114,23 +73,14 @@ void addLine(float[] pointArray, int pos, int length) { else if (line.cap == Cap.SQUARE) squared = true; - if (pool == null) { - curItem = ShortPool.get(); - pool = curItem; - - mVertex = new short[12]; - } - // amount of vertices used verticesCnt += length + (rounded ? 6 : 2); - ShortItem si = curItem; - - x = pointArray[pointPos++]; - y = pointArray[pointPos++]; + x = points[ipos++]; + y = points[ipos++]; - nextX = pointArray[pointPos++]; - nextY = pointArray[pointPos++]; + nextX = points[ipos++]; + nextY = points[ipos++]; // Calculate triangle corners for the given width vx = nextX - x; @@ -144,47 +94,116 @@ else if (line.cap == Cap.SQUARE) ux = -vy; uy = vx; - float uxw = ux; - float uyw = uy; - - float vxw = vx; - float vyw = vy; int tsize = Tile.TILE_SIZE; - short v[] = mVertex; + if (pool == null) { + pool = curItem = ShortPool.get(); + } - v[0] = (short) (x * S); - v[1] = (short) (y * S); + ShortItem si = curItem; + short v[] = si.vertices; + int opos = si.used; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } boolean outside = (x <= 0 || x >= tsize || y <= 0 || y >= tsize) - && (x - vxw <= 0 || x - vxw >= tsize || y - vyw <= 0 || y - vyw >= tsize); + && (x - vx <= 0 || x - vx >= tsize || y - vy <= 0 || y - vy >= tsize); + + short ox, oy, dx, dy; + + ox = (short) (x * S); + oy = (short) (y * S); if (rounded && !outside) { - v[2] = (short) ((uxw - vxw) * S1000); - v[3] = (short) ((uyw - vyw) * S1000); - v[4] = -1; - v[5] = 1; - si = addVertex(v, si); - si = addVertex(v, si); - - v[2] = (short) (-(uxw + vxw) * S1000); - v[3] = (short) (-(uyw + vyw) * S1000); - v[4] = 1; - v[5] = 1; - si = addVertex(v, si); + + // For rounded line edges + dx = (short) ((ux - vx) * S1000); + dy = (short) ((uy - vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 1; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 1; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + dx = (short) (-(ux + vx) * S1000); + dy = (short) (-(uy + vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = 1; + v[opos + 5] = 1; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } // Start of line - v[2] = (short) ((uxw) * S1000); - v[3] = (short) ((uyw) * S1000); - v[4] = -1; - v[5] = 0; - si = addVertex(v, si); - - v[2] = (short) ((-uxw) * S1000); - v[3] = (short) ((-uyw) * S1000); - v[4] = 1; - v[5] = 0; - si = addVertex(v, si); + dx = (short) (ux * S1000); + dy = (short) (uy * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = (short) (-dx); + v[opos + 3] = (short) (-dy); + v[opos + 4] = 1; + v[opos + 5] = 0; + opos += 6; } else { // outside means line is probably clipped @@ -192,29 +211,61 @@ else if (line.cap == Cap.SQUARE) // for now, just extend the line a little if (squared) { - vxw = 0; - vyw = 0; + vx = 0; + vy = 0; } else if (!outside) { - vxw *= 0.5; - vyw *= 0.5; + vx *= 0.5; + vy *= 0.5; } if (rounded) verticesCnt -= 2; - // Add the first point twice to be able to draw with GL_TRIANGLE_STRIP - v[2] = (short) ((uxw - vxw) * S1000); - v[3] = (short) ((uyw - vyw) * S1000); - v[4] = -1; - v[5] = 0; - si = addVertex(v, si); - si = addVertex(v, si); - - v[2] = (short) (-(uxw + vxw) * S1000); - v[3] = (short) (-(uyw + vyw) * S1000); - v[4] = 1; - v[5] = 0; - si = addVertex(v, si); + dx = (short) ((ux - vx) * S1000); + dy = (short) ((uy - vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + dx = (short) (-(ux + vx) * S1000); + dy = (short) (-(uy + vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = 1; + v[opos + 5] = 0; + opos += 6; } prevX = x; @@ -222,9 +273,9 @@ else if (line.cap == Cap.SQUARE) x = nextX; y = nextY; - for (; pointPos < pos + length;) { - nextX = pointArray[pointPos++]; - nextY = pointArray[pointPos++]; + for (; ipos < pos + length;) { + nextX = points[ipos++]; + nextY = points[ipos++]; // Unit vector pointing back to previous node vx = prevX - x; @@ -261,22 +312,43 @@ else if (line.cap == Cap.SQUARE) } } - uxw = ux * S1000; - uyw = uy * S1000; - - v[6] = v[0] = (short) (x * S); - v[7] = v[1] = (short) (y * S); + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } - v[2] = (short) uxw; - v[3] = (short) uyw; - v[4] = -1; - v[5] = 0; + ox = (short) (x * S); + oy = (short) (y * S); + + dx = (short) (ux * S1000); + dy = (short) (uy * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } - v[8] = (short) -uxw; - v[9] = (short) -uyw; - v[10] = 1; - v[11] = 0; - si = addTwoVertex(v, si); + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = (short) -dx; + v[opos + 3] = (short) -dy; + v[opos + 4] = 1; + v[opos + 5] = 0; + opos += 6; prevX = x; prevY = y; @@ -295,71 +367,164 @@ else if (line.cap == Cap.SQUARE) ux = vy; uy = -vx; - uxw = ux; - uyw = uy; - - vxw = vx; - vyw = vy; - outside = (x <= 0 || x >= tsize || y <= 0 || y >= tsize) - && (x - vxw <= 0 || x - vxw >= tsize || y - vyw <= 0 || y - vyw >= tsize); + && (x - vx <= 0 || x - vx >= tsize || y - vy <= 0 || y - vy >= tsize); + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } - v[0] = (short) (x * S); - v[1] = (short) (y * S); + ox = (short) (x * S); + oy = (short) (y * S); if (rounded && !outside) { - v[2] = (short) ((uxw) * S1000); - v[3] = (short) ((uyw) * S1000); - v[4] = -1; - v[5] = 0; - si = addVertex(v, si); - - v[2] = (short) ((-uxw) * S1000); - v[3] = (short) ((-uyw) * S1000); - v[4] = 1; - v[5] = 0; - si = addVertex(v, si); + + dx = (short) (ux * S1000); + dy = (short) (uy * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = (short) -dx; + v[opos + 3] = (short) -dy; + v[opos + 4] = 1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } // For rounded line edges - v[2] = (short) ((uxw - vxw) * S1000); - v[3] = (short) ((uyw - vyw) * S1000); - v[4] = -1; - v[5] = -1; - si = addVertex(v, si); - - v[2] = (short) (-(uxw + vxw) * S1000); - v[3] = (short) (-(uyw + vyw) * S1000); - v[4] = 1; - v[5] = -1; - si = addVertex(v, si); - si = addVertex(v, si); + dx = (short) ((ux - vx) * S1000); + dy = (short) ((uy - vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = -1; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + dx = (short) (-(ux + vx) * S1000); + dy = (short) (-(uy + vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = 1; + v[opos + 5] = -1; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = 1; + v[opos + 5] = -1; + opos += 6; } else { if (squared) { - vxw = 0; - vyw = 0; + vx = 0; + vy = 0; } else if (!outside) { - vxw *= 0.5; - vyw *= 0.5; + vx *= 0.5; + vy *= 0.5; } if (rounded) verticesCnt -= 2; - v[2] = (short) ((uxw) * S1000); - v[3] = (short) ((uyw) * S1000); - v[4] = -1; - v[5] = 0; - si = addVertex(v, si); - - v[2] = (short) (-(uxw + vxw) * S1000); - v[3] = (short) (-(uyw + vyw) * S1000); - v[4] = 1; - v[5] = 0; - si = addVertex(v, si); - si = addVertex(v, si); + dx = (short) ((ux - vx) * S1000); + dy = (short) ((uy - vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = -1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + dx = (short) (-(ux + vx) * S1000); + dy = (short) (-(uy + vy) * S1000); + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = 1; + v[opos + 5] = 0; + opos += 6; + + if (opos == ShortItem.SIZE) { + si.used = ShortItem.SIZE; + si.next = ShortPool.get(); + si = si.next; + opos = 0; + v = si.vertices; + } + + v[opos + 0] = ox; + v[opos + 1] = oy; + v[opos + 2] = dx; + v[opos + 3] = dy; + v[opos + 4] = 1; + v[opos + 5] = 0; + opos += 6; } + si.used = opos; curItem = si; } } diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java index b0a1ff8..318dadc 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapGenerator.java @@ -511,6 +511,7 @@ public boolean executeJob(MapGeneratorJob mapGeneratorJob) { PolygonLayers.clear(mPolyLayers); mLineLayers = null; mPolyLayers = null; + mLabels = null; tile.isLoading = false; return false; } diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java index 4ecc293..4abe4f5 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java @@ -85,6 +85,7 @@ public class MapRenderer implements org.mapsforge.android.IMapRenderer { private final MapView mMapView; private static ArrayList mJobList; private static ArrayList mVBOs; + private static TileCacheKey mTileCacheKey; private static HashMap mTiles; @@ -152,6 +153,7 @@ class TilesData { @Override public void setRenderTheme(RenderTheme t) { int bg = t.getMapBackground(); + Log.d(TAG, "BG" + bg); float[] c = new float[4]; c[0] = (bg >> 16 & 0xff) / 255.0f; c[1] = (bg >> 8 & 0xff) / 255.0f; @@ -246,103 +248,78 @@ private static boolean childIsActive(GLMapTile t) { return false; } - private static void limitCache(int remove) { + private static boolean tileInUse(GLMapTile t) { byte z = mLastZoom; - // Log.d(TAG, "--------------------------------"); - for (int j = mTileList.size() - 1, cnt = 0; cnt < remove && j > 0; j--) { - GLMapTile t = mTileList.remove(j); - // dont remove tile used by renderthread or mapgenerator - // FIXME set tile loading state in main thread - if (t.isLoading) { + if (t.zoomLevel == z + 1) { + if (t.parent != null + && t.parent.isActive + && !(t.parent.isReady || t.parent.newData)) + return true; + } else if (t.zoomLevel == z - 1) { + if (childIsActive(t)) + return true; - Log.d(TAG, "cancel loading " + t + " " + (t.zoomLevel - mCurZ) - + " " + (t.zoomLevel - mDrawZ) + " " + t.distance); - t.isCanceled = true; - } - else if (t.isActive) { - Log.d(TAG, "EEEK removing active " + t + " " + (t.zoomLevel - mCurZ) - + " " + (t.zoomLevel - mDrawZ) + " " + t.distance); - mTileList.add(t); - continue; + } else if (t.zoomLevel == z - 2) { + for (int i = 0; i < 4; i++) { + if (t.child[i] != null && childIsActive(t.child[i])) + return true; } - // check if this tile is used as proxy for not yet drawn active tile - // TODO to be simplified... - else if (t.isReady || t.newData) { - if (t.zoomLevel == z + 1) { - if (t.parent != null && t.parent.isActive - && !(t.parent.isReady || t.parent.newData)) { - mTileList.add(t); - Log.d(TAG, "EEEK removing active proxy child"); - continue; - } - } else if (t.zoomLevel == z - 1) { - GLMapTile c = null; - for (int i = 0; i < 4; i++) { - c = t.child[i]; - if (c != null && c.isActive && !(c.isReady || c.newData)) - break; - c = null; - } + } + return false; + } - if (c != null) { - Log.d(TAG, "EEEK removing active proxy parent"); - mTileList.add(t); - continue; - } - } else if (t.zoomLevel == z - 2) { - GLMapTile c = null, c2 = null; - for (int i = 0; i < 4; i++) { - c = t.child[i]; - if (c != null) { - for (int k = 0; k < 4; k++) { - c2 = c.child[k]; - if (c2 != null && c2.isActive - && !(c2.isReady || c2.newData)) - break; - - c2 = null; - } - if (c2 != null) - break; - } - c = null; - } + private static void limitCache(int remove) { - if (c != null) { - // Log.d(TAG, "EEEK removing active second level proxy parent"); + for (int j = mTileList.size() - 1, cnt = 0; cnt < remove && j > 0; j--) { + + GLMapTile t = mTileList.remove(j); + + synchronized (t) { + // dont remove tile used by renderthread or mapgenerator + // FIXME set tile loading state in main thread + // if (t.isLoading) { + // Log.d(TAG, "cancel loading " + t + " " + (t.zoomLevel - mCurZ) + // + " " + (t.zoomLevel - mDrawZ) + " " + t.distance); + // t.isCanceled = true; + // } + // else + if (t.isActive || t.isLoading) { + Log.d(TAG, "EEEK removing active " + t + " " + (t.zoomLevel - mCurZ) + + " " + (t.zoomLevel - mDrawZ) + " " + t.distance); + mTileList.add(t); + continue; + } else if (t.isReady || t.newData) { + // check if this tile is used as proxy for not yet drawn active tile + + if (tileInUse(t)) { + Log.d(TAG, "X removing proxy: " + t); mTileList.add(t); continue; } } - } - // Log.d(TAG, ">>> remove " + t + " " + (t.zoomLevel - mCurZ) - // + " " + t.distance); - cnt++; - mTileCacheKey.set(t.tileX, t.tileY, t.zoomLevel); - mTiles.remove(mTileCacheKey); - - // clear references to this tile - for (int i = 0; i < 4; i++) { - if (t.child[i] != null) - t.child[i].parent = null; - } + cnt++; + mTileCacheKey.set(t.tileX, t.tileY, t.zoomLevel); + mTiles.remove(mTileCacheKey); - if (t.parent != null) { + // clear references to this tile for (int i = 0; i < 4; i++) { - if (t.parent.child[i] == t) { - t.parent.child[i] = null; - break; - } + if (t.child[i] != null) + t.child[i].parent = null; } - } - synchronized (mVBOs) { + if (t.parent != null) { + for (int i = 0; i < 4; i++) { + if (t.parent.child[i] == t) { + t.parent.child[i] = null; + break; + } + } + } clearTile(t); } } - // Log.d(TAG, "--------------------------------<<"); } private boolean updateVisibleList(double x, double y, int zdir) { @@ -370,27 +347,29 @@ private boolean updateVisibleList(double x, double y, int zdir) { return false; int max = newTiles.tiles.length - 1; - long limit = (long) Math.pow(2, zoomLevel) - 1; + long limit = (long) Math.pow(2, zoomLevel); if (tileTop < 0) tileTop = 0; if (tileLeft < 0) tileLeft = 0; if (tileBottom >= limit) - tileBottom = limit; + tileBottom = limit - 1; if (tileRight >= limit) - tileRight = limit; + tileRight = limit - 1; - for (long tileY = tileTop; tileY <= tileBottom; tileY++) { - for (long tileX = tileLeft; tileX <= tileRight; tileX++) { + for (long yy = tileTop; yy <= tileBottom; yy++) { + for (long xx = tileLeft; xx <= tileRight; xx++) { // FIXME if (tiles == max) break; - GLMapTile tile = mTiles.get(mTileCacheKey.set(tileX, tileY, zoomLevel)); + long tx = xx;// % limit; + + GLMapTile tile = mTiles.get(mTileCacheKey.set(tx, yy, zoomLevel)); if (tile == null) { - tile = new GLMapTile(tileX, tileY, zoomLevel); + tile = new GLMapTile(tx, yy, zoomLevel); TileCacheKey key = new TileCacheKey(mTileCacheKey); // FIXME use sparse matrix or sth. @@ -399,15 +378,15 @@ private boolean updateVisibleList(double x, double y, int zdir) { Log.d(TAG, "eeek collision"); mTileList.add(tile); - mTileCacheKey.set((tileX >> 1), (tileY >> 1), (byte) (zoomLevel - 1)); + mTileCacheKey.set((tx >> 1), (yy >> 1), (byte) (zoomLevel - 1)); tile.parent = mTiles.get(mTileCacheKey); - int idx = (int) ((tileX & 0x01) + 2 * (tileY & 0x01)); + int idx = (int) ((tx & 0x01) + 2 * (yy & 0x01)); // set this tile to be child of its parent if (tile.parent != null) { tile.parent.child[idx] = tile; } else if (zdir > 0 && zoomLevel > 0) { - tile.parent = new GLMapTile(tileX >> 1, tileY >> 1, + tile.parent = new GLMapTile(tx >> 1, yy >> 1, (byte) (zoomLevel - 1)); key = new TileCacheKey(mTileCacheKey); if (mTiles.put(key, tile.parent) != null) @@ -436,6 +415,7 @@ private boolean updateVisibleList(double x, double y, int zdir) { MapGeneratorJob job = new MapGeneratorJob(tile.parent, mJobParameter, mDebugSettings); + if (!mJobList.contains(job)) mJobList.add(job); } @@ -443,19 +423,10 @@ private boolean updateVisibleList(double x, double y, int zdir) { } } - // scramble tile draw order: might help to make gl - // pipelines more independent... just a guess :) - // for (int i = 1; i < tiles / 2; i += 2) { - // GLMapTile tmp = newTiles.tiles[i]; - // newTiles.tiles[i] = newTiles.tiles[tiles - i]; - // newTiles.tiles[tiles - i] = tmp; - // } - newTiles.cnt = tiles; // pass new tile list to glThread synchronized (this) { - for (int i = 0; i < curTiles.cnt; i++) { boolean found = false; @@ -467,18 +438,12 @@ private boolean updateVisibleList(double x, double y, int zdir) { if (curTiles.tiles[i] == newTiles.tiles[j]) found = true; - if (!found) { + if (!found) curTiles.tiles[i].isActive = false; - // activeList.remove(newTiles.tiles[i]); - } } - for (int i = 0; i < tiles; i++) { - if (!newTiles.tiles[i].isActive) { - newTiles.tiles[i].isActive = true; - // activeList.add(newTiles.tiles[i]); - } - } + for (int i = 0; i < tiles; i++) + newTiles.tiles[i].isActive = true; TilesData tmp = curTiles; curTiles = newTiles; @@ -493,19 +458,14 @@ private boolean updateVisibleList(double x, double y, int zdir) { mUpdateTiles = true; } - // see FIXME in passTile - // synchronized (mTiles) { updateTileDistances(); - // } if (mJobList.size() > 0) mMapView.addJobs(mJobList); int removes = mTiles.size() - CACHE_TILES; - if (removes > 20) { - // Log.d(TAG, "---- remove " + removes + " on " + zoomLevel + " active:" - // + active + "------"); + if (removes > 10) { Collections.sort(mTileList, mTileDistanceSort); limitCache(removes); } @@ -534,18 +494,23 @@ private static void setTileChildren(GLMapTile tile) { } private static void clearTile(GLMapTile t) { + t.newData = false; + t.isLoading = false; + t.isReady = false; + LineLayers.clear(t.lineLayers); PolygonLayers.clear(t.polygonLayers); - if (t.vbo != null) - mVBOs.add(t.vbo); - t.labels = null; t.lineLayers = null; t.polygonLayers = null; - t.newData = false; - t.isLoading = false; - t.isReady = false; + + if (t.vbo != null) { + synchronized (mVBOs) { + mVBOs.add(t.vbo); + } + } + t.vbo = null; } /** @@ -624,6 +589,8 @@ else if (mLastScale - scale > 0.2 || mLastScale - scale < -0.2) { mCurY = y; mCurZ = zoomLevel; mCurScale = scale; + // Log.d(TAG, "draw at:" + tileX + " " + tileY + " " + mCurX + " " + mCurY + // + " " + mCurZ); } } @@ -632,25 +599,13 @@ else if (mLastScale - scale > 0.2 || mLastScale - scale < -0.2) { if (changedPos) updateVisibleList(x, y, zdir); - } - private static final int MAX_TILES_IN_QUEUE = 40; - - /** - * called by MapWorkers when tile is loaded - */ - @Override - public synchronized boolean passTile(MapGeneratorJob mapGeneratorJob) { - GLMapTile tile = (GLMapTile) mapGeneratorJob.tile; + int size = mTilesLoaded.size(); + if (size < MAX_TILES_IN_QUEUE) + return; - if (tile.isCanceled) { - Log.d(TAG, "passTile: canceld " + tile); - clearTile(tile); - return true; - } + synchronized (mTilesLoaded) { - int size = mTilesLoaded.size(); - if (size > MAX_TILES_IN_QUEUE) { // remove uploaded tiles for (int i = 0; i < size;) { GLMapTile t = mTilesLoaded.get(i); @@ -663,40 +618,53 @@ public synchronized boolean passTile(MapGeneratorJob mapGeneratorJob) { i++; } + // clear loaded but not used tiles if (size > MAX_TILES_IN_QUEUE) { - // FIXME pass tile via queue back to mainloop instead... - // synchronized (mTiles) { - // Collections.sort(mTilesLoaded, mTileDistanceSort); - // } - // clear loaded but not used tiles - while (size-- > MAX_TILES_IN_QUEUE) { + while (size-- > MAX_TILES_IN_QUEUE - 20) { GLMapTile t = mTilesLoaded.get(size); - // FIXME race condition: tile could be uploaded as proxy - // therefore sync with upload tile data - // synchronized (t) { - // dont remove tile if currently used or is direct parent - // or child of currently active tile - if (t.isActive || childIsActive(t) || - (tile.parent != null && tile.parent.isActive)) { - // Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive); - continue; + + synchronized (t) { + if (tileInUse(t)) { + Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive); + continue; + } + mTilesLoaded.remove(size); + Log.d(TAG, "remove unused tile data: " + t); + + clearTile(t); } - mTilesLoaded.remove(size); - // Log.d(TAG, "remove unused tile data: " + t); - clearTile(t); - // TODO could also remove from mTileHash/List ? - // } } } } + } + + private static final int MAX_TILES_IN_QUEUE = 40; + + /** + * called by MapWorkers when tile is loaded + */ + @Override + public synchronized boolean passTile(MapGeneratorJob mapGeneratorJob) { + GLMapTile tile = (GLMapTile) mapGeneratorJob.tile; + + // if (tile.isCanceled) { + // // no one should be able to use this tile now, mapgenerator passed it, + // // glthread does nothing until newdata is set. + // Log.d(TAG, "passTile: canceled " + tile); + // clearTile(tile); + // return true; + // } + tile.newData = true; tile.isLoading = false; - mTilesLoaded.add(0, tile); - if (!timing) mMapView.requestRender(); + synchronized (mTilesLoaded) { + mTilesLoaded.add(0, tile); + } + return true; } @@ -754,96 +722,90 @@ private boolean uploadTileData(GLMapTile tile) { } // Upload line data to vertex buffer object + synchronized (tile) { + if (!tile.newData) + return false; - // FIXME do this in main thread: - if (tile.vbo == null) { synchronized (mVBOs) { - if (mVBOs.size() < 1) { - Log.d(TAG, "uploadTileData, no VBOs left"); - return false; + if (tile.vbo == null) { + if (mVBOs.size() < 1) { + Log.d(TAG, "uploadTileData, no VBOs left"); + return false; + } + tile.vbo = mVBOs.remove(mVBOs.size() - 1); } - tile.vbo = mVBOs.remove(mVBOs.size() - 1); } - } - - // synchronized (tile) { - if (!tile.newData) - return false; - - tile.isReady = true; - tile.newData = false; - // } - int lineSize = LineLayers.sizeOf(tile.lineLayers); - int polySize = PolygonLayers.sizeOf(tile.polygonLayers); - int newSize = lineSize + polySize; + int lineSize = LineLayers.sizeOf(tile.lineLayers); + int polySize = PolygonLayers.sizeOf(tile.polygonLayers); + int newSize = lineSize + polySize; - if (newSize == 0) { - LineLayers.clear(tile.lineLayers); - PolygonLayers.clear(tile.polygonLayers); - tile.lineLayers = null; - tile.polygonLayers = null; - return false; - } + if (newSize == 0) { + LineLayers.clear(tile.lineLayers); + PolygonLayers.clear(tile.polygonLayers); + tile.lineLayers = null; + tile.polygonLayers = null; + return false; + } - // Log.d(TAG, "uploadTileData, " + tile); + Log.d(TAG, "uploadTileData, " + tile); - glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); + glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); - sbuf = shortBuffer[uploadCnt]; + sbuf = shortBuffer[uploadCnt]; - // add fill coordinates - newSize += 8; + // add fill coordinates + newSize += 8; - // FIXME probably not a good idea to do this in gl thread... - if (sbuf == null || sbuf.capacity() < newSize) { - ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES).order( - ByteOrder.nativeOrder()); - sbuf = bbuf.asShortBuffer(); - shortBuffer[uploadCnt] = sbuf; - sbuf.put(mFillCoords, 0, 8); - } + // FIXME probably not a good idea to do this in gl thread... + if (sbuf == null || sbuf.capacity() < newSize) { + ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES).order( + ByteOrder.nativeOrder()); + sbuf = bbuf.asShortBuffer(); + shortBuffer[uploadCnt] = sbuf; + sbuf.put(mFillCoords, 0, 8); + } - sbuf.clear(); - sbuf.position(8); + sbuf.clear(); + sbuf.position(8); - PolygonLayers.compileLayerData(tile.polygonLayers, sbuf); - LineLayers.compileLayerData(tile.lineLayers, sbuf); - sbuf.flip(); + PolygonLayers.compileLayerData(tile.polygonLayers, sbuf); + LineLayers.compileLayerData(tile.lineLayers, sbuf); + sbuf.flip(); - newSize *= SHORT_BYTES; + newSize *= SHORT_BYTES; - if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4) { - GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf); - } else { - mBufferMemoryUsage -= tile.vbo.size; - tile.vbo.size = newSize; - glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW); - mBufferMemoryUsage += tile.vbo.size; - } - tile.lineOffset = (8 + polySize) * SHORT_BYTES; + if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4) { + GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf); + } else { + mBufferMemoryUsage -= tile.vbo.size; + tile.vbo.size = newSize; + glBufferData(GL_ARRAY_BUFFER, tile.vbo.size, sbuf, GL_DYNAMIC_DRAW); + mBufferMemoryUsage += tile.vbo.size; + } + tile.lineOffset = (8 + polySize) * SHORT_BYTES; - // tile.isLoading = false; + // tile.isLoading = false; - uploadCnt++; + uploadCnt++; + tile.isReady = true; + tile.newData = false; + } return true; } - private float[] mClearColor; + private float[] mClearColor = null; private static long mRedrawCnt = 0; @Override public void onDrawFrame(GL10 glUnused) { long start = 0, poly_time = 0, clear_time = 0; - if (mClearColor != null) { - glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], - mClearColor[3]); - mClearColor = null; - } - - glClear(GLES20.GL_COLOR_BUFFER_BIT);// | GLES20.GL_DEPTH_BUFFER_BIT); + glClear(GLES20.GL_COLOR_BUFFER_BIT + | GLES20.GL_DEPTH_BUFFER_BIT + // | GLES20.GL_STENCIL_BUFFER_BIT + ); if (mInitial) return; @@ -903,8 +865,7 @@ public void onDrawFrame(GL10 glUnused) { if (!setTileScissor(tile, 1)) continue; - if (updateTextures < 4 && tile.texture == null - && TextRenderer.drawToTexture(tile)) + if (tile.texture == null && TextRenderer.drawToTexture(tile)) updateTextures++; if (tile.newData) { @@ -930,8 +891,8 @@ public void onDrawFrame(GL10 glUnused) { if (updateTextures > 0) { TextRenderer.compileTextures(); - if (updateTextures == 4) - mMapView.requestRender(); + // if (updateTextures == 4) + // mMapView.requestRender(); } // if (GlUtils.checkGlOutOfMemory("upload: " + mBufferMemoryUsage) // && LIMIT_BUFFERS > MB) @@ -941,18 +902,25 @@ public void onDrawFrame(GL10 glUnused) { clear_time = (SystemClock.uptimeMillis() - start); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GLES20.glEnable(GLES20.GL_DEPTH_TEST); + glEnable(GLES20.GL_POLYGON_OFFSET_FILL); for (int i = 0; i < tileCnt; i++) { - if (tiles[i].isVisible && !tiles[i].isReady) { - drawProxyTile(tiles[i]); + if (tiles[i].isVisible && tiles[i].isReady) { + drawTile(tiles[i], 1); } } + // proxies are clipped to the region where nothing was drawn to depth buffer for (int i = 0; i < tileCnt; i++) { - if (tiles[i].isVisible && tiles[i].isReady) { - drawTile(tiles[i], 1); + if (tiles[i].isVisible && !tiles[i].isReady) { + drawProxyTile(tiles[i]); } } + + glDisable(GLES20.GL_POLYGON_OFFSET_FILL); + glDisable(GLES20.GL_DEPTH_TEST); + mDrawCount = 0; glEnable(GL_BLEND); @@ -1006,6 +974,9 @@ private static void drawTile(GLMapTile tile, float div) { boolean clipped = false; + // GLES20.glEnable(GLES20.GL_DEPTH_TEST); + // glEnable(GLES20.GL_POLYGON_OFFSET_FILL); + for (; pl != null || ll != null;) { int lnext = Integer.MAX_VALUE; int pnext = Integer.MAX_VALUE; @@ -1024,28 +995,31 @@ private static void drawTile(GLMapTile tile, float div) { if (!clipped) { clipped = true; - glDisable(GLES20.GL_DEPTH_TEST); + // glDisable(GLES20.GL_DEPTH_TEST); } } else { // nastay if (!clipped) PolygonLayers.drawPolygons(null, 0, mMVPMatrix, mDrawZ, mDrawScale, mDrawCount, true); - else { - GLES20.glEnable(GLES20.GL_DEPTH_TEST); - glEnable(GLES20.GL_POLYGON_OFFSET_FILL); - GLES20.glPolygonOffset(0, mDrawCount); - } + // else { + // GLES20.glEnable(GLES20.GL_DEPTH_TEST); + // glEnable(GLES20.GL_POLYGON_OFFSET_FILL); + // GLES20.glPolygonOffset(0, mDrawCount); + // } glEnable(GL_BLEND); ll = LineLayers.drawLines(tile, ll, pnext, mMVPMatrix, div, mDrawZ, mDrawScale); - glDisable(GLES20.GL_DEPTH_TEST); - glDisable(GLES20.GL_POLYGON_OFFSET_FILL); + // glDisable(GLES20.GL_DEPTH_TEST); + // glDisable(GLES20.GL_POLYGON_OFFSET_FILL); } } + // glDisable(GLES20.GL_POLYGON_OFFSET_FILL); + // glDisable(GLES20.GL_DEPTH_TEST); + mDrawCount++; } @@ -1059,7 +1033,7 @@ private static boolean drawProxyChild(GLMapTile tile) { continue; } - if (c.isReady && c.isVisible) { + if (c.isReady) { drawTile(c, 2); drawn++; } @@ -1069,29 +1043,29 @@ private static boolean drawProxyChild(GLMapTile tile) { } private static void drawProxyTile(GLMapTile tile) { - if (mDrawScale < 1.5f) { - if (!drawProxyChild(tile)) { - if (tile.parent != null) { - if (tile.parent.isReady) { - drawTile(tile.parent, 0.5f); - } else { - GLMapTile p = tile.parent.parent; - if (p != null && p.isReady) - drawTile(p, 0.25f); - } - } - } - } else { - if (tile.parent != null && tile.parent.isReady) { - drawTile(tile.parent, 0.5f); - } else if (!drawProxyChild(tile)) { - if (tile.parent != null) { - GLMapTile p = tile.parent.parent; - if (p != null && p.isReady) - drawTile(p, 0.25f); - } + // if (mDrawScale < 1.5f) { + // if (!drawProxyChild(tile)) { + // if (tile.parent != null) { + // if (tile.parent.isReady) { + // drawTile(tile.parent, 0.5f); + // } else { + // GLMapTile p = tile.parent.parent; + // if (p != null && p.isReady) + // drawTile(p, 0.25f); + // } + // } + // } + // } else { + if (tile.parent != null && tile.parent.isReady) { + drawTile(tile.parent, 0.5f); + } else if (!drawProxyChild(tile)) { + if (tile.parent != null) { + GLMapTile p = tile.parent.parent; + if (p != null && p.isReady) + drawTile(p, 0.25f); } } + // } } @Override @@ -1147,7 +1121,14 @@ public void onSurfaceChanged(GL10 glUnused, int width, int height) { glDisable(GLES20.GL_DEPTH_TEST); glDepthMask(false); glDisable(GL_DITHER); - glClearColor(0.98f, 0.98f, 0.97f, 1.0f); + + if (mClearColor != null) { + + glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], + mClearColor[3]); + } else { + glClearColor(0.98f, 0.98f, 0.97f, 1.0f); + } glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayers.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayers.java index f20dcd0..44ab69f 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayers.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/PolygonLayers.java @@ -17,8 +17,6 @@ import static android.opengl.GLES20.GL_BLEND; import static android.opengl.GLES20.GL_EQUAL; import static android.opengl.GLES20.GL_INVERT; -import static android.opengl.GLES20.GL_KEEP; -import static android.opengl.GLES20.GL_NEVER; import static android.opengl.GLES20.GL_STENCIL_TEST; import static android.opengl.GLES20.GL_TRIANGLE_FAN; import static android.opengl.GLES20.GL_TRIANGLE_STRIP; @@ -84,6 +82,10 @@ private static void fillPolygons(int count, double zoom, float scale) { // do not modify stencil buffer glStencilMask(0); + // clip with depth mask + // GLES20.glEnable(GLES20.GL_DEPTH_TEST); + // glEnable(GLES20.GL_POLYGON_OFFSET_FILL); + for (int c = 0; c < count; c++) { PolygonLayer l = mFillPolys[c]; @@ -137,6 +139,9 @@ else if (alpha < 0) if (blend) glDisable(GL_BLEND); + + // glDisable(GLES20.GL_DEPTH_TEST); + // glDisable(GLES20.GL_POLYGON_OFFSET_FILL); } static PolygonLayer drawPolygons(PolygonLayer layer, int next, @@ -156,6 +161,8 @@ static PolygonLayer drawPolygons(PolygonLayer layer, int next, PolygonLayer l = layer; + boolean first = clip; + for (; l != null && l.layer < next; l = l.next) { // fade out polygon layers (set in RederTheme) if (l.area.fade > 0 && l.area.fade > zoom) @@ -166,18 +173,34 @@ static PolygonLayer drawPolygons(PolygonLayer layer, int next, glColorMask(false, false, false, false); // never pass the test, i.e. always apply first stencil op (sfail) - glStencilFunc(GL_NEVER, 0, 0xff); + glStencilFunc(GLES20.GL_ALWAYS, 0, 0xff); // clear stencilbuffer glStencilMask(0xFF); // glClear(GL_STENCIL_BUFFER_BIT); // clear stencilbuffer (tile region) - glStencilOp(GL_ZERO, GL_KEEP, GL_KEEP); + glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); + + if (first) { + // glEnable(GLES20.GL_DEPTH_TEST); + // glEnable(GLES20.GL_POLYGON_OFFSET_FILL); + GLES20.glPolygonOffset(0, drawCount); + GLES20.glDepthMask(true); + GLES20.glDepthFunc(GLES20.GL_LESS); + } + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + if (first) { + first = false; + GLES20.glDepthMask(false); + GLES20.glDepthFunc(GLES20.GL_EQUAL); + // glDisable(GLES20.GL_DEPTH_TEST); + } + // stencil op for stencil method polygon drawing - glStencilOp(GL_INVERT, GL_KEEP, GL_KEEP); + glStencilOp(GL_INVERT, GL_INVERT, GL_INVERT); } mFillPolys[cnt] = l; @@ -199,9 +222,8 @@ static PolygonLayer drawPolygons(PolygonLayer layer, int next, glDisable(GL_STENCIL_TEST); - if (clip) { + if (clip && first) drawDepthClip(drawCount); - } // required on GalaxyII, Android 2.3.3 (cant just VAA enable once...) GLES20.glDisableVertexAttribArray(hPolygonVertexPosition); @@ -243,19 +265,13 @@ static PolygonLayer drawPolygons(PolygonLayer layer, int next, static void drawDepthClip(short drawCount) { glColorMask(false, false, false, false); - glEnable(GLES20.GL_DEPTH_TEST); - glEnable(GLES20.GL_POLYGON_OFFSET_FILL); - // GLES20.GL_PO + // glEnable(GLES20.GL_DEPTH_TEST); + // glEnable(GLES20.GL_POLYGON_OFFSET_FILL); GLES20.glPolygonOffset(0, drawCount); - // int i[] = new int[1]; - // GLES20.glGetIntegerv(GLES20.GL_POLYGON_OFFSET_UNITS, i, 0); - // Log.d("...", "UNITS " + i[0]); - // - // System.out.println("set offset: " + drawCount); GLES20.glDepthMask(true); - GLES20.glDepthFunc(GLES20.GL_ALWAYS); + GLES20.glDepthFunc(GLES20.GL_LESS); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortItem.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortItem.java index 37a6608..e668f24 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortItem.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2012 Hannes Janetzek * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software @@ -25,5 +25,5 @@ public class ShortItem { } // must be multiple of 6 and 4 (expected in LineLayer/PolygonLayer) - static final int SIZE = 240; + static final int SIZE = 480; } diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortPool.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortPool.java index 4b0e71c..d4074a1 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortPool.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/ShortPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2012 Hannes Janetzek * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software @@ -17,7 +17,7 @@ import android.util.Log; public class ShortPool { - private static final int POOL_LIMIT = 6000; + private static final int POOL_LIMIT = 2500; static private ShortItem pool = null; static private int count = 0; @@ -31,6 +31,9 @@ static synchronized void finish() { static synchronized ShortItem get() { + if (pool == null && count > 0) { + Log.d("ShortPool", "XXX wrong count: " + count); + } if (pool == null) { countAll++; return new ShortItem(); @@ -44,7 +47,7 @@ static synchronized ShortItem get() { for (ShortItem tmp = pool; tmp != null; tmp = tmp.next) c++; - Log.d("ShortPool", "eek wrong count: " + count + " left" + c); + Log.d("ShortPool", "XXX wrong count: " + count + " left" + c); return new ShortItem(); } @@ -55,10 +58,16 @@ static synchronized ShortItem get() { return it; } + // private static float load = 1.0f; + // private static int loadCount = 0; + static synchronized void add(ShortItem items) { if (items == null) return; + // int pall = countAll; + // int pcnt = count; + // limit pool items if (countAll < POOL_LIMIT) { @@ -66,6 +75,8 @@ static synchronized void add(ShortItem items) { while (true) { count++; + // load += (float) last.used / ShortItem.SIZE; + // loadCount++; if (last.next == null) break; @@ -75,6 +86,8 @@ static synchronized void add(ShortItem items) { last.next = pool; pool = items; + // Log.d("Pool", "added: " + (count - pcnt) + " " + count + " " + countAll + // + " load: " + (load / loadCount)); } else { // int cleared = 0; @@ -85,9 +98,14 @@ static synchronized void add(ShortItem items) { countAll--; + // load += (float) prev.used / ShortItem.SIZE; + // loadCount++; + prev.next = null; } + // Log.d("Pool", "dropped: " + (pall - countAll) + " " + count + " " + // + countAll + " load: " + (load / loadCount)); } } } From 11d544cc98645e7682351707b731d03fc03b0988 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 29 Aug 2012 10:45:50 +0200 Subject: [PATCH 006/425] less logging --- .../mapsforge/android/glrenderer/MapRenderer.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java index 4abe4f5..ac1140e 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java @@ -625,11 +625,11 @@ else if (mLastScale - scale > 0.2 || mLastScale - scale < -0.2) { synchronized (t) { if (tileInUse(t)) { - Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive); + // Log.d(TAG, "keep unused tile data: " + t + " " + t.isActive); continue; } mTilesLoaded.remove(size); - Log.d(TAG, "remove unused tile data: " + t); + // Log.d(TAG, "remove unused tile data: " + t); clearTile(t); } @@ -748,7 +748,7 @@ private boolean uploadTileData(GLMapTile tile) { return false; } - Log.d(TAG, "uploadTileData, " + tile); + // Log.d(TAG, "uploadTileData, " + tile); glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); @@ -889,11 +889,9 @@ public void onDrawFrame(GL10 glUnused) { } } - if (updateTextures > 0) { + if (updateTextures > 0) TextRenderer.compileTextures(); - // if (updateTextures == 4) - // mMapView.requestRender(); - } + // if (GlUtils.checkGlOutOfMemory("upload: " + mBufferMemoryUsage) // && LIMIT_BUFFERS > MB) // LIMIT_BUFFERS -= MB; From 5d7ed446713c633d8a88cc68ae4f0d457c6c5210 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 29 Aug 2012 10:46:03 +0200 Subject: [PATCH 007/425] update copyright --- .../src/org/mapsforge/android/glrenderer/TextItem.java | 2 +- .../src/org/mapsforge/android/glrenderer/TextTexture.java | 2 +- VectorTileMap/src/org/mapsforge/database/pbmap/Tags.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextItem.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextItem.java index 5a8848e..ffde6f4 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextItem.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextItem.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2012 Hannes Janetzek * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextTexture.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextTexture.java index eefe4fa..08239bc 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/TextTexture.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/TextTexture.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2012 Hannes Janetzek * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software diff --git a/VectorTileMap/src/org/mapsforge/database/pbmap/Tags.java b/VectorTileMap/src/org/mapsforge/database/pbmap/Tags.java index 30ee50b..e2db2d9 100644 --- a/VectorTileMap/src/org/mapsforge/database/pbmap/Tags.java +++ b/VectorTileMap/src/org/mapsforge/database/pbmap/Tags.java @@ -1,5 +1,5 @@ /* - * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2012 Hannes Janetzek * * This program is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software From aafc2d1e6dc571c022bb60ef396ec89346663b4b Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 29 Aug 2012 10:46:16 +0200 Subject: [PATCH 008/425] theme tweaks --- .../rendertheme/osmarender/osmarender.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml b/VectorTileMap/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml index 37f219a..e87a8cc 100644 --- a/VectorTileMap/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml +++ b/VectorTileMap/src/org/mapsforge/android/rendertheme/osmarender/osmarender.xml @@ -64,7 +64,7 @@ - + @@ -175,8 +175,10 @@ - - + + + + @@ -259,10 +261,10 @@ - - + - + + @@ -290,8 +292,8 @@ - + @@ -483,7 +485,7 @@ - + From a2cd907d6e68b29a5eeb881dc61472d8b5ea0522 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 29 Aug 2012 11:01:19 +0200 Subject: [PATCH 009/425] cancel loading when tile is removed from cache --- .../mapsforge/android/glrenderer/MapRenderer.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java index ac1140e..792a32d 100644 --- a/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java +++ b/VectorTileMap/src/org/mapsforge/android/glrenderer/MapRenderer.java @@ -278,14 +278,12 @@ private static void limitCache(int remove) { synchronized (t) { // dont remove tile used by renderthread or mapgenerator // FIXME set tile loading state in main thread - // if (t.isLoading) { - // Log.d(TAG, "cancel loading " + t + " " + (t.zoomLevel - mCurZ) - // + " " + (t.zoomLevel - mDrawZ) + " " + t.distance); - // t.isCanceled = true; - // } - // else - if (t.isActive || t.isLoading) { - Log.d(TAG, "EEEK removing active " + t + " " + (t.zoomLevel - mCurZ) + if (t.isLoading) { + Log.d(TAG, "cancel loading " + t + " " + (t.zoomLevel - mCurZ) + + " " + (t.zoomLevel - mDrawZ) + " " + t.distance); + t.isCanceled = true; + } else if (t.isActive || t.isLoading) { + Log.d(TAG, "X removing active " + t + " " + (t.zoomLevel - mCurZ) + " " + (t.zoomLevel - mDrawZ) + " " + t.distance); mTileList.add(t); continue; From e632e829dea618293d6c85e3c45f5908bb20703d Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 3 Sep 2012 00:13:13 +0200 Subject: [PATCH 010/425] - making oscimap default backend - added about-screen - added TreeTile for Tile lookup, dropping that HashMap - using simple line-shader instead of std-derivatives one, about twice as faster here - use distance calculation from MapRenderer - removing TileScheduler - no need for MapGeneratorJob, pass MapTile directly to MapWorkers - added two-finger tap gestures for zoom-in/out - added tub/tron rendertheme - started caching for oscimap - add x/y coordinates to MapPosition, using it in MapRenderer - create tag hash when needed - no need for long tile coordinates max zoomlevel 31 should suffice --- VectorTileMap/AndroidManifest.xml | 1 + VectorTileMap/assets/info.xml | 9 +- VectorTileMap/assets/mapsforge_logo.png | Bin 28415 -> 0 bytes .../layout/activity_advanced_map_viewer.xml | 8 +- VectorTileMap/res/menu/options_menu.xml | 9 +- .../res/menu/options_menu_pre_honeycomb.xml | 6 + VectorTileMap/res/values-de/strings.xml | 4 +- VectorTileMap/res/values-fi/strings.xml | 6 - VectorTileMap/res/values-it/strings.xml | 4 +- VectorTileMap/res/values/arrays.xml | 62 +- VectorTileMap/res/values/strings.xml | 5 +- VectorTileMap/res/values/styles.xml | 1 - .../org/mapsforge/android/IMapRenderer.java | 6 +- .../org/mapsforge/android/MapActivity.java | 2 +- .../org/mapsforge/android/MapScaleBar.java | 19 +- .../src/org/mapsforge/android/MapView.java | 210 +--- .../mapsforge/android/MapViewPosition.java | 65 +- .../mapsforge/android/MapViewProjection.java | 31 +- .../mapsforge/android/MapZoomControls.java | 12 +- .../android/glrenderer/GLMapTile.java | 23 +- .../android/glrenderer/LineLayers.java | 22 +- .../android/glrenderer/MapGenerator.java | 116 +- .../android/glrenderer/MapRenderer.java | 840 +++++++-------- .../android/glrenderer/PolygonLayers.java | 59 +- .../mapsforge/android/glrenderer/Shaders.java | 54 +- .../android/glrenderer/ShortPool.java | 2 +- .../android/glrenderer/TextRenderer.java | 68 +- .../android/glrenderer/TreeTile.java | 152 +++ .../android/glrenderer/WayDecorator.java | 8 +- .../android/input/ScaleListener.java | 117 ++- .../mapsforge/android/input/TouchHandler.java | 241 +++-- .../android/mapgenerator/IMapGenerator.java | 4 +- .../android/mapgenerator/JobParameters.java | 82 -- .../android/mapgenerator/JobQueue.java | 81 +- .../mapgenerator/MapDatabaseFactory.java | 2 +- .../android/mapgenerator/MapDownloader.java | 43 - .../android/mapgenerator/MapGeneratorJob.java | 182 ---- .../android/mapgenerator/MapTile.java | 14 +- .../android/mapgenerator/MapWorker.java | 8 +- .../mapsforge/android/mapgenerator/Theme.java | 3 - .../android/mapgenerator/TileCacheKey.java | 84 -- .../android/mapgenerator/TileScheduler.java | 68 -- .../android/rendertheme/IRenderCallback.java | 4 + .../rendertheme/InternalRenderTheme.java | 4 +- .../android/rendertheme/PositiveRule.java | 6 +- .../android/rendertheme/RenderTheme.java | 9 +- .../rendertheme/RenderThemeHandler.java | 14 +- .../rendertheme/osmarender/osmarender.xml | 4 +- .../rendertheme/osmarender/tronrender.xml | 992 ++++++++++++++++++ .../android/swrenderer/GLMapTile.java | 2 +- .../android/swrenderer/MapGenerator.java | 29 +- .../android/swrenderer/MapRenderer.java | 289 +++-- .../src/org/mapsforge/app/TileMap.java | 104 +- .../mapsforge/app/filepicker/FilePicker.java | 9 +- .../app/preferences/EditPreferences.java | 7 +- .../src/org/mapsforge/core/GeoPoint.java | 18 +- .../src/org/mapsforge/core/MapPosition.java | 82 +- .../mapsforge/core/MercatorProjection.java | 34 +- VectorTileMap/src/org/mapsforge/core/Tag.java | 16 +- .../src/org/mapsforge/core/Tile.java | 94 +- .../mapsforge/database/pbmap/MapDatabase.java | 335 ++++-- 61 files changed, 2753 insertions(+), 2032 deletions(-) delete mode 100644 VectorTileMap/assets/mapsforge_logo.png create mode 100644 VectorTileMap/src/org/mapsforge/android/glrenderer/TreeTile.java delete mode 100644 VectorTileMap/src/org/mapsforge/android/mapgenerator/JobParameters.java delete mode 100644 VectorTileMap/src/org/mapsforge/android/mapgenerator/MapDownloader.java delete mode 100644 VectorTileMap/src/org/mapsforge/android/mapgenerator/MapGeneratorJob.java delete mode 100644 VectorTileMap/src/org/mapsforge/android/mapgenerator/TileCacheKey.java delete mode 100644 VectorTileMap/src/org/mapsforge/android/mapgenerator/TileScheduler.java create mode 100644 VectorTileMap/src/org/mapsforge/android/rendertheme/osmarender/tronrender.xml diff --git a/VectorTileMap/AndroidManifest.xml b/VectorTileMap/AndroidManifest.xml index a4905fc..dcdbeb2 100755 --- a/VectorTileMap/AndroidManifest.xml +++ b/VectorTileMap/AndroidManifest.xml @@ -29,6 +29,7 @@ + \ No newline at end of file diff --git a/VectorTileMap/assets/info.xml b/VectorTileMap/assets/info.xml index 6a03b6e..9bb5d5e 100644 --- a/VectorTileMap/assets/info.xml +++ b/VectorTileMap/assets/info.xml @@ -6,9 +6,10 @@ -

mapsforge logo
Version 0.2.4

-

This software is a part of the mapsforge project and distributed under the LGPL3 license.

-

Map data © OpenStreetMap contributors, CC-BY-SA.

-

Please report all bugs and feature requests either via our issue tracker or our public mapsforge-dev mailing list.

+

mapsforge logo
Version 0.2.1

+

OpenScienceMap is distributed under the LGPL3 license.

+

This software is based on mapsforge library 0.2.4.

+

Map data © OpenStreetMap contributors, still using CC-BY-SA.

+

Please report all bugs and feature requests via our issue tracker

\ No newline at end of file diff --git a/VectorTileMap/assets/mapsforge_logo.png b/VectorTileMap/assets/mapsforge_logo.png deleted file mode 100644 index 7e054869bd9eedfbe8d261622ee2ea1048d25246..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28415 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 m%M$>=Y_^a{S?b!yr2hxQbyIVj0?da10000 - + android:visibility="gone" />--> \ No newline at end of file diff --git a/VectorTileMap/res/menu/options_menu.xml b/VectorTileMap/res/menu/options_menu.xml index bc29d58..965415e 100644 --- a/VectorTileMap/res/menu/options_menu.xml +++ b/VectorTileMap/res/menu/options_menu.xml @@ -47,6 +47,9 @@ + @@ -58,9 +61,9 @@ android:showAsAction="never" android:title="@string/menu_mapfile"/> - + diff --git a/VectorTileMap/res/menu/options_menu_pre_honeycomb.xml b/VectorTileMap/res/menu/options_menu_pre_honeycomb.xml index bd8e33e..6cc3be0 100644 --- a/VectorTileMap/res/menu/options_menu_pre_honeycomb.xml +++ b/VectorTileMap/res/menu/options_menu_pre_honeycomb.xml @@ -48,6 +48,9 @@ + @@ -61,6 +64,9 @@ + diff --git a/VectorTileMap/res/values-de/strings.xml b/VectorTileMap/res/values-de/strings.xml index d667815..3415ebf 100755 --- a/VectorTileMap/res/values-de/strings.xml +++ b/VectorTileMap/res/values-de/strings.xml @@ -1,10 +1,10 @@ - + angloamerikanisch diff --git a/VectorTileMap/res/values-fi/strings.xml b/VectorTileMap/res/values-fi/strings.xml index 3d3ce7f..95db533 100644 --- a/VectorTileMap/res/values-fi/strings.xml +++ b/VectorTileMap/res/values-fi/strings.xml @@ -1,11 +1,5 @@ - - Mapfile - PostGIS - OpenScienceMap - - hyvin pieni pieni diff --git a/VectorTileMap/res/values-it/strings.xml b/VectorTileMap/res/values-it/strings.xml index b5e0565..da87b44 100755 --- a/VectorTileMap/res/values-it/strings.xml +++ b/VectorTileMap/res/values-it/strings.xml @@ -1,10 +1,10 @@ - + minuscola diff --git a/VectorTileMap/res/values/arrays.xml b/VectorTileMap/res/values/arrays.xml index f34e3ba..ac47270 100644 --- a/VectorTileMap/res/values/arrays.xml +++ b/VectorTileMap/res/values/arrays.xml @@ -1,31 +1,37 @@ - - MAP_READER - POSTGIS_READER - PBMAP_READER - - POSTGIS_READER - - - imperial - metric - - metric - - - 0.7 - 0.85 - 1.0 - 1.3 - 1.6 - - 1.0 - - - Map - Routes - Overlays - etc - + + + + + POSTGIS_READER + PBMAP_READER + + + PBMAP_READER + + + imperial + metric + + + metric + + + 0.7 + 0.85 + 1.0 + 1.3 + 1.6 + + + 1.0 + + + Map + Routes + Overlays + etc + + \ No newline at end of file diff --git a/VectorTileMap/res/values/strings.xml b/VectorTileMap/res/values/strings.xml index 264fa79..abbf7c4 100755 --- a/VectorTileMap/res/values/strings.xml +++ b/VectorTileMap/res/values/strings.xml @@ -1,8 +1,8 @@ - Mapfile - PostGIS + + PostGIS OpenScienceMap @@ -59,6 +59,7 @@ Preferences Render theme Default Theme + Tube Theme Select XML file … Screenshot JPEG (lossy) diff --git a/VectorTileMap/res/values/styles.xml b/VectorTileMap/res/values/styles.xml index 705ab3a..f0c0969 100644 --- a/VectorTileMap/res/values/styles.xml +++ b/VectorTileMap/res/values/styles.xml @@ -3,7 +3,6 @@ - - - - \ No newline at end of file From 16331a860cfd94105f68080d72a382d6bfe8e48c Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 28 Oct 2012 17:30:30 +0100 Subject: [PATCH 084/425] fix against overflowing max polygon offset (whatever that value might be...) --- .../src/org/oscim/renderer/GLRenderer.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/VectorTileMap/src/org/oscim/renderer/GLRenderer.java b/VectorTileMap/src/org/oscim/renderer/GLRenderer.java index 6c92596..aaf70c7 100644 --- a/VectorTileMap/src/org/oscim/renderer/GLRenderer.java +++ b/VectorTileMap/src/org/oscim/renderer/GLRenderer.java @@ -176,8 +176,10 @@ void setVisible(int y, int x1, int x2) { } }; - /** @param mapView - * the MapView */ + /** + * @param mapView + * the MapView + */ public GLRenderer(MapView mapView) { mMapView = mapView; @@ -522,6 +524,8 @@ static void draw() { GLES20.glEnable(GL_DEPTH_TEST); GLES20.glEnable(GL_POLYGON_OFFSET_FILL); + mDrawCount = 0; + for (int i = 0; i < tileCnt; i++) { MapTile t = tiles[i]; if (t.isVisible && t.isReady) @@ -541,7 +545,8 @@ static void draw() { GLES20.glDisable(GL_POLYGON_OFFSET_FILL); GLES20.glDisable(GL_DEPTH_TEST); - mDrawCount = 0; + // Log.d(TAG, "tiles: " + mDrawCount); + mDrawSerial++; GLES20.glEnable(GL_BLEND); @@ -624,6 +629,12 @@ private static void drawTile(MapTile tile) { GLES20.glPolygonOffset(0, mDrawCount++); + // seems there are not infinite offset units possible + // this should suffice for at least two rows, i.e. + // having not two neighbours with the same depth + if (mDrawCount == 20) + mDrawCount = 0; + GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); boolean clipped = false; @@ -792,6 +803,7 @@ public void onSurfaceCreated(GL10 gl, EGLConfig config) { // String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS); // Log.d(TAG, "Extensions: " + ext); + // GLES20.GL_POLYGON_OFFSET_UNITS LineRenderer.init(); PolygonRenderer.init(); // TextRenderer.init(); From d4188d39ba0cfdbac86c9e948d709749d83bbb29 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 28 Oct 2012 17:31:03 +0100 Subject: [PATCH 085/425] increase min-distance point reduction for overlay paths --- .../src/org/oscim/overlay/PathOverlay.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/VectorTileMap/src/org/oscim/overlay/PathOverlay.java b/VectorTileMap/src/org/oscim/overlay/PathOverlay.java index de53c5e..79a838f 100644 --- a/VectorTileMap/src/org/oscim/overlay/PathOverlay.java +++ b/VectorTileMap/src/org/oscim/overlay/PathOverlay.java @@ -45,6 +45,7 @@ public class PathOverlay extends Overlay { class RenderPath extends RenderOverlay { private static final byte MAX_ZOOM = 20; + private static final int MIN_DIST = 4; // pre-projected points to zoomlovel 20 private int[] mPreprojected; @@ -143,7 +144,7 @@ public synchronized void update(MapPosition curPos, boolean positionChanged, // skip too near points int dx = x - px; int dy = y - py; - if ((i == 0) || dx > 2 || dx < -2 || dy > 2 || dy < -2) { + if ((i == 0) || dx > MIN_DIST || dx < -MIN_DIST || dy > MIN_DIST || dy < -MIN_DIST) { projected[i + 0] = px = x; projected[i + 1] = py = y; i += 2; @@ -189,11 +190,13 @@ public void setPaint(final Paint pPaint) { mPaint = pPaint; } - /** Draw a great circle. Calculate a point for every 100km along the path. + /** + * Draw a great circle. Calculate a point for every 100km along the path. * @param startPoint * start point of the great circle * @param endPoint - * end point of the great circle */ + * end point of the great circle + */ public void addGreatCircle(final GeoPoint startPoint, final GeoPoint endPoint) { synchronized (mPoints) { @@ -207,13 +210,15 @@ public void addGreatCircle(final GeoPoint startPoint, final GeoPoint endPoint) { } } - /** Draw a great circle. + /** + * Draw a great circle. * @param startPoint * start point of the great circle * @param endPoint * end point of the great circle * @param numberOfPoints - * number of points to calculate along the path */ + * number of points to calculate along the path + */ public void addGreatCircle(final GeoPoint startPoint, final GeoPoint endPoint, final int numberOfPoints) { // adapted from page From 309c70b1838a3991588dd7387ecc67daf27a6e1a Mon Sep 17 00:00:00 2001 From: jeff Date: Tue, 30 Oct 2012 11:56:46 +0100 Subject: [PATCH 086/425] TileMapApp: improve POI overlays --- TileMapApp/AndroidManifest.xml | 18 +- .../res/drawable-mdpi/bonuspack_bubble.9.png | Bin 1686 -> 741 bytes TileMapApp/res/layout/bonuspack_bubble.xml | 119 ++++---- TileMapApp/res/layout/item_layout.xml | 4 +- TileMapApp/res/layout/items_list.xml | 9 +- TileMapApp/src/org/oscim/app/POIActivity.java | 82 +++-- TileMapApp/src/org/oscim/app/POISearch.java | 92 ++++-- TileMapApp/src/org/oscim/app/TileMap.java | 280 ++++++++++-------- .../org/oscim/app/filepicker/FilePicker.java | 1 + .../app/preferences/EditPreferences.java | 1 + .../osmdroid/location/FlickrPOIProvider.java | 81 +++-- .../osmdroid/location/FourSquareProvider.java | 117 ++++++-- .../location/NominatimPOIProvider.java | 5 +- TileMapApp/src/org/osmdroid/location/POI.java | 2 + .../org/osmdroid/location/POIProvider.java | 9 + .../osmdroid/location/PicasaPOIProvider.java | 6 +- .../overlays/ItemizedOverlayWithBubble.java | 7 +- .../src/org/osmdroid/overlays/POIOverlay.java | 160 +++++++++- .../org/osmdroid/utils/BonusPackHelper.java | 4 + VectorTileMap/AndroidManifest.xml | 31 +- .../src/org/oscim/overlay/Overlay.java | 22 +- .../src/org/oscim/overlay/OverlayManager.java | 65 ++-- .../src/org/oscim/renderer/TileGenerator.java | 9 +- .../src/org/oscim/renderer/TileManager.java | 16 +- VectorTileMap/src/org/oscim/view/MapView.java | 10 +- 25 files changed, 756 insertions(+), 394 deletions(-) create mode 100644 TileMapApp/src/org/osmdroid/location/POIProvider.java mode change 100755 => 100644 VectorTileMap/AndroidManifest.xml diff --git a/TileMapApp/AndroidManifest.xml b/TileMapApp/AndroidManifest.xml index bdc8e52..e815ed2 100644 --- a/TileMapApp/AndroidManifest.xml +++ b/TileMapApp/AndroidManifest.xml @@ -5,6 +5,7 @@ android:versionCode="10" android:versionName="0.1" > + @@ -24,12 +25,22 @@ + + + + + + + + + @@ -40,11 +51,10 @@ android:launchMode="singleTop" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:windowSoftInputMode="stateHidden" /> + - - - \ No newline at end of file +--> diff --git a/TileMapApp/res/drawable-mdpi/bonuspack_bubble.9.png b/TileMapApp/res/drawable-mdpi/bonuspack_bubble.9.png index bf8ed8016396fd1717916692507def2cee5f35c6..31a2ba65da355e3e5f6b5fb7d727fc67d7ca3708 100644 GIT binary patch delta 692 zcmV;l0!#gt4dn$PiBL{Q4GJ0x0000DNk~Le000130000$2nGNE0G<%A!jU0Ae;XDg z9qz^Kb^rhZWl2OqRCwC$oIQ@(P!xsV4=#cfxla)(D#2Yygo4Daus*G*px{ zlOa`h5R2IgRR&E%8ap62m`G0KAGp&1Le2ycu!rZ#ITErZ8t%vOx%WL=X6$&;`ty{O zdQE(Ne7kcFrKg+12DI<{UxW}6f1p#i42a@lvG|@b#x{VN5JCze3cz!^R;$(aTs#mL zP>K>j0KfnY{k`7-FdPiP5C8-4e*u_)BfaSX4n{-(Fa*E=48YLofH9uI!TAP^I~i~= z07C$L9_`+ye~M4)oJ)Uh_7D*x0$)T#jw530Waz|v*kK3EJkyLn2~l&R{O3EU@FB_@w1>89(-wm~>_w z$Kkf1_3@nwe|w|Rc-8Cme?DlM_Kb5b{;|ka6h*(g-R|{ZF!-*BlW&w78FXE*nx^@p z-|t^)npWfoUQR?rQ55}ZHk((&;qb?NKL4spO;RPmK@i+dCX<)8ZGWp&DsK*XhtLLY ztJV5sS(f8@o~2SvN&!wlzBLTvLQ1La;~NBl-)^_BEz5Eii-o0He?7_p-i7Qq&e$-F z*HTI?nHEGuVHoKGp5W#}x~^Aiwc1D5b?-dSvpvtVUDv&< z*Xx(Mu2(@QMaa|XbUd5Q#(9Fv^8~q6Dm@oM@X=`W6S0s*!;sRHQkr@C&4)S|n-oVf aya&>-JqyFnijn{T3IG5}MNUMnLSTZXMK?hJ delta 1644 zcmV-y29x>a1(ppViBL{Q4GJ0x0000DNk~Le0001H0000$2nGNE0O%?LNs%Exe;5QX zIEHvPlmGw*7D+@wRCwC$oKI*ISscgTWF|9dW71}liFKn@C?(v6aZv-> zR_j5JB0^6VQ9D3jqHQX{O1YlCuL_=-~MH_#b!f+*ut61S&X= zBR=OsEX%4nj>7?BSqOrVs;a7{Z8lr7rKKgle*OBkWrgv0JP%zi*Ou||ab0Mu zS}g$pfFwx(!>}*+6Vl3hiLbe*Vj>6v7LUitCr_SKUcGv?HWG=@$B!Q$zI^$z{S#q& zdwc)dyLaz)tJOMn{rdF*qtTc}&-Kc~vMhe|=+U2iKA+{rjT;XS9Xhmqo-mD#jqifN zpz-eAyL}rsZsbsx6^?j3e@^b%v*$OH$;1VNK{EgV5C8!1`0?XEckI|Grnwpwv zQ4}%MU4?^Tm~3lnYxvr=Yqop$?)5sI&fir40MOUhXA%TKwR`vOaY>Q@YK3n*ckYbi zI4(YT@ZiIUiAJNVD2fuSR%>RZ12tk{tX3;WQB-bpbW}ShOd^rce-H#A8VrVcsj(=E zm`bIR7W@3Uth8(VntLiWD2fs_8jUbpHxjey+ic9*Y3r!?ZbaWhFLZ z%F4JtImb0r-N{q>$l2JtPSK@LjqCii=ieCS_e6=jMYz7H~kiRQK z!T&q0{1qSkkt;(Z2Jt1r2!ep&IF5!*@xcfH0MO}lSwRp~f5~K$plKTIBr5_CLX{** zpscJcH#cI|ty`CY5aQ8jR7=xz5@ltDCmM~G^E^+iUAr~|z|0#=hr^LjsZ^5J>t%Ae z943n5H*JN;%H-Y#1_q3xC}IwWV*-G@J(!y}Z;riv`?k{K@${R`W{8|i;h5PV`-@($ zhv9J8I5%Pde*nPca`_>Icu!AHJ>PWKyHo>v@tSOHLS?f2OC;ojd2RtgKuzgXB04KYRA< zkMVe1TWU6BvsqOtmC|UnTG-Li@v^(SyJ=p&yx0%By1IOUK%g?2Op>K!P%@dM1_uX! z=;-M1@7lF1w$!=({r$%7?rwWSLqm*VnCVioL#NYaYin!YyIij31q&HD21|p%z-`#D z;eA(Ef0uu$^W1KC?Xzdksxp}jB{v&ox!})kx5q|CModd3DcNimd;a|S>bknRNjcgm zOJUmE+edkxCmua|#4IMv;NYMkola9NEiI$6oRg7j?r$=gSc}DyzHs5f%Z0x0>FKHU z`~7B?Wi@ET6p+To##lHUrY9yQK1ohesT2_me+G>y<@bGXIgiwt}OcB_& zZQHv@Bx0DHoFwz7I9|Pa#n|okn4E2swJ`hl?;qoNp78m6MoE$|Ns_RqPoElCmL=ty ze~l>yv)P=kudk2ax^?UR?c2BSH#ax`Q&CYdEqA+QzoV?FsVNo?hv`%*Ma;M{lbo|L z#Rzlo;K4DT=LxUZYZ@9FGO#R5wY9a$-i;|@RSd&0EWVy{VEqd!TiSZTBFF9k0000 - - - - - -