/// <summary> /// Render the current complete scene. /// </summary> void Render() { try { //if (Monitor.TryEnter(_cacheRenderer, 10)) lock(_cacheRenderer) { try { // use object var rect = _rect; // create the view. var size = (float)System.Math.Max(_rect.Width, _rect.Height); var view = _cacheRenderer.Create((int)(size * _extra), (int)(size * _extra), this.Map, (float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, _invertX, _invertY, this.MapTilt); if (rect.Width == 0) { // only render if a proper size is known. return; } // calculate width/height. var imageWidth = (int)(size * _extra * _scaleFactor); var imageHeight = (int)(size * _extra * _scaleFactor); // create a new bitmap context. var space = CGColorSpace.CreateDeviceRGB(); int bytesPerPixel = 4; int bytesPerRow = bytesPerPixel * imageWidth; int bitsPerComponent = 8; // get old image if available. var image = new CGBitmapContext(null, imageWidth, imageHeight, bitsPerComponent, bytesPerRow, space, // kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Big); long before = DateTime.Now.Ticks; // build the layers list. var layers = new List<Layer>(); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { layers.Add(this.Map[layerIdx]); } // add the internal layer. try { image.SetFillColor(1, 1, 1, 1); image.FillRect(new CGRect( 0, 0, imageWidth, imageHeight)); // notify the map that the view has changed. var normalView = _cacheRenderer.Create((float)_rect.Width, (float)_rect.Height, this.Map, (float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, _invertX, _invertY, this.MapTilt); this.Map.ViewChanged((float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, normalView, view); long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.iOS.UI.MapView", TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); float zoomFactor = this.MapZoom; float sceneZoomFactor = (float)this.Map.Projection.ToZoomFactor(this.MapZoom); // does the rendering. bool complete = _cacheRenderer.Render(new CGContextWrapper(image, new CGRect(0, 0, (int)(size * _extra), (int)(size * _extra))), _map.Projection, layers, view, sceneZoomFactor); long afterRendering = DateTime.Now.Ticks; if (complete) { // there was no cancellation, the rendering completely finished. lock (_bufferSynchronisation) { if (_onScreenBuffer != null && _onScreenBuffer.NativeImage != null) { // on screen buffer. _onScreenBuffer.NativeImage.Dispose(); } // add the newly rendered image again. _onScreenBuffer = new ImageTilted2D(view.Rectangle, new NativeImage(image.ToImage()), float.MinValue, float.MaxValue); // store the previous view. _previouslyRenderedView = view; } // make sure this view knows that there is a new rendering. this.InvokeOnMainThread(SetNeedsDisplay); } long after = DateTime.Now.Ticks; if(complete) { // notify invalidation listener about a succesfull rendering. OsmSharp.Logging.Log.TraceEvent("OsmSharp.iOS.UI.MapView", TraceEventType.Information, "Rendering succesfull after {0}ms.", new TimeSpan(after - before).TotalMilliseconds); _listener.NotifyRenderSuccess(view, zoomFactor, (int)new TimeSpan(after - before).TotalMilliseconds); } else { // rendering incomplete. OsmSharp.Logging.Log.TraceEvent("OsmSharp.iOS.UI.MapView", TraceEventType.Information, "Rendering cancelled.", new TimeSpan(after - before).TotalMilliseconds); } } finally { } } finally { // make sure the object lock is release. Monitor.Exit(_cacheRenderer); } } } catch (Exception) { _cacheRenderer.Reset(); } }
/// <summary> /// Renders the current complete scene. /// </summary> private void Render() { try { if (_renderingSuspended) { // no rendering when rendering is suspended. return; } if (_cacheRenderer.IsRunning) { // cancel previous render. _cacheRenderer.CancelAndWait(); } // make sure only on thread at the same time is using the renderer. lock (_cacheRenderer) { this.Map.ViewChangedCancel(); // build the layers list. var layers = new List<Layer>(); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { // get the layer. if (this.Map[layerIdx].IsVisible) { layers.Add(this.Map[layerIdx]); } } // add the internal layers. layers.Add(_makerLayer); if (this.SurfaceHeight == 0) { // the surface has no height yet. Impossible to render like this. return; } // get old image if available. NativeImage image = null; if (_offScreenBuffer != null) { // get the native image from the off-screen buffer. image = _offScreenBuffer.NativeImage as NativeImage; } // resize image if needed. float sizeX = this.SurfaceWidth; float sizeY = this.SurfaceHeight; //if(this.MapAllowTilt) //{ // when rotation is allowed make sure a square is rendered. // sizeX = System.Math.Max(this.SurfaceWidth, this.SurfaceHeight); // sizeY = System.Math.Max(this.SurfaceWidth, this.SurfaceHeight); //} // float size = System.Math.Max(this.SurfaceHeight, this.SurfaceWidth); if (image == null || image.Image.Width != (int)(sizeX * _extra) || image.Image.Height != (int)(sizeY * _extra)) { // create a bitmap and render there. if (image != null) { // make sure to dispose the old image. image.Dispose(); } image = new NativeImage(global::Android.Graphics.Bitmap.CreateBitmap((int)(sizeX * _extra), (int)(sizeY * _extra), global::Android.Graphics.Bitmap.Config.Argb8888)); } // create and reset the canvas. using (var canvas = new global::Android.Graphics.Canvas(image.Image)) { canvas.DrawColor(new global::Android.Graphics.Color( SimpleColor.FromKnownColor(KnownColor.White).Value)); // create the view. double[] sceneCenter = this.Map.Projection.ToPixel(this.MapCenter.Latitude, this.MapCenter.Longitude); float mapZoom = this.MapZoom; float sceneZoomFactor = (float)this.Map.Projection.ToZoomFactor(this.MapZoom); // create the view for this control. float scaledNormalWidth = image.Image.Width / _bufferFactor; float scaledNormalHeight = image.Image.Height / _bufferFactor; var view = View2D.CreateFrom((float)sceneCenter[0], (float)sceneCenter[1], scaledNormalWidth * _extra, scaledNormalHeight * _extra, sceneZoomFactor, _invertX, _invertY, this.MapTilt); long before = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering Start"); // notify the map that the view has changed. if (_previouslyChangedView == null || !_previouslyChangedView.Equals(view)) { // report change once! var normalView = View2D.CreateFrom((float)sceneCenter[0], (float)sceneCenter[1], scaledNormalWidth, scaledNormalHeight, sceneZoomFactor, _invertX, _invertY, this.MapTilt); this.Map.ViewChanged((float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, normalView, view); _previouslyChangedView = view; long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); } // does the rendering. _cacheRenderer.Density = this.MapScaleFactor; bool complete = _cacheRenderer.Render(canvas, _map.Projection, layers, view, (float)this.Map.Projection.ToZoomFactor(this.MapZoom)); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering took: {0}ms @ zoom level {1} and {2}", (new TimeSpan(afterRendering - before).TotalMilliseconds), this.MapZoom, this.MapCenter); if (complete) { // there was no cancellation, the rendering completely finished. // add the result to the scene cache. // add the newly rendered image again. if (_offScreenBuffer == null) { // create the offscreen buffer first. _offScreenBuffer = new ImageTilted2D(view.Rectangle, image, float.MinValue, float.MaxValue); } else { // augment the previous buffer. _offScreenBuffer.Bounds = view.Rectangle; _offScreenBuffer.NativeImage = image; } var temp = _onScreenBuffer; _onScreenBuffer = _offScreenBuffer; _offScreenBuffer = temp; } long after = DateTime.Now.Ticks; if (complete) { // report a successful render to listener. _listener.NotifyRenderSuccess(view, mapZoom, (int)new TimeSpan(after - before).TotalMilliseconds); } } } // notify the the current surface of the new rendering. this.PostInvalidate(); } catch (Exception ex) { // exceptions can be thrown when the mapview is disposed while rendering. // don't worry too much about these, the mapview is garbage anyway. OsmSharp.Logging.Log.TraceEvent("MapViewSurface", TraceEventType.Critical, string.Format("An unhandled exception occured:{0}", ex.ToString())); } }
/// <summary> /// Render the current complete scene. /// </summary> void Render() { try { lock (_renderSynchronisation) { RectangleF rect = _rect; // create the view. View2D view = _cacheRenderer.Create((int)(rect.Width * _extra), (int)(rect.Height * _extra), this.Map, (float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, _invertX, _invertY, this.MapTilt); if (rect.Width == 0) { // only render if a proper size is known. return; } // calculate width/height. int imageWidth = (int)(rect.Width * _extra * _scaleFactor); int imageHeight = (int)(rect.Height * _extra * _scaleFactor); // create a new bitmap context. CGColorSpace space = CGColorSpace.CreateDeviceRGB(); int bytesPerPixel = 4; int bytesPerRow = bytesPerPixel * imageWidth; int bitsPerComponent = 8; // get old image if available. CGBitmapContext image = new CGBitmapContext(null, imageWidth, imageHeight, bitsPerComponent, bytesPerRow, space, // kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Big); long before = DateTime.Now.Ticks; // build the layers list. var layers = new List<Layer>(); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { layers.Add(this.Map[layerIdx]); } // add the internal layer. try { image.SetRGBFillColor(1, 1, 1, 1); image.FillRect(new RectangleF( 0, 0, imageWidth, imageHeight)); // notify the map that the view has changed. this.Map.ViewChanged((float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, view); long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); float sceneZoomFactor = (float)this.Map.Projection.ToZoomFactor(this.MapZoom); // does the rendering. bool complete = _cacheRenderer.Render(new CGContextWrapper(image, new RectangleF(0, 0, (int)(rect.Width * _extra), (int)(rect.Height * _extra))), layers, view, sceneZoomFactor); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering took: {0}ms @ zoom level {1}", (new TimeSpan(afterRendering - afterViewChanged).TotalMilliseconds), this.MapZoom); if (complete) { // there was no cancellation, the rendering completely finished. lock (_bufferSynchronisation) { if (_onScreenBuffer != null && _onScreenBuffer.Tag != null) { // on screen buffer. (_onScreenBuffer.Tag as CGImage).Dispose(); } // add the newly rendered image again. _onScreenBuffer = new ImageTilted2D(view.Rectangle, new byte[0], float.MinValue, float.MaxValue); _onScreenBuffer.Tag = image.ToImage(); // store the previous view. _previousRenderedZoom = view; } this.InvokeOnMainThread(InvalidateMap); } long after = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.iOS.UI.MapView", TraceEventType.Information, "Rendering in {0}ms", new TimeSpan(after - before).TotalMilliseconds); } finally { } } } catch (Exception) { _cacheRenderer.Reset(); } }
/// <summary> /// Diposes of all resources associated with this object. /// </summary> /// <param name="disposing"></param> protected override void Dispose(bool disposing) { if (disposing == true) { //someone wants the deterministic release of all resources //Let us release all the managed resources } else { // Do nothing, no one asked a dispose, the object went out of // scope and finalized is called so lets next round of GC // release these resources } // Release the unmanaged resource in any case as they will not be // released by GC this._cacheRenderer = null; if (this._offScreenBuffer != null) { // dispose of the map view surface. this._offScreenBuffer.Dispose(); this._offScreenBuffer = null; } if (this._onScreenBuffer != null) { // dispose of the map view surface. this._onScreenBuffer.Dispose(); this._onScreenBuffer = null; } if (this._mapViewAnimator != null) { _mapViewAnimator.Stop(); _mapViewAnimator = null; } if (this._map != null) { this._map = null; } }
/// <summary> /// Renders the current complete scene. /// </summary> private void Render() { if (_cacheRenderer.IsRunning) { _cacheRenderer.CancelAndWait (); } // make sure only on thread at the same time is using the renderer. lock (_cacheRenderer) { this.Map.ViewChangedCancel(); // build the layers list. var layers = new List<Layer> (); for (int layerIdx = 0; layerIdx < this.Map.LayerCount; layerIdx++) { // get the layer. layers.Add (this.Map[layerIdx]); } // add the internal layers. layers.Add (_makerLayer); // get old image if available. global::Android.Graphics.Bitmap image = null; if (_offScreenBuffer != null) { image = _offScreenBuffer.Tag as global::Android.Graphics.Bitmap; } if (this.SurfaceHeight == 0) { return; } // resize image if needed. float size = System.Math.Max(this.SurfaceHeight, this.SurfaceWidth); if (image == null || image.Width != (int)(size * _extra) || image.Height != (int)(size * _extra)) { // create a bitmap and render there. image = global::Android.Graphics.Bitmap.CreateBitmap((int)(size * _extra), (int)(size * _extra), global::Android.Graphics.Bitmap.Config.Argb8888); } // create and reset the canvas. global::Android.Graphics.Canvas canvas = new global::Android.Graphics.Canvas (image); canvas.DrawColor (new global::Android.Graphics.Color( SimpleColor.FromKnownColor(KnownColor.White).Value)); // create the view. double[] sceneCenter = this.Map.Projection.ToPixel(this.MapCenter.Latitude, this.MapCenter.Longitude); float mapZoom = this.MapZoom; float sceneZoomFactor = (float)this.Map.Projection.ToZoomFactor(this.MapZoom); // create the view for this control. View2D view = View2D.CreateFrom((float)sceneCenter[0], (float)sceneCenter[1], size * _extra, size * _extra, sceneZoomFactor, _invertX, _invertY, this.MapTilt); long before = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering Start"); // notify the map that the view has changed. this.Map.ViewChanged ((float)this.Map.Projection.ToZoomFactor(this.MapZoom), this.MapCenter, view); long afterViewChanged = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "View change took: {0}ms @ zoom level {1}", (new TimeSpan(afterViewChanged - before).TotalMilliseconds), this.MapZoom); // does the rendering. bool complete = _cacheRenderer.Render(canvas, layers, view, (float)this.Map.Projection.ToZoomFactor(this.MapZoom)); long afterRendering = DateTime.Now.Ticks; OsmSharp.Logging.Log.TraceEvent("OsmSharp.Android.UI.MapView", TraceEventType.Information, "Rendering took: {0}ms @ zoom level {1}", (new TimeSpan(afterRendering - afterViewChanged).TotalMilliseconds), this.MapZoom); if(complete) { // there was no cancellation, the rendering completely finished. // add the result to the scene cache. // add the newly rendered image again. _offScreenBuffer = new ImageTilted2D(view.Rectangle, new byte[0], float.MinValue, float.MaxValue); _offScreenBuffer.Tag = image; var temp = _onScreenBuffer; _onScreenBuffer = _offScreenBuffer; _offScreenBuffer = temp; } // notify the the current surface of the new rendering. this.PostInvalidate(); long after = DateTime.Now.Ticks; if (complete) { // report a successfull render to listener. _listener.NotifyRenderSuccess(view, mapZoom, (int)new TimeSpan(after - before).TotalMilliseconds); } } }