通通WPF随笔(3)——艺术二维码素材生成器

简介: 原文:通通WPF随笔(3)——艺术二维码素材生成器      最近公司让我开发一个条形码的生成控件,花了半天时间搞定觉得不过瘾,什么年代了该用二维码了吧。于是wiki了一下二维码的资料。   比较常见的就是QR码(Quick Response)即快速识别码,为了验证“快速”,我特地和条形码做了一次比较:经过我测试条形码的code 128编码方式可以表示数字、字母、和符号,而且长度也可以很长。
原文: 通通WPF随笔(3)——艺术二维码素材生成器

 


 

  最近公司让我开发一个条形码的生成控件,花了半天时间搞定觉得不过瘾,什么年代了该用二维码了吧。于是wiki了一下二维码的资料。

  比较常见的就是QR码(Quick Response)即快速识别码,为了验证“快速”,我特地和条形码做了一次比较:经过我测试条形码的code 128编码方式可以表示数字、字母、和符号,而且长度也可以很长。当我用“我查查”进行识别测试时发现,当长度达到20个字符时就很难识别出来了,速度也比较慢,也许是软件的原因吧。但二维码不同,其中包括了汉字等乱七八糟的一大堆东西,同样可以秒识。

  看了各种外国设计师的艺术二维码设计让我一发不可收拾。先来欣赏一下国外大师的设计吧:

这里选了几张个人比较喜欢,比较有代表性的。

   

1.总体思路


  这是一张普通的二维码:  

  

  我们可以把它看作由很多正方形组成的矩阵,每一个正方形就是矩阵中的一个节点。那3个大方框和右下角的小方框是用来定位二维码的方向位置的。

  先排除颜色贴图不管,那些艺术二维码的区别无非就是节点的形状不同而已,有方的、圆的、还有在方的边缘做了平滑处理的。如果能用这些节点生成最基本的黑白原型,用PS加上颜色和贴图简直就是轻而易举。所以我做了这个一个快速原型工具,如下图:

通过修改左边的参数就可以实现各种效果:

 改变节点大小

使用随机节点大小和随机颜色

圆形节点

圆形随机大小、随机颜色

自定义形状节点

  生成各种素材,经过PS的拼装处理,基本可以实现任何效果了。

(有些比较难识别,是因为比较难找到作为位置定位的那几个方框,所以还得PS处理时,要把那几个方框做得明显些)

 关于方形边缘平滑处理效果,由于用PS比较容易实现,就懒得研究代码了,录制个PS动作更快些。如下:

这个是女朋友生日那天临时设计的。 

   

   

 

2.详细设计


  

  关于二维码的生成这里我用了一个开源项目QrCode.Nethttp://qrcodenet.codeplex.com/)其实这个工程里面已经自带了WPF的控件,可以生成最基本的正方形节点二维码,且有一个属性是Path的材质,这样生成普通的二维码已经方便了,但是不能满足我们多样化的需求。

  所以,我只用到了他生成一个二维数组来表示二维码的算法,二维数组的元素只有0和1,其中0表示没有黑块,1表示有,这样只要根据自定义路径的大小计算出每个块的位置,用代码布局一下就出来了。

  我自己写了一个可以用任意形状路径填充节点的类,并增加随机节点大小和随机颜色功能,而且修复了它的一些Bug,这样即满足了多样化的需求,而且整个二维码都是用路径来表示,换句话说就是矢量图,这样就可以生成任意大小的图片,满足各种用途。

  原本的这个开源项目可以导出.eps矢量格式,可供Adobe Illustrator 使用,而Adobe Illustrator的标准格式.AI,可以被Expression Design 4导入,Expression Design 4又可以转换为XAML文件,也就是说这么多软件都可以用来加工我们的二维码原型,这样就增加了设计的可能性。但是当我看完它转换的实现源码时,我愣住了,完全由作者自己写的代码,只是在做字符串的拼接,形成.eps的格式,而且只考虑到了直线路径...所以我就只能将矢量图输出为位图来保存(这里保存为.png格式,因为png是无损压缩格式,作为中介不错)通过PS加工,好在分辨率基本可以满足大部分需求了。为什么Expression Design 4不能导入XAML格式啊....

   由于代码比较多,思路我上面已经说明了,只是编码的问题了,这里我就贴出源码,感兴趣的可以自己研究一下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Gma.QrCodeNet.Encoding;
using Gma.QrCodeNet.Encoding.Windows.Render;
using System.Windows;
using System.Windows.Controls;

namespace QRCodeFactory
{
    static class PathRender
    {
        /// <summary>
        /// 以矩形来填充矩阵点
        /// </summary>
        /// <param name="QrMatrix">算出来的矩阵</param>
        /// <param name="xScale">isRandom?随机取值的最小值:每个点的宽度</param>
        /// <param name="yScale">isRandom?随机取值的最大值:每个点的高度</param>
        /// <param name="isRandom">是否随机大小</param>
        /// <returns>返回路径填充材质</returns>
        public static StreamGeometry DrawRectGeometry(BitMatrix QrMatrix, double xScale, double yScale, bool isRandom)
        {
            int width = QrMatrix == null ? 21 : QrMatrix.Width;

            StreamGeometry qrCodeStream = new StreamGeometry();
            qrCodeStream.FillRule = FillRule.EvenOdd;

            if (QrMatrix == null)
                return qrCodeStream;
           

            using (StreamGeometryContext qrCodeCtx = qrCodeStream.Open())
            {
                for (int y = 0; y < width; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (QrMatrix[x, y])
                        {
                            if (isRandom)
                                qrCodeCtx.DrawRectGeometry(x, y, (double)(new Random(x + y + Environment.TickCount).Next((int)(xScale * 100), (int)(yScale * 100))) / 100, (double)(new Random(x + y + Environment.TickCount).Next((int)(xScale * 100), (int)(yScale * 100))) / 100);
                            else
                                qrCodeCtx.DrawRectGeometry(x, y, xScale, yScale);
                        }
                    }
                }
            }

            qrCodeStream.Freeze();

            return qrCodeStream;
        }

        /// <summary>
        /// 以圆点来填充矩阵点
        /// </summary>
        /// <param name="QrMatrix">算出来的矩阵</param>
        /// <param name="xScale">isRandom?随机取值的最小值:每个点的宽度</param>
        /// <param name="yScale">isRandom?随机取值的最大值:每个点的高度</param>
        /// <param name="isRandom">是否随机大小</param>
        /// <returns>返回路径填充材质</returns>
        public static StreamGeometry DrawEllipseGeometry(BitMatrix QrMatrix, double xScale, double yScale, bool isRandom)
        {
            int width = QrMatrix == null ? 21 : QrMatrix.Width;

            StreamGeometry qrCodeStream = new StreamGeometry();
            qrCodeStream.FillRule = FillRule.EvenOdd;

            if (QrMatrix == null)
                return qrCodeStream;

            using (StreamGeometryContext qrCodeCtx = qrCodeStream.Open())
            {
                for (int y = 0; y < width; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        if (QrMatrix[x, y])
                        {
                            if (isRandom)
                                qrCodeCtx.DrawEllipseGeometry(x, y, (double)(new Random(x + y + Environment.TickCount).Next((int)(xScale * 100), (int)(yScale * 100))) / 100, (double)(new Random(x + y + Environment.TickCount).Next((int)(xScale * 100), (int)(yScale * 100))) / 100);
                            else
                                qrCodeCtx.DrawEllipseGeometry(x, y, xScale, yScale);
                        }
                    }
                }
            }


            qrCodeStream.Freeze();

            return qrCodeStream;
        }

        /// <summary>
        /// 以自定义图形来填充矩阵点
        /// </summary>
        /// <param name="QrMatrix">算出来的矩阵</param>
        /// <param name="xScale">isRandom?随机取值的最小值:每个点的宽度</param>
        /// <param name="yScale">isRandom?随机取值的最大值:每个点的高度</param>
        /// <param name="isRandomSize">是否随机大小</param>
        /// <returns>返回路径填充材质</returns>
        public static void DrawCustomGeometry(BitMatrix QrMatrix, ref Grid drawGrid, Path pathGeo, double xScale, double yScale, bool isRandomSize,bool isRandomColor)
        {
            int width = QrMatrix == null ? 21 : QrMatrix.Width;
            drawGrid.Width = drawGrid.Height = width * pathGeo.Width;
            for (int y = 0; y < width; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (QrMatrix[x, y])
                    {
                        Path newPath = new Path();//创建一个路径,代表一点
                        newPath.StrokeThickness = 0;
                        newPath.Stretch = Stretch.UniformToFill;//填充方式s
                        newPath.HorizontalAlignment = HorizontalAlignment.Left;
                        newPath.VerticalAlignment = VerticalAlignment.Top;
                        newPath.Data = pathGeo.Data;
                        newPath.RenderTransformOrigin = new Point(0.5, 0.5);
                        TranslateTransform newTTF = new TranslateTransform(x * pathGeo.Width, y * pathGeo.Height);
                        newPath.RenderTransform = newTTF;
                        if (isRandomSize)//如果随机大小
                        {
                            newPath.Width = newPath.Height = pathGeo.Width * (double)(new Random(x + y + Environment.TickCount).Next((int)(xScale * 100), (int)(yScale * 100))) / 100;
                        }
                        else
                        {
                            newPath.Width = pathGeo.Width * xScale;
                            newPath.Height = pathGeo.Height * yScale;
                        }
                        if (isRandomColor)//如果随机颜色
                            newPath.Fill = new SolidColorBrush(GetRandomColor());
                        else
                            newPath.Fill = Brushes.Black;
                        

                        drawGrid.Children.Add(newPath);
                    }
                }
            }


        }

        internal static void DrawRectGeometry(this StreamGeometryContext ctx, double X, double Y, double Width, double Height)
        {
            ctx.BeginFigure(new Point(X, Y),true, true);
            ctx.LineTo(new Point(X, Y + Height), true, true);
            ctx.LineTo(new Point(X + Width, Y + Height), true, true);
            ctx.LineTo(new Point(X + Width, Y), true, true);
        }

        internal static void DrawEllipseGeometry(this StreamGeometryContext ctx, double X, double Y, double Width, double Height)
        {
            X = X * 2;
            Y = Y * 2;
            Height = Height * 2;
            Width = Width * 2;

            ctx.BeginFigure(new Point(X, Y + Height / 2), true, true);
            ctx.ArcTo(new Point(X + Width, Y + Height / 2), new Size(Width / 2, Height / 2), 90, false, SweepDirection.Clockwise, true, true);
            ctx.ArcTo(new Point(X, Y + Height / 2), new Size(Width / 2, Height / 2), 90, false, SweepDirection.Clockwise, true, true);

        }

        public static Color GetRandomColor()
        {
            Random randomNum_1 = new Random(Guid.NewGuid().GetHashCode());
            System.Threading.Thread.Sleep(randomNum_1.Next(1));
            int int_Red = randomNum_1.Next(255);

            Random randomNum_2 = new Random((int)DateTime.Now.Ticks);
            int int_Green = randomNum_2.Next(255);

            Random randomNum_3 = new Random(Guid.NewGuid().GetHashCode());

            int int_Blue = randomNum_3.Next(255);
            int_Blue = (int_Red + int_Green > 380) ? int_Red + int_Green - 380 : int_Blue;
            int_Blue = (int_Blue > 255) ? 255 : int_Blue;


            return GetDarkerColor(Color.FromArgb(Convert.ToByte(255),Convert.ToByte(int_Red), Convert.ToByte(int_Green), Convert.ToByte(int_Blue)));
        }

        //获取加深颜色
        public static Color GetDarkerColor(Color color)
        {
            const int max = 255;
            int increase = new Random(Guid.NewGuid().GetHashCode()).Next(30, 255); //还可以根据需要调整此处的值


            int r = Math.Abs(Math.Min(color.R - increase, max));
            int g = Math.Abs(Math.Min(color.G - increase, max));
            int b = Math.Abs(Math.Min(color.B - increase, max));


            return Color.FromArgb(Convert.ToByte(255), Convert.ToByte(r), Convert.ToByte(g), Convert.ToByte(b));
        }
    }
}
View Code

 

  下面欣赏一下我心血来潮时设计的:

 

 下载地址:http://files.cnblogs.com/tong-tong/TTQRCodeFactory.zip

 

后记


  

    自从本人淘宝女装店倒闭以后就一直不甘心,本来想开家店卖下二维码的设计赚点饭钱,结果事太多了,愣是没有时间来装修店铺...各位博友如果有二维码的设计需求的话也可以找我哦,价格公道,只收饭钱~~~~

 

 

 

 

  

目录
相关文章
|
前端开发 开发工具
Emgu-WPF学习使用-识别二维码的位置
原文:Emgu-WPF学习使用-识别二维码的位置    参考链接:http://blog.csdn.net/gaobobo138968/article/details/47663607    我完全参照该链接实现了二维码的位置锁定,向原作者致敬。
869 0
|
数据库 C# 索引
通通WPF随笔(1)——基于lucene.NET让ComboBox拥有强大的下拉联想功能
原文:通通WPF随笔(1)——基于lucene.NET让ComboBox拥有强大的下拉联想功能       我一直很疑惑百度、谷哥搜索框的下拉联想功能是怎么实现的?是不断地查询数据库吗?其实到现在我也不知道,他们是怎么实现这么高效的。
1349 0
|
.NET C# C++
通通WPF随笔(2)——自己制作轻量级asp.net网站服务
原文:通通WPF随笔(2)——自己制作轻量级asp.net网站服务          大学玩asp.net时就发现VS在Debug时会起一个web服务,这东西也太神奇了服务起得这么快,而相对于IIS又这么渺小。
1272 0
|
文字识别 C# Windows
通通WPF随笔(4)——通通手写输入法(基于Tablet pc实现)
原文:通通WPF随笔(4)——通通手写输入法(基于Tablet pc实现)          从我在博客园写第一篇博客到现在已经有1年半了,我的第一篇博客写的就是手写识别,当时,客户需求在应用中加入手写输入功能,由于第三方的手写输入法都无法定制界面,所以领导决定自主开发,所以我就很简单地基于Tablet pc写了一个WPF控件,由于几个瓶颈问题,导致这个输入功能只能在我们的UI框架里使用,而无法做到像输入法那样可以输入到任意窗口。
2122 0
|
1月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
|
5月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
66 1
|
9月前
|
C# Windows
WPF技术之图形系列Polygon控件
WPF Polygon是Windows Presentation Foundation (WPF)框架中的一个标记元素,用于绘制多边形形状。它可以通过设置多个点的坐标来定义多边形的形状,可以绘制任意复杂度的多边形。
480 0
|
9月前
|
C# Windows
WPF技术之RichTextBox控件
WPF RichTextBox是Windows Presentation Foundation (WPF)中提供的一个强大的文本编辑控件,它可以显示富文本格式的文本,支持多种文本处理操作。
355 0
|
5月前
|
前端开发 C# 容器
浅谈WPF之控件拖拽与拖动
使用过office的visio软件画图的小伙伴都知道,画图软件分为两部分,左侧图形库,存放各种图标,右侧是一个画布,将左侧图形库的图标控件拖拽到右侧画布,就会生成一个新的控件,并且可以自由拖动。那如何在WPF程序中,实现类似的功能呢?今天就以一个简单的小例子,简述如何在WPF中实现控件的拖拽和拖动,仅供学习分享使用,如有不足之处,还请指正。
116 2
|
9月前
|
数据挖掘 数据处理 C#
WPF技术之DataGrid控件
WPF DataGrid是一种可以显示和编辑数据的界面控件。它可以作为表格形式展示数据,支持添加、删除、修改、排序和分组操作。
189 0