.net如何根据不规则png图形裁剪图片

需要表述

项目中遇到一些不规则的图片作品需要合成,给一张不规则的png图片(比如圆形图片,中间是白色的圆形区域,外面是透明区域),需要将用白色区域裁剪用户图片并且要求效率不低于1s

解决方法

现采用.net中的Region 多边形区域采集和裁剪,并用到了win32的api:gdi32.dll

 

示意代码

 //区域采集函数

        public static Region ImageToRegion(Image AImage, Color ATransparent)
        {
            if (AImage == null) return null;
            Bitmap vBitmap = new Bitmap(AImage);
            BitmapData vBitmapData = vBitmap.LockBits(new Rectangle(0, 0, vBitmap.Width, vBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
            int vAddress = (int)vBitmapData.Scan0;
            int vOffset = vBitmapData.Stride - vBitmap.Width * 4; // 每行多出的字节数
            int h = vBitmap.Height, w = vBitmap.Width;
            int vTransparent = ColorTranslator.ToWin32(ATransparent); // 透明色
            int vAllocRect = (0x1000 - sizeof(uint) * 8) / sizeof(int); // 预分配的矩形数
            if (h * w < vAllocRect) vAllocRect = h * w;

            Byte[] vBuffer = new byte[sizeof(uint) * 8 + sizeof(int) * 4 * vAllocRect];
            //头信息dwSize\iType\nCount\nRegSize
            uint vCount = 0;
            vBuffer[0] = sizeof(uint) * 8; //dwSize//头信息大小
            vBuffer[4] = 1; //iType//int RDH_RECTANGLES = 1;//数据类型
            IntPtr vResult = IntPtr.Zero;

            uint vPointer = sizeof(uint) * 8;
            bool vWriteRect = false;
            bool vWriteAlways = false;

            for (int y = 0; y < h; y++)
            {
                int vBlockStart = 0;
                bool vLastMaskBit = false;
                for (int x = 0; x < w; x++)
                {
                    int i = Marshal.ReadInt32((IntPtr)vAddress) & 0x00FFFFFF;
                    if (i == 0) // 透明色 (黑色区域也会被识别成透明色)
                    {
                        if (vLastMaskBit)
                            vWriteRect = true;
                    }
                    else
                    {
                        if (!vLastMaskBit)
                        {
                            vBlockStart = x;
                            vLastMaskBit = true;
                        }
                    }

                    if (x == w - 1)
                    {
                        if (y == h - 1)
                        {
                            vWriteRect = true;
                            vWriteAlways = true;
                        }
                        else if (vLastMaskBit)
                        {
                            vWriteRect = true;
                        }
                        x++;
                    }

                    if (vWriteRect)
                    {
                        if (vLastMaskBit)
                        {
                            vCount++;
                            WriteRect(vBuffer, ref vPointer, new Rectangle(vBlockStart, y, x - vBlockStart, 1));
                        }

                        if (vCount == vAllocRect || vWriteAlways)
                        {
                            vBuffer[8] = (byte)vCount;
                            vBuffer[9] = (byte)(vCount >> 8);
                            vBuffer[10] = (byte)(vCount >> 16);
                            vBuffer[11] = (byte)(vCount >> 24);
                            IntPtr hTemp = ExtCreateRegion(IntPtr.Zero,
                                sizeof(uint) * 8 + sizeof(int) * 4 * vCount,
                                ref vBuffer[0]);
                            if (vResult == IntPtr.Zero)
                                vResult = hTemp;
                            else
                            {
                                CombineRgn(vResult, vResult, hTemp, RGN_OR);
                                DeleteObject(hTemp);
                            }
                            vCount = 0;
                            vPointer = sizeof(uint) * 4;
                            vWriteAlways = false;
                        }
                        vWriteRect = false;
                        vLastMaskBit = false;
                    }
                    vAddress += 4;
                }
                vAddress += vOffset;
            }

            vBitmap.UnlockBits(vBitmapData);
            return Region.FromHrgn(vResult);
        }

 


        //按指定的裁剪区域对图像进行裁剪
        public static Bitmap CutRegionImage(Bitmap b, Region region)
        {
            Graphics g = System.Drawing.Graphics.FromImage(b);

            // 获取区域边界
            RectangleF validRect = region.GetBounds(g);

            int x = (int)validRect.X;
            int y = (int)validRect.Y;
            int width = (int)validRect.Width;
            int height = (int)validRect.Height;

            // 对区域进行平移
            Region validRegion = region;
            validRegion.Translate(-x, -y);

            Bitmap dstImage = new Bitmap(width, height);
            Graphics dstGraphics = System.Drawing.Graphics.FromImage(dstImage);

            // 设置剪辑区域
            dstGraphics.SetClip(validRegion, CombineMode.Replace);

            // 绘图
            dstGraphics.DrawImage(b, new Rectangle(0, 0, width, height),
              validRect, GraphicsUnit.Pixel);

            g.Dispose();
            dstGraphics.Dispose();
            b.Dispose();

            return dstImage;
        }

 

    //调用win32 api的函数定义

        [DllImport("gdi32.dll")]
        public static extern IntPtr ExtCreateRegion(IntPtr lpXform, uint nCount,
            ref byte lpRgnData);

        [DllImport("gdi32.dll")]
        public static extern int CombineRgn(IntPtr hrgnDest, IntPtr hrgnSrc1, IntPtr hrgnSrc2,
            int fnCombineMode);

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

 

结果示意图

.net如何根据不规则png图形裁剪图片_第1张图片

.net如何根据不规则png图形裁剪图片_第2张图片

 

完整代码

/Files/ruimingde/PngImage.txt

 

你可能感兴趣的:(.net)