Created
January 19, 2014 22:56
-
-
Save robfe/8512110 to your computer and use it in GitHub Desktop.
Render ZXing qrcodes into a geometry for a WPF path
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Path Name="imageBarcodeEncoder" Fill="Black" Width="200" Height="200" Data="[set this from a GeometryBarcodeWriter]"/> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Windows.Media; | |
using ZXing.Rendering; | |
namespace ZXing.Presentation | |
{ | |
/// <summary> | |
/// A smart class to encode some content to a barcode image into a geometry | |
/// </summary> | |
public class GeometryBarcodeWriter : BarcodeWriterGeneric<Geometry> | |
{ | |
/// <summary> | |
/// Initializes a new instance of the <see cref="BarcodeWriter"/> class. | |
/// </summary> | |
public GeometryBarcodeWriter() | |
{ | |
Renderer = new GeometryRenderer(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Windows; | |
using System.Windows.Media; | |
using ZXing.Common; | |
namespace ZXing.Rendering | |
{ | |
public class GeometryRenderer : IBarcodeRenderer<Geometry> | |
{ | |
public Geometry Render(BitMatrix matrix, BarcodeFormat format, string content) | |
{ | |
return Render(matrix, format, content, null); | |
} | |
public Geometry Render(BitMatrix matrix, BarcodeFormat format, string content, EncodingOptions options) | |
{ | |
var edges = new HashSet<Edge>(); | |
var edgeMap = new Dictionary<Coordinate, List<Edge>>(); | |
var cols = matrix.Width; | |
var rows = matrix.Height; | |
for (int c = 0; c <= cols; c++) | |
{ | |
for (int r = 0; r <= rows; r++) | |
{ | |
var cell = GetCell(c, r, matrix); | |
var westCell = GetCell(c - 1, r, matrix); | |
var northCell = GetCell(c, r - 1, matrix); | |
if (northCell != cell) | |
{ | |
AddEdge(new Edge(c, r, c + 1, r), edges, edgeMap); | |
} | |
if (westCell != cell) | |
{ | |
AddEdge(new Edge(c, r, c, r + 1), edges, edgeMap); | |
} | |
} | |
} | |
var cycles = new List<List<Coordinate>>(); | |
while (edges.Count > 0) | |
{ | |
var edge = edges.First(); | |
RemoveEdge(edge, edges, edgeMap); | |
if (IsEdgeLeftHand(matrix, edge)) | |
{ | |
edge = edge.Reversed(); | |
} | |
var currentCycle = new List<Coordinate> { edge.From, edge.To }; | |
while (edge.To != currentCycle[0]) | |
{ | |
var moves = from direction in Turns(edge.From - edge.To) | |
let nextCoordinate = direction + edge.To | |
from e in EdgesFrom(edge.To, edgeMap) | |
where e.To == nextCoordinate || e.From == nextCoordinate | |
select e; | |
var nextEdge = moves.First(); | |
RemoveEdge(nextEdge, edges, edgeMap); | |
edge = nextEdge.To != edge.To ? nextEdge : nextEdge.Reversed(); | |
currentCycle.Add(edge.To); | |
} | |
cycles.Add(currentCycle); | |
} | |
return new PathGeometry(cycles.Select(x => new PathFigure(x.First().ToPoint(1), x.Skip(1).Select(y => new LineSegment(y.ToPoint(1), true)), true))); | |
} | |
private static bool IsEdgeLeftHand(BitMatrix b, Edge edge) | |
{ | |
var cell = GetCell(edge.From.Col, edge.From.Row, b); | |
return (edge.From.Row < edge.To.Row && cell) || (!(edge.From.Col < edge.To.Col && cell)); | |
} | |
static IEnumerable<Edge> EdgesFrom(Coordinate c, Dictionary<Coordinate, List<Edge>> edgeMap) | |
{ | |
return edgeMap.ContainsKey(c) ? edgeMap[c] : Enumerable.Empty<Edge>(); | |
} | |
static IEnumerable<Coordinate> Turns(Coordinate currentDirection) | |
{ | |
int index = Array.IndexOf(Coordinate.Directions, currentDirection); | |
return Coordinate.Directions.Skip(index + 1).Concat(Coordinate.Directions.Take(index)); | |
} | |
private static bool GetCell(int c, int r, BitMatrix matrix) | |
{ | |
if (r < 0 || r >= matrix.Height) | |
{ | |
return false; | |
} | |
if (c < 0 || c >= matrix.Width) | |
{ | |
return false; | |
} | |
return matrix[c, r]; | |
} | |
private static void RemoveEdge(Edge e, HashSet<Edge> edges, Dictionary<Coordinate, List<Edge>> edgeMap) | |
{ | |
edges.Remove(e); | |
edgeMap[e.From].Remove(e); | |
edgeMap[e.To].Remove(e); | |
} | |
private static void AddEdge(Edge e, HashSet<Edge> edges, Dictionary<Coordinate, List<Edge>> edgeMap) | |
{ | |
edges.Add(e); | |
AddCoordinate(e.From, e, edgeMap); | |
AddCoordinate(e.To, e, edgeMap); | |
} | |
private static void AddCoordinate(Coordinate c, Edge e, Dictionary<Coordinate, List<Edge>> edgeMap) | |
{ | |
List<Edge> list; | |
if (!edgeMap.TryGetValue(c, out list)) | |
{ | |
edgeMap[c] = list = new List<Edge>(); | |
} | |
list.Add(e); | |
} | |
struct Coordinate | |
{ | |
public readonly int Row, Col; | |
public Coordinate(int col, int row) | |
{ | |
Col = col; | |
Row = row; | |
} | |
public bool Equals(Coordinate other) | |
{ | |
return other.Row == Row && other.Col == Col; | |
} | |
public override bool Equals(object obj) | |
{ | |
if (ReferenceEquals(null, obj)) return false; | |
if (obj.GetType() != typeof(Coordinate)) return false; | |
return Equals((Coordinate)obj); | |
} | |
public override int GetHashCode() | |
{ | |
unchecked | |
{ | |
return (Row * 397) ^ Col; | |
} | |
} | |
public override string ToString() | |
{ | |
var s = ""; | |
if (this == North) s = " n"; | |
if (this == West) s = " w"; | |
if (this == South) s = " s"; | |
if (this == East) s = " e"; | |
return String.Format("({0}, {1}{2})", Col, Row, s); | |
} | |
public static bool operator ==(Coordinate left, Coordinate right) | |
{ | |
return left.Equals(right); | |
} | |
public static bool operator !=(Coordinate left, Coordinate right) | |
{ | |
return !left.Equals(right); | |
} | |
public static Coordinate operator +(Coordinate c1, Coordinate c2) | |
{ | |
return new Coordinate(c1.Col + c2.Col, c1.Row + c2.Row); | |
} | |
public static Coordinate operator -(Coordinate c1, Coordinate c2) | |
{ | |
return new Coordinate(c1.Col - c2.Col, c1.Row - c2.Row); | |
} | |
public Point ToPoint(double scale) | |
{ | |
return new Point(Col * scale, Row * scale); | |
} | |
static readonly Coordinate West = new Coordinate(-1, 0); | |
static readonly Coordinate South = new Coordinate(0, 1); | |
static readonly Coordinate East = new Coordinate(1, 0); | |
static readonly Coordinate North = new Coordinate(0, -1); | |
public static readonly Coordinate[] Directions = new[] | |
{ | |
West, | |
South, | |
East, | |
North, | |
}; | |
} | |
struct Edge | |
{ | |
public readonly Coordinate From, To; | |
public Edge(Coordinate from, Coordinate to) | |
{ | |
From = from; | |
To = to; | |
} | |
public Edge(int fromCol, int fromRow, int toCol, int toRow) | |
: this(new Coordinate(fromCol, fromRow), new Coordinate(toCol, toRow)) | |
{ | |
} | |
public bool Equals(Edge other) | |
{ | |
return other.From.Equals(From) && other.To.Equals(To); | |
} | |
public override bool Equals(object obj) | |
{ | |
if (ReferenceEquals(null, obj)) return false; | |
if (obj.GetType() != typeof(Edge)) return false; | |
return Equals((Edge)obj); | |
} | |
public override int GetHashCode() | |
{ | |
unchecked | |
{ | |
return (From.GetHashCode() * 397) ^ To.GetHashCode(); | |
} | |
} | |
public override string ToString() | |
{ | |
char angle = ' '; | |
if (From.Col == To.Col) | |
{ | |
angle = '|'; | |
} | |
if (From.Row == To.Row) | |
{ | |
angle = '-'; | |
} | |
return string.Format("[{0} {2} {1}]", From, To, angle); | |
} | |
public static bool operator ==(Edge left, Edge right) | |
{ | |
return left.Equals(right); | |
} | |
public static bool operator !=(Edge left, Edge right) | |
{ | |
return !left.Equals(right); | |
} | |
public Edge Reversed() | |
{ | |
return new Edge(To, From); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment