commit ed37b7ba01f6bf40c72b2b53868919e7551f0538 Author: BlubbFish Date: Mon Aug 3 22:08:05 2026 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06e16d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vs +DataMatrix.net/bin +DataMatrix.net/obj \ No newline at end of file diff --git a/DataMatrix.net.sln b/DataMatrix.net.sln new file mode 100644 index 0000000..2a5ed7b --- /dev/null +++ b/DataMatrix.net.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36414.22 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataMatrix.net", "DataMatrix.net\DataMatrix.net.csproj", "{360B7A7C-0D9D-42FA-850E-BA87CAF0F60A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {360B7A7C-0D9D-42FA-850E-BA87CAF0F60A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {360B7A7C-0D9D-42FA-850E-BA87CAF0F60A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {360B7A7C-0D9D-42FA-850E-BA87CAF0F60A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {360B7A7C-0D9D-42FA-850E-BA87CAF0F60A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {21728DFC-EB10-4117-B50A-250B02AF7DFE} + EndGlobalSection +EndGlobal diff --git a/DataMatrix.net/C40TextState.cs b/DataMatrix.net/C40TextState.cs new file mode 100644 index 0000000..6c69024 --- /dev/null +++ b/DataMatrix.net/C40TextState.cs @@ -0,0 +1,45 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + + +namespace DataMatrix.net +{ + internal struct C40TextState + { + #region Properties + + internal int Shift { get; set; } + + internal bool UpperShift { get; set; } + + #endregion + + } +} \ No newline at end of file diff --git a/DataMatrix.net/DataMatrix.net.csproj b/DataMatrix.net/DataMatrix.net.csproj new file mode 100644 index 0000000..64f773d --- /dev/null +++ b/DataMatrix.net/DataMatrix.net.csproj @@ -0,0 +1,18 @@ + + + + net10.0 + enable + enable + LGPL-3.0-only + DataMatrix.net + Michael Faschinger + 0.5.0 + https://github.com/xas/DataMatrix.NET + + + + + + + diff --git a/DataMatrix.net/DmtxBestLine.cs b/DataMatrix.net/DmtxBestLine.cs new file mode 100644 index 0000000..f9dd0b7 --- /dev/null +++ b/DataMatrix.net/DmtxBestLine.cs @@ -0,0 +1,57 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal struct DmtxBestLine + { + internal int Angle { get; set; } + + internal int HOffset { get; set; } + + internal int Mag { get; set; } + + internal int StepBeg { get; set; } + + internal int StepPos { get; set; } + + internal int StepNeg { get; set; } + + internal int DistSq { get; set; } + + internal double Devn { get; set; } + + internal DmtxPixelLoc LocBeg { get; set; } + + internal DmtxPixelLoc LocPos { get; set; } + + internal DmtxPixelLoc LocNeg { get; set; } + } +} diff --git a/DataMatrix.net/DmtxBresLine.cs b/DataMatrix.net/DmtxBresLine.cs new file mode 100644 index 0000000..2d5c349 --- /dev/null +++ b/DataMatrix.net/DmtxBresLine.cs @@ -0,0 +1,314 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; + +namespace DataMatrix.net +{ + internal struct DmtxBresLine + { + #region Fields + int _xStep; + int _yStep; + int _xDelta; + int _yDelta; + bool _steep; + int _xOut; + int _yOut; + int _travel; + int _outward; + int _error; + DmtxPixelLoc _loc; + DmtxPixelLoc _loc0; + DmtxPixelLoc _loc1; + #endregion + + #region Constructors + internal DmtxBresLine(DmtxBresLine orig) + { + this._error = orig._error; + this._loc = new DmtxPixelLoc { X = orig._loc.X, Y = orig._loc.Y }; + this._loc0 = new DmtxPixelLoc { X = orig._loc0.X, Y = orig._loc0.Y }; + this._loc1 = new DmtxPixelLoc { X = orig._loc1.X, Y = orig._loc1.Y }; + this._outward = orig._outward; + this._steep = orig._steep; + this._travel = orig._travel; + this._xDelta = orig._xDelta; + this._xOut = orig._xOut; + this._xStep = orig._xStep; + this._yDelta = orig._yDelta; + this._yOut = orig._yOut; + this._yStep = orig._yStep; + } + + internal DmtxBresLine(DmtxPixelLoc loc0, DmtxPixelLoc loc1, DmtxPixelLoc locInside) + { + int cp; + DmtxPixelLoc locBeg, locEnd; + + + /* Values that stay the same after initialization */ + this._loc0 = loc0; + this._loc1 = loc1; + this._xStep = (loc0.X < loc1.X) ? +1 : -1; + this._yStep = (loc0.Y < loc1.Y) ? +1 : -1; + this._xDelta = Math.Abs(loc1.X - loc0.X); + this._yDelta = Math.Abs(loc1.Y - loc0.Y); + this._steep = (this._yDelta > this._xDelta); + + /* Take cross product to determine outward step */ + if (this._steep) + { + /* Point first vector up to get correct sign */ + if (loc0.Y < loc1.Y) + { + locBeg = loc0; + locEnd = loc1; + } + else + { + locBeg = loc1; + locEnd = loc0; + } + cp = (((locEnd.X - locBeg.X) * (locInside.Y - locEnd.Y)) - + ((locEnd.Y - locBeg.Y) * (locInside.X - locEnd.X))); + + this._xOut = (cp > 0) ? +1 : -1; + this._yOut = 0; + } + else + { + /* Point first vector left to get correct sign */ + if (loc0.X > loc1.X) + { + locBeg = loc0; + locEnd = loc1; + } + else + { + locBeg = loc1; + locEnd = loc0; + } + cp = (((locEnd.X - locBeg.X) * (locInside.Y - locEnd.Y)) - + ((locEnd.Y - locBeg.Y) * (locInside.X - locEnd.X))); + + this._xOut = 0; + this._yOut = (cp > 0) ? +1 : -1; + } + + /* Values that change while stepping through line */ + this._loc = loc0; + this._travel = 0; + this._outward = 0; + this._error = (this._steep) ? this._yDelta / 2 : this._xDelta / 2; + } + #endregion + + #region Methods + internal bool GetStep(DmtxPixelLoc target, ref int travel, ref int outward) + { + /* Determine necessary step along and outward from Bresenham line */ + if (this._steep) + { + travel = (this._yStep > 0) ? target.Y - this._loc.Y : this._loc.Y - target.Y; + Step(travel, 0); + outward = (this._xOut > 0) ? target.X - this._loc.X : this._loc.X - target.X; + if (this._yOut != 0) + { + throw new Exception("Invald yOut value for bresline step!"); + } + } + else + { + travel = (this._xStep > 0) ? target.X - this._loc.X : this._loc.X - target.X; + Step(travel, 0); + outward = (this._yOut > 0) ? target.Y - this._loc.Y : this._loc.Y - target.Y; + if (this._xOut != 0) + { + throw new Exception("Invald xOut value for bresline step!"); + } + } + + return true; + } + + + internal bool Step(int travel, int outward) + { + int i; + + if (Math.Abs(travel) >= 2) + { + throw new ArgumentException("Invalid value for 'travel' in BaseLineStep!"); + } + + /* Perform forward step */ + if (travel > 0) + { + this._travel++; + if (this._steep) + { + this._loc = new DmtxPixelLoc() { X = this._loc.X, Y = this._loc.Y + this._yStep }; + this._error -= this._xDelta; + if (this._error < 0) + { + this._loc = new DmtxPixelLoc() { X = this._loc.X + this._xStep, Y = this._loc.Y }; + this._error += this._yDelta; + } + } + else + { + this._loc = new DmtxPixelLoc() { X = this._loc.X + this._xStep, Y = this._loc.Y }; + this._error -= this._yDelta; + if (this._error < 0) + { + this._loc = new DmtxPixelLoc() { X = this._loc.X, Y = this._loc.Y + this._yStep }; + this._error += this._xDelta; + } + } + } + else if (travel < 0) + { + this._travel--; + if (this._steep) + { + this._loc = new DmtxPixelLoc() { X = this._loc.X, Y = this._loc.Y - this._yStep }; + this._error += this._xDelta; + if (this.Error >= this.YDelta) + { + this._loc = new DmtxPixelLoc() { X = this._loc.X - this._xStep, Y = this._loc.Y }; + this._error -= this._yDelta; + } + } + else + { + this._loc = new DmtxPixelLoc() { X = this._loc.X - this._xStep, Y = this._loc.Y }; + this._error += this._yDelta; + if (this._error >= this._xDelta) + { + this._loc = new DmtxPixelLoc() { X = this._loc.X, Y = this._loc.Y - this._yStep }; + this._error -= this._xDelta; + } + } + } + + for (i = 0; i < outward; i++) + { + /* Outward steps */ + this._outward++; + this._loc = new DmtxPixelLoc() { X = this._loc.X + this._xOut, Y = this._loc.Y + this._yOut }; + } + + return true; + } + #endregion + + #region Properties + internal int XStep + { + get { return _xStep; } + set { _xStep = value; } + } + + internal int YStep + { + get { return _yStep; } + set { _yStep = value; } + } + + internal int XDelta + { + get { return _xDelta; } + set { _xDelta = value; } + } + + internal int YDelta + { + get { return _yDelta; } + set { _yDelta = value; } + } + + internal bool Steep + { + get { return _steep; } + set { _steep = value; } + } + + internal int XOut + { + get { return _xOut; } + set { _xOut = value; } + } + + internal int YOut + { + get { return _yOut; } + set { _yOut = value; } + } + + internal int Travel + { + get { return _travel; } + set { _travel = value; } + } + + internal int Outward + { + get { return _outward; } + set { _outward = value; } + } + + internal int Error + { + get { return _error; } + set { _error = value; } + } + + internal DmtxPixelLoc Loc + { + get { return _loc; } + set { _loc = value; } + } + + + internal DmtxPixelLoc Loc0 + { + get { return _loc0; } + set { _loc0 = value; } + } + + internal DmtxPixelLoc Loc1 + { + get { return _loc1; } + set { _loc1 = value; } + } + #endregion + } +} diff --git a/DataMatrix.net/DmtxChannel.cs b/DataMatrix.net/DmtxChannel.cs new file mode 100644 index 0000000..d41c9a5 --- /dev/null +++ b/DataMatrix.net/DmtxChannel.cs @@ -0,0 +1,77 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal class DmtxChannel + { + byte[] _encodedWords; + + internal byte[] Input { get; set; } + + internal DmtxScheme EncScheme { get; set; } + + internal DmtxChannelStatus Invalid { get; set; } + + internal int InputIndex { get; set; } + + internal int EncodedLength { get; set; } + + internal int CurrentLength { get; set; } + + internal int FirstCodeWord { get; set; } + + internal byte[] EncodedWords + { + get { return this._encodedWords ?? (this._encodedWords = new byte[1558]); } + } + } + + internal class DmtxChannelGroup + { + DmtxChannel[] _channels; + + internal DmtxChannel[] Channels + { + get + { + if (_channels == null) + { + _channels = new DmtxChannel[6]; + for (int i = 0; i < 6; i++) + { + _channels[i] = new DmtxChannel(); + } + } + return _channels; + } + } + } +} diff --git a/DataMatrix.net/DmtxCommon.cs b/DataMatrix.net/DmtxCommon.cs new file mode 100644 index 0000000..d836a20 --- /dev/null +++ b/DataMatrix.net/DmtxCommon.cs @@ -0,0 +1,309 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; + +namespace DataMatrix.net +{ + internal static class DmtxCommon + { + internal static void GenReedSolEcc(DmtxMessage message, DmtxSymbolSize sizeIdx) + { + byte[] g = new byte[69]; + byte[] b = new byte[68]; + + int symbolDataWords = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx); + int symbolErrorWords = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolErrorWords, sizeIdx); + int symbolTotalWords = symbolDataWords + symbolErrorWords; + int blockErrorWords = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribBlockErrorWords, sizeIdx); + int step = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribInterleavedBlocks, sizeIdx); + if (blockErrorWords != symbolErrorWords / step) + { + throw new Exception("Error generation reed solomon error correction"); + } + + for (int gI = 0; gI < g.Length; gI++) + { + g[gI] = 0x01; + } + + // Generate ECC polynomia + for (int i = 1; i <= blockErrorWords; i++) + { + for (int j = i - 1; j >= 0; j--) + { + g[j] = GfDoublify(g[j], i); // g[j] *= 2**i + if (j > 0) + g[j] = GfSum(g[j], g[j - 1]); // g[j] += g[j-1] + } + } + + // Populate error codeword array + for (int block = 0; block < step; block++) + { + for (int bI = 0; bI < b.Length; bI++) + { + b[bI] = 0; + } + + for (int i = block; i < symbolDataWords; i += step) + { + int val = GfSum(b[blockErrorWords - 1], message.Code[i]); + for (int j = blockErrorWords - 1; j > 0; j--) + { + b[j] = GfSum(b[j - 1], GfProduct(g[j], val)); + } + b[0] = GfProduct(g[0], val); + } + + int blockDataWords = GetBlockDataSize(sizeIdx, block); + int bIndex = blockErrorWords; + + for (int i = block + (step * blockDataWords); i < symbolTotalWords; i += step) + { + message.Code[i] = b[--bIndex]; + } + + if (bIndex != 0) + { + throw new Exception("Error generation error correction code!"); + } + } + } + + private static byte GfProduct(byte a, int b) + { + if (a == 0 || b == 0) + return 0; + + return (byte)DmtxConstants.aLogVal[(DmtxConstants.logVal[a] + DmtxConstants.logVal[b]) % 255]; + } + + private static byte GfSum(byte a, byte b) + { + return (byte)(a ^ b); + } + + private static byte GfDoublify(byte a, int b) + { + if (a == 0) /* XXX this is right, right? */ + return 0; + if (b == 0) + return a; /* XXX this is right, right? */ + + return (byte)DmtxConstants.aLogVal[(DmtxConstants.logVal[a] + b) % 255]; + } + + internal static int GetSymbolAttribute(DmtxSymAttribute attribute, DmtxSymbolSize sizeIdx) + { + if (sizeIdx < 0 || (int)sizeIdx >= DmtxConstants.DmtxSymbolSquareCount + DmtxConstants.DmtxSymbolRectCount) + return DmtxConstants.DmtxUndefined; + + switch (attribute) + { + case DmtxSymAttribute.DmtxSymAttribSymbolRows: + return DmtxConstants.SymbolRows[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribSymbolCols: + return DmtxConstants.SymbolCols[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribDataRegionRows: + return DmtxConstants.DataRegionRows[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribDataRegionCols: + return DmtxConstants.DataRegionCols[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribHorizDataRegions: + return DmtxConstants.HorizDataRegions[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribVertDataRegions: + return ((int)sizeIdx < DmtxConstants.DmtxSymbolSquareCount) ? DmtxConstants.HorizDataRegions[(int)sizeIdx] : 1; + case DmtxSymAttribute.DmtxSymAttribMappingMatrixRows: + return DmtxConstants.DataRegionRows[(int)sizeIdx] * + GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribVertDataRegions, sizeIdx); + case DmtxSymAttribute.DmtxSymAttribMappingMatrixCols: + return DmtxConstants.DataRegionCols[(int)sizeIdx] * DmtxConstants.HorizDataRegions[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribInterleavedBlocks: + return DmtxConstants.InterleavedBlocks[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribBlockErrorWords: + return DmtxConstants.BlockErrorWords[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribBlockMaxCorrectable: + return DmtxConstants.BlockMaxCorrectable[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribSymbolDataWords: + return DmtxConstants.SymbolDataWords[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribSymbolErrorWords: + return DmtxConstants.BlockErrorWords[(int)sizeIdx] * DmtxConstants.InterleavedBlocks[(int)sizeIdx]; + case DmtxSymAttribute.DmtxSymAttribSymbolMaxCorrectable: + return DmtxConstants.BlockMaxCorrectable[(int)sizeIdx] * DmtxConstants.InterleavedBlocks[(int)sizeIdx]; + } + return DmtxConstants.DmtxUndefined; + } + + internal static int GetBlockDataSize(DmtxSymbolSize sizeIdx, int blockIdx) + { + int symbolDataWords = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx); + int interleavedBlocks = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribInterleavedBlocks, sizeIdx); + int count = symbolDataWords / interleavedBlocks; + + if (symbolDataWords < 1 || interleavedBlocks < 1) + return DmtxConstants.DmtxUndefined; + + return (sizeIdx == DmtxSymbolSize.DmtxSymbol144x144 && blockIdx < 8) ? count + 1 : count; + } + + internal static DmtxSymbolSize FindCorrectSymbolSize(int dataWords, DmtxSymbolSize sizeIdxRequest) + { + DmtxSymbolSize sizeIdx; + if (dataWords <= 0) + { + return DmtxSymbolSize.DmtxSymbolShapeAuto; + } + + if (sizeIdxRequest == DmtxSymbolSize.DmtxSymbolSquareAuto || sizeIdxRequest == DmtxSymbolSize.DmtxSymbolRectAuto) + { + DmtxSymbolSize idxBeg; + DmtxSymbolSize idxEnd; + if (sizeIdxRequest == DmtxSymbolSize.DmtxSymbolSquareAuto) + { + idxBeg = 0; + idxEnd = (DmtxSymbolSize)DmtxConstants.DmtxSymbolSquareCount; + } + else + { + idxBeg = (DmtxSymbolSize)DmtxConstants.DmtxSymbolSquareCount; + idxEnd = (DmtxSymbolSize)(DmtxConstants.DmtxSymbolSquareCount + DmtxConstants.DmtxSymbolRectCount); + } + + for (sizeIdx = idxBeg; sizeIdx < idxEnd; sizeIdx++) + { + if (GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx) >= dataWords) + break; + } + + if (sizeIdx == idxEnd) + { + return DmtxSymbolSize.DmtxSymbolShapeAuto; + } + } + else + { + sizeIdx = sizeIdxRequest; + } + + if (dataWords > GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx)) + { + return DmtxSymbolSize.DmtxSymbolShapeAuto; + } + + return sizeIdx; + } + + internal static int GetBitsPerPixel(DmtxPackOrder pack) + { + switch (pack) + { + case DmtxPackOrder.DmtxPack1bppK: + return 1; + case DmtxPackOrder.DmtxPack8bppK: + return 8; + case DmtxPackOrder.DmtxPack16bppRGB: + case DmtxPackOrder.DmtxPack16bppRGBX: + case DmtxPackOrder.DmtxPack16bppXRGB: + case DmtxPackOrder.DmtxPack16bppBGR: + case DmtxPackOrder.DmtxPack16bppBGRX: + case DmtxPackOrder.DmtxPack16bppXBGR: + case DmtxPackOrder.DmtxPack16bppYCbCr: + return 16; + case DmtxPackOrder.DmtxPack24bppRGB: + case DmtxPackOrder.DmtxPack24bppBGR: + case DmtxPackOrder.DmtxPack24bppYCbCr: + return 24; + case DmtxPackOrder.DmtxPack32bppRGBX: + case DmtxPackOrder.DmtxPack32bppXRGB: + case DmtxPackOrder.DmtxPack32bppBGRX: + case DmtxPackOrder.DmtxPack32bppXBGR: + case DmtxPackOrder.DmtxPack32bppCMYK: + return 32; + } + + return DmtxConstants.DmtxUndefined; + } + + internal static T Min(T x, T y) where T : IComparable + { + return x.CompareTo(y) < 0 ? x : y; + } + + internal static T Max(T x, T y) where T : IComparable + { + return x.CompareTo(y) < 0 ? y : x; + } + + internal static bool DecodeCheckErrors(byte[] code, int codeIndex, DmtxSymbolSize sizeIdx, int fix) + { + byte[] data = new byte[255]; + + int interleavedBlocks = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribInterleavedBlocks, sizeIdx); + int blockErrorWords = GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribBlockErrorWords, sizeIdx); + + const int fixedErr = 0; + int fixedErrSum = 0; + for (int i = 0; i < interleavedBlocks; i++) + { + int blockTotalWords = blockErrorWords + GetBlockDataSize(sizeIdx, i); + + int j; + for (j = 0; j < blockTotalWords; j++) + data[j] = code[j * interleavedBlocks + i]; + + fixedErrSum += fixedErr; + + for (j = 0; j < blockTotalWords; j++) + code[j * interleavedBlocks + i] = data[j]; + } + + if (fix != DmtxConstants.DmtxUndefined && fix >= 0 && fix < fixedErrSum) + { + return false; + } + + return true; + } + + internal static double RightAngleTrueness(DmtxVector2 c0, DmtxVector2 c1, DmtxVector2 c2, double angle) + { + DmtxVector2 vA = (c0 - c1); + DmtxVector2 vB = (c2 - c1); + vA.Norm(); + vB.Norm(); + + DmtxMatrix3 m = DmtxMatrix3.Rotate(angle); + vB *= m; + + return vA.Dot(vB); + } + + } +} diff --git a/DataMatrix.net/DmtxConstants.cs b/DataMatrix.net/DmtxConstants.cs new file mode 100644 index 0000000..8ccc525 --- /dev/null +++ b/DataMatrix.net/DmtxConstants.cs @@ -0,0 +1,363 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal static class DmtxConstants + { + internal static readonly double DmtxAlmostZero = 0.000001; + + internal static readonly int DmtxModuleOff = 0x00; + internal static readonly int DmtxModuleOnRed = 0x01; + internal static readonly int DmtxModuleOnGreen = 0x02; + internal static readonly int DmtxModuleOnBlue = 0x04; + internal static readonly int DmtxModuleOnRGB = 0x07; /* OnRed | OnGreen | OnBlue */ + internal static readonly int DmtxModuleOn = 0x07; + internal static readonly int DmtxModuleUnsure = 0x08; + internal static readonly int DmtxModuleAssigned = 0x10; + internal static readonly int DmtxModuleVisited = 0x20; + internal static readonly int DmtxModuleData = 0x40; + + internal static readonly byte DmtxCharAsciiPad = 129; + internal static readonly byte DmtxCharAsciiUpperShift = 235; + internal static readonly byte DmtxCharTripletShift1 = 0; + internal static readonly byte DmtxCharTripletShift2 = 1; + internal static readonly byte DmtxCharTripletShift3 = 2; + internal static readonly byte DmtxCharFNC1 = 232; + internal static readonly byte DmtxCharStructuredAppend = 233; + internal static readonly byte DmtxChar05Macro = 236; + internal static readonly byte DmtxChar06Macro = 237; + + internal static readonly int DmtxC40TextBasicSet = 0; + internal static readonly int DmtxC40TextShift1 = 1; + internal static readonly int DmtxC40TextShift2 = 2; + internal static readonly int DmtxC40TextShift3 = 3; + + internal static readonly int DmtxCharTripletUnlatch = 254; + internal static readonly int DmtxCharEdifactUnlatch = 31; + + internal static readonly byte DmtxCharC40Latch = 230; + internal static readonly byte DmtxCharTextLatch = 239; + internal static readonly byte DmtxCharX12Latch = 238; + internal static readonly byte DmtxCharEdifactLatch = 240; + internal static readonly byte DmtxCharBase256Latch = 231; + + internal static readonly int[] SymbolRows = new int[] { 10, 12, 14, 16, 18, 20, 22, 24, 26, + 32, 36, 40, 44, 48, 52, + 64, 72, 80, 88, 96, 104, + 120, 132, 144, + 8, 8, 12, 12, 16, 16 }; + + internal static readonly int[] SymbolCols = new int[] { 10, 12, 14, 16, 18, 20, 22, 24, 26, + 32, 36, 40, 44, 48, 52, + 64, 72, 80, 88, 96, 104, + 120, 132, 144, + 18, 32, 26, 36, 36, 48 }; + + internal static readonly int[] DataRegionRows = new int[] { 8, 10, 12, 14, 16, 18, 20, 22, 24, + 14, 16, 18, 20, 22, 24, + 14, 16, 18, 20, 22, 24, + 18, 20, 22, + 6, 6, 10, 10, 14, 14 }; + + internal static readonly int[] DataRegionCols = new int[] { 8, 10, 12, 14, 16, 18, 20, 22, 24, + 14, 16, 18, 20, 22, 24, + 14, 16, 18, 20, 22, 24, + 18, 20, 22, + 16, 14, 24, 16, 16, 22 }; + + internal static readonly int[] HorizDataRegions = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, + 6, 6, 6, + 1, 2, 1, 2, 2, 2 }; + + internal static readonly int[] InterleavedBlocks = new int[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, + 2, 4, 4, 4, 4, 6, + 6, 8, 10, + 1, 1, 1, 1, 1, 1 }; + + internal static readonly int[] SymbolDataWords = new int[] { 3, 5, 8, 12, 18, 22, 30, 36, 44, + 62, 86, 114, 144, 174, 204, + 280, 368, 456, 576, 696, 816, + 1050, 1304, 1558, + 5, 10, 16, 22, 32, 49 }; + + internal static readonly int[] BlockErrorWords = new int[] { 5, 7, 10, 12, 14, 18, 20, 24, 28, + 36, 42, 48, 56, 68, 42, + 56, 36, 48, 56, 68, 56, + 68, 62, 62, + 7, 11, 14, 18, 24, 28 }; + + internal static readonly int[] BlockMaxCorrectable = new int[] { 2, 3, 5, 6, 7, 9, 10, 12, 14, + 18, 21, 24, 28, 34, 21, + 28, 18, 24, 28, 34, 28, + 34, 31, 31, + 3, 5, 7, 9, 12, 14 }; + internal static readonly int DmtxSymbolSquareCount = 24; + internal static readonly int DmtxSymbolRectCount = 6; + internal static readonly int DmtxUndefined = -1; + + internal static readonly int[] DmtxPatternX = new int[] { -1, 0, 1, 1, 1, 0, -1, -1 }; + internal static readonly int[] DmtxPatternY = new int[] { -1, -1, -1, 0, 1, 1, 1, 0 }; + internal static readonly DmtxPointFlow DmtxBlankEdge = new DmtxPointFlow() { Plane = 0, Arrive = 0, Depart = 0, Mag = DmtxConstants.DmtxUndefined, Loc = new DmtxPixelLoc() { X = -1, Y = -1 } }; + + internal static readonly int DmtxHoughRes = 180; + internal static readonly int DmtxNeighborNone = 8; + + internal static readonly int[] rHvX = + { 256, 256, 256, 256, 255, 255, 255, 254, 254, 253, 252, 251, 250, 249, 248, + 247, 246, 245, 243, 242, 241, 239, 237, 236, 234, 232, 230, 228, 226, 224, + 222, 219, 217, 215, 212, 210, 207, 204, 202, 199, 196, 193, 190, 187, 184, + 181, 178, 175, 171, 168, 165, 161, 158, 154, 150, 147, 143, 139, 136, 132, + 128, 124, 120, 116, 112, 108, 104, 100, 96, 92, 88, 83, 79, 75, 71, + 66, 62, 58, 53, 49, 44, 40, 36, 31, 27, 22, 18, 13, 9, 4, + 0, -4, -9, -13, -18, -22, -27, -31, -36, -40, -44, -49, -53, -58, -62, + -66, -71, -75, -79, -83, -88, -92, -96, -100, -104, -108, -112, -116, -120, -124, + -128, -132, -136, -139, -143, -147, -150, -154, -158, -161, -165, -168, -171, -175, -178, + -181, -184, -187, -190, -193, -196, -199, -202, -204, -207, -210, -212, -215, -217, -219, + -222, -224, -226, -228, -230, -232, -234, -236, -237, -239, -241, -242, -243, -245, -246, + -247, -248, -249, -250, -251, -252, -253, -254, -254, -255, -255, -255, -256, -256, -256 }; + + internal static readonly int[] rHvY = + { 0, 4, 9, 13, 18, 22, 27, 31, 36, 40, 44, 49, 53, 58, 62, + 66, 71, 75, 79, 83, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, + 128, 132, 136, 139, 143, 147, 150, 154, 158, 161, 165, 168, 171, 175, 178, + 181, 184, 187, 190, 193, 196, 199, 202, 204, 207, 210, 212, 215, 217, 219, + 222, 224, 226, 228, 230, 232, 234, 236, 237, 239, 241, 242, 243, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 254, 255, 255, 255, 256, 256, 256, + 256, 256, 256, 256, 255, 255, 255, 254, 254, 253, 252, 251, 250, 249, 248, + 247, 246, 245, 243, 242, 241, 239, 237, 236, 234, 232, 230, 228, 226, 224, + 222, 219, 217, 215, 212, 210, 207, 204, 202, 199, 196, 193, 190, 187, 184, + 181, 178, 175, 171, 168, 165, 161, 158, 154, 150, 147, 143, 139, 136, 132, + 128, 124, 120, 116, 112, 108, 104, 100, 96, 92, 88, 83, 79, 75, 71, + 66, 62, 58, 53, 49, 44, 40, 36, 31, 27, 22, 18, 13, 9, 4 }; + + internal static readonly int[] aLogVal = + { 1, 2, 4, 8, 16, 32, 64, 128, 45, 90, 180, 69, 138, 57, 114, 228, + 229, 231, 227, 235, 251, 219, 155, 27, 54, 108, 216, 157, 23, 46, 92, 184, + 93, 186, 89, 178, 73, 146, 9, 18, 36, 72, 144, 13, 26, 52, 104, 208, + 141, 55, 110, 220, 149, 7, 14, 28, 56, 112, 224, 237, 247, 195, 171, 123, + 246, 193, 175, 115, 230, 225, 239, 243, 203, 187, 91, 182, 65, 130, 41, 82, + 164, 101, 202, 185, 95, 190, 81, 162, 105, 210, 137, 63, 126, 252, 213, 135, + 35, 70, 140, 53, 106, 212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, + 217, 159, 19, 38, 76, 152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, + 117, 234, 249, 223, 147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, + 189, 87, 174, 113, 226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, + 214, 129, 47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, + 127, 254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206, + 177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161, 111, + 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132, 37, 74, + 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248, 221, 151, + 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75, 150, 1 }; + + internal static readonly int[] logVal = + {-255, 255, 1, 240, 2, 225, 241, 53, 3, 38, 226, 133, 242, 43, 54, 210, + 4, 195, 39, 114, 227, 106, 134, 28, 243, 140, 44, 23, 55, 118, 211, 234, + 5, 219, 196, 96, 40, 222, 115, 103, 228, 78, 107, 125, 135, 8, 29, 162, + 244, 186, 141, 180, 45, 99, 24, 49, 56, 13, 119, 153, 212, 199, 235, 91, + 6, 76, 220, 217, 197, 11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, + 229, 86, 79, 171, 108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, + 245, 173, 187, 204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, + 57, 147, 14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, + 7, 161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179, + 42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194, 113, + 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127, 247, 146, 66, + 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73, 164, 144, 85, 170, + 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82, 72, 182, 215, 191, 251, + 47, 178, 89, 151, 101, 94, 160, 123, 26, 112, 232, 21, 51, 238, 208, 131, + 58, 69, 148, 18, 15, 16, 68, 17, 121, 149, 129, 19, 155, 59, 249, 70, + 214, 250, 168, 71, 201, 156, 64, 60, 237, 130, 111, 20, 93, 122, 177, 150 }; + + } + + internal enum DmtxFormat + { + Matrix, + Mosaic, + } + + internal enum DmtxSymAttribute + { + DmtxSymAttribSymbolRows, + DmtxSymAttribSymbolCols, + DmtxSymAttribDataRegionRows, + DmtxSymAttribDataRegionCols, + DmtxSymAttribHorizDataRegions, + DmtxSymAttribVertDataRegions, + DmtxSymAttribMappingMatrixRows, + DmtxSymAttribMappingMatrixCols, + DmtxSymAttribInterleavedBlocks, + DmtxSymAttribBlockErrorWords, + DmtxSymAttribBlockMaxCorrectable, + DmtxSymAttribSymbolDataWords, + DmtxSymAttribSymbolErrorWords, + DmtxSymAttribSymbolMaxCorrectable + } + + public enum DmtxSymbolSize + { + DmtxSymbolRectAuto = -3, + DmtxSymbolSquareAuto = -2, + DmtxSymbolShapeAuto = -1, + DmtxSymbol10x10 = 0, + DmtxSymbol12x12, + DmtxSymbol14x14, + DmtxSymbol16x16, + DmtxSymbol18x18, + DmtxSymbol20x20, + DmtxSymbol22x22, + DmtxSymbol24x24, + DmtxSymbol26x26, + DmtxSymbol32x32, + DmtxSymbol36x36, + DmtxSymbol40x40, + DmtxSymbol44x44, + DmtxSymbol48x48, + DmtxSymbol52x52, + DmtxSymbol64x64, + DmtxSymbol72x72, + DmtxSymbol80x80, + DmtxSymbol88x88, + DmtxSymbol96x96, + DmtxSymbol104x104, + DmtxSymbol120x120, + DmtxSymbol132x132, + DmtxSymbol144x144, + DmtxSymbol8x18, + DmtxSymbol8x32, + DmtxSymbol12x26, + DmtxSymbol12x36, + DmtxSymbol16x36, + DmtxSymbol16x48 + } + + internal enum DmtxFlip + { + DmtxFlipNone = 0x00, + DmtxFlipX = 0x01 << 0, + DmtxFlipY = 0x01 << 1 + } + + internal enum DmtxPackOrder + { + /* Custom format */ + DmtxPackCustom = 100, + /* 1 bpp */ + DmtxPack1bppK = 200, + /* 8 bpp grayscale */ + DmtxPack8bppK = 300, + /* 16 bpp formats */ + DmtxPack16bppRGB = 400, + DmtxPack16bppRGBX, + DmtxPack16bppXRGB, + DmtxPack16bppBGR, + DmtxPack16bppBGRX, + DmtxPack16bppXBGR, + DmtxPack16bppYCbCr, + /* 24 bpp formats */ + DmtxPack24bppRGB = 500, + DmtxPack24bppBGR, + DmtxPack24bppYCbCr, + /* 32 bpp formats */ + DmtxPack32bppRGBX = 600, + DmtxPack32bppXRGB, + DmtxPack32bppBGRX, + DmtxPack32bppXBGR, + DmtxPack32bppCMYK + } + + internal enum DmtxRange + { + DmtxRangeGood, + DmtxRangeBad, + DmtxRangeEnd + } + + internal enum DmtxDirection + { + DmtxDirNone = 0x00, + DmtxDirUp = 0x01 << 0, + DmtxDirLeft = 0x01 << 1, + DmtxDirDown = 0x01 << 2, + DmtxDirRight = 0x01 << 3, + DmtxDirHorizontal = DmtxDirLeft | DmtxDirRight, + DmtxDirVertical = DmtxDirUp | DmtxDirDown, + DmtxDirRightUp = DmtxDirRight | DmtxDirUp, + DmtxDirLeftDown = DmtxDirLeft | DmtxDirDown + } + + public enum DmtxScheme + { + DmtxSchemeAutoFast = -2, + DmtxSchemeAutoBest = -1, + DmtxSchemeAscii = 0, + DmtxSchemeC40, + DmtxSchemeText, + DmtxSchemeX12, + DmtxSchemeEdifact, + DmtxSchemeBase256, + DmtxSchemeAsciiGS1 + } + + internal enum DmtxMaskBit + { + DmtxMaskBit8 = 0x01 << 0, + DmtxMaskBit7 = 0x01 << 1, + DmtxMaskBit6 = 0x01 << 2, + DmtxMaskBit5 = 0x01 << 3, + DmtxMaskBit4 = 0x01 << 4, + DmtxMaskBit3 = 0x01 << 5, + DmtxMaskBit2 = 0x01 << 6, + DmtxMaskBit1 = 0x01 << 7 + } + + internal enum DmtxEdge + { + DmtxEdgeTop = 0x01 << 0, + DmtxEdgeBottom = 0x01 << 1, + DmtxEdgeLeft = 0x01 << 2, + DmtxEdgeRight = 0x01 << 3 + } + + enum DmtxChannelStatus + { + DmtxChannelValid = 0x00, + DmtxChannelUnsupportedChar = 0x01 << 0, + DmtxChannelCannotUnlatch = 0x01 << 1 + } + + enum DmtxUnlatch + { + Explicit, + Implicit + } +} diff --git a/DataMatrix.net/DmtxDecode.cs b/DataMatrix.net/DmtxDecode.cs new file mode 100644 index 0000000..53f6a0f --- /dev/null +++ b/DataMatrix.net/DmtxDecode.cs @@ -0,0 +1,2236 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; +using System.Collections.Generic; + +namespace DataMatrix.net +{ + internal class DmtxDecode + { + #region Fields + int _edgeMin; + int _edgeMax; + int _scanGap; + double _squareDevn; + DmtxSymbolSize _sizeIdxExpected; + int _edgeThresh; + + /* Image modifiers */ + int _xMin; + int _yMin; + int _xMax; + int _yMax; + int _scale; + /* Internals */ + byte[] _cache; + DmtxImage _image; + DmtxScanGrid _grid; + #endregion + + #region Constructors + internal DmtxDecode(DmtxImage img, int scale) + { + + int width = img.Width / scale; + int height = img.Height / scale; + + this._edgeMin = DmtxConstants.DmtxUndefined; + this._edgeMax = DmtxConstants.DmtxUndefined; + this._scanGap = 1; + this._squareDevn = Math.Cos(50.0 * (Math.PI / 180.0)); + this._sizeIdxExpected = DmtxSymbolSize.DmtxSymbolShapeAuto; + this._edgeThresh = 10; + + this._xMin = 0; + this._xMax = width - 1; + this._yMin = 0; + this._yMax = height - 1; + this._scale = scale; + + this._cache = new byte[width * height]; + + this._image = img; + ValidateSettingsAndInitScanGrid(); + } + #endregion + + #region Methods + private void ValidateSettingsAndInitScanGrid() + { + if (this._squareDevn <= 0.0 || this._squareDevn >= 1.0) + { + throw new ArgumentException("Invalid decode settings!"); + } + + if (this._scanGap < 1) + { + throw new ArgumentException("Invalid decode settings!"); + } + + if (this._edgeThresh < 1 || this._edgeThresh > 100) + { + throw new ArgumentException("Invalid decode settings!"); + } + + /* Reinitialize scangrid in case any inputs changed */ + this._grid = new DmtxScanGrid(this); + } + + internal int GetCacheIndex(int x, int y) + { + if (x < 0 || x >= Width || y < 0 || y >= Height) + { + throw new ArgumentException("Error: x and/or y outside cache size"); + } + + return y * Width + x; + } + + bool GetPixelValue(int x, int y, int channel, ref int value) + { + int xUnscaled = x * this._scale; + int yUnscaled = y * this._scale; + + return this._image.GetPixelValue(xUnscaled, yUnscaled, channel, ref value); + + } + + internal void CacheFillQuad(DmtxPixelLoc p0, DmtxPixelLoc p1, DmtxPixelLoc p2, DmtxPixelLoc p3) + { + DmtxBresLine[] lines = new DmtxBresLine[4]; + DmtxPixelLoc pEmpty = new DmtxPixelLoc { X = 0, Y = 0 }; + int posY; + int i, idx; + + lines[0] = new DmtxBresLine(p0, p1, pEmpty); + lines[1] = new DmtxBresLine(p1, p2, pEmpty); + lines[2] = new DmtxBresLine(p2, p3, pEmpty); + lines[3] = new DmtxBresLine(p3, p0, pEmpty); + + int minY = this._yMax; + int maxY = 0; + + minY = DmtxCommon.Min(minY, p0.Y); + maxY = DmtxCommon.Max(maxY, p0.Y); + minY = DmtxCommon.Min(minY, p1.Y); + maxY = DmtxCommon.Max(maxY, p1.Y); + minY = DmtxCommon.Min(minY, p2.Y); + maxY = DmtxCommon.Max(maxY, p2.Y); + minY = DmtxCommon.Min(minY, p3.Y); + maxY = DmtxCommon.Max(maxY, p3.Y); + + int sizeY = maxY - minY + 1; + + int[] scanlineMin = new int[sizeY]; + int[] scanlineMax = new int[sizeY]; + + for (i = 0; i < sizeY; i++) + { + scanlineMin[i] = this._xMax; + } + + for (i = 0; i < 4; i++) + { + while (lines[i].Loc.X != lines[i].Loc1.X || lines[i].Loc.Y != lines[i].Loc1.Y) + { + idx = lines[i].Loc.Y - minY; + scanlineMin[idx] = DmtxCommon.Min(scanlineMin[idx], lines[i].Loc.X); + scanlineMax[idx] = DmtxCommon.Max(scanlineMax[idx], lines[i].Loc.X); + lines[i].Step(1, 0); + } + } + + for (posY = minY; posY < maxY && posY < this._yMax; posY++) + { + idx = posY - minY; + int posX; + for (posX = scanlineMin[idx]; posX < scanlineMax[idx] && posX < this._xMax; posX++) + { + if (posX < 0 || posY < 0) + { + continue; + } + try + { + int cacheIndex = GetCacheIndex(posX, posY); + this._cache[cacheIndex] |= 0x80; + } + catch + { + // FIXXXME: log here as soon as there is a logger + } + } + } + } + + internal DmtxMessage MosaicRegion(DmtxRegion reg, int fix) + { + int colorPlane = reg.FlowBegin.Plane; + + reg.FlowBegin.Plane = 0; /* kind of a hack */ + DmtxMessage rMsg = MatrixRegion(reg, fix); + + reg.FlowBegin.Plane = 1; /* kind of a hack */ + DmtxMessage gMsg = MatrixRegion(reg, fix); + + reg.FlowBegin.Plane = 2; /* kind of a hack */ + DmtxMessage bMsg = MatrixRegion(reg, fix); + + reg.FlowBegin.Plane = colorPlane; + + DmtxMessage oMsg = new DmtxMessage(reg.SizeIdx, DmtxFormat.Mosaic); + + List totalMessage = new List(); + for (int i = 0; i < bMsg.OutputSize; i++) + { + if (bMsg.Output[i] == 0) + { + break; + } + totalMessage.Add(bMsg.Output[i]); + } + for (int i = 0; i < gMsg.OutputSize; i++) + { + if (gMsg.Output[i] == 0) + { + break; + } + totalMessage.Add(gMsg.Output[i]); + } + for (int i = 0; i < rMsg.OutputSize; i++) + { + if (rMsg.Output[i] == 0) + { + break; + } + totalMessage.Add(rMsg.Output[i]); + } + totalMessage.Add(0); + oMsg.Output = totalMessage.ToArray(); + return oMsg; + } + + internal DmtxMessage MatrixRegion(DmtxRegion reg, int fix) + { + DmtxMessage result = new DmtxMessage(reg.SizeIdx, DmtxFormat.Matrix); + DmtxVector2 topLeft = new DmtxVector2(); + DmtxVector2 topRight = new DmtxVector2(); + DmtxVector2 bottomLeft = new DmtxVector2(); + DmtxVector2 bottomRight = new DmtxVector2(); + DmtxPixelLoc pxTopLeft = new DmtxPixelLoc(); + DmtxPixelLoc pxTopRight = new DmtxPixelLoc(); + DmtxPixelLoc pxBottomLeft = new DmtxPixelLoc(); + DmtxPixelLoc pxBottomRight = new DmtxPixelLoc(); + + if (!PopulateArrayFromMatrix(reg, result)) + { + throw new Exception("Populating Array from matrix failed!"); + } + + /* maybe place remaining logic into new dmtxDecodePopulatedArray() + function so other people can pass in their own arrays */ + + ModulePlacementEcc200(result.Array, result.Code, + reg.SizeIdx, DmtxConstants.DmtxModuleOnRed | DmtxConstants.DmtxModuleOnGreen | DmtxConstants.DmtxModuleOnBlue); + + if (DmtxCommon.DecodeCheckErrors(result.Code, 0, reg.SizeIdx, fix) != true) + { + return null; + } + + topLeft.X = bottomLeft.X = topLeft.Y = topRight.Y = -0.1; + topRight.X = bottomRight.X = bottomLeft.Y = bottomRight.Y = 1.1; + + topLeft *= reg.Fit2Raw; + topRight *= reg.Fit2Raw; + bottomLeft *= reg.Fit2Raw; + bottomRight *= reg.Fit2Raw; + + pxTopLeft.X = (int)(0.5 + topLeft.X); + pxTopLeft.Y = (int)(0.5 + topLeft.Y); + pxBottomLeft.X = (int)(0.5 + bottomLeft.X); + pxBottomLeft.Y = (int)(0.5 + bottomLeft.Y); + pxTopRight.X = (int)(0.5 + topRight.X); + pxTopRight.Y = (int)(0.5 + topRight.Y); + pxBottomRight.X = (int)(0.5 + bottomRight.X); + pxBottomRight.Y = (int)(0.5 + bottomRight.Y); + + CacheFillQuad(pxTopLeft, pxTopRight, pxBottomRight, pxBottomLeft); + + result.DecodeDataStream(reg.SizeIdx, null); + + return result; + } + + private bool PopulateArrayFromMatrix(DmtxRegion reg, DmtxMessage msg) + { + /* Capture number of regions present in barcode */ + int xRegionTotal = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribHorizDataRegions, reg.SizeIdx); + int yRegionTotal = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribVertDataRegions, reg.SizeIdx); + + /* Capture region dimensions (not including border modules) */ + int mapWidth = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionCols, reg.SizeIdx); + int mapHeight = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionRows, reg.SizeIdx); + + int weightFactor = 2 * (mapHeight + mapWidth + 2); + if (weightFactor <= 0) + { + throw new ArgumentException("PopulateArrayFromMatrix error: Weight Factor must be greater 0"); + } + + /* Tally module changes for each region in each direction */ + for (int yRegionCount = 0; yRegionCount < yRegionTotal; yRegionCount++) + { + + /* Y location of mapping region origin in symbol coordinates */ + int yOrigin = yRegionCount * (mapHeight + 2) + 1; + + for (int xRegionCount = 0; xRegionCount < xRegionTotal; xRegionCount++) + { + int[,] tally = new int[24, 24]; /* Large enough to map largest single region */ + /* X location of mapping region origin in symbol coordinates */ + int xOrigin = xRegionCount * (mapWidth + 2) + 1; + + for (int i = 0; i < 24; i++) + { + for (int j = 0; j < 24; j++) + { + tally[i, j] = 0; + } + } + TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirUp); + TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirLeft); + TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirDown); + TallyModuleJumps(reg, tally, xOrigin, yOrigin, mapWidth, mapHeight, DmtxDirection.DmtxDirRight); + + /* Decide module status based on final tallies */ + for (int mapRow = 0; mapRow < mapHeight; mapRow++) + { + for (int mapCol = 0; mapCol < mapWidth; mapCol++) + { + + int rowTmp = (yRegionCount * mapHeight) + mapRow; + rowTmp = yRegionTotal * mapHeight - rowTmp - 1; + int colTmp = (xRegionCount * mapWidth) + mapCol; + int idx = (rowTmp * xRegionTotal * mapWidth) + colTmp; + + if (tally[mapRow, mapCol] / (double)weightFactor >= 0.5) + msg.Array[idx] = (byte)DmtxConstants.DmtxModuleOnRGB; + else + msg.Array[idx] = (byte)DmtxConstants.DmtxModuleOff; + + msg.Array[idx] |= (byte)DmtxConstants.DmtxModuleAssigned; + } + } + } + } + + return true; + } + + private void TallyModuleJumps(DmtxRegion reg, int[,] tally, int xOrigin, int yOrigin, int mapWidth, int mapHeight, DmtxDirection dir) + { + int extent; + int lineStart, lineStop; + int travelStart, travelStop; + int line; + + if (!(dir == DmtxDirection.DmtxDirUp || dir == DmtxDirection.DmtxDirLeft || dir == DmtxDirection.DmtxDirDown || dir == DmtxDirection.DmtxDirRight)) + { + throw new ArgumentException("Only orthogonal directions are allowed in tally module jumps!"); + } + + int travelStep = (dir == DmtxDirection.DmtxDirUp || dir == DmtxDirection.DmtxDirRight) ? 1 : -1; + + /* Abstract row and column progress using pointers to allow grid + traversal in all 4 directions using same logic */ + bool horizontal = false; + if ((dir & DmtxDirection.DmtxDirHorizontal) != 0x00) + { + horizontal = true; + extent = mapWidth; + lineStart = yOrigin; + lineStop = yOrigin + mapHeight; + travelStart = (travelStep == 1) ? xOrigin - 1 : xOrigin + mapWidth; + travelStop = (travelStep == 1) ? xOrigin + mapWidth : xOrigin - 1; + } + else + { + extent = mapHeight; + lineStart = xOrigin; + lineStop = xOrigin + mapWidth; + travelStart = (travelStep == 1) ? yOrigin - 1 : yOrigin + mapHeight; + travelStop = (travelStep == 1) ? yOrigin + mapHeight : yOrigin - 1; + } + + bool darkOnLight = (reg.OffColor > reg.OnColor); + int jumpThreshold = Math.Abs((int)(0.4 * (reg.OffColor - reg.OnColor) + 0.5)); + + if (jumpThreshold < 0) + { + throw new Exception("Negative jump threshold is not allowed in tally module jumps"); + } + + + for (line = lineStart; line < lineStop; line++) + { + + /* Capture tModule for each leading border module as normal but + decide status based on predictable barcode border pattern */ + + int travel = travelStart; + int color = horizontal ? this.ReadModuleColor(reg, line, travel, reg.SizeIdx, reg.FlowBegin.Plane) + : this.ReadModuleColor(reg, travel, line, reg.SizeIdx, reg.FlowBegin.Plane); + int tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor; + + int statusModule = (travelStep == 1 || (line & 0x01) == 0) ? DmtxConstants.DmtxModuleOnRGB : DmtxConstants.DmtxModuleOff; + + int weight = extent; + + while ((travel += travelStep) != travelStop) + { + + int tPrev = tModule; + int statusPrev = statusModule; + + /* For normal data-bearing modules capture color and decide + module status based on comparison to previous "known" module */ + + color = horizontal ? this.ReadModuleColor(reg, line, travel, reg.SizeIdx, reg.FlowBegin.Plane) + : this.ReadModuleColor(reg, travel, line, reg.SizeIdx, reg.FlowBegin.Plane); + tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor; + + if (statusPrev == DmtxConstants.DmtxModuleOnRGB) + { + statusModule = tModule < tPrev - jumpThreshold ? DmtxConstants.DmtxModuleOff : DmtxConstants.DmtxModuleOnRGB; + } + else if (statusPrev == DmtxConstants.DmtxModuleOff) + { + statusModule = tModule > tPrev + jumpThreshold ? DmtxConstants.DmtxModuleOnRGB : DmtxConstants.DmtxModuleOff; + } + int mapCol; + int mapRow; + if (horizontal) + { + mapRow = line - yOrigin; + mapCol = travel - xOrigin; + } + else + { + mapRow = travel - yOrigin; + mapCol = line - xOrigin; + } + if (!(mapRow < 24 && mapCol < 24)) + { + throw new Exception("Tally module mump failed, index out of range!"); + } + + if (statusModule == DmtxConstants.DmtxModuleOnRGB) + { + tally[mapRow, mapCol] += (2 * weight); + } + + weight--; + } + + if (weight != 0) + { + throw new Exception("Tally module jump failed, weight <> 0!"); + } + } + } + + private int ReadModuleColor(DmtxRegion reg, int symbolRow, int symbolCol, DmtxSymbolSize sizeIdx, int colorPlane) + { + int i; + int color; + double[] sampleX = { 0.5, 0.4, 0.5, 0.6, 0.5 }; + double[] sampleY = { 0.5, 0.5, 0.4, 0.5, 0.6 }; + DmtxVector2 p = new DmtxVector2(); + + int symbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, sizeIdx); + int symbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, sizeIdx); + + int colorTmp = color = 0; + for (i = 0; i < 5; i++) + { + + p.X = (1.0 / symbolCols) * (symbolCol + sampleX[i]); + p.Y = (1.0 / symbolRows) * (symbolRow + sampleY[i]); + + p *= reg.Fit2Raw; + + GetPixelValue((int)(p.X + 0.5), (int)(p.Y + 0.5), colorPlane, ref colorTmp); + color += colorTmp; + } + + return color / 5; + } + + internal static int ModulePlacementEcc200(byte[] modules, byte[] codewords, DmtxSymbolSize sizeIdx, int moduleOnColor) + { + if ((moduleOnColor & (DmtxConstants.DmtxModuleOnRed | DmtxConstants.DmtxModuleOnGreen | DmtxConstants.DmtxModuleOnBlue)) == 0) + { + throw new Exception("Error with module placement ECC 200"); + } + + int mappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, sizeIdx); + int mappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, sizeIdx); + + /* Start in the nominal location for the 8th bit of the first character */ + int chr = 0; + int row = 4; + int col = 0; + + do + { + /* Repeatedly first check for one of the special corner cases */ + if ((row == mappingRows) && (col == 0)) + PatternShapeSpecial1(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor); + else if ((row == mappingRows - 2) && (col == 0) && (mappingCols % 4 != 0)) + PatternShapeSpecial2(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor); + else if ((row == mappingRows - 2) && (col == 0) && (mappingCols % 8 == 4)) + PatternShapeSpecial3(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor); + else if ((row == mappingRows + 4) && (col == 2) && (mappingCols % 8 == 0)) + PatternShapeSpecial4(modules, mappingRows, mappingCols, codewords, chr++, moduleOnColor); + + /* Sweep upward diagonally, inserting successive characters */ + do + { + if ((row < mappingRows) && (col >= 0) && (modules[row * mappingCols + col] & DmtxConstants.DmtxModuleVisited) == 0) + PatternShapeStandard(modules, mappingRows, mappingCols, row, col, codewords, chr++, moduleOnColor); + row -= 2; + col += 2; + } while ((row >= 0) && (col < mappingCols)); + row += 1; + col += 3; + + /* Sweep downward diagonally, inserting successive characters */ + do + { + if ((row >= 0) && (col < mappingCols) && (modules[row * mappingCols + col] & DmtxConstants.DmtxModuleVisited) == 0) + PatternShapeStandard(modules, mappingRows, mappingCols, row, col, codewords, chr++, moduleOnColor); + row += 2; + col -= 2; + } while ((row < mappingRows) && (col >= 0)); + row += 3; + col += 1; + /* ... until the entire modules array is scanned */ + } while ((row < mappingRows) || (col < mappingCols)); + + /* If lower righthand corner is untouched then fill in the fixed pattern */ + if ((modules[mappingRows * mappingCols - 1] & + DmtxConstants.DmtxModuleVisited) == 0) + { + + modules[mappingRows * mappingCols - 1] |= (byte)moduleOnColor; + modules[(mappingRows * mappingCols) - mappingCols - 2] |= (byte)moduleOnColor; + } /* XXX should this fixed pattern also be used in reading somehow? */ + + /* XXX compare that chr == region->dataSize here */ + return chr; /* XXX number of codewords read off */ + } + + internal static void PatternShapeStandard(byte[] modules, int mappingRows, int mappingCols, int row, int col, byte[] codeword, int codeWordIndex, int moduleOnColor) + { + PlaceModule(modules, mappingRows, mappingCols, row - 2, col - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, row - 2, col - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, row - 1, col - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, row - 1, col - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, row - 1, col, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, row, col - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, row, col - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, row, col, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor); + } + + internal static void PatternShapeSpecial1(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor) + { + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 2, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 3, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor); + } + + internal static void PatternShapeSpecial2(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor) + { + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 3, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 2, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 4, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 3, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor); + } + + internal static void PatternShapeSpecial3(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor) + { + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 3, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 2, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 2, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 3, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor); + } + + internal static void PatternShapeSpecial4(byte[] modules, int mappingRows, int mappingCols, byte[] codeword, int codeWordIndex, int moduleOnColor) + { + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, 0, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit1, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, mappingRows - 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit2, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 3, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit3, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit4, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 0, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit5, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 3, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit6, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 2, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit7, moduleOnColor); + PlaceModule(modules, mappingRows, mappingCols, 1, mappingCols - 1, codeword, codeWordIndex, DmtxMaskBit.DmtxMaskBit8, moduleOnColor); + } + + internal static void PlaceModule(byte[] modules, int mappingRows, int mappingCols, int row, int col, byte[] codeword, int codeWordIndex, DmtxMaskBit mask, int moduleOnColor) + { + if (row < 0) + { + row += mappingRows; + col += 4 - ((mappingRows + 4) % 8); + } + if (col < 0) + { + col += mappingCols; + row += 4 - ((mappingCols + 4) % 8); + } + + /* If module has already been assigned then we are decoding the pattern into codewords */ + if ((modules[row * mappingCols + col] & DmtxConstants.DmtxModuleAssigned) != 0) + { + if ((modules[row * mappingCols + col] & moduleOnColor) != 0) + codeword[codeWordIndex] |= (byte)mask; + else + codeword[codeWordIndex] &= (byte)(0xff ^ (int)mask); + } + /* Otherwise we are encoding the codewords into a pattern */ + else + { + if ((codeword[codeWordIndex] & (byte)mask) != 0x00) + modules[row * mappingCols + col] |= (byte)moduleOnColor; + + modules[row * mappingCols + col] |= (byte)DmtxConstants.DmtxModuleAssigned; + } + + modules[row * mappingCols + col] |= (byte)DmtxConstants.DmtxModuleVisited; + } + + internal DmtxRegion RegionFindNext(TimeSpan timeout) + { + DmtxPixelLoc loc = new DmtxPixelLoc(); + DateTime startTime = DateTime.Now; + /* Continue until we find a region or run out of chances */ + for (; ; ) + { + DmtxRange locStatus = this._grid.PopGridLocation(ref loc); + if (locStatus == DmtxRange.DmtxRangeEnd) + break; + + /* Scan location for presence of valid barcode region */ + DmtxRegion reg = RegionScanPixel(loc.X, loc.Y); + if (reg != null) + return reg; + + /* Ran out of time? */ + if (DateTime.Now - startTime > timeout) + { + break; + } + } + + return null; + } + + DmtxRegion RegionScanPixel(int x, int y) + { + DmtxRegion reg = new DmtxRegion(); + DmtxPixelLoc loc = new DmtxPixelLoc {X = x, Y = y}; + + + int cacheIndex = this.DecodeGetCache(loc.X, loc.Y); + if (cacheIndex == -1) + return null; + + if (this._cache[cacheIndex] != 0x00) + return null; + + /* Test for presence of any reasonable edge at this location */ + DmtxPointFlow flowBegin = this.MatrixRegionSeekEdge(loc); + if (flowBegin.Mag < (int)(this._edgeThresh * 7.65 + 0.5)) + return null; + + /* Determine barcode orientation */ + if (MatrixRegionOrientation(reg, flowBegin) == false) + return null; + if (RegionUpdateXfrms(reg) == false) + return null; + + /* Define top edge */ + if (MatrixRegionAlignCalibEdge(reg, DmtxEdge.DmtxEdgeTop) == false) + return null; + if (RegionUpdateXfrms(reg) == false) + return null; + + /* Define right edge */ + if (MatrixRegionAlignCalibEdge(reg, DmtxEdge.DmtxEdgeRight) == false) + return null; + if (RegionUpdateXfrms(reg) == false) + return null; + + //CALLBACK_MATRIX(®); + + /* Calculate the best fitting symbol size */ + if (MatrixRegionFindSize(reg) == false) + return null; + + /* Found a valid matrix region */ + return new DmtxRegion(reg); + } + + int DecodeGetCache(int x, int y) + { + int width = this.Width; + int height = this.Height; + + if (x < 0 || x >= width || y < 0 || y >= height) + return DmtxConstants.DmtxUndefined; + return y * width + x; + } + + DmtxPointFlow MatrixRegionSeekEdge(DmtxPixelLoc loc) + { + DmtxPointFlow[] flowPlane = new DmtxPointFlow[3]; + + int channelCount = _image.ChannelCount; + + /* Find whether red, green, or blue shows the strongest edge */ + int strongIdx = 0; + for (int i = 0; i < channelCount; i++) + { + flowPlane[i] = GetPointFlow(i, loc, DmtxConstants.DmtxNeighborNone); + if (i > 0 && flowPlane[i].Mag > flowPlane[strongIdx].Mag) + strongIdx = i; + } + + if (flowPlane[strongIdx].Mag < 10) + return DmtxConstants.DmtxBlankEdge; + + DmtxPointFlow flow = flowPlane[strongIdx]; + + DmtxPointFlow flowPos = this.FindStrongestNeighbor(flow, +1); + DmtxPointFlow flowNeg = this.FindStrongestNeighbor(flow, -1); + if (flowPos.Mag != 0 && flowNeg.Mag != 0) + { + DmtxPointFlow flowPosBack = FindStrongestNeighbor(flowPos, -1); + DmtxPointFlow flowNegBack = FindStrongestNeighbor(flowNeg, +1); + if (flowPos.Arrive == (flowPosBack.Arrive + 4) % 8 && + flowNeg.Arrive == (flowNegBack.Arrive + 4) % 8) + { + flow.Arrive = DmtxConstants.DmtxNeighborNone; + //CALLBACK_POINT_PLOT(flow.Loc, 1, 1, 1); + return flow; + } + } + + return DmtxConstants.DmtxBlankEdge; + } + + DmtxPointFlow FindStrongestNeighbor(DmtxPointFlow center, int sign) + { + DmtxPixelLoc loc = new DmtxPixelLoc(); + DmtxPointFlow[] flow = new DmtxPointFlow[8]; + + int attempt = (sign < 0) ? center.Depart : (center.Depart + 4) % 8; + + int occupied = 0; + int strongIdx = DmtxConstants.DmtxUndefined; + for (int i = 0; i < 8; i++) + { + + loc.X = center.Loc.X + DmtxConstants.DmtxPatternX[i]; + loc.Y = center.Loc.Y + DmtxConstants.DmtxPatternY[i]; + int cacheIndex = DecodeGetCache(loc.X, loc.Y); + if (cacheIndex == DmtxConstants.DmtxUndefined) + { + continue; + } + + if ((this._cache[cacheIndex] & 0x80) != 0x00) + { + if (++occupied > 2) + return DmtxConstants.DmtxBlankEdge; + continue; + } + + int attemptDiff = Math.Abs(attempt - i); + if (attemptDiff > 4) + attemptDiff = 8 - attemptDiff; + if (attemptDiff > 1) + continue; + + flow[i] = GetPointFlow(center.Plane, loc, i); + + if (strongIdx == DmtxConstants.DmtxUndefined || flow[i].Mag > flow[strongIdx].Mag || + (flow[i].Mag == flow[strongIdx].Mag && ((i & 0x01) != 0))) + { + strongIdx = i; + } + } + + return (strongIdx == DmtxConstants.DmtxUndefined) ? DmtxConstants.DmtxBlankEdge : flow[strongIdx]; + } + + DmtxPointFlow GetPointFlow(int colorPlane, DmtxPixelLoc loc, int arrive) + { + int[] coefficient = new[] { 0, 1, 2, 1, 0, -1, -2, -1 }; + int patternIdx; + int compass; + int[] mag = new int[4]; + int[] colorPattern = new int[8]; + DmtxPointFlow flow = new DmtxPointFlow(); + + for (patternIdx = 0; patternIdx < 8; patternIdx++) + { + int xAdjust = loc.X + DmtxConstants.DmtxPatternX[patternIdx]; + int yAdjust = loc.Y + DmtxConstants.DmtxPatternY[patternIdx]; + bool err = GetPixelValue(xAdjust, yAdjust, colorPlane, ref colorPattern[patternIdx]); + if (err == false) + { + return DmtxConstants.DmtxBlankEdge; + } + } + + /* Calculate this pixel's flow intensity for each direction (-45, 0, 45, 90) */ + int compassMax = 0; + for (compass = 0; compass < 4; compass++) + { + + /* Add portion from each position in the convolution matrix pattern */ + for (patternIdx = 0; patternIdx < 8; patternIdx++) + { + + int coefficientIdx = (patternIdx - compass + 8) % 8; + if (coefficient[coefficientIdx] == 0) + continue; + + int color = colorPattern[patternIdx]; + + switch (coefficient[coefficientIdx]) + { + case 2: + mag[compass] += 2 * color; + break; + case 1: + mag[compass] += color; + break; + case -2: + mag[compass] -= 2 * color; + break; + case -1: + mag[compass] -= color; + break; + } + } + + /* Identify strongest compass flow */ + if (compass != 0 && Math.Abs(mag[compass]) > Math.Abs(mag[compassMax])) + compassMax = compass; + } + + /* Convert signed compass direction into unique flow directions (0-7) */ + flow.Plane = colorPlane; + flow.Arrive = arrive; + flow.Depart = (mag[compassMax] > 0) ? compassMax + 4 : compassMax; + flow.Mag = Math.Abs(mag[compassMax]); + flow.Loc = loc; + + return flow; + } + + bool MatrixRegionFindSize(DmtxRegion reg) + { + DmtxSymbolSize sizeIdxBeg, sizeIdxEnd; + DmtxSymbolSize sizeIdx; + int bestColorOffAvg; + + DmtxSymbolSize bestSizeIdx = DmtxSymbolSize.DmtxSymbolShapeAuto; + int bestContrast = 0; + int bestColorOnAvg = bestColorOffAvg = 0; + + if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolShapeAuto) + { + sizeIdxBeg = 0; + sizeIdxEnd = (DmtxSymbolSize)(DmtxConstants.DmtxSymbolSquareCount + DmtxConstants.DmtxSymbolRectCount); + } + else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolSquareAuto) + { + sizeIdxBeg = 0; + sizeIdxEnd = (DmtxSymbolSize)DmtxConstants.DmtxSymbolSquareCount; + } + else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolRectAuto) + { + sizeIdxBeg = (DmtxSymbolSize)DmtxConstants.DmtxSymbolSquareCount; + sizeIdxEnd = (DmtxSymbolSize)(DmtxConstants.DmtxSymbolSquareCount + DmtxConstants.DmtxSymbolRectCount); + } + else + { + sizeIdxBeg = this._sizeIdxExpected; + sizeIdxEnd = this._sizeIdxExpected + 1; + } + + /* Test each barcode size to find best contrast in calibration modules */ + for (sizeIdx = sizeIdxBeg; sizeIdx < sizeIdxEnd; sizeIdx++) + { + + int symbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, sizeIdx); + int symbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, sizeIdx); + int colorOffAvg; + int colorOnAvg = colorOffAvg = 0; + + /* Sum module colors along horizontal calibration bar */ + int row = symbolRows - 1; + int col; + int color; + for (col = 0; col < symbolCols; col++) + { + color = ReadModuleColor(reg, row, col, sizeIdx, reg.FlowBegin.Plane); + if ((col & 0x01) != 0x00) + colorOffAvg += color; + else + colorOnAvg += color; + } + + /* Sum module colors along vertical calibration bar */ + col = symbolCols - 1; + for (row = 0; row < symbolRows; row++) + { + color = ReadModuleColor(reg, row, col, sizeIdx, reg.FlowBegin.Plane); + if ((row & 0x01) != 0x00) + colorOffAvg += color; + else + colorOnAvg += color; + } + + colorOnAvg = (colorOnAvg * 2) / (symbolRows + symbolCols); + colorOffAvg = (colorOffAvg * 2) / (symbolRows + symbolCols); + + int contrast = Math.Abs(colorOnAvg - colorOffAvg); + if (contrast < 20) + continue; + + if (contrast > bestContrast) + { + bestContrast = contrast; + bestSizeIdx = sizeIdx; + bestColorOnAvg = colorOnAvg; + bestColorOffAvg = colorOffAvg; + } + } + + /* If no sizes produced acceptable contrast then call it quits */ + if (bestSizeIdx == DmtxSymbolSize.DmtxSymbolShapeAuto || bestContrast < 20) + return false; + + reg.SizeIdx = bestSizeIdx; + reg.OnColor = bestColorOnAvg; + reg.OffColor = bestColorOffAvg; + + reg.SymbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, reg.SizeIdx); + reg.SymbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, reg.SizeIdx); + reg.MappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, reg.SizeIdx); + reg.MappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, reg.SizeIdx); + + /* Tally jumps on horizontal calibration bar to verify sizeIdx */ + int jumpCount = this.CountJumpTally(reg, 0, reg.SymbolRows - 1, DmtxDirection.DmtxDirRight); + int errors = Math.Abs(1 + jumpCount - reg.SymbolCols); + if (jumpCount < 0 || errors > 2) + return false; + + /* Tally jumps on vertical calibration bar to verify sizeIdx */ + jumpCount = CountJumpTally(reg, reg.SymbolCols - 1, 0, DmtxDirection.DmtxDirUp); + errors = Math.Abs(1 + jumpCount - reg.SymbolRows); + if (jumpCount < 0 || errors > 2) + return false; + + /* Tally jumps on horizontal finder bar to verify sizeIdx */ + errors = CountJumpTally(reg, 0, 0, DmtxDirection.DmtxDirRight); + if (jumpCount < 0 || errors > 2) + return false; + + /* Tally jumps on vertical finder bar to verify sizeIdx */ + errors = CountJumpTally(reg, 0, 0, DmtxDirection.DmtxDirUp); + if (errors < 0 || errors > 2) + return false; + + /* Tally jumps on surrounding whitespace, else fail */ + errors = CountJumpTally(reg, 0, -1, DmtxDirection.DmtxDirRight); + if (errors < 0 || errors > 2) + return false; + + errors = CountJumpTally(reg, -1, 0, DmtxDirection.DmtxDirUp); + if (errors < 0 || errors > 2) + return false; + + errors = CountJumpTally(reg, 0, reg.SymbolRows, DmtxDirection.DmtxDirRight); + if (errors < 0 || errors > 2) + return false; + + errors = CountJumpTally(reg, reg.SymbolCols, 0, DmtxDirection.DmtxDirUp); + if (errors < 0 || errors > 2) + return false; + + return true; + } + + int CountJumpTally(DmtxRegion reg, int xStart, int yStart, DmtxDirection dir) + { + int x, xInc = 0; + int y, yInc = 0; + int state = DmtxConstants.DmtxModuleOn; + int jumpCount = 0; + + if (xStart != 0 && yStart != 0) + { + throw new Exception("CountJumpTally failed, xStart or yStart must be zero!"); + } + + if (dir == DmtxDirection.DmtxDirRight) + { + xInc = 1; + } + else + { + yInc = 1; + } + + if (xStart == -1 || xStart == reg.SymbolCols || + yStart == -1 || yStart == reg.SymbolRows) + { + state = DmtxConstants.DmtxModuleOff; + } + + bool darkOnLight = (reg.OffColor > reg.OnColor); + int jumpThreshold = Math.Abs((int)(0.4 * (reg.OnColor - reg.OffColor) + 0.5)); + int color = this.ReadModuleColor(reg, yStart, xStart, reg.SizeIdx, reg.FlowBegin.Plane); + int tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor; + + for (x = xStart + xInc, y = yStart + yInc; + (dir == DmtxDirection.DmtxDirRight && x < reg.SymbolCols) || + (dir == DmtxDirection.DmtxDirUp && y < reg.SymbolRows); + x += xInc, y += yInc) + { + + int tPrev = tModule; + color = ReadModuleColor(reg, y, x, reg.SizeIdx, reg.FlowBegin.Plane); + tModule = (darkOnLight) ? reg.OffColor - color : color - reg.OffColor; + + if (state == DmtxConstants.DmtxModuleOff) + { + if (tModule > tPrev + jumpThreshold) + { + jumpCount++; + state = DmtxConstants.DmtxModuleOn; + } + } + else + { + if (tModule < tPrev - jumpThreshold) + { + jumpCount++; + state = DmtxConstants.DmtxModuleOff; + } + } + } + + return jumpCount; + } + + bool MatrixRegionOrientation(DmtxRegion reg, DmtxPointFlow begin) + { + int cross; + DmtxSymbolSize symbolShape; + int maxDiagonal; + DmtxBestLine line2X; + + if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolSquareAuto || + (this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol10x10 && + this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol144x144)) + symbolShape = DmtxSymbolSize.DmtxSymbolSquareAuto; + else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolRectAuto || + (this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol8x18 && + this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol16x48)) + symbolShape = DmtxSymbolSize.DmtxSymbolRectAuto; + else + symbolShape = DmtxSymbolSize.DmtxSymbolShapeAuto; + + if (_edgeMax != DmtxConstants.DmtxUndefined) + { + if (symbolShape == DmtxSymbolSize.DmtxSymbolRectAuto) + maxDiagonal = (int)(1.23 * _edgeMax + 0.5); /* sqrt(5/4) + 10% */ + else + maxDiagonal = (int)(1.56 * _edgeMax + 0.5); /* sqrt(2) + 10% */ + } + else + { + maxDiagonal = DmtxConstants.DmtxUndefined; + } + + /* Follow to end in both directions */ + bool err = this.TrailBlazeContinuous(reg, begin, maxDiagonal); + if (err == false || reg.StepsTotal < 40) + { + TrailClear(reg, 0x40); + return false; + } + + /* Filter out region candidates that are smaller than expected */ + if (this._edgeMin != DmtxConstants.DmtxUndefined) + { + int scale = _scale; + + int minArea; + if (symbolShape == DmtxSymbolSize.DmtxSymbolSquareAuto) + minArea = (this._edgeMin * this._edgeMin) / (scale * scale); + else + minArea = (2 * this._edgeMin * this._edgeMin) / (scale * scale); + + if ((reg.BoundMax.X - reg.BoundMin.X) * (reg.BoundMax.Y - reg.BoundMin.Y) < minArea) + { + TrailClear(reg, 0x40); + return false; + } + } + + DmtxBestLine line1X = this.FindBestSolidLine(reg, 0, 0, 1, DmtxConstants.DmtxUndefined); + if (line1X.Mag < 5) + { + TrailClear(reg, 0x40); + return false; + } + + this.FindTravelLimits(reg, ref line1X); + if (line1X.DistSq < 100 || line1X.Devn * 10 >= Math.Sqrt(line1X.DistSq)) + { + TrailClear(reg, 0x40); + return false; + } + if (!(line1X.StepPos >= line1X.StepNeg)) + { + throw new Exception("Error calculating matrix region orientation"); + } + + DmtxFollow fTmp = this.FollowSeek(reg, line1X.StepPos + 5); + DmtxBestLine line2P = this.FindBestSolidLine(reg, fTmp.Step, line1X.StepNeg, 1, line1X.Angle); + + fTmp = FollowSeek(reg, line1X.StepNeg - 5); + DmtxBestLine line2N = this.FindBestSolidLine(reg, fTmp.Step, line1X.StepPos, -1, line1X.Angle); + if (DmtxCommon.Max(line2P.Mag, line2N.Mag) < 5) + return false; + + if (line2P.Mag > line2N.Mag) + { + line2X = line2P; + this.FindTravelLimits(reg, ref line2X); + if (line2X.DistSq < 100 || line2X.Devn * 10 >= Math.Sqrt(line2X.DistSq)) + return false; + + cross = ((line1X.LocPos.X - line1X.LocNeg.X) * (line2X.LocPos.Y - line2X.LocNeg.Y)) - + ((line1X.LocPos.Y - line1X.LocNeg.Y) * (line2X.LocPos.X - line2X.LocNeg.X)); + if (cross > 0) + { + /* Condition 2 */ + reg.Polarity = +1; + reg.LocR = line2X.LocPos; + reg.StepR = line2X.StepPos; + reg.LocT = line1X.LocNeg; + reg.StepT = line1X.StepNeg; + reg.LeftLoc = line1X.LocBeg; + reg.LeftAngle = line1X.Angle; + reg.BottomLoc = line2X.LocBeg; + reg.BottomAngle = line2X.Angle; + reg.LeftLine = line1X; + reg.BottomLine = line2X; + } + else + { + /* Condition 3 */ + reg.Polarity = -1; + reg.LocR = line1X.LocNeg; + reg.StepR = line1X.StepNeg; + reg.LocT = line2X.LocPos; + reg.StepT = line2X.StepPos; + reg.LeftLoc = line2X.LocBeg; + reg.LeftAngle = line2X.Angle; + reg.BottomLoc = line1X.LocBeg; + reg.BottomAngle = line1X.Angle; + reg.LeftLine = line2X; + reg.BottomLine = line1X; + } + } + else + { + line2X = line2N; + this.FindTravelLimits(reg, ref line2X); + if (line2X.DistSq < 100 || line2X.Devn / Math.Sqrt(line2X.DistSq) >= 0.1) + return false; + + cross = ((line1X.LocNeg.X - line1X.LocPos.X) * (line2X.LocNeg.Y - line2X.LocPos.Y)) - + ((line1X.LocNeg.Y - line1X.LocPos.Y) * (line2X.LocNeg.X - line2X.LocPos.X)); + if (cross > 0) + { + /* Condition 1 */ + reg.Polarity = -1; + reg.LocR = line2X.LocNeg; + reg.StepR = line2X.StepNeg; + reg.LocT = line1X.LocPos; + reg.StepT = line1X.StepPos; + reg.LeftLoc = line1X.LocBeg; + reg.LeftAngle = line1X.Angle; + reg.BottomLoc = line2X.LocBeg; + reg.BottomAngle = line2X.Angle; + reg.LeftLine = line1X; + reg.BottomLine = line2X; + } + else + { + /* Condition 4 */ + reg.Polarity = +1; + reg.LocR = line1X.LocPos; + reg.StepR = line1X.StepPos; + reg.LocT = line2X.LocNeg; + reg.StepT = line2X.StepNeg; + reg.LeftLoc = line2X.LocBeg; + reg.LeftAngle = line2X.Angle; + reg.BottomLoc = line1X.LocBeg; + reg.BottomAngle = line1X.Angle; + reg.LeftLine = line2X; + reg.BottomLine = line1X; + } + } + + reg.LeftKnown = reg.BottomKnown = 1; + + return true; + } + + DmtxBestLine FindBestSolidLine(DmtxRegion reg, int step0, int step1, int streamDir, int houghAvoid) + { + int[,] hough = new int[3, DmtxConstants.DmtxHoughRes]; + char[] houghTest = new char[DmtxConstants.DmtxHoughRes]; + int i; + int step; + int sign = 0; + int tripSteps = 0; + DmtxBestLine line = new DmtxBestLine(); + + int angleBest = 0; + int hOffsetBest = 0; + + /* Always follow path flowing away from the trail start */ + if (step0 != 0) + { + if (step0 > 0) + { + sign = +1; + tripSteps = (step1 - step0 + reg.StepsTotal) % reg.StepsTotal; + } + else + { + sign = -1; + tripSteps = (step0 - step1 + reg.StepsTotal) % reg.StepsTotal; + } + if (tripSteps == 0) + tripSteps = reg.StepsTotal; + } + else if (step1 != 0) + { + sign = (step1 > 0) ? +1 : -1; + tripSteps = Math.Abs(step1); + } + else if (step1 == 0) + { + sign = +1; + tripSteps = reg.StepsTotal; + } + if (sign != streamDir) + { + throw new Exception("Sign must equal stream direction!"); + } + + DmtxFollow follow = this.FollowSeek(reg, step0); + DmtxPixelLoc rHp = follow.Loc; + + line.StepBeg = line.StepPos = line.StepNeg = step0; + line.LocBeg = follow.Loc; + line.LocPos = follow.Loc; + line.LocNeg = follow.Loc; + + /* Predetermine which angles to test */ + for (i = 0; i < DmtxConstants.DmtxHoughRes; i++) + { + if (houghAvoid == DmtxConstants.DmtxUndefined) + { + houghTest[i] = (char)1; + } + else + { + int houghMin = (houghAvoid + DmtxConstants.DmtxHoughRes / 6) % DmtxConstants.DmtxHoughRes; + int houghMax = (houghAvoid - DmtxConstants.DmtxHoughRes / 6 + DmtxConstants.DmtxHoughRes) % DmtxConstants.DmtxHoughRes; + if (houghMin > houghMax) + houghTest[i] = (i > houghMin || i < houghMax) ? (char)1 : (char)0; + else + houghTest[i] = (i > houghMin && i < houghMax) ? (char)1 : (char)0; + } + } + + /* Test each angle for steps along path */ + for (step = 0; step < tripSteps; step++) + { + + int xDiff = follow.Loc.X - rHp.X; + int yDiff = follow.Loc.Y - rHp.Y; + + /* Increment Hough accumulator */ + for (i = 0; i < DmtxConstants.DmtxHoughRes; i++) + { + + if (houghTest[i] == 0) + continue; + + int dH = (DmtxConstants.rHvX[i] * yDiff) - (DmtxConstants.rHvY[i] * xDiff); + if (dH >= -384 && dH <= 384) + { + int hOffset; + if (dH > 128) + hOffset = 2; + else if (dH >= -128) + hOffset = 1; + else + hOffset = 0; + + hough[hOffset, i]++; + + /* New angle takes over lead */ + if (hough[hOffset, i] > hough[hOffsetBest, angleBest]) + { + angleBest = i; + hOffsetBest = hOffset; + } + } + } + + /* CALLBACK_POINT_PLOT(follow.loc, (sign > 1) ? 4 : 3, 1, 2); */ + + follow = FollowStep(reg, follow, sign); + } + + line.Angle = angleBest; + line.HOffset = hOffsetBest; + line.Mag = hough[hOffsetBest, angleBest]; + + return line; + } + + DmtxFollow FollowSeek(DmtxRegion reg, int seek) + { + int i; + DmtxFollow follow = new DmtxFollow {Loc = reg.FlowBegin.Loc, Step = 0, Ptr = this._cache}; + + follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y); + + int sign = (seek > 0) ? +1 : -1; + for (i = 0; i != seek; i += sign) + { + follow = FollowStep(reg, follow, sign); + if (Math.Abs(follow.Step) > reg.StepsTotal) + { + throw new Exception("Follow step count larger total step count!"); + } + } + return follow; + } + + + bool TrailBlazeContinuous(DmtxRegion reg, DmtxPointFlow flowBegin, int maxDiagonal) + { + int negAssigns; + int sign; + DmtxPixelLoc boundMax; + + DmtxPixelLoc boundMin = boundMax = flowBegin.Loc; + int cacheBegIndex = DecodeGetCache(flowBegin.Loc.X, flowBegin.Loc.Y); + this._cache[cacheBegIndex] = 0x80 | 0x40; + + reg.FlowBegin = flowBegin; + + int posAssigns = negAssigns = 0; + for (sign = 1; sign >= -1; sign -= 2) + { + + DmtxPointFlow flow = flowBegin; + int cacheIndex = cacheBegIndex; + + int steps; + for (steps = 0; ; steps++) + { + + if (maxDiagonal != DmtxConstants.DmtxUndefined && (boundMax.X - boundMin.X > maxDiagonal || + boundMax.Y - boundMin.Y > maxDiagonal)) + break; + + /* Find the strongest eligible neighbor */ + DmtxPointFlow flowNext = FindStrongestNeighbor(flow, sign); + if (flowNext.Mag < 50) + break; + + /* Get the neighbor's cache location */ + int cacheNextIndex = DecodeGetCache(flowNext.Loc.X, flowNext.Loc.Y); + if ((this._cache[cacheNextIndex] & 0x80) != 0) + { + throw new Exception("Error creating Trail Blaze"); + } + + /* Mark departure from current location. If flowing downstream + * (sign < 0) then departure vector here is the arrival vector + * of the next location. Upstream flow uses the opposite rule. */ + this._cache[cacheIndex] |= (sign < 0) ? (byte)(flowNext.Arrive) : (byte)(flowNext.Arrive << 3); + + /* Mark known direction for next location */ + /* If testing downstream (sign < 0) then next upstream is opposite of next arrival */ + /* If testing upstream (sign > 0) then next downstream is opposite of next arrival */ + this._cache[cacheNextIndex] = (sign < 0) ? (byte)(((flowNext.Arrive + 4) % 8) << 3) : (byte)((flowNext.Arrive + 4) % 8); + this._cache[cacheNextIndex] |= (0x80 | 0x40); /* Mark location as visited and assigned */ + + if (sign > 0) + posAssigns++; + else + negAssigns++; + cacheIndex = cacheNextIndex; + flow = flowNext; + + if (flow.Loc.X > boundMax.X) + boundMax.X = flow.Loc.X; + else if (flow.Loc.X < boundMin.X) + boundMin.X = flow.Loc.X; + if (flow.Loc.Y > boundMax.Y) + boundMax.Y = flow.Loc.Y; + else if (flow.Loc.Y < boundMin.Y) + boundMin.Y = flow.Loc.Y; + + /* CALLBACK_POINT_PLOT(flow.loc, (sign > 0) ? 2 : 3, 1, 2); */ + } + + if (sign > 0) + { + reg.FinalPos = flow.Loc; + reg.JumpToNeg = steps; + } + else + { + reg.FinalNeg = flow.Loc; + reg.JumpToPos = steps; + } + } + reg.StepsTotal = reg.JumpToPos + reg.JumpToNeg; + reg.BoundMin = boundMin; + reg.BoundMax = boundMax; + + /* Clear "visited" bit from trail */ + int clears = this.TrailClear(reg, 0x80); + if (posAssigns + negAssigns != clears - 1) + { + throw new Exception("Error cleaning after trail blaze continuous"); + } + + /* XXX clean this up ... redundant test above */ + if (maxDiagonal != DmtxConstants.DmtxUndefined && (boundMax.X - boundMin.X > maxDiagonal || + boundMax.Y - boundMin.Y > maxDiagonal)) + return false; + + return true; + } + + int TrailClear(DmtxRegion reg, int clearMask) + { + if ((clearMask | 0xff) != 0xff) + { + throw new Exception("TrailClear mask is invalid!"); + } + + /* Clear "visited" bit from trail */ + int clears = 0; + DmtxFollow follow = this.FollowSeek(reg, 0); + while (Math.Abs(follow.Step) <= reg.StepsTotal) + { + if ((follow.CurrentPtr & clearMask) == 0x00) + { + throw new Exception("Error performing TrailClear"); + } + follow.CurrentPtr &= (byte)(clearMask ^ 0xff); + follow = FollowStep(reg, follow, +1); + clears++; + } + + return clears; + } + + DmtxFollow FollowStep(DmtxRegion reg, DmtxFollow followBeg, int sign) + { + int stepMod; + DmtxFollow follow = new DmtxFollow(); + + + if (Math.Abs(sign) != 1) + { + throw new Exception("Invalid parameter 'sign', can only be -1 or +1"); + } + + int factor = reg.StepsTotal + 1; + if (sign > 0) + stepMod = (factor + (followBeg.Step % factor)) % factor; + else + stepMod = (factor - (followBeg.Step % factor)) % factor; + + /* End of positive trail -- magic jump */ + if (sign > 0 && stepMod == reg.JumpToNeg) + { + follow.Loc = reg.FinalNeg; + } + /* End of negative trail -- magic jump */ + else if (sign < 0 && stepMod == reg.JumpToPos) + { + follow.Loc = reg.FinalPos; + } + /* Trail in progress -- normal jump */ + else + { + int patternIdx = (sign < 0) ? followBeg.Neighbor & 0x07 : ((followBeg.Neighbor & 0x38) >> 3); + follow.Loc = new DmtxPixelLoc { X = followBeg.Loc.X + DmtxConstants.DmtxPatternX[patternIdx], Y = followBeg.Loc.Y + DmtxConstants.DmtxPatternY[patternIdx] }; + } + + follow.Step = followBeg.Step + sign; + follow.Ptr = this._cache; + follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y); + + return follow; + } + + void FindTravelLimits(DmtxRegion reg, ref DmtxBestLine line) + { + int i; + int negTravel; + int posWanderMin, posWanderMax, posWanderMinLock, posWanderMaxLock; + int negWanderMin, negWanderMax, negWanderMinLock, negWanderMaxLock; + DmtxFollow followNeg; + DmtxPixelLoc negMax; + + /* line->stepBeg is already known to sit on the best Hough line */ + DmtxFollow followPos = followNeg = this.FollowSeek(reg, line.StepBeg); + DmtxPixelLoc loc0 = followPos.Loc; + + int cosAngle = DmtxConstants.rHvX[line.Angle]; + int sinAngle = DmtxConstants.rHvY[line.Angle]; + + int distSqMax = 0; + DmtxPixelLoc posMax = negMax = followPos.Loc; + + int posTravel = negTravel = 0; + int posWander = posWanderMin = posWanderMax = posWanderMinLock = posWanderMaxLock = 0; + int negWander = negWanderMin = negWanderMax = negWanderMinLock = negWanderMaxLock = 0; + + for (i = 0; i < reg.StepsTotal / 2; i++) + { + + bool posRunning = (i < 10 || Math.Abs(posWander) < Math.Abs(posTravel)); + bool negRunning = (i < 10 || Math.Abs(negWander) < Math.Abs(negTravel)); + + int distSq; + int xDiff; + int yDiff; + if (posRunning) + { + xDiff = followPos.Loc.X - loc0.X; + yDiff = followPos.Loc.Y - loc0.Y; + posTravel = (cosAngle * xDiff) + (sinAngle * yDiff); + posWander = (cosAngle * yDiff) - (sinAngle * xDiff); + + if (posWander >= -3 * 256 && posWander <= 3 * 256) + { + distSq = DistanceSquared(followPos.Loc, negMax); + if (distSq > distSqMax) + { + posMax = followPos.Loc; + distSqMax = distSq; + line.StepPos = followPos.Step; + line.LocPos = followPos.Loc; + posWanderMinLock = posWanderMin; + posWanderMaxLock = posWanderMax; + } + } + else + { + posWanderMin = DmtxCommon.Min(posWanderMin, posWander); + posWanderMax = DmtxCommon.Max(posWanderMax, posWander); + } + } + else if (!negRunning) + { + break; + } + + if (negRunning) + { + xDiff = followNeg.Loc.X - loc0.X; + yDiff = followNeg.Loc.Y - loc0.Y; + negTravel = (cosAngle * xDiff) + (sinAngle * yDiff); + negWander = (cosAngle * yDiff) - (sinAngle * xDiff); + + if (negWander >= -3 * 256 && negWander < 3 * 256) + { + distSq = DistanceSquared(followNeg.Loc, posMax); + if (distSq > distSqMax) + { + negMax = followNeg.Loc; + distSqMax = distSq; + line.StepNeg = followNeg.Step; + line.LocNeg = followNeg.Loc; + negWanderMinLock = negWanderMin; + negWanderMaxLock = negWanderMax; + } + } + else + { + negWanderMin = DmtxCommon.Min(negWanderMin, negWander); + negWanderMax = DmtxCommon.Max(negWanderMax, negWander); + } + } + + followPos = FollowStep(reg, followPos, +1); + followNeg = FollowStep(reg, followNeg, -1); + } + line.Devn = DmtxCommon.Max(posWanderMaxLock - posWanderMinLock, negWanderMaxLock - negWanderMinLock) / 256; + line.DistSq = distSqMax; + + return; + } + + int DistanceSquared(DmtxPixelLoc a, DmtxPixelLoc b) + { + int xDelta = a.X - b.X; + int yDelta = a.Y - b.Y; + + return (xDelta * xDelta) + (yDelta * yDelta); + } + + bool RegionUpdateXfrms(DmtxRegion reg) + { + DmtxRay2 rLeft = new DmtxRay2(); + DmtxRay2 rBottom = new DmtxRay2(); + DmtxRay2 rTop = new DmtxRay2(); + DmtxRay2 rRight = new DmtxRay2(); + DmtxVector2 p00 = new DmtxVector2(); + DmtxVector2 p10 = new DmtxVector2(); + DmtxVector2 p11 = new DmtxVector2(); + DmtxVector2 p01 = new DmtxVector2(); + + if (!(reg.LeftKnown != 0 && reg.BottomKnown != 0)) + { + throw new ArgumentException("Error updating Xfrms!"); + } + + /* Build ray representing left edge */ + rLeft.P.X = reg.LeftLoc.X; + rLeft.P.Y = reg.LeftLoc.Y; + double radians = reg.LeftAngle * (Math.PI / DmtxConstants.DmtxHoughRes); + rLeft.V.X = Math.Cos(radians); + rLeft.V.Y = Math.Sin(radians); + rLeft.TMin = 0.0; + rLeft.TMax = rLeft.V.Norm(); + + /* Build ray representing bottom edge */ + rBottom.P.X = reg.BottomLoc.X; + rBottom.P.Y = reg.BottomLoc.Y; + radians = reg.BottomAngle * (Math.PI / DmtxConstants.DmtxHoughRes); + rBottom.V.X = Math.Cos(radians); + rBottom.V.Y = Math.Sin(radians); + rBottom.TMin = 0.0; + rBottom.TMax = rBottom.V.Norm(); + + /* Build ray representing top edge */ + if (reg.TopKnown != 0) + { + rTop.P.X = reg.TopLoc.X; + rTop.P.Y = reg.TopLoc.Y; + radians = reg.TopAngle * (Math.PI / DmtxConstants.DmtxHoughRes); + rTop.V.X = Math.Cos(radians); + rTop.V.Y = Math.Sin(radians); + rTop.TMin = 0.0; + rTop.TMax = rTop.V.Norm(); + } + else + { + rTop.P.X = reg.LocT.X; + rTop.P.Y = reg.LocT.Y; + radians = reg.BottomAngle * (Math.PI / DmtxConstants.DmtxHoughRes); + rTop.V.X = Math.Cos(radians); + rTop.V.Y = Math.Sin(radians); + rTop.TMin = 0.0; + rTop.TMax = rBottom.TMax; + } + + /* Build ray representing right edge */ + if (reg.RightKnown != 0) + { + rRight.P.X = reg.RightLoc.X; + rRight.P.Y = reg.RightLoc.Y; + radians = reg.RightAngle * (Math.PI / DmtxConstants.DmtxHoughRes); + rRight.V.X = Math.Cos(radians); + rRight.V.Y = Math.Sin(radians); + rRight.TMin = 0.0; + rRight.TMax = rRight.V.Norm(); + } + else + { + rRight.P.X = reg.LocR.X; + rRight.P.Y = reg.LocR.Y; + radians = reg.LeftAngle * (Math.PI / DmtxConstants.DmtxHoughRes); + rRight.V.X = Math.Cos(radians); + rRight.V.Y = Math.Sin(radians); + rRight.TMin = 0.0; + rRight.TMax = rLeft.TMax; + } + + /* Calculate 4 corners, real or imagined */ + if (!p00.Intersect(rLeft, rBottom)) + return false; + + if (!p10.Intersect(rBottom, rRight)) + return false; + + if (!p11.Intersect(rRight, rTop)) + return false; + + if (!p01.Intersect(rTop, rLeft)) + return false; + + if (!RegionUpdateCorners(reg, p00, p10, p11, p01)) + return false; + + return true; + } + + bool RegionUpdateCorners(DmtxRegion reg, DmtxVector2 p00, + DmtxVector2 p10, DmtxVector2 p11, DmtxVector2 p01) + { + double xMax = this.Width - 1; + double yMax = this.Height - 1; + + if (p00.X < 0.0 || p00.Y < 0.0 || p00.X > xMax || p00.Y > yMax || + p01.X < 0.0 || p01.Y < 0.0 || p01.X > xMax || p01.Y > yMax || + p10.X < 0.0 || p10.Y < 0.0 || p10.X > xMax || p10.Y > yMax) + return false; + + DmtxVector2 vOT = p01 - p00; + DmtxVector2 vOR = p10 - p00; + DmtxVector2 vTX = p11 - p01; + DmtxVector2 vRX = p11 - p10; + double dimOT = vOT.Mag(); + double dimOR = vOR.Mag(); + double dimTX = vTX.Mag(); + double dimRX = vRX.Mag(); + + /* Verify that sides are reasonably long */ + if (dimOT <= 8.0 || dimOR <= 8.0 || dimTX <= 8.0 || dimRX <= 8.0) + return false; + + /* Verify that the 4 corners define a reasonably fat quadrilateral */ + double ratio = dimOT / dimRX; + if (ratio <= 0.5 || ratio >= 2.0) + return false; + + ratio = dimOR / dimTX; + if (ratio <= 0.5 || ratio >= 2.0) + return false; + + /* Verify this is not a bowtie shape */ + if (vOR.Cross(vRX) <= 0.0 || vOT.Cross(vTX) >= 0.0) + return false; + + if (DmtxCommon.RightAngleTrueness(p00, p10, p11, Math.PI / 2.0) <= this._squareDevn) + return false; + if (DmtxCommon.RightAngleTrueness(p10, p11, p01, Math.PI / 2.0) <= this._squareDevn) + return false; + + /* Calculate values needed for transformations */ + double tx = -1 * p00.X; + double ty = -1 * p00.Y; + DmtxMatrix3 mtxy = DmtxMatrix3.Translate(tx, ty); + + double phi = Math.Atan2(vOT.X, vOT.Y); + DmtxMatrix3 mphi = DmtxMatrix3.Rotate(phi); + DmtxMatrix3 m = mtxy * mphi; + + DmtxVector2 vTmp = p10 * m; + double shx = -vTmp.Y / vTmp.X; + DmtxMatrix3 mshx = DmtxMatrix3.Shear(0.0, shx); + m *= mshx; + + double scx = 1.0 / vTmp.X; + DmtxMatrix3 mscx = DmtxMatrix3.Scale(scx, 1.0); + m *= mscx; + vTmp = p11 * m; + + double scy = 1.0 / vTmp.Y; + DmtxMatrix3 mscy = DmtxMatrix3.Scale(1.0, scy); + m *= mscy; + + vTmp = p11 * m; + double skx = vTmp.X; + DmtxMatrix3 mskx = DmtxMatrix3.LineSkewSide(1.0, skx, 1.0); + m *= mskx; + + vTmp = p01 * m; + double sky = vTmp.Y; + DmtxMatrix3 msky = DmtxMatrix3.LineSkewTop(sky, 1.0, 1.0); + reg.Raw2Fit = m * msky; + + /* Create inverse matrix by reverse (avoid straight matrix inversion) */ + msky = DmtxMatrix3.LineSkewTopInv(sky, 1.0, 1.0); + mskx = DmtxMatrix3.LineSkewSideInv(1.0, skx, 1.0); + m = msky * mskx; + + DmtxMatrix3 mscxy = DmtxMatrix3.Scale(1.0 / scx, 1.0 / scy); + m *= mscxy; + + mshx = DmtxMatrix3.Shear(0.0, -shx); + m *= mshx; + + mphi = DmtxMatrix3.Rotate(-phi); + m *= mphi; + + mtxy = DmtxMatrix3.Translate(-tx, -ty); + reg.Fit2Raw = m * mtxy; + + return true; + } + + bool MatrixRegionAlignCalibEdge(DmtxRegion reg, DmtxEdge edgeLoc) + { + int streamDir; + int avoidAngle; + DmtxSymbolSize symbolShape; + DmtxVector2 pTmp = new DmtxVector2(); + DmtxPixelLoc loc1 = new DmtxPixelLoc(); + DmtxPixelLoc locOrigin = new DmtxPixelLoc(); + DmtxFollow follow; + + /* Determine pixel coordinates of origin */ + pTmp.X = 0.0; + pTmp.Y = 0.0; + pTmp *= reg.Fit2Raw; + locOrigin.X = (int)(pTmp.X + 0.5); + locOrigin.Y = (int)(pTmp.Y + 0.5); + + if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolSquareAuto || + (this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol10x10 && + this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol144x144)) + symbolShape = DmtxSymbolSize.DmtxSymbolSquareAuto; + else if (this._sizeIdxExpected == DmtxSymbolSize.DmtxSymbolRectAuto || + (this._sizeIdxExpected >= DmtxSymbolSize.DmtxSymbol8x18 && + this._sizeIdxExpected <= DmtxSymbolSize.DmtxSymbol16x48)) + symbolShape = DmtxSymbolSize.DmtxSymbolRectAuto; + else + symbolShape = DmtxSymbolSize.DmtxSymbolShapeAuto; + + /* Determine end locations of test line */ + if (edgeLoc == DmtxEdge.DmtxEdgeTop) + { + streamDir = reg.Polarity * -1; + avoidAngle = reg.LeftLine.Angle; + follow = FollowSeekLoc(reg.LocT); + pTmp.X = 0.8; + pTmp.Y = (symbolShape == DmtxSymbolSize.DmtxSymbolRectAuto) ? 0.2 : 0.6; + } + else + { + streamDir = reg.Polarity; + avoidAngle = reg.BottomLine.Angle; + follow = FollowSeekLoc(reg.LocR); + pTmp.X = (symbolShape == DmtxSymbolSize.DmtxSymbolSquareAuto) ? 0.7 : 0.9; + pTmp.Y = 0.8; + } + + pTmp *= reg.Fit2Raw; + loc1.X = (int)(pTmp.X + 0.5); + loc1.Y = (int)(pTmp.Y + 0.5); + + DmtxPixelLoc loc0 = follow.Loc; + DmtxBresLine line = new DmtxBresLine(loc0, loc1, locOrigin); + int steps = this.TrailBlazeGapped(reg, line, streamDir); + + DmtxBestLine bestLine = this.FindBestSolidLine2(loc0, steps, streamDir, avoidAngle); + + if (edgeLoc == DmtxEdge.DmtxEdgeTop) + { + reg.TopKnown = 1; + reg.TopAngle = bestLine.Angle; + reg.TopLoc = bestLine.LocBeg; + } + else + { + reg.RightKnown = 1; + reg.RightAngle = bestLine.Angle; + reg.RightLoc = bestLine.LocBeg; + } + + return true; + } + + int TrailBlazeGapped(DmtxRegion reg, DmtxBresLine line, int streamDir) + { + int distSq; + int travel = 0; + int outward = 0; + int[] dirMap = { 0, 1, 2, 7, 8, 3, 6, 5, 4 }; + + DmtxPixelLoc loc0 = line.Loc; + DmtxPointFlow flow = this.GetPointFlow(reg.FlowBegin.Plane, loc0, DmtxConstants.DmtxNeighborNone); + int distSqMax = (line.XDelta * line.XDelta) + (line.YDelta * line.YDelta); + int steps = 0; + bool onEdge = true; + + DmtxPixelLoc beforeStep = loc0; + int beforeCacheIndex = DecodeGetCache(loc0.X, loc0.Y); + if (beforeCacheIndex == -1) + return 0; + + this._cache[beforeCacheIndex] = 0; + + do + { + if (onEdge) + { + DmtxPointFlow flowNext = FindStrongestNeighbor(flow, streamDir); + if (flowNext.Mag == DmtxConstants.DmtxUndefined) + break; + + (new DmtxBresLine(line)).GetStep(flowNext.Loc, ref travel, ref outward); + if (flowNext.Mag < 50 || outward < 0 || (outward == 0 && travel < 0)) + { + onEdge = false; + } + else + { + line.Step(travel, outward); + flow = flowNext; + } + } + + if (!onEdge) + { + line.Step(1, 0); + flow = GetPointFlow(reg.FlowBegin.Plane, line.Loc, DmtxConstants.DmtxNeighborNone); + if (flow.Mag > 50) + onEdge = true; + } + + DmtxPixelLoc afterStep = line.Loc; + int afterCacheIndex = DecodeGetCache(afterStep.X, afterStep.Y); + if (afterCacheIndex == -1) + break; + + /* Determine step direction using pure magic */ + int xStep = afterStep.X - beforeStep.X; + int yStep = afterStep.Y - beforeStep.Y; + if (Math.Abs(xStep) > 1 || Math.Abs(yStep) > 1) + { + throw new Exception("Invalid step directions!"); + } + int stepDir = dirMap[3 * yStep + xStep + 4]; + + if (stepDir == 8) + { + throw new Exception("Invalid step direction!"); + } + if (streamDir < 0) + { + this._cache[beforeCacheIndex] |= (byte)(0x40 | stepDir); + this._cache[afterCacheIndex] = (byte)(((stepDir + 4) % 8) << 3); + } + else + { + this._cache[beforeCacheIndex] |= (byte)(0x40 | (stepDir << 3)); + this._cache[afterCacheIndex] = (byte)((stepDir + 4) % 8); + } + + /* Guaranteed to have taken one step since top of loop */ + int xDiff = line.Loc.X - loc0.X; + int yDiff = line.Loc.Y - loc0.Y; + distSq = (xDiff * xDiff) + (yDiff * yDiff); + + beforeStep = line.Loc; + beforeCacheIndex = afterCacheIndex; + steps++; + + } while (distSq < distSqMax); + + return steps; + } + + DmtxBestLine FindBestSolidLine2(DmtxPixelLoc loc0, int tripSteps, int sign, int houghAvoid) + { + int[,] hough = new int[3, DmtxConstants.DmtxHoughRes]; + char[] houghTest = new char[DmtxConstants.DmtxHoughRes]; + int i; + int step; + DmtxBestLine line = new DmtxBestLine(); + + int angleBest = 0; + int hOffsetBest = 0; + + DmtxFollow follow = this.FollowSeekLoc(loc0); + DmtxPixelLoc rHp = line.LocBeg = line.LocPos = line.LocNeg = follow.Loc; + line.StepBeg = line.StepPos = line.StepNeg = 0; + + /* Predetermine which angles to test */ + for (i = 0; i < DmtxConstants.DmtxHoughRes; i++) + { + if (houghAvoid == DmtxConstants.DmtxUndefined) + { + houghTest[i] = (char)1; + } + else + { + int houghMin = (houghAvoid + DmtxConstants.DmtxHoughRes / 6) % DmtxConstants.DmtxHoughRes; + int houghMax = (houghAvoid - DmtxConstants.DmtxHoughRes / 6 + DmtxConstants.DmtxHoughRes) % DmtxConstants.DmtxHoughRes; + if (houghMin > houghMax) + houghTest[i] = (i > houghMin || i < houghMax) ? (char)1 : (char)0; + else + houghTest[i] = (i > houghMin && i < houghMax) ? (char)1 : (char)0; + } + } + + /* Test each angle for steps along path */ + for (step = 0; step < tripSteps; step++) + { + + int xDiff = follow.Loc.X - rHp.X; + int yDiff = follow.Loc.Y - rHp.Y; + + /* Increment Hough accumulator */ + for (i = 0; i < DmtxConstants.DmtxHoughRes; i++) + { + + if (houghTest[i] == 0) + continue; + + int dH = (DmtxConstants.rHvX[i] * yDiff) - (DmtxConstants.rHvY[i] * xDiff); + if (dH >= -384 && dH <= 384) + { + int hOffset; + if (dH > 128) + hOffset = 2; + else if (dH >= -128) + hOffset = 1; + else + hOffset = 0; + + hough[hOffset, i]++; + + /* New angle takes over lead */ + if (hough[hOffset, i] > hough[hOffsetBest, angleBest]) + { + angleBest = i; + hOffsetBest = hOffset; + } + } + } + follow = FollowStep2(follow, sign); + } + + line.Angle = angleBest; + line.HOffset = hOffsetBest; + line.Mag = hough[hOffsetBest, angleBest]; + + return line; + } + + DmtxFollow FollowStep2(DmtxFollow followBeg, int sign) + { + DmtxFollow follow = new DmtxFollow(); + + if (Math.Abs(sign) != 1) + { + throw new Exception("Invalid parameter 'sign', can only be -1 or +1"); + } + if ((followBeg.Neighbor & 0x40) == 0x00) + { + throw new Exception("Invalid value for neighbor!"); + } + + int patternIdx = (sign < 0) ? followBeg.Neighbor & 0x07 : ((followBeg.Neighbor & 0x38) >> 3); + follow.Loc = new DmtxPixelLoc { X = followBeg.Loc.X + DmtxConstants.DmtxPatternX[patternIdx], Y = followBeg.Loc.Y + DmtxConstants.DmtxPatternY[patternIdx] }; + + follow.Step = followBeg.Step + sign; + follow.Ptr = this._cache; + follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y); + + return follow; + } + + DmtxFollow FollowSeekLoc(DmtxPixelLoc loc) + { + DmtxFollow follow = new DmtxFollow {Loc = loc, Step = 0, Ptr = this._cache}; + + follow.PtrIndex = DecodeGetCache(follow.Loc.X, follow.Loc.Y); + + return follow; + } + #endregion + + #region Properties + internal int EdgeMin + { + get { return _edgeMin; } + set + { + _edgeMin = value; + ValidateSettingsAndInitScanGrid(); + } + } + + internal int EdgeMax + { + get { return _edgeMax; } + set { _edgeMax = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int ScanGap + { + get { return _scanGap; } + set { _scanGap = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int SquareDevn + { + get { return (int)(Math.Acos(this._squareDevn) * 180.0 / Math.PI); } + set { _squareDevn = Math.Cos(value * (Math.PI / 180.0)); ValidateSettingsAndInitScanGrid(); } + } + + internal DmtxSymbolSize SizeIdxExpected + { + get { return _sizeIdxExpected; } + set { _sizeIdxExpected = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int EdgeThresh + { + get { return _edgeThresh; } + set { _edgeThresh = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int XMin + { + get { return _xMin; } + set { _xMin = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int XMax + { + get { return _xMax; } + set { _xMax = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int YMin + { + get { return _yMin; } + set { _yMin = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int YMax + { + get { return _yMax; } + set { _yMax = value; ValidateSettingsAndInitScanGrid(); } + } + + internal int Scale + { + get { return _scale; } + set { _scale = value; ValidateSettingsAndInitScanGrid(); } + } + + internal byte[] Cache + { + get { return _cache; } + set { _cache = value; ValidateSettingsAndInitScanGrid(); } + } + + internal DmtxImage Image + { + get { return _image; } + set { _image = value; ValidateSettingsAndInitScanGrid(); } + } + + + internal DmtxScanGrid Grid + { + get { return _grid; } + set { _grid = value; } + } + + internal int Height + { + get + { + return _image.Height / _scale; + } + } + + + internal int Width + { + get + { + return _image.Width / _scale; + } + } + #endregion + } +} diff --git a/DataMatrix.net/DmtxEncode.cs b/DataMatrix.net/DmtxEncode.cs new file mode 100644 index 0000000..6ee2eaf --- /dev/null +++ b/DataMatrix.net/DmtxEncode.cs @@ -0,0 +1,1679 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace DataMatrix.net +{ + internal class DmtxEncode + { + #region Fields + int _method; + DmtxScheme _scheme; + DmtxSymbolSize _sizeIdxRequest; + int _marginSize; + int _moduleSize; + DmtxPackOrder _pixelPacking; + DmtxFlip _imageFlip; + int _rowPadBytes; + DmtxMessage _message; + DmtxImage _image; + DmtxRegion _region; + bool[,] _rawData; + #endregion + + #region Constructors + internal DmtxEncode() + { + this._scheme = DmtxScheme.DmtxSchemeAscii; + this._sizeIdxRequest = DmtxSymbolSize.DmtxSymbolSquareAuto; + this._marginSize = 10; + this._moduleSize = 5; + this._pixelPacking = DmtxPackOrder.DmtxPack24bppRGB; + this._imageFlip = DmtxFlip.DmtxFlipNone; + this._rowPadBytes = 0; + } + + private DmtxEncode(DmtxEncode src) + { + this._scheme = src._scheme; + this._sizeIdxRequest = src._sizeIdxRequest; + this._marginSize = src._marginSize; + this._moduleSize = src._moduleSize; + this._pixelPacking = src._pixelPacking; + this._imageFlip = src._imageFlip; + this._rowPadBytes = src._rowPadBytes; + this._image = src._image; + this._message = src._message; + this._method = src._method; + this._region = src._region; + } + + #endregion + + #region Methods + internal bool EncodeDataMatrixRaw(byte[] inputString) + { + return EncodeDataMatrix(null, null, inputString, true); + } + + internal bool EncodeDataMatrix(Color? foreColor, Color? backColor, byte[] inputString) + { + return EncodeDataMatrix(foreColor, backColor, inputString, false); + } + + + internal bool EncodeDataMatrix(Color? foreColor, Color? backColor, byte[] inputString, bool encodeRaw) + { + byte[] buf = new byte[4096]; + + /* Encode input string into data codewords */ + DmtxSymbolSize sizeIdx = this._sizeIdxRequest; + int dataWordCount = EncodeDataCodewords(buf, inputString, ref sizeIdx); + if (dataWordCount <= 0) + { + return false; + } + + /* EncodeDataCodewords() should have updated any auto sizeIdx to a real one */ + if (sizeIdx == DmtxSymbolSize.DmtxSymbolSquareAuto || sizeIdx == DmtxSymbolSize.DmtxSymbolRectAuto) + { + throw new Exception("Invalid symbol size for encoding!"); + } + + /* Add pad characters to match a standard symbol size (whether smallest or requested) */ + int padCount = this.AddPadChars(buf, ref dataWordCount, DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx)); + + /* XXX we can remove a lot of this redundant data */ + this._region = new DmtxRegion(); + this._region.SizeIdx = sizeIdx; + this._region.SymbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, sizeIdx); + this._region.SymbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, sizeIdx); + this._region.MappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, sizeIdx); + this._region.MappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, sizeIdx); + + /* Allocate memory for message and array */ + this._message = new DmtxMessage(sizeIdx, DmtxFormat.Matrix) { PadCount = padCount }; + for (int i = 0; i < dataWordCount; i++) + { + this._message.Code[i] = buf[i]; + } + + /* Generate error correction codewords */ + DmtxCommon.GenReedSolEcc(this._message, this._region.SizeIdx); + + /* Module placement in region */ + DmtxDecode.ModulePlacementEcc200(this._message.Array, this._message.Code, + this._region.SizeIdx, DmtxConstants.DmtxModuleOnRGB); + + int width = 2 * this._marginSize + (this._region.SymbolCols * this._moduleSize); + int height = 2 * this._marginSize + (this._region.SymbolRows * this._moduleSize); + int bitsPerPixel = DmtxCommon.GetBitsPerPixel(this._pixelPacking); + if (bitsPerPixel == DmtxConstants.DmtxUndefined) + return false; + if (bitsPerPixel % 8 != 0) + { + throw new Exception("Invalid color depth for encoding!"); + } + + /* Allocate memory for the image to be generated */ + // pxl = (unsigned char *)malloc(width * height * (bitsPerPixel/8) + enc->rowPadBytes); + byte[] pxl = new byte[width * height * (bitsPerPixel / 8) + this._rowPadBytes]; + + this._image = new DmtxImage(pxl, width, height, this._pixelPacking) + { ImageFlip = this._imageFlip, RowPadBytes = this._rowPadBytes }; + + /* Insert finder and aligment pattern modules */ + if (encodeRaw) + PrintPatternRaw(); + else + PrintPattern(foreColor, backColor); + + return true; + } + + internal bool EncodeDataMosaic(byte[] inputString) + { + int[] splitInputSize = new int[3]; + DmtxSymbolSize sizeIdxRequest; + DmtxSymbolSize splitSizeIdxAttempt, splitSizeIdxLast; + List buf = new List(3); + for (int i = 0; i < 3; i++) + { + buf.Add(new byte[4096]); + } + int row, col; + + /* 1) count how many codewords it would take to encode the whole thing + * 2) take ceiling N of codeword count divided by 3 + * 3) using minimum symbol size that can accomodate N codewords: + * 4) create several barcodes over iterations of increasing numbers of + * input codewords until you go one too far + * 5) if codewords remain after filling R, G, and B barcodes then go back + * to 3 and try with next larger size + * 6) take the 3 different images you created and write out a new barcode + */ + + /* Encode full input string to establish baseline data codeword count */ + DmtxSymbolSize sizeIdx = sizeIdxRequest = this._sizeIdxRequest; + /* XXX buf can be changed here to use all 3 buffers' length */ + int dataWordCount = this.EncodeDataCodewords(buf[0], inputString, ref sizeIdx); + if (dataWordCount <= 0) + return false; + + /* Use 1/3 (ceiling) of inputSize establish input size target */ + int tmpInputSize = (inputString.Length + 2) / 3; + splitInputSize[0] = tmpInputSize; + splitInputSize[1] = tmpInputSize; + splitInputSize[2] = inputString.Length - (splitInputSize[0] + splitInputSize[1]); + /* XXX clean up above lines later for corner cases */ + + /* Use 1/3 (floor) of dataWordCount establish first symbol size attempt */ + DmtxSymbolSize splitSizeIdxFirst = this.FindCorrectSymbolSize(tmpInputSize, sizeIdxRequest); + if (splitSizeIdxFirst == DmtxSymbolSize.DmtxSymbolShapeAuto) + return false; + + /* Set the last possible symbol size for this symbol shape or specific size request */ + if (sizeIdxRequest == DmtxSymbolSize.DmtxSymbolSquareAuto) + { + splitSizeIdxLast = DmtxSymbolSize.DmtxSymbol144x144; + } + else if (sizeIdxRequest == DmtxSymbolSize.DmtxSymbolRectAuto) + { + splitSizeIdxLast = DmtxSymbolSize.DmtxSymbol16x48; + } + else + { + splitSizeIdxLast = splitSizeIdxFirst; + } + + /* XXX would be nice if we could choose a size and then fill up each + layer as we go, but this can cause problems with all data fits on + first 2 layers. Revisit this later after things look a bit cleaner. */ + + /* Try increasing symbol sizes until 3 of them can hold all input values */ + byte[] tmpRed = new byte[splitInputSize[0]]; + for (int i = 0; i < splitInputSize[0]; i++) + { + tmpRed[i] = inputString[i]; + } + byte[] tmpGreen = new byte[splitInputSize[1]]; + for (int i = splitInputSize[0]; i < splitInputSize[0] + splitInputSize[1]; i++) + { + tmpGreen[i - splitInputSize[0]] = inputString[i]; + } + byte[] tmpBlue = new byte[splitInputSize[2]]; + for (int i = splitInputSize[0] + splitInputSize[1]; i < inputString.Length; i++) + { + tmpBlue[i - splitInputSize[0] - splitInputSize[1]] = inputString[i]; + } + + for (splitSizeIdxAttempt = splitSizeIdxFirst; splitSizeIdxAttempt <= splitSizeIdxLast; splitSizeIdxAttempt++) + { + /* RED LAYER */ + sizeIdx = splitSizeIdxAttempt; + EncodeDataCodewords(buf[0], tmpRed, ref sizeIdx); + if (sizeIdx != splitSizeIdxAttempt) + continue; + + /* GREEN LAYER */ + sizeIdx = splitSizeIdxAttempt; + EncodeDataCodewords(buf[1], tmpGreen, ref sizeIdx); + if (sizeIdx != splitSizeIdxAttempt) + continue; + + /* BLUE LAYER */ + sizeIdx = splitSizeIdxAttempt; + EncodeDataCodewords(buf[2], tmpBlue, ref sizeIdx); + if (sizeIdx != splitSizeIdxAttempt) + continue; + + break; + } + + this._sizeIdxRequest = splitSizeIdxAttempt; + + /* Now we have the correct lengths for splitInputSize, and they all fit into the desired size */ + DmtxEncode encGreen = new DmtxEncode(this); + DmtxEncode encBlue = new DmtxEncode(this); + + /* First encode red to the main encode struct (image portion will be overwritten) */ + EncodeDataMatrix(null, null, tmpRed); + encGreen.EncodeDataMatrix(null, null, tmpGreen); + encBlue.EncodeDataMatrix(null, null, tmpBlue); + + int mappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, splitSizeIdxAttempt); + int mappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, splitSizeIdxAttempt); + for (int i = 0; i < this._region.MappingCols * this._region.MappingRows; i++) + { + this._message.Array[i] = 0; + } + DmtxDecode.ModulePlacementEcc200(this._message.Array, this._message.Code, this._region.SizeIdx, DmtxConstants.DmtxModuleOnRed); + //string s = Encoding.ASCII.GetString(_array); + //Console.WriteLine(s); + /* Data Mosaic will traverse this array multiple times -- reset + DmtxModuleAssigned and DMX_MODULE_VISITED bits before starting */ + for (row = 0; row < mappingRows; row++) + { + for (col = 0; col < mappingCols; col++) + { + this._message.Array[row * mappingCols + col] &= (byte)(0xff ^ (DmtxConstants.DmtxModuleAssigned | DmtxConstants.DmtxModuleVisited)); + } + } + + DmtxDecode.ModulePlacementEcc200(this._message.Array, encGreen.Message.Code, this._region.SizeIdx, DmtxConstants.DmtxModuleOnGreen); + + /* Data Mosaic will traverse this array multiple times -- reset + DmtxModuleAssigned and DMX_MODULE_VISITED bits before starting */ + for (row = 0; row < mappingRows; row++) + { + for (col = 0; col < mappingCols; col++) + { + this._message.Array[row * mappingCols + col] &= (byte)(0xff ^ (DmtxConstants.DmtxModuleAssigned | DmtxConstants.DmtxModuleVisited)); + } + } + + DmtxDecode.ModulePlacementEcc200(this._message.Array, encBlue.Message.Code, this._region.SizeIdx, DmtxConstants.DmtxModuleOnBlue); + + PrintPattern(null, null); + + return true; + } + + private void PrintPatternRaw() + { + this._rawData = new bool[this._region.SymbolCols, this._region.SymbolRows]; + + for (int symbolRow = 0; symbolRow < this._region.SymbolRows; symbolRow++) + { + for (int symbolCol = 0; symbolCol < this._region.SymbolCols; symbolCol++) + { + int moduleStatus = this._message.SymbolModuleStatus(this._region.SizeIdx, symbolRow, symbolCol); + this._rawData[symbolCol, this._region.SymbolRows - symbolRow - 1] = ((moduleStatus & DmtxConstants.DmtxModuleOnBlue) != 0x00); + } + } + } + + private void PrintPattern(Color? foreColor, Color? backColor) + { + int symbolRow; + int[] rgb = new int[3]; + + double txy = this._marginSize; + + DmtxMatrix3 m1 = DmtxMatrix3.Translate(txy, txy); + DmtxMatrix3 m2 = DmtxMatrix3.Scale(this._moduleSize, this._moduleSize); + DmtxMatrix3 rxfrm = m2 * m1; + + int rowSize = this._image.RowSizeBytes; + int height = this._image.Height; + + for (int pxlIndex = 0; pxlIndex < rowSize * height - 2; pxlIndex += 3) + { + this._image.Pxl[pxlIndex] = backColor.HasValue ? backColor.Value.R : (byte)0xff; + this._image.Pxl[pxlIndex + 1] = backColor.HasValue ? backColor.Value.G : (byte)0xff; + this._image.Pxl[pxlIndex + 2] = backColor.HasValue ? backColor.Value.B : (byte)0xff; + } + + for (symbolRow = 0; symbolRow < this._region.SymbolRows; symbolRow++) + { + int symbolCol; + for (symbolCol = 0; symbolCol < this._region.SymbolCols; symbolCol++) + { + DmtxVector2 vIn = new DmtxVector2(symbolCol, symbolRow); + DmtxVector2 vOut = vIn * rxfrm; + + int pixelCol = (int)(vOut.X); + int pixelRow = (int)(vOut.Y); + int moduleStatus = this._message.SymbolModuleStatus(this._region.SizeIdx, symbolRow, symbolCol); + + for (int i = pixelRow; i < pixelRow + this._moduleSize; i++) + { + for (int j = pixelCol; j < pixelCol + this._moduleSize; j++) + { + if (foreColor.HasValue && backColor.HasValue) + { + rgb[0] = ((moduleStatus & DmtxConstants.DmtxModuleOnRed) != 0x00) ? foreColor.Value.B : backColor.Value.B; + rgb[1] = ((moduleStatus & DmtxConstants.DmtxModuleOnGreen) != 0x00) ? foreColor.Value.G : backColor.Value.G; + rgb[2] = ((moduleStatus & DmtxConstants.DmtxModuleOnBlue) != 0x00) ? foreColor.Value.R : backColor.Value.R; + } + else + { + rgb[0] = ((moduleStatus & DmtxConstants.DmtxModuleOnBlue) != 0x00) ? 0 : 255; + rgb[1] = ((moduleStatus & DmtxConstants.DmtxModuleOnGreen) != 0x00) ? 0 : 255; + rgb[2] = ((moduleStatus & DmtxConstants.DmtxModuleOnRed) != 0x00) ? 0 : 255; + } + /* dmtxImageSetRgb(enc->image, j, i, rgb); */ + this._image.SetPixelValue(j, i, 0, (byte)rgb[0]); + this._image.SetPixelValue(j, i, 1, (byte)rgb[1]); + this._image.SetPixelValue(j, i, 2, (byte)rgb[2]); + } + } + } + } + } + + private int AddPadChars(byte[] buf, ref int dataWordCount, int paddedSize) + { + int padCount = 0; + + /* First pad character is not randomized */ + if (dataWordCount < paddedSize) + { + padCount++; + buf[dataWordCount++] = DmtxConstants.DmtxCharAsciiPad; + } + + /* All remaining pad characters are randomized based on character position */ + while (dataWordCount < paddedSize) + { + padCount++; + buf[dataWordCount] = Randomize253State(DmtxConstants.DmtxCharAsciiPad, dataWordCount + 1); + dataWordCount++; + } + + return padCount; + } + + private byte Randomize253State(byte codewordValue, int codewordPosition) + { + int pseudoRandom = ((149 * codewordPosition) % 253) + 1; + int tmp = codewordValue + pseudoRandom; + if (tmp > 254) + tmp -= 254; + + if (tmp < 0 || tmp > 255) + { + throw new Exception("Error randomizing 253 state!"); + } + + return (byte)tmp; + } + + int EncodeDataCodewords(byte[] buf, byte[] inputString, ref DmtxSymbolSize sizeIdx) + { + int dataWordCount; + + /* + * This function needs to take both dataWordCount and sizeIdx into account + * because symbol size is tied to an encodation. That is, a data stream + * might be different from one symbol size to another + */ + + /* Encode input string into data codewords */ + switch (this._scheme) + { + case DmtxScheme.DmtxSchemeAutoBest: + dataWordCount = EncodeAutoBest(buf, inputString); + break; + case DmtxScheme.DmtxSchemeAutoFast: + dataWordCount = 0; + break; + default: + dataWordCount = EncodeSingleScheme(buf, inputString, this._scheme); + break; + } + + /* XXX must fix ... will need to handle sizeIdx requests here because it is + needed by Encode...() for triplet termination */ + + /* parameter sizeIdx is requested value, returned sizeIdx is decision */ + sizeIdx = FindCorrectSymbolSize(dataWordCount, sizeIdx); + if (sizeIdx == DmtxSymbolSize.DmtxSymbolShapeAuto) + return 0; + + return dataWordCount; + } + + private DmtxSymbolSize FindCorrectSymbolSize(int dataWords, DmtxSymbolSize sizeIdxRequest) + { + DmtxSymbolSize sizeIdx; + + if (dataWords <= 0) + return DmtxSymbolSize.DmtxSymbolShapeAuto; + + if (sizeIdxRequest == DmtxSymbolSize.DmtxSymbolSquareAuto || sizeIdxRequest == DmtxSymbolSize.DmtxSymbolRectAuto) + { + int idxBeg; + int idxEnd; + if (sizeIdxRequest == DmtxSymbolSize.DmtxSymbolSquareAuto) + { + idxBeg = 0; + idxEnd = DmtxConstants.DmtxSymbolSquareCount; + } + else + { + idxBeg = DmtxConstants.DmtxSymbolSquareCount; + idxEnd = DmtxConstants.DmtxSymbolSquareCount + DmtxConstants.DmtxSymbolRectCount; + } + + for (sizeIdx = (DmtxSymbolSize)idxBeg; sizeIdx < (DmtxSymbolSize)idxEnd; sizeIdx++) + { + if (DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx) >= dataWords) + { + break; + } + } + + if (sizeIdx == (DmtxSymbolSize)idxEnd) + { + return DmtxSymbolSize.DmtxSymbolShapeAuto; + } + } + else + { + sizeIdx = sizeIdxRequest; + } + + if (dataWords > DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx)) + { + return DmtxSymbolSize.DmtxSymbolShapeAuto; + } + + return sizeIdx; + } + + private int EncodeSingleScheme(byte[] buf, byte[] codewords, DmtxScheme scheme) + { + DmtxChannel channel = new DmtxChannel(); + + InitChannel(channel, codewords); + + while (channel.InputIndex < channel.Input.Length) + { + bool err = EncodeNextWord(channel, scheme); + if (!err) + return 0; + + /* DumpChannel(&channel); */ + + if (channel.Invalid != DmtxChannelStatus.DmtxChannelValid) + { + return 0; + } + } + /* DumpChannel(&channel); */ + + int size = channel.EncodedLength / 12; + for (int i = 0; i < size; i++) + { + buf[i] = channel.EncodedWords[i]; + } + + return size; + } + + private int EncodeAutoBest(byte[] buf, byte[] codewords) + { + DmtxScheme targetScheme; + DmtxChannelGroup optimal = new DmtxChannelGroup(); + DmtxChannelGroup best = new DmtxChannelGroup(); + + /* Intialize optimizing channels and encode first codeword from default ASCII */ + for (targetScheme = DmtxScheme.DmtxSchemeAscii; targetScheme <= DmtxScheme.DmtxSchemeBase256; targetScheme++) + { + DmtxChannel channel = (optimal.Channels[(int)targetScheme]); + InitChannel(channel, codewords); + bool err = EncodeNextWord(channel, targetScheme); + if (err) + return 0; + } + + /* For each remaining word in the input stream, test the efficiency of + getting to this encodation scheme for each input character by + switching here from each of the other channels (which are always + optimally encoded) */ + while (optimal.Channels[0].InputIndex < optimal.Channels[0].Input.Length) + { /* XXX only tracking first channel */ + + for (targetScheme = DmtxScheme.DmtxSchemeAscii; targetScheme <= DmtxScheme.DmtxSchemeBase256; targetScheme++) + { + best.Channels[(int)targetScheme] = FindBestChannel(optimal, targetScheme); + } + optimal = best; + } + + /* Choose a winner now that all channels are finished */ + DmtxChannel winner = optimal.Channels[(int)DmtxScheme.DmtxSchemeAscii]; + for (targetScheme = DmtxScheme.DmtxSchemeAscii + 1; targetScheme <= DmtxScheme.DmtxSchemeBase256; targetScheme++) + { + if (optimal.Channels[(int)targetScheme].Invalid != DmtxChannelStatus.DmtxChannelValid) + { + continue; + } + + if (optimal.Channels[(int)targetScheme].EncodedLength < winner.EncodedLength) + { + winner = optimal.Channels[(int)targetScheme]; + } + } + + /* XXX get rid of buf concept and try to do something with channel -> matrix copy instead */ + int winnerSize = winner.EncodedLength / 12; + for (int i = 0; i < winnerSize; i++) + { + buf[i] = winner.EncodedWords[i]; + } + + return winnerSize; + } + + private DmtxChannel FindBestChannel(DmtxChannelGroup group, DmtxScheme targetScheme) + { + DmtxChannel winner = null; + + for (DmtxScheme encFrom = DmtxScheme.DmtxSchemeAscii; encFrom <= DmtxScheme.DmtxSchemeBase256; encFrom++) + { + + DmtxChannel channel = group.Channels[(int)encFrom]; + + /* If from channel doesn't hold valid data because it couldn't + represent the previous value then skip it */ + if (channel.Invalid != DmtxChannelStatus.DmtxChannelValid) + { + continue; + } + + /* If channel has already processed all of its input values then it + cannot be used as a starting point */ + if (channel.InputIndex == channel.Input.Length) + continue; + + bool err = EncodeNextWord(channel, targetScheme); + if (err == false) + { + /* XXX fix this */ + } + + /* If channel scheme can't represent next word then stop for this channel */ + if ((channel.Invalid & DmtxChannelStatus.DmtxChannelUnsupportedChar) != 0) + { + winner = channel; + break; + } + + /* If channel scheme was unable to unlatch here then skip */ + if ((channel.Invalid & DmtxChannelStatus.DmtxChannelCannotUnlatch) != 0) + { + continue; + } + + if (winner == null || channel.CurrentLength < winner.CurrentLength) + { + winner = channel; + } + } + + return winner; + } + + private bool EncodeNextWord(DmtxChannel channel, DmtxScheme targetScheme) + { + /* Change to new encodation scheme if necessary */ + if (channel.EncScheme != targetScheme) + { + ChangeEncScheme(channel, targetScheme, DmtxUnlatch.Explicit); + if (channel.Invalid != DmtxChannelStatus.DmtxChannelValid) + return false; + } + + if (channel.EncScheme != targetScheme) + { + throw new Exception("For encoding, channel scheme must equal target scheme!"); + } + + /* Encode next input value */ + switch (channel.EncScheme) + { + case DmtxScheme.DmtxSchemeAscii: + return EncodeAsciiCodeword(channel); + case DmtxScheme.DmtxSchemeC40: + return EncodeTripletCodeword(channel); + case DmtxScheme.DmtxSchemeText: + return EncodeTripletCodeword(channel); + case DmtxScheme.DmtxSchemeX12: + return EncodeTripletCodeword(channel); + case DmtxScheme.DmtxSchemeEdifact: + return EncodeEdifactCodeword(channel); + case DmtxScheme.DmtxSchemeBase256: + return EncodeBase256Codeword(channel); + default: + return false; + } + } + + private bool EncodeBase256Codeword(DmtxChannel channel) + { + int i; + int newDataLength; + int headerByteCount; + byte[] headerByte = new byte[2]; + + if (channel.EncScheme != DmtxScheme.DmtxSchemeBase256) + { + throw new Exception("Invalid encoding scheme selected!"); + } + + int firstBytePtrIndex = channel.FirstCodeWord / 12; + headerByte[0] = DmtxMessage.UnRandomize255State(channel.EncodedWords[firstBytePtrIndex], channel.FirstCodeWord / 12 + 1); + + /* newSchemeLength contains size byte(s) too */ + if (headerByte[0] <= 249) + { + newDataLength = headerByte[0]; + } + else + { + newDataLength = 250 * (headerByte[0] - 249); + newDataLength += DmtxMessage.UnRandomize255State(channel.EncodedWords[firstBytePtrIndex + 1], channel.FirstCodeWord / 12 + 2); + } + + newDataLength++; + + if (newDataLength <= 249) + { + headerByteCount = 1; + headerByte[0] = (byte)newDataLength; + headerByte[1] = 0; /* unused */ + } + else + { + headerByteCount = 2; + headerByte[0] = (byte)(newDataLength / 250 + 249); + headerByte[1] = (byte)(newDataLength % 250); + } + + /* newDataLength does not include header bytes */ + if (newDataLength <= 0 || newDataLength > 1555) + { + throw new Exception("Encoding failed, data length out of range!"); + } + + /* One time shift of codewords when passing the 250 byte size threshold */ + if (newDataLength == 250) + { + for (i = channel.CurrentLength / 12 - 1; i > channel.FirstCodeWord / 12; i--) + { + byte valueTmp = DmtxMessage.UnRandomize255State(channel.EncodedWords[i], i + 1); + channel.EncodedWords[i + 1] = Randomize255State(valueTmp, i + 2); + } + IncrementProgress(channel, 12); + channel.EncodedLength += 12; /* ugly */ + } + + /* Update scheme length in Base 256 header */ + for (i = 0; i < headerByteCount; i++) + { + channel.EncodedWords[firstBytePtrIndex + i] = Randomize255State(headerByte[i], channel.FirstCodeWord / 12 + i + 1); + } + + PushInputWord(channel, Randomize255State(channel.Input[channel.InputIndex], channel.CurrentLength / 12 + 1)); + IncrementProgress(channel, 12); + channel.InputIndex++; + + /* XXX will need to introduce an EndOfSymbolBase256() that recognizes + opportunity to encode headerLength of 0 if remaining Base 256 message + exactly matches symbol capacity */ + + return true; + } + + private bool EncodeEdifactCodeword(DmtxChannel channel) + { + if (channel.EncScheme != DmtxScheme.DmtxSchemeEdifact) + { + throw new Exception("Invalid encoding scheme selected!"); + } + + byte inputValue = channel.Input[channel.InputIndex]; + + if (inputValue < 32 || inputValue > 94) + { + channel.Invalid = DmtxChannelStatus.DmtxChannelUnsupportedChar; + return false; + } + + PushInputWord(channel, (byte)(inputValue & 0x3f)); + IncrementProgress(channel, 9); + channel.InputIndex++; + + CheckForEndOfSymbolEdifact(channel); + + return true; + } + + private void CheckForEndOfSymbolEdifact(DmtxChannel channel) + { + /* This function tests if the remaining input values can be completed using + * one of the valid end-of-symbol cases, and finishes encodation if possible. + * + * This function must exit in ASCII encodation. EDIFACT must always be + * unlatched, although implicit Unlatch is possible. + * + * End Symbol ASCII EDIFACT End Codeword + * Case Words Words Values Condition Sequence + * ---- ------ ----- ------- --------- ------------------------------- + * (a) 1 0 Special PAD + * (b) 1 1 Special ASCII (could be 2 digits) + * (c) 1 >= 2 Continue Need larger symbol + * (d) 2 0 Special PAD PAD + * (e) 2 1 Special ASCII PAD + * (f) 2 2 Special ASCII ASCII + * (g) 2 >= 3 Continue Need larger symbol + * (h) N/A N/A 0 Normal UNLATCH + * (i) N/A N/A >= 1 Continue Not end of symbol + * + * Note: All "Special" cases (a,b,d,e,f) require clean byte boundary to start + */ + + /* Count remaining input values assuming EDIFACT encodation */ + if (channel.InputIndex > channel.Input.Length) + { + throw new Exception("Input index out of range while encoding!"); + } + + int edifactValues = channel.Input.Length - channel.InputIndex; + + /* Can't end symbol right now if there are 5+ values remaining + (noting that '9999' can still terminate in case (f)) */ + if (edifactValues > 4) /* subset of (i) -- performance only */ + return; + + /* Find minimum symbol size big enough to accomodate remaining codewords */ + /* XXX broken -- what if someone asks for DmtxSymbolRectAuto or specific sizeIdx? */ + + int currentByte = channel.CurrentLength / 12; + DmtxSymbolSize sizeIdx = this.FindCorrectSymbolSize(currentByte, DmtxSymbolSize.DmtxSymbolSquareAuto); + /* XXX test for sizeIdx == DmtxUndefined here */ + int symbolCodewords = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx) - currentByte; + + /* Test for special case condition */ + if (channel.CurrentLength % 12 == 0 && + (symbolCodewords == 1 || symbolCodewords == 2)) + { + /* Count number of codewords left to write (assuming ASCII) */ + /* XXX temporary hack ... later create function that knows about shifts and digits */ + int asciiCodewords = edifactValues; + + if (asciiCodewords <= symbolCodewords) + { /* (a,b,d,e,f) */ + ChangeEncScheme(channel, DmtxScheme.DmtxSchemeAscii, DmtxUnlatch.Implicit); + + /* XXX this loop should produce exactly asciiWords codewords ... assert somehow? */ + for (int i = 0; i < edifactValues; i++) + { + bool err = EncodeNextWord(channel, DmtxScheme.DmtxSchemeAscii); + if (err == false) + { + return; + } + if (channel.Invalid != DmtxChannelStatus.DmtxChannelValid) + { + throw new Exception("Error checking for end of symbol edifact"); + } + } + } + /* else (c,g) -- do nothing */ + } + else if (edifactValues == 0) + { /* (h) */ + ChangeEncScheme(channel, DmtxScheme.DmtxSchemeAscii, DmtxUnlatch.Explicit); + } + /* else (i) -- do nothing */ + + return; + } + + private void PushInputWord(DmtxChannel channel, byte codeword) + { + /* XXX should this assertion actually be a legit runtime test? */ + if ((channel.EncodedLength / 12 > 3 * 1558)) + { + throw new Exception("Can't push input word, encoded length exceeds limits!"); + }/* increased for Mosaic */ + + /* XXX this is currently pretty ugly, but can wait until the + rewrite. What is required is to go through and decide on a + consistent approach (probably that all encodation schemes use + currentLength except for triplet-based schemes which use + currentLength and encodedLength). All encodation schemes should + maintain both currentLength and encodedLength though. Perhaps + another approach would be to maintain currentLength and "extraLength" */ + + switch (channel.EncScheme) + { + case DmtxScheme.DmtxSchemeAscii: + channel.EncodedWords[channel.CurrentLength / 12] = codeword; + channel.EncodedLength += 12; + break; + + case DmtxScheme.DmtxSchemeC40: + channel.EncodedWords[channel.EncodedLength / 12] = codeword; + channel.EncodedLength += 12; + break; + + case DmtxScheme.DmtxSchemeText: + channel.EncodedWords[channel.EncodedLength / 12] = codeword; + channel.EncodedLength += 12; + break; + + case DmtxScheme.DmtxSchemeX12: + channel.EncodedWords[channel.EncodedLength / 12] = codeword; + channel.EncodedLength += 12; + break; + + case DmtxScheme.DmtxSchemeEdifact: + /* EDIFACT is the only encodation scheme where we don't encode up to the + next byte boundary. This is because EDIFACT can be unlatched at any + point, including mid-byte, so we can't guarantee what the next + codewords will be. All other encodation schemes only unlatch on byte + boundaries, allowing us to encode to the next boundary knowing that + we have predicted the only codewords that could be used while in this + scheme. */ + + /* write codeword value to next 6 bits (might span codeword bytes) and + then pad any remaining bits until next byte boundary with zero bits. */ + int pos = channel.CurrentLength % 4; + int startByte = ((channel.CurrentLength + 9) / 12) - pos; + + DmtxQuadruplet quad = GetQuadrupletValues(channel.EncodedWords[startByte], + channel.EncodedWords[startByte + 1], + channel.EncodedWords[startByte + 2]); + quad.Value[pos] = codeword; + + for (int i = pos + 1; i < 4; i++) + quad.Value[i] = 0; + + /* Only write the necessary codewords */ + switch (pos) + { + case 3: + channel.EncodedWords[startByte + 2] = (byte)(((quad.Value[2] & 0x03) << 6) | quad.Value[3]); + break; + case 2: + channel.EncodedWords[startByte + 2] = (byte)(((quad.Value[2] & 0x03) << 6) | quad.Value[3]); + break; + case 1: + channel.EncodedWords[startByte + 1] = (byte)(((quad.Value[1] & 0x0f) << 4) | (quad.Value[2] >> 2)); + break; + case 0: + channel.EncodedWords[startByte] = (byte)((quad.Value[0] << 2) | (quad.Value[1] >> 4)); + break; + } + + channel.EncodedLength += 9; + break; + + case DmtxScheme.DmtxSchemeBase256: + channel.EncodedWords[channel.CurrentLength / 12] = codeword; + channel.EncodedLength += 12; + break; + } + } + + private bool EncodeTripletCodeword(DmtxChannel channel) + { + int[] outputWords = new int[4]; /* biggest: upper shift to non-basic set */ + byte[] buffer = new byte[6]; /* biggest: 2 words followed by 4-word upper shift */ + DmtxTriplet triplet = new DmtxTriplet(); + + if (channel.EncScheme != DmtxScheme.DmtxSchemeX12 && + channel.EncScheme != DmtxScheme.DmtxSchemeText && + channel.EncScheme != DmtxScheme.DmtxSchemeC40) + { + throw new Exception("Invalid encoding scheme selected!"); + } + + if (channel.CurrentLength > channel.EncodedLength) + { + throw new Exception("Encoding length out of range!"); + } + + /* If there are no pre-encoded codewords then generate some */ + if (channel.CurrentLength == channel.EncodedLength) + { + + if (channel.CurrentLength % 12 != 0) + { + throw new Exception("Invalid encoding length!"); + } + + /* Ideally we would only encode one codeword triplet here (the + minimum that you can do at a time) but we can't leave the channel + with the last encoded word as a shift. The following loop + prevents this condition by encoding until we have a clean break or + until we reach the end of the input data. */ + + int ptrIndex = channel.InputIndex; + + int tripletCount = 0; + for (;;) + { + + /* Fill array with at least 3 values (the minimum necessary to + encode a triplet), but possibly up to 6 values due to presence + of upper shifts. Note that buffer may already contain values + from a previous iteration of the outer loop, and this step + "tops off" the buffer to make sure there are at least 3 values. */ + + while (tripletCount < 3 && ptrIndex < channel.Input.Length) + { + byte inputWord = channel.Input[ptrIndex++]; + int count = GetC40TextX12Words(outputWords, inputWord, channel.EncScheme); + + if (count == 0) + { + channel.Invalid = DmtxChannelStatus.DmtxChannelUnsupportedChar; + return false; + } + + for (int i = 0; i < count; i++) + { + buffer[tripletCount++] = (byte)outputWords[i]; + } + } + + /* Take the next 3 values from buffer to encode */ + triplet.Value[0] = buffer[0]; + triplet.Value[1] = buffer[1]; + triplet.Value[2] = buffer[2]; + + if (tripletCount >= 3) + { + PushTriplet(channel, triplet); + buffer[0] = buffer[3]; + buffer[1] = buffer[4]; + buffer[2] = buffer[5]; + tripletCount -= 3; + } + + /* If we reach the end of input and have not encountered a clean + break opportunity then complete the symbol here */ + + if (ptrIndex == channel.Input.Length) + { + /* tripletCount represents the number of values in triplet waiting to be pushed + inputCount represents the number of values after inputPtr waiting to be pushed */ + while (channel.CurrentLength < channel.EncodedLength) + { + IncrementProgress(channel, 8); + channel.InputIndex++; + } + + /* If final triplet value was shift then IncrementProgress will + overextend us .. hack it back a little. Note that this means + this barcode is invalid unless one of the specific end-of-symbol + conditions explicitly allows it. */ + if (channel.CurrentLength == channel.EncodedLength + 8) + { + channel.CurrentLength = channel.EncodedLength; + channel.InputIndex--; + } + + if (channel.Input.Length < channel.InputIndex) + { + throw new Exception("Channel input index exceeds range!"); + } + + int inputCount = channel.Input.Length - channel.InputIndex; + + bool err = ProcessEndOfSymbolTriplet(channel, triplet, tripletCount, inputCount); + if (err == false) + return false; + break; + } + + /* If there are no triplet values remaining in the buffer then + break. This guarantees that we will always stop encoding on a + clean "unshifted" break */ + + if (tripletCount == 0) + break; + } + } + + /* Pre-encoded codeword is available for consumption */ + if (channel.CurrentLength < channel.EncodedLength) + { + IncrementProgress(channel, 8); + channel.InputIndex++; + } + + return true; + } + + private int GetC40TextX12Words(int[] outputWords, byte inputWord, DmtxScheme encScheme) + { + if (encScheme != DmtxScheme.DmtxSchemeX12 && + encScheme != DmtxScheme.DmtxSchemeText && + encScheme != DmtxScheme.DmtxSchemeC40) + { + throw new Exception("Invalid encoding scheme selected!"); + } + + int count = 0; + + /* Handle extended ASCII with Upper Shift character */ + if (inputWord > 127) + { + if (encScheme == DmtxScheme.DmtxSchemeX12) + { + return 0; + } + + outputWords[count++] = DmtxConstants.DmtxCharTripletShift2; + outputWords[count++] = 30; + inputWord -= 128; + } + + /* Handle all other characters according to encodation scheme */ + if (encScheme == DmtxScheme.DmtxSchemeX12) + { + if (inputWord == 13) + outputWords[count++] = 0; + else if (inputWord == 42) + outputWords[count++] = 1; + else if (inputWord == 62) + outputWords[count++] = 2; + else if (inputWord == 32) + outputWords[count++] = 3; + else if (inputWord >= 48 && inputWord <= 57) + outputWords[count++] = inputWord - 44; + else if (inputWord >= 65 && inputWord <= 90) + outputWords[count++] = inputWord - 51; + } + else + { /* encScheme is C40 or Text */ + if (inputWord <= 31) + { + outputWords[count++] = DmtxConstants.DmtxCharTripletShift1; + outputWords[count++] = inputWord; + } + else if (inputWord == 32) + { + outputWords[count++] = 3; + } + else if (inputWord <= 47) + { + outputWords[count++] = DmtxConstants.DmtxCharTripletShift2; + outputWords[count++] = inputWord - 33; + } + else if (inputWord <= 57) + { + outputWords[count++] = inputWord - 44; + } + else if (inputWord <= 64) + { + outputWords[count++] = DmtxConstants.DmtxCharTripletShift2; + outputWords[count++] = inputWord - 43; + } + else if (inputWord <= 90 && encScheme == DmtxScheme.DmtxSchemeC40) + { + outputWords[count++] = inputWord - 51; + } + else if (inputWord <= 90 && encScheme == DmtxScheme.DmtxSchemeText) + { + outputWords[count++] = DmtxConstants.DmtxCharTripletShift3; + outputWords[count++] = inputWord - 64; + } + else if (inputWord <= 95) + { + outputWords[count++] = DmtxConstants.DmtxCharTripletShift2; + outputWords[count++] = inputWord - 69; + } + else if (inputWord == 96 && encScheme == DmtxScheme.DmtxSchemeText) + { + outputWords[count++] = DmtxConstants.DmtxCharTripletShift3; + outputWords[count++] = 0; + } + else if (inputWord <= 122 && encScheme == DmtxScheme.DmtxSchemeText) + { + outputWords[count++] = inputWord - 83; + } + else if (inputWord <= 127) + { + outputWords[count++] = DmtxConstants.DmtxCharTripletShift3; + outputWords[count++] = inputWord - 96; + } + } + + return count; + } + + private bool ProcessEndOfSymbolTriplet(DmtxChannel channel, DmtxTriplet triplet, int tripletCount, int inputCount) + { + bool err; + + /* In this function we process some special cases from the Data Matrix + * standard, and as such we circumvent the normal functions for + * accomplishing certain tasks. This breaks our internal counts, but this + * function always marks the end of processing so it will not affect + * anything downstream. This approach allows the normal encoding functions + * to be built with very strict checks and assertions. + * + * EXIT CONDITIONS: + * + * triplet symbol action + * ------- ------ ------------------- + * 1 0 need bigger symbol + * 1 1 special case (d) + * 1 2 special case (c) + * 1 3 unlatch ascii pad + * 1 4 unlatch ascii pad pad + * 2 0 need bigger symbol + * 2 1 need bigger symbol + * 2 2 special case (b) + * 2 3 unlatch ascii ascii + * 2 4 unlatch ascii ascii pad + * 3 0 need bigger symbol + * 3 1 need bigger symbol + * 3 2 special case (a) + * 3 3 c40 c40 unlatch + * 3 4 c40 c40 unlatch pad + */ + + /* We should always reach this point on a byte boundary */ + if (channel.CurrentLength % 12 != 0) + { + throw new Exception("Invalid current length for encoding!"); + } + + /* XXX Capture how many extra input values will be counted ... for later adjustment */ + int inputAdjust = tripletCount - inputCount; + + /* Find minimum symbol size big enough to accomodate remaining codewords */ + int currentByte = channel.CurrentLength / 12; + + DmtxSymbolSize sizeIdx = this.FindCorrectSymbolSize(currentByte + ((inputCount == 3) ? 2 : inputCount), this._sizeIdxRequest); + + if (sizeIdx == DmtxSymbolSize.DmtxSymbolShapeAuto) + return false; + + /* XXX test for sizeIdx == DmtxUndefined here */ + int remainingCodewords = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx) - currentByte; + + /* XXX the big problem with all of these special cases is what if one of + these last words requires multiple bytes in ASCII (like upper shift?). + We probably need to add a test against this and then just force an + unlatch if we see this coming. */ + + /* Special case (d): Unlatch is implied (switch manually) */ + if (inputCount == 1 && remainingCodewords == 1) + { + ChangeEncScheme(channel, DmtxScheme.DmtxSchemeAscii, DmtxUnlatch.Implicit); + err = EncodeNextWord(channel, DmtxScheme.DmtxSchemeAscii); + if (err == false) + return false; + if (channel.Invalid != DmtxChannelStatus.DmtxChannelValid || channel.InputIndex != channel.Input.Length) + { + throw new Exception("Error processing end of symbol triplet!"); + } + } + else if (remainingCodewords == 2) + { + /* Special case (a): Unlatch is implied */ + if (tripletCount == 3) + { + PushTriplet(channel, triplet); + IncrementProgress(channel, 24); + channel.EncScheme = DmtxScheme.DmtxSchemeAscii; + channel.InputIndex += 3; + channel.InputIndex -= inputAdjust; + } + /* Special case (b): Unlatch is implied */ + else if (tripletCount == 2) + { + /* assert(2nd C40 is not a shift character); */ + triplet.Value[2] = 0; + PushTriplet(channel, triplet); + IncrementProgress(channel, 24); + channel.EncScheme = DmtxScheme.DmtxSchemeAscii; + channel.InputIndex += 2; + channel.InputIndex -= inputAdjust; + } + /* Special case (c) */ + else if (tripletCount == 1) + { + ChangeEncScheme(channel, DmtxScheme.DmtxSchemeAscii, DmtxUnlatch.Explicit); + err = EncodeNextWord(channel, DmtxScheme.DmtxSchemeAscii); + if (err == false) + return false; + if (channel.Invalid != DmtxChannelStatus.DmtxChannelValid) + { + throw new Exception("Error processing end of symbol triplet!"); + } + /* XXX I can still think of a case that looks ugly here. What if + the final 2 C40 codewords are a Shift word and a non-Shift + word. This special case will unlatch after the shift ... which + is probably legal but I'm not loving it. Give it more thought. */ + } + } + else + { + /* assert(remainingCodewords == 0 || remainingCodewords >= 3); */ + + currentByte = channel.CurrentLength / 12; + remainingCodewords = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx) - currentByte; + + if (remainingCodewords > 0) + { + ChangeEncScheme(channel, DmtxScheme.DmtxSchemeAscii, DmtxUnlatch.Explicit); + + while (channel.InputIndex < channel.Input.Length) + { + err = EncodeNextWord(channel, DmtxScheme.DmtxSchemeAscii); + if (err == false) + return false; + + if (channel.Invalid != DmtxChannelStatus.DmtxChannelValid) + { + throw new Exception("Error processing end of symbol triplet!"); + } + } + } + } + + if (channel.InputIndex != channel.Input.Length) + { + throw new Exception("Could not fully process end of symbol triplet!"); + } + + return true; + } + + private void PushTriplet(DmtxChannel channel, DmtxTriplet triplet) + { + int tripletValue = (1600 * triplet.Value[0]) + (40 * triplet.Value[1]) + triplet.Value[2] + 1; + PushInputWord(channel, (byte)(tripletValue / 256)); + PushInputWord(channel, (byte)(tripletValue % 256)); + } + + private bool EncodeAsciiCodeword(DmtxChannel channel) + { + if (channel.EncScheme != DmtxScheme.DmtxSchemeAscii) + { + throw new Exception("Invalid encoding scheme selected!"); + } + + byte inputValue = channel.Input[channel.InputIndex]; + + /* XXX this is problematic ... We should not be looking backward in the + channel to determine what state we're in. Add the necessary logic to + fix the current bug (prevprev != 253) but when rewriting encoder later + make sure double digit ascii as treated as a forward-encoded condition. + i.e., encode ahead of where we currently stand, and not comparable to other + channels because currently sitting between byte boundaries (like the + triplet-based schemes). Much simpler. */ + + /* XXX another thought on the future rewrite: if adopting a forward-encoding + approach on double digits then the following input situation: + + digit digit c40a c40b c40c + + would create: + + ASCII_double C40_triplet1ab C40_triplet2bc + + although it might be more efficient in some cases to do + + digit C40_triplet1(digit a) C40_triplet2(a b) + + (I can't think of a situation like this, but I can't rule it out either) + Unfortunately the forward encoding approach would never allow ascii to unlatch + between the ASCII_double input words. + + One approach that would definitely avoid this is to treat ASCII_dd as a + separate channel when using "--best". However, when encoding to single- + scheme ascii you would always use the ASCII_dd approach. + + This function, EncodeAsciiCodeword(), should have another parameter to flag + whether or not to compress double digits. When encoding in single scheme + ascii, then compress the double digits. If using --best then use both options + as separate channels. */ + + /* 2nd digit char in a row - overwrite first digit word with combined value */ + if (IsDigit(inputValue) && channel.CurrentLength >= channel.FirstCodeWord + 12) + { + int prevIndex = (channel.CurrentLength - 12) / 12; + byte prevValue = (byte)(channel.EncodedWords[prevIndex] - 1); + + byte prevPrevValue = ((prevIndex > channel.FirstCodeWord / 12) ? channel.EncodedWords[prevIndex - 1] : (byte)0); + + if (prevPrevValue != 235 && IsDigit(prevValue)) + { + channel.EncodedWords[prevIndex] = (byte)(10 * (prevValue - '0') + (inputValue - '0') + 130); + channel.InputIndex++; + return true; + } + } + + /* Extended ASCII char */ + if (inputValue == DmtxConstants.DmtxCharFNC1) + { + PushInputWord(channel, DmtxConstants.DmtxCharFNC1); + IncrementProgress(channel, 12); + channel.InputIndex++; + return true; + } + if (inputValue >= 128) + { + PushInputWord(channel, DmtxConstants.DmtxCharAsciiUpperShift); + IncrementProgress(channel, 12); + inputValue -= 128; + } + + PushInputWord(channel, (byte)(inputValue + 1)); + IncrementProgress(channel, 12); + channel.InputIndex++; + + return true; + } + + private bool IsDigit(byte inputValue) + { + return inputValue >= 48 && inputValue <= 57; + } + + private void ChangeEncScheme(DmtxChannel channel, DmtxScheme targetScheme, DmtxUnlatch unlatchType) + { + if (channel.EncScheme == targetScheme) + { + throw new Exception("Target scheme already equals channel scheme, cannot be changed!"); + } + + /* Unlatch to ASCII (base encodation scheme) */ + switch (channel.EncScheme) + { + case DmtxScheme.DmtxSchemeAscii: + /* Nothing to do */ + if (channel.CurrentLength % 12 != 0) + { + throw new Exception("Invalid current length detected encoding ascii code"); + } + break; + + case DmtxScheme.DmtxSchemeC40: + case DmtxScheme.DmtxSchemeText: + case DmtxScheme.DmtxSchemeX12: + + /* Can't unlatch unless currently at a byte boundary */ + if ((channel.CurrentLength % 12) != 0) + { + channel.Invalid = DmtxChannelStatus.DmtxChannelCannotUnlatch; + return; + } + + /* Can't unlatch if last word in previous triplet is a shift */ + if (channel.CurrentLength != channel.EncodedLength) + { + channel.Invalid = DmtxChannelStatus.DmtxChannelCannotUnlatch; + return; + } + + /* Unlatch to ASCII and increment progress */ + if (unlatchType == DmtxUnlatch.Explicit) + { + PushInputWord(channel, (byte)DmtxConstants.DmtxCharTripletUnlatch); + IncrementProgress(channel, 12); + } + break; + + case DmtxScheme.DmtxSchemeEdifact: + + /* must overwrite next 6 bits (after current) with 011111 (31) and + then fill remaining bits until next byte bounday with zeros + then set encodedLength, encodedTwothirdsbits, currentLength, + currentTwothirdsbits. PushInputWord guarantees that remaining + bits are padded to 0, so just push the unlatch code and then + increment current and encoded length */ + if (channel.CurrentLength % 3 != 0) + { + throw new Exception("Error changing encryption scheme, current length is invalid!"); + } + + if (unlatchType == DmtxUnlatch.Explicit) + { + PushInputWord(channel, (byte)DmtxConstants.DmtxCharEdifactUnlatch); + IncrementProgress(channel, 9); + } + + /* Advance progress to next byte boundary */ + int advance = (channel.CurrentLength % 4) * 3; + channel.CurrentLength += advance; + channel.EncodedLength += advance; + /* assert(remaining bits are zero); */ + break; + + case DmtxScheme.DmtxSchemeBase256: + + /* since Base 256 stores the length value at the beginning of the + string instead of using an unlatch character, "unlatching" Base + 256 involves going to the beginning of this stretch of Base 256 + codewords and update the placeholder with the current length. + Note that the Base 256 length value can either be 1 or 2 bytes, + depending on the length of the current stretch of Base 256 + chars. However, this value will already have the correct + number of codewords allocated since this is checked every time + a new Base 256 codeword is pushed to the channel. */ + break; + } + channel.EncScheme = DmtxScheme.DmtxSchemeAscii; + + /* Latch to new encodation scheme */ + switch (targetScheme) + { + case DmtxScheme.DmtxSchemeAscii: + /* Nothing to do */ + break; + case DmtxScheme.DmtxSchemeC40: + PushInputWord(channel, DmtxConstants.DmtxCharC40Latch); + IncrementProgress(channel, 12); + break; + case DmtxScheme.DmtxSchemeText: + PushInputWord(channel, DmtxConstants.DmtxCharTextLatch); + IncrementProgress(channel, 12); + break; + case DmtxScheme.DmtxSchemeX12: + PushInputWord(channel, DmtxConstants.DmtxCharX12Latch); + IncrementProgress(channel, 12); + break; + case DmtxScheme.DmtxSchemeEdifact: + PushInputWord(channel, DmtxConstants.DmtxCharEdifactLatch); + IncrementProgress(channel, 12); + break; + case DmtxScheme.DmtxSchemeBase256: + PushInputWord(channel, DmtxConstants.DmtxCharBase256Latch); + IncrementProgress(channel, 12); + + /* Write temporary field length (0 indicates remainder of symbol) */ + PushInputWord(channel, Randomize255State(0, 2)); + IncrementProgress(channel, 12); + break; + } + channel.EncScheme = targetScheme; + channel.FirstCodeWord = channel.CurrentLength - 12; + if (channel.FirstCodeWord % 12 != 0) + { + throw new Exception("Error while changin encoding scheme, invalid first code word!"); + } + } + + private byte Randomize255State(byte codewordValue, int codewordPosition) + { + int pseudoRandom = ((149 * codewordPosition) % 255) + 1; + int tmp = codewordValue + pseudoRandom; + + return (byte)((tmp <= 255) ? tmp : tmp - 256); + } + + private void IncrementProgress(DmtxChannel channel, int encodedUnits) + { + /* XXX this function became a misnomer when we started incrementing by + * an amount other than what was specified with the C40/Text exception. + * Maybe a new name/convention is in order. + */ + + /* In C40 and Text encodation schemes while we normally use 5 1/3 bits + * to encode a regular character, we also must account for the extra + * 5 1/3 bits (for a total of 10 2/3 bits that gets used for a shifted + * character. + */ + + if (channel.EncScheme == DmtxScheme.DmtxSchemeC40 || channel.EncScheme == DmtxScheme.DmtxSchemeText) + { + + int pos = (channel.CurrentLength % 6) / 2; + int startByte = (channel.CurrentLength / 12) - (pos >> 1); + DmtxTriplet triplet = GetTripletValues(channel.EncodedWords[startByte], channel.EncodedWords[startByte + 1]); + + /* Note that we will alway increment progress according to a whole + input codeword, so the value at "pos" is guaranteed to not be in + a shifted state. */ + if (triplet.Value[pos] <= 2) + channel.CurrentLength += 8; + } + + channel.CurrentLength += encodedUnits; + } + + private static DmtxTriplet GetTripletValues(byte cw1, byte cw2) + { + DmtxTriplet triplet = new DmtxTriplet(); + + /* XXX this really belongs with the decode functions */ + + int compact = (cw1 << 8) | cw2; + triplet.Value[0] = (byte)((compact - 1) / 1600); + triplet.Value[1] = (byte)(((compact - 1) / 40) % 40); + triplet.Value[2] = (byte)((compact - 1) % 40); + + return triplet; + } + + private DmtxQuadruplet GetQuadrupletValues(byte cw1, byte cw2, byte cw3) + { + DmtxQuadruplet quad = new DmtxQuadruplet(); + + /* XXX this really belongs with the decode functions */ + + quad.Value[0] = (byte)(cw1 >> 2); + quad.Value[1] = (byte)(((cw1 & 0x03) << 4) | ((cw2 & 0xf0) >> 4)); + quad.Value[2] = (byte)(((cw2 & 0x0f) << 2) | ((cw3 & 0xc0) >> 6)); + quad.Value[3] = (byte)(cw3 & 0x3f); + + return quad; + } + + private static void InitChannel(DmtxChannel channel, byte[] codewords) + { + channel.EncScheme = DmtxScheme.DmtxSchemeAscii; + channel.Invalid = DmtxChannelStatus.DmtxChannelValid; + channel.InputIndex = 0; + channel.Input = codewords; + } + #endregion + + #region Properties + internal int Method + { + get { return _method; } + set { _method = value; } + } + + internal DmtxScheme Scheme + { + get { return _scheme; } + set { _scheme = value; } + } + + internal DmtxSymbolSize SizeIdxRequest + { + get { return _sizeIdxRequest; } + set { _sizeIdxRequest = value; } + } + + internal int MarginSize + { + get { return _marginSize; } + set { _marginSize = value; } + } + + internal int ModuleSize + { + get { return _moduleSize; } + set { _moduleSize = value; } + } + + internal DmtxPackOrder PixelPacking + { + get { return _pixelPacking; } + set { _pixelPacking = value; } + } + + internal DmtxFlip ImageFlip + { + get { return _imageFlip; } + set { _imageFlip = value; } + } + + internal int RowPadBytes + { + get { return _rowPadBytes; } + set { _rowPadBytes = value; } + } + + internal DmtxMessage Message + { + get { return _message; } + set { _message = value; } + } + + internal DmtxImage Image + { + get { return _image; } + set { _image = value; } + } + + internal DmtxRegion Region + { + get { return _region; } + set { _region = value; } + } + + public bool[,] RawData + { + get { return _rawData; } + set { _rawData = value; } + } + #endregion + } +} diff --git a/DataMatrix.net/DmtxFollow.cs b/DataMatrix.net/DmtxFollow.cs new file mode 100644 index 0000000..c25ad0d --- /dev/null +++ b/DataMatrix.net/DmtxFollow.cs @@ -0,0 +1,81 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal struct DmtxFollow + { + #region Fields + + int _ptrIndex; + #endregion + + #region Properties + internal int PtrIndex + { + set + { + _ptrIndex = value; + } + } + + internal byte CurrentPtr + { + get + { + return this.Ptr[_ptrIndex]; + } + set + { + this.Ptr[_ptrIndex] = value; + } + } + + internal byte[] Ptr { get; set; } + + internal byte Neighbor + { + get + { + return this.Ptr[_ptrIndex]; + } + set + { + this.Ptr[_ptrIndex] = value; + } + } + + internal int Step { get; set; } + + internal DmtxPixelLoc Loc { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/DataMatrix.net/DmtxImage.cs b/DataMatrix.net/DmtxImage.cs new file mode 100644 index 0000000..fef6764 --- /dev/null +++ b/DataMatrix.net/DmtxImage.cs @@ -0,0 +1,269 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; + +namespace DataMatrix.net +{ + internal class DmtxImage + { + #region Fields + + int _rowPadBytes; + + #endregion + + #region Constructor + internal DmtxImage(byte[] pxl, int width, int height, DmtxPackOrder pack) + { + this.BitsPerChannel = new int[4]; + this.ChannelStart = new int[4]; + if (pxl == null || width < 1 || height < 1) + { + throw new ArgumentException("Cannot create image of size null"); + } + + this.Pxl = pxl; + this.Width = width; + this.Height = height; + this.PixelPacking = pack; + this.BitsPerPixel = DmtxCommon.GetBitsPerPixel(pack); + this.BytesPerPixel = this.BitsPerPixel / 8; + this._rowPadBytes = 0; + this.RowSizeBytes = this.Width * this.BytesPerPixel + this._rowPadBytes; + this.ImageFlip = DmtxFlip.DmtxFlipNone; + + /* Leave channelStart[] and bitsPerChannel[] with zeros from calloc */ + this.ChannelCount = 0; + + switch (pack) + { + case DmtxPackOrder.DmtxPackCustom: + break; + case DmtxPackOrder.DmtxPack1bppK: + throw new ArgumentException("Cannot create image: not supported pack order!"); + case DmtxPackOrder.DmtxPack8bppK: + SetChannel(0, 8); + break; + case DmtxPackOrder.DmtxPack16bppRGB: + case DmtxPackOrder.DmtxPack16bppBGR: + case DmtxPackOrder.DmtxPack16bppYCbCr: + SetChannel(0, 5); + SetChannel(5, 5); + SetChannel(10, 5); + break; + case DmtxPackOrder.DmtxPack24bppRGB: + case DmtxPackOrder.DmtxPack24bppBGR: + case DmtxPackOrder.DmtxPack24bppYCbCr: + case DmtxPackOrder.DmtxPack32bppRGBX: + case DmtxPackOrder.DmtxPack32bppBGRX: + SetChannel(0, 8); + SetChannel(8, 8); + SetChannel(16, 8); + break; + case DmtxPackOrder.DmtxPack16bppRGBX: + case DmtxPackOrder.DmtxPack16bppBGRX: + SetChannel(0, 5); + SetChannel(5, 5); + SetChannel(10, 5); + break; + case DmtxPackOrder.DmtxPack16bppXRGB: + case DmtxPackOrder.DmtxPack16bppXBGR: + SetChannel(1, 5); + SetChannel(6, 5); + SetChannel(11, 5); + break; + case DmtxPackOrder.DmtxPack32bppXRGB: + case DmtxPackOrder.DmtxPack32bppXBGR: + SetChannel(8, 8); + SetChannel(16, 8); + SetChannel(24, 8); + break; + case DmtxPackOrder.DmtxPack32bppCMYK: + SetChannel(0, 8); + SetChannel(8, 8); + SetChannel(16, 8); + SetChannel(24, 8); + break; + default: + throw new ArgumentException("Cannot create image: Invalid Pack Order"); + } + } + #endregion + + #region Methods + internal bool SetChannel(int channelStart, int bitsPerChannel) + { + if (this.ChannelCount >= 4) /* IMAGE_MAX_CHANNEL */ + return false; + + /* New channel extends beyond pixel data */ + + this.BitsPerChannel[this.ChannelCount] = bitsPerChannel; + this.ChannelStart[this.ChannelCount] = channelStart; + (this.ChannelCount)++; + + return true; + } + + internal int GetByteOffset(int x, int y) + { + if (this.ImageFlip == DmtxFlip.DmtxFlipX) + { + throw new ArgumentException("DmtxFlipX is not an option!"); + } + + if (!ContainsInt(0, x, y)) + return DmtxConstants.DmtxUndefined; + + if (this.ImageFlip == DmtxFlip.DmtxFlipY) + return (y * this.RowSizeBytes + x * this.BytesPerPixel); + + return ((this.Height - y - 1) * this.RowSizeBytes + x * this.BytesPerPixel); + } + + internal bool GetPixelValue(int x, int y, int channel, ref int value) + { + if (channel >= this.ChannelCount) + { + throw new ArgumentException("Channel greater than channel count!"); + } + + int offset = GetByteOffset(x, y); + if (offset == DmtxConstants.DmtxUndefined) + { + return false; + } + + switch (this.BitsPerChannel[channel]) + { + case 1: + break; + case 5: + break; + case 8: + if (this.ChannelStart[channel] % 8 != 0 || this.BitsPerPixel % 8 != 0) + { + throw new Exception("Error getting pixel value"); + } + value = this.Pxl[offset + channel]; + break; + } + + return true; + } + + internal bool SetPixelValue(int x, int y, int channel, byte value) + { + if (channel >= this.ChannelCount) + { + throw new ArgumentException("Channel greater than channel count!"); + } + + int offset = GetByteOffset(x, y); + if (offset == DmtxConstants.DmtxUndefined) + { + return false; + } + + switch (this.BitsPerChannel[channel]) + { + case 1: + break; + case 5: + break; + case 8: + if (this.ChannelStart[channel] % 8 != 0 || this.BitsPerPixel % 8 != 0) + { + throw new Exception("Error getting pixel value"); + } + this.Pxl[offset + channel] = value; + break; + } + + return true; + } + + internal bool ContainsInt(int margin, int x, int y) + { + if (x - margin >= 0 && x + margin < this.Width && + y - margin >= 0 && y + margin < this.Height) + return true; + + return false; + } + + internal bool ContainsFloat(double x, double y) + { + if (x >= 0.0 && x < this.Width && y >= 0.0 && y < this.Height) + { + return true; + } + return false; + } + #endregion + + #region Properties + + internal int Width { get; set; } + + internal int Height { get; set; } + + internal DmtxPackOrder PixelPacking { get; set; } + + internal int BitsPerPixel { get; set; } + + internal int BytesPerPixel { get; set; } + + internal int RowPadBytes + { + get { return _rowPadBytes; } + set + { + _rowPadBytes = value; + this.RowSizeBytes = this.Width * (this.BitsPerPixel / 8) + this._rowPadBytes; + } + } + + internal int RowSizeBytes { get; set; } + + internal DmtxFlip ImageFlip { get; set; } + + internal int ChannelCount { get; set; } + + internal int[] ChannelStart { get; set; } + + internal int[] BitsPerChannel { get; set; } + + internal byte[] Pxl { get; set; } + + #endregion + } +} diff --git a/DataMatrix.net/DmtxImageDecoder.cs b/DataMatrix.net/DmtxImageDecoder.cs new file mode 100644 index 0000000..a07431f --- /dev/null +++ b/DataMatrix.net/DmtxImageDecoder.cs @@ -0,0 +1,154 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Diagnostics; + +namespace DataMatrix.net +{ + public class DmtxImageDecoder + { + /// + /// returns a list of all decoded DataMatrix codes in the image provided + /// + public List DecodeImage(Bitmap image) + { + return DecodeImage(image, int.MaxValue, TimeSpan.MaxValue); + } + + /// + /// returns a list of all decoded DataMatrix codes in the image provided + /// that can be found in the given time span + /// + public List DecodeImage(Bitmap image, TimeSpan timeSpan) + { + return DecodeImage(image, int.MaxValue, timeSpan); + } + + /// + /// returns a list of all decoded DataMatrix codes in the image provided + /// + public List DecodeImageMosaic(Bitmap image) + { + return DecodeImageMosaic(image, int.MaxValue, TimeSpan.MaxValue); + } + + /// + /// returns a list of DataMatrix codes in the image provided that can be + /// found in the given time span, but no more than maxResultCount codes + /// (useful, if you e.g. expect only one code to be in the image) + /// + public List DecodeImageMosaic(Bitmap image, int maxResultCount, TimeSpan timeOut) + { + return DecodeImage(image, maxResultCount, timeOut, true); + } + + /// + /// returns a list of all decoded DataMatrix codes in the image provided + /// that can be found in the given time span + /// + public List DecodeImageMosaic(Bitmap image, TimeSpan timeSpan) + { + return DecodeImage(image, int.MaxValue, timeSpan); + } + + /// + /// returns a list of DataMatrix codes in the image provided that can be + /// found in the given time span, but no more than maxResultCount codes + /// (useful, if you e.g. expect only one code to be in the image) + /// + public List DecodeImage(Bitmap image, int maxResultCount, TimeSpan timeOut) + { + return DecodeImage(image, maxResultCount, timeOut, false); + } + + private List DecodeImage(Bitmap image, int maxResultCount, TimeSpan timeOut, bool isMosaic) + { + List result = new List(); + int stride; + byte[] rawImg = ImageToByteArray(image, out stride); + DmtxImage dmtxImg = new DmtxImage(rawImg, image.Width, image.Height, DmtxPackOrder.DmtxPack24bppRGB); + dmtxImg.RowPadBytes = stride % 3; + DmtxDecode decode = new DmtxDecode(dmtxImg, 1); + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + while (true) + { + if (stopWatch.Elapsed > timeOut) + { + break; + } + DmtxRegion region = decode.RegionFindNext(timeOut); + if (region != null) + { + DmtxMessage msg = isMosaic ? decode.MosaicRegion(region, -1) : decode.MatrixRegion(region, -1); + string message = Encoding.ASCII.GetString(msg.Output, 0, msg.Output.Length); + message = message.Substring(0, message.IndexOf('\0')); + if (!result.Contains(message)) + { + result.Add(message); + if (result.Count >= maxResultCount) + { + break; + } + } + } + else + { + break; + } + } + return result; + } + + + private byte[] ImageToByteArray(Bitmap b, out int stride) + { + Rectangle rect = new Rectangle(0, 0, b.Width, b.Height); + BitmapData bd = b.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + byte[] pxl = new byte[bd.Stride * b.Height]; + Marshal.Copy(bd.Scan0, pxl, 0, bd.Stride * b.Height); + stride = bd.Stride; + return pxl; + } + finally + { + b.UnlockBits(bd); + } + } + } +} diff --git a/DataMatrix.net/DmtxImageEncoder.cs b/DataMatrix.net/DmtxImageEncoder.cs new file mode 100644 index 0000000..a4a4c92 --- /dev/null +++ b/DataMatrix.net/DmtxImageEncoder.cs @@ -0,0 +1,318 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Drawing.Imaging; +using System.Globalization; + +namespace DataMatrix.net +{ + public class DmtxImageEncoder + { + public static readonly int DefaultDotSize = 5; + public static readonly int DefaultMargin = 10; + public static readonly Color DefaultBackColor = Color.White; + public static readonly Color DefaultForeColor = Color.Black; + + public Bitmap EncodeImageMosaic(string val) + { + return EncodeImageMosaic(val, DefaultDotSize); + } + + public Bitmap EncodeImageMosaic(string val, int dotSize) + { + return EncodeImageMosaic(val, dotSize, DefaultMargin); + } + + public Bitmap EncodeImageMosaic(string val, int dotSize, int margin) + { + DmtxImageEncoderOptions options = new DmtxImageEncoderOptions {MarginSize = margin, ModuleSize = dotSize}; + return EncodeImageMosaic(val, options); + } + + public Bitmap EncodeImageMosaic(string val, DmtxImageEncoderOptions options) + { + return EncodeImage(val, options, true); + } + + private Bitmap EncodeImage(string val, DmtxImageEncoderOptions options, bool isMosaic) + { + DmtxEncode encode = new DmtxEncode + { + ModuleSize = options.ModuleSize, + MarginSize = options.MarginSize, + SizeIdxRequest = options.SizeIdx + }; + byte[] valAsByteArray = GetRawDataAndSetEncoding(val, options, encode); + if (isMosaic) + { + encode.EncodeDataMosaic(valAsByteArray); + } + else + { + encode.EncodeDataMatrix(options.ForeColor, options.BackColor, valAsByteArray); + } + return CopyDataToBitmap(encode.Image.Pxl, encode.Image.Width, encode.Image.Height); + } + + private static byte[] GetRawDataAndSetEncoding(string code, DmtxImageEncoderOptions options, DmtxEncode encode) + { + byte[] result = options.Encoding.GetBytes(code); + encode.Scheme = options.Scheme; + if (options.Scheme == DmtxScheme.DmtxSchemeAsciiGS1) + { + List prefixedRawData = new List(new[] { (byte)232 }); + prefixedRawData.AddRange(result); + result = prefixedRawData.ToArray(); + encode.Scheme = DmtxScheme.DmtxSchemeAscii; + } + return result; + } + + public Bitmap EncodeImage(string val) + { + return EncodeImage(val, DefaultDotSize, DefaultMargin); + } + + public Bitmap EncodeImage(string val, int dotSize) + { + return EncodeImage(val, dotSize, DefaultMargin); + } + + public Bitmap EncodeImage(string val, int dotSize, int margin) + { + DmtxImageEncoderOptions options = new DmtxImageEncoderOptions {MarginSize = margin, ModuleSize = dotSize}; + return EncodeImage(val, options); + } + + public Bitmap EncodeImage(string val, DmtxImageEncoderOptions options) + { + return EncodeImage(val, options, false); + } + + public string EncodeSvgImage(string val) + { + return EncodeSvgImage(val, DefaultDotSize, DefaultMargin, DefaultForeColor, DefaultBackColor); + } + + public string EncodeSvgImage(string val, int dotSize) + { + return EncodeSvgImage(val, dotSize, DefaultMargin, DefaultForeColor, DefaultBackColor); + } + + public string EncodeSvgImage(string val, int dotSize, int margin) + { + return EncodeSvgImage(val, dotSize, margin, DefaultForeColor, DefaultBackColor); + } + + public string EncodeSvgImage(string val, int dotSize, int margin, Color foreColor, Color backColor) + { + DmtxImageEncoderOptions options = new DmtxImageEncoderOptions + { + ModuleSize = dotSize, + MarginSize = margin, + ForeColor = foreColor, + BackColor = backColor + }; + return EncodeSvgImage(val, options); + } + + public bool[,] EncodeRawData(string val) + { + return EncodeRawData(val, new DmtxImageEncoderOptions()); + } + + public bool[,] EncodeRawData(string val, DmtxImageEncoderOptions options) + { + DmtxEncode encode = new DmtxEncode + { + ModuleSize = 1, + MarginSize = 0, + SizeIdxRequest = options.SizeIdx, + Scheme = options.Scheme + }; + + byte[] valAsByteArray = GetRawDataAndSetEncoding(val, options, encode); + + encode.EncodeDataMatrixRaw(valAsByteArray); + + return encode.RawData; + } + + public string EncodeSvgImage(string val, DmtxImageEncoderOptions options) + { + DmtxEncode encode = new DmtxEncode + { + ModuleSize = options.ModuleSize, + MarginSize = options.MarginSize, + SizeIdxRequest = options.SizeIdx, + Scheme = options.Scheme + }; + + byte[] valAsByteArray = GetRawDataAndSetEncoding(val, options, encode); + + encode.EncodeDataMatrix(options.ForeColor, options.BackColor, valAsByteArray); + + return EncodeSvgFile(encode, "", options.ModuleSize, options.MarginSize, options.ForeColor, options.BackColor); + } + + internal static Bitmap CopyDataToBitmap(byte[] data, int width, int height) + { + data = InsertPaddingBytes(data, width, height, 24); + + Bitmap bmp = new Bitmap(width, height); + BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); + Marshal.Copy(data, 0, bmpData.Scan0, data.Length); + bmp.UnlockBits(bmpData); + return bmp; + } + + private static byte[] InsertPaddingBytes(byte[] data, int width, int height, int bitsPerPixel) + { + int paddedWidth = 4 * ((width * bitsPerPixel + 31) / 32); + int padding = paddedWidth - 3 * width; + if (padding == 0) + { + return data; + } + byte[] newData = new byte[paddedWidth * height]; + for (int i = 0; i < height; i++) + { + for (int j = 0; j < width; j++) + { + newData[i * paddedWidth + 3 * j] = data[3 * (i * width + j)]; + newData[i * paddedWidth + 3 * j + 1] = data[3 * (i * width + j) + 1]; + newData[i * paddedWidth + 3 * j + 2] = data[3 * (i * width + j) + 2]; + } + for (int k = 0; k < padding; k++) + { + newData[i * paddedWidth + 3 * k] = 255; + newData[i * paddedWidth + 3 * k + 1] = 255; + newData[i * paddedWidth + 3 * k + 2] = 255; + } + } + return newData; + } + + private static NumberFormatInfo _dotFormatProvider; + + internal string EncodeSvgFile(DmtxEncode enc, string format, int moduleSize, int margin, Color foreColor, Color backColor) + { + bool defineOnly = false; + string idString = null; + string style; + string outputString = ""; + + if (_dotFormatProvider == null) + { + _dotFormatProvider = new NumberFormatInfo {NumberDecimalSeparator = "."}; + } + + if (format == "svg:") + { + defineOnly = true; + idString = format.Substring(4); + } + + if (string.IsNullOrEmpty(idString)) + { + idString = "dmtx_0001"; + } + + int width = 2 * enc.MarginSize + (enc.Region.SymbolCols * enc.ModuleSize); + int height = 2 * enc.MarginSize + (enc.Region.SymbolRows * enc.ModuleSize); + + int symbolCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolCols, enc.Region.SizeIdx); + int symbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, enc.Region.SizeIdx); + + /* Print SVG Header */ + if (!defineOnly) + { + outputString += string.Format( + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + " Layout:{0}x%{1} Symbol:{3}x{4} Data Matrix\n", width, height, idString, symbolCols, symbolRows); + } + + if (backColor != Color.White) + { + style = string.Format("style=\"fill:#{0}{1}{2};fill-opacity:{3};stroke:none\" ", + backColor.R.ToString("X2"), backColor.G.ToString("X2"), backColor.B.ToString("X2"), ((double)backColor.A / (double)byte.MaxValue).ToString("0.##", _dotFormatProvider)); + outputString += string.Format(" \n", + width, height, style); + } + + /* Write Data Matrix ON modules */ + for (int row = 0; row < enc.Region.SymbolRows; row++) + { + int rowInv = enc.Region.SymbolRows - row - 1; + for (int col = 0; col < enc.Region.SymbolCols; col++) + { + int module = enc.Message.SymbolModuleStatus(enc.Region.SizeIdx, row, col); + style = string.Format("style=\"fill:#{0}{1}{2};fill-opacity:{3};stroke:none\" ", + foreColor.R.ToString("X2"), foreColor.G.ToString("X2"), foreColor.B.ToString("X2"), ((double)foreColor.A / (double)byte.MaxValue).ToString("0.##", _dotFormatProvider)); + + if ((module & DmtxConstants.DmtxModuleOn) != 0) + { + outputString += string.Format(" \n", + moduleSize, moduleSize, + col * moduleSize + margin, + rowInv * moduleSize + margin, style); + } + } + } + + outputString += " \n"; + + /* Close SVG document */ + if (!defineOnly) + { + outputString += string.Format("\n" + + "\n" + + "\n\n", idString); + } + + return outputString; + } + } +} diff --git a/DataMatrix.net/DmtxImageEncoderOptions.cs b/DataMatrix.net/DmtxImageEncoderOptions.cs new file mode 100644 index 0000000..ef7d324 --- /dev/null +++ b/DataMatrix.net/DmtxImageEncoderOptions.cs @@ -0,0 +1,73 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace DataMatrix.net +{ + public class DmtxImageEncoderOptions + { + #region Constructor + + public DmtxImageEncoderOptions() + { + BackColor = Color.White; + ForeColor = Color.Black; + SizeIdx = DmtxSymbolSize.DmtxSymbolSquareAuto; + Scheme = DmtxScheme.DmtxSchemeAscii; + ModuleSize = 5; + MarginSize = 10; + Encoding = Encoding.ASCII; + } + + #endregion + + #region Properties + + public int MarginSize { get; set; } + + public int ModuleSize { get; set; } + + public DmtxScheme Scheme { get; set; } + + public DmtxSymbolSize SizeIdx { get; set; } + + public Color ForeColor { get; set; } + + public Color BackColor { get; set; } + + public Encoding Encoding { get; set; } + + #endregion + } +} diff --git a/DataMatrix.net/DmtxMatrix3.cs b/DataMatrix.net/DmtxMatrix3.cs new file mode 100644 index 0000000..86104af --- /dev/null +++ b/DataMatrix.net/DmtxMatrix3.cs @@ -0,0 +1,249 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; + +namespace DataMatrix.net +{ + internal class DmtxMatrix3 + { + #region Fields + double[,] _data; + #endregion + + #region Constructors + DmtxMatrix3() + { + } + + /// + /// copy constructor + /// + /// the matrix to copy + internal DmtxMatrix3(DmtxMatrix3 src) + { + _data = new[,] { { src[0, 0], src[0, 1], src[0, 2] }, { src[1, 0], src[1, 1], src[1, 2] }, { src[2, 0], src[2, 1], src[2, 2] } }; + } + #endregion + + #region internal Static Methods + /// + /// creates a 3x3 identitiy matrix: + /// 1 0 0 + /// 0 1 0 + /// 0 0 1 + /// + internal static DmtxMatrix3 Identity() + { + return Translate(0, 0); + } + + /// + /// generates a 3x3 translate transformation matrix + /// 1 0 0 + /// 0 1 0 + /// tx ty 1 + /// + /// + /// + internal static DmtxMatrix3 Translate(double tx, double ty) + { + DmtxMatrix3 result = new DmtxMatrix3 {_data = new[,] {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {tx, ty, 1.0}}}; + return result; + } + + /// + /// generates a 3x3 rotate transformation matrix + /// cos(angle) sin(angle) 0 + /// -sin(angle) cos(angle) 0 + /// 0 0 1 + /// + /// + internal static DmtxMatrix3 Rotate(double angle) + { + DmtxMatrix3 result = new DmtxMatrix3 { _data = new[,] { + {Math.Cos(angle), Math.Sin(angle), 0.0}, + {-Math.Sin(angle), Math.Cos(angle), 0.0}, {0, 0, 1.0} }}; + return result; + } + + /// + /// generates a 3x3 scale transformation matrix + /// sx 0 0 + /// 0 sy 0 + /// 0 0 1 + /// + /// + /// + /// + internal static DmtxMatrix3 Scale(double sx, double sy) + { + DmtxMatrix3 result = new DmtxMatrix3 {_data = new[,] {{sx, 0.0, 0.0}, {0.0, sy, 0.0}, {0, 0, 1.0}}}; + return result; + } + + /// + /// generates a 3x3 shear transformation matrix + /// 0 shx 0 + /// shy 0 0 + /// 0 0 1 + /// + /// + internal static DmtxMatrix3 Shear(double shx, double shy) + { + DmtxMatrix3 result = new DmtxMatrix3 {_data = new[,] {{1.0, shy, 0.0}, {shx, 1.0, 0.0}, {0, 0, 1.0}}}; + return result; + } + + /// + /// generates a 3x3 top line skew transformation matrix + /// b1/b0 0 (b1-b0)/(sz * b0) + /// 0 sz/b0 0 + /// 0 0 1 + /// + /// + internal static DmtxMatrix3 LineSkewTop(double b0, double b1, double sz) + { + if (b0 < DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("b0 must be larger than zero in top line skew transformation"); + } + DmtxMatrix3 result = new DmtxMatrix3 + {_data = new[,] {{b1/b0, 0.0, (b1 - b0)/(sz*b0)}, {0.0, sz/b0, 0.0}, {0, 0, 1.0}}}; + return result; + } + + + /// + /// generates a 3x3 top line skew transformation matrix (inverse) + /// b0/b1 0 (b0-b1)/(sz * b1) + /// 0 b0/sz 0 + /// 0 0 1 + /// + /// + internal static DmtxMatrix3 LineSkewTopInv(double b0, double b1, double sz) + { + if (b1 < DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("b1 must be larger than zero in top line skew transformation (inverse)"); + } + DmtxMatrix3 result = new DmtxMatrix3 + {_data = new[,] {{b0/b1, 0.0, (b0 - b1)/(sz*b1)}, {0.0, b0/sz, 0.0}, {0, 0, 1.0}}}; + return result; + } + + /// + /// generates a 3x3 side line skew transformation matrix (inverse) + /// sz/b0 0 0 + /// 0 b1/b0 (b1-b0)/(sz*b0) + /// 0 0 1 + /// + /// + internal static DmtxMatrix3 LineSkewSide(double b0, double b1, double sz) + { + if (b0 < DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("b0 must be larger than zero in side line skew transformation (inverse)"); + } + DmtxMatrix3 result = new DmtxMatrix3 + {_data = new[,] {{sz/b0, 0.0, 0.0}, {0.0, b1/b0, (b1 - b0)/(sz*b0)}, {0, 0, 1.0}}}; + return result; + } + + /// + /// generates a 3x3 side line skew transformation matrix (inverse) + /// b0/sz 0 0 + /// 0 b0/b1 (b0 - b1) / (sz * b1) + /// 0 0 1 + /// + /// + internal static DmtxMatrix3 LineSkewSideInv(double b0, double b1, double sz) + { + if (b1 < DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("b1 must be larger than zero in top line skew transformation (inverse)"); + } + DmtxMatrix3 result = new DmtxMatrix3 + {_data = new[,] {{b0/sz, 0.0, 0.0}, {0.0, b0/b1, (b0 - b1)/(sz*b1)}, {0, 0, 1.0}}}; + return result; + } + + public static DmtxMatrix3 operator *(DmtxMatrix3 m1, DmtxMatrix3 m2) + { + DmtxMatrix3 result = new DmtxMatrix3 {_data = new[,] {{0.0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; + + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + for (int k = 0; k < 3; k++) + { + result[i, j] += m1[i, k] * m2[k, j]; + } + } + } + return result; + } + + public static DmtxVector2 operator *(DmtxVector2 vector, DmtxMatrix3 matrix) + { + double w = Math.Abs(vector.X * matrix[0, 2] + vector.Y * matrix[1, 2] + matrix[2, 2]); + if (w <= DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("Multiplication of vector and matrix resulted in invalid result"); + } + DmtxVector2 result = new DmtxVector2((vector.X*matrix[0,0] + vector.Y * matrix[1,0] + matrix[2,0])/w, + (vector.X * matrix[0,1] + vector.Y * matrix[1,1] + matrix[2,1])/w); + return result; + } + #endregion + + #region Methods + public override string ToString() + { + return string.Format("{0}\t{1}\t{2}\n{3}\t{4}\t{5}\n{6}\t{7}\t{8}\n", _data[0, 0], _data[0, 1], _data[0, 2], _data[1, 0], _data[1, 1], _data[1, 2], _data[2, 0], _data[2, 1], _data[2, 2]); + } + #endregion + + #region Properties + internal double this[int i, int j] + { + get + { + return _data[i, j]; + } + set + { + _data[i, j] = value; + } + } + #endregion + } +} diff --git a/DataMatrix.net/DmtxMessage.cs b/DataMatrix.net/DmtxMessage.cs new file mode 100644 index 0000000..60ded91 --- /dev/null +++ b/DataMatrix.net/DmtxMessage.cs @@ -0,0 +1,554 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; + +namespace DataMatrix.net +{ + internal class DmtxMessage + { + #region Fields + int _outputIdx; /* Internal index used to store output progress */ + + #endregion + + #region Constructors + internal DmtxMessage(DmtxSymbolSize sizeIdx, DmtxFormat symbolFormat) + { + if (symbolFormat != DmtxFormat.Matrix && symbolFormat != DmtxFormat.Mosaic) + { + throw new ArgumentException("Only DmtxFormats Matrix and Mosaic are currently supported"); + } + int mappingRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixRows, sizeIdx); + int mappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, sizeIdx); + + this.Array = new byte[mappingCols * mappingRows]; + + int codeSize = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx) + DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolErrorWords, sizeIdx); + this.Code = new byte[codeSize]; + + this.Output = new byte[10 * codeSize]; + } + #endregion + + #region Methods + internal void DecodeDataStream(DmtxSymbolSize sizeIdx, byte[] outputStart) + { + bool macro = false; + + this.Output = outputStart ?? this.Output; + this._outputIdx = 0; + + byte[] ptr = this.Code; + int dataEndIndex = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolDataWords, sizeIdx); + + /* Print macro header if first codeword triggers it */ + if (ptr[0] == DmtxConstants.DmtxChar05Macro || ptr[0] == DmtxConstants.DmtxChar06Macro) + { + PushOutputMacroHeader(ptr[0]); + macro = true; + } + + for (int codeIter = 0; codeIter < dataEndIndex; ) + { + + DmtxScheme encScheme = GetEncodationScheme(this.Code[codeIter]); + if (encScheme != DmtxScheme.DmtxSchemeAscii) + codeIter++; + + switch (encScheme) + { + case DmtxScheme.DmtxSchemeAscii: + codeIter = DecodeSchemeAscii(codeIter, dataEndIndex); + break; + case DmtxScheme.DmtxSchemeC40: + case DmtxScheme.DmtxSchemeText: + codeIter = DecodeSchemeC40Text(codeIter, dataEndIndex, encScheme); + break; + case DmtxScheme.DmtxSchemeX12: + codeIter = DecodeSchemeX12(codeIter, dataEndIndex); + break; + case DmtxScheme.DmtxSchemeEdifact: + codeIter = DecodeSchemeEdifact(codeIter, dataEndIndex); + break; + case DmtxScheme.DmtxSchemeBase256: + codeIter = DecodeSchemeBase256(codeIter, dataEndIndex); + break; + } + } + + /* Print macro trailer if required */ + if (macro) + { + PushOutputMacroTrailer(); + } + } + + private void PushOutputMacroHeader(byte macroType) + { + PushOutputWord((byte)'['); + PushOutputWord((byte)')'); + PushOutputWord((byte)'>'); + PushOutputWord(30); /* ASCII RS */ + PushOutputWord((byte)'0'); + + if (macroType == DmtxConstants.DmtxChar05Macro) + { + PushOutputWord((byte)'5'); + } + else if (macroType == DmtxConstants.DmtxChar06Macro) + { + PushOutputWord((byte)'6'); + } + else + { + throw new ArgumentException("Macro Header only supported for char05 and char06"); + } + + PushOutputWord(29); /* ASCII GS */ + } + + void PushOutputMacroTrailer() + { + PushOutputWord(30); /* ASCII RS */ + PushOutputWord(4); /* ASCII EOT */ + } + + void PushOutputWord(byte value) + { + this.Output[this._outputIdx++] = value; + } + + static DmtxScheme GetEncodationScheme(byte val) + { + if (val == DmtxConstants.DmtxCharC40Latch) + { + return DmtxScheme.DmtxSchemeC40; + } + if (val == DmtxConstants.DmtxCharBase256Latch) + { + return DmtxScheme.DmtxSchemeBase256; + } + if (val == DmtxConstants.DmtxCharEdifactLatch) + { + return DmtxScheme.DmtxSchemeEdifact; + } + if (val == DmtxConstants.DmtxCharTextLatch) + { + return DmtxScheme.DmtxSchemeText; + } + if (val == DmtxConstants.DmtxCharX12Latch) + { + return DmtxScheme.DmtxSchemeX12; + } + return DmtxScheme.DmtxSchemeAscii; + } + + int DecodeSchemeAscii(int startIndex, int endIndex) + { + bool upperShift = false; + + while (startIndex < endIndex) + { + + byte codeword = this.Code[startIndex]; + + if (GetEncodationScheme(this.Code[startIndex]) != DmtxScheme.DmtxSchemeAscii) + return startIndex; + + startIndex++; + + if (upperShift) + { + PushOutputWord((byte)(codeword + 127)); + upperShift = false; + } + else if (codeword == DmtxConstants.DmtxCharAsciiUpperShift) + { + upperShift = true; + } + else if (codeword == DmtxConstants.DmtxCharAsciiPad) + { + this.PadCount = endIndex - startIndex; + return endIndex; + } + else if (codeword <= 128) + { + PushOutputWord((byte)(codeword - 1)); + } + else if (codeword <= 229) + { + byte digits = (byte)(codeword - 130); + PushOutputWord((byte)(digits / 10 + '0')); + PushOutputWord((byte)(digits - (digits / 10) * 10 + '0')); + } + } + + return startIndex; + } + + int DecodeSchemeC40Text(int startIndex, int endIndex, DmtxScheme encScheme) + { + int[] c40Values = new int[3]; + C40TextState state = new C40TextState {Shift = DmtxConstants.DmtxC40TextBasicSet, UpperShift = false}; + + + if (!(encScheme == DmtxScheme.DmtxSchemeC40 || encScheme == DmtxScheme.DmtxSchemeText)) + { + throw new ArgumentException("Invalid scheme selected for decodind!"); + } + + while (startIndex < endIndex) + { + + /* FIXME Also check that ptr+1 is safe to access */ + int packed = (this.Code[startIndex] << 8) | this.Code[startIndex + 1]; + c40Values[0] = ((packed - 1) / 1600); + c40Values[1] = ((packed - 1) / 40) % 40; + c40Values[2] = (packed - 1) % 40; + startIndex += 2; + + int i; + for (i = 0; i < 3; i++) + { + if (state.Shift == DmtxConstants.DmtxC40TextBasicSet) + { /* Basic set */ + if (c40Values[i] <= 2) + { + state.Shift = c40Values[i] + 1; + } + else if (c40Values[i] == 3) + { + PushOutputC40TextWord(ref state, ' '); + } + else if (c40Values[i] <= 13) + { + PushOutputC40TextWord(ref state, c40Values[i] - 13 + '9'); /* 0-9 */ + } + else if (c40Values[i] <= 39) + { + if (encScheme == DmtxScheme.DmtxSchemeC40) + { + PushOutputC40TextWord(ref state, c40Values[i] - 39 + 'Z'); /* A-Z */ + } + else if (encScheme == DmtxScheme.DmtxSchemeText) + { + PushOutputC40TextWord(ref state, c40Values[i] - 39 + 'z'); /* a-z */ + } + } + } + else if (state.Shift == DmtxConstants.DmtxC40TextShift1) + { /* Shift 1 set */ + PushOutputC40TextWord(ref state, c40Values[i]); /* ASCII 0 - 31 */ + } + else if (state.Shift == DmtxConstants.DmtxC40TextShift2) + { /* Shift 2 set */ + if (c40Values[i] <= 14) + { + PushOutputC40TextWord(ref state, c40Values[i] + 33); /* ASCII 33 - 47 */ + } + else if (c40Values[i] <= 21) + { + PushOutputC40TextWord(ref state, c40Values[i] + 43); /* ASCII 58 - 64 */ + } + else if (c40Values[i] <= 26) + { + PushOutputC40TextWord(ref state, c40Values[i] + 69); /* ASCII 91 - 95 */ + } + else if (c40Values[i] == 27) + { + PushOutputC40TextWord(ref state, 0x1d); /* FNC1 -- XXX depends on position? */ + } + else if (c40Values[i] == 30) + { + state.UpperShift = true; + state.Shift = DmtxConstants.DmtxC40TextBasicSet; + } + } + else if (state.Shift == DmtxConstants.DmtxC40TextShift3) + { /* Shift 3 set */ + if (encScheme == DmtxScheme.DmtxSchemeC40) + { + PushOutputC40TextWord(ref state, c40Values[i] + 96); + } + else if (encScheme == DmtxScheme.DmtxSchemeText) + { + if (c40Values[i] == 0) + PushOutputC40TextWord(ref state, c40Values[i] + 96); + else if (c40Values[i] <= 26) + PushOutputC40TextWord(ref state, c40Values[i] - 26 + 'Z'); /* A-Z */ + else + PushOutputC40TextWord(ref state, c40Values[i] - 31 + 127); /* { | } ~ DEL */ + } + } + } + + /* Unlatch if codeword 254 follows 2 codewords in C40/Text encodation */ + if (this.Code[startIndex] == DmtxConstants.DmtxCharTripletUnlatch) + return startIndex + 1; + + /* Unlatch is implied if only one codeword remains */ + if (endIndex - startIndex == 1) + return startIndex; + } + return startIndex; + } + + void PushOutputC40TextWord(ref C40TextState state, int value) + { + if (!(value >= 0 && value < 256)) + { + throw new ArgumentException("Invalid value: Exceeds range for conversion to byte"); + } + + this.Output[this._outputIdx] = (byte)value; + + if (state.UpperShift) + { + if (!(value >= 0 && value < 256)) + { + throw new ArgumentException("Invalid value: Exceeds range for conversion to upper case character"); + } + this.Output[this._outputIdx] += 128; + } + + this._outputIdx++; + + state.Shift = DmtxConstants.DmtxC40TextBasicSet; + state.UpperShift = false; + } + + private int DecodeSchemeX12(int startIndex, int endIndex) + { + int[] x12Values = new int[3]; + + while (startIndex < endIndex) + { + + /* FIXME Also check that ptr+1 is safe to access */ + int packed = (this.Code[startIndex] << 8) | this.Code[startIndex + 1]; + x12Values[0] = ((packed - 1) / 1600); + x12Values[1] = ((packed - 1) / 40) % 40; + x12Values[2] = (packed - 1) % 40; + startIndex += 2; + + for (int i = 0; i < 3; i++) + { + if (x12Values[i] == 0) + PushOutputWord(13); + else if (x12Values[i] == 1) + PushOutputWord(42); + else if (x12Values[i] == 2) + PushOutputWord(62); + else if (x12Values[i] == 3) + PushOutputWord(32); + else if (x12Values[i] <= 13) + PushOutputWord((byte)(x12Values[i] + 44)); + else if (x12Values[i] <= 90) + PushOutputWord((byte)(x12Values[i] + 51)); + } + + /* Unlatch if codeword 254 follows 2 codewords in C40/Text encodation */ + if (this.Code[startIndex] == DmtxConstants.DmtxCharTripletUnlatch) + return startIndex + 1; + + /* Unlatch is implied if only one codeword remains */ + if (endIndex - startIndex == 1) + return startIndex; + } + + return startIndex; + } + + int DecodeSchemeEdifact(int startIndex, int endIndex) + { + byte[] unpacked = new byte[4]; + + while (startIndex < endIndex) + { + + /* FIXME Also check that ptr+2 is safe to access -- shouldn't be a + problem because I'm guessing you can guarantee there will always + be at least 3 error codewords */ + unpacked[0] = (byte)((this.Code[startIndex] & 0xfc) >> 2); + unpacked[1] = (byte)((this.Code[startIndex] & 0x03) << 4 | (this.Code[startIndex + 1] & 0xf0) >> 4); + unpacked[2] = (byte)((this.Code[startIndex + 1] & 0x0f) << 2 | (this.Code[startIndex + 2] & 0xc0) >> 6); + unpacked[3] = (byte)(this.Code[startIndex + 2] & 0x3f); + + for (int i = 0; i < 4; i++) + { + + /* Advance input ptr (4th value comes from already-read 3rd byte) */ + if (i < 3) + startIndex++; + + /* Test for unlatch condition */ + if (unpacked[i] == DmtxConstants.DmtxCharEdifactUnlatch) + { + if (this.Output[_outputIdx] != 0) + {/* XXX dirty why? */ + throw new Exception("Error decoding edifact scheme"); + } + return startIndex; + } + + PushOutputWord((byte)(unpacked[i] ^ (((unpacked[i] & 0x20) ^ 0x20) << 1))); + } + + /* Unlatch is implied if fewer than 3 codewords remain */ + if (endIndex - startIndex < 3) + { + return startIndex; + } + } + return startIndex; + } + + int DecodeSchemeBase256(int startIndex, int endIndex) + { + int tempEndIndex; + + /* Find positional index used for unrandomizing */ + int idx = startIndex + 1; + + int d0 = UnRandomize255State(this.Code[startIndex++], idx++); + if (d0 == 0) + { + tempEndIndex = endIndex; + } + else if (d0 <= 249) + { + tempEndIndex = startIndex + d0; + } + else + { + int d1 = UnRandomize255State(this.Code[startIndex++], idx++); + tempEndIndex = startIndex + (d0 - 249) * 250 + d1; + } + + if (tempEndIndex > endIndex) + { + throw new Exception("Error decoding scheme base 256"); + } + + while (startIndex < tempEndIndex) + { + PushOutputWord(UnRandomize255State(this.Code[startIndex++], idx++)); + } + + return startIndex; + } + + internal static byte UnRandomize255State(byte value, int idx) + { + int pseudoRandom = ((149 * idx) % 255) + 1; + int tmp = value - pseudoRandom; + if (tmp < 0) + tmp += 256; + + if (tmp < 0 || tmp >= 256) + { + throw new Exception("Error unrandomizing 255 state"); + } + + return (byte)tmp; + } + + internal int SymbolModuleStatus(DmtxSymbolSize sizeIdx, int symbolRow, int symbolCol) + { + int dataRegionRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionRows, sizeIdx); + int dataRegionCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribDataRegionCols, sizeIdx); + int symbolRows = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribSymbolRows, sizeIdx); + int mappingCols = DmtxCommon.GetSymbolAttribute(DmtxSymAttribute.DmtxSymAttribMappingMatrixCols, sizeIdx); + + int symbolRowReverse = symbolRows - symbolRow - 1; + int mappingRow = symbolRowReverse - 1 - 2 * (symbolRowReverse / (dataRegionRows + 2)); + int mappingCol = symbolCol - 1 - 2 * (symbolCol / (dataRegionCols + 2)); + + /* Solid portion of alignment patterns */ + if (symbolRow % (dataRegionRows + 2) == 0 || symbolCol % (dataRegionCols + 2) == 0) + { + return (DmtxConstants.DmtxModuleOnRGB | (DmtxConstants.DmtxModuleData == 0 ? 1 : 0)); // check if unary not is correct + } + + /* Horinzontal calibration bars */ + if ((symbolRow + 1) % (dataRegionRows + 2) == 0) + { + return (((symbolCol & 0x01) != 0) ? 0 : DmtxConstants.DmtxModuleOnRGB) | (DmtxConstants.DmtxModuleData == 0 ? 1 : 0); + } + + /* Vertical calibration bars */ + if ((symbolCol + 1) % (dataRegionCols + 2) == 0) + { + return (((symbolRow & 0x01) != 0) ? 0 : DmtxConstants.DmtxModuleOnRGB) | (DmtxConstants.DmtxModuleData == 0 ? 1 : 0); + } + + /* Data modules */ + return (this.Array[mappingRow * mappingCols + mappingCol] | DmtxConstants.DmtxModuleData); + } + + #endregion + + #region Properties + + internal int PadCount { get; set; } + + internal byte[] Array { get; set; } + + internal byte[] Code { get; set; } + + internal byte[] Output { get; set; } + + internal int ArraySize + { + get + { + return this.Array.Length; + } + } + + internal int CodeSize + { + get + { + return this.Code.Length; + } + } + + internal int OutputSize + { + get + { + return this.Output.Length; + } + } + #endregion + } +} diff --git a/DataMatrix.net/DmtxMultiplet.cs b/DataMatrix.net/DmtxMultiplet.cs new file mode 100644 index 0000000..fa9faab --- /dev/null +++ b/DataMatrix.net/DmtxMultiplet.cs @@ -0,0 +1,56 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal struct DmtxTriplet + { + byte[] _value; + + internal byte[] Value + { + get { return this._value ?? (this._value = new byte[3]); } + } + } + + /** + * @struct DmtxQuadruplet + * @brief DmtxQuadruplet + */ + internal struct DmtxQuadruplet + { + byte[] _value; + + internal byte[] Value + { + get { return this._value ?? (this._value = new byte[4]); } + } + } +} diff --git a/DataMatrix.net/DmtxPixelLoc.cs b/DataMatrix.net/DmtxPixelLoc.cs new file mode 100644 index 0000000..fd89d52 --- /dev/null +++ b/DataMatrix.net/DmtxPixelLoc.cs @@ -0,0 +1,47 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace DataMatrix.net +{ + internal struct DmtxPixelLoc + { + #region Properties + + internal int X { get; set; } + + internal int Y { get; set; } + + #endregion + } +} diff --git a/DataMatrix.net/DmtxPointFlow.cs b/DataMatrix.net/DmtxPointFlow.cs new file mode 100644 index 0000000..568481a --- /dev/null +++ b/DataMatrix.net/DmtxPointFlow.cs @@ -0,0 +1,49 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal class DmtxPointFlow + { + #region Properties + + internal int Plane { get; set; } + + internal int Arrive { get; set; } + + internal int Depart { get; set; } + + internal int Mag { get; set; } + + internal DmtxPixelLoc Loc { get; set; } + + #endregion + } +} diff --git a/DataMatrix.net/DmtxRay2.cs b/DataMatrix.net/DmtxRay2.cs new file mode 100644 index 0000000..8548f74 --- /dev/null +++ b/DataMatrix.net/DmtxRay2.cs @@ -0,0 +1,67 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal class DmtxRay2 + { + #region Fields + + DmtxVector2 _p; + DmtxVector2 _v; + #endregion + + #region Properties + internal DmtxVector2 P + { + get { return this._p ?? (this._p = new DmtxVector2()); } + set + { + _p = value; + } + } + + internal DmtxVector2 V + { + get { return this._v ?? (this._v = new DmtxVector2()); } + set + { + _v = value; + } + } + + + internal double TMin { get; set; } + + internal double TMax { get; set; } + + #endregion + } +} diff --git a/DataMatrix.net/DmtxRegion.cs b/DataMatrix.net/DmtxRegion.cs new file mode 100644 index 0000000..3f1625a --- /dev/null +++ b/DataMatrix.net/DmtxRegion.cs @@ -0,0 +1,161 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +namespace DataMatrix.net +{ + internal class DmtxRegion + { + #region Constructors + + internal DmtxRegion() + { + } + + internal DmtxRegion(DmtxRegion src) + { + this.BottomAngle = src.BottomAngle; + this.BottomKnown = src.BottomKnown; + this.BottomLine = src.BottomLine; + this.BottomLoc = src.BottomLoc; + this.BoundMax = src.BoundMax; + this.BoundMin = src.BoundMin; + this.FinalNeg = src.FinalNeg; + this.FinalPos = src.FinalPos; + this.Fit2Raw = new DmtxMatrix3(src.Fit2Raw); + this.FlowBegin = src.FlowBegin; + this.JumpToNeg = src.JumpToNeg; + this.JumpToPos = src.JumpToPos; + this.LeftAngle = src.LeftAngle; + this.LeftKnown = src.LeftKnown; + this.LeftLine = src.LeftLine; + this.LeftLoc = src.LeftLoc; + this.LocR = src.LocR; + this.LocT = src.LocT; + this.MappingCols = src.MappingCols; + this.MappingRows = src.MappingRows; + this.OffColor = src.OffColor; + this.OnColor = src.OnColor; + this.Polarity = src.Polarity; + this.Raw2Fit = new DmtxMatrix3(src.Raw2Fit); + this.RightAngle = src.RightAngle; + this.RightKnown = src.RightKnown; + this.RightLoc = src.RightLoc; + this.SizeIdx = src.SizeIdx; + this.StepR = src.StepR; + this.StepsTotal = src.StepsTotal; + this.StepT = src.StepT; + this.SymbolCols = src.SymbolCols; + this.SymbolRows = src.SymbolRows; + this.TopAngle = src.TopAngle; + this.TopKnown = src.TopKnown; + this.TopLoc = src.TopLoc; + } + #endregion + + #region Methods + #endregion + + #region Properties + + internal int JumpToPos { get; set; } + + internal int JumpToNeg { get; set; } + + internal int StepsTotal { get; set; } + + internal DmtxPixelLoc FinalPos { get; set; } + + internal DmtxPixelLoc FinalNeg { get; set; } + + internal DmtxPixelLoc BoundMin { get; set; } + + internal DmtxPixelLoc BoundMax { get; set; } + + internal DmtxPointFlow FlowBegin { get; set; } + + internal int Polarity { get; set; } + + internal int StepR { get; set; } + + internal int StepT { get; set; } + + internal DmtxPixelLoc LocR { get; set; } + + internal DmtxPixelLoc LocT { get; set; } + + internal int LeftKnown { get; set; } + + internal int LeftAngle { get; set; } + + internal DmtxPixelLoc LeftLoc { get; set; } + + internal DmtxBestLine LeftLine { get; set; } + + internal int BottomKnown { get; set; } + + internal int BottomAngle { get; set; } + + internal DmtxPixelLoc BottomLoc { get; set; } + + internal DmtxBestLine BottomLine { get; set; } + + internal int TopKnown { get; set; } + + internal int TopAngle { get; set; } + + internal DmtxPixelLoc TopLoc { get; set; } + + internal int RightKnown { get; set; } + + internal int RightAngle { get; set; } + + internal DmtxPixelLoc RightLoc { get; set; } + + internal int OnColor { get; set; } + + internal int OffColor { get; set; } + + internal DmtxSymbolSize SizeIdx { get; set; } + + internal int SymbolRows { get; set; } + + internal int SymbolCols { get; set; } + + internal int MappingRows { get; set; } + + internal int MappingCols { get; set; } + + internal DmtxMatrix3 Raw2Fit { get; set; } + + internal DmtxMatrix3 Fit2Raw { get; set; } + + #endregion + } +} diff --git a/DataMatrix.net/DmtxScanGrid.cs b/DataMatrix.net/DmtxScanGrid.cs new file mode 100644 index 0000000..45ac2a5 --- /dev/null +++ b/DataMatrix.net/DmtxScanGrid.cs @@ -0,0 +1,369 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; + +namespace DataMatrix.net +{ + internal struct DmtxScanGrid + { + #region Fields + int _minExtent; + int _maxExtent; + int _xOffset; + int _yOffset; + int _xMin; + int _xMax; + int _yMin; + int _yMax; + + int _total; + int _extent; + int _jumpSize; + int _pixelTotal; + int _startPos; + + int _pixelCount; + int _xCenter; + int _yCenter; + #endregion + + #region Constructors + internal DmtxScanGrid(DmtxDecode dec) + { + int smallestFeature = dec.ScanGap; + this._xMin = dec.XMin; + this._xMax = dec.XMax; + this._yMin = dec.YMin; + this._yMax = dec.YMax; + + /* Values that get set once */ + int xExtent = this._xMax - this._xMin; + int yExtent = this._yMax - this._yMin; + int maxExtent = (xExtent > yExtent) ? xExtent : yExtent; + + if (maxExtent < 1) + { + throw new ArgumentException("Invalid max extent for Scan Grid: Must be greater than 0"); + } + + int extent = 1; + this._minExtent = extent; + for (; extent < maxExtent; extent = ((extent + 1) * 2) - 1) + { + if (extent <= smallestFeature) + { + _minExtent = extent; + } + } + + this._maxExtent = extent ; + + this._xOffset = (this._xMin + this._xMax - this._maxExtent) / 2; + this._yOffset = (this._yMin + this._yMax - this._maxExtent) / 2; + + /* Values that get reset for every level */ + this._total = 1; + this._extent = this._maxExtent; + + this._jumpSize = this._extent + 1; + this._pixelTotal = 2 * this._extent - 1; + this._startPos = this._extent / 2; + this._pixelCount = 0; + this._xCenter = this._yCenter = this._startPos; + + SetDerivedFields(); + } + #endregion + + + #region Methods + internal DmtxRange PopGridLocation(ref DmtxPixelLoc loc) + { + DmtxRange locStatus; + + do + { + locStatus = GetGridCoordinates(ref loc); + + /* Always leave grid pointing at next available location */ + this._pixelCount++; + + } while (locStatus == DmtxRange.DmtxRangeBad); + + return locStatus; + } + + private DmtxRange GetGridCoordinates(ref DmtxPixelLoc locRef) + { + + /* Initially pixelCount may fall beyond acceptable limits. Update grid + * state before testing coordinates */ + + /* Jump to next cross pattern horizontally if current column is done */ + if (this._pixelCount >= this._pixelTotal) + { + this._pixelCount = 0; + this._xCenter += this._jumpSize; + } + + /* Jump to next cross pattern vertically if current row is done */ + if (this._xCenter > this._maxExtent) + { + this._xCenter = this._startPos; + this._yCenter += this._jumpSize; + } + + /* Increment level when vertical step goes too far */ + if (this._yCenter > this._maxExtent) + { + this._total *= 4; + this._extent /= 2; + SetDerivedFields(); + } + + if (this._extent == 0 || this._extent < this._minExtent) + { + locRef.X = locRef.Y = -1; + return DmtxRange.DmtxRangeEnd; + } + + int count = this._pixelCount; + + if (count >= this._pixelTotal) + { + throw new Exception("Scangrid is beyong image limits!"); + } + + DmtxPixelLoc loc = new DmtxPixelLoc(); + if (count == this._pixelTotal - 1) + { + /* center pixel */ + loc.X = this._xCenter; + loc.Y = this._yCenter; + } + else + { + int half = this._pixelTotal / 2; + int quarter = half / 2; + + /* horizontal portion */ + if (count < half) + { + loc.X = this._xCenter + ((count < quarter) ? (count - quarter) : (half - count)); + loc.Y = this._yCenter; + } + /* vertical portion */ + else + { + count -= half; + loc.X = this._xCenter; + loc.Y = this._yCenter + ((count < quarter) ? (count - quarter) : (half - count)); + } + } + + loc.X += this._xOffset; + loc.Y += this._yOffset; + + locRef.X = loc.X; + locRef.Y = loc.Y; + + if (loc.X < this._xMin || loc.X > this._xMax || + loc.Y < this._yMin || loc.Y > this._yMax) + { + return DmtxRange.DmtxRangeBad; + } + + return DmtxRange.DmtxRangeGood; + } + + + /// + /// Update derived fields based on current state + /// + private void SetDerivedFields() + { + this._jumpSize = this._extent + 1; + this._pixelTotal = 2 * this._extent - 1; + this._startPos = this._extent / 2; + this._pixelCount = 0; + this._xCenter = this._yCenter = this._startPos; + } + #endregion + + #region Properties + /// + /// Smallest cross size used in scan + /// + internal int MinExtent + { + get { return _minExtent; } + set { _minExtent = value; } + } + + /// + /// Size of bounding grid region (2^N - 1) + /// + internal int MaxExtent + { + get { return _maxExtent; } + set { _maxExtent = value; } + } + + /// + /// Offset to obtain image X coordinate + /// + internal int XOffset + { + get { return _xOffset; } + set { _xOffset = value; } + } + + /// + /// Offset to obtain image Y coordinate + /// + internal int YOffset + { + get { return _yOffset; } + set { _yOffset = value; } + } + + /// + /// Minimum X in image coordinate system + /// + internal int XMin + { + get { return _xMin; } + set { _xMin = value; } + } + + + /// + /// Maximum X in image coordinate system + /// + internal int XMax + { + get { return _xMax; } + set { _xMax = value; } + } + + /// + /// Minimum Y in image coordinate system + /// + internal int YMin + { + get { return _yMin; } + set { _yMin = value; } + } + + /// + /// Maximum Y in image coordinate system + /// + internal int YMax + { + get { return _yMax; } + set { _yMax = value; } + } + + /// + /// Total number of crosses at this size + /// + internal int Total + { + get { return _total; } + set { _total = value; } + } + + /// + /// Length/width of cross in pixels + /// + internal int Extent + { + get { return _extent; } + set { _extent = value; } + } + + /// + /// Distance in pixels between cross centers + /// + internal int JumpSize + { + get { return _jumpSize; } + set { _jumpSize = value; } + } + + /// + /// Total pixel count within an individual cross path + /// + internal int PixelTotal + { + get { return _pixelTotal; } + set { _pixelTotal = value; } + } + + /// + /// X and Y coordinate of first cross center in pattern + /// + internal int StartPos + { + get { return _startPos; } + set { _startPos = value; } + } + + /// + /// Progress (pixel count) within current cross pattern + /// + internal int PixelCount + { + get { return _pixelCount; } + set { _pixelCount = value; } + } + + /// + /// X center of current cross pattern + /// + internal int XCenter + { + get { return _xCenter; } + set { _xCenter = value; } + } + + + /// + /// Y center of current cross pattern + /// + internal int YCenter + { + get { return _yCenter; } + set { _yCenter = value; } + } + #endregion + } +} diff --git a/DataMatrix.net/DmtxVector2.cs b/DataMatrix.net/DmtxVector2.cs new file mode 100644 index 0000000..3e83196 --- /dev/null +++ b/DataMatrix.net/DmtxVector2.cs @@ -0,0 +1,152 @@ +/* +DataMatrix.Net + +DataMatrix.Net - .net library for decoding DataMatrix codes. +Copyright (C) 2009/2010 Michael Faschinger + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. +You can also redistribute and/or modify it under the terms of the +GNU Lesser General Public License as published by the Free Software +Foundation; either version 3.0 of the License or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License or the GNU Lesser General Public License +for more details. + +You should have received a copy of the GNU General Public +License and the GNU Lesser General Public License along with this +library; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Contact: Michael Faschinger - michfasch@gmx.at + +*/ + +using System; + +namespace DataMatrix.net +{ + internal class DmtxVector2 + { + #region Constructors + internal DmtxVector2() + { + this.X = 0.0; + this.Y = 0.0; + } + + internal DmtxVector2(double x, double y) + { + this.X = x; + this.Y = y; + } + #endregion + + #region Operators + public static DmtxVector2 operator +(DmtxVector2 v1, DmtxVector2 v2) + { + DmtxVector2 result = new DmtxVector2(v1.X, v1.Y); + result.X += v2.X; + result.Y += v2.Y; + return result; + } + + public static DmtxVector2 operator -(DmtxVector2 v1, DmtxVector2 v2) + { + DmtxVector2 result = new DmtxVector2(v1.X, v1.Y); + result.X -= v2.X; + result.Y -= v2.Y; + return result; + } + + public static DmtxVector2 operator *(DmtxVector2 v1, double factor) + { + return new DmtxVector2(v1.X * factor, v1.Y * factor); + } + #endregion + + #region Methods + internal double Cross(DmtxVector2 v2) + { + return (this.X * v2.Y - this.Y * v2.X); + } + + internal double Norm() + { + double mag = Mag(); + if (mag <= DmtxConstants.DmtxAlmostZero) + { + return -1.0; // FIXXXME: This doesn't look clean, as noted in original dmtx source + } + this.X /= mag; + this.Y /= mag; + return mag; + } + + internal double Dot(DmtxVector2 v2) + { + return Math.Sqrt(this.X * v2.X + this.Y * v2.Y); + } + + internal double Mag() + { + return Math.Sqrt(this.X * this.X + this.Y * this.Y); + } + + internal double DistanceFromRay2(DmtxRay2 ray) + { + if (Math.Abs(1.0 - ray.V.Mag()) > DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("DistanceFromRay2: The ray's V vector must be a unit vector"); + } + return ray.V.Cross(this - ray.P); + } + + internal double DistanceAlongRay2(DmtxRay2 ray) + { + if (Math.Abs(1.0 - ray.V.Mag()) > DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("DistanceAlongRay2: The ray's V vector must be a unit vector"); + } + return (this - ray.P).Dot(ray.V); + } + + internal bool Intersect(DmtxRay2 p0, DmtxRay2 p1) + { + double denominator = p1.V.Cross(p0.V); + if (Math.Abs(denominator) < DmtxConstants.DmtxAlmostZero) + { + return false; + } + double numerator = p1.V.Cross(p1.P - p0.P); + return PointAlongRay2(p0, numerator / denominator); + } + + internal bool PointAlongRay2(DmtxRay2 ray, double t) + { + if (Math.Abs(1.0 - ray.V.Mag()) > DmtxConstants.DmtxAlmostZero) + { + throw new ArgumentException("PointAlongRay: The ray's V vector must be a unit vector"); + } + DmtxVector2 tmp = new DmtxVector2(ray.V.X * t, ray.V.Y * t); + this.X = ray.P.X + tmp.X; + this.Y = ray.P.Y + tmp.Y; + return true; + } + #endregion + + #region Properties + + internal double X { get; set; } + + internal double Y { get; set; } + + #endregion + } +}