作者: 小红帽
查看: 9107|回复: 13
打印 上一主题 下一主题

【特效算法】3D翻转,矩形图片转梯形图片

[复制链接]
跳转到指定楼层
楼主
小红帽 发表于 2014-9-22 14:46:23 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
查看: 9107|回复: 13
为了实现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;
            }









评分

参与人数 1金钱 +2 贡献 +1 收起 理由
xiaobo + 2 + 1 感谢分享,LZ辛苦了~

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 转播转播
回复 论坛版权

使用道具 举报

沙发
gharbor 发表于 2014-9-22 22:00:53 | 只看该作者
不错!!!!!!!!!!!!
回复

使用道具 举报

板凳
xiaobo 发表于 2014-9-22 22:24:54 | 只看该作者
智商不足,补充中。。。
地板
乔克斯 发表于 2014-9-23 00:02:15 | 只看该作者
超棒~努力研究~
5#
Blue_Pen 发表于 2014-9-23 00:30:57 来自手机 | 只看该作者
必须顶起,研究好久了,没结果。。。后来放弃了
6#
原始 发表于 2014-10-21 13:46:23 | 只看该作者
研究中,有心得了就找你分享哈
7#
psetpsetpset 发表于 2015-2-5 20:51:13 | 只看该作者
借鉴一下。
回复

使用道具 举报

8#
hi5438 发表于 2015-4-2 23:48:45 | 只看该作者
顶起 学习学习
9#
黑水无涯 发表于 2015-6-2 19:18:49 来自手机 | 只看该作者
厉害,效果太棒了。
10#
squeeze2009 发表于 2015-7-15 16:14:02 | 只看该作者
努力学习中,向楼主学习
您需要登录后才可以回帖 登录 | 加入CSkin博客

本版积分规则

QQ|申请友链|小黑屋|手机版|Archiver|CSkin ( 粤ICP备13070794号

Powered by Discuz! X3.2  © 2001-2013 Comsenz Inc.  Designed by ARTERY.cn
GMT+8, 2024-4-25 19:46, Processed in 0.709969 second(s), 31 queries , Gzip On.

快速回复 返回顶部 返回列表