Esempio n. 1
0
        public static unsafe Bitmap GetImageFromMask(int width, int height, ReadOnlySpan <byte> mask)
        {
            Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);

            var data = bmp.BasicLockBits(ImageLockMode.WriteOnly);

            uint *ptrInt    = (uint *)data.Scan0;
            int   bmpStride = data.Stride / 4;

            int stride = ((width + 7) / 8) * 8;

            FastBitArray arr          = new FastBitArray(mask);
            int          strideFactor = 0;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (arr.GetReverse(x + strideFactor))
                    {
                        *(ptrInt + x + (y * bmpStride)) = 0xFFFFFFFFu;
                    }
                    else
                    {
                        *(ptrInt + x + (y * bmpStride)) = 0xFF000000u;
                    }
                }

                strideFactor += stride;
            }

            bmp.UnlockBits(data);

            return(bmp);
        }
Esempio n. 2
0
        public static AssetSprite.CollisionMaskInfo GetInfoForSprite(ProjectFile pf, GMSprite spr, out List <Bitmap> bitmaps, bool suggestPrecise = false)
        {
            bitmaps = new List <Bitmap>(spr.TextureItems.Count);

            var info = new AssetSprite.CollisionMaskInfo
            {
                Mode = (MaskMode)spr.BBoxMode
            };

            if (spr.SepMasks == GMSprite.SepMaskType.AxisAlignedRect)
            {
                info.Type = MaskType.Rectangle;
            }
            else if (spr.SepMasks == GMSprite.SepMaskType.RotatedRect)
            {
                info.Type = MaskType.RectangleWithRotation;
            }

            // Some basic conditions to bail
            if (spr.CollisionMasks.Count != 1 && spr.CollisionMasks.Count != spr.TextureItems.Count)
            {
                return(info);
            }
            if (spr.CollisionMasks.Count == 0)
            {
                return(info);
            }

            // Get bitmaps from frames
            bitmaps = GetBitmaps(pf, spr.Width, spr.Height, spr.TextureItems);
            List <BitmapData> bitmapData = new List <BitmapData>(bitmaps.Count);

            foreach (var item in bitmaps)
            {
                bitmapData.Add(item.BasicLockBits());
            }

            int boundLeft   = Math.Clamp(spr.MarginLeft, 0, spr.Width),
                boundRight  = Math.Clamp(spr.MarginRight, 0, spr.Width - 1),
                boundTop    = Math.Clamp(spr.MarginTop, 0, spr.Height),
                boundBottom = Math.Clamp(spr.MarginBottom, 0, spr.Height - 1);

            switch (spr.SepMasks)
            {
            case GMSprite.SepMaskType.AxisAlignedRect:
            case GMSprite.SepMaskType.RotatedRect:
                switch (info.Mode)
                {
                case MaskMode.Automatic:
                    // Scan for the lowest alpha value in the bounding box
                    // When comparing each pixel, compare to the one in that spot with the highest alpha in every frame

                    bool foundNonzero = false;
                    byte lowest       = 0;
                    byte highest      = 0;

                    int stride = ((spr.Width + 7) / 8) * 8;

                    FastBitArray mask         = new FastBitArray(spr.CollisionMasks[0].Memory.Span);
                    int          strideFactor = boundTop * stride;

                    for (int y = boundTop; y <= boundBottom; y++)
                    {
                        for (int x = boundLeft; x <= boundRight; x++)
                        {
                            if (mask.GetReverse(x + strideFactor))
                            {
                                byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y);
                                if (highestAlpha > highest)
                                {
                                    highest = highestAlpha;
                                }
                                if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest))
                                {
                                    lowest       = highestAlpha;
                                    foundNonzero = true;
                                }
                            }
                        }

                        strideFactor += stride;
                    }

                    if (foundNonzero)
                    {
                        if (lowest == highest)
                        {
                            lowest = 0;         // Could be anything
                        }
                        else
                        {
                            --lowest;
                        }
                    }
                    info.AlphaTolerance = lowest;
                    break;

                case MaskMode.Manual:
                    info.Left   = spr.MarginLeft;
                    info.Right  = spr.MarginRight;
                    info.Top    = spr.MarginTop;
                    info.Bottom = spr.MarginBottom;
                    break;
                }
                break;

            case GMSprite.SepMaskType.Precise:
            {
                int stride = ((spr.Width + 7) / 8) * 8;

                bool foundNonzero = false;
                byte lowest       = 0;
                byte highest      = 0;

                if (spr.CollisionMasks.Count > 1 && spr.CollisionMasks.Count == spr.TextureItems.Count)
                {
                    info.Type = MaskType.PrecisePerFrame;

                    unsafe
                    {
                        for (int i = 0; i < spr.CollisionMasks.Count; i++)
                        {
                            BitmapData   item         = bitmapData[i];
                            FastBitArray mask         = new FastBitArray(spr.CollisionMasks[i].Memory.Span);
                            int          strideFactor = boundTop * stride;
                            for (int y = boundTop; y <= boundBottom; y++)
                            {
                                for (int x = boundLeft; x <= boundRight; x++)
                                {
                                    if (mask.GetReverse(x + strideFactor))
                                    {
                                        byte val = *((byte *)item.Scan0 + (x * 4) + (y * item.Stride) + 3);
                                        if (val > highest)
                                        {
                                            highest = val;
                                        }
                                        if (val != 0 && (!foundNonzero || val < lowest))
                                        {
                                            lowest       = val;
                                            foundNonzero = true;
                                        }
                                    }
                                }

                                strideFactor += stride;
                            }
                        }
                    }
                }
                else
                {
                    info.Type = MaskType.Precise;

                    // Scan for highest alpha, as well as diamond/ellipses
                    FastBitArray mask = new FastBitArray(spr.CollisionMasks[0].Memory.Span);

                    bool  isDiamond = true, isEllipse = true;
                    float centerX = ((spr.MarginLeft + spr.MarginRight) / 2);
                    float centerY = ((spr.MarginTop + spr.MarginBottom) / 2);
                    float radiusX = centerX - spr.MarginLeft + 0.5f;
                    float radiusY = centerY - spr.MarginTop + 0.5f;

                    int strideFactor = boundTop * stride;

                    if (!suggestPrecise && radiusX > 0f && radiusY > 0f)
                    {
                        for (int y = boundTop; y <= boundBottom; y++)
                        {
                            for (int x = boundLeft; x <= boundRight; x++)
                            {
                                float normalX   = (x - centerX) / radiusX;
                                float normalY   = (y - centerY) / radiusY;
                                bool  inDiamond = Math.Abs(normalX) + Math.Abs(normalY) <= 1f;
                                bool  inEllipse = Math.Pow(normalX, 2.0d) + Math.Pow(normalY, 2.0d) <= 1.0d;

                                if (mask.GetReverse(x + strideFactor))
                                {
                                    isDiamond &= inDiamond;
                                    isEllipse &= inEllipse;

                                    byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y);
                                    if (highestAlpha > highest)
                                    {
                                        highest = highestAlpha;
                                    }
                                    if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest))
                                    {
                                        lowest       = highestAlpha;
                                        foundNonzero = true;
                                    }
                                }
                                // Can't eliminate based on this, they can be split into pieces with multiple frames
                                //else
                                //{
                                //    isDiamond &= !inDiamond;
                                //    isEllipse &= !inEllipse;
                                //}
                            }

                            strideFactor += stride;
                        }
                    }
                    else
                    {
                        // Version without diamond/ellipse checks
                        isDiamond = false;
                        isEllipse = false;

                        for (int y = boundTop; y <= boundBottom; y++)
                        {
                            for (int x = boundLeft; x <= boundRight; x++)
                            {
                                if (mask.GetReverse(x + strideFactor))
                                {
                                    byte highestAlpha = GetHighestAlphaAt(bitmapData, x, y);
                                    if (highestAlpha > highest)
                                    {
                                        highest = highestAlpha;
                                    }
                                    if (highestAlpha != 0 && (!foundNonzero || highestAlpha < lowest))
                                    {
                                        lowest       = highestAlpha;
                                        foundNonzero = true;
                                    }
                                }
                            }

                            strideFactor += stride;
                        }
                    }

                    if (isDiamond)
                    {
                        info.Type = MaskType.Diamond;
                    }
                    else if (isEllipse)
                    {
                        info.Type = MaskType.Ellipse;
                    }
                }

                if (info.Mode == MaskMode.Manual ||
                    (info.Mode == MaskMode.Automatic && info.Type != MaskType.Precise && info.Type != MaskType.PrecisePerFrame))
                {
                    info.Left   = spr.MarginLeft;
                    info.Right  = spr.MarginRight;
                    info.Top    = spr.MarginTop;
                    info.Bottom = spr.MarginBottom;
                }

                if (info.Mode == MaskMode.Automatic || info.Type == MaskType.Precise ||
                    (info.Mode == MaskMode.Manual && info.Type == MaskType.PrecisePerFrame))
                {
                    if (foundNonzero)
                    {
                        if (lowest == highest)
                        {
                            lowest = 0;         // Could be anything
                        }
                        else
                        {
                            --lowest;
                        }
                    }
                    info.AlphaTolerance = lowest;
                }
            }
            break;
            }

            for (int i = 0; i < bitmaps.Count; i++)
            {
                bitmaps[i].UnlockBits(bitmapData[i]);
            }

            return(info);
        }