From b4dd83fc09aaafc2caf1afbbeb3d43fc6106e374 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Sun, 30 Sep 2012 20:42:04 +0200 Subject: [PATCH] - improve tile locking (use ref counter instead of boolean) - flip over date line (inserting placeholder tiles in renderer) - make view coordinates consistent with tile coordinates (flip on y-axis) --- src/org/oscim/app/TileMap.java | 67 +- .../oscim/database/oscimap/MapDatabase.java | 4 +- src/org/oscim/database/pbmap/MapDatabase.java | 28 +- src/org/oscim/database/test/MapDatabase.java | 103 +- src/org/oscim/utils/GLSurfaceView.java | 1887 ----------------- src/org/oscim/view/Compass.java | 2 +- src/org/oscim/view/MapView.java | 4 +- src/org/oscim/view/MapViewPosition.java | 43 +- src/org/oscim/view/overlay/Overlay.java | 35 - src/org/oscim/view/overlay/TiledOverlay.java | 19 - src/org/oscim/view/renderer/GLRenderer.java | 442 ++-- src/org/oscim/view/renderer/LineRenderer.java | 14 +- src/org/oscim/view/renderer/MapRenderer.java | 463 ++-- src/org/oscim/view/renderer/MapTile.java | 26 +- .../oscim/view/renderer/PolygonRenderer.java | 18 +- src/org/oscim/view/renderer/ScanBox.java | 205 +- src/org/oscim/view/renderer/Shaders.java | 14 +- src/org/oscim/view/renderer/TextRenderer.java | 53 +- src/org/oscim/view/renderer/WayDecorator.java | 11 +- 19 files changed, 854 insertions(+), 2584 deletions(-) delete mode 100644 src/org/oscim/utils/GLSurfaceView.java delete mode 100644 src/org/oscim/view/overlay/Overlay.java delete mode 100644 src/org/oscim/view/overlay/TiledOverlay.java diff --git a/src/org/oscim/app/TileMap.java b/src/org/oscim/app/TileMap.java index 48dba1f6..0d860a7c 100755 --- a/src/org/oscim/app/TileMap.java +++ b/src/org/oscim/app/TileMap.java @@ -45,13 +45,15 @@ import android.widget.TextView; import android.widget.Toast; /** - * A map application which uses the features from the mapsforge map library. The map can be centered to the current - * location. A simple file browser for selecting the map file is also included. Some preferences can be adjusted via the - * {@link EditPreferences} activity. + * A map application which uses the features from the mapsforge map library. The + * map can be centered to the current location. A simple file browser for + * selecting the map file is also included. Some preferences can be adjusted via + * the {@link EditPreferences} activity. */ public class TileMap extends MapActivity { // implements ActionBar.OnNavigationListener { - // private static final String BUNDLE_CENTER_AT_FIRST_FIX = "centerAtFirstFix"; + // private static final String BUNDLE_CENTER_AT_FIRST_FIX = + // "centerAtFirstFix"; private static final String BUNDLE_SHOW_MY_LOCATION = "showMyLocation"; private static final String BUNDLE_SNAP_TO_LOCATION = "snapToLocation"; private static final int DIALOG_ENTER_COORDINATES = 0; @@ -408,7 +410,8 @@ public class TileMap extends MapActivity { editText.setText(Double.toString(mapCenter.getLongitude())); SeekBar zoomlevel = (SeekBar) dialog.findViewById(R.id.zoomLevel); - zoomlevel.setMax(20); // FIXME mMapView.getMapGenerator().getZoomLevelMax()); + zoomlevel.setMax(20); // FIXME + // mMapView.getMapGenerator().getZoomLevelMax()); zoomlevel.setProgress(mMapView.getMapPosition().getZoomLevel()); final TextView textView = (TextView) dialog.findViewById(R.id.zoomlevelValue); @@ -417,34 +420,42 @@ public class TileMap extends MapActivity { // } else if (id == DIALOG_INFO_MAP_FILE) { // MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo(); // - // TextView textView = (TextView) dialog.findViewById(R.id.infoMapFileViewName); + // TextView textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewName); // textView.setText(mMapView.getMapFile()); // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewSize); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewSize); // textView.setText(FileUtils.formatFileSize(mapInfo.fileSize, // getResources())); // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewVersion); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewVersion); // textView.setText(String.valueOf(mapInfo.fileVersion)); // - // // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewDebug); + // // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewDebug); // // if (mapFileInfo.debugFile) { // // textView.setText(R.string.info_map_file_debug_yes); // // } else { // // textView.setText(R.string.info_map_file_debug_no); // // } // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewDate); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewDate); // Date date = new Date(mapInfo.mapDate); // textView.setText(DateFormat.getDateTimeInstance().format(date)); // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewArea); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewArea); // BoundingBox boundingBox = mapInfo.boundingBox; // textView.setText(boundingBox.getMinLatitude() + ", " // + boundingBox.getMinLongitude() + " - \n" - // + boundingBox.getMaxLatitude() + ", " + boundingBox.getMaxLongitude()); + // + boundingBox.getMaxLatitude() + ", " + + // boundingBox.getMaxLongitude()); // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewStartPosition); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewStartPosition); // GeoPoint startPosition = mapInfo.startPosition; // if (startPosition == null) { // textView.setText(null); @@ -453,7 +464,8 @@ public class TileMap extends MapActivity { // + startPosition.getLongitude()); // } // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewStartZoomLevel); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewStartZoomLevel); // Byte startZoomLevel = mapInfo.startZoomLevel; // if (startZoomLevel == null) { // textView.setText(null); @@ -465,10 +477,12 @@ public class TileMap extends MapActivity { // .findViewById(R.id.infoMapFileViewLanguagePreference); // textView.setText(mapInfo.languagePreference); // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewComment); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewComment); // textView.setText(mapInfo.comment); // - // textView = (TextView) dialog.findViewById(R.id.infoMapFileViewCreatedBy); + // textView = (TextView) + // dialog.findViewById(R.id.infoMapFileViewCreatedBy); // textView.setText(mapInfo.createdBy); } else { super.onPrepareDialog(id, dialog); @@ -483,13 +497,17 @@ public class TileMap extends MapActivity { .getDefaultSharedPreferences(this); // MapScaleBar mapScaleBar = mapView.getMapScaleBar(); - // mapScaleBar.setShowMapScaleBar(preferences.getBoolean("showScaleBar", false)); - // String scaleBarUnitDefault = getString(R.string.preferences_scale_bar_unit_default); - // String scaleBarUnit = preferences.getString("scaleBarUnit", scaleBarUnitDefault); + // mapScaleBar.setShowMapScaleBar(preferences.getBoolean("showScaleBar", + // false)); + // String scaleBarUnitDefault = + // getString(R.string.preferences_scale_bar_unit_default); + // String scaleBarUnit = preferences.getString("scaleBarUnit", + // scaleBarUnitDefault); // mapScaleBar.setImperialUnits(scaleBarUnit.equals("imperial")); // if (preferences.contains("mapGenerator")) { - // String name = preferences.getString("mapGenerator", MapGeneratorInternal.SW_RENDERER.name()); + // String name = preferences.getString("mapGenerator", + // MapGeneratorInternal.SW_RENDERER.name()); // MapGeneratorInternal mapGeneratorInternalNew; // try { // mapGeneratorInternalNew = MapGeneratorInternal.valueOf(name); @@ -498,7 +516,8 @@ public class TileMap extends MapActivity { // } // // if (mapGeneratorInternalNew != mapGeneratorInternal) { - // TileGenerator mapGenerator = MapGeneratorFactory.createMapGenerator(mapGeneratorInternalNew); + // TileGenerator mapGenerator = + // MapGeneratorFactory.createMapGenerator(mapGeneratorInternalNew); // mapView.setMapGenerator(mapGenerator); // mapGeneratorInternal = mapGeneratorInternalNew; // } @@ -525,7 +544,8 @@ public class TileMap extends MapActivity { } // try { - // String textScaleDefault = getString(R.string.preferences_text_scale_default); + // String textScaleDefault = + // getString(R.string.preferences_text_scale_default); // mMapView.setTextScale(Float.parseFloat(preferences.getString("textScale", // textScaleDefault))); // } catch (NumberFormatException e) { @@ -596,7 +616,8 @@ public class TileMap extends MapActivity { } /** - * Uses the UI thread to display the given text message as toast notification. + * Uses the UI thread to display the given text message as toast + * notification. * * @param text * the text message to display diff --git a/src/org/oscim/database/oscimap/MapDatabase.java b/src/org/oscim/database/oscimap/MapDatabase.java index c0e90b38..5693cf28 100644 --- a/src/org/oscim/database/oscimap/MapDatabase.java +++ b/src/org/oscim/database/oscimap/MapDatabase.java @@ -65,8 +65,8 @@ public class MapDatabase implements IMapDatabase { private static final String CACHE_FILE = "%d-%d-%d.tile"; private static final String SERVER_ADDR = "city.informatik.uni-bremen.de"; - private static final String URL = "/osci/map-live/"; - // private static final String URL = "/osci/oscim/"; + // private static final String URL = "/osci/map-live/"; + private static final String URL = "/osci/oscim/"; private final static float REF_TILE_SIZE = 4096.0f; diff --git a/src/org/oscim/database/pbmap/MapDatabase.java b/src/org/oscim/database/pbmap/MapDatabase.java index 1426b481..cacd1dad 100644 --- a/src/org/oscim/database/pbmap/MapDatabase.java +++ b/src/org/oscim/database/pbmap/MapDatabase.java @@ -91,10 +91,13 @@ public class MapDatabase implements IMapDatabase { private static final String CACHE_FILE = "%d-%d-%d.tile"; private static final String SERVER_ADDR = "city.informatik.uni-bremen.de"; - // private static final String URL = "http://city.informatik.uni-bremen.de:8020/test/%d/%d/%d.osmtile"; + // private static final String URL = + // "http://city.informatik.uni-bremen.de:8020/test/%d/%d/%d.osmtile"; private static final String URL = "http://city.informatik.uni-bremen.de/osmstache/test/%d/%d/%d.osmtile"; - // private static final String URL = "http://city.informatik.uni-bremen.de/tiles/tiles.py///test/%d/%d/%d.osmtile"; - // private static final String URL = "http://city.informatik.uni-bremen.de/osmstache/gis2/%d/%d/%d.osmtile"; + // private static final String URL = + // "http://city.informatik.uni-bremen.de/tiles/tiles.py///test/%d/%d/%d.osmtile"; + // private static final String URL = + // "http://city.informatik.uni-bremen.de/osmstache/gis2/%d/%d/%d.osmtile"; private final static float REF_TILE_SIZE = 4096.0f; @@ -360,7 +363,7 @@ public class MapDatabase implements IMapDatabase { return file; } - // /////////////// hand sewed tile protocol buffers decoder /////////////////// + // /////////////// hand sewed tile protocol buffers decoder //////////////// private static final int BUFFER_SIZE = 65536; private final byte[] mReadBuffer = new byte[BUFFER_SIZE]; @@ -604,7 +607,7 @@ public class MapDatabase implements IMapDatabase { lastY = lat + lastY; mMapGenerator.renderPointOfInterest(layer, - tags, lastY / scale, lastX / scale); + tags, Tile.TILE_SIZE - lastY / scale, lastX / scale); cnt += 2; } return cnt; @@ -800,7 +803,7 @@ public class MapDatabase implements IMapDatabase { } else { y = ((result >>> 1) ^ -(result & 1)); lastY = lastY + y; - coords[cnt++] = lastY / scale; + coords[cnt++] = Tile.TILE_SIZE - lastY / scale; even = true; } } @@ -940,7 +943,8 @@ public class MapDatabase implements IMapDatabase { return result; } - // ///////////////////////// Lightweight HttpClient /////////////////////////////////////// + // ///////////////////////// Lightweight HttpClient + // /////////////////////////////////////// // would have written simple tcp server/client for this... private int mMaxReq = 0; @@ -1080,7 +1084,8 @@ public class MapDatabase implements IMapDatabase { len += pos; // this does the same but with a few more allocations: - // byte[] request = String.format(REQUEST, Integer.valueOf(tile.zoomLevel), + // byte[] request = String.format(REQUEST, + // Integer.valueOf(tile.zoomLevel), // Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes(); try { @@ -1138,7 +1143,7 @@ public class MapDatabase implements IMapDatabase { } - // //////////////////////////// Tile cache //////////////////////////////////// + // //////////////////////////// Tile cache /////////////////////////////// private boolean cacheRead(Tile tile, File f) { if (f.exists() && f.length() > 0) { @@ -1212,7 +1217,10 @@ public class MapDatabase implements IMapDatabase { mCacheFile = null; } - /* All code below is taken from or based on Google's Protocol Buffers implementation: */ + /* + * All code below is taken from or based on Google's Protocol Buffers + * implementation: + */ // Protocol Buffers - Google's data interchange format // Copyright 2008 Google Inc. All rights reserved. diff --git a/src/org/oscim/database/test/MapDatabase.java b/src/org/oscim/database/test/MapDatabase.java index 0a1211fb..fe4e0d3c 100644 --- a/src/org/oscim/database/test/MapDatabase.java +++ b/src/org/oscim/database/test/MapDatabase.java @@ -38,6 +38,8 @@ public class MapDatabase implements IMapDatabase { // private Tag[] mTags = { new Tag("boundary", "administrative"), new // Tag("admin_level", "2") }; private Tag[] mTags = { new Tag("natural", "water") }; + private Tag[] mTagsWay = { new Tag("highway", "primary"), new Tag("name", "Highway Rd") }; + private Tag[] mNameTags; private final MapInfo mMapInfo = @@ -50,10 +52,12 @@ public class MapDatabase implements IMapDatabase { @Override public QueryResult executeQuery(JobTile tile, IMapDatabaseCallback mapDatabaseCallback) { - float lat1 = -0.5f; - float lon1 = -0.5f; - float lat2 = Tile.TILE_SIZE + 0.5f; - float lon2 = Tile.TILE_SIZE + 0.5f; + int size = Tile.TILE_SIZE; + + float lat1 = -1; + float lon1 = -1; + float lat2 = size + 1; + float lon2 = size + 1; mCoords[0] = lon1; mCoords[1] = lat1; @@ -70,13 +74,13 @@ public class MapDatabase implements IMapDatabase { mCoords[8] = lon1; mCoords[9] = lat1; - mIndex[0] = 8; - mIndex[1] = 2; + mIndex[0] = 10; + mIndex[1] = 0; lon1 = 40; - lon2 = Tile.TILE_SIZE - 40; + lon2 = size - 40; lat1 = 40; - lat2 = Tile.TILE_SIZE - 40; + lat2 = size - 40; mCoords[10] = lon1; mCoords[11] = lat1; @@ -93,19 +97,84 @@ public class MapDatabase implements IMapDatabase { mCoords[18] = lon1; mCoords[19] = lat1; - mIndex[2] = 8; - mIndex[3] = 2; + mIndex[2] = 10; + mIndex[3] = 0; mapDatabaseCallback.renderWay((byte) 0, mTags, mCoords, mIndex, true); - lon1 = Tile.TILE_SIZE / 2; - lat1 = Tile.TILE_SIZE / 2; + mIndex[0] = 4; + mIndex[1] = -1; - mNameTags = new Tag[2]; - mNameTags[0] = new Tag("place", "city"); - mNameTags[1] = new Tag("name", tile.toString()); - mapDatabaseCallback.renderPointOfInterest((byte) 0, mNameTags, (int) lat1, - (int) lon1); + // middle horizontal + mCoords[0] = 0; + mCoords[1] = size / 2; + mCoords[2] = size; + mCoords[3] = size / 2; + Tag[] tags = new Tag[2]; + tags[0] = mTagsWay[0]; + tags[1] = mTagsWay[1]; + mapDatabaseCallback.renderWay((byte) 0, tags, mCoords, mIndex, false); + + // center up + mCoords[0] = size / 2; + mCoords[1] = -size / 2; + mCoords[2] = size / 2; + mCoords[3] = size / 2; + tags = new Tag[2]; + tags[0] = mTagsWay[0]; + tags[1] = mTagsWay[1]; + mapDatabaseCallback.renderWay((byte) 0, tags, mCoords, mIndex, + false); + + // center down + mCoords[0] = size / 2; + mCoords[1] = size / 2; + mCoords[2] = size / 2; + mCoords[3] = size / 2 + size; + tags = new Tag[2]; + tags[0] = mTagsWay[0]; + tags[1] = mTagsWay[1]; + mapDatabaseCallback.renderWay((byte) 0, tags, mCoords, mIndex, false); + + // left-top to center + mCoords[0] = size / 2; + mCoords[1] = size / 2; + mCoords[2] = 10; + mCoords[3] = 10; + tags = new Tag[2]; + tags[0] = mTagsWay[0]; + tags[1] = mTagsWay[1]; + mapDatabaseCallback.renderWay((byte) 1, tags, mCoords, mIndex, false); + + // middle horizontal + mCoords[0] = 0; + mCoords[1] = 10; + mCoords[2] = size; + mCoords[3] = 10; + tags = new Tag[2]; + tags[0] = mTagsWay[0]; + tags[1] = mTagsWay[1]; + mapDatabaseCallback.renderWay((byte) 1, tags, mCoords, mIndex, false); + + // middle horizontal + mCoords[0] = 10; + mCoords[1] = 0; + mCoords[2] = 10; + mCoords[3] = size; + tags = new Tag[2]; + tags[0] = mTagsWay[0]; + tags[1] = mTagsWay[1]; + mapDatabaseCallback.renderWay((byte) 1, tags, mCoords, mIndex, false); + + // lon1 = size / 2; + // lat1 = size / 2; + + // mNameTags = new Tag[2]; + // mNameTags[0] = new Tag("place", "city"); + // mNameTags[1] = new Tag("name", tile.toString()); + // mapDatabaseCallback.renderPointOfInterest((byte) 0, mNameTags, (int) + // lat1, + // (int) lon1); return QueryResult.SUCCESS; } diff --git a/src/org/oscim/utils/GLSurfaceView.java b/src/org/oscim/utils/GLSurfaceView.java deleted file mode 100644 index 5601345e..00000000 --- a/src/org/oscim/utils/GLSurfaceView.java +++ /dev/null @@ -1,1887 +0,0 @@ -package org.oscim.utils; - -/* - * 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. - */ - -import java.io.Writer; -import java.lang.ref.WeakReference; -import java.util.ArrayList; - -import javax.microedition.khronos.egl.EGL10; -import javax.microedition.khronos.egl.EGL11; -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.egl.EGLContext; -import javax.microedition.khronos.egl.EGLDisplay; -import javax.microedition.khronos.egl.EGLSurface; -import javax.microedition.khronos.opengles.GL; -import javax.microedition.khronos.opengles.GL10; - -import android.content.Context; -import android.opengl.GLDebugHelper; -import android.opengl.GLSurfaceView.EGLConfigChooser; -import android.opengl.GLSurfaceView.Renderer; -import android.util.AttributeSet; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -/** - * An implementation of SurfaceView that uses the dedicated surface for - * displaying OpenGL rendering. - *

- * A GLSurfaceView provides the following features: - *

- *

- *
- *

Developer Guides

- *

- * For more information about how to use OpenGL, read the OpenGL developer guide. - *

- *

Using GLSurfaceView

- *

- * Typically you use GLSurfaceView by subclassing it and overriding one or more - * of the View system input event methods. If your application does not need to - * override event methods then GLSurfaceView can be used as-is. For the most - * part GLSurfaceView behavior is customized by calling "set" methods rather - * than by subclassing. For example, unlike a regular View, drawing is delegated - * to a separate Renderer object which is registered with the GLSurfaceView - * using the {@link #setRenderer(Renderer)} call. - *

- *

Initializing GLSurfaceView

All you have to do to initialize a - * GLSurfaceView is call {@link #setRenderer(Renderer)}. However, if desired, - * you can modify the default behavior of GLSurfaceView by calling one or more - * of these methods before calling setRenderer: - * - *

- *

Specifying the android.view.Surface

By default GLSurfaceView will - * create a PixelFormat.RGB_565 format surface. If a translucent surface is - * required, call getHolder().setFormat(PixelFormat.TRANSLUCENT). The exact - * format of a TRANSLUCENT surface is device dependent, but it will be a - * 32-bit-per-pixel surface with 8 bits per component. - *

- *

Choosing an EGL Configuration

A given Android device may support - * multiple EGLConfig rendering configurations. The available configurations may - * differ in how may channels of data are present, as well as how many bits are - * allocated to each channel. Therefore, the first thing GLSurfaceView has to do - * when starting to render is choose what EGLConfig to use. - *

- * By default GLSurfaceView chooses a EGLConfig that has an RGB_565 pixel - * format, with at least a 16-bit depth buffer and no stencil. - *

- * If you would prefer a different EGLConfig you can override the default - * behavior by calling one of the setEGLConfigChooser methods. - *

- *

Debug Behavior

You can optionally modify the behavior of - * GLSurfaceView by calling one or more of the debugging methods - * {@link #setDebugFlags(int)}, and {@link #setGLWrapper}. These methods may be - * called before and/or after setRenderer, but typically they are called before - * setRenderer so that they take effect immediately. - *

- *

Setting a Renderer

Finally, you must call {@link #setRenderer} to - * register a {@link Renderer}. The renderer is responsible for doing the actual - * OpenGL rendering. - *

- *

Rendering Mode

Once the renderer is set, you can control whether the - * renderer draws continuously or on-demand by calling {@link #setRenderMode}. - * The default is continuous rendering. - *

- *

Activity Life-cycle

A GLSurfaceView must be notified when the - * activity is paused and resumed. GLSurfaceView clients are required to call - * {@link #onPause()} when the activity pauses and {@link #onResume()} when the - * activity resumes. These calls allow GLSurfaceView to pause and resume the - * rendering thread, and also allow GLSurfaceView to release and recreate the - * OpenGL display. - *

- *

Handling events

- *

- * To handle an event you will typically subclass GLSurfaceView and override the - * appropriate method, just as you would with any other View. However, when - * handling the event, you may need to communicate with the Renderer object - * that's running in the rendering thread. You can do this using any standard - * Java cross-thread communication mechanism. In addition, one relatively easy - * way to communicate with your renderer is to call - * {@link #queueEvent(Runnable)}. For example: - * - *

- * class MyGLSurfaceView extends GLSurfaceView {
- * 
- * 	private MyRenderer mMyRenderer;
- * 
- * 	public void start() {
- *         mMyRenderer = ...;
- *         setRenderer(mMyRenderer);
- *     }
- * 
- * 	public boolean onKeyDown(int keyCode, KeyEvent event) {
- * 		if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
- * 			queueEvent(new Runnable() {
- * 				// This method will be called on the rendering
- * 				// thread:
- * 				public void run() {
- * 					mMyRenderer.handleDpadCenter();
- * 				}
- * 			});
- * 			return true;
- * 		}
- * 		return super.onKeyDown(keyCode, event);
- * 	}
- * }
- * 
- * 
- * 
- */ -public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback { - private final static String TAG = "GLSurfaceView"; - private final static boolean LOG_ATTACH_DETACH = false; - private final static boolean LOG_THREADS = true; - private final static boolean LOG_PAUSE_RESUME = false; - private final static boolean LOG_SURFACE = false; - private final static boolean LOG_RENDERER = false; - private final static boolean LOG_RENDERER_DRAW_FRAME = false; - private final static boolean LOG_EGL = false; - /** - * The renderer only renders when the surface is created, or when - * {@link #requestRender} is called. - * - * @see #getRenderMode() - * @see #setRenderMode(int) - * @see #requestRender() - */ - public final static int RENDERMODE_WHEN_DIRTY = 0; - /** - * The renderer is called continuously to re-render the scene. - * - * @see #getRenderMode() - * @see #setRenderMode(int) - */ - public final static int RENDERMODE_CONTINUOUSLY = 1; - - /** - * Check glError() after every GL call and throw an exception if glError - * indicates that an error has occurred. This can be used to help track down - * which OpenGL ES call is causing an error. - * - * @see #getDebugFlags - * @see #setDebugFlags - */ - public final static int DEBUG_CHECK_GL_ERROR = 1; - - /** - * Log GL calls to the system log at "verbose" level with tag - * "GLSurfaceView". - * - * @see #getDebugFlags - * @see #setDebugFlags - */ - public final static int DEBUG_LOG_GL_CALLS = 2; - - /** - * Standard View constructor. In order to render something, you must call - * {@link #setRenderer} to register a renderer. - * - * @param context - * ... - */ - public GLSurfaceView(Context context) { - super(context); - init(); - } - - /** - * Standard View constructor. In order to render something, you must call - * {@link #setRenderer} to register a renderer. - * - * @param context - * .... - * @param attrs - * ... - */ - public GLSurfaceView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - @Override - protected void finalize() throws Throwable { - try { - if (mGLThread != null) { - // GLThread may still be running if this view was never - // attached to a window. - mGLThread.requestExitAndWait(); - } - } finally { - super.finalize(); - } - } - - private void init() { - // Install a SurfaceHolder.Callback so we get notified when the - // underlying surface is created and destroyed - SurfaceHolder holder = getHolder(); - holder.addCallback(this); - // setFormat is done by SurfaceView in SDK 2.3 and newer. Uncomment - // this statement if back-porting to 2.2 or older: - // holder.setFormat(PixelFormat.RGB_565); - // - // setType is not needed for SDK 2.0 or newer. Uncomment this - // statement if back-porting this code to older SDKs. - // holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); - } - - /** - * Set the glWrapper. If the glWrapper is not null, its - * {@link GLWrapper#wrap(GL)} method is called whenever a surface is - * created. A GLWrapper can be used to wrap the GL object that's passed to - * the renderer. Wrapping a GL object enables examining and modifying the - * behavior of the GL calls made by the renderer. - *

- * Wrapping is typically used for debugging purposes. - *

- * The default value is null. - * - * @param glWrapper - * the new GLWrapper - */ - public void setGLWrapper(GLWrapper glWrapper) { - mGLWrapper = glWrapper; - } - - /** - * Set the debug flags to a new value. The value is constructed by - * OR-together zero or more of the DEBUG_CHECK_* constants. The debug flags - * take effect whenever a surface is created. The default value is zero. - * - * @param debugFlags - * the new debug flags - * @see #DEBUG_CHECK_GL_ERROR - * @see #DEBUG_LOG_GL_CALLS - */ - public void setDebugFlags(int debugFlags) { - mDebugFlags = debugFlags; - } - - /** - * Get the current value of the debug flags. - * - * @return the current value of the debug flags. - */ - public int getDebugFlags() { - return mDebugFlags; - } - - /** - * Control whether the EGL context is preserved when the GLSurfaceView is - * paused and resumed. - *

- * If set to true, then the EGL context may be preserved when the - * GLSurfaceView is paused. Whether the EGL context is actually preserved or - * not depends upon whether the Android device that the program is running - * on can support an arbitrary number of EGL contexts or not. Devices that - * can only support a limited number of EGL contexts must release the EGL - * context in order to allow multiple applications to share the GPU. - *

- * If set to false, the EGL context will be released when the GLSurfaceView - * is paused, and recreated when the GLSurfaceView is resumed. - *

- * The default is false. - * - * @param preserveOnPause - * preserve the EGL context when paused - */ - public void setPreserveEGLContextOnPause(boolean preserveOnPause) { - mPreserveEGLContextOnPause = preserveOnPause; - } - - /** - * @return true if the EGL context will be preserved when paused - */ - public boolean getPreserveEGLContextOnPause() { - return mPreserveEGLContextOnPause; - } - - /** - * Set the renderer associated with this view. Also starts the thread that - * will call the renderer, which in turn causes the rendering to start. - *

- * This method should be called once and only once in the life-cycle of a - * GLSurfaceView. - *

- * The following GLSurfaceView methods can only be called before - * setRenderer is called: - *

- *

- * The following GLSurfaceView methods can only be called after - * setRenderer is called: - *

- * - * @param renderer - * the renderer to use to perform OpenGL drawing. - */ - public void setRenderer(Renderer renderer) { - checkRenderThreadState(); - if (mEGLConfigChooser == null) { - mEGLConfigChooser = new SimpleEGLConfigChooser(true); - } - if (mEGLContextFactory == null) { - mEGLContextFactory = new DefaultContextFactory(); - } - if (mEGLWindowSurfaceFactory == null) { - mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); - } - mRenderer = renderer; - mGLThread = new GLThread(mThisWeakRef); - mGLThread.start(); - } - - /** - * Install a custom EGLContextFactory. - *

- * If this method is called, it must be called before - * {@link #setRenderer(Renderer)} is called. - *

- * If this method is not called, then by default a context will be created - * with no shared context and with a null attribute list. - */ - public void setEGLContextFactory(EGLContextFactory factory) { - checkRenderThreadState(); - mEGLContextFactory = factory; - } - - /** - * Install a custom EGLWindowSurfaceFactory. - *

- * If this method is called, it must be called before - * {@link #setRenderer(Renderer)} is called. - *

- * If this method is not called, then by default a window surface will be - * created with a null attribute list. - */ - public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) { - checkRenderThreadState(); - mEGLWindowSurfaceFactory = factory; - } - - /** - * Install a custom EGLConfigChooser. - *

- * If this method is called, it must be called before - * {@link #setRenderer(Renderer)} is called. - *

- * If no setEGLConfigChooser method is called, then by default the view will - * choose an EGLConfig that is compatible with the current - * android.view.Surface, with a depth buffer depth of at least 16 bits. - * - * @param configChooser - */ - public void setEGLConfigChooser(EGLConfigChooser configChooser) { - checkRenderThreadState(); - mEGLConfigChooser = configChooser; - } - - /** - * Install a config chooser which will choose a config as close to 16-bit - * RGB as possible, with or without an optional depth buffer as close to - * 16-bits as possible. - *

- * If this method is called, it must be called before - * {@link #setRenderer(Renderer)} is called. - *

- * If no setEGLConfigChooser method is called, then by default the view will - * choose an RGB_565 surface with a depth buffer depth of at least 16 bits. - * - * @param needDepth - */ - public void setEGLConfigChooser(boolean needDepth) { - setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth)); - } - - /** - * Install a config chooser which will choose a config with at least the - * specified depthSize and stencilSize, and exactly the specified redSize, - * greenSize, blueSize and alphaSize. - *

- * If this method is called, it must be called before - * {@link #setRenderer(Renderer)} is called. - *

- * If no setEGLConfigChooser method is called, then by default the view will - * choose an RGB_565 surface with a depth buffer depth of at least 16 bits. - */ - public void setEGLConfigChooser(int redSize, int greenSize, int blueSize, - int alphaSize, int depthSize, int stencilSize) { - setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize, - blueSize, alphaSize, depthSize, stencilSize)); - } - - /** - * Inform the default EGLContextFactory and default EGLConfigChooser which - * EGLContext client version to pick. - *

- * Use this method to create an OpenGL ES 2.0-compatible context. Example: - * - *

-	 * public MyView(Context context) {
-	 * 	super(context);
-	 * 	setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
-	 * 	setRenderer(new MyRenderer());
-	 * }
-	 * 
- *

- * Note: Activities which require OpenGL ES 2.0 should indicate this by - * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the - * activity's AndroidManifest.xml file. - *

- * If this method is called, it must be called before - * {@link #setRenderer(Renderer)} is called. - *

- * This method only affects the behavior of the default EGLContexFactory and - * the default EGLConfigChooser. If - * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then - * the supplied EGLContextFactory is responsible for creating an OpenGL ES - * 2.0-compatible context. If {@link #setEGLConfigChooser(EGLConfigChooser)} - * has been called, then the supplied EGLConfigChooser is responsible for - * choosing an OpenGL ES 2.0-compatible config. - * - * @param version - * The EGLContext client version to choose. Use 2 for OpenGL ES - * 2.0 - */ - public void setEGLContextClientVersion(int version) { - checkRenderThreadState(); - mEGLContextClientVersion = version; - } - - /** - * Set the rendering mode. When renderMode is RENDERMODE_CONTINUOUSLY, the - * renderer is called repeatedly to re-render the scene. When renderMode is - * RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface is - * created, or when {@link #requestRender} is called. Defaults to - * RENDERMODE_CONTINUOUSLY. - *

- * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system - * performance by allowing the GPU and CPU to idle when the view does not - * need to be updated. - *

- * This method can only be called after {@link #setRenderer(Renderer)} - * - * @param renderMode - * one of the RENDERMODE_X constants - * @see #RENDERMODE_CONTINUOUSLY - * @see #RENDERMODE_WHEN_DIRTY - */ - public void setRenderMode(int renderMode) { - mGLThread.setRenderMode(renderMode); - } - - /** - * Get the current rendering mode. May be called from any thread. Must not - * be called before a renderer has been set. - * - * @return the current rendering mode. - * @see #RENDERMODE_CONTINUOUSLY - * @see #RENDERMODE_WHEN_DIRTY - */ - public int getRenderMode() { - return mGLThread.getRenderMode(); - } - - /** - * Request that the renderer render a frame. This method is typically used - * when the render mode has been set to {@link #RENDERMODE_WHEN_DIRTY}, so - * that frames are only rendered on demand. May be called from any thread. - * Must not be called before a renderer has been set. - */ - public void requestRender() { - mGLThread.requestRender(); - } - - /** - * This method is part of the SurfaceHolder.Callback interface, and is not - * normally called or subclassed by clients of GLSurfaceView. - */ - public void surfaceCreated(SurfaceHolder holder) { - mGLThread.surfaceCreated(); - } - - /** - * This method is part of the SurfaceHolder.Callback interface, and is not - * normally called or subclassed by clients of GLSurfaceView. - */ - public void surfaceDestroyed(SurfaceHolder holder) { - // Surface will be destroyed when we return - mGLThread.surfaceDestroyed(); - } - - /** - * This method is part of the SurfaceHolder.Callback interface, and is not - * normally called or subclassed by clients of GLSurfaceView. - */ - public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - mGLThread.onWindowResize(w, h); - } - - /** - * Inform the view that the activity is paused. The owner of this view must - * call this method when the activity is paused. Calling this method will - * pause the rendering thread. Must not be called before a renderer has been - * set. - */ - public void onPause() { - mGLThread.onPause(); - } - - /** - * Inform the view that the activity is resumed. The owner of this view must - * call this method when the activity is resumed. Calling this method will - * recreate the OpenGL display and resume the rendering thread. Must not be - * called before a renderer has been set. - */ - public void onResume() { - mGLThread.onResume(); - } - - /** - * Queue a runnable to be run on the GL rendering thread. This can be used - * to communicate with the Renderer on the rendering thread. Must not be - * called before a renderer has been set. - * - * @param r - * the runnable to be run on the GL rendering thread. - */ - public void queueEvent(Runnable r) { - mGLThread.queueEvent(r); - } - - /** - * This method is used as part of the View class and is not normally called - * or subclassed by clients of GLSurfaceView. - */ - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (LOG_ATTACH_DETACH) { - Log.d(TAG, "onAttachedToWindow reattach =" + mDetached); - } - if (mDetached && (mRenderer != null)) { - int renderMode = RENDERMODE_CONTINUOUSLY; - if (mGLThread != null) { - renderMode = mGLThread.getRenderMode(); - } - mGLThread = new GLThread(mThisWeakRef); - if (renderMode != RENDERMODE_CONTINUOUSLY) { - mGLThread.setRenderMode(renderMode); - } - mGLThread.start(); - } - mDetached = false; - } - - /** - * This method is used as part of the View class and is not normally called - * or subclassed by clients of GLSurfaceView. Must not be called before a - * renderer has been set. - */ - @Override - protected void onDetachedFromWindow() { - if (LOG_ATTACH_DETACH) { - Log.d(TAG, "onDetachedFromWindow"); - } - if (mGLThread != null) { - mGLThread.requestExitAndWait(); - } - mDetached = true; - super.onDetachedFromWindow(); - } - - // ---------------------------------------------------------------------- - - /** - * An interface used to wrap a GL interface. - *

- * Typically used for implementing debugging and tracing on top of the - * default GL interface. You would typically use this by creating your own - * class that implemented all the GL methods by delegating to another GL - * instance. Then you could add your own behavior before or after calling - * the delegate. All the GLWrapper would do was instantiate and return the - * wrapper GL instance: - * - *

-	 * class MyGLWrapper implements GLWrapper {
-	 *     GL wrap(GL gl) {
-	 *         return new MyGLImplementation(gl);
-	 *     }
-	 *     static class MyGLImplementation implements GL,GL10,GL11,... {
-	 *         ...
-	 *     }
-	 * }
-	 * 
- * - * @see #setGLWrapper(GLWrapper) - */ - public interface GLWrapper { - /** - * Wraps a gl interface in another gl interface. - * - * @param gl - * a GL interface that is to be wrapped. - * @return either the input argument or another GL object that wraps the - * input argument. - */ - GL wrap(GL gl); - } - - /** - * An interface for customizing the eglCreateContext and eglDestroyContext - * calls. - *

- * This interface must be implemented by clients wishing to call - * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)} - */ - public interface EGLContextFactory { - EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig); - - void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context); - } - - private final class DefaultContextFactory implements EGLContextFactory { - private int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - - @Override - public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) { - int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion, - EGL10.EGL_NONE }; - - return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, - mEGLContextClientVersion != 0 ? attrib_list : null); - } - - @Override - public void destroyContext(EGL10 egl, EGLDisplay display, - EGLContext context) { - if (!egl.eglDestroyContext(display, context)) { - Log.e("DefaultContextFactory", "display:" + display + " context: " - + context); - if (LOG_THREADS) { - Log.i("DefaultContextFactory", "tid=" - + Thread.currentThread().getId()); - } - EglHelper.throwEglException("eglDestroyContex", egl.eglGetError()); - } - } - } - - /** - * An interface for customizing the eglCreateWindowSurface and - * eglDestroySurface calls. - *

- * This interface must be implemented by clients wishing to call - * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)} - */ - public interface EGLWindowSurfaceFactory { - /** - * @param egl - * ... - * @param display - * ... - * @param config - * ... - * @param nativeWindow - * ... - * @return null if the surface cannot be constructed. - */ - EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, - Object nativeWindow); - - void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface); - } - - private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory { - - @Override - public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, - EGLConfig config, Object nativeWindow) { - EGLSurface result = null; - try { - result = egl.eglCreateWindowSurface(display, config, nativeWindow, null); - } catch (IllegalArgumentException e) { - // This exception indicates that the surface flinger surface - // is not valid. This can happen if the surface flinger surface - // has - // been torn down, but the application has not yet been - // notified via SurfaceHolder.Callback.surfaceDestroyed. - // In theory the application should be notified first, - // but in practice sometimes it is not. See b/4588890 - Log.e(TAG, "eglCreateWindowSurface", e); - } - return result; - } - - @Override - public void destroySurface(EGL10 egl, EGLDisplay display, - EGLSurface surface) { - egl.eglDestroySurface(display, surface); - } - } - - private abstract class BaseConfigChooser - implements EGLConfigChooser { - public BaseConfigChooser(int[] configSpec) { - mConfigSpec = filterConfigSpec(configSpec); - } - - @Override - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { - int[] num_config = new int[1]; - if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, - num_config)) { - throw new IllegalArgumentException("eglChooseConfig failed"); - } - - int numConfigs = num_config[0]; - - if (numConfigs <= 0) { - throw new IllegalArgumentException( - "No configs match configSpec"); - } - - EGLConfig[] configs = new EGLConfig[numConfigs]; - if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, - num_config)) { - throw new IllegalArgumentException("eglChooseConfig#2 failed"); - } - EGLConfig config = chooseConfig(egl, display, configs); - if (config == null) { - throw new IllegalArgumentException("No config chosen"); - } - return config; - } - - abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs); - - protected int[] mConfigSpec; - - private int[] filterConfigSpec(int[] configSpec) { - if (mEGLContextClientVersion != 2) { - return configSpec; - } - /* - * We know none of the subclasses define EGL_RENDERABLE_TYPE. And we - * know the configSpec is well formed. - */ - int len = configSpec.length; - int[] newConfigSpec = new int[len + 2]; - System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1); - newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE; - newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */ - newConfigSpec[len + 1] = EGL10.EGL_NONE; - return newConfigSpec; - } - } - - /** - * Choose a configuration with exactly the specified r,g,b,a sizes, and at - * least the specified depth and stencil sizes. - */ - private class ComponentSizeChooser extends BaseConfigChooser { - public ComponentSizeChooser(int redSize, int greenSize, int blueSize, - int alphaSize, int depthSize, int stencilSize) { - super(new int[] { - EGL10.EGL_RED_SIZE, redSize, - EGL10.EGL_GREEN_SIZE, greenSize, - EGL10.EGL_BLUE_SIZE, blueSize, - EGL10.EGL_ALPHA_SIZE, alphaSize, - EGL10.EGL_DEPTH_SIZE, depthSize, - EGL10.EGL_STENCIL_SIZE, stencilSize, - EGL10.EGL_NONE }); - mValue = new int[1]; - mRedSize = redSize; - mGreenSize = greenSize; - mBlueSize = blueSize; - mAlphaSize = alphaSize; - mDepthSize = depthSize; - mStencilSize = stencilSize; - } - - @Override - public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, - EGLConfig[] configs) { - for (EGLConfig config : configs) { - int d = findConfigAttrib(egl, display, config, - EGL10.EGL_DEPTH_SIZE, 0); - int s = findConfigAttrib(egl, display, config, - EGL10.EGL_STENCIL_SIZE, 0); - if ((d >= mDepthSize) && (s >= mStencilSize)) { - int r = findConfigAttrib(egl, display, config, - EGL10.EGL_RED_SIZE, 0); - int g = findConfigAttrib(egl, display, config, - EGL10.EGL_GREEN_SIZE, 0); - int b = findConfigAttrib(egl, display, config, - EGL10.EGL_BLUE_SIZE, 0); - int a = findConfigAttrib(egl, display, config, - EGL10.EGL_ALPHA_SIZE, 0); - if ((r == mRedSize) && (g == mGreenSize) - && (b == mBlueSize) && (a == mAlphaSize)) { - return config; - } - } - } - return null; - } - - private int findConfigAttrib(EGL10 egl, EGLDisplay display, - EGLConfig config, int attribute, int defaultValue) { - - if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) { - return mValue[0]; - } - return defaultValue; - } - - private int[] mValue; - // Subclasses can adjust these values: - protected int mRedSize; - protected int mGreenSize; - protected int mBlueSize; - protected int mAlphaSize; - protected int mDepthSize; - protected int mStencilSize; - } - - /** - * This class will choose a RGB_565 surface with or without a depth buffer. - */ - private class SimpleEGLConfigChooser extends ComponentSizeChooser { - public SimpleEGLConfigChooser(boolean withDepthBuffer) { - super(5, 6, 5, 0, withDepthBuffer ? 16 : 0, 0); - } - } - - /** - * An EGL helper class. - */ - - private static class EglHelper { - public EglHelper(WeakReference glSurfaceViewWeakRef) { - mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; - } - - /** - * Initialize EGL for a given configuration spec. - */ - public void start() { - if (LOG_EGL) { - Log.w("EglHelper", "start() tid=" + Thread.currentThread().getId()); - } - /* - * Get an EGL instance - */ - mEgl = (EGL10) EGLContext.getEGL(); - - /* - * Get to the default display. - */ - mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - - if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { - throw new RuntimeException("eglGetDisplay failed"); - } - - /* - * We can now initialize EGL for that display - */ - int[] version = new int[2]; - if (!mEgl.eglInitialize(mEglDisplay, version)) { - throw new RuntimeException("eglInitialize failed"); - } - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view == null) { - mEglConfig = null; - mEglContext = null; - } else { - mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); - - /* - * Create an EGL context. We want to do this as rarely as we - * can, because an EGL context is a somewhat heavy object. - */ - mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, - mEglConfig); - } - if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { - mEglContext = null; - throwEglException("createContext"); - } - if (LOG_EGL) { - Log.w("EglHelper", "createContext " + mEglContext + " tid=" - + Thread.currentThread().getId()); - } - - mEglSurface = null; - } - - /** - * Create an egl surface for the current SurfaceHolder surface. If a - * surface already exists, destroy it before creating the new surface. - * - * @return true if the surface was created successfully. - */ - public boolean createSurface() { - if (LOG_EGL) { - Log.w("EglHelper", "createSurface() tid=" - + Thread.currentThread().getId()); - } - /* - * Check preconditions. - */ - if (mEgl == null) { - throw new RuntimeException("egl not initialized"); - } - if (mEglDisplay == null) { - throw new RuntimeException("eglDisplay not initialized"); - } - if (mEglConfig == null) { - throw new RuntimeException("mEglConfig not initialized"); - } - - /* - * The window size has changed, so we need to create a new surface. - */ - destroySurfaceImp(); - - /* - * Create an EGL surface we can render into. - */ - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view != null) { - mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, - mEglDisplay, mEglConfig, view.getHolder()); - } else { - mEglSurface = null; - } - - if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { - int error = mEgl.eglGetError(); - if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { - Log.e("EglHelper", - "createWindowSurface returned EGL_BAD_NATIVE_WINDOW."); - } - return false; - } - - /* - * Before we can issue GL commands, we need to make sure the context - * is current and bound to a surface. - */ - if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { - /* - * Could not make the context current, probably because the - * underlying SurfaceView surface has been destroyed. - */ - logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError()); - return false; - } - - return true; - } - - /** - * Create a GL object for the current EGL context. - * - * @return ... - */ - GL createGL() { - - GL gl = mEglContext.getGL(); - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view != null) { - if (view.mGLWrapper != null) { - gl = view.mGLWrapper.wrap(gl); - } - - if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { - int configFlags = 0; - Writer log = null; - if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { - configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; - } - if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { - log = new LogWriter(); - } - gl = GLDebugHelper.wrap(gl, configFlags, log); - } - } - return gl; - } - - /** - * Display the current render surface. - * - * @return the EGL error code from eglSwapBuffers. - */ - public int swap() { - if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { - return mEgl.eglGetError(); - } - return EGL10.EGL_SUCCESS; - } - - public void destroySurface() { - if (LOG_EGL) { - Log.w("EglHelper", "destroySurface() tid=" - + Thread.currentThread().getId()); - } - destroySurfaceImp(); - } - - private void destroySurfaceImp() { - if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { - mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_CONTEXT); - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view != null) { - view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, - mEglSurface); - } - mEglSurface = null; - } - } - - public void finish() { - if (LOG_EGL) { - Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId()); - } - if (mEglContext != null) { - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view != null) { - view.mEGLContextFactory - .destroyContext(mEgl, mEglDisplay, mEglContext); - } - mEglContext = null; - } - if (mEglDisplay != null) { - mEgl.eglTerminate(mEglDisplay); - mEglDisplay = null; - } - } - - private void throwEglException(String function) { - throwEglException(function, mEgl.eglGetError()); - } - - public static void throwEglException(String function, int error) { - String message = formatEglError(function, error); - if (LOG_THREADS) { - Log.e("EglHelper", "throwEglException tid=" - + Thread.currentThread().getId() + " " - + message); - } - throw new RuntimeException(message); - } - - public static void logEglErrorAsWarning(String tag, String function, int error) { - Log.w(tag, formatEglError(function, error)); - } - - public static String formatEglError(String function, int error) { - return function + " failed: "; // + - // EGLLogWrapper.getErrorString(error); - } - - private WeakReference mGLSurfaceViewWeakRef; - EGL10 mEgl; - EGLDisplay mEglDisplay; - EGLSurface mEglSurface; - EGLConfig mEglConfig; - EGLContext mEglContext; - - } - - /** - * A generic GL Thread. Takes care of initializing EGL and GL. Delegates to - * a Renderer instance to do the actual drawing. Can be configured to render - * continuously or on request. All potentially blocking synchronization is - * done through the sGLThreadManager object. This avoids multiple-lock - * ordering issues. - */ - static class GLThread extends Thread { - GLThread(WeakReference glSurfaceViewWeakRef) { - super(); - mWidth = 0; - mHeight = 0; - mRequestRender = true; - mRenderMode = RENDERMODE_CONTINUOUSLY; - mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; - } - - @Override - public void run() { - setName("GLThread " + getId()); - if (LOG_THREADS) { - Log.i("GLThread", "starting tid=" + getId()); - } - - try { - guardedRun(); - } catch (InterruptedException e) { - // fall thru and exit normally - } finally { - sGLThreadManager.threadExiting(this); - } - } - - /* - * This private method should only be called inside a - * synchronized(sGLThreadManager) block. - */ - private void stopEglSurfaceLocked() { - if (mHaveEglSurface) { - mHaveEglSurface = false; - mEglHelper.destroySurface(); - } - } - - /* - * This private method should only be called inside a - * synchronized(sGLThreadManager) block. - */ - private void stopEglContextLocked() { - if (mHaveEglContext) { - mEglHelper.finish(); - mHaveEglContext = false; - sGLThreadManager.releaseEglContextLocked(this); - } - } - - private void guardedRun() throws InterruptedException { - mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); - mHaveEglContext = false; - mHaveEglSurface = false; - try { - GL10 gl = null; - boolean createEglContext = false; - boolean createEglSurface = false; - boolean createGlInterface = false; - boolean lostEglContext = false; - boolean sizeChanged = false; - boolean wantRenderNotification = false; - boolean doRenderNotification = false; - boolean askedToReleaseEglContext = false; - int w = 0; - int h = 0; - Runnable event = null; - - while (true) { - synchronized (sGLThreadManager) { - while (true) { - if (mShouldExit) { - return; - } - - if (!mEventQueue.isEmpty()) { - event = mEventQueue.remove(0); - break; - } - - // Update the pause state. - boolean pausing = false; - if (mPaused != mRequestPaused) { - pausing = mRequestPaused; - mPaused = mRequestPaused; - sGLThreadManager.notifyAll(); - if (LOG_PAUSE_RESUME) { - Log.i("GLThread", "mPaused is now " + mPaused - + " tid=" + getId()); - } - } - - // Do we need to give up the EGL context? - if (mShouldReleaseEglContext) { - if (LOG_SURFACE) { - Log.i("GLThread", - "releasing EGL context because asked to tid=" - + getId()); - } - stopEglSurfaceLocked(); - stopEglContextLocked(); - mShouldReleaseEglContext = false; - askedToReleaseEglContext = true; - } - - // Have we lost the EGL context? - if (lostEglContext) { - stopEglSurfaceLocked(); - stopEglContextLocked(); - lostEglContext = false; - } - - // When pausing, release the EGL surface: - if (pausing && mHaveEglSurface) { - if (LOG_SURFACE) { - Log.i("GLThread", - "releasing EGL surface because paused tid=" - + getId()); - } - stopEglSurfaceLocked(); - } - - // When pausing, optionally release the EGL Context: - if (pausing && mHaveEglContext) { - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - boolean preserveEglContextOnPause = view == null ? - false : view.mPreserveEGLContextOnPause; - if (!preserveEglContextOnPause - || sGLThreadManager - .shouldReleaseEGLContextWhenPausing()) { - stopEglContextLocked(); - if (LOG_SURFACE) { - Log.i("GLThread", - "releasing EGL context because paused tid=" - + getId()); - } - } - } - - // When pausing, optionally terminate EGL: - if (pausing) { - if (sGLThreadManager.shouldTerminateEGLWhenPausing()) { - mEglHelper.finish(); - if (LOG_SURFACE) { - Log.i("GLThread", - "terminating EGL because paused tid=" - + getId()); - } - } - } - - // Have we lost the SurfaceView surface? - if ((!mHasSurface) && (!mWaitingForSurface)) { - if (LOG_SURFACE) { - Log.i("GLThread", - "noticed surfaceView surface lost tid=" - + getId()); - } - if (mHaveEglSurface) { - stopEglSurfaceLocked(); - } - mWaitingForSurface = true; - mSurfaceIsBad = false; - sGLThreadManager.notifyAll(); - } - - // Have we acquired the surface view surface? - if (mHasSurface && mWaitingForSurface) { - if (LOG_SURFACE) { - Log.i("GLThread", - "noticed surfaceView surface acquired tid=" - + getId()); - } - mWaitingForSurface = false; - sGLThreadManager.notifyAll(); - } - - if (doRenderNotification) { - if (LOG_SURFACE) { - Log.i("GLThread", "sending render notification tid=" - + getId()); - } - wantRenderNotification = false; - doRenderNotification = false; - mRenderComplete = true; - sGLThreadManager.notifyAll(); - } - - // Ready to draw? - if (readyToDraw()) { - - // If we don't have an EGL context, try to - // acquire one. - if (!mHaveEglContext) { - if (askedToReleaseEglContext) { - askedToReleaseEglContext = false; - } else if (sGLThreadManager - .tryAcquireEglContextLocked(this)) { - try { - mEglHelper.start(); - } catch (RuntimeException t) { - sGLThreadManager - .releaseEglContextLocked(this); - throw t; - } - mHaveEglContext = true; - createEglContext = true; - - sGLThreadManager.notifyAll(); - } - } - - if (mHaveEglContext && !mHaveEglSurface) { - mHaveEglSurface = true; - createEglSurface = true; - createGlInterface = true; - sizeChanged = true; - } - - if (mHaveEglSurface) { - if (mSizeChanged) { - sizeChanged = true; - w = mWidth; - h = mHeight; - wantRenderNotification = true; - if (LOG_SURFACE) { - Log.i("GLThread", - "noticing that we want render notification tid=" - + getId()); - } - - // Destroy and recreate the EGL surface. - createEglSurface = true; - - mSizeChanged = false; - } - mRequestRender = false; - sGLThreadManager.notifyAll(); - break; - } - } - - // By design, this is the only place in a GLThread - // thread where we wait(). - if (LOG_THREADS) { - Log.i("GLThread", "waiting tid=" + getId() - + " mHaveEglContext: " + mHaveEglContext - + " mHaveEglSurface: " + mHaveEglSurface - + " mPaused: " + mPaused - + " mHasSurface: " + mHasSurface - + " mSurfaceIsBad: " + mSurfaceIsBad - + " mWaitingForSurface: " + mWaitingForSurface - + " mWidth: " + mWidth - + " mHeight: " + mHeight - + " mRequestRender: " + mRequestRender - + " mRenderMode: " + mRenderMode); - } - sGLThreadManager.wait(); - } - } // end of synchronized(sGLThreadManager) - - if (event != null) { - event.run(); - event = null; - continue; - } - - if (createEglSurface) { - if (LOG_SURFACE) { - Log.w("GLThread", "egl createSurface"); - } - if (!mEglHelper.createSurface()) { - synchronized (sGLThreadManager) { - mSurfaceIsBad = true; - sGLThreadManager.notifyAll(); - } - continue; - } - createEglSurface = false; - } - - if (createGlInterface) { - gl = (GL10) mEglHelper.createGL(); - - sGLThreadManager.checkGLDriver(gl); - createGlInterface = false; - } - - if (createEglContext) { - if (LOG_RENDERER) { - Log.w("GLThread", "onSurfaceCreated"); - } - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view != null) { - view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); - } - createEglContext = false; - } - - if (sizeChanged) { - if (LOG_RENDERER) { - Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); - } - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view != null) { - view.mRenderer.onSurfaceChanged(gl, w, h); - } - sizeChanged = false; - } - - if (LOG_RENDERER_DRAW_FRAME) { - Log.w("GLThread", "onDrawFrame tid=" + getId()); - } - { - GLSurfaceView view = mGLSurfaceViewWeakRef.get(); - if (view != null) { - view.mRenderer.onDrawFrame(gl); - } - } - int swapError = mEglHelper.swap(); - switch (swapError) { - case EGL10.EGL_SUCCESS: - break; - case EGL11.EGL_CONTEXT_LOST: - if (LOG_SURFACE) { - Log.i("GLThread", "egl context lost tid=" + getId()); - } - lostEglContext = true; - break; - default: - // Other errors typically mean that the current - // surface is bad, - // probably because the SurfaceView surface has been - // destroyed, - // but we haven't been notified yet. - // Log the error to help developers understand why - // rendering stopped. - EglHelper.logEglErrorAsWarning("GLThread", "eglSwapBuffers", - swapError); - - synchronized (sGLThreadManager) { - mSurfaceIsBad = true; - sGLThreadManager.notifyAll(); - } - break; - } - - if (wantRenderNotification) { - doRenderNotification = true; - } - } - - } finally { - /* - * clean-up everything... - */ - synchronized (sGLThreadManager) { - stopEglSurfaceLocked(); - stopEglContextLocked(); - } - } - } - - public boolean ableToDraw() { - return mHaveEglContext && mHaveEglSurface && readyToDraw(); - } - - private boolean readyToDraw() { - return (!mPaused) && mHasSurface && (!mSurfaceIsBad) - && (mWidth > 0) && (mHeight > 0) - && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); - } - - public void setRenderMode(int renderMode) { - if (!((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY))) { - throw new IllegalArgumentException("renderMode"); - } - synchronized (sGLThreadManager) { - mRenderMode = renderMode; - sGLThreadManager.notifyAll(); - } - } - - public int getRenderMode() { - synchronized (sGLThreadManager) { - return mRenderMode; - } - } - - public void requestRender() { - synchronized (sGLThreadManager) { - mRequestRender = true; - sGLThreadManager.notifyAll(); - } - } - - public void surfaceCreated() { - synchronized (sGLThreadManager) { - if (LOG_THREADS) { - Log.i("GLThread", "surfaceCreated tid=" + getId()); - } - mHasSurface = true; - sGLThreadManager.notifyAll(); - while ((mWaitingForSurface) && (!mExited)) { - try { - sGLThreadManager.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - } - - public void surfaceDestroyed() { - synchronized (sGLThreadManager) { - if (LOG_THREADS) { - Log.i("GLThread", "surfaceDestroyed tid=" + getId()); - } - mHasSurface = false; - sGLThreadManager.notifyAll(); - while ((!mWaitingForSurface) && (!mExited)) { - try { - sGLThreadManager.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - } - - public void onPause() { - synchronized (sGLThreadManager) { - if (LOG_PAUSE_RESUME) { - Log.i("GLThread", "onPause tid=" + getId()); - } - mRequestPaused = true; - sGLThreadManager.notifyAll(); - while ((!mExited) && (!mPaused)) { - if (LOG_PAUSE_RESUME) { - Log.i("Main thread", "onPause waiting for mPaused."); - } - try { - sGLThreadManager.wait(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - } - - public void onResume() { - synchronized (sGLThreadManager) { - if (LOG_PAUSE_RESUME) { - Log.i("GLThread", "onResume tid=" + getId()); - } - mRequestPaused = false; - mRequestRender = true; - mRenderComplete = false; - sGLThreadManager.notifyAll(); - while ((!mExited) && mPaused && (!mRenderComplete)) { - if (LOG_PAUSE_RESUME) { - Log.i("Main thread", "onResume waiting for !mPaused."); - } - try { - sGLThreadManager.wait(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - } - - public void onWindowResize(int w, int h) { - synchronized (sGLThreadManager) { - mWidth = w; - mHeight = h; - mSizeChanged = true; - mRequestRender = true; - mRenderComplete = false; - sGLThreadManager.notifyAll(); - - // Wait for thread to react to resize and render a frame - while (!mExited && !mPaused && !mRenderComplete - && ableToDraw()) { - if (LOG_SURFACE) { - Log.i("Main thread", - "onWindowResize waiting for render complete from tid=" - + getId()); - } - try { - sGLThreadManager.wait(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - } - - public void requestExitAndWait() { - // don't call this from GLThread thread or it is a guaranteed - // deadlock! - synchronized (sGLThreadManager) { - mShouldExit = true; - sGLThreadManager.notifyAll(); - while (!mExited) { - try { - sGLThreadManager.wait(); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - } - - public void requestReleaseEglContextLocked() { - mShouldReleaseEglContext = true; - sGLThreadManager.notifyAll(); - } - - /** - * Queue an "event" to be run on the GL rendering thread. - * - * @param r - * the runnable to be run on the GL rendering thread. - */ - public void queueEvent(Runnable r) { - if (r == null) { - throw new IllegalArgumentException("r must not be null"); - } - synchronized (sGLThreadManager) { - mEventQueue.add(r); - sGLThreadManager.notifyAll(); - } - } - - // Once the thread is started, all accesses to the following member - // variables are protected by the sGLThreadManager monitor - private boolean mShouldExit; - private boolean mExited; - private boolean mRequestPaused; - private boolean mPaused; - private boolean mHasSurface; - private boolean mSurfaceIsBad; - private boolean mWaitingForSurface; - private boolean mHaveEglContext; - private boolean mHaveEglSurface; - private boolean mShouldReleaseEglContext; - private int mWidth; - private int mHeight; - private int mRenderMode; - private boolean mRequestRender; - private boolean mRenderComplete; - private ArrayList mEventQueue = new ArrayList(); - private boolean mSizeChanged = true; - - // End of member variables protected by the sGLThreadManager monitor. - - private EglHelper mEglHelper; - - /** - * Set once at thread construction time, nulled out when the parent view - * is garbage called. This weak reference allows the GLSurfaceView to be - * garbage collected while the GLThread is still alive. - */ - private WeakReference mGLSurfaceViewWeakRef; - - } - - static class LogWriter extends Writer { - - @Override - public void close() { - flushBuilder(); - } - - @Override - public void flush() { - flushBuilder(); - } - - @Override - public void write(char[] buf, int offset, int count) { - for (int i = 0; i < count; i++) { - char c = buf[offset + i]; - if (c == '\n') { - flushBuilder(); - } - else { - mBuilder.append(c); - } - } - } - - private void flushBuilder() { - if (mBuilder.length() > 0) { - Log.v("GLSurfaceView", mBuilder.toString()); - mBuilder.delete(0, mBuilder.length()); - } - } - - private StringBuilder mBuilder = new StringBuilder(); - } - - private void checkRenderThreadState() { - if (mGLThread != null) { - throw new IllegalStateException( - "setRenderer has already been called for this instance."); - } - } - - private static class GLThreadManager { - private static String TAG = "GLThreadManager"; - - public synchronized void threadExiting(GLThread thread) { - if (LOG_THREADS) { - Log.i("GLThread", "exiting tid=" + thread.getId()); - } - thread.mExited = true; - if (mEglOwner == thread) { - mEglOwner = null; - } - notifyAll(); - } - - /* - * Tries once to acquire the right to use an EGL context. Does not - * block. Requires that we are already in the sGLThreadManager monitor - * when this is called. - * @return true if the right to use an EGL context was acquired. - */ - public boolean tryAcquireEglContextLocked(GLThread thread) { - if (mEglOwner == thread || mEglOwner == null) { - mEglOwner = thread; - notifyAll(); - return true; - } - checkGLESVersion(); - if (mMultipleGLESContextsAllowed) { - return true; - } - // Notify the owning thread that it should release the context. - // TODO: implement a fairness policy. Currently - // if the owning thread is drawing continuously it will just - // reacquire the EGL context. - if (mEglOwner != null) { - mEglOwner.requestReleaseEglContextLocked(); - } - return false; - } - - /* - * Releases the EGL context. Requires that we are already in the - * sGLThreadManager monitor when this is called. - */ - public void releaseEglContextLocked(GLThread thread) { - if (mEglOwner == thread) { - mEglOwner = null; - } - notifyAll(); - } - - public synchronized boolean shouldReleaseEGLContextWhenPausing() { - // Release the EGL context when pausing even if - // the hardware supports multiple EGL contexts. - // Otherwise the device could run out of EGL contexts. - return mLimitedGLESContexts; - } - - public synchronized boolean shouldTerminateEGLWhenPausing() { - checkGLESVersion(); - return !mMultipleGLESContextsAllowed; - } - - public synchronized void checkGLDriver(GL10 gl) { - if (!mGLESDriverCheckComplete) { - checkGLESVersion(); - String renderer = gl.glGetString(GL10.GL_RENDERER); - if (mGLESVersion < kGLES_20) { - mMultipleGLESContextsAllowed = - !renderer.startsWith(kMSM7K_RENDERER_PREFIX); - notifyAll(); - } - mLimitedGLESContexts = !mMultipleGLESContextsAllowed; - if (LOG_SURFACE) { - Log.w(TAG, "checkGLDriver renderer = \"" + renderer - + "\" multipleContextsAllowed = " - + mMultipleGLESContextsAllowed - + " mLimitedGLESContexts = " + mLimitedGLESContexts); - } - mGLESDriverCheckComplete = true; - } - } - - private void checkGLESVersion() { - // if (!mGLESVersionCheckComplete) { - // mGLESVersion = SystemProperties.getInt( - // "ro.opengles.version", - // ConfigurationInfo.GL_ES_VERSION_UNDEFINED); - // if (mGLESVersion >= kGLES_20) { - // mMultipleGLESContextsAllowed = true; - // } - // if (LOG_SURFACE) { - // Log.w(TAG, "checkGLESVersion mGLESVersion =" + - // " " + mGLESVersion + " mMultipleGLESContextsAllowed = " - // + mMultipleGLESContextsAllowed); - // } - // mGLESVersionCheckComplete = true; - // } - } - - /** - * This check was required for some pre-Android-3.0 hardware. Android - * 3.0 provides support for hardware-accelerated views, therefore - * multiple EGL contexts are supported on all Android 3.0+ EGL drivers. - */ - private boolean mGLESVersionCheckComplete; - private int mGLESVersion; - private boolean mGLESDriverCheckComplete; - private boolean mMultipleGLESContextsAllowed; - private boolean mLimitedGLESContexts; - private static final int kGLES_20 = 0x20000; - private static final String kMSM7K_RENDERER_PREFIX = - "Q3Dimension MSM7500 "; - private GLThread mEglOwner; - } - - /* package */static final GLThreadManager sGLThreadManager = new GLThreadManager(); - - private final WeakReference mThisWeakRef = - new WeakReference(this); - private GLThread mGLThread; - private Renderer mRenderer; - private boolean mDetached; - private EGLConfigChooser mEGLConfigChooser; - private EGLContextFactory mEGLContextFactory; - private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory; - private GLWrapper mGLWrapper; - private int mDebugFlags; - private int mEGLContextClientVersion; - private boolean mPreserveEGLContextOnPause; -} diff --git a/src/org/oscim/view/Compass.java b/src/org/oscim/view/Compass.java index b90063c7..ba7d94b7 100644 --- a/src/org/oscim/view/Compass.java +++ b/src/org/oscim/view/Compass.java @@ -29,7 +29,7 @@ public class Compass { mAngle = event.values[0]; if (mMapView != null) { - mMapView.getMapPosition().setRotation(mAngle); + mMapView.getMapPosition().setRotation(-mAngle); mMapView.redrawMap(); } } diff --git a/src/org/oscim/view/MapView.java b/src/org/oscim/view/MapView.java index b65b5fd4..4332b5be 100644 --- a/src/org/oscim/view/MapView.java +++ b/src/org/oscim/view/MapView.java @@ -57,6 +57,8 @@ public class MapView extends FrameLayout { public static final boolean debugFrameTime = false; public static final boolean testRegionZoom = false; + public static final boolean staticLabeling = true; + private static final boolean debugDatabase = false; RegionLookup mRegionLookup; @@ -82,8 +84,6 @@ public class MapView extends FrameLayout { private String mRenderTheme; private Map mMapOptions; - // private final Handler mHandler; - /** * @param context * the enclosing MapActivity instance. diff --git a/src/org/oscim/view/MapViewPosition.java b/src/org/oscim/view/MapViewPosition.java index 8fef7a3b..619cdaa7 100644 --- a/src/org/oscim/view/MapViewPosition.java +++ b/src/org/oscim/view/MapViewPosition.java @@ -27,9 +27,10 @@ import android.util.Log; * together with its zoom level. */ public class MapViewPosition { - // private static final String TAG = "MapViewPosition"; + private static final String TAG = "MapViewPosition"; public final static int MAX_ZOOMLEVEL = 17; + public final static int MIN_ZOOMLEVEL = 2; private final static float MAX_ANGLE = 20; @@ -68,7 +69,7 @@ public class MapViewPosition { void setViewport(int width, int height) { Matrix.frustumM(mProjMatrix, 0, -0.5f * width, 0.5f * width, - -0.5f * height, 0.5f * height, 1, 2); + 0.5f * height, -0.5f * height, 1, 2); Matrix.translateM(mProjMatrix, 0, 0, 0, -1); @@ -92,14 +93,14 @@ public class MapViewPosition { // && mapPosition.scale == mScale // && mapPosition.angle == mRotation) // return false; + byte z = mZoomLevel; mapPosition.lat = mLatitude; mapPosition.lon = mLongitude; mapPosition.angle = mRotation; - mapPosition.zoomLevel = mZoomLevel; mapPosition.scale = mScale; + mapPosition.zoomLevel = z; - byte z = mZoomLevel; mapPosition.x = MercatorProjection.longitudeToPixelX(mLongitude, z); mapPosition.y = MercatorProjection.latitudeToPixelY(mLatitude, z); @@ -138,13 +139,12 @@ public class MapViewPosition { if (mv[3] != 0) { float w = 1 / mv[3]; - float xx = mv[0] * w; - float yy = mv[1] * w; - - coords[position] = xx; - coords[position + 1] = yy; + coords[position] = mv[0] * w; + coords[position + 1] = mv[1] * w; + } else { + // else what? + Log.d(TAG, "... what?"); } - // else what? } private void updateMatrix() { @@ -152,7 +152,7 @@ public class MapViewPosition { // tilt map float tilt = mTilt; - Matrix.setRotateM(mTmpMatrix, 0, -tilt / (mHeight / 2), 1, 0, 0); + Matrix.setRotateM(mTmpMatrix, 0, tilt / (mHeight / 2), 1, 0, 0); // apply first rotation, then tilt Matrix.multiplyMM(mRotateMatrix, 0, mTmpMatrix, 0, mRotateMatrix, 0); @@ -177,10 +177,10 @@ public class MapViewPosition { float tilt = FloatMath.sin((float) Math.toRadians(mTilt)) * 4; - unproject(-1, 1, tilt, mBBoxCoords, 0); // top-left - unproject(1, 1, tilt, mBBoxCoords, 2); // top-right - unproject(1, -1, -tilt, mBBoxCoords, 4); // bottom-right - unproject(-1, -1, -tilt, mBBoxCoords, 6); // bottom-left + unproject(-1, 1, -tilt, mBBoxCoords, 0); // top-left + unproject(1, 1, -tilt, mBBoxCoords, 2); // top-right + unproject(1, -1, tilt, mBBoxCoords, 4); // bottom-right + unproject(-1, -1, tilt, mBBoxCoords, 6); // bottom-left byte z = mZoomLevel; double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, z); @@ -333,8 +333,8 @@ public class MapViewPosition { if (mMapView.enableRotation || mMapView.enableCompass) { double rad = Math.toRadians(mRotation); - double x = dx * Math.cos(rad) + dy * -Math.sin(rad); - double y = dx * Math.sin(rad) + dy * Math.cos(rad); + double x = dx * Math.cos(rad) + dy * Math.sin(rad); + double y = dx * -Math.sin(rad) + dy * Math.cos(rad); dx = x; dy = y; } @@ -357,7 +357,7 @@ public class MapViewPosition { moveMap(cx, cy); // Log.d("MapViewPosition", "rotate:" + angle + " " + (mRotation - // angle)); - mRotation -= angle; + mRotation += angle; updateMatrix(); } @@ -426,12 +426,13 @@ public class MapViewPosition { int z = FastMath.log2((int) newScale); - if (z <= 0 || (z >= MAX_ZOOMLEVEL && mScale >= 8)) + if (z < MIN_ZOOMLEVEL || (z >= MAX_ZOOMLEVEL && mScale >= 8)) return false; if (z > MAX_ZOOMLEVEL) { - // z16 shows everything, just increase scaling - if (mScale * scale > 8) + // z17 shows everything, just increase scaling + // need to fix this for ScanBox + if (mScale * scale > 2) // 8) return false; mScale *= scale; diff --git a/src/org/oscim/view/overlay/Overlay.java b/src/org/oscim/view/overlay/Overlay.java deleted file mode 100644 index 4d5a2a04..00000000 --- a/src/org/oscim/view/overlay/Overlay.java +++ /dev/null @@ -1,35 +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 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.view.overlay; - -public class Overlay { - - synchronized void move() { - - } - - synchronized void addBitmap() { - - } - - synchronized boolean onTouch() { - - return true; - } - - synchronized void draw() { - - } -} diff --git a/src/org/oscim/view/overlay/TiledOverlay.java b/src/org/oscim/view/overlay/TiledOverlay.java deleted file mode 100644 index a3c68284..00000000 --- a/src/org/oscim/view/overlay/TiledOverlay.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2010, 2011, 2012 mapsforge.org - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see . - */ -package org.oscim.view.overlay; - -public class TiledOverlay extends Overlay { - -} diff --git a/src/org/oscim/view/renderer/GLRenderer.java b/src/org/oscim/view/renderer/GLRenderer.java index 91b6f7e1..00802c26 100644 --- a/src/org/oscim/view/renderer/GLRenderer.java +++ b/src/org/oscim/view/renderer/GLRenderer.java @@ -21,7 +21,6 @@ import static android.opengl.GLES20.GL_DYNAMIC_DRAW; import static android.opengl.GLES20.GL_ONE; import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA; import static android.opengl.GLES20.GL_POLYGON_OFFSET_FILL; -import static android.opengl.GLES20.GL_STENCIL_BUFFER_BIT; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -63,7 +62,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { private final MapView mMapView; private final MapViewPosition mMapViewPosition; - private static final MapPosition mMapPosition = new MapPosition(); + private static MapPosition mMapPosition; private static ArrayList mVBOs; @@ -82,7 +81,8 @@ public class GLRenderer implements GLSurfaceView.Renderer { // mNextTiles is set by TileLoader and swapped with // mDrawTiles in onDrawFrame in GL thread. - private static TilesData mNextTiles, mDrawTiles; + private static TilesData mNextTiles; + /* package */static TilesData mDrawTiles; // flag set by updateVisibleList when current visible tiles // changed. used in onDrawFrame to flip mNextTiles/mDrawTiles @@ -95,9 +95,78 @@ public class GLRenderer implements GLSurfaceView.Renderer { private static boolean mUpdateColor = false; - // lock to synchronize Main- and GL-Thread + // drawlock to synchronize Main- and GL-Thread static ReentrantLock tilelock = new ReentrantLock(); - static ReentrantLock lock = new ReentrantLock(); + static ReentrantLock drawlock = new ReentrantLock(); + + /* package */static int mHolderCount; + + // scanline fill class used to check tile visibility + private static ScanBox mScanBox = new ScanBox() { + @Override + void setVisible(int y, int x1, int x2) { + int cnt = mDrawTiles.cnt; + + MapTile[] tiles = mDrawTiles.tiles; + + for (int i = 0; i < cnt; i++) { + MapTile t = tiles[i]; + if (t.tileY == y && t.tileX >= x1 && t.tileX < x2) + t.isVisible = true; + } + + int xmax = 1 << mZoom; + if (x1 >= 0 && x2 < xmax) + return; + + // add placeholder tiles to show both sides + // of date line... + for (int x = x1; x < x2; x++) { + MapTile holder = null; + MapTile tile = null; + boolean found = false; + + int xx = x; + + if (x >= 0 && x < xmax) + continue; + + if (x < 0) + xx = xmax + x; + else + xx = x - xmax; + + if (xx < 0 || xx >= xmax) + continue; + + for (int i = cnt; i < cnt + mHolderCount; i++) + if (tiles[i].tileX == x && tiles[i].tileY == y) { + found = true; + break; + } + + if (found) + continue; + + for (int i = 0; i < cnt; i++) + if (tiles[i].tileX == xx && tiles[i].tileY == y) { + tile = tiles[i]; + break; + } + + if (tile == null) + continue; + + // Log.d(TAG, "add placeholder " + y + " " + x + ">>" + xx + " " + // + tile); + + holder = new MapTile(x, y, mZoom); + holder.isVisible = true; + holder.holder = tile; + tiles[cnt + mHolderCount++] = holder; + } + } + }; /** * @param mapView @@ -108,8 +177,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { mMapView = mapView; mMapViewPosition = mapView.getMapViewPosition(); - // mMapPosition = new MapPosition(); - + mMapPosition = new MapPosition(); mMapPosition.init(); Matrix.setIdentityM(mMVPMatrix, 0); @@ -141,63 +209,38 @@ public class GLRenderer implements GLSurfaceView.Renderer { } /** - * called by TileLoader when list of active tiles changed + * 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 - * @return mNextTiles (the previously active tiles) */ - static TilesData updateTiles(TilesData tiles) { - GLRenderer.tilelock.lock(); + static void updateTiles(TilesData tiles) { - // unlock previously active tiles - for (int i = 0; i < mNextTiles.cnt; i++) { - MapTile t = mNextTiles.tiles[i]; - boolean found = false; - - for (int j = 0; j < tiles.cnt; j++) { - if (tiles.tiles[j] == t) { - found = true; - break; - } - } - if (found) - continue; - - for (int j = 0; j < mDrawTiles.cnt; j++) { - if (mDrawTiles.tiles[j] == t) { - found = true; - break; - } - } - if (found) - continue; - - t.unlock(); - } - - TilesData tmp = mNextTiles; - mNextTiles = tiles; + MapTile[] newTiles = tiles.tiles; // lock tiles (and their proxies) to not be removed from cache - for (int i = 0; i < mNextTiles.cnt; i++) { - MapTile t = mNextTiles.tiles[i]; - if (!t.isLocked) - t.lock(); - } + for (int i = 0, n = tiles.cnt; i < n; i++) + newTiles[i].lock(); - for (int j = 0; j < mDrawTiles.cnt; j++) { - MapTile t = mDrawTiles.tiles[j]; - if (!t.isLocked) - t.lock(); - } + // dont flip next/drawTiles while copying + GLRenderer.tilelock.lock(); - // GLThread flips mNextTiles with mDrawTiles + 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(); - - return tmp; } /** @@ -267,7 +310,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { // add fill coordinates newSize += 8; - // FIXME probably not a good idea to do this in gl thread... + // probably not a good idea to do this in gl thread... if (sbuf.capacity() < newSize) { ByteBuffer bbuf = ByteBuffer.allocateDirect(newSize * SHORT_BYTES) .order(ByteOrder.nativeOrder()); @@ -317,7 +360,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (tile.vbo.size > newSize && tile.vbo.size < newSize * 4 && mBufferMemoryUsage < LIMIT_BUFFERS) { GLES20.glBufferSubData(GL_ARRAY_BUFFER, 0, newSize, sbuf); - // Log.d(TAG, "reuse buffer " + tile.vbo.size + " " + newSize); } else { mBufferMemoryUsage -= tile.vbo.size; tile.vbo.size = newSize; @@ -385,68 +427,26 @@ public class GLRenderer implements GLSurfaceView.Renderer { CACHE_TILES -= 50; } - private static boolean isVisible(MapTile tile) { - float dx, dy, scale, div = 1; - MapPosition mapPosition = mMapPosition; - int diff = mapPosition.zoomLevel - tile.zoomLevel; - - if (diff < 0) - div = (1 << -diff); - else if (diff > 0) - div = (1.0f / (1 << diff)); - - scale = mapPosition.scale / div; - dx = (float) (tile.pixelX - mapPosition.x * div); - dy = (float) (tile.pixelY - mapPosition.y * div); - - int size = Tile.TILE_SIZE; - int sx = (int) (dx * scale); - int sy = (int) (dy * scale); - - // FIXME little hack, need to do scanline check or sth - // this kindof works for typical screen aspect - if (mRotate) { - int ssize = mWidth > mHeight ? mWidth : mHeight; - ssize += Tile.TILE_SIZE; - if (sy > ssize / 2 || sx > ssize / 2 - || sx + size * scale < -ssize / 2 - || sy + size * scale < -ssize / 2) { - tile.isVisible = false; - return false; - } - } else { - if (sy > mHeight / 2 || sx > mWidth / 2 - || sx + size * scale < -mWidth / 2 - || sy + size * scale < -mHeight / 2) { - tile.isVisible = false; - return false; - } - } - tile.isVisible = true; - - return true; - } - private static boolean mRotate = false; private static void setMatrix(float[] matrix, MapTile tile, float div, boolean project) { - float x, y, scale; - MapPosition mapPosition = mMapPosition; - scale = mapPosition.scale / (div * COORD_MULTIPLIER); - x = (float) (tile.pixelX - mapPosition.x * div); - y = (float) (tile.pixelY - mapPosition.y * div); + float x = (float) (tile.pixelX - mapPosition.x * div); + float y = (float) (tile.pixelY - mapPosition.y * div); + float scale = mapPosition.scale / div; Matrix.setIdentityM(matrix, 0); - // scale to tile to world coordinates - Matrix.scaleM(matrix, 0, scale, scale, 1); - // translate relative to map center - Matrix.translateM(matrix, 0, x * COORD_MULTIPLIER, - -(y + Tile.TILE_SIZE) * COORD_MULTIPLIER, 0); + matrix[12] = x * scale; + matrix[13] = y * scale; + + // scale to tile to world coordinates + scale /= COORD_MULTIPLIER; + matrix[0] = scale; + matrix[5] = scale; if (mRotate) Matrix.multiplyMM(matrix, 0, mapPosition.rotation, 0, matrix, 0); @@ -471,11 +471,17 @@ public class GLRenderer implements GLSurfaceView.Renderer { // prevent main thread recreating all tiles (updateMap) // while rendering is going. not have seen this happen // yet though - GLRenderer.lock.lock(); + GLRenderer.drawlock.lock(); if (MapView.debugFrameTime) start = SystemClock.uptimeMillis(); + if (mUpdateColor) { + float cc[] = mClearColor; + GLES20.glClearColor(cc[0], cc[1], cc[2], cc[3]); + mUpdateColor = false; + } + // Note: it seems faster to also clear the stencil buffer even // when not needed. probaly otherwise it is masked out from the // depth buffer as they share the same memory region afaik @@ -496,14 +502,17 @@ public class GLRenderer implements GLSurfaceView.Renderer { } if (mDrawTiles == null || mDrawTiles.cnt == 0) { - GLRenderer.lock.unlock(); + GLRenderer.drawlock.unlock(); return; } + mRotate = mMapView.enableRotation || mMapView.enableCompass; + // get current MapPosition, set mTileCoords (mapping of screen to model // coordinates) MapPosition mapPosition = mMapPosition; - boolean changed = mMapViewPosition.getMapPosition(mapPosition, mTileCoords); + float[] coords = mTileCoords; + boolean changed = mMapViewPosition.getMapPosition(mapPosition, coords); int tileCnt = mDrawTiles.cnt; MapTile[] tiles = mDrawTiles.tiles; @@ -517,29 +526,19 @@ public class GLRenderer implements GLSurfaceView.Renderer { // zoom-level changed. float div = scaleDiv(tiles[0]); - mRotate = mMapView.enableRotation || mMapView.enableCompass; - float s = Tile.TILE_SIZE; float scale = mapPosition.scale / div; - float px = (float) (mapPosition.x * div); - float py = (float) (mapPosition.y * div); + double px = mapPosition.x * div; + double py = mapPosition.y * div; - mTileCoords[0] = (px + mTileCoords[0] / scale) / s; - mTileCoords[1] = (py - mTileCoords[1] / scale) / s; - mTileCoords[2] = (px + mTileCoords[2] / scale) / s; - mTileCoords[3] = (py - mTileCoords[3] / scale) / s; - mTileCoords[4] = (px + mTileCoords[4] / scale) / s; - mTileCoords[5] = (py - mTileCoords[5] / scale) / s; - mTileCoords[6] = (px + mTileCoords[6] / scale) / s; - mTileCoords[7] = (py - mTileCoords[7] / scale) / s; + for (int i = 0; i < 8; i += 2) { + coords[i + 0] = (float) ((px + coords[i + 0] / scale) / s); + coords[i + 1] = (float) ((py + coords[i + 1] / scale) / s); + } - byte z = tiles[0].zoomLevel; - ScanBox.scan(mTileCoords, mDrawTiles, 1 << z); - } - - if (mUpdateColor && mClearColor != null) { - GLES20.glClearColor(mClearColor[0], mClearColor[1], mClearColor[2], mClearColor[3]); - mUpdateColor = false; + mHolderCount = 0; + mScanBox.scan(coords, tiles[0].zoomLevel); + tileCnt += mHolderCount; } uploadCnt = 0; @@ -554,15 +553,21 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (!tile.isVisible) continue; - if (tile.texture == null && TextRenderer.drawToTexture(tile)) - updateTextures++; + if (MapView.staticLabeling) { + if (tile.texture == null && TextRenderer.drawToTexture(tile)) + updateTextures++; + } if (tile.newData) { uploadTileData(tile); continue; } - - if (!tile.isReady) { + if (tile.holder != null) { + if (tile.holder.newData) { + uploadTileData(tile.holder); + } + tile.isReady = tile.holder.isReady; + } else if (!tile.isReady) { // check near relatives if they can serve as proxy MapTile rel = tile.rel.parent.tile; if (rel != null && rel.newData) { @@ -583,22 +588,26 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (uploadCnt > 0) checkBufferUsage(); - if (updateTextures > 0) - TextRenderer.compileTextures(); + if (MapView.staticLabeling) { + if (updateTextures > 0) + TextRenderer.compileTextures(); + } GLES20.glEnable(GL_DEPTH_TEST); GLES20.glEnable(GL_POLYGON_OFFSET_FILL); for (int i = 0; i < tileCnt; i++) { - if (tiles[i].isVisible && tiles[i].isReady) - drawTile(tiles[i]); + MapTile t = tiles[i]; + if (t.isVisible && t.isReady) + drawTile(t); } // proxies are clipped to the region where nothing was drawn to depth // buffer. TODO draw all parent before grandparent for (int i = 0; i < tileCnt; i++) { - if (tiles[i].isVisible && !tiles[i].isReady) - drawProxyTile(tiles[i]); + MapTile t = tiles[i]; + if (t.isVisible && !t.isReady && (t.holder == null)) + drawProxyTile(t); } // GlUtils.checkGlError("end draw"); @@ -608,56 +617,52 @@ public class GLRenderer implements GLSurfaceView.Renderer { mDrawCount = 0; mDrawSerial++; - GLES20.glEnable(GL_BLEND); - int z = mapPosition.zoomLevel; - float s = mapPosition.scale; + if (MapView.staticLabeling) { + GLES20.glEnable(GL_BLEND); + 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; + 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(FloatMath.sqrt(s) / scale, mProjMatrix); - else - TextRenderer.beginDraw(s, mProjMatrix); + if (z >= TileGenerator.STROKE_MAX_ZOOM_LEVEL) + TextRenderer.beginDraw(scale / FloatMath.sqrt(s), mProjMatrix); + else + TextRenderer.beginDraw(1 / s, mProjMatrix); - for (int i = 0; i < tileCnt; i++) { - if (!tiles[i].isVisible || tiles[i].texture == null) - continue; + for (int i = 0; i < tileCnt; i++) { + MapTile t = tiles[i]; + if (!t.isVisible) + continue; - setMatrix(mMVPMatrix, tiles[i], 1, false); - TextRenderer.drawTile(tiles[i], mMVPMatrix); + 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(); } - TextRenderer.endDraw(); // TODO call overlay renderer here - // s = 0.5f; - // mTileCoords[0] = -s; - // mTileCoords[1] = s; - // mTileCoords[2] = s; - // mTileCoords[3] = s; - // mTileCoords[4] = -s; - // mTileCoords[5] = -s; - // mTileCoords[6] = s; - // mTileCoords[7] = -s; - // - // Matrix.setIdentityM(mMVPMatrix, 0); - // // Matrix.scaleM(mMVPMatrix, 0, 0.99f, 0.99f, 1); - // // Matrix.multiplyMM(mMVPMatrix, 0, mRotateMatrix, 0, mMVPMatrix, 0); - // // Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0); - // PolygonRenderer.debugDraw(mMVPMatrix, mTileCoords); - if (MapView.debugFrameTime) { GLES20.glFinish(); Log.d(TAG, "draw took " + (SystemClock.uptimeMillis() - start)); } - GLRenderer.lock.unlock(); + GLRenderer.drawlock.unlock(); } - // used to not draw a tile twice per frame... - private static byte mDrawSerial = 0; + // used to not draw a tile twice per frame. + private static int mDrawSerial = 0; private static void drawTile(MapTile tile) { // draw parents only once @@ -665,15 +670,16 @@ public class GLRenderer implements GLSurfaceView.Renderer { return; float div = scaleDiv(tile); + float[] mvp = mMVPMatrix; + MapPosition pos = mMapPosition; tile.lastDraw = mDrawSerial; - int z = mMapPosition.zoomLevel; - float s = mMapPosition.scale; - float[] mvp = mMVPMatrix; - setMatrix(mvp, tile, div, true); + if (tile.holder != null) + tile = tile.holder; + GLES20.glPolygonOffset(0, mDrawCount++); GLES20.glBindBuffer(GL_ARRAY_BUFFER, tile.vbo.id); @@ -696,21 +702,22 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (pl != null && pnext < lnext) { GLES20.glDisable(GL_BLEND); - pl = PolygonRenderer.drawPolygons(pl, lnext, mvp, z, s, !clipped); + pl = PolygonRenderer.drawPolygons(pos, pl, lnext, mvp, !clipped); clipped = true; } else { // FIXME if (!clipped) { - PolygonRenderer.drawPolygons(null, 0, mvp, z, s, true); + PolygonRenderer.drawPolygons(pos, null, 0, mvp, true); clipped = true; } GLES20.glEnable(GL_BLEND); - ll = LineRenderer.drawLines(tile, ll, pnext, mvp, div, z, s, simpleShader); + ll = LineRenderer.drawLines(pos, ll, pnext, mvp, div, + simpleShader, tile.lineOffset); } } } - // TODO could use tile.proxies here + // TODO should check tile.proxies here private static boolean drawProxyChild(MapTile tile) { int drawn = 0; for (int i = 0; i < 4; i++) { @@ -721,10 +728,10 @@ public class GLRenderer implements GLSurfaceView.Renderer { if (c == null) continue; - if (!isVisible(c)) { - drawn++; - continue; - } + // if (!isVisible(c)) { + // drawn++; + // continue; + // } if (c.isReady) { drawTile(c); @@ -789,10 +796,11 @@ public class GLRenderer implements GLSurfaceView.Renderer { mHeight = height; float s = 0.5f; - // use this to scale only the view to see which tiles are rendered + // use this to scale only the view to see better which tiles are + // rendered // s = 1.0f; Matrix.frustumM(mProjMatrix, 0, -s * width, s * width, - -s * height, s * height, 1, 2); + s * height, -s * height, 1, 2); Matrix.translateM(mProjMatrix, 0, 0, 0, -1); // set to zero: we modify the z value with polygon-offset for clipping @@ -805,7 +813,6 @@ public class GLRenderer implements GLSurfaceView.Renderer { mMapView.redrawMap(); return; } - mNewSurface = false; mBufferMemoryUsage = 0; @@ -827,17 +834,10 @@ public class GLRenderer implements GLSurfaceView.Renderer { // Set up textures TextRenderer.setup(numTiles); - if (mClearColor != null) { - GLES20.glClearColor(mClearColor[0], mClearColor[1], - mClearColor[2], mClearColor[3]); - } else { - GLES20.glClearColor(0.98f, 0.98f, 0.97f, 1.0f); - } - - GlUtils.checkGlError("onSurfaceChanged"); - - GLES20.glClear(GL_STENCIL_BUFFER_BIT); + if (mClearColor != null) + mUpdateColor = true; + // FIXME this should be synchronized mMapView.redrawMap(); } @@ -858,6 +858,7 @@ public class GLRenderer implements GLSurfaceView.Renderer { TextRenderer.init(); mNewSurface = true; + // mUpdateColor = true; // glEnable(GL_SCISSOR_TEST); // glScissor(0, 0, mWidth, mHeight); @@ -870,3 +871,46 @@ public class GLRenderer implements GLSurfaceView.Renderer { private boolean mNewSurface; } + +// +// private static boolean isVisible(MapTile tile) { +// float dx, dy, scale, div = 1; +// MapPosition mapPosition = mMapPosition; +// int diff = mapPosition.zoomLevel - tile.zoomLevel; +// +// if (diff < 0) +// div = (1 << -diff); +// else if (diff > 0) +// div = (1.0f / (1 << diff)); +// +// scale = mapPosition.scale / div; +// dx = (float) (tile.pixelX - mapPosition.x * div); +// dy = (float) (tile.pixelY - mapPosition.y * div); +// +// int size = Tile.TILE_SIZE; +// int sx = (int) (dx * scale); +// int sy = (int) (dy * scale); +// +// // FIXME little hack, need to do scanline check or sth +// // this kindof works for typical screen aspect +// if (mRotate) { +// int ssize = mWidth > mHeight ? mWidth : mHeight; +// ssize += Tile.TILE_SIZE; +// if (sy > ssize / 2 || sx > ssize / 2 +// || sx + size * scale < -ssize / 2 +// || sy + size * scale < -ssize / 2) { +// tile.isVisible = false; +// return false; +// } +// } else { +// if (sy > mHeight / 2 || sx > mWidth / 2 +// || sx + size * scale < -mWidth / 2 +// || sy + size * scale < -mHeight / 2) { +// tile.isVisible = false; +// return false; +// } +// } +// tile.isVisible = true; +// +// return true; +// } diff --git a/src/org/oscim/view/renderer/LineRenderer.java b/src/org/oscim/view/renderer/LineRenderer.java index a8b4a2d3..8060eeb6 100644 --- a/src/org/oscim/view/renderer/LineRenderer.java +++ b/src/org/oscim/view/renderer/LineRenderer.java @@ -18,6 +18,7 @@ import java.nio.ShortBuffer; import org.oscim.theme.renderinstruction.Line; import org.oscim.utils.GlUtils; +import org.oscim.view.MapPosition; import android.opengl.GLES20; import android.util.FloatMath; @@ -74,26 +75,25 @@ class LineRenderer { return true; } - // static int mSimple = 1; + static LineLayer drawLines(MapPosition pos, LineLayer layer, int next, + float[] matrix, float div, int mode, int bufferOffset) { - static LineLayer drawLines(MapTile tile, LineLayer layer, int next, float[] matrix, - float div, double zoom, float scale, int mode) { - // int mode = mSimple; + int zoom = pos.zoomLevel; + float scale = pos.scale; if (layer == null) return null; - // TODO should use fast line program when view is not tilted GLES20.glUseProgram(lineProgram[mode]); GLES20.glEnableVertexAttribArray(hLineVertexPosition[mode]); GLES20.glEnableVertexAttribArray(hLineTexturePosition[mode]); GLES20.glVertexAttribPointer(hLineVertexPosition[mode], 2, GLES20.GL_SHORT, - false, 8, tile.lineOffset + LINE_VERTICES_DATA_POS_OFFSET); + false, 8, bufferOffset + LINE_VERTICES_DATA_POS_OFFSET); GLES20.glVertexAttribPointer(hLineTexturePosition[mode], 2, GLES20.GL_SHORT, - false, 8, tile.lineOffset + LINE_VERTICES_DATA_TEX_OFFSET); + false, 8, bufferOffset + LINE_VERTICES_DATA_TEX_OFFSET); GLES20.glUniformMatrix4fv(hLineMatrix[mode], 1, false, matrix, 0); diff --git a/src/org/oscim/view/renderer/MapRenderer.java b/src/org/oscim/view/renderer/MapRenderer.java index 9af9416f..d34bd040 100644 --- a/src/org/oscim/view/renderer/MapRenderer.java +++ b/src/org/oscim/view/renderer/MapRenderer.java @@ -17,13 +17,12 @@ package org.oscim.view.renderer; import java.util.ArrayList; import java.util.Collections; -import org.oscim.core.MercatorProjection; import org.oscim.core.Tile; -import org.oscim.database.MapInfo; import org.oscim.theme.RenderTheme; import org.oscim.utils.GlConfigChooser; import org.oscim.view.MapPosition; import org.oscim.view.MapView; +import org.oscim.view.MapViewPosition; import org.oscim.view.generator.JobTile; import android.content.Context; @@ -37,9 +36,13 @@ public class MapRenderer extends GLSurfaceView { private GLRenderer mRenderer; private static final int MAX_TILES_IN_QUEUE = 40; + private static final int CACHE_THRESHOLD = 50; private static MapView mMapView; + private static final MapPosition mMapPosition = new MapPosition(); + private final MapViewPosition mMapViewPosition; + // new jobs for the MapWorkers private static ArrayList mJobList; @@ -49,16 +52,21 @@ public class MapRenderer extends GLSurfaceView { // tiles that have new data to upload, see passTile() private static ArrayList mTilesLoaded; - // current center tile, values used to check if position has + // TODO current boundary tiles, values used to check if position has // changed for updating current tile list - private static long mTileX, mTileY; - private static float mPrevScale; - private static byte mPrevZoom; + private static boolean mInitial; // private static MapPosition mCurPosition, mDrawPosition; private static int mWidth = 0, mHeight = 0; + // maps zoom-level to available zoom-levels in MapDatabase + // e.g. 16->16, 15->16, 14->13, 13->13, 12->13,.... + // private static int[] mZoomLevels; + + private static float[] mTileCoords = new float[8]; + private static int[] mBoundaryTiles = new int[8]; + // used for passing tiles to be rendered from TileLoader(Main-Thread) to // GLThread static final class TilesData { @@ -70,16 +78,61 @@ public class MapRenderer extends GLSurfaceView { } } - private static TilesData mCurrentTiles; + /* package */static TilesData mCurrentTiles; - // maps zoom-level to available zoom-levels in MapDatabase - // e.g. 16->16, 15->16, 14->13, 13->13, 12->13,.... - private static int[] mZoomLevels; + 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; + int xmax = 1 << mZoom; + + for (int x = x1; x < x2; x++) { + // MapTile holder = null; + MapTile tile = null; + + // boolean found = false; + if (cnt == max) { + Log.d(TAG, "reached max currentTiles " + max); + break; + } + int xx = x; + + if (x < 0 || x >= xmax) { + // flip-around date line + if (x < 0) + xx = xmax + x; + else + xx = x - xmax; + + if (xx < 0 || xx >= xmax) { + // Log.d(TAG, "tile out of bounds " + y + " " + xx); + continue; + } + } + + for (int i = 0; i < cnt; i++) + if (tiles[i].tileX == xx && tiles[i].tileY == y) { + tile = tiles[i]; + break; + } + + if (tile == null) { + tile = addTile(xx, y, mZoom, 0); + tiles[cnt++] = tile; + } + } + mCurrentTiles.cnt = cnt; + } + }; public MapRenderer(Context context, MapView mapView) { super(context); mMapView = mapView; + mMapViewPosition = mapView.getMapViewPosition(); Log.d(TAG, "init GLSurfaceLayer"); setEGLConfigChooser(new GlConfigChooser()); @@ -116,16 +169,9 @@ public class MapRenderer extends GLSurfaceView { if (mMapView == null) return; - MapPosition mapPosition = mMapView.getMapPosition().getMapPosition(); - - if (mapPosition == null) { - Log.d(TAG, "X no map position"); - return; - } - - if (clear) { + if (clear || mInitial) { // make sure onDrawFrame is not running - GLRenderer.lock.lock(); + GLRenderer.drawlock.lock(); // remove all tiles references Log.d(TAG, "CLEAR"); for (MapTile t : mTiles) @@ -134,74 +180,71 @@ public class MapRenderer extends GLSurfaceView { mTiles.clear(); mTilesLoaded.clear(); QuadTree.init(); - mInitial = true; - GLRenderer.lock.unlock(); - } - - if (mInitial) { // set up TileData arrays that are passed to gl-thread - int numTiles = (mWidth / (Tile.TILE_SIZE / 2) + 2) - * (mHeight / (Tile.TILE_SIZE / 2) + 2); + int num = mWidth; + if (mWidth < mHeight) + num = mHeight; + + int size = Tile.TILE_SIZE >> 1; + + int numTiles = (num * num) / (size * size) * 4; mRenderer.clearTiles(numTiles); mCurrentTiles = new TilesData(numTiles); - MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo(); - if (mapInfo != null) - mZoomLevels = mapInfo.zoomLevel; - } + // MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo(); + // if (mapInfo != null) + // mZoomLevels = mapInfo.zoomLevel; + GLRenderer.drawlock.unlock(); - byte zoomLevel = mapPosition.zoomLevel; - float scale = mapPosition.scale; - - long tileX = MercatorProjection.pixelXToTileX(mapPosition.x, zoomLevel) - * Tile.TILE_SIZE; - long tileY = MercatorProjection.pixelYToTileY(mapPosition.y, zoomLevel) - * Tile.TILE_SIZE; - - int zdir = 0; - if (mInitial || mPrevZoom != zoomLevel) { changedPos = true; - - } else if (tileX != mTileX || tileY != mTileY) { - if (mPrevScale - scale > 0 && scale > 1.2) - zdir = 1; - changedPos = true; - - } else if (mPrevScale - scale > 0.2 || mPrevScale - scale < -0.2) { - if (mPrevScale - scale > 0 && scale > 1.2) - zdir = 1; - changedPos = true; - } - - if (mInitial) { mInitial = false; } - mTileX = tileX; - mTileY = tileY; - mPrevZoom = zoomLevel; + MapPosition mapPosition = mMapPosition; + mMapViewPosition.getMapPosition(mapPosition, mTileCoords); - // GLRenderer.updatePosition(mapPosition); + float s = Tile.TILE_SIZE; + // load some additional tiles more than currently visible + float scale = mapPosition.scale * 0.75f; + double px = mapPosition.x; + double py = mapPosition.y; + float[] coords = mTileCoords; + int zdir = 0; - if (!MapView.debugFrameTime) - requestRender(); + for (int i = 0; i < 8; i += 2) { + coords[i + 0] = (float) ((px + coords[i + 0] / scale) / s); + coords[i + 1] = (float) ((py + coords[i + 1] / scale) / s); + } + + 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. if (changedPos) { - mPrevScale = scale; - updateVisibleList(mapPosition, zdir); if (!MapView.debugFrameTime) requestRender(); int remove = mTiles.size() - GLRenderer.CACHE_TILES; - if (remove > 50) + if (remove > CACHE_THRESHOLD) limitCache(mapPosition, remove); - } - limitLoadQueue(); + limitLoadQueue(); + } else { + if (!MapView.debugFrameTime) + requestRender(); + } } /** @@ -214,118 +257,15 @@ public class MapRenderer extends GLSurfaceView { * zoom direction */ private static void updateVisibleList(MapPosition mapPosition, int zdir) { - double x = mapPosition.x; - double y = mapPosition.y; - byte zoomLevel = mapPosition.zoomLevel; - float scale = mapPosition.scale; - - double add = 1.0f / scale; - int offsetX = (int) ((mWidth >> 1) * add) + Tile.TILE_SIZE; - int offsetY = (int) ((mHeight >> 1) * add) + Tile.TILE_SIZE; - - long pixelRight = (long) x + offsetX; - long pixelBottom = (long) y + offsetY; - long pixelLeft = (long) x - offsetX; - long pixelTop = (long) y - offsetY; - - int tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel); - int tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel); - int tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel); - int tileBottom = MercatorProjection.pixelYToTileY(pixelBottom, zoomLevel); mJobList.clear(); - // set non processed tiles to isLoading == false mMapView.addJobs(null); - - int tiles = 0; - int max = mCurrentTiles.tiles.length - 1; - - boolean fetchChildren = false; - boolean fetchParent = false; - boolean fetchProxy = false; - if (mZoomLevels != null) { - // check MapDatabase zoom-level-mapping - if (mZoomLevels[zoomLevel] == 0) { - mCurrentTiles.cnt = 0; - mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles); - return; - } - - if (mZoomLevels[zoomLevel] > zoomLevel) { - fetchChildren = true; - fetchProxy = true; - - } else if (mZoomLevels[zoomLevel] < zoomLevel) { - fetchParent = true; - fetchProxy = true; - } - } - - for (int yy = tileTop; yy <= tileBottom; yy++) { - for (int xx = tileLeft; xx <= tileRight; xx++) { - - if (tiles == max) - break; - - MapTile tile = QuadTree.getTile(xx, yy, zoomLevel); - - if (tile == null) { - tile = new MapTile(xx, yy, zoomLevel); - - QuadTree.add(tile); - mTiles.add(tile); - } - - if (!fetchProxy && !tile.isActive()) { - mJobList.add(tile); - } - - mCurrentTiles.tiles[tiles++] = tile; - - if (fetchChildren) { - byte z = (byte) (zoomLevel + 1); - for (int i = 0; i < 4; i++) { - int cx = (xx << 1) + (i % 2); - int cy = (yy << 1) + (i >> 1); - - MapTile c = QuadTree.getTile(cx, cy, z); - - if (c == null) { - c = new MapTile(cx, cy, z); - - QuadTree.add(c); - mTiles.add(c); - } - - if (!c.isActive()) { - mJobList.add(c); - } - } - } - - if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) { - // prefetch parent - MapTile p = tile.rel.parent.tile; - - if (p == null) { - p = new MapTile(xx >> 1, yy >> 1, (byte) (zoomLevel - 1)); - - QuadTree.add(p); - mTiles.add(p); - mJobList.add(p); - - } else if (!p.isActive()) { - if (!mJobList.contains(p)) - mJobList.add(p); - } - } - } - } - - // pass new tile list to glThread - mCurrentTiles.cnt = tiles; - mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles); + mCurrentTiles.cnt = 0; + mScanBox.scan(mTileCoords, mapPosition.zoomLevel); + // Log.d(TAG, "visible: " + mCurrentTiles.cnt + "/" + + // mCurrentTiles.tiles.length); + GLRenderer.updateTiles(mCurrentTiles); // note: this sets isLoading == true for all job tiles if (mJobList.size() > 0) { @@ -335,6 +275,67 @@ public class MapRenderer extends GLSurfaceView { } } + /* 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()) { + mJobList.add(tile); + } + + // mCurrentTiles.tiles[tiles++] = tile; + + // if (fetchChildren) { + // byte z = (byte) (zoomLevel + 1); + // for (int i = 0; i < 4; i++) { + // int cx = (xx << 1) + (i % 2); + // int cy = (yy << 1) + (i >> 1); + // + // MapTile c = QuadTree.getTile(cx, cy, z); + // + // if (c == null) { + // c = new MapTile(cx, cy, z); + // + // QuadTree.add(c); + // mTiles.add(c); + // } + // + // if (!c.isActive()) { + // mJobList.add(c); + // } + // } + // } + + // 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); + } + } + return tile; + } + private static void clearTile(MapTile t) { t.newData = false; @@ -407,8 +408,6 @@ public class MapRenderer extends GLSurfaceView { } private static void limitCache(MapPosition mapPosition, int remove) { - int removes = remove; - int size = mTiles.size(); // remove orphaned tiles @@ -418,44 +417,46 @@ public class MapRenderer extends GLSurfaceView { if (t.isLocked() || t.isActive()) { i++; } else { - // Log.d(TAG, "remove empty tile" + cur); + // Log.d(TAG, "remove empty tile" + t); clearTile(t); mTiles.remove(i); - removes--; + remove--; size--; } } - // Log.d(TAG, "remove tiles: " + removes + " " + size); - - if (removes <= 0) + if (remove <= 0) return; updateTileDistances(mTiles, mapPosition); Collections.sort(mTiles); - for (int i = 1; i <= removes; i++) { + for (int i = 1; i < remove; i++) { MapTile t = mTiles.remove(size - i); synchronized (t) { if (t.isLocked()) { // dont remove tile used by renderthread - Log.d(TAG, "X not removing " + t + " " + t.isLocked + " " - + t.distance); + Log.d(TAG, "X not removing " + t + // + " " + t.isLocked + + " " + t.distance); mTiles.add(t); - - // } else if (t.isLoading) { - // // FIXME if we add tile back on next limit cache - // // this will be removed. clearTile could interfere with - // // MapGenerator... clear in passTile(). - // Log.d(TAG, "X cancel loading " + t + " " + t.distance); - // t.isLoading = false; - // // mTiles.add(t); - } else { - clearTile(t); + continue; } + + if (t.isLoading) { + // NOTE: if we add tile back then on next limitCache + // the tile will be removed. clearTile could interfere with + // MapGenerator. so clear in passTile() instead. + // mTiles.add(t); + t.isLoading = false; + Log.d(TAG, "X cancel loading " + t + " " + t.distance); + continue; + } + + clearTile(t); } } } @@ -524,8 +525,8 @@ public class MapRenderer extends GLSurfaceView { MapTile tile = (MapTile) jobTile; if (!tile.isLoading) { - // no one should be able to use this tile now, mapgenerator passed - // it, glthread does nothing until newdata is set. + // 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); @@ -573,4 +574,78 @@ public class MapRenderer extends GLSurfaceView { super.onSizeChanged(w, h, oldw, oldh); } + // private static void updateVisibleList(MapPosition mapPosition, int zdir) + // { + // double x = mapPosition.x; + // double y = mapPosition.y; + // byte zoomLevel = mapPosition.zoomLevel; + // float scale = mapPosition.scale; + // + // double add = 1.0f / scale; + // int offsetX = (int) ((mWidth >> 1) * add) + Tile.TILE_SIZE; + // int offsetY = (int) ((mHeight >> 1) * add) + Tile.TILE_SIZE; + // + // long pixelRight = (long) x + offsetX; + // long pixelBottom = (long) y + offsetY; + // long pixelLeft = (long) x - offsetX; + // long pixelTop = (long) y - offsetY; + // + // int tileLeft = MercatorProjection.pixelXToTileX(pixelLeft, zoomLevel); + // int tileTop = MercatorProjection.pixelYToTileY(pixelTop, zoomLevel); + // int tileRight = MercatorProjection.pixelXToTileX(pixelRight, zoomLevel); + // int tileBottom = MercatorProjection.pixelYToTileY(pixelBottom, + // zoomLevel); + // + // mJobList.clear(); + // + // // set non processed tiles to isLoading == false + // mMapView.addJobs(null); + // + // int tiles = 0; + // int max = mCurrentTiles.tiles.length - 1; + // + // // boolean fetchChildren = false; + // // boolean fetchParent = false; + // // boolean fetchProxy = false; + // // if (mZoomLevels != null) { + // // // check MapDatabase zoom-level-mapping + // // if (mZoomLevels[zoomLevel] == 0) { + // // mCurrentTiles.cnt = 0; + // // mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles); + // // return; + // // } + // // + // // if (mZoomLevels[zoomLevel] > zoomLevel) { + // // fetchChildren = true; + // // fetchProxy = true; + // // + // // } else if (mZoomLevels[zoomLevel] < zoomLevel) { + // // fetchParent = true; + // // fetchProxy = true; + // // } + // // } + // + // for (int yy = tileTop; yy <= tileBottom; yy++) { + // for (int xx = tileLeft; xx <= tileRight; xx++) { + // + // if (tiles == max) + // break; + // + // // MapTile tile = + // addTile(xx, yy, zoomLevel, zdir); + // // mCurrentTiles.tiles[tiles++] = tile; + // } + // } + // + // // pass new tile list to glThread + // mCurrentTiles.cnt = tiles; + // mCurrentTiles = GLRenderer.updateTiles(mCurrentTiles); + // + // // note: this sets isLoading == true for all job tiles + // if (mJobList.size() > 0) { + // updateTileDistances(mJobList, mapPosition); + // Collections.sort(mJobList); + // mMapView.addJobs(mJobList); + // } + // } } diff --git a/src/org/oscim/view/renderer/MapTile.java b/src/org/oscim/view/renderer/MapTile.java index a8845808..202cfec0 100644 --- a/src/org/oscim/view/renderer/MapTile.java +++ b/src/org/oscim/view/renderer/MapTile.java @@ -41,7 +41,7 @@ class MapTile extends JobTile { /** * tile is used by render thread. set by updateVisibleList (main thread). */ - boolean isLocked; + // boolean isLocked; /** * tile has new data to upload to gl @@ -63,29 +63,38 @@ class MapTile extends JobTile { */ QuadTree rel; - byte lastDraw = 0; + 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 byte proxies; // counting the tiles that use this tile as proxy byte refs; + byte locked; + + // this tile sits in fo another tile. e.g. x:-1,y:0,z:1 for x:1,y:0 + MapTile holder; + boolean isActive() { return isLoading || newData || isReady; } boolean isLocked() { - return isLocked || refs > 0; + return locked > 0 || refs > 0; } void lock() { - isLocked = true; + if (holder != null) + return; - if (isReady || newData) + locked++; + + if (locked > 1 || isReady || newData) return; MapTile p = rel.parent.tile; @@ -113,9 +122,12 @@ class MapTile extends JobTile { } void unlock() { - isLocked = false; + if (holder != null) + return; - if (proxies == 0) + locked--; + + if (locked > 0 || proxies == 0) return; if ((proxies & (1 << 4)) != 0) { diff --git a/src/org/oscim/view/renderer/PolygonRenderer.java b/src/org/oscim/view/renderer/PolygonRenderer.java index a423e25e..f2bd343d 100644 --- a/src/org/oscim/view/renderer/PolygonRenderer.java +++ b/src/org/oscim/view/renderer/PolygonRenderer.java @@ -41,15 +41,18 @@ import java.nio.FloatBuffer; import java.nio.ShortBuffer; import org.oscim.utils.GlUtils; +import org.oscim.view.MapPosition; import android.opengl.GLES20; class PolygonRenderer { - private static final String TAG = "PolygonRenderer"; + // private static final String TAG = "PolygonRenderer"; private static final int NUM_VERTEX_SHORTS = 2; private static final int POLYGON_VERTICES_DATA_POS_OFFSET = 0; - private static int STENCIL_BITS = 8; + private static final int STENCIL_BITS = 8; + + private static final float FADE_START = 1.3f; private static PolygonLayer[] mFillPolys; @@ -76,7 +79,7 @@ class PolygonRenderer { return true; } - private static void fillPolygons(double zoom, float scale) { + private static void fillPolygons(int zoom, float scale) { boolean blend = false; /* draw to framebuffer */ @@ -96,7 +99,7 @@ class PolygonRenderer { if (l.area.fade >= zoom || l.area.color[3] != 1.0) { /* fade in/out || draw alpha color */ if (l.area.fade >= zoom) { - f = (scale > 1.3f ? scale : 1.3f) - f; + f = (scale > FADE_START ? scale : FADE_START) - f; if (f > 1.0f) f = 1.0f; } @@ -159,8 +162,11 @@ class PolygonRenderer { // stencil buffer index to start fill private static int mStart; - static PolygonLayer drawPolygons(final PolygonLayer layer, final int next, - final float[] matrix, final double zoom, final float scale, boolean first) { + static PolygonLayer drawPolygons(MapPosition pos, PolygonLayer layer, int next, + float[] matrix, boolean first) { + + int zoom = pos.zoomLevel; + float scale = pos.scale; glUseProgram(polygonProgram); GLES20.glEnableVertexAttribArray(hPolygonVertexPosition); diff --git a/src/org/oscim/view/renderer/ScanBox.java b/src/org/oscim/view/renderer/ScanBox.java index ba8c22da..75778091 100644 --- a/src/org/oscim/view/renderer/ScanBox.java +++ b/src/org/oscim/view/renderer/ScanBox.java @@ -17,23 +17,10 @@ package org.oscim.view.renderer; -import org.oscim.view.renderer.MapRenderer.TilesData; - import android.util.FloatMath; +import android.util.Log; -public class ScanBox { - - interface Callback { - void call(MapTile tile); - } - - class SetVisible implements Callback { - - @Override - public void call(MapTile tile) { - tile.isVisible = true; - } - } +public abstract class ScanBox { static class Edge { float x0, y0, x1, y1, dx, dy; @@ -44,84 +31,50 @@ public class ScanBox { this.y0 = y0; this.x1 = x1; this.y1 = y1; - this.dx = x1 - x0; - this.dy = y1 - y0; + dx = x1 - x0; + dy = y1 - y0; } else { this.x0 = x1; this.y0 = y1; this.x1 = x0; this.y1 = y0; - this.dx = x0 - x1; - this.dy = y0 - y1; + dx = x0 - x1; + dy = y0 - y1; } } } - static Edge ab = new Edge(); - static Edge bc = new Edge(); - static Edge ca = new Edge(); + private Edge ab = new Edge(); + private Edge bc = new Edge(); + private Edge ca = new Edge(); + protected byte mZoom; - static void scanSpans(Edge e0, Edge e1) { + void scan(float[] coords, byte zoom) { + mZoom = zoom; - // sort edge by x-coordinate - if (e0.x0 == e1.x0 && e0.y0 == e1.y0) { - if (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) { - Edge t = e0; - e0 = e1; - e1 = t; - } - } else { - if (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0) { - Edge t = e0; - e0 = e1; - e1 = t; - } - } + ab.set(coords[0], coords[1], coords[2], coords[3]); + bc.set(coords[2], coords[3], coords[4], coords[5]); + ca.set(coords[4], coords[5], coords[0], coords[1]); + scanTriangle(); - float m0 = e0.dx / e0.dy; - float m1 = e1.dx / e1.dy; - - int d0 = e0.dx > 0 ? 1 : 0;// use y + 1 to compute x0 - int d1 = e1.dx < 0 ? 1 : 0; // use y + 1 to compute x1 - - float x0, x1; - - int y = (int) Math.max(0, FloatMath.floor(e1.y0)); - int bottom = (int) Math.min(mMax, FloatMath.ceil(e1.y1)); - - for (; y < bottom; y++) { - // float x0 = (m0 * Math.min(e0.dy, y + d0 - e0.y0) + e0.x0); - // float x1 = (m1 * Math.min(e1.dy, y + d1 - e1.y0) + e1.x0); - - x0 = y + d0 - e0.y0; - if (e0.dy < x0) - x0 = e0.dy; - - x0 = e0.x0 + m0 * x0; - - if (x0 < 0) - x0 = 0; - else - x0 = FloatMath.ceil(x0); - - x1 = y + d1 - e1.y0; - if (e1.dy < x1) - x1 = e1.dy; - - x1 = e1.x0 + m1 * x1; - - if (x1 < 0) - x1 = 0; - else - x1 = FloatMath.floor(x1); - - setVisible(y, (int) x1, (int) x0); - - // setVisible(y, (int) (x1 - 0.5f), (int) (x0 + 0.5f)); - } + ab.set(coords[4], coords[5], coords[6], coords[7]); + bc.set(coords[6], coords[7], coords[0], coords[1]); + ca.set(coords[0], coords[1], coords[4], coords[5]); + scanTriangle(); } - static void scanTriangle() { + /** + * @param y + * ... + * @param x1 + * ... + * @param x2 + * ... + */ + void setVisible(int y, int x1, int x2) { + } + + private void scanTriangle() { if (ab.dy > bc.dy) { Edge t = ab; @@ -138,6 +91,10 @@ public class ScanBox { bc = ca; ca = t; } + // ca.dy > bc.dy > ab.dy + + if (ca.dy == 0) + return; if (ab.dy != 0) scanSpans(ca, ab); @@ -146,42 +103,68 @@ public class ScanBox { scanSpans(ca, bc); } - private static int mMax; + private static final int MAX_SLOPE = 4; - public static void scan(float[] coords, TilesData tiles, int max) { - sTiles = tiles; - cntDoubles = 0; - mMax = max; + private void scanSpans(Edge e0, Edge e1) { - ab.set(coords[0], coords[1], coords[2], coords[3]); - bc.set(coords[2], coords[3], coords[4], coords[5]); - ca.set(coords[4], coords[5], coords[0], coords[1]); - scanTriangle(); + int y0 = (int) Math.max(0, FloatMath.floor(e1.y0)); + int y1 = (int) Math.min((1 << mZoom), FloatMath.ceil(e1.y1)); - ab.set(coords[4], coords[5], coords[6], coords[7]); - bc.set(coords[6], coords[7], coords[0], coords[1]); - ca.set(coords[0], coords[1], coords[4], coords[5]); - scanTriangle(); - - // Log.d("..", "= x1 && tiles[i].tileX < x2) { - // if (tiles[i].isVisible) { - // Log.d("..", ">>>" + y + " " + tiles[i].tileX); - // cntDoubles++; - // } - tiles[i].isVisible = true; - } + // sort edge by x-coordinate + if (e0.x0 == e1.x0 && e0.y0 == e1.y0) { + // bottom-flat + if (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) { + Edge t = e0; + e0 = e1; + e1 = t; } + } else { + // top-flat + if (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0) { + Edge t = e0; + e0 = e1; + e1 = t; + } + } + + float m0 = e0.dx / e0.dy; + float m1 = e1.dx / e1.dy; + + // still needed? + if (m0 > MAX_SLOPE) + m0 = MAX_SLOPE; + else if (m0 < -MAX_SLOPE) + m0 = -MAX_SLOPE; + + if (m1 > MAX_SLOPE) + m1 = MAX_SLOPE; + else if (m1 < -MAX_SLOPE) + m1 = -MAX_SLOPE; + + int d0 = e0.dx > 0 ? 1 : 0; // use y + 1 to compute x0 + int d1 = e1.dx < 0 ? 1 : 0; // use y + 1 to compute x1 + + float x0, x1, dy; + + for (int y = y0; y < y1; y++) { + dy = y + d0 - e0.y0; + if (e0.dy < dy) + dy = e0.dy; + + x0 = e0.x0 + m0 * dy; + x0 = FloatMath.ceil(x0); + + dy = y + d1 - e1.y0; + if (e1.dy < dy) + dy = e1.dy; + + x1 = e1.x0 + m1 * dy; + x1 = FloatMath.floor(x1); + + if (x1 > x0) + Log.d("...", "X set visible" + y + " " + x1 + "/" + x0); + + setVisible(y, (int) x1, (int) x0); } } } diff --git a/src/org/oscim/view/renderer/Shaders.java b/src/org/oscim/view/renderer/Shaders.java index 398dd787..d0204044 100644 --- a/src/org/oscim/view/renderer/Shaders.java +++ b/src/org/oscim/view/renderer/Shaders.java @@ -19,7 +19,6 @@ class Shaders { final static String lineVertexShader = "" + "precision mediump float;" - // + "invariant gl_Position;" + "uniform mat4 u_mvp;" + "uniform float u_width;" + "attribute vec2 a_position;" @@ -111,20 +110,20 @@ class Shaders { + "precision highp float; " + "attribute vec4 vertex;" + "attribute vec2 tex_coord;" - + "uniform mat4 mvp;" - + "uniform mat4 rotation;" - + "uniform float scale;" + + "uniform mat4 u_mv;" + + "uniform mat4 u_proj;" + + "uniform float u_scale;" + "varying vec2 tex_c;" + "const vec2 div = vec2(1.0/4096.0,1.0/2048.0);" + "const float coord_scale = 0.125;" + "void main() {" + " vec4 pos;" + " if (mod(vertex.x, 2.0) == 0.0){" - + " pos = rotation * (mvp * vec4(vertex.xy + vertex.zw * (1.0 / scale), 0.0, 1.0));" + + " pos = u_proj * (u_mv * vec4(vertex.xy + vertex.zw * u_scale, 0.0, 1.0));" + " } else {" // place as billboard - + " vec4 dir = mvp * vec4(vertex.xy, 0.0, 1.0);" - + " pos = rotation * (dir + vec4(vertex.zw * coord_scale, 0.0, 0.0));" + + " vec4 dir = u_mv * vec4(vertex.xy, 0.0, 1.0);" + + " pos = u_proj * (dir + vec4(vertex.zw * coord_scale, 0.0, 0.0));" + " }" + " gl_Position = pos;" + " tex_c = tex_coord * div;" @@ -175,7 +174,6 @@ class Shaders { final static String textFragmentShader = "" + "precision highp float;" + "uniform sampler2D tex;" - + "uniform vec4 col;" + "varying vec2 tex_c;" + "void main() {" + " gl_FragColor = texture2D(tex, tex_c.xy);" diff --git a/src/org/oscim/view/renderer/TextRenderer.java b/src/org/oscim/view/renderer/TextRenderer.java index eba3e990..4b25eb39 100644 --- a/src/org/oscim/view/renderer/TextRenderer.java +++ b/src/org/oscim/view/renderer/TextRenderer.java @@ -54,8 +54,8 @@ public class TextRenderer { private static int mVerticesVBO; private static int mTextProgram; - private static int hTextUVPMatrix; - private static int hTextRotationMatrix; + private static int hTextMVMatrix; + private static int hTextProjectionMatrix; private static int hTextVertex; private static int hTextScale; private static int hTextTextureCoord; @@ -83,11 +83,10 @@ public class TextRenderer { mTextProgram = GlUtils.createProgram(Shaders.textVertexShader, Shaders.textFragmentShader); - hTextUVPMatrix = GLES20.glGetUniformLocation(mTextProgram, "mvp"); - hTextRotationMatrix = GLES20.glGetUniformLocation(mTextProgram, "rotation"); - + hTextMVMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_mv"); + hTextProjectionMatrix = GLES20.glGetUniformLocation(mTextProgram, "u_proj"); + hTextScale = GLES20.glGetUniformLocation(mTextProgram, "u_scale"); hTextVertex = GLES20.glGetAttribLocation(mTextProgram, "vertex"); - hTextScale = GLES20.glGetUniformLocation(mTextProgram, "scale"); hTextTextureCoord = GLES20.glGetAttribLocation(mTextProgram, "tex_coord"); } @@ -150,12 +149,6 @@ public class TextRenderer { indices[i + 3] = (short) (j + 2); indices[i + 4] = (short) (j + 3); indices[i + 5] = (short) (j + 0); - // indices[i + 0] = (short) (j + 0); - // indices[i + 1] = (short) (j + 0); - // indices[i + 2] = (short) (j + 1); - // indices[i + 3] = (short) (j + 3); - // indices[i + 4] = (short) (j + 2); - // indices[i + 5] = (short) (j + 2); } mShortBuffer.clear(); @@ -192,7 +185,7 @@ public class TextRenderer { if (tex.tile == null) break; - if (!tex.tile.isLocked) + if (!tex.tile.isLocked()) break; tex = null; @@ -293,11 +286,10 @@ public class TextRenderer { if (t.caption != null) { x1 = x3 = (short) (SCALE * (-hw)); - y1 = y3 = (short) (SCALE * (-hh)); + y1 = y3 = (short) (SCALE * (hh)); x2 = x4 = (short) (SCALE * (hw)); - y2 = y4 = (short) (SCALE * (hh)); - } - else { + 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); @@ -322,14 +314,14 @@ public class TextRenderer { // 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)); + 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); @@ -394,7 +386,8 @@ public class TextRenderer { GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, mBitmap, mBitmapFormat, mBitmapType); - // FIXME shouldnt be needed here, still looking for sometimes corrupted labels.. + // FIXME shouldnt be needed here, still looking for sometimes corrupted + // labels.. GLES20.glFlush(); return true; @@ -412,7 +405,7 @@ public class TextRenderer { for (int i = 0; i < mTextures.length; i++) { tex = mTextures[i]; - if (tex.tile == null || !tex.tile.isLocked) + if (tex.tile == null) // || !tex.tile.isLocked) continue; mShortBuffer.put(tex.vertices, 0, tex.length); @@ -426,14 +419,14 @@ public class TextRenderer { mShortBuffer); } - static void beginDraw(float scale, float[] rotation) { + static void beginDraw(float scale, float[] projection) { GLES20.glUseProgram(mTextProgram); GLES20.glEnableVertexAttribArray(hTextTextureCoord); GLES20.glEnableVertexAttribArray(hTextVertex); GLES20.glUniform1f(hTextScale, scale); - GLES20.glUniformMatrix4fv(hTextRotationMatrix, 1, false, rotation, 0); + GLES20.glUniformMatrix4fv(hTextProjectionMatrix, 1, false, projection, 0); if (debug) { GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); @@ -461,7 +454,7 @@ public class TextRenderer { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, tile.texture.id); - GLES20.glUniformMatrix4fv(hTextUVPMatrix, 1, false, matrix, 0); + GLES20.glUniformMatrix4fv(hTextMVMatrix, 1, false, matrix, 0); if (debug) { GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); diff --git a/src/org/oscim/view/renderer/WayDecorator.java b/src/org/oscim/view/renderer/WayDecorator.java index 9d26f813..746471b7 100644 --- a/src/org/oscim/view/renderer/WayDecorator.java +++ b/src/org/oscim/view/renderer/WayDecorator.java @@ -136,8 +136,8 @@ final class WayDecorator { } else if ((currentY - nextY) == 0) break; - float diff = ((diffX) / (diffY) - (float) (currentX - nextX) - / (currentY - nextY)); + float diff = diffX / diffY - + (float) (currentX - nextX) / (currentY - nextY); // skip segments with corners if (diff >= 0.1f || diff <= -0.1f) @@ -221,8 +221,8 @@ final class WayDecorator { // check overlapping labels of road with more than one // way - short top2 = (t2.y1 < t2.y2 ? t2.y1 : t2.y2); - short bot2 = (t2.y1 < t2.y2 ? t2.y2 : t2.y1); + short top2 = t2.y1 < t2.y2 ? t2.y1 : t2.y2; + short bot2 = t2.y1 < t2.y2 ? t2.y2 : t2.y1; if (x1 - 10 < t2.x2 && t2.x1 - 10 < x2 && top - 10 < bot2 && top2 - 10 < bot) { @@ -240,7 +240,8 @@ final class WayDecorator { continue; } - // Log.d("mapsforge", "add " + text + " " + first + " " + last); + // Log.d("mapsforge", "add " + text + " " + first + " " + + // last); if (previousX < currentX) { x1 = previousX;