/// <inheritdoc />
        public virtual IFramebufferReference GrabFramebufferReference(Size size, IImmutableSet <Screen> layout)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException(nameof(RfbRenderTarget));
            }

            PixelSize requiredPixelSize = Conversions.GetPixelSize(size);

            // Creation of a new buffer necessary?
            // No synchronization necessary, because the only conflicting write operation is
            // in this same method and the field is marked volatile to avoid caching issues.
            // ReSharper disable once InconsistentlySynchronizedField
            bool sizeChanged = _bitmap == null || _bitmap.PixelSize != requiredPixelSize;

            WriteableBitmap bitmap;

            if (sizeChanged)
            {
                // Create new bitmap with required size and the format that is preferred by the current platform (therefore 'null').
                // TODO: Detect DPI dynamically
                bitmap = new WriteableBitmap(requiredPixelSize, new Vector(96.0f, 96.0f), null);

                // Wait for the rendering being finished before replacing the bitmap
                lock (_bitmapReplacementLock)
                {
                    _bitmap?.Dispose();
                    _bitmap = bitmap;
                }
            }
            else
            {
                // ReSharper disable once InconsistentlySynchronizedField
                bitmap = _bitmap !;
            }

            // Lock framebuffer and return as converted reference
            // ReSharper disable once InconsistentlySynchronizedField
            ILockedFramebuffer lockedFramebuffer = bitmap.Lock();

            return(new AvaloniaFramebufferReference(lockedFramebuffer, () => Dispatcher.UIThread.Post(() => {
                if (sizeChanged)
                {
                    InvalidateMeasure();
                }
                InvalidateVisual();
            })));
        }
Exemple #2
0
        /// <summary>
        /// Finds all of the bodies in the provided image (areas of connected black pixels).
        /// </summary>
        /// <param name="Image">The image to find the bodies in</param>
        /// <returns>A collection of bodies in the image</returns>
        public List <Body> FindBodies(EtchableImage Image)
        {
            List <Body> bodies = new List <Body>();

            bool[,] visitedPixels = new bool[Image.Width, Image.Height];

            using (ILockedFramebuffer buffer = Image.Bitmap.Lock())
            {
                IntPtr address = buffer.Address;

                for (int y = 0; y < Image.Height; y++)
                {
                    IntPtr rowOffsetAddress = address + (y * Image.Width * 4);

                    for (int x = 0; x < Image.Width; x++)
                    {
                        // Ignore pixels we've already looked at
                        if (visitedPixels[x, y])
                        {
                            continue;
                        }

                        // Set the flag for this pixel because now we've looked at it
                        visitedPixels[x, y] = true;

                        // Ignore white pixels
                        IntPtr pixelAddress = rowOffsetAddress + (x * 4);

                        // Get the blue channel since that isn't used for path drawing,
                        // so it will always have the correct pixel value of 0 or 255
                        byte pixelValue = Marshal.ReadByte(pixelAddress);
                        if (pixelValue == 0xFF)
                        {
                            continue;
                        }

                        // If we get here, we have a new body!
                        Body body = ProcessBody(buffer, x, y, visitedPixels);
                        bodies.Add(body);
                    }
                }
            }

            return(bodies);
        }
 public static unsafe uint[] ToBitmap(WriteableBitmap wb, out int width, out int height)
 {
     using (ILockedFramebuffer l = wb.Lock())
     {
         uint *    bmpAddress = (uint *)l.Address.ToPointer();
         PixelSize ps         = wb.PixelSize;
         height = ps.Height;
         width  = ps.Width;
         uint[] arr = new uint[height * width];
         for (int py = 0; py < height; py++)
         {
             for (int px = 0; px < width; px++)
             {
                 int i = px + (py * width);
                 arr[i] = *(bmpAddress + i);
             }
         }
         return(arr);
     }
 }
Exemple #4
0
        private unsafe void RenderTick()
        {
            var time = new TimeBarrier(MaxFPS);

            time.Start();

            DateTime lastRenderTime = DateTime.Now;

            while (!_isDisposed)
            {
                DateTime now = DateTime.Now;
                using (ILockedFramebuffer l = _screen.Lock())
                {
                    uint *bmpAddress = (uint *)l.Address.ToPointer();
                    Game.Instance.RenderTick(bmpAddress, RenderWidth, RenderHeight, _showFPS ? (int)Math.Round(1_000 / now.Subtract(lastRenderTime).TotalMilliseconds) : (int?)null);
                }
                InvalidateVisual();
                lastRenderTime = now;
                time.Wait();
            }
            time.Stop();
        }
        private WriteableBitmap GetSurface(PixelBuffer pixelBuffer)
        {
            if (!Monitor.IsEntered(_syncRoot))
            {
                throw new InvalidOperationException();
            }

            WriteableBitmap surface = pixelBuffer.Surface;

            if (surface is null || surface.PixelSize != new PixelSize(pixelBuffer.Width, pixelBuffer.Height))
            {
                surface?.Dispose();
                surface             = new WriteableBitmap(new PixelSize(pixelBuffer.Width, pixelBuffer.Height), OffscreenGraphics.DpiScale.Dpi, PixelFormat.Bgra8888, AlphaFormat.Premul);
                pixelBuffer.Surface = surface;
            }

            using (ILockedFramebuffer frameBuffer = surface.Lock())
            {
                Marshal.Copy(pixelBuffer.DIB, 0, frameBuffer.Address, pixelBuffer.Size);
                pixelBuffer.ClearDirtyRectangle();
            }
            return(surface);
        }
 public static unsafe uint[][] LoadBitmapSheet(string resource, int spriteWidth, int spriteHeight)
 {
     using (WriteableBitmap wb = ToWriteableBitmap(new Bitmap(Utils.GetResourceStream(resource))))
         using (ILockedFramebuffer l = wb.Lock())
         {
             uint *    bmpAddress  = (uint *)l.Address.ToPointer();
             PixelSize ps          = wb.PixelSize;
             int       sheetWidth  = ps.Width;
             int       sheetHeight = ps.Height;
             int       numSpritesX = sheetWidth / spriteWidth;
             int       numSpritesY = sheetHeight / spriteHeight;
             uint[][]  sprites     = new uint[numSpritesX * numSpritesY][];
             int       sprite      = 0;
             for (int sy = 0; sy < numSpritesY; sy++)
             {
                 for (int sx = 0; sx < numSpritesX; sx++)
                 {
                     sprites[sprite++] = GetBitmapUnchecked(bmpAddress, sheetWidth, sx * spriteWidth, sy * spriteHeight, spriteWidth, spriteHeight);
                 }
             }
             return(sprites);
         }
 }
        /// <summary>
        /// Generates a thumbnail of the page.
        /// </summary>
        /// <returns>A <see cref="WriteableBitmap"/> containing the thumbnail of the page.</returns>
        private WriteableBitmap GenerateThumbnail()
        {
            //Render the whole page.
            Rectangle bounds = Document.Pages[this.FindControl <PDFRenderer>("MuPDFRenderer").PageNumber].Bounds;

            //Determine the appropriate zoom factor to render a thumbnail of the right size for the NavigatorCanvas, taking into account DPI scaling
            double maxDimension = Math.Max(bounds.Width, bounds.Height);
            double zoom         = 200 / maxDimension * ((VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1);

            //Get the actual size in pixels of the image.
            RoundedRectangle roundedBounds = bounds.Round(zoom);

            //Initialize the image
            WriteableBitmap bmp = new WriteableBitmap(new PixelSize(roundedBounds.Width, roundedBounds.Height), new Vector(96, 96), Avalonia.Platform.PixelFormat.Rgba8888, AlphaFormat.Unpremul);

            //Render the page to the bitmap, without marshaling.
            using (ILockedFramebuffer fb = bmp.Lock())
            {
                Document.Render(this.FindControl <PDFRenderer>("MuPDFRenderer").PageNumber, bounds, zoom, PixelFormats.RGBA, fb.Address);
            }

            return(bmp);
        }
Exemple #8
0
            public unsafe void DrawAll(bool borderBlocks, bool wasResized)
            {
                WriteableBitmap bmp = borderBlocks ? BorderBlocksBitmap : BlocksBitmap;

                using (ILockedFramebuffer l = bmp.Lock())
                {
                    uint *bmpAddress = (uint *)l.Address.ToPointer();
                    int   width      = borderBlocks ? BorderWidth : Width;
                    int   height     = borderBlocks ? BorderHeight : Height;
                    int   bmpWidth   = width * Overworld.Block_NumPixelsX;
                    int   bmpHeight  = height * Overworld.Block_NumPixelsY;
                    RenderUtils.FillColor(bmpAddress, bmpWidth, bmpHeight, 0, 0, bmpWidth, bmpHeight, 0xFF000000);
                    Block[][] arr = borderBlocks ? BorderBlocks : Blocks;
                    for (int y = 0; y < height; y++)
                    {
                        Block[] arrY = arr[y];
                        for (int x = 0; x < width; x++)
                        {
                            arrY[x].BlocksetBlock.Draw(bmpAddress, bmpWidth, bmpHeight, x * Overworld.Block_NumPixelsX, y * Overworld.Block_NumPixelsY);
                        }
                    }
                }
                OnDrew?.Invoke(this, borderBlocks, wasResized);
            }
Exemple #9
0
        static WriteableBitmap set_data(byte[] buffer, int width, int height)
        {
            WriteableBitmap bmp    = new WriteableBitmap(new PixelSize(width, height), new Vector(96, 96), PixelFormat.Rgba8888);
            int             stride = width * 4;

            using (ILockedFramebuffer fb = bmp.Lock())
            {
                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        unsafe
                        {
                            byte *data = (byte *)fb.Address;
                            data[y * stride + x * 4]     = buffer[y * width * 3 + x * 3 + 0];
                            data[y * stride + x * 4 + 1] = buffer[y * width * 3 + x * 3 + 1];
                            data[y * stride + x * 4 + 2] = buffer[y * width * 3 + x * 3 + 2];
                            data[y * stride + x * 4 + 3] = 255;
                        }
                    }
                }
            }
            return(bmp);
        }
 public FramebufferShim(ILockedFramebuffer target) :
     base(target.Size, target.Dpi, target.Format)
 {
     _target = target;
 }
Exemple #11
0
 public FramebufferShim(ILockedFramebuffer target) :
     base(target.Width, target.Height, target.Dpi.X, target.Dpi.Y, target.Format)
 {
     _target = target;
 }
 public LockedFramebufferWrapper(ILockedFramebuffer inner, IDisposable disposable)
 {
     _inner      = inner;
     _disposable = disposable;
 }
Exemple #13
0
        /// <summary>
        /// Breaks the provided image into horizontal raster etch segments.
        /// </summary>
        /// <param name="Image">The image to get the raster etch lines for</param>
        /// <returns>A collection of horizontal lines (each of which can have multiple segments)
        /// necessary to etch the image in raster mode.</returns>
        private List <Line> GetRasterEtchLines(EtchableImage Image)
        {
            List <Line> lines = new List <Line>();

            using (ILockedFramebuffer bitmapBuffer = Image.Bitmap.Lock())
            {
                IntPtr bitmapAddress = bitmapBuffer.Address;

                for (int y = 0; y < Image.Height; y++)
                {
                    Line line            = new Line(y);
                    bool isInEtchSegment = false;
                    int  etchStart       = 0;

                    for (int x = 0; x < Image.Width; x++)
                    {
                        // Get the blue channel - since this is a black and white image, the channel doesn't really matter
                        byte pixelValue = Marshal.ReadByte(bitmapAddress);

                        // This is a white pixel
                        if (pixelValue > 127)
                        {
                            if (isInEtchSegment)
                            {
                                // Create a new etch segment from the recorded start to the previous pixel
                                EtchSegment segment = new EtchSegment(etchStart, x - 1);
                                line.Segments.Add(segment);
                                isInEtchSegment = false;
                            }
                        }

                        // This is a black pixel
                        else
                        {
                            if (!isInEtchSegment)
                            {
                                // Start a new etch segment
                                isInEtchSegment = true;
                                etchStart       = x;
                            }

                            // Check if we're at the last pixel in the row but still in an etch segment
                            else if (x == Image.Width - 1)
                            {
                                EtchSegment segment = new EtchSegment(etchStart, x);
                                line.Segments.Add(segment);
                                isInEtchSegment = false;
                            }
                        }

                        // Move to the next pixel in the bitmap buffer
                        bitmapAddress += 4;
                    }

                    // Ignore lines with no etch segments
                    if (line.Segments.Count > 0)
                    {
                        lines.Add(line);
                    }
                }
            }

            return(lines);
        }
 public FramebufferProxy(ILockedFramebuffer fb, Action onDispose)
 {
     _fb        = fb;
     _onDispose = onDispose;
 }
Exemple #15
0
        /// <summary>
        /// Finds the outline for a body given its starting (top left) pixel, in the form of a continuous path
        /// that's suitable for etching.
        /// </summary>
        /// <param name="ImageBuffer">The buffer containing the pixel values of the image being processed</param>
        /// <param name="StartPoint">The top-left point of the body</returns>
        public Path GetOutlinePath(ILockedFramebuffer ImageBuffer, Point StartPoint)
        {
            List <Point>    outline       = new List <Point>();
            HashSet <Point> visitedPoints = new HashSet <Point>();

            Point        currentPoint     = StartPoint;
            List <Point> currentNeighbors = GetNeighbors(currentPoint, ImageBuffer.Size);

            while (true)
            {
                // Add this point to the outline
                outline.Add(currentPoint);
                visitedPoints.Add(currentPoint);

                // Check each neighbor point to find the next one in the outline
                bool foundNextOutlinePoint = false;
                foreach (Point currentNeighbor in currentNeighbors)
                {
                    // If this point is already part of the outline, ignore it
                    if (visitedPoints.Contains(currentNeighbor))
                    {
                        continue;
                    }

                    // If this point is white, ignore it
                    byte currentNeighborValue = GetPixelValueAtPoint(ImageBuffer, currentNeighbor);
                    if (currentNeighborValue == 0xFF)
                    {
                        continue;
                    }

                    // Check if this point has any white pixel neighbors; if it does, it is on the outline and
                    // becomes the next point. Start the search from the current point, going clockwise, so it
                    // will always try to find the outermost point and won't cut through the middle of really thin
                    // bodies.
                    List <Point> nextNeighbors = GetNeighbors(currentNeighbor, ImageBuffer.Size, currentPoint);
                    foreach (Point nextNeighbor in nextNeighbors)
                    {
                        byte nextNeighborValue = GetPixelValueAtPoint(ImageBuffer, nextNeighbor);
                        if (nextNeighborValue == 0xFF)
                        {
                            currentPoint          = currentNeighbor;
                            currentNeighbors      = nextNeighbors;
                            foundNextOutlinePoint = true;
                            break;
                        }
                    }
                    if (foundNextOutlinePoint)
                    {
                        break;
                    }

                    // Check if this point is at the image boundary; if it is, it's on the outline and
                    // becomes the next point.
                    if (currentNeighbor.Y == 0 ||
                        currentNeighbor.Y == ImageBuffer.Size.Height - 1 ||
                        currentNeighbor.X == 0 ||
                        currentNeighbor.X == ImageBuffer.Size.Width - 1)
                    {
                        currentPoint          = currentNeighbor;
                        currentNeighbors      = nextNeighbors;
                        foundNextOutlinePoint = true;
                        break;
                    }
                }

                // We couldn't find any more points that belong to the outline, so we're done.
                if (!foundNextOutlinePoint)
                {
                    Path path = new Path(outline);
                    return(path);
                }
            }
        }
 public FramebufferDrawingContextImpl(SKCanvas canvas, SKSurface surface, ILockedFramebuffer framebuffer) : base(canvas)
 {
     _canvas      = canvas;
     _surface     = surface;
     _framebuffer = framebuffer;
 }
Exemple #17
0
        public static Bitmap RenderString(string str, StringRenderStyle style)
        {
            // Return null for bad strings
            if (string.IsNullOrWhiteSpace(str))
            {
                return(null);
            }

            string path; int charHeight, spaceWidth;

            switch (style)
            {
            case StringRenderStyle.BattleName: path = "BattleName"; charHeight = 11; spaceWidth = 2; break;

            case StringRenderStyle.BattleLevel: path = "BattleLevel"; charHeight = 10; spaceWidth = 7; break;

            case StringRenderStyle.BattleHP: path = "BattleHP"; charHeight = 8; spaceWidth = 0; break;

            default: path = "Default"; charHeight = 15; spaceWidth = 4; break;
            }

            int index;

            string GetCharKey()
            {
                string key = $"FONT_{path}_";

                if (index + 6 <= str.Length && str.Substring(index, 6) == "[PKMN]")
                {
                    key   += "PKMN";
                    index += 6;
                }
                else if (index + 4 <= str.Length && str.Substring(index, 4) == "[LV]")
                {
                    key   += "LV";
                    index += 4;
                }
                else
                {
                    key += ((int)str[index]).ToString("X");
                    index++;
                }
                const string questionMark = "FONT_Default_3F";

                return(DoesResourceExist($"Kermalis.PokemonBattleEngineClient.FONT.{path}.{key}.png") ? key : questionMark);
            }

            // Measure how large the string will end up
            int stringWidth = 0, stringHeight = charHeight, curLineWidth = 0;

            index = 0;
            while (index < str.Length)
            {
                if (str[index] == ' ')
                {
                    index++;
                    curLineWidth += spaceWidth;
                }
                else if (str[index] == '\r')
                {
                    index++;
                    continue;
                }
                else if (str[index] == '\n')
                {
                    index++;
                    stringHeight += charHeight + 1;
                    if (curLineWidth > stringWidth)
                    {
                        stringWidth = curLineWidth;
                    }
                    curLineWidth = 0;
                }
                else
                {
                    string key = GetCharKey();
                    if (!loadedBitmaps.ContainsKey(key))
                    {
                        loadedBitmaps.TryAdd(key, UriToBitmap(new Uri($"resm:Kermalis.PokemonBattleEngineClient.FONT.{path}.{key}.png?assembly=PokemonBattleEngineClient")));
                    }
                    curLineWidth += loadedBitmaps[key].PixelSize.Width;
                }
            }
            if (curLineWidth > stringWidth)
            {
                stringWidth = curLineWidth;
            }

            // Draw the string
            var wb = new WriteableBitmap(new PixelSize(stringWidth, stringHeight), new Vector(96, 96), PixelFormat.Bgra8888);

            using (IRenderTarget rtb = AvaloniaLocator.Current.GetService <IPlatformRenderInterface>().CreateRenderTarget(new[] { new WriteableBitmapSurface(wb) }))
                using (IDrawingContextImpl ctx = rtb.CreateDrawingContext(null))
                {
                    double x = 0, y = 0;
                    index = 0;
                    while (index < str.Length)
                    {
                        if (str[index] == ' ')
                        {
                            index++;
                            x += spaceWidth;
                        }
                        else if (str[index] == '\r')
                        {
                            index++;
                            continue;
                        }
                        else if (str[index] == '\n')
                        {
                            index++;
                            y += charHeight + 1;
                            x  = 0;
                        }
                        else
                        {
                            Bitmap bmp = loadedBitmaps[GetCharKey()];
                            ctx.DrawImage(bmp.PlatformImpl, 1.0, new Rect(0, 0, bmp.PixelSize.Width, charHeight), new Rect(x, y, bmp.PixelSize.Width, charHeight));
                            x += bmp.PixelSize.Width;
                        }
                    }
                }
            // Edit colors
            using (ILockedFramebuffer l = wb.Lock())
            {
                uint primary = 0xFFFFFFFF, secondary = 0xFF000000, tertiary = 0xFF808080;
                switch (style)
                {
                case StringRenderStyle.MenuBlack: primary = 0xFF5A5252; secondary = 0xFFA5A5AD; break;

                case StringRenderStyle.BattleWhite:     //secondary = 0xF0FFFFFF; break; // Looks horrible because of Avalonia's current issues
                case StringRenderStyle.MenuWhite: secondary = 0xFF848484; break;

                case StringRenderStyle.BattleName:
                case StringRenderStyle.BattleLevel: primary = 0xFFF7F7F7; secondary = 0xFF181818; break;

                case StringRenderStyle.BattleHP: primary = 0xFFF7F7F7; secondary = 0xFF101010; tertiary = 0xFF9C9CA5; break;
                }
                for (int x = 0; x < stringWidth; x++)
                {
                    for (int y = 0; y < stringHeight; y++)
                    {
                        var  address = new IntPtr(l.Address.ToInt64() + (x * sizeof(uint)) + (y * l.RowBytes));
                        uint pixel   = (uint)Marshal.ReadInt32(address);
                        if (pixel == 0xFFFFFFFF)
                        {
                            Marshal.WriteInt32(address, (int)primary);
                        }
                        else if (pixel == 0xFF000000)
                        {
                            Marshal.WriteInt32(address, (int)secondary);
                        }
                        else if (pixel == 0xFF808080)
                        {
                            Marshal.WriteInt32(address, (int)tertiary);
                        }
                    }
                }
            }
            return(wb);
        }