CSkin博客

标题: 【特效算法】3D翻转,矩形图片转梯形图片 [打印本页]

作者: 小红帽    时间: 2014-9-22 14:46
标题: 【特效算法】3D翻转,矩形图片转梯形图片
为了实现QQ的3D翻转特效,找了很多所谓的3D变换算法,大都是C++代码,一大堆,看的头晕。找到一个C#的,代码也是一大堆,重要的是不支持透明,而且速度特慢。算了,还是自己写一个吧。
专门为3D翻转而设计的算法,超精简!
只是没有抗锯齿,有没有高手能帮忙优化一下!

下面是图像变换算法,要实现3D翻转则就要对图像多次变换,并且呈现出来,那些代码就不贴了。

[C#] 纯文本查看 复制代码
        /// <summary>
        /// 矩形图片变换成梯形图片,用于QQ的3D翻转特效
        /// </summary>
        /// <param name="src">原图</param>
        /// <param name="compressH">左侧面或者右侧面纵向缩放的比例</param>
        /// <param name="compressW">横向缩放的比例</param>
        /// <param name="isLeft">是否是左侧面缩放</param>
        /// <param name="isCenter">是否水平居中</param>
        /// <returns></returns>
        public static Bitmap TrapezoidTransformation(Bitmap src, double compressH, double compressW, bool isLeft, bool isCenter)
        {
            Rectangle rect = new Rectangle(0, 0, src.Width, src.Height);
            using (Bitmap resultH = new Bitmap(rect.Width, rect.Height))
            {
                Bitmap resultW = new Bitmap(rect.Width, rect.Height);

                #region 指针算法,高速
                //LockBits将Bitmap锁定到内存中
                BitmapData srcData = src.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                BitmapData resultHData = resultH.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                BitmapData resultWData = resultW.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                unsafe
                {
                    //指向地址(分量顺序是BGRA)
                    byte* srcP = (byte*)srcData.Scan0;//原图地址
                    byte* resultHP = (byte*)resultHData.Scan0;//侧面压缩结果图像地址
                    byte* resultWP = (byte*)resultWData.Scan0;//纵向压缩
                    int dataW = srcData.Stride;//每行的数据量

                    double changeY = (1.0 - compressH) * rect.Height / 2 / rect.Width;//Y变化率

                    for (int y = 0; y < rect.Height; y++)
                    {
                        for (int x = 0; x < rect.Width; x++)
                        {
                            double h = 0;
                            double nH = 0;
                            if (isLeft && (y >= changeY * (rect.Width - x)))
                            {
                                h = rect.Height - 2.0 * changeY * (rect.Width - x);//变化之后的每竖像素高度
                                nH = y - (changeY * (rect.Width - x));//当前像素在变化之后的高度
                            }
                            else if (!isLeft && (y >= changeY * x))
                            {
                                h = rect.Height - 2.0 * changeY * x;//变化之后的每竖像素高度
                                nH = y - (changeY * x);//当前像素在变化之后的高度
                            }

                            double p = 1.0 * nH / h;//当前像素在变化之后的位置高度百分比
                            int nY = (int)(rect.Height * p);//变化之后像素在原图片中的Y轴位置

                            if (nY < rect.Height && nY > -1)
                            {
                                //result.SetPixel(x + offsetX, y, src.GetPixel(nX, nY));
                                byte* sp = srcP + nY * dataW + x * 4;//原图的像素偏移的数据位置

                                resultHP[0] = sp[0];
                                resultHP[1] = sp[1];
                                resultHP[2] = sp[2];
                                resultHP[3] = sp[3];
                            }

                            resultHP += 4;
                        } // x
                    } // y

                    resultHP = (byte*)resultHData.Scan0;//重置地址

                    //纵向压缩
                    int offsetX = 0;//居中偏移
                    if (isCenter)
                    {
                        offsetX = (int)((rect.Width - compressW * rect.Width) / 2);
                    }

                    for (int y = 0; y < rect.Height; y++)
                    {
                        for (int x = 0; x < rect.Width; x++)
                        {
                            int nX = (int)(1.0 * x / compressW);//纵向压缩后像素在原图片中的X轴位置
                            if (nX > -1 && nX < rect.Width)
                            {
                                //resultW.SetPixel(x, y, resultH.GetPixel(nX, y));
                                byte* hp = resultHP + nX * 4 + dataW * y;
                                byte* wp = resultWP + offsetX * 4;

                                wp[0] = hp[0];
                                wp[1] = hp[1];
                                wp[2] = hp[2];
                                wp[3] = hp[3];
                            }

                            resultWP += 4;
                        }
                    }


                    src.UnlockBits(srcData);//从内存中解除锁定
                    resultH.UnlockBits(resultHData);
                    resultW.UnlockBits(resultWData);

                }

                #endregion

                #region 位图像素设置,低速

                ////侧面压缩
                //double changeY = (1.0 - compressH) * rect.Height / 2 / rect.Width;//Y变化率

                //for (int y = 0; y < rect.Height; y++)
                //{
                //    for (int x = 0; x < rect.Width; x++)
                //    {
                //        double h = 0;
                //        double nH = 0;
                //        if (isLeft && (y >= changeY * (rect.Width - x)))
                //        {
                //            h = rect.Height - 2.0 * changeY * (rect.Width - x);//变化之后的每竖像素高度
                //            nH = y - (changeY * (rect.Width - x));//当前像素在变化之后的高度

                //        }
                //        else if (!isLeft && (y >= changeY * x))
                //        {
                //            h = rect.Height - 2.0 * changeY * x;//变化之后的每竖像素高度
                //            nH = y - (changeY * x);//当前像素在变化之后的高度
                //        }

                //        double p = 1.0 * nH / h;//当前像素在变化之后的位置高度百分比
                //        int nY = (int)(rect.Height * p);//变化之后像素在原图片中的Y轴位置

                //        if (nY < rect.Height && nY > -1)
                //        {
                //            resultH.SetPixel(x, y, src.GetPixel(x, nY));
                //        }
                //    } // x
                //} // y

                //// 纵向压缩
                //int offsetX = 0;
                //if (isCenter)
                //{
                //    offsetX = (int)((rect.Width - compressW * rect.Width) / 2);
                //}
                //for (int y = 0; y < rect.Height; y++)
                //{
                //    for (int x = 0; x < rect.Width; x++)
                //    {
                //        int nX = (int)(1.0 * x / compressW);//纵向压缩后像素在原图片中的X轴位置
                //        if (nX > -1 && nX < rect.Width && x + offsetX < rect.Width)
                //        {
                //            resultW.SetPixel(x + offsetX, y, resultH.GetPixel(nX, y));
                //        }
                //    }
                //}

                #endregion

                return resultW;
            }










作者: gharbor    时间: 2014-9-22 22:00
不错!!!!!!!!!!!!
作者: xiaobo    时间: 2014-9-22 22:24
智商不足,补充中。。。
作者: 乔克斯    时间: 2014-9-23 00:02
超棒~努力研究~
作者: Blue_Pen    时间: 2014-9-23 00:30
必须顶起,研究好久了,没结果。。。后来放弃了
作者: 原始    时间: 2014-10-21 13:46
研究中,有心得了就找你分享哈
作者: psetpsetpset    时间: 2015-2-5 20:51
借鉴一下。
作者: hi5438    时间: 2015-4-2 23:48
顶起 学习学习
作者: 黑水无涯    时间: 2015-6-2 19:18
厉害,效果太棒了。
作者: squeeze2009    时间: 2015-7-15 16:14
努力学习中,向楼主学习
作者: 残剑风雪    时间: 2015-10-19 10:59
瞬间萌比的举手...
作者: zixing    时间: 2016-6-12 10:31
图片转换算法挺复杂 的
作者: 宝贝老佛爷    时间: 2017-6-29 12:57
不错的例子
作者: maker316    时间: 2019-3-15 17:15
努力学习中,向楼主学习




欢迎光临 CSkin博客 (http://bbs.cskin.net/) Powered by Discuz! X3.2