예제 #1
0
        private void AddBitmapRequestToList(BasicRectangle boundsBitmap, BasicRectangle boundsWaveForm, float zoom, WaveFormDisplayType displayType)
        {
            // Make sure we don't slow down GetTile() by creating a task and running LINQ queries on another thread
            Task.Factory.StartNew(() =>
                {
                    var request = new WaveFormBitmapRequest()
                    {
                        DisplayType = displayType,
                        BoundsBitmap = boundsBitmap,
                        BoundsWaveForm = boundsWaveForm,
                        Zoom = zoom
                    };

                    // Check if a tile already exists
                    WaveFormTile existingTile = null;
                    lock (_lockerTiles)
                    {
                        existingTile = _tiles.FirstOrDefault(obj => obj.ContentOffset.X == boundsBitmap.X && obj.Zoom == zoom);
                    }

                    lock (_lockerRequests)
                    {
                        // Check if bitmap has already been requested in queue
                        var existingRequest = _requests.FirstOrDefault(obj =>
                            obj.BoundsBitmap.Equals(request.BoundsBitmap) &&
                            obj.BoundsWaveForm.Equals(request.BoundsWaveForm) &&
                            obj.Zoom == request.Zoom);

                        // Request a new bitmap only if necessary
                        if (existingRequest == null && existingTile == null)
                        {
                            //Console.WriteLine("WaveFormCacheService - Adding bitmap request to queue - zoom: {0} boundsBitmap: {1} boundsWaveForm: {2}", zoom, boundsBitmap, boundsWaveForm);
                            _requests.Add(request);

                            // Remove the oldest request from the list if we hit the maximum 
                            if(_requests.Count > MaxNumberOfRequests)
                                _requests.RemoveAt(0);

                            //Console.WriteLine("............. PULSING");
                            Monitor.Pulse(_lockerRequests);                        
                        }
                    }
                });
        }
예제 #2
0
        public List<WaveFormTile> GetTiles(WaveFormBitmapRequest request)
        {
            if (request.Zoom != _lastZoom && request.Zoom > 1)
            {
                lock (_lockerCache)
                {
                    // Bug: when requesting the smaller tiles, the zoom changes
                    //Console.WriteLine("WaveFormCacheService - Zoom has changed - lastZoom: {0} zoom: {1}", _lastZoom, request.Zoom);
                    _lastZoom = request.Zoom;
                    _tileCacheForZoom.Clear();
                }
            }

            //float coveredAreaX = 0;
            float zoomThreshold = (float)Math.Floor(request.Zoom);
            var boundsWaveFormAdjusted = new BasicRectangle(0, 0, request.BoundsWaveForm.Width * zoomThreshold, request.BoundsWaveForm.Height);
            var tiles = new List<WaveFormTile>();
            //List<WaveFormTile> previouslyAvailableTiles = new List<WaveFormTile>();
            for (int a = request.StartTile; a < request.EndTile; a++)
            {
                WaveFormTile tile = null;
                float tileX = a * request.TileSize;

                // Add lock for cache
                WaveFormTile cachedTile = null;
                lock (_lockerCache)
                {
                    if(request.IsScrollBar)
                        cachedTile = _tileCacheForScrollBar.FirstOrDefault(x => x.ContentOffset.X == tileX);
                    else
                        cachedTile = _tileCacheForZoom.FirstOrDefault(x => x.ContentOffset.X == tileX);
                }
                if (cachedTile != null)
                {
                    //Console.WriteLine(">>>>>>>>>> Taking cached tile!");
                    tile = cachedTile;
                } 
                else
                {
                    // This is a hot line, and needs to be avoided as much as possible.
                    // the problem is that tiles vary in time in quality. 
                    // maybe as a first, when a tile at the right zoom is available, cache it locally so it isn't necessary to call the algo again.
                    var availableTiles = GetAvailableTilesForPosition(tileX, request.Zoom);
                    var boundsBitmap = new BasicRectangle(tileX, 0, TileSize, request.BoundsWaveForm.Height);
                    if (availableTiles != null && availableTiles.Count > 0)
                    {
                        // TEMP: Add every tile for zoom == 100% (TESTING) -- This fixes the empty areas and proves the coveredAreaX technique doesn't work.
                        var tileLowRes = availableTiles.FirstOrDefault(x => x.Zoom == 1);
                        if (tileLowRes != null && !tiles.Contains(tileLowRes))
                            tiles.Add(tileLowRes);

                        // Get the tile with the zoom that is the closest to the current zoom threshold 
                        tile = GetOptimalTileAtZoom(availableTiles, zoomThreshold);

                        // If we could not find a tile at this zoom level, we need to generate one 
                        if (tile.Zoom != zoomThreshold)
                        {
                            //Console.WriteLine("WaveFormCacheService - Requesting a new bitmap (zoom doesn't match) - zoomThreshold: {0} tile.Zoom: {1} boundsBitmap: {2} boundsWaveForm: {3}", zoomThreshold, tile.Zoom, boundsBitmap, request.BoundsWaveForm);
                            AddBitmapRequestToList(boundsBitmap, boundsWaveFormAdjusted, zoomThreshold, request.DisplayType);
                        } 
                        else
                        {
                            lock (_lockerCache)
                            {
                                _tileCacheForZoom.Add(tile);
                            }
                        }
                    } 
                    else
                    {
                        // We need to request a new bitmap at this zoom threshold because there are no bitmaps available (usually zoom @ 100%)
                        //Console.WriteLine("WaveFormCacheService - Requesting a new bitmap - zoom: {0} zoomThreshold: {1} boundsWaveForm: {2}", zoomThreshold, boundsBitmap, request.BoundsWaveForm);
                        AddBitmapRequestToList(boundsBitmap, boundsWaveFormAdjusted, zoomThreshold, request.DisplayType);
                    }
                }

                //Console.WriteLine("WaveFormCacheService - GetTiles - tile {0} x: {1} Zoom: {2} // tileFound: {3} tile.X: {4} tile.Zoom: {5}", a, tileX, zoom, tile == null, tile != null ? tile.ContentOffset.X : -1, tile != null ? tile.Zoom : -1);
                if (tile != null)
                {
                    // Calculate the new covered area (adjusted with the zoom delta)
                    //float currentTileDeltaZoom = request.Zoom/tile.Zoom;
                    //float currentTileX = tile.ContentOffset.X*currentTileDeltaZoom;
                    //float currentTileWidth = request.TileSize*currentTileDeltaZoom;

                    //// Check if the new tile leaves an empty area behind
                    //if (coveredAreaX < tile.ContentOffset.X)
                    //{
                    //    //Console.WriteLine("[...] WaveFormCacheService - GetTiles - An empty area has been found - coveredAreaX: {0} tile.ContentOffset.X: {1}", coveredAreaX, currentTileX);
                    //    //var tilesToFillEmptyArea = GetAvailableTilesToFillBounds(tileX, zoom, new BasicRectangle(coveredAreaX, 0, tile.ContentOffset.X - coveredAreaX, boundsWaveForm.Height));
                    //    var tilesToFillEmptyArea = GetAvailableTilesToFillBounds(zoom, new BasicRectangle(coveredAreaX, 0, tile.ContentOffset.X - coveredAreaX, boundsWaveForm.Height));
                    //    //Console.WriteLine("[...] $$$$$$$$$$$$ WaveFormCacheService - GetTiles - tilesToFillEmptyArea.Count: {0}", tilesToFillEmptyArea.Count);
                    //    //foreach (var daTile in tilesToFillEmptyArea)
                    //    //    Console.WriteLine("        .....>>>>>> tile.ContentOffset.X: {0} tile.Zoom: {1}", daTile.ContentOffset.X, daTile.Zoom);    

                    //    // Go through previously available tiles to find a tile to cover the empty area.
                    //    // This should return at least one tile (there should always be one around at zoom=100% except for the initial loading)
                    //    WaveFormTile tileToCoverEmptyArea = null;
                    //    //foreach (var previouslyAvailableTile in previouslyAvailableTiles)
                    //    //{
                    //    //    float previousTileDeltaZoom = zoom/previouslyAvailableTile.Zoom;
                    //    //    float previousTileX = previouslyAvailableTile.ContentOffset.X * previousTileDeltaZoom;
                    //    //    float previousTileWidth = tileSize * previousTileDeltaZoom;
                    //    //    if (previousTileX < coveredAreaX &&
                    //    //        previousTileX + previousTileWidth >= currentTileX)
                    //    //    {
                    //    //        tileToCoverEmptyArea = previouslyAvailableTile;
                    //    //        Console.WriteLine("[...] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! WaveFormCacheService - GetTiles - ==> A tile has been found to cover the empty area - tileToCover.ContentOffset.X: {0} tileWidth: {1} tile.Zoom: {2}", previousTileX, previousTileWidth, tileToCoverEmptyArea.Zoom);
                    //    //        break;
                    //    //    }
                    //    //}

                    //    //tileToCoverEmptyArea = tilesToFillEmptyArea.OrderByDescending(x => x.Zoom).FirstOrDefault();
                    //    tileToCoverEmptyArea = tilesToFillEmptyArea.FirstOrDefault(x => x.Zoom == 1);

                    //    // We found a tile to cover the area. If not, this should be only when refreshing the wave form for the first time @ 100%
                    //    if (tileToCoverEmptyArea != null)
                    //    {
                    //        Console.WriteLine("---> Adding tile to fill empty - tile.X: {0} tile.Zoom: {1} -- {2}", tileToCoverEmptyArea.ContentOffset.X, tileToCoverEmptyArea.Zoom, DateTime.Now);
                    //        tiles.Add(tileToCoverEmptyArea);
                    //    }
                    //    else
                    //    {
                    //        // The problem is that there are sometimes a tile could not be found the previous list... is that really a good source of info?
                    //        // maybe try the previous list and if not, fall back to the 100% zoom.
                    //        // or actually make a linq query similar to the one with adjusted content offset x.
                    //        Console.WriteLine("[!!!] WARNING: WaveFormCacheService - GetTiles - An empty area could not be filled by a tile!");
                    //    }
                    //}

                    // Update the covered area position after trying to fill any empty areas left behind
                    // Note: There are still empty areas, this might not be the best way to make sure areas are all filled... 
                    //coveredAreaX = currentTileX + currentTileWidth;

                    // Keep the available tiles from the last index so we can search through this list to cover an empty area if needed
                    //previouslyAvailableTiles = availableTiles;

                    // Add tile to list of tiles to draw (TO DO: Check for existing tiles with the same zoom + offset
                    if(!tiles.Contains(tile))
                        tiles.Add(tile);
                }
            }

            // Order tiles by zoom and then by content offset x; this makes sure that the tiles with the nearest zoom level get drawn on top of farther zoom levels
            // maybe replace this linq query by inserting the tiles in the list in the right order (at tiles.Add(tile) just up from here)
            // Also use Distinct to prevent drawing the same tile multiple times
            // B U G: This might crash if a tile is removed from the list....
            var tilesOrdered = tiles.OrderBy(obj => obj.Zoom).ThenBy(obj => obj.ContentOffset.X).ToList();
            return tilesOrdered;
        }
예제 #3
0
        private void DrawTiles(IGraphicsContext context, int tileSize, float realScrollBarHeight)
        {
            float deltaZoom = (float) (Zoom/Math.Floor(Zoom));
            int startDirtyTile = (int)Math.Floor((ContentOffset.X + context.DirtyRect.X) / ((float)tileSize * deltaZoom));
            int numberOfDirtyTilesToDraw = (int)Math.Ceiling(context.DirtyRect.Width / tileSize) + 1;
            var request = new WaveFormBitmapRequest()
            {
                StartTile = startDirtyTile,
                EndTile = startDirtyTile + numberOfDirtyTilesToDraw,
                TileSize = tileSize,
                BoundsWaveForm = Frame,
                Zoom = _zoom,
                DisplayType = _displayType
            };
            var tiles = _waveFormCacheService.GetTiles(request);

            //Console.WriteLine("WaveFormControl - #### startTile: {0} startTileX: {1} contentOffset.X: {2} contentOffset.X/tileSize: {3} numberOfTilesToFillWidth: {4} firstTileX: {5}", startTile, startTile * tileSize, ContentOffset.X, ContentOffset.X / tileSize, numberOfTilesToFillWidth, (startTile * tileSize) - ContentOffset.X);
            foreach (var tile in tiles)
            {
                float tileDeltaZoom = Zoom / tile.Zoom;
                float x = tile.ContentOffset.X * tileDeltaZoom;
                float tileWidth = tileSize * tileDeltaZoom;
                float tileHeight = (ShowScrollBar && Zoom > 1) ? Frame.Height - realScrollBarHeight : Frame.Height;
                //Console.WriteLine("WaveFormControl - Draw - tile - x: {0} tileWidth: {1} deltaZoom: {2}", x, tileWidth, deltaZoom);
                //Console.WriteLine("WaveFormControl - Draw - tile - tile.ContentOffset.X: {0} x: {1} tileWidth: {2} tile.Zoom: {3}", tile.ContentOffset.X, x, tileWidth, tile.Zoom);

                //context.DrawImage(new BasicRectangle(x - ContentOffset.X, 0, tileWidth, Frame.Height), tile.Image.ImageSize, tile.Image.Image);
                context.DrawImage(new BasicRectangle(x - ContentOffset.X, 0, tileWidth, tileHeight), tile.Image.ImageSize, tile.Image.Image);

                // Debug overlay
                //context.DrawRectangle(new BasicRectangle(x - ContentOffset.X, 0, tileWidth, Frame.Height), new BasicBrush(new BasicColor(0, 0, 255, 50)), _penCursorLine);
                //context.DrawText(string.Format("{0:0.0}", tile.Zoom), new BasicPoint(x - ContentOffset.X + 2, 4), _textColor, "Roboto", 11);
            }
        }
예제 #4
0
        private void DrawScrollBar(IGraphicsContext context, int tileSize, float realScrollBarHeight)
        {
            if (ShowScrollBar && Zoom > 1)
            {
                int startTile = 0;
                int numberOfTilesToFillWidth = (int)Math.Ceiling(Frame.Width / tileSize);// + 1; // maybe a bug here? when one of the tile is partially drawn, you need another one?
                var requestScrollBar = new WaveFormBitmapRequest()
                {
                    StartTile = startTile,
                    EndTile = numberOfTilesToFillWidth,
                    TileSize = tileSize,
                    BoundsWaveForm = new BasicRectangle(0, 0, Frame.Width, ScrollBarHeight), // Frame
                    Zoom = 1,
                    IsScrollBar = true,
                    DisplayType = _displayType
                };
                // TODO: Cache those tiles, we do not need to request them continually since these tiles are always at 100%
                var tilesScrollBar = _waveFormCacheService.GetTiles(requestScrollBar);
                foreach (var tile in tilesScrollBar)
                {
                    context.DrawImage(new BasicRectangle(tile.ContentOffset.X, Frame.Height - realScrollBarHeight, tileSize, realScrollBarHeight), tile.Image.ImageSize, tile.Image.Image);
                }

                // Draw a veil over the area that's not visible. The veil alpha gets stronger as the zoom progresses.
                byte startAlpha = 170;
                byte maxAlpha = 210;
                byte alpha = (byte)Math.Min(maxAlpha, (startAlpha + (60 * (Zoom / 10))));
                var colorVisibleArea = new BasicColor(32, 40, 46, alpha);
                float visibleAreaWidth = (1 / Zoom) * Frame.Width;
                float visibleAreaX = (1 / Zoom) * ContentOffset.X;
                var rectLeftArea = new BasicRectangle(0, Frame.Height - realScrollBarHeight, visibleAreaX, realScrollBarHeight);
                var rectRightArea = new BasicRectangle(visibleAreaX + visibleAreaWidth, Frame.Height - realScrollBarHeight, Frame.Width - visibleAreaX - visibleAreaWidth, realScrollBarHeight);
                //context.DrawRectangle(new BasicRectangle(visibleAreaX, Frame.Height - scrollBarHeight, visibleAreaWidth, scrollBarHeight), new BasicBrush(colorVisibleArea), new BasicPen());
                context.DrawRectangle(rectLeftArea, new BasicBrush(colorVisibleArea), new BasicPen());
                context.DrawRectangle(rectRightArea, new BasicBrush(colorVisibleArea), new BasicPen());
            }
        }
예제 #5
0
 /// <summary>
 /// Requests a wave form bitmap to be generated. Once the bitmap is generated, the OnGenerateWaveFormBitmapEnded event is fired.
 /// </summary>
 public void RequestBitmap(WaveFormBitmapRequest request)
 {
     ThreadPool.QueueUserWorkItem(new WaitCallback(RequestBitmapInternal), request);
 }