Esempio n. 1
0
        private async void DrawSpectrogram()
        {
            if (_bitmap == null)
            {
                return;
            }

            // stop here if a drawing operation is already in progress
            if (_isDrawing)
            {
                _isRequested = true;
                return;
            }

            // set the drawing flag
            _isDrawing = true;

            // preprocess the data to draw
            var mapped  = Data.Where(p => p.Y is double[]).Select(p => new Tuple <double, double[]>(AxisX.GetItemPixel(p.X), (double[])p.Y)).OrderBy(p => p.Item1).ToList();
            var palette = ColorPalette;

            // lock the backbuffer
            var bitmap = _bitmap;

            bitmap.Lock();
            var width      = bitmap.PixelWidth;
            var height     = bitmap.PixelHeight;
            var stride     = bitmap.BackBufferStride;
            var backbuffer = bitmap.BackBuffer;

            // fill the backbuffer from a background thread
            await Task.Run(() =>
            {
                // map data points to axis pixels
                if (mapped.Count > 0)
                {
                    var rows         = mapped.First().Item2.Length;
                    var rowHeight    = height / (double)rows;
                    var maximumValue = mapped.Aggregate(new double[rows], (c, p) =>
                    {
                        for (var i = 0; i < c.Length; i++)
                        {
                            c[i] = Math.Max(c[i], p.Item2[i]);
                        }
                        return(c);
                    });

                    // iterate over all pixels in the series
                    var lastColumn = 0;
                    var nextColumn = 1;
                    for (var x = 0.0; x < width; x++)
                    {
                        // find the next data column
                        while (nextColumn < mapped.Count - 1 && x > mapped[nextColumn].Item1)
                        {
                            lastColumn  = nextColumn;
                            nextColumn += 1;
                        }

                        // calculate the horizontal relative position in between the two columns
                        var factorX = (x - mapped[lastColumn].Item1) / (mapped[nextColumn].Item1 - mapped[lastColumn].Item1);

                        // draw the column
                        var lastRow = 0;
                        var nextRow = 1;
                        for (var y = 0.0; y < height; y++)
                        {
                            // find the next data row
                            while (nextRow < mapped[nextColumn].Item2.Length - 1 && y > nextRow * rowHeight)
                            {
                                lastRow  = nextRow;
                                nextRow += 1;
                            }

                            // calculate the horizontal relative position in between the two rows
                            var factorY = (y - lastRow * rowHeight) / rowHeight;

                            // interpolate the point value
                            double c;
                            if (factorX >= 0 && factorX <= 1 && factorY >= 0 && factorY <= 1)
                            {
                                var a00 = mapped[lastColumn].Item2[lastRow];
                                var a10 = mapped[nextColumn].Item2[lastRow] - mapped[lastColumn].Item2[lastRow];
                                var a01 = mapped[lastColumn].Item2[nextRow] - mapped[lastColumn].Item2[lastRow];
                                var a11 = mapped[nextColumn].Item2[nextRow] + mapped[lastColumn].Item2[lastRow] - (mapped[nextColumn].Item2[lastRow] + mapped[lastColumn].Item2[nextRow]);
                                c       = a00 + a10 * factorX + a01 * factorY + a11 * factorX * factorY;
                            }
                            else
                            {
                                c = 0;
                            }

                            // scale the pixel value to 0..1
                            var scaledMaximum = factorY * (maximumValue[nextRow] - maximumValue[lastRow]) + maximumValue[lastRow];
                            var scaledValue   = scaledMaximum > 1e-3 ? c / scaledMaximum : 0;

                            // get the color for the pixel
                            var color = palette[(int)(scaledValue * (palette.Count - 1))];

                            // draw the pixel
                            var value = (byte)(255 * scaledValue);
                            unsafe
                            {
                                var address  = (byte *)(backbuffer + ((int)(height - 1 - y) * stride) + (int)x * 4);
                                *(address++) = color.B;
                                *(address++) = color.G;
                                *(address++) = color.R;
                                *(address++) = color.A;
                            }
                        }
                    }
                }
                else
                {
                    var color = palette[0];
                    for (var i = 0; i < width *height * 4; i += 4)
                    {
                        unsafe
                        {
                            var address  = (byte *)(backbuffer + i);
                            *(address++) = color.B;
                            *(address++) = color.G;
                            *(address++) = color.R;
                            *(address++) = color.A;
                        }
                    }
                }
            });

            // write the bitmap contents
            bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
            bitmap.Unlock();

            // invalidate visuals to redraw
            _isDrawing = false;
            InvalidateVisual();

            if (_isRequested)
            {
                _isRequested = false;
                DrawSpectrogram();
            }
        }