zoukankan      html  css  js  c++  java
  • IM即时通讯群组头像拼接.net core 解决方案

    一、需求概述

    多人聊天(群组,讨论组,聊天室,以下统称: “群组” )生成一个拼接头像,需要把最先加入群组的几个人(最多4个人,以下简称:头部用户A、B、C、D)的头像拼凑成在一起。

    群组创建后,A、B、C、D其中任何一个修改了自己的头像,需要 "异步" 更新群组头像。

    以上是简单的需求描述。

    本文使用.net core实现了N张图片拼接算法。

    完整代码点击:https://github.com/night-king/ImageMerge

    二、方案实现探讨

    本需求可以在服务端实现,也可以在客户端实现。

    (1)服务端实现:

             群组创建时,下载A、B、C、D的头像,然后合并即可;

             头部用户修改头像后,后台任务刷新群组头像。

    (2)客户端实现:客户端包括iOS,Android,React,Web。

             情况有些复杂,所以最后决定直接采用服务端实现。

     Github上搜索无果,只能自己动手完成了。

    先看效果:

    采用四位Github大神头像作为源图片:

        

    结果如下:

    三、解决方案

    群组2个用户,3个用户,4个以及以上用户头像不一样,总结如下:

        /// <summary>
        /// 合并布局枚举
        /// </summary>
        public enum Merge2LayoutEnum
        {
            /// <summary>
            /// 2张图片,上下各1个长方形
            ///  ———————————————————
            /// |                  |
            /// |        R1        |
            /// |                  |
            ///  ———————————————————
            /// |                  |
            /// |        R2        |
            /// |                  |
            ///  ———————————————————
            /// </summary>
            Merge2R1 = 1,
    
            /// <summary>
            /// 2张图片,左右各1个长方形
            ///  ———————————————————
            /// |         |        |
            /// |         |        |
            /// |         |        |
            /// |    R1   |   R2   |
            /// |         |        |
            /// |         |        |
            /// |         |        |
            ///  ———————————————————
            /// </summary>
            Merge2R2 = 2
        }
    
        public enum Merge3LayoutEnum
        {
    
            /// <summary>
            /// 3张图片, 上面一个长方形,下面2个正方形并排
            ///  ———————————————————
            /// |                  |
            /// |         R1       |
            /// |                  |
            ///  ———————————————————
            /// |         |        |
            /// |    S1   |   S2   |
            /// |         |        |
            ///  ———————————————————
            /// </summary>
            Merge1R2S1 = 3,
    
            /// <summary>
            /// 3张图片,上面2个正方形并排,下面一个长方形
            ///  ———————————————————
            /// |         |        |
            /// |    S1   |   S2   |
            /// |         |        |
            ///  ———————————————————
            /// |                  |
            /// |         R1       |
            /// |                  |
            /// ———————————————————
            /// </summary>
            Merge1R2S2 = 4,
    
    
            /// <summary>
            /// 3张图片,上面2个正方形并排,下面一个长方形
            ///  ———————————————————
            /// |         |        |
            /// |         |    S1  |
            /// |         |        |
            /// |    R1   |—————————
            /// |         |        |
            /// |         |    S2  |
            /// |         |        |
            ///  ———————————————————
            /// </summary>
            Merge1R2S3 = 5,
    
    
            /// <summary>
            /// 3张图片,上面2个正方形并排,下面一个长方形
            ///  ———————————————————
            /// |         |        |
            /// |    S2   |        |
            /// |         |        |
            /// |—————————|    R1  |
            /// |         |        |
            /// |    S2   |        |
            /// |         |        |
            ///  ———————————————————
            /// </summary>
            Merge1R2S4 = 6
        }
    
        public enum Merge4LayoutEnum
        {
            /// <summary>
            /// 4张图片,上面一个长方形,下面2个正方形并排
            ///  ———————————————————
            /// |         |        |
            /// |    S1   |   S2   |
            /// |         |        |
            ///  ———————————————————
            /// |         |        |
            /// |    S3   |   S4   |
            /// |         |        |
            ///  ———————————————————
            /// </summary>
            Merge4S = 7
    
        }

    以下是2张图片实现代码:

            /// <summary>
            /// 合并2张图片
            /// </summary>
            /// <param name="image1"></param>
            /// <param name="image2"></param>
            /// <param name="layout"></param>
            /// <returns></returns>
            public static Image Merge2Images(Image image1, Image image2, Merge2LayoutEnum layout = Merge2LayoutEnum.Merge2R1, int size = 250)
            {
                var width = size;
                var height = size;
                var pf = PixelFormat.Format32bppArgb;
                using (var bg = new Bitmap(width, height, pf))
                {
                    using (var g = Graphics.FromImage(bg))
                    {
                        g.FillRectangle((Brush)Brushes.White, 0, 0, width, height);//全幅背景为白色
                        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                        switch (layout)
                        {
                            /// <summary>
                            /// 2张图片,上下各1个长方形
                            ///  ———————————————————
                            /// |                  |
                            /// |        R1        |
                            /// |                  |
                            ///  ———————————————————
                            /// |                  |
                            /// |        R2        |
                            /// |                  |
                            ///  ———————————————————
                            /// </summary>
                            case Merge2LayoutEnum.Merge2R1:
                                {
                                    using (var img1 = ZoomToSqure(image1, size))
                                    {
                                        var newHeight = height / 2;// =125
                                        var newWidth = width;//       =250
                                        var srcWidth = img1.Width;//  =250
                                        var srcHeight = img1.Height * newHeight / newWidth;//=250*125/250=125
                                        g.DrawImage(img1, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img2 = ZoomToSqure(image2, size))
                                    {
                                        var newHeight = height / 2;// =125
                                        var newWidth = width;     //  =250
                                        var srcWidth = img2.Width;//  =250
                                        var srcHeight = img2.Height * newHeight / newWidth;//=250*125/250=125
                                        g.DrawImage(img2, new Rectangle(0, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                }
                                break;
                            /// <summary>
                            /// 2张图片,左右各1个长方形
                            ///  ———————————————————
                            /// |         |        |
                            /// |         |        |
                            /// |         |        |
                            /// |    R1   |   R2   |
                            /// |         |        |
                            /// |         |        |
                            /// |         |        |
                            ///  ———————————————————
                            /// </summary>
                            case Merge2LayoutEnum.Merge2R2:
                                {
                                    using (var img1 = ZoomToSqure(image1, size))
                                    {
                                        var newWidth = width / 2;//   =125
                                        var newHeight = height;  //   =250
                                        var srcWidth = img1.Width * newWidth / newHeight;//=250*125/250=125
                                        var srcHeight = img1.Height;//  =250
                                        g.DrawImage(img1, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img2 = ZoomToSqure(image2, size))
                                    {
                                        var newWidth = width / 2;//   =125
                                        var newHeight = height;  //   =250
                                        var srcWidth = img2.Width * newWidth / newHeight;//=500*125/250=250
                                        var srcHeight = img2.Height;//  =500
                                        g.DrawImage(img2, new Rectangle(width / 2, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                }
                                break;
                        }
                        g.Save();
                        image1.Dispose();
                        image2.Dispose();
                    }
    
                    using (var ms = new MemoryStream())
                    {
                        bg.Save(ms, ImageFormat.Png);
                        var buffers = ms.ToArray();
                        return ConvertToImage(buffers);
                    }
                }
            }
    View Code

    以下是三种图片的生成算法

            /// <summary>
            /// 合并3张图片
            /// </summary>
            /// <param name="image1"></param>
            /// <param name="image2"></param>
            /// <param name="image3"></param>
            /// <param name="layout"></param>
            /// <returns></returns>
            public static Image Merge3Images(Image image1, Image image2, Image image3, Merge3LayoutEnum layout = Merge3LayoutEnum.Merge1R2S1, int size = 250)
            {
                var width = size;
                var height = size;
                var pf = PixelFormat.Format32bppArgb;
                using (var bg = new Bitmap(width, height, pf))
                {
                    using (var g = Graphics.FromImage(bg))
                    {
                        g.FillRectangle((Brush)Brushes.White, 0, 0, width, height);//全幅背景为白色
                        switch (layout)
                        {
                            /// <summary>
                            /// 3张图片, 上面一个长方形,下面2个正方形并排
                            ///  ———————————————————
                            /// |                  |
                            /// |         R1       |
                            /// |                  |
                            ///  ———————————————————
                            /// |         |        |
                            /// |    S1   |   S2   |
                            /// |         |        |
                            ///  ———————————————————
                            /// </summary>
                            case Merge3LayoutEnum.Merge1R2S1:
                                {
                                    using (var img1 = ZoomToSqure(image1, size))
                                    {
                                        var newHeight = height / 2;// =125
                                        var newWidth = width;//       =250
                                        var srcWidth = img1.Width;//  =250
                                        var srcHeight = img1.Height * newHeight / newWidth;//=250*125/250=125
                                        g.DrawImage(img1, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img2 = ZoomToSqure(image2, size))
                                    {
                                        var newHeight = height / 2;//  =125
                                        var newWidth = width / 2;  //  =125
                                        var srcWidth = img2.Width; //  =250
                                        var srcHeight = img2.Height;// =250
                                        g.DrawImage(img2, new Rectangle(0, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img3 = ZoomToSqure(image3, size))
                                    {
                                        var newHeight = height / 2; //  =125
                                        var newWidth = width / 2;   //  =125
                                        var srcWidth = img3.Width; //   =250
                                        var srcHeight = img3.Height;//  =250
                                        g.DrawImage(img3, new Rectangle(width / 2, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                }
                                break;
                            /// <summary>
                            /// 3张图片,上面2个正方形并排,下面一个长方形
                            ///  ———————————————————
                            /// |         |        |
                            /// |    S1   |   S2   |
                            /// |         |        |
                            ///  ———————————————————
                            /// |                  |
                            /// |         R1       |
                            /// |                  |
                            /// ———————————————————
                            /// </summary>
                            case Merge3LayoutEnum.Merge1R2S2:
                                {
                                    using (var img1 = ZoomToSqure(image1, size))
                                    {
                                        var newHeight = height / 2;//  =125
                                        var newWidth = width / 2;  //  =125
                                        var srcWidth = img1.Width; //  =250
                                        var srcHeight = img1.Height;// =250
                                        g.DrawImage(img1, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img2 = ZoomToSqure(image2, size))
                                    {
                                        var newHeight = height / 2; //  =125
                                        var newWidth = width / 2;   //  =125
                                        var srcWidth = img2.Width; //   =250
                                        var srcHeight = img2.Height;//  =250
                                        g.DrawImage(img2, new Rectangle(width / 2, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img3 = ZoomToSqure(image3, size))
                                    {
                                        var newHeight = height / 2;// =125
                                        var newWidth = width;//       =250
                                        var srcWidth = img3.Width;//  =250
                                        var srcHeight = img3.Height * newHeight / newWidth;//=250*125/250=125
                                        g.DrawImage(img3, new Rectangle(0, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                }
                                break;
                            /// <summary>
                            /// 3张图片,上面2个正方形并排,下面一个长方形
                            ///  ———————————————————
                            /// |         |        |
                            /// |         |    S1  |
                            /// |         |        |
                            /// |    R1   |—————————
                            /// |         |        |
                            /// |         |    S2  |
                            /// |         |        |
                            ///  ———————————————————
                            /// </summary>
                            case Merge3LayoutEnum.Merge1R2S3:
                                {
                                    using (var img1 = ZoomToSqure(image1, size))
                                    {
                                        var newHeight = height;//     =250
                                        var newWidth = width / 2;//   =125
                                        var srcHeight = img1.Height;//=250
                                        var srcWidth = img1.Width * newWidth / newHeight;//  =250*125/250=125;
                                        g.DrawImage(img1, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img2 = ZoomToSqure(image2, size))
                                    {
                                        var newHeight = height / 2;//  =125
                                        var newWidth = width / 2;  //  =125
                                        var srcWidth = img2.Width; //  =250
                                        var srcHeight = img2.Height;// = 250
                                        g.DrawImage(img2, new Rectangle(width / 2, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img3 = ZoomToSqure(image3, size))
                                    {
                                        var newHeight = height / 2; //  =125
                                        var newWidth = width / 2;   //  =125
                                        var srcWidth = img3.Width; //   =250
                                        var srcHeight = img3.Height;//  =250
                                        g.DrawImage(img3, new Rectangle(width / 2, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                }
                                break;
                            /// <summary>
                            /// 3张图片,上面2个正方形并排,下面一个长方形
                            ///  ———————————————————
                            /// |         |        |
                            /// |    S2   |        |
                            /// |         |        |
                            /// |—————————|    R1  |
                            /// |         |        |
                            /// |    S2   |        |
                            /// |         |        |
                            ///  ———————————————————
                            /// </summary>
                            case Merge3LayoutEnum.Merge1R2S4:
                                {
                                    using (var img1 = ZoomToSqure(image1, size))
                                    {
                                        var newHeight = height / 2;//  =125
                                        var newWidth = width / 2;  //  =125
                                        var srcWidth = img1.Width; //  =250
                                        var srcHeight = img1.Height;// =250
                                        g.DrawImage(img1, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img2 = ZoomToSqure(image2, size))
                                    {
                                        var newHeight = height / 2; //  =125
                                        var newWidth = width / 2;   //  =125
                                        var srcWidth = img2.Width; //   =250
                                        var srcHeight = img2.Height;//  =250
                                        g.DrawImage(img2, new Rectangle(0, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img3 = ZoomToSqure(image3, size))
                                    {
                                        var newHeight = height;//     =250
                                        var newWidth = width / 2;//   =125
                                        var srcHeight = img3.Height;//=250
                                        var srcWidth = img3.Width * newWidth / newHeight;//  =250*125/250=125;
                                        g.DrawImage(img3, new Rectangle(width / 2, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                }
                                break;
                        }
                        g.Save();
                        image1.Dispose();
                        image2.Dispose();
                        image3.Dispose();
                    }
                    using (var ms = new MemoryStream())
                    {
                        bg.Save(ms, ImageFormat.Png);
                        var buffers = ms.ToArray();
                        return ConvertToImage(buffers);
                    }
                }
            }
    View Code

    以下是4张图片的生成是算法:

            /// <summary>
            /// 合并4张图片
            /// </summary>
            /// <param name="image1"></param>
            /// <param name="image2"></param>
            /// <param name="image3"></param>
            /// <param name="image4"></param>
            /// <param name="layout"></param>
            /// <returns></returns>
            public static Image Merge4Images(Image image1, Image image2, Image image3, Image image4, Merge4LayoutEnum layout = Merge4LayoutEnum.Merge4S, int size = 250)
            {
                var width = size;
                var height = size;
                var pf = PixelFormat.Format32bppArgb;
                using (var bg = new Bitmap(width, height, pf))
                {
                    using (var g = Graphics.FromImage(bg))
                    {
                        g.FillRectangle((Brush)Brushes.White, 0, 0, width, height);//全幅背景为白色
                        switch (layout)
                        {
                            /// <summary>
                            /// 4张图片,上面一个长方形,下面2个正方形并排
                            ///  ———————————————————
                            /// |         |        |
                            /// |    S1   |   S2   |
                            /// |         |        |
                            ///  ———————————————————
                            /// |         |        |
                            /// |    S3   |   S4   |
                            /// |         |        |
                            ///  ———————————————————
                            /// </summary>
                            case Merge4LayoutEnum.Merge4S:
                                {
                                    using (var img1 = ZoomToSqure(image1, size))
                                    {
                                        var newHeight = height / 2;//  =125
                                        var newWidth = width / 2;  //  =125
                                        var srcWidth = img1.Width; //  =250
                                        var srcHeight = img1.Height;// =250
                                        g.DrawImage(img1, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img2 = ZoomToSqure(image2, size))
                                    {
                                        var newHeight = height / 2; //  =125
                                        var newWidth = width / 2;   //  =125
                                        var srcWidth = img2.Width; //   =250
                                        var srcHeight = img2.Height;//  =250
                                        g.DrawImage(img2, new Rectangle(width / 2, 0, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img3 = ZoomToSqure(image3, size))
                                    {
                                        var newHeight = height / 2; //  =125
                                        var newWidth = width / 2;   //  =125
                                        var srcWidth = img3.Width; //   =250
                                        var srcHeight = img3.Height;//  =250
                                        g.DrawImage(img3, new Rectangle(0, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                    using (var img4 = ZoomToSqure(image4, size))
                                    {
                                        var newHeight = height / 2; //  =125
                                        var newWidth = width / 2;   //  =125
                                        var srcWidth = img4.Width; //   =250
                                        var srcHeight = img4.Height;//  =250
                                        g.DrawImage(img4, new Rectangle(width / 2, height / 2, newWidth, newHeight), new Rectangle(0, 0, srcWidth, srcHeight), GraphicsUnit.Pixel);
                                    }
                                }
                                break;
                        }
                        g.Save();
                        image1.Dispose();
                        image2.Dispose();
                        image3.Dispose();
                        image4.Dispose();
                    }
                    using (var ms = new MemoryStream())
                    {
                        bg.Save(ms, ImageFormat.Png);
                        var buffers = ms.ToArray();
                        return ConvertToImage(buffers);
                    }
                }
            }
    View Code

    以下是用到的Helper方法:

            /// <summary>
            /// 下载图片
            /// </summary>
            /// <param name="imageUrl"></param>
            /// <returns></returns>
            public static byte[] Download(string imageUrl)
            {
                if (imageUrl.StartsWith("http"))
                {
                    using (var ms = new MemoryStream())
                    {
                        var request = (HttpWebRequest)HttpWebRequest.Create(imageUrl);// 打开网络连接
                        using (var rs = request.GetResponse().GetResponseStream())// 向服务器请求,获得服务器的回应数据流
                        {
                            byte[] btArray = new byte[512];// 定义一个字节数据,用来向readStream读取内容和向writeStream写入内容
                            int size = rs.Read(btArray, 0, btArray.Length);// 向远程文件读第一次
    
                            while (size > 0)// 如果读取长度大于零则继续读
                            {
                                ms.Write(btArray, 0, size);// 写入本地文件
                                size = rs.Read(btArray, 0, btArray.Length);// 继续向远程文件读取
                            }
                            return ms.ToArray();
                        }
                    }
                }
                else
                {
                    using (var ms = new MemoryStream())
                    {
                        var img = Image.FromFile(imageUrl);
                        img.Save(ms, img.RawFormat);
                        return ms.ToArray();
                    }
                }
            }
    
            /// <summary>
            /// 将byte数组转化为Image
            /// </summary>
            /// <param name="buffer"></param>
            /// <returns></returns>
            public static Image ConvertToImage(byte[] buffer)
            {
                using (MemoryStream ms = new MemoryStream(buffer))
                {
                    return Image.FromStream(ms);
                }
            }
    
            /// <summary>
            /// 将Image转化为byte数组
            /// </summary>
            /// <param name="image"></param>
            /// <returns></returns>
            public static byte[] ConvertToByte(Image image)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    image.Save(ms, image.RawFormat);
                    return ms.ToArray();
                }
            }
    
    
            /// <summary>
            /// 等比缩放/放大成正方形图片
            /// </summary>
            /// <param name="orginal">原始图片</param>
            /// <param name="size">目标宽高</param>
            /// <param name="cute">超出部分是否剪裁</param>
            /// <returns></returns>
            public static Image ZoomToSqure(Image orginal, int size, bool cute = true)
            {
                var width = 0d;//图片宽度
                var height = 0d;//图片高度
                if (orginal.Width > orginal.Height)//原始图片宽度大于高度
                {
                    height = size;
                    width = cute ? size : (orginal.Width * height / orginal.Height);
                }
                else if (orginal.Width < orginal.Height)//原始图片高度大于宽度
                {
                    width = size;
                    height = cute ? size : (orginal.Height * width / orginal.Width);
                }
                else//原始图片是正方形,刚好
                {
                    width = size;
                    height = size;
                }
                var board = new Bitmap((int)width, (int)height);
                using (var g = Graphics.FromImage(board))
                {
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;//设置质量
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;//设置质量
                    g.Clear(Color.White);//置背景色
                    g.DrawImage(orginal, new Rectangle(0, 0, board.Width, board.Height), new Rectangle(0, 0, orginal.Width, orginal.Height), System.Drawing.GraphicsUnit.Pixel);  //画图
                    orginal.Dispose();//释放原图
                    return board;
                }
            }
    View Code

    完整代码点击:https://github.com/night-king/ImageMerge

             

  • 相关阅读:
    易用性问题回复
    阅读心得2:《余额宝技术架构及演进 》
    假期周进度报告8
    假期周进步报告7
    假期周进度报告6
    假期周进度报告5
    假期周进度报告4
    假期周进度报告3
    JAVA中SSH框架
    一张图说明CDN网络的原理
  • 原文地址:https://www.cnblogs.com/deepleo/p/ImageMerge.html
Copyright © 2011-2022 走看看