天天看點

通通WPF随筆(3)——藝術二維碼素材生成器

原文: 通通WPF随筆(3)——藝術二維碼素材生成器

  最近公司讓我開發一個條形碼的生成控件,花了半天時間搞定覺得不過瘾,什麼年代了該用二維碼了吧。于是wiki了一下二維碼的資料。

  比較常見的就是QR碼(Quick Response)即快速識别碼,為了驗證“快速”,我特地和條形碼做了一次比較:經過我測試條形碼的code 128編碼方式可以表示數字、字母、和符号,而且長度也可以很長。當我用“我查查”進行識别測試時發現,當長度達到20個字元時就很難識别出來了,速度也比較慢,也許是軟體的原因吧。但二維碼不同,其中包括了漢字等亂七八糟的一大堆東西,同樣可以秒識。

  看了各種外國設計師的藝術二維碼設計讓我一發不可收拾。先來欣賞一下國外大師的設計吧:

通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器

這裡選了幾張個人比較喜歡,比較有代表性的。

   

1.總體思路

  這是一張普通的二維碼:  

  

通通WPF随筆(3)——藝術二維碼素材生成器
  我們可以把它看作由很多
通通WPF随筆(3)——藝術二維碼素材生成器

正方形組成的矩陣,每一個正方形就是矩陣中的一個節點。那3個大方框和右下角的小方框是用來定位二維碼的方向位置的。

  先排除顔色貼圖不管,那些藝術二維碼的差別無非就是節點的形狀不同而已,有方的、圓的、還有在方的邊緣做了平滑處理的。如果能用這些節點生成最基本的黑白原型,用PS加上顔色和貼圖簡直就是輕而易舉。是以我做了這個一個快速原型工具,如下圖:

通通WPF随筆(3)——藝術二維碼素材生成器

通過修改左邊的參數就可以實作各種效果:

 改變節點大小

通通WPF随筆(3)——藝術二維碼素材生成器
使用随機節點大小和随機顔色
通通WPF随筆(3)——藝術二維碼素材生成器
圓形節點
通通WPF随筆(3)——藝術二維碼素材生成器
圓形随機大小、随機顔色
通通WPF随筆(3)——藝術二維碼素材生成器
自定義形狀節點
通通WPF随筆(3)——藝術二維碼素材生成器

  生成各種素材,經過PS的拼裝處理,基本可以實作任何效果了。

(有些比較難識别,是因為比較難找到作為位置定位的那幾個方框,是以還得PS處理時,要把那幾個方框做得明顯些)

 關于方形邊緣平滑處理效果,由于用PS比較容易實作,就懶得研究代碼了,錄制個PS動作更快些。如下:

通通WPF随筆(3)——藝術二維碼素材生成器

這個是女朋友生日那天臨時設計的。 

   

   

2.詳細設計

  關于二維碼的生成這裡我用了一個開源項目

QrCode.Net http://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格式啊....

   由于代碼比較多,思路我上面已經說明了,隻是編碼的問題了,這裡我就貼出源碼,感興趣的可以自己研究一下

通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器

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

  下面欣賞一下我心血來潮時設計的:

通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器
通通WPF随筆(3)——藝術二維碼素材生成器

 下載下傳位址:

http://files.cnblogs.com/tong-tong/TTQRCodeFactory.zip

後記

    自從本人淘寶女裝店倒閉以後就一直不甘心,本來想開家店賣下二維碼的設計賺點飯錢,結果事太多了,愣是沒有時間來裝修店鋪...各位博友如果有二維碼的設計需求的話也可以找我哦,價格公道,隻收飯錢~~~~

繼續閱讀