This commit is contained in:
TheXamlGuy
2024-04-26 23:05:36 +01:00
parent 9f90ef693d
commit bc55c4649b
206 changed files with 3106 additions and 3204 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ public class FrameHandler(INavigationContext navigationContext) :
async void HandleNavigatingFrom(object? _, async void HandleNavigatingFrom(object? _,
NavigatingCancelEventArgs args) NavigatingCancelEventArgs args)
{ {
Dictionary<string,object> results = []; Dictionary<string, object> results = [];
control.RemoveHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom); control.RemoveHandler(Frame.NavigatingFromEvent, HandleNavigatingFrom);
NavigatedFrom(sender, control, () => results); NavigatedFrom(sender, control, () => results);
-1
View File
@@ -34,4 +34,3 @@ public class NavigationContext(INavigationContextCollection contexts) :
} }
} }
} }
@@ -2,5 +2,4 @@
public record ComponentConfiguration public record ComponentConfiguration
{ {
} }
@@ -18,7 +18,6 @@ public partial class ComponentConfigurationViewModel<TConfiguration, TValue, THe
TDescription description, TDescription description,
TAction action) : base(provider, factory, mediator, publisher, subscriber, disposer) TAction action) : base(provider, factory, mediator, publisher, subscriber, disposer)
{ {
} }
public Task Handle(Changed<TConfiguration> args, public Task Handle(Changed<TConfiguration> args,
@@ -58,6 +57,7 @@ public partial class ComponentConfigurationViewModel<TConfiguration, TValue, TAc
Value = valueDelegate.Invoke(configuration); Value = valueDelegate.Invoke(configuration);
return base.Activated(); return base.Activated();
} }
public Task Handle(Changed<TConfiguration> args, public Task Handle(Changed<TConfiguration> args,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
-1
View File
@@ -15,7 +15,6 @@ public class ComponentHost(IServiceProvider services,
public void Dispose() public void Dispose()
{ {
} }
public async Task StartAsync(CancellationToken cancellationToken = default) public async Task StartAsync(CancellationToken cancellationToken = default)
+1 -2
View File
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
@@ -12,7 +12,6 @@ public class ConfigurationChangedHandler<TConfiguration, TValue>(ConfigurationVa
{ {
if (configurationValue.TryUpdate(configuration, out TValue value)) if (configurationValue.TryUpdate(configuration, out TValue value))
{ {
} }
} }
+1 -2
View File
@@ -1,5 +1,4 @@
namespace Toolkit.Foundation;
namespace Toolkit.Foundation;
public record Create<TValue>(TValue Value); public record Create<TValue>(TValue Value);
+1 -1
View File
@@ -144,12 +144,12 @@ public static class Test
services.AddTransient(provider => services.AddTransient(provider =>
provider.GetRequiredKeyedService<IConfigurationDescriptor<TConfiguration>>(section).Value); provider.GetRequiredKeyedService<IConfigurationDescriptor<TConfiguration>>(section).Value);
} }
}); });
return builder; return builder;
} }
} }
public class DefaultBuilder : public class DefaultBuilder :
HostBuilder HostBuilder
{ {
@@ -56,20 +56,26 @@ public class DictionaryStringObjectJsonConverter :
return date; return date;
} }
return reader.GetString(); return reader.GetString();
case JsonTokenType.False: case JsonTokenType.False:
return false; return false;
case JsonTokenType.True: case JsonTokenType.True:
return true; return true;
case JsonTokenType.Null: case JsonTokenType.Null:
return null; return null;
case JsonTokenType.Number: case JsonTokenType.Number:
if (reader.TryGetInt64(out var result)) if (reader.TryGetInt64(out var result))
{ {
return result; return result;
} }
return reader.GetDecimal(); return reader.GetDecimal();
case JsonTokenType.StartObject: case JsonTokenType.StartObject:
return Read(ref reader, null!, options); return Read(ref reader, null!, options);
case JsonTokenType.StartArray: case JsonTokenType.StartArray:
List<object?> list = []; List<object?> list = [];
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
@@ -77,6 +83,7 @@ public class DictionaryStringObjectJsonConverter :
list.Add(ExtractValue(ref reader, options)); list.Add(ExtractValue(ref reader, options));
} }
return list; return list;
default: default:
return default; return default;
} }
+2 -2
View File
@@ -1,6 +1,6 @@
using System.Reactive.Disposables; using System.Collections;
using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Reactive.Disposables;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
@@ -2,5 +2,4 @@
public interface IComponentConfigurationViewModel public interface IComponentConfigurationViewModel
{ {
} }
@@ -2,4 +2,3 @@
public interface IComponentScopeCollection : public interface IComponentScopeCollection :
IList<ComponentScopeDescriptor>; IList<ComponentScopeDescriptor>;
@@ -4,4 +4,3 @@ public interface IComponentScopeProvider
{ {
ComponentScopeDescriptor? Get(string key); ComponentScopeDescriptor? Get(string key);
} }
+1 -2
View File
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
-1
View File
@@ -2,5 +2,4 @@
public interface IContentTemplate public interface IContentTemplate
{ {
} }
-1
View File
@@ -5,7 +5,6 @@ public interface IFactory<TParameter, TService>
TService? Create(TParameter value); TService? Create(TParameter value);
} }
public interface IFactory<TService> public interface IFactory<TService>
{ {
TService? Create(); TService? Create();
+7 -9
View File
@@ -1,11 +1,9 @@
namespace Toolkit.Foundation;
namespace Toolkit.Foundation
public interface IMediator
{ {
Task<TResponse?> Handle<TRequest, TResponse>(TRequest request, public interface IMediator
CancellationToken cancellationToken = default) {
where TRequest : notnull; Task<object?> Handle(object message, CancellationToken cancellationToken = default);
Task<TResponse?> Handle<TRequest, TResponse>(TRequest request, CancellationToken cancellationToken = default) where TRequest : notnull;
Task<object?> Handle(object request, CancellationToken }
cancellationToken = default);
} }
-1
View File
@@ -4,4 +4,3 @@ public interface INavigation
{ {
Type Type { get; set; } Type Type { get; set; }
} }
-1
View File
@@ -7,4 +7,3 @@ public interface INavigationScope
Task NavigateBackAsync(object? context, CancellationToken cancellationToken = default); Task NavigateBackAsync(object? context, CancellationToken cancellationToken = default);
} }
@@ -9,4 +9,3 @@ public interface IConfirmation
{ {
Task<bool> Confirm(); Task<bool> Confirm();
} }
+1
View File
@@ -19,6 +19,7 @@ public interface IPublisher
object key, object key,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
where TMessage : notnull; where TMessage : notnull;
Task PublishUI<TMessage>(object key, Task PublishUI<TMessage>(object key,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
where TMessage : new(); where TMessage : new();
-6
View File
@@ -1,6 +0,0 @@
namespace Toolkit.Foundation;
public interface IRequest<out TResponse> :
IMessage;
public interface IRequest : IRequest<Unit>;
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Configuration.Json; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.FileProviders.Physical; using Microsoft.Extensions.FileProviders.Physical;
+1 -2
View File
@@ -1,5 +1,4 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public record KeyAccelerator(VirtualKey Key, public record KeyAccelerator(VirtualKey Key,
VirtualKey[]? Modifiers = null) : VirtualKey[]? Modifiers = null);
IRequest;
+1 -1
View File
@@ -27,7 +27,7 @@ public class Mediator(IServiceProvider provider) :
public Task<object?> Handle(object message, public Task<object?> Handle(object message,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (message.GetType().GetInterface(typeof(IRequest<>).Name) is Type requestType && if (message.GetType().GetInterface(message.GetType().Name) is Type requestType &&
requestType.GetGenericArguments().Length == 1) requestType.GetGenericArguments().Length == 1)
{ {
Type responseType = requestType.GetGenericArguments()[0]; Type responseType = requestType.GetGenericArguments()[0];
@@ -18,5 +18,3 @@ public class NavigateBackHandler(IComponentScopeProvider provider) :
} }
} }
} }
-7
View File
@@ -1,7 +0,0 @@
namespace Toolkit.Foundation;
public record NavigatingFrom(object Content) :
IRequest<IReadOnlyCollection<object>>;
public record NavigatingTo(object Content) :
IRequest<IReadOnlyCollection<object>>;
-1
View File
@@ -5,4 +5,3 @@ public record Navigation :
{ {
public required Type Type { get; set; } public required Type Type { get; set; }
} }
-1
View File
@@ -101,4 +101,3 @@ public class NavigationScope(IPublisher publisher,
} }
} }
} }
@@ -5,6 +5,7 @@ using System.Collections.Specialized;
using System.Reactive.Disposables; using System.Reactive.Disposables;
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class ObservableCollectionViewModel<TViewModel> : public partial class ObservableCollectionViewModel<TViewModel> :
ObservableObject, ObservableObject,
IObservableCollectionViewModel<TViewModel>, IObservableCollectionViewModel<TViewModel>,
@@ -113,7 +114,6 @@ public partial class ObservableCollectionViewModel<TViewModel> :
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
} }
this[index] = item!; this[index] = item!;
@@ -172,7 +172,6 @@ public partial class ObservableCollectionViewModel<TViewModel> :
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
} }
Add(item!); Add(item!);
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public partial class ObservableViewModel : public partial class ObservableViewModel :
ObservableObject, ObservableObject,
IObservableViewModel, IObservableViewModel,
IInitializer, IInitializer,
+3 -2
View File
@@ -66,14 +66,14 @@ public class Publisher(ISubscriptionManager subscriptionManager,
null, cancellationToken); null, cancellationToken);
public Task Publish<TMessage>(CancellationToken cancellationToken = default) public Task Publish<TMessage>(CancellationToken cancellationToken = default)
where TMessage : new() => where TMessage : new() =>
Publish(new TMessage(), async args => await args(), Publish(new TMessage(), async args => await args(),
null, cancellationToken); null, cancellationToken);
public Task PublishUI<TMessage>(object key, public Task PublishUI<TMessage>(object key,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
where TMessage : new() => where TMessage : new() =>
Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken); key, cancellationToken);
public Task PublishUI<TMessage>(TMessage message, public Task PublishUI<TMessage>(TMessage message,
@@ -88,6 +88,7 @@ public class Publisher(ISubscriptionManager subscriptionManager,
where TMessage : notnull => where TMessage : notnull =>
Publish(message, args => dispatcher.InvokeAsync(async () => await args()), Publish(message, args => dispatcher.InvokeAsync(async () => await args()),
key, cancellationToken); key, cancellationToken);
public Task PublishUI<TMessage>(CancellationToken cancellationToken = default) public Task PublishUI<TMessage>(CancellationToken cancellationToken = default)
where TMessage : new() => where TMessage : new() =>
Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()), Publish(new TMessage(), args => dispatcher.InvokeAsync(async () => await args()),
+1 -2
View File
@@ -1,5 +1,4 @@
namespace Toolkit.Foundation;
namespace Toolkit.Foundation;
public record Remove<TValue>(TValue Value); public record Remove<TValue>(TValue Value);
+1 -2
View File
@@ -1,5 +1,4 @@
namespace Toolkit.Foundation;
namespace Toolkit.Foundation;
public record Replace<TValue>(int Index, TValue Value); public record Replace<TValue>(int Index, TValue Value);
+1 -1
View File
@@ -1,3 +1,3 @@
namespace Toolkit.Foundation; namespace Toolkit.Foundation;
public record StartProcess(string Process) : IRequest; public record StartProcess(string Process);
+1 -3
View File
@@ -1,6 +1,4 @@
using System.Reflection; namespace Toolkit.Foundation;
namespace Toolkit.Foundation;
public class SubscriptionManager(SubscriptionCollection subscriptions) : public class SubscriptionManager(SubscriptionCollection subscriptions) :
ISubscriptionManager ISubscriptionManager
-1
View File
@@ -15,7 +15,6 @@ public partial class ValueViewModel<TValue>(IServiceProvider provider,
protected virtual void OnChanged(TValue? value) protected virtual void OnChanged(TValue? value)
{ {
} }
partial void OnValueChanged(TValue? value) => OnChanged(value); partial void OnValueChanged(TValue? value) => OnChanged(value);
+6 -3
View File
@@ -23,7 +23,6 @@ internal static class ComparisonLogic
} }
} }
if (leftOperand is IComparable leftComparableOperand && if (leftOperand is IComparable leftComparableOperand &&
rightOperand is IComparable rightComparableOperand) rightOperand is IComparable rightComparableOperand)
{ {
@@ -35,6 +34,7 @@ internal static class ComparisonLogic
case ComparisonConditionType.Equal: case ComparisonConditionType.Equal:
result = Equals(leftOperand, rightOperand); result = Equals(leftOperand, rightOperand);
break; break;
case ComparisonConditionType.NotEqual: case ComparisonConditionType.NotEqual:
result = !Equals(leftOperand, rightOperand); result = !Equals(leftOperand, rightOperand);
break; break;
@@ -54,11 +54,9 @@ internal static class ComparisonLogic
} }
catch (FormatException) catch (FormatException)
{ {
} }
catch (InvalidCastException) catch (InvalidCastException)
{ {
} }
if (convertedOperand == null) if (convertedOperand == null)
@@ -74,18 +72,23 @@ internal static class ComparisonLogic
case ComparisonConditionType.Equal: case ComparisonConditionType.Equal:
result = comparison == 0; result = comparison == 0;
break; break;
case ComparisonConditionType.GreaterThan: case ComparisonConditionType.GreaterThan:
result = comparison > 0; result = comparison > 0;
break; break;
case ComparisonConditionType.GreaterThanOrEqual: case ComparisonConditionType.GreaterThanOrEqual:
result = comparison >= 0; result = comparison >= 0;
break; break;
case ComparisonConditionType.LessThan: case ComparisonConditionType.LessThan:
result = comparison < 0; result = comparison < 0;
break; break;
case ComparisonConditionType.LessThanOrEqual: case ComparisonConditionType.LessThanOrEqual:
result = comparison <= 0; result = comparison <= 0;
break; break;
case ComparisonConditionType.NotEqual: case ComparisonConditionType.NotEqual:
result = comparison != 0; result = comparison != 0;
break; break;
@@ -1,6 +1,5 @@
using Avalonia; using Avalonia;
using Avalonia.Metadata; using Avalonia.Metadata;
using Toolkit.UI.Avalonia;
namespace Toolkit.UI.Avalonia; namespace Toolkit.UI.Avalonia;
+3 -1
View File
@@ -51,6 +51,7 @@ public class NavigateAction :
get => GetValue(RouteProperty); get => GetValue(RouteProperty);
set => SetValue(RouteProperty, value); set => SetValue(RouteProperty, value);
} }
public string Scope public string Scope
{ {
get => GetValue(ScopeProperty); get => GetValue(ScopeProperty);
@@ -67,7 +68,8 @@ public class NavigateAction :
if (control.DataContext is IObservableViewModel observableViewModel) if (control.DataContext is IObservableViewModel observableViewModel)
{ {
object[] parameters = [.. Parameters ?? Enumerable.Empty<object?>(), .. object[] parameters = [.. Parameters ?? Enumerable.Empty<object?>(),
..
ParameterBindings is { Count: > 0 } ? ParameterBindings is { Count: > 0 } ?
ParameterBindings.Select(binding => new KeyValuePair<string, object>(binding.Key, binding.Value)).ToArray() : ParameterBindings.Select(binding => new KeyValuePair<string, object>(binding.Key, binding.Value)).ToArray() :
Enumerable.Empty<KeyValuePair<string, object>>()]; Enumerable.Empty<KeyValuePair<string, object>>()];
+1
View File
@@ -16,6 +16,7 @@ public class ParameterBinding :
get => GetValue(KeyProperty); get => GetValue(KeyProperty);
set => SetValue(KeyProperty, value); set => SetValue(KeyProperty, value);
} }
public object Value public object Value
{ {
get => GetValue(ValueProperty); get => GetValue(ValueProperty);
@@ -17,21 +17,21 @@ public class BlurBehind :
public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialDark = public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialDark =
(ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial() (ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial()
{ {
MaterialOpacity = 0.25, MaterialOpacity = 0.25,
TintColor = Colors.Black, TintColor = Colors.Black,
TintOpacity = 0.7, TintOpacity = 0.7,
PlatformTransparencyCompensationLevel = 0 PlatformTransparencyCompensationLevel = 0
}.ToImmutable(); }.ToImmutable();
public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialLight = public static readonly ImmutableExperimentalAcrylicMaterial DefaultAcrylicMaterialLight =
(ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial() (ImmutableExperimentalAcrylicMaterial)new ExperimentalAcrylicMaterial()
{ {
MaterialOpacity = 0.0, MaterialOpacity = 0.0,
TintColor = Colors.White, TintColor = Colors.White,
TintOpacity = 0.3, TintOpacity = 0.3,
PlatformTransparencyCompensationLevel = 0 PlatformTransparencyCompensationLevel = 0
}.ToImmutable(); }.ToImmutable();
static BlurBehind() static BlurBehind()
{ {
@@ -63,7 +63,6 @@ public class BlurBehind :
public void Dispose() public void Dispose()
{ {
} }
public bool Equals(ICustomDrawOperation? other) => public bool Equals(ICustomDrawOperation? other) =>
@@ -74,7 +73,7 @@ public class BlurBehind :
public void Render(ImmediateDrawingContext context) public void Render(ImmediateDrawingContext context)
{ {
if (context.TryGetFeature<ISkiaSharpApiLeaseFeature>() is ISkiaSharpApiLeaseFeature leaseFeature) if (context.TryGetFeature<ISkiaSharpApiLeaseFeature>() is ISkiaSharpApiLeaseFeature leaseFeature)
{ {
using ISkiaSharpApiLease? lease = leaseFeature.Lease(); using ISkiaSharpApiLease? lease = leaseFeature.Lease();
if (lease.SkCanvas is SKCanvas canvas) if (lease.SkCanvas is SKCanvas canvas)
@@ -105,11 +104,9 @@ public class BlurBehind :
canvas.DrawRect(0, 0, (float)bounds.Width, (float)bounds.Height, blurSnapPaint); canvas.DrawRect(0, 0, (float)bounds.Width, (float)bounds.Height, blurSnapPaint);
} }
} }
} }
} }
} }
} }
} }
@@ -242,7 +242,6 @@ public class CarouselView :
{ {
itemVisuals[(newIndex + i - 2 + columnCount) % columnCount].Offset = itemVisuals[(newIndex + i - 2 + columnCount) % columnCount].Offset =
new Vector3((float)(offsets[i] - centreOffset), 0, 0); new Vector3((float)(offsets[i] - centreOffset), 0, 0);
} }
}; };
@@ -253,6 +252,7 @@ public class CarouselView :
} }
} }
} }
private void OnCollectionChanged(object? sender, private void OnCollectionChanged(object? sender,
NotifyCollectionChangedEventArgs args) => ArrangeItems(newIndex); NotifyCollectionChangedEventArgs args) => ArrangeItems(newIndex);
@@ -1,9 +1,9 @@
using Avalonia.Media.Imaging; using Avalonia;
using Avalonia.Styling;
using Avalonia;
using SukiUI.Utilities.Background;
using Avalonia.Platform;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Styling;
using SukiUI.Utilities.Background;
namespace Toolkit.UI.Controls.Avalonia; namespace Toolkit.UI.Controls.Avalonia;
@@ -1,6 +1,7 @@
using Avalonia.Threading; using Avalonia.Threading;
namespace Toolkit.UI.Controls.Avalonia; namespace Toolkit.UI.Controls.Avalonia;
public class ScopedBatchHelper public class ScopedBatchHelper
{ {
private DispatcherTimer? timer; private DispatcherTimer? timer;
@@ -1,127 +1,124 @@
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Gma.QrCodeNet.Encoding; namespace Gma.QrCodeNet.Encoding;
internal sealed class BitList : IEnumerable<bool> internal sealed class BitList : IEnumerable<bool>
{ {
internal BitList() internal BitList()
{ {
Count = 0; Count = 0;
List = new List<byte>(32); List = new List<byte>(32);
} }
internal BitList(IEnumerable<byte> byteArray) internal BitList(IEnumerable<byte> byteArray)
{ {
Count = byteArray.Count(); Count = byteArray.Count();
List = byteArray.ToList(); List = byteArray.ToList();
} }
internal List<byte> List { get; } internal List<byte> List { get; }
internal int Count { get; private set; } internal int Count { get; private set; }
internal bool this[int index] internal bool this[int index]
{ {
get get
{ {
if (index < 0 || index >= Count) if (index < 0 || index >= Count)
{ {
throw new ArgumentOutOfRangeException(nameof(index), "Index out of range"); throw new ArgumentOutOfRangeException(nameof(index), "Index out of range");
} }
int value_Renamed = List[index >> 3] & 0xff; int value_Renamed = List[index >> 3] & 0xff;
return ((value_Renamed >> (7 - (index & 0x7))) & 1) == 1; return ((value_Renamed >> (7 - (index & 0x7))) & 1) == 1;
} }
} }
public IEnumerator<bool> GetEnumerator() public IEnumerator<bool> GetEnumerator()
{ {
int numBytes = Count >> 3; int numBytes = Count >> 3;
int remainder = Count & 0x7; int remainder = Count & 0x7;
byte value; byte value;
for (int index = 0; index < numBytes; index++) for (int index = 0; index < numBytes; index++)
{ {
value = List[index]; value = List[index];
for (int shiftNum = 7; shiftNum >= 0; shiftNum--) for (int shiftNum = 7; shiftNum >= 0; shiftNum--)
{ {
yield return ((value >> shiftNum) & 1) == 1; yield return ((value >> shiftNum) & 1) == 1;
} }
} }
if (remainder > 0) if (remainder > 0)
{ {
value = List[numBytes]; value = List[numBytes];
for (int index = 0; index < remainder; index++) for (int index = 0; index < remainder; index++)
{ {
yield return ((value >> (7 - index)) & 1) == 1; yield return ((value >> (7 - index)) & 1) == 1;
} }
} }
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
return GetEnumerator(); return GetEnumerator();
} }
private int ToBit(bool item) private int ToBit(bool item)
{ {
return item ? 1 : 0; return item ? 1 : 0;
} }
internal void Add(bool item) internal void Add(bool item)
{ {
int numBitsinLastByte = Count & 0x7; int numBitsinLastByte = Count & 0x7;
// Add one more byte to List when we have no bits in the last byte. // Add one more byte to List when we have no bits in the last byte.
if (numBitsinLastByte == 0) if (numBitsinLastByte == 0)
{ {
List.Add(0); List.Add(0);
} }
List[Count >> 3] |= (byte)(ToBit(item) << (7 - numBitsinLastByte)); List[Count >> 3] |= (byte)(ToBit(item) << (7 - numBitsinLastByte));
Count++; Count++;
} }
internal void Add(IEnumerable<bool> items) internal void Add(IEnumerable<bool> items)
{ {
foreach (bool item in items) foreach (bool item in items)
{ {
Add(item); Add(item);
} }
} }
internal void Add(int value, int bitCount) internal void Add(int value, int bitCount)
{ {
if (bitCount is < 0 or > 32) if (bitCount is < 0 or > 32)
{ {
throw new ArgumentOutOfRangeException(nameof(bitCount), $"{nameof(bitCount)} must be greater than or equal to 0"); throw new ArgumentOutOfRangeException(nameof(bitCount), $"{nameof(bitCount)} must be greater than or equal to 0");
} }
int numBitsLeft = bitCount; int numBitsLeft = bitCount;
while (numBitsLeft > 0) while (numBitsLeft > 0)
{ {
if ((Count & 0x7) == 0 && numBitsLeft >= 8) if ((Count & 0x7) == 0 && numBitsLeft >= 8)
{ {
// Add one more byte to List. // Add one more byte to List.
byte newByte = (byte)((value >> (numBitsLeft - 8)) & 0xFF); byte newByte = (byte)((value >> (numBitsLeft - 8)) & 0xFF);
AppendByte(newByte); AppendByte(newByte);
numBitsLeft -= 8; numBitsLeft -= 8;
} }
else else
{ {
bool bit = ((value >> (numBitsLeft - 1)) & 1) == 1; bool bit = ((value >> (numBitsLeft - 1)) & 1) == 1;
Add(bit); Add(bit);
numBitsLeft--; numBitsLeft--;
} }
} }
} }
private void AppendByte(byte item) private void AppendByte(byte item)
{ {
List.Add(item); List.Add(item);
Count += 8; Count += 8;
} }
} }
@@ -2,23 +2,23 @@ namespace Gma.QrCodeNet.Encoding;
public abstract class BitMatrix public abstract class BitMatrix
{ {
public abstract int Width { get; } public abstract int Width { get; }
public abstract int Height { get; } public abstract int Height { get; }
public abstract bool[,] InternalArray { get; } public abstract bool[,] InternalArray { get; }
public abstract bool this[int i, int j] { get; set; } public abstract bool this[int i, int j] { get; set; }
internal void CopyTo(TriStateMatrix target, MatrixRectangle sourceArea, MatrixPoint targetPoint, MatrixStatus mstatus) internal void CopyTo(TriStateMatrix target, MatrixRectangle sourceArea, MatrixPoint targetPoint, MatrixStatus mstatus)
{ {
for (int j = 0; j < sourceArea.Size.Height; j++) for (int j = 0; j < sourceArea.Size.Height; j++)
{ {
for (int i = 0; i < sourceArea.Size.Width; i++) for (int i = 0; i < sourceArea.Size.Width; i++)
{ {
bool value = this[sourceArea.Location.X + i, sourceArea.Location.Y + j]; bool value = this[sourceArea.Location.X + i, sourceArea.Location.Y + j];
target[targetPoint.X + i, targetPoint.Y + j, mstatus] = value; target[targetPoint.X + i, targetPoint.Y + j, mstatus] = value;
} }
} }
} }
internal void CopyTo(TriStateMatrix target, MatrixPoint targetPoint, MatrixStatus mstatus) => CopyTo(target, new MatrixRectangle(new MatrixPoint(0, 0), new MatrixSize(Width, Height)), targetPoint, mstatus); internal void CopyTo(TriStateMatrix target, MatrixPoint targetPoint, MatrixStatus mstatus) => CopyTo(target, new MatrixRectangle(new MatrixPoint(0, 0), new MatrixSize(Width, Height)), targetPoint, mstatus);
} }
@@ -2,30 +2,30 @@ namespace Gma.QrCodeNet.Encoding;
public abstract class BitMatrixBase : BitMatrix public abstract class BitMatrixBase : BitMatrix
{ {
protected BitMatrixBase(int width, bool[,] internalArray) protected BitMatrixBase(int width, bool[,] internalArray)
{ {
Width = width; Width = width;
InternalArray = internalArray; InternalArray = internalArray;
} }
protected BitMatrixBase(bool[,] internalArray) protected BitMatrixBase(bool[,] internalArray)
{ {
InternalArray = internalArray; InternalArray = internalArray;
int width = internalArray.GetLength(0); int width = internalArray.GetLength(0);
Width = width; Width = width;
} }
public override bool[,] InternalArray { get; } public override bool[,] InternalArray { get; }
public override int Width { get; } public override int Width { get; }
public static bool CanCreate(bool[,] internalArray) public static bool CanCreate(bool[,] internalArray)
{ {
if (internalArray is null) if (internalArray is null)
{ {
return false; return false;
} }
return internalArray.GetLength(0) == internalArray.GetLength(1); return internalArray.GetLength(0) == internalArray.GetLength(1);
} }
} }
@@ -1,48 +1,46 @@
using System;
namespace Gma.QrCodeNet.Encoding.DataEncodation; namespace Gma.QrCodeNet.Encoding.DataEncodation;
public static class CharCountIndicatorTable public static class CharCountIndicatorTable
{ {
/// <remarks>ISO/IEC 18004:2000 Table 3 Page 18</remarks> /// <remarks>ISO/IEC 18004:2000 Table 3 Page 18</remarks>
public static int[] GetCharCountIndicatorSet() public static int[] GetCharCountIndicatorSet()
{ {
return new int[] { 8, 16, 16 }; return new int[] { 8, 16, 16 };
} }
public static int GetBitCountInCharCountIndicator(int version) public static int GetBitCountInCharCountIndicator(int version)
{ {
int[] charCountIndicatorSet = GetCharCountIndicatorSet(); int[] charCountIndicatorSet = GetCharCountIndicatorSet();
int versionGroup = GetVersionGroup(version); int versionGroup = GetVersionGroup(version);
return charCountIndicatorSet[versionGroup]; return charCountIndicatorSet[versionGroup];
} }
/// <summary> /// <summary>
/// Used to define length of the Character Count Indicator <see cref="GetBitCountInCharCountIndicator"/> /// Used to define length of the Character Count Indicator <see cref="GetBitCountInCharCountIndicator"/>
/// </summary> /// </summary>
/// <returns>Returns the 0 based index of the row from Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator. </returns> /// <returns>Returns the 0 based index of the row from Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator. </returns>
private static int GetVersionGroup(int version) private static int GetVersionGroup(int version)
{ {
if (version > 40) if (version > 40)
{ {
throw new InvalidOperationException($"Unexpected version: {version}."); throw new InvalidOperationException($"Unexpected version: {version}.");
} }
else if (version >= 27) else if (version >= 27)
{ {
return 2; return 2;
} }
else if (version >= 10) else if (version >= 10)
{ {
return 1; return 1;
} }
else if (version > 0) else if (version > 0)
{ {
return 0; return 0;
} }
else else
{ {
throw new InvalidOperationException($"Unexpected version: {version}."); throw new InvalidOperationException($"Unexpected version: {version}.");
} }
} }
} }
@@ -1,4 +1,3 @@
using System;
using Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition; using Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition;
using Gma.QrCodeNet.Encoding.Terminate; using Gma.QrCodeNet.Encoding.Terminate;
using Gma.QrCodeNet.Encoding.Versions; using Gma.QrCodeNet.Encoding.Versions;
@@ -10,53 +9,53 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation;
/// Which uses sub functions under several different namespaces</remarks> /// Which uses sub functions under several different namespaces</remarks>
internal static class DataEncode internal static class DataEncode
{ {
internal static EncodationStruct Encode(string content, ErrorCorrectionLevel ecLevel) internal static EncodationStruct Encode(string content, ErrorCorrectionLevel ecLevel)
{ {
RecognitionStruct recognitionResult = InputRecognise.Recognise(content); RecognitionStruct recognitionResult = InputRecognise.Recognise(content);
EncoderBase encoderBase = CreateEncoder(recognitionResult.EncodingName); EncoderBase encoderBase = CreateEncoder(recognitionResult.EncodingName);
BitList encodeContent = encoderBase.GetDataBits(content); BitList encodeContent = encoderBase.GetDataBits(content);
int encodeContentLength = encodeContent.Count; int encodeContentLength = encodeContent.Count;
VersionControlStruct vcStruct = VersionControlStruct vcStruct =
VersionControl.InitialSetup(encodeContentLength, ecLevel, recognitionResult.EncodingName); VersionControl.InitialSetup(encodeContentLength, ecLevel, recognitionResult.EncodingName);
BitList dataCodewords = new(); BitList dataCodewords = new();
// Eci header // Eci header
if (vcStruct.IsContainECI && vcStruct.ECIHeader is { }) if (vcStruct.IsContainECI && vcStruct.ECIHeader is { })
{ {
dataCodewords.Add(vcStruct.ECIHeader); dataCodewords.Add(vcStruct.ECIHeader);
} }
// Header // Header
dataCodewords.Add(encoderBase.GetModeIndicator()); dataCodewords.Add(encoderBase.GetModeIndicator());
int numLetter = encodeContentLength >> 3; int numLetter = encodeContentLength >> 3;
dataCodewords.Add(encoderBase.GetCharCountIndicator(numLetter, vcStruct.VersionDetail.Version)); dataCodewords.Add(encoderBase.GetCharCountIndicator(numLetter, vcStruct.VersionDetail.Version));
// Data // Data
dataCodewords.Add(encodeContent); dataCodewords.Add(encodeContent);
// Terminator Padding // Terminator Padding
dataCodewords.TerminateBites(dataCodewords.Count, vcStruct.VersionDetail.NumDataBytes); dataCodewords.TerminateBites(dataCodewords.Count, vcStruct.VersionDetail.NumDataBytes);
int dataCodewordsCount = dataCodewords.Count; int dataCodewordsCount = dataCodewords.Count;
if ((dataCodewordsCount & 0x7) != 0) if ((dataCodewordsCount & 0x7) != 0)
{ {
throw new ArgumentException($"{nameof(dataCodewords)} is not byte sized."); throw new ArgumentException($"{nameof(dataCodewords)} is not byte sized.");
} }
else if (dataCodewordsCount >> 3 != vcStruct.VersionDetail.NumDataBytes) else if (dataCodewordsCount >> 3 != vcStruct.VersionDetail.NumDataBytes)
{ {
throw new ArgumentException($"{nameof(dataCodewords)} num of bytes not equal to {nameof(vcStruct.VersionDetail.NumDataBytes)} for current version"); throw new ArgumentException($"{nameof(dataCodewords)} num of bytes not equal to {nameof(vcStruct.VersionDetail.NumDataBytes)} for current version");
} }
var encStruct = new EncodationStruct(vcStruct, dataCodewords); var encStruct = new EncodationStruct(vcStruct, dataCodewords);
return encStruct; return encStruct;
} }
private static EncoderBase CreateEncoder(string encodingName) private static EncoderBase CreateEncoder(string encodingName)
{ {
return new EightBitByteEncoder(encodingName); return new EightBitByteEncoder(encodingName);
} }
} }
@@ -1,255 +1,252 @@
using System;
using System.Collections.Generic;
namespace Gma.QrCodeNet.Encoding.DataEncodation; namespace Gma.QrCodeNet.Encoding.DataEncodation;
public sealed class ECISet public sealed class ECISet
{ {
/// <summary> /// <summary>
/// ISO/IEC 18004:2006 Chapter 6.4.2 Mode indicator = 0111 Page 23 /// ISO/IEC 18004:2006 Chapter 6.4.2 Mode indicator = 0111 Page 23
/// </summary> /// </summary>
private const int ECIMode = 7; private const int ECIMode = 7;
private const int ECIIndicatorNumBits = 4; private const int ECIIndicatorNumBits = 4;
private Dictionary<string, int>? _nameToValue; private Dictionary<string, int>? _nameToValue;
private Dictionary<int, string>? _valueToName; private Dictionary<int, string>? _valueToName;
/// <summary> /// <summary>
/// Initialize ECI Set. /// Initialize ECI Set.
/// </summary> /// </summary>
/// <param name="option">AppendOption is enum under ECISet /// <param name="option">AppendOption is enum under ECISet
/// Use NameToValue during Encode. ValueToName during Decode</param> /// Use NameToValue during Encode. ValueToName during Decode</param>
internal ECISet(AppendOption option) internal ECISet(AppendOption option)
{ {
Initialize(option); Initialize(option);
} }
public enum AppendOption public enum AppendOption
{ {
NameToValue, NameToValue,
ValueToName, ValueToName,
Both Both
} }
/// <summary> /// <summary>
/// Length indicator for number of ECI codewords /// Length indicator for number of ECI codewords
/// </summary> /// </summary>
/// <remarks>ISO/IEC 18004:2006 Chapter 6.4.2 Page 24. /// <remarks>ISO/IEC 18004:2006 Chapter 6.4.2 Page 24.
/// 1 codeword length = 0. Any additional codeword add 1 to front. Eg: 3 = 110</remarks> /// 1 codeword length = 0. Any additional codeword add 1 to front. Eg: 3 = 110</remarks>
/// <description>Bits required for each one is: /// <description>Bits required for each one is:
/// one = 1, two = 2, three = 3</description> /// one = 1, two = 2, three = 3</description>
private enum ECICodewordsLength private enum ECICodewordsLength
{ {
One = 0, One = 0,
Two = 2, Two = 2,
Three = 6 Three = 6
} }
/// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks> /// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks>
/// <param name="eCIValue">Range: 0 ~ 999999</param> /// <param name="eCIValue">Range: 0 ~ 999999</param>
/// <returns>Number of Codewords(Byte) for ECI Assignment Value</returns> /// <returns>Number of Codewords(Byte) for ECI Assignment Value</returns>
private static int NumOfCodewords(int eCIValue) private static int NumOfCodewords(int eCIValue)
{ {
if (eCIValue is >= 0 and <= 127) if (eCIValue is >= 0 and <= 127)
{ {
return 1; return 1;
} }
else if (eCIValue is > 127 and <= 16383) else if (eCIValue is > 127 and <= 16383)
{ {
return 2; return 2;
} }
else if (eCIValue is > 16383 and <= 999999) else if (eCIValue is > 16383 and <= 999999)
{ {
return 3; return 3;
} }
else else
{ {
throw new ArgumentOutOfRangeException($"{nameof(eCIValue)} should be in range: 0 to 999999."); throw new ArgumentOutOfRangeException($"{nameof(eCIValue)} should be in range: 0 to 999999.");
} }
} }
/// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks> /// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks>
/// <param name="eCIValue">Range: 0 ~ 999999</param> /// <param name="eCIValue">Range: 0 ~ 999999</param>
/// <returns>Number of bits for ECI Assignment Value</returns> /// <returns>Number of bits for ECI Assignment Value</returns>
private static int NumOfAssignmentBits(int eCIValue) => NumOfCodewords(eCIValue) * 8; private static int NumOfAssignmentBits(int eCIValue) => NumOfCodewords(eCIValue) * 8;
private void AppendECI(string name, int value, AppendOption option) private void AppendECI(string name, int value, AppendOption option)
{ {
switch (option) switch (option)
{ {
case AppendOption.NameToValue: case AppendOption.NameToValue:
_nameToValue?.Add(name, value); _nameToValue?.Add(name, value);
break; break;
case AppendOption.ValueToName: case AppendOption.ValueToName:
_valueToName?.Add(value, name); _valueToName?.Add(value, name);
break; break;
case AppendOption.Both: case AppendOption.Both:
_nameToValue?.Add(name, value); _nameToValue?.Add(name, value);
_valueToName?.Add(value, name); _valueToName?.Add(value, name);
break; break;
default: default:
throw new InvalidOperationException($"There is no such {nameof(AppendOption)}."); throw new InvalidOperationException($"There is no such {nameof(AppendOption)}.");
} }
} }
private void Initialize(AppendOption option) private void Initialize(AppendOption option)
{ {
switch (option) switch (option)
{ {
case AppendOption.NameToValue: case AppendOption.NameToValue:
_nameToValue = new Dictionary<string, int>(); _nameToValue = new Dictionary<string, int>();
break; break;
case AppendOption.ValueToName: case AppendOption.ValueToName:
_valueToName = new Dictionary<int, string>(); _valueToName = new Dictionary<int, string>();
break; break;
case AppendOption.Both: case AppendOption.Both:
_nameToValue = new Dictionary<string, int>(); _nameToValue = new Dictionary<string, int>();
_valueToName = new Dictionary<int, string>(); _valueToName = new Dictionary<int, string>();
break; break;
default: default:
throw new InvalidOperationException($"There is no such {nameof(AppendOption)}."); throw new InvalidOperationException($"There is no such {nameof(AppendOption)}.");
} }
// ECI table. Source 01 URL: http://strokescribe.com/en/ECI.html // ECI table. Source 01 URL: http://strokescribe.com/en/ECI.html
// ECI table. Source 02 URL: http://lab.must.or.kr/Extended-Channel-Interpretations-ECI-Encoding.ashx // ECI table. Source 02 URL: http://lab.must.or.kr/Extended-Channel-Interpretations-ECI-Encoding.ashx
// ToDo. Fill up remaining missing table. // ToDo. Fill up remaining missing table.
AppendECI("iso-8859-1", 1, option); AppendECI("iso-8859-1", 1, option);
AppendECI("IBM437", 2, option); AppendECI("IBM437", 2, option);
// AppendECI("iso-8859-1", 3, option); //ECI value 1 is default encoding. // AppendECI("iso-8859-1", 3, option); //ECI value 1 is default encoding.
AppendECI("iso-8859-2", 4, option); AppendECI("iso-8859-2", 4, option);
AppendECI("iso-8859-3", 5, option); AppendECI("iso-8859-3", 5, option);
AppendECI("iso-8859-4", 6, option); AppendECI("iso-8859-4", 6, option);
AppendECI("iso-8859-5", 7, option); AppendECI("iso-8859-5", 7, option);
AppendECI("iso-8859-6", 8, option); AppendECI("iso-8859-6", 8, option);
AppendECI("iso-8859-7", 9, option); AppendECI("iso-8859-7", 9, option);
AppendECI("iso-8859-8", 10, option); AppendECI("iso-8859-8", 10, option);
AppendECI("iso-8859-9", 11, option); AppendECI("iso-8859-9", 11, option);
AppendECI("windows-874", 13, option); AppendECI("windows-874", 13, option);
AppendECI("iso-8859-13", 15, option); AppendECI("iso-8859-13", 15, option);
AppendECI("iso-8859-15", 17, option); AppendECI("iso-8859-15", 17, option);
AppendECI("shift_jis", 20, option); AppendECI("shift_jis", 20, option);
AppendECI("utf-8", 26, option); AppendECI("utf-8", 26, option);
} }
/// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks> /// <remarks>ISO/IEC 18004:2006E ECI Designator Page 24</remarks>
/// <param name="eCIValue">Range: 0 ~ 999999</param> /// <param name="eCIValue">Range: 0 ~ 999999</param>
/// <returns>Number of bits for ECI Header</returns> /// <returns>Number of bits for ECI Header</returns>
internal static int NumOfECIHeaderBits(int eCIValue) => NumOfAssignmentBits(eCIValue) + 4; internal static int NumOfECIHeaderBits(int eCIValue) => NumOfAssignmentBits(eCIValue) + 4;
internal int GetECIValueByName(string encodingName) internal int GetECIValueByName(string encodingName)
{ {
if (_nameToValue is null) if (_nameToValue is null)
{ {
Initialize(AppendOption.NameToValue); Initialize(AppendOption.NameToValue);
} }
if (_nameToValue!.TryGetValue(encodingName, out int eCIValue)) if (_nameToValue!.TryGetValue(encodingName, out int eCIValue))
{ {
return eCIValue; return eCIValue;
} }
else else
{ {
throw new ArgumentOutOfRangeException($"ECI does not contain encoding: {encodingName}."); throw new ArgumentOutOfRangeException($"ECI does not contain encoding: {encodingName}.");
} }
} }
internal string GetECINameByValue(int eCIValue) internal string GetECINameByValue(int eCIValue)
{ {
if (_valueToName is null) if (_valueToName is null)
{ {
Initialize(AppendOption.ValueToName); Initialize(AppendOption.ValueToName);
} }
if (_valueToName!.TryGetValue(eCIValue, out var eCIName)) if (_valueToName!.TryGetValue(eCIValue, out var eCIName))
{ {
return eCIName; return eCIName;
} }
else else
{ {
throw new ArgumentOutOfRangeException($"ECI does not contain value: {eCIValue}."); throw new ArgumentOutOfRangeException($"ECI does not contain value: {eCIValue}.");
} }
} }
/// <returns>ECI table in Dictionary collection</returns> /// <returns>ECI table in Dictionary collection</returns>
public Dictionary<string, int>? GetECITable() public Dictionary<string, int>? GetECITable()
{ {
if (_nameToValue is null) if (_nameToValue is null)
{ {
Initialize(AppendOption.NameToValue); Initialize(AppendOption.NameToValue);
} }
return _nameToValue; return _nameToValue;
} }
public bool ContainsECIName(string encodingName) public bool ContainsECIName(string encodingName)
{ {
if (_nameToValue is null) if (_nameToValue is null)
{ {
Initialize(AppendOption.NameToValue); Initialize(AppendOption.NameToValue);
} }
return _nameToValue!.ContainsKey(encodingName); return _nameToValue!.ContainsKey(encodingName);
} }
public bool ContainsECIValue(int eciValue) public bool ContainsECIValue(int eciValue)
{ {
if (_valueToName is null) if (_valueToName is null)
{ {
Initialize(AppendOption.ValueToName); Initialize(AppendOption.ValueToName);
} }
return _valueToName!.ContainsKey(eciValue); return _valueToName!.ContainsKey(eciValue);
} }
/// <remarks>ISO/IEC 18004:2006 Chapter 6.4.2 Page 24.</remarks> /// <remarks>ISO/IEC 18004:2006 Chapter 6.4.2 Page 24.</remarks>
internal BitList GetECIHeader(string encodingName) internal BitList GetECIHeader(string encodingName)
{ {
int eciValue = GetECIValueByName(encodingName); int eciValue = GetECIValueByName(encodingName);
BitList dataBits = new() BitList dataBits = new()
{ {
{ ECIMode, ECIIndicatorNumBits } { ECIMode, ECIIndicatorNumBits }
}; };
int eciAssignmentByte = NumOfCodewords(eciValue); int eciAssignmentByte = NumOfCodewords(eciValue);
// Number of bits = Num codewords indicator + codeword value = Number of codewords * 8 // Number of bits = Num codewords indicator + codeword value = Number of codewords * 8
// Chapter 6.4.2.1 ECI Designator ISOIEC 18004:2006 Page 24 // Chapter 6.4.2.1 ECI Designator ISOIEC 18004:2006 Page 24
int eciAssignmentBits; int eciAssignmentBits;
switch (eciAssignmentByte) switch (eciAssignmentByte)
{ {
case 1: case 1:
// Indicator = 0. Page 24. Chapter 6.4.2.1 // Indicator = 0. Page 24. Chapter 6.4.2.1
dataBits.Add((int)ECICodewordsLength.One, 1); dataBits.Add((int)ECICodewordsLength.One, 1);
eciAssignmentBits = (eciAssignmentByte * 8) - 1; eciAssignmentBits = (eciAssignmentByte * 8) - 1;
break; break;
case 2: case 2:
// Indicator = 10. Page 24. Chapter 6.4.2.1 // Indicator = 10. Page 24. Chapter 6.4.2.1
dataBits.Add((int)ECICodewordsLength.Two, 2); dataBits.Add((int)ECICodewordsLength.Two, 2);
eciAssignmentBits = (eciAssignmentByte * 8) - 2; eciAssignmentBits = (eciAssignmentByte * 8) - 2;
break; break;
case 3: case 3:
// Indicator = 110. Page 24. Chapter 6.4.2.1 // Indicator = 110. Page 24. Chapter 6.4.2.1
dataBits.Add((int)ECICodewordsLength.Three, 3); dataBits.Add((int)ECICodewordsLength.Three, 3);
eciAssignmentBits = (eciAssignmentByte * 8) - 3; eciAssignmentBits = (eciAssignmentByte * 8) - 3;
break; break;
default: default:
throw new InvalidOperationException("Assignment Codewords should be either 1, 2 or 3."); throw new InvalidOperationException("Assignment Codewords should be either 1, 2 or 3.");
} }
dataBits.Add(eciValue, eciAssignmentBits); dataBits.Add(eciValue, eciAssignmentBits);
return dataBits; return dataBits;
} }
} }
@@ -1,5 +1,3 @@
using System;
namespace Gma.QrCodeNet.Encoding.DataEncodation; namespace Gma.QrCodeNet.Encoding.DataEncodation;
/// <summary> /// <summary>
@@ -14,67 +12,67 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation;
/// <remarks>ISO/IEC 18004:2000 Chapter 8.4.4 Page 22</remarks> /// <remarks>ISO/IEC 18004:2000 Chapter 8.4.4 Page 22</remarks>
internal class EightBitByteEncoder : EncoderBase internal class EightBitByteEncoder : EncoderBase
{ {
private const string DefaultEncoding = QRCodeConstantVariable.DefaultEncoding; private const string DefaultEncoding = QRCodeConstantVariable.DefaultEncoding;
/// <summary> /// <summary>
/// Bitcount, Chapter 8.4.4, P.24 /// Bitcount, Chapter 8.4.4, P.24
/// </summary> /// </summary>
private const int EightBitByteBitcount = 8; private const int EightBitByteBitcount = 8;
/// <summary> /// <summary>
/// EightBitByte encoder's encoding will change according to different region /// EightBitByte encoder's encoding will change according to different region
/// </summary> /// </summary>
/// <param name="encoding">Default encoding is "iso-8859-1"</param> /// <param name="encoding">Default encoding is "iso-8859-1"</param>
internal EightBitByteEncoder(string encoding) : base() internal EightBitByteEncoder(string encoding) : base()
{ {
Encoding = encoding ?? DefaultEncoding; Encoding = encoding ?? DefaultEncoding;
} }
internal EightBitByteEncoder() : base() internal EightBitByteEncoder() : base()
{ {
Encoding = DefaultEncoding; Encoding = DefaultEncoding;
} }
internal string Encoding { get; private set; } internal string Encoding { get; private set; }
protected byte[] EncodeContent(string content, string encoding) => System.Text.Encoding.GetEncoding(encoding).GetBytes(content); protected byte[] EncodeContent(string content, string encoding) => System.Text.Encoding.GetEncoding(encoding).GetBytes(content);
internal override BitList GetDataBits(string content) internal override BitList GetDataBits(string content)
{ {
var eciSet = new ECISet(ECISet.AppendOption.NameToValue); var eciSet = new ECISet(ECISet.AppendOption.NameToValue);
if (!eciSet.ContainsECIName(Encoding)) if (!eciSet.ContainsECIName(Encoding))
{ {
throw new ArgumentOutOfRangeException( throw new ArgumentOutOfRangeException(
nameof(Encoding), nameof(Encoding),
$"Current ECI table does not support this encoding. Please check {nameof(ECISet)} class for more info."); $"Current ECI table does not support this encoding. Please check {nameof(ECISet)} class for more info.");
} }
byte[] contentBytes = EncodeContent(content, Encoding); byte[] contentBytes = EncodeContent(content, Encoding);
return GetDataBitsByByteArray(contentBytes, Encoding); return GetDataBitsByByteArray(contentBytes, Encoding);
} }
internal BitList GetDataBitsByByteArray(byte[] encodeContent, string encodingName) internal BitList GetDataBitsByByteArray(byte[] encodeContent, string encodingName)
{ {
var dataBits = new BitList(); var dataBits = new BitList();
// Current plan for UTF8 support is put Byte order Mark in front of content byte. // Current plan for UTF8 support is put Byte order Mark in front of content byte.
// Also include ECI header before encoding header. Which will be add with encoding header. // Also include ECI header before encoding header. Which will be add with encoding header.
if (encodingName == "utf-8") if (encodingName == "utf-8")
{ {
byte[] utf8BOM = QRCodeConstantVariable.UTF8ByteOrderMark; byte[] utf8BOM = QRCodeConstantVariable.UTF8ByteOrderMark;
for (int index = 0; index < utf8BOM.Length; index++) for (int index = 0; index < utf8BOM.Length; index++)
{ {
dataBits.Add(utf8BOM[index], EightBitByteBitcount); dataBits.Add(utf8BOM[index], EightBitByteBitcount);
} }
} }
for (int index = 0; index < encodeContent.Length; index++) for (int index = 0; index < encodeContent.Length; index++)
{ {
dataBits.Add(encodeContent[index], EightBitByteBitcount); dataBits.Add(encodeContent[index], EightBitByteBitcount);
} }
return dataBits; return dataBits;
} }
protected override int GetBitCountInCharCountIndicator(int version) => CharCountIndicatorTable.GetBitCountInCharCountIndicator(version); protected override int GetBitCountInCharCountIndicator(int version) => CharCountIndicatorTable.GetBitCountInCharCountIndicator(version);
} }
@@ -4,12 +4,12 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation;
internal struct EncodationStruct internal struct EncodationStruct
{ {
internal EncodationStruct(VersionControlStruct vcStruct, BitList dataCodewords) internal EncodationStruct(VersionControlStruct vcStruct, BitList dataCodewords)
{ {
VersionDetail = vcStruct.VersionDetail; VersionDetail = vcStruct.VersionDetail;
DataCodewords = dataCodewords; DataCodewords = dataCodewords;
} }
internal VersionDetail VersionDetail { get; set; } internal VersionDetail VersionDetail { get; set; }
internal BitList DataCodewords { get; set; } internal BitList DataCodewords { get; set; }
} }
@@ -2,45 +2,45 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation;
public abstract class EncoderBase public abstract class EncoderBase
{ {
internal EncoderBase() internal EncoderBase()
{ {
} }
protected virtual int GetDataLength(string content) => content.Length; protected virtual int GetDataLength(string content) => content.Length;
/// <summary> /// <summary>
/// Returns the bit representation of input data. /// Returns the bit representation of input data.
/// </summary> /// </summary>
internal abstract BitList GetDataBits(string content); internal abstract BitList GetDataBits(string content);
/// <summary> /// <summary>
/// Returns bit representation of Modevalue. /// Returns bit representation of Modevalue.
/// </summary> /// </summary>
/// <remarks>See Chapter 8.4 Data encodation, Table 2 — Mode indicators</remarks> /// <remarks>See Chapter 8.4 Data encodation, Table 2 — Mode indicators</remarks>
internal BitList GetModeIndicator() internal BitList GetModeIndicator()
{ {
BitList modeIndicatorBits = new() BitList modeIndicatorBits = new()
{ {
{ 0001 << 2, 4 } { 0001 << 2, 4 }
}; };
return modeIndicatorBits; return modeIndicatorBits;
} }
internal BitList GetCharCountIndicator(int characterCount, int version) internal BitList GetCharCountIndicator(int characterCount, int version)
{ {
BitList characterCountBits = new(); BitList characterCountBits = new();
int bitCount = GetBitCountInCharCountIndicator(version); int bitCount = GetBitCountInCharCountIndicator(version);
characterCountBits.Add(characterCount, bitCount); characterCountBits.Add(characterCount, bitCount);
return characterCountBits; return characterCountBits;
} }
/// <summary> /// <summary>
/// Defines the length of the Character Count Indicator, /// Defines the length of the Character Count Indicator,
/// which varies according to the mode and the symbol version in use /// which varies according to the mode and the symbol version in use
/// </summary> /// </summary>
/// <returns>Number of bits in Character Count Indicator.</returns> /// <returns>Number of bits in Character Count Indicator.</returns>
/// <remarks> /// <remarks>
/// See Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator. /// See Chapter 8.4 Data encodation, Table 3 — Number of bits in Character Count Indicator.
/// </remarks> /// </remarks>
protected abstract int GetBitCountInCharCountIndicator(int version); protected abstract int GetBitCountInCharCountIndicator(int version);
} }
@@ -1,57 +1,54 @@
using System;
using System.Collections.Generic;
namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition; namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition;
public static class InputRecognise public static class InputRecognise
{ {
public static RecognitionStruct Recognise(string content) public static RecognitionStruct Recognise(string content)
{ {
string encodingName = EightBitByteRecognision(content, 0, content.Length); string encodingName = EightBitByteRecognision(content, 0, content.Length);
return new RecognitionStruct(encodingName); return new RecognitionStruct(encodingName);
} }
private static string EightBitByteRecognision(string content, int startPos, int contentLength) private static string EightBitByteRecognision(string content, int startPos, int contentLength)
{ {
if(string.IsNullOrEmpty(content)) if (string.IsNullOrEmpty(content))
throw new ArgumentNullException(nameof(content)); throw new ArgumentNullException(nameof(content));
var eciSets = new ECISet(ECISet.AppendOption.NameToValue); var eciSets = new ECISet(ECISet.AppendOption.NameToValue);
Dictionary<string, int>? eciSet = eciSets.GetECITable(); Dictionary<string, int>? eciSet = eciSets.GetECITable();
if(eciSet == null) if (eciSet == null)
return string.Empty; return string.Empty;
// we will not check for utf8 encoding. // we will not check for utf8 encoding.
eciSet.Remove(QRCodeConstantVariable.UTF8Encoding); eciSet.Remove(QRCodeConstantVariable.UTF8Encoding);
eciSet.Remove(QRCodeConstantVariable.DefaultEncoding); eciSet.Remove(QRCodeConstantVariable.DefaultEncoding);
int scanPos = startPos; int scanPos = startPos;
// default encoding as priority // default encoding as priority
scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, QRCodeConstantVariable.DefaultEncoding, scanPos, contentLength); scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, QRCodeConstantVariable.DefaultEncoding, scanPos, contentLength);
if (scanPos == -1) if (scanPos == -1)
{ {
return QRCodeConstantVariable.DefaultEncoding; return QRCodeConstantVariable.DefaultEncoding;
} }
foreach (KeyValuePair<string, int> kvp in eciSet) foreach (KeyValuePair<string, int> kvp in eciSet)
{ {
scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, kvp.Key, scanPos, contentLength); scanPos = ModeEncodeCheck.TryEncodeEightBitByte(content, kvp.Key, scanPos, contentLength);
if (scanPos == -1) if (scanPos == -1)
{ {
return kvp.Key; return kvp.Key;
} }
} }
if (scanPos == -1) if (scanPos == -1)
{ {
throw new ArgumentException("foreach Loop check give wrong result."); throw new ArgumentException("foreach Loop check give wrong result.");
} }
else else
{ {
return QRCodeConstantVariable.UTF8Encoding; return QRCodeConstantVariable.UTF8Encoding;
} }
} }
} }
@@ -1,72 +1,70 @@
using System;
namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition; namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition;
public static class ModeEncodeCheck public static class ModeEncodeCheck
{ {
/// <summary> /// <summary>
/// Encoding.GetEncoding.GetBytes will transform char to 0x3F if that char not belong to current encoding table. /// Encoding.GetEncoding.GetBytes will transform char to 0x3F if that char not belong to current encoding table.
/// 0x3F is '?' /// 0x3F is '?'
/// </summary> /// </summary>
private const int QuestionMarkChar = 0x3F; private const int QuestionMarkChar = 0x3F;
/// <summary> /// <summary>
/// Use given encoding to check input string from starting position. If encoding table is suitable solution. /// Use given encoding to check input string from starting position. If encoding table is suitable solution.
/// it will return -1. Else it will return failed encoding position. /// it will return -1. Else it will return failed encoding position.
/// </summary> /// </summary>
/// <param name="content">Input string</param> /// <param name="content">Input string</param>
/// <param name="encodingName">Encoding name. Check ECI table</param> /// <param name="encodingName">Encoding name. Check ECI table</param>
/// <returns>Returns -1 if from starting position to end encoding success. Else returns fail position</returns> /// <returns>Returns -1 if from starting position to end encoding success. Else returns fail position</returns>
internal static int TryEncodeEightBitByte(string content, string encodingName, int startingPosition, int contentLength) internal static int TryEncodeEightBitByte(string content, string encodingName, int startingPosition, int contentLength)
{ {
if (string.IsNullOrEmpty(content)) if (string.IsNullOrEmpty(content))
{ {
throw new IndexOutOfRangeException("Input cannot be null or empty."); throw new IndexOutOfRangeException("Input cannot be null or empty.");
} }
System.Text.Encoding encoding; System.Text.Encoding encoding;
try try
{ {
encoding = System.Text.Encoding.GetEncoding(encodingName); encoding = System.Text.Encoding.GetEncoding(encodingName);
} }
catch (ArgumentException) catch (ArgumentException)
{ {
return startingPosition; return startingPosition;
} }
char[] currentChar = new char[1]; char[] currentChar = new char[1];
byte[] bytes; byte[] bytes;
for (int index = startingPosition; index < contentLength; index++) for (int index = startingPosition; index < contentLength; index++)
{ {
currentChar[0] = content[index]; currentChar[0] = content[index];
bytes = encoding.GetBytes(currentChar); bytes = encoding.GetBytes(currentChar);
int length = bytes.Length; int length = bytes.Length;
if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar) if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar)
{ {
return index; return index;
} }
else if (length > 1) else if (length > 1)
{ {
return index; return index;
} }
} }
for (int index = 0; index < startingPosition; index++) for (int index = 0; index < startingPosition; index++)
{ {
currentChar[0] = content[index]; currentChar[0] = content[index];
bytes = encoding.GetBytes(currentChar); bytes = encoding.GetBytes(currentChar);
int length = bytes.Length; int length = bytes.Length;
if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar) if (currentChar[0] != '?' && length == 1 && bytes[0] == QuestionMarkChar)
{ {
return index; return index;
} }
else if (length > 1) else if (length > 1)
{ {
return index; return index;
} }
} }
return -1; return -1;
} }
} }
@@ -2,11 +2,11 @@ namespace Gma.QrCodeNet.Encoding.DataEncodation.InputRecognition;
public struct RecognitionStruct public struct RecognitionStruct
{ {
public RecognitionStruct(string encodingName) public RecognitionStruct(string encodingName)
: this() : this()
{ {
EncodingName = encodingName; EncodingName = encodingName;
} }
public string EncodingName { get; private set; } public string EncodingName { get; private set; }
} }
@@ -2,59 +2,59 @@ namespace Gma.QrCodeNet.Encoding.EncodingRegion;
internal static class BCHCalculator internal static class BCHCalculator
{ {
/// <summary> /// <summary>
/// Calculate int length by search for Most significant bit /// Calculate int length by search for Most significant bit
/// </summary> /// </summary>
/// <param name="num">Input Number</param> /// <param name="num">Input Number</param>
/// <returns>Most significant bit</returns> /// <returns>Most significant bit</returns>
internal static int PosMSB(int num) => num == 0 ? 0 : BinarySearchPos(num, 0, 32) + 1; internal static int PosMSB(int num) => num == 0 ? 0 : BinarySearchPos(num, 0, 32) + 1;
/// <summary> /// <summary>
/// Search for right side bit of Most significant bit /// Search for right side bit of Most significant bit
/// </summary> /// </summary>
/// <param name="num">Input number</param> /// <param name="num">Input number</param>
/// <param name="lowBoundary">Lower boundary. At start should be 0</param> /// <param name="lowBoundary">Lower boundary. At start should be 0</param>
/// <param name="highBoundary">Higher boundary. At start should be 32</param> /// <param name="highBoundary">Higher boundary. At start should be 32</param>
/// <returns>Most significant bit - 1</returns> /// <returns>Most significant bit - 1</returns>
private static int BinarySearchPos(int num, int lowBoundary, int highBoundary) private static int BinarySearchPos(int num, int lowBoundary, int highBoundary)
{ {
int mid = (lowBoundary + highBoundary) / 2; int mid = (lowBoundary + highBoundary) / 2;
int shiftResult = num >> mid; int shiftResult = num >> mid;
if (shiftResult == 1) if (shiftResult == 1)
{ {
return mid; return mid;
} }
else if (shiftResult < 1) else if (shiftResult < 1)
{ {
return BinarySearchPos(num, lowBoundary, mid); return BinarySearchPos(num, lowBoundary, mid);
} }
else else
{ {
return BinarySearchPos(num, mid, highBoundary); return BinarySearchPos(num, mid, highBoundary);
} }
} }
/// <summary> /// <summary>
/// With input number and polynomial number. Method will calculate BCH value and return /// With input number and polynomial number. Method will calculate BCH value and return
/// </summary> /// </summary>
/// <param name="num">Input number</param> /// <param name="num">Input number</param>
/// <param name="poly">Polynomial number</param> /// <param name="poly">Polynomial number</param>
/// <returns>BCH value</returns> /// <returns>BCH value</returns>
internal static int CalculateBCH(int num, int poly) internal static int CalculateBCH(int num, int poly)
{ {
int polyMSB = PosMSB(poly); int polyMSB = PosMSB(poly);
// num's length will be old length + new length - 1. // num's length will be old length + new length - 1.
// Once divide poly number. BCH number will be one length short than Poly number's length. // Once divide poly number. BCH number will be one length short than Poly number's length.
num <<= (polyMSB - 1); num <<= (polyMSB - 1);
int numMSB = PosMSB(num); int numMSB = PosMSB(num);
while (PosMSB(num) >= polyMSB) while (PosMSB(num) >= polyMSB)
{ {
// left shift Poly number to same level as num. Then xor. // left shift Poly number to same level as num. Then xor.
// Remove most significant bits of num. // Remove most significant bits of num.
num ^= poly << (numMSB - polyMSB); num ^= poly << (numMSB - polyMSB);
numMSB = PosMSB(num); numMSB = PosMSB(num);
} }
return num; return num;
} }
} }
@@ -1,72 +1,70 @@
using System;
namespace Gma.QrCodeNet.Encoding.EncodingRegion; namespace Gma.QrCodeNet.Encoding.EncodingRegion;
/// <remarks>ISO/IEC 18004:2000 Chapter 8.7.3 Page 46</remarks> /// <remarks>ISO/IEC 18004:2000 Chapter 8.7.3 Page 46</remarks>
internal static class Codeword internal static class Codeword
{ {
internal static void TryEmbedCodewords(this TriStateMatrix tsMatrix, BitList codewords) internal static void TryEmbedCodewords(this TriStateMatrix tsMatrix, BitList codewords)
{ {
int sWidth = tsMatrix.Width; int sWidth = tsMatrix.Width;
int codewordsSize = codewords.Count; int codewordsSize = codewords.Count;
int bitIndex = 0; int bitIndex = 0;
int directionUp = -1; int directionUp = -1;
int x = sWidth - 1; int x = sWidth - 1;
int y = sWidth - 1; int y = sWidth - 1;
while (x > 0) while (x > 0)
{ {
// Skip vertical timing pattern // Skip vertical timing pattern
if (x == 6) if (x == 6)
{ {
x -= 1; x -= 1;
} }
while (y >= 0 && y < sWidth) while (y >= 0 && y < sWidth)
{ {
for (int xOffset = 0; xOffset < 2; xOffset++) for (int xOffset = 0; xOffset < 2; xOffset++)
{ {
int xPos = x - xOffset; int xPos = x - xOffset;
if (tsMatrix.MStatus(xPos, y) == MatrixStatus.None) if (tsMatrix.MStatus(xPos, y) == MatrixStatus.None)
{ {
bool bit; bool bit;
if (bitIndex < codewordsSize) if (bitIndex < codewordsSize)
{ {
bit = codewords[bitIndex]; bit = codewords[bitIndex];
bitIndex++; bitIndex++;
} }
else else
{ {
bit = false; bit = false;
} }
tsMatrix[xPos, y, MatrixStatus.Data] = bit; tsMatrix[xPos, y, MatrixStatus.Data] = bit;
} }
} }
y = NextY(y, directionUp); y = NextY(y, directionUp);
} }
directionUp = ChangeDirection(directionUp); directionUp = ChangeDirection(directionUp);
y = NextY(y, directionUp); y = NextY(y, directionUp);
x -= 2; x -= 2;
} }
if (bitIndex != codewordsSize) if (bitIndex != codewordsSize)
{ {
throw new Exception($"Not all bits from {nameof(codewords)} consumed by matrix: {bitIndex} / {codewordsSize}."); throw new Exception($"Not all bits from {nameof(codewords)} consumed by matrix: {bitIndex} / {codewordsSize}.");
} }
} }
internal static int NextY(int y, int directionUp) internal static int NextY(int y, int directionUp)
{ {
return y + directionUp; return y + directionUp;
} }
internal static int ChangeDirection(int directionUp) internal static int ChangeDirection(int directionUp)
{ {
return -directionUp; return -directionUp;
} }
} }
@@ -1,4 +1,3 @@
using System;
using Gma.QrCodeNet.Encoding.Masking; using Gma.QrCodeNet.Encoding.Masking;
namespace Gma.QrCodeNet.Encoding.EncodingRegion; namespace Gma.QrCodeNet.Encoding.EncodingRegion;
@@ -10,105 +9,105 @@ namespace Gma.QrCodeNet.Encoding.EncodingRegion;
/// <remarks>ISO/IEC 18004:2000 Chapter 8.9 Page 53</remarks> /// <remarks>ISO/IEC 18004:2000 Chapter 8.9 Page 53</remarks>
internal static class FormatInformation internal static class FormatInformation
{ {
/// <summary> /// <summary>
/// From Appendix C in JISX0510:2004 (p.65). /// From Appendix C in JISX0510:2004 (p.65).
/// </summary> /// </summary>
private const int FormatInfoPoly = 0x537; private const int FormatInfoPoly = 0x537;
/// <summary> /// <summary>
/// From Appendix C in JISX0510:2004 (p.65). /// From Appendix C in JISX0510:2004 (p.65).
/// </summary> /// </summary>
private const int FormatInfoMaskPattern = 0x5412; private const int FormatInfoMaskPattern = 0x5412;
/// <summary> /// <summary>
/// Embed format information to tristatematrix. /// Embed format information to tristatematrix.
/// Process combination of create info bits, BCH error correction bits calculation, embed towards matrix. /// Process combination of create info bits, BCH error correction bits calculation, embed towards matrix.
/// </summary> /// </summary>
/// <remarks>ISO/IEC 18004:2000 Chapter 8.9 Page 53</remarks> /// <remarks>ISO/IEC 18004:2000 Chapter 8.9 Page 53</remarks>
internal static void EmbedFormatInformation(this TriStateMatrix triMatrix, ErrorCorrectionLevel errorLevel, Pattern pattern) internal static void EmbedFormatInformation(this TriStateMatrix triMatrix, ErrorCorrectionLevel errorLevel, Pattern pattern)
{ {
BitList formatInfo = GetFormatInfoBits(errorLevel, pattern); BitList formatInfo = GetFormatInfoBits(errorLevel, pattern);
int width = triMatrix.Width; int width = triMatrix.Width;
for (int index = 0; index < 15; index++) for (int index = 0; index < 15; index++)
{ {
MatrixPoint point = PointForInfo1(index); MatrixPoint point = PointForInfo1(index);
bool bit = formatInfo[index]; bool bit = formatInfo[index];
triMatrix[point.X, point.Y, MatrixStatus.NoMask] = bit; triMatrix[point.X, point.Y, MatrixStatus.NoMask] = bit;
if (index < 7) if (index < 7)
{ {
triMatrix[8, width - 1 - index, MatrixStatus.NoMask] = bit; triMatrix[8, width - 1 - index, MatrixStatus.NoMask] = bit;
} }
else else
{ {
triMatrix[width - 8 + (index - 7), 8, MatrixStatus.NoMask] = bit; triMatrix[width - 8 + (index - 7), 8, MatrixStatus.NoMask] = bit;
} }
} }
} }
private static MatrixPoint PointForInfo1(int bitsIndex) private static MatrixPoint PointForInfo1(int bitsIndex)
{ {
if (bitsIndex <= 7) if (bitsIndex <= 7)
{ {
return bitsIndex >= 6 return bitsIndex >= 6
? new MatrixPoint(bitsIndex + 1, 8) ? new MatrixPoint(bitsIndex + 1, 8)
: new MatrixPoint(bitsIndex, 8); : new MatrixPoint(bitsIndex, 8);
} }
else else
{ {
return bitsIndex == 8 return bitsIndex == 8
? new MatrixPoint(8, 8 - (bitsIndex - 7)) ? new MatrixPoint(8, 8 - (bitsIndex - 7))
: new MatrixPoint(8, 8 - (bitsIndex - 7) - 1); : new MatrixPoint(8, 8 - (bitsIndex - 7) - 1);
} }
} }
private static BitList GetFormatInfoBits(ErrorCorrectionLevel errorLevel, Pattern pattern) private static BitList GetFormatInfoBits(ErrorCorrectionLevel errorLevel, Pattern pattern)
{ {
int formatInfo = (int)pattern.MaskPatternType; int formatInfo = (int)pattern.MaskPatternType;
// Pattern bits length = 3 // Pattern bits length = 3
formatInfo |= GetErrorCorrectionIndicatorBits(errorLevel) << 3; formatInfo |= GetErrorCorrectionIndicatorBits(errorLevel) << 3;
int bchCode = BCHCalculator.CalculateBCH(formatInfo, FormatInfoPoly); int bchCode = BCHCalculator.CalculateBCH(formatInfo, FormatInfoPoly);
// bchCode length = 10 // bchCode length = 10
formatInfo = (formatInfo << 10) | bchCode; formatInfo = (formatInfo << 10) | bchCode;
// xor maskPattern // xor maskPattern
formatInfo ^= FormatInfoMaskPattern; formatInfo ^= FormatInfoMaskPattern;
BitList resultBits = new() BitList resultBits = new()
{ {
{ formatInfo, 15 } { formatInfo, 15 }
}; };
if (resultBits.Count != 15) if (resultBits.Count != 15)
{ {
throw new Exception("FormatInfoBits length is not 15"); throw new Exception("FormatInfoBits length is not 15");
} }
else else
{ {
return resultBits; return resultBits;
} }
} }
/// <summary> /// <summary>
/// According Table 25 — Error correction level indicators /// According Table 25 — Error correction level indicators
/// Using these bits as enum values would destroy their order which currently corresponds to error correction strength. /// Using these bits as enum values would destroy their order which currently corresponds to error correction strength.
/// </summary> /// </summary>
internal static int GetErrorCorrectionIndicatorBits(ErrorCorrectionLevel errorLevel) internal static int GetErrorCorrectionIndicatorBits(ErrorCorrectionLevel errorLevel)
{ {
// L 01 // L 01
// M 00 // M 00
// Q 11 // Q 11
// H 10 // H 10
return errorLevel switch return errorLevel switch
{ {
ErrorCorrectionLevel.H => 0x02, ErrorCorrectionLevel.H => 0x02,
ErrorCorrectionLevel.L => 0x01, ErrorCorrectionLevel.L => 0x01,
ErrorCorrectionLevel.M => 0x00, ErrorCorrectionLevel.M => 0x00,
ErrorCorrectionLevel.Q => 0x03, ErrorCorrectionLevel.Q => 0x03,
_ => throw new ArgumentException($"Unsupported error correction level [{errorLevel}]", nameof(errorLevel)) _ => throw new ArgumentException($"Unsupported error correction level [{errorLevel}]", nameof(errorLevel))
}; };
} }
} }
@@ -1,5 +1,3 @@
using System;
namespace Gma.QrCodeNet.Encoding.EncodingRegion; namespace Gma.QrCodeNet.Encoding.EncodingRegion;
/// <summary> /// <summary>
@@ -8,63 +6,63 @@ namespace Gma.QrCodeNet.Encoding.EncodingRegion;
/// <remarks>ISO/IEC 18004:2000 Chapter 8.10 Page 54</remarks> /// <remarks>ISO/IEC 18004:2000 Chapter 8.10 Page 54</remarks>
internal static class VersionInformation internal static class VersionInformation
{ {
private const int VIRectangleHeight = 3; private const int VIRectangleHeight = 3;
private const int VIRectangleWidth = 6; private const int VIRectangleWidth = 6;
private const int LengthDataBits = 6; private const int LengthDataBits = 6;
private const int LengthECBits = 12; private const int LengthECBits = 12;
private const int VersionBCHPoly = 0x1f25; private const int VersionBCHPoly = 0x1f25;
/// <summary> /// <summary>
/// Embed version information to Matrix /// Embed version information to Matrix
/// Only for version greater than or equal to 7 /// Only for version greater than or equal to 7
/// </summary> /// </summary>
internal static void EmbedVersionInformation(this TriStateMatrix tsMatrix, int version) internal static void EmbedVersionInformation(this TriStateMatrix tsMatrix, int version)
{ {
if (version < 7) if (version < 7)
{ {
return; return;
} }
BitList versionInfo = VersionInfoBitList(version); BitList versionInfo = VersionInfoBitList(version);
int matrixWidth = tsMatrix.Width; int matrixWidth = tsMatrix.Width;
// 1 cell between version info and position stencil // 1 cell between version info and position stencil
int shiftLength = QRCodeConstantVariable.PositionStencilWidth + VIRectangleHeight + 1; int shiftLength = QRCodeConstantVariable.PositionStencilWidth + VIRectangleHeight + 1;
// Reverse order input // Reverse order input
int viIndex = LengthDataBits + LengthECBits - 1; int viIndex = LengthDataBits + LengthECBits - 1;
for (int viWidth = 0; viWidth < VIRectangleWidth; viWidth++) for (int viWidth = 0; viWidth < VIRectangleWidth; viWidth++)
{ {
for (int viHeight = 0; viHeight < VIRectangleHeight; viHeight++) for (int viHeight = 0; viHeight < VIRectangleHeight; viHeight++)
{ {
bool bit = versionInfo[viIndex]; bool bit = versionInfo[viIndex];
viIndex--; viIndex--;
// Bottom left // Bottom left
tsMatrix[viWidth, (matrixWidth - shiftLength + viHeight), MatrixStatus.NoMask] = bit; tsMatrix[viWidth, (matrixWidth - shiftLength + viHeight), MatrixStatus.NoMask] = bit;
// Top right // Top right
tsMatrix[(matrixWidth - shiftLength + viHeight), viWidth, MatrixStatus.NoMask] = bit; tsMatrix[(matrixWidth - shiftLength + viHeight), viWidth, MatrixStatus.NoMask] = bit;
} }
} }
} }
private static BitList VersionInfoBitList(int version) private static BitList VersionInfoBitList(int version)
{ {
BitList result = new() BitList result = new()
{ {
{ version, LengthDataBits }, { version, LengthDataBits },
{ BCHCalculator.CalculateBCH(version, VersionBCHPoly), LengthECBits } { BCHCalculator.CalculateBCH(version, VersionBCHPoly), LengthECBits }
}; };
if (result.Count != (LengthECBits + LengthDataBits)) if (result.Count != (LengthECBits + LengthDataBits))
{ {
throw new Exception("Version Info creation error. Result is not 18 bits"); throw new Exception("Version Info creation error. Result is not 18 bits");
} }
return result; return result;
} }
} }
@@ -1,84 +1,82 @@
using Gma.QrCodeNet.Encoding.ReedSolomon; using Gma.QrCodeNet.Encoding.ReedSolomon;
using System;
using System.Collections.Generic;
namespace Gma.QrCodeNet.Encoding.ErrorCorrection; namespace Gma.QrCodeNet.Encoding.ErrorCorrection;
internal static class ECGenerator internal static class ECGenerator
{ {
internal static BitList FillECCodewords(BitList dataCodewords, VersionDetail vd) internal static BitList FillECCodewords(BitList dataCodewords, VersionDetail vd)
{ {
List<byte> dataCodewordsByte = dataCodewords.List; List<byte> dataCodewordsByte = dataCodewords.List;
int ecBlockGroup1 = vd.ECBlockGroup1; int ecBlockGroup1 = vd.ECBlockGroup1;
int numDataBytesGroup1 = vd.NumDataBytesGroup1; int numDataBytesGroup1 = vd.NumDataBytesGroup1;
int numDataBytesGroup2 = vd.NumDataBytesGroup2; int numDataBytesGroup2 = vd.NumDataBytesGroup2;
int ecBytesPerBlock = vd.NumECBytesPerBlock; int ecBytesPerBlock = vd.NumECBytesPerBlock;
int dataBytesOffset = 0; int dataBytesOffset = 0;
byte[][] dByteJArray = new byte[vd.NumECBlocks][]; byte[][] dByteJArray = new byte[vd.NumECBlocks][];
byte[][] ecByteJArray = new byte[vd.NumECBlocks][]; byte[][] ecByteJArray = new byte[vd.NumECBlocks][];
GaloisField256 gf256 = GaloisField256.QRCodeGaloisField; GaloisField256 gf256 = GaloisField256.QRCodeGaloisField;
GeneratorPolynomial generator = new(gf256); GeneratorPolynomial generator = new(gf256);
for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) for (int blockId = 0; blockId < vd.NumECBlocks; blockId++)
{ {
if (blockId < ecBlockGroup1) if (blockId < ecBlockGroup1)
{ {
dByteJArray[blockId] = new byte[numDataBytesGroup1]; dByteJArray[blockId] = new byte[numDataBytesGroup1];
for (int index = 0; index < numDataBytesGroup1; index++) for (int index = 0; index < numDataBytesGroup1; index++)
{ {
dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index]; dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index];
} }
dataBytesOffset += numDataBytesGroup1; dataBytesOffset += numDataBytesGroup1;
} }
else else
{ {
dByteJArray[blockId] = new byte[numDataBytesGroup2]; dByteJArray[blockId] = new byte[numDataBytesGroup2];
for (int index = 0; index < numDataBytesGroup2; index++) for (int index = 0; index < numDataBytesGroup2; index++)
{ {
dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index]; dByteJArray[blockId][index] = dataCodewordsByte[dataBytesOffset + index];
} }
dataBytesOffset += numDataBytesGroup2; dataBytesOffset += numDataBytesGroup2;
} }
ecByteJArray[blockId] = ReedSolomonEncoder.Encode(dByteJArray[blockId], ecBytesPerBlock, generator); ecByteJArray[blockId] = ReedSolomonEncoder.Encode(dByteJArray[blockId], ecBytesPerBlock, generator);
} }
if (vd.NumDataBytes != dataBytesOffset) if (vd.NumDataBytes != dataBytesOffset)
{ {
throw new ArgumentException("Data bytes do not match offset"); throw new ArgumentException("Data bytes do not match offset");
} }
BitList codewords = new(); BitList codewords = new();
int maxDataLength = ecBlockGroup1 == vd.NumECBlocks ? numDataBytesGroup1 : numDataBytesGroup2; int maxDataLength = ecBlockGroup1 == vd.NumECBlocks ? numDataBytesGroup1 : numDataBytesGroup2;
for (int dataId = 0; dataId < maxDataLength; dataId++) for (int dataId = 0; dataId < maxDataLength; dataId++)
{ {
for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) for (int blockId = 0; blockId < vd.NumECBlocks; blockId++)
{ {
if (!(dataId == numDataBytesGroup1 && blockId < ecBlockGroup1)) if (!(dataId == numDataBytesGroup1 && blockId < ecBlockGroup1))
{ {
codewords.Add(dByteJArray[blockId][dataId], 8); codewords.Add(dByteJArray[blockId][dataId], 8);
} }
} }
} }
for (int ecId = 0; ecId < ecBytesPerBlock; ecId++) for (int ecId = 0; ecId < ecBytesPerBlock; ecId++)
{ {
for (int blockId = 0; blockId < vd.NumECBlocks; blockId++) for (int blockId = 0; blockId < vd.NumECBlocks; blockId++)
{ {
codewords.Add(ecByteJArray[blockId][ecId], 8); codewords.Add(ecByteJArray[blockId][ecId], 8);
} }
} }
if (vd.NumTotalBytes != codewords.Count >> 3) if (vd.NumTotalBytes != codewords.Count >> 3)
{ {
throw new ArgumentException($"Total bytes: {vd.NumTotalBytes}. Actual bits: {codewords.Count}"); throw new ArgumentException($"Total bytes: {vd.NumTotalBytes}. Actual bits: {codewords.Count}");
} }
return codewords; return codewords;
} }
} }
@@ -2,8 +2,8 @@ namespace Gma.QrCodeNet.Encoding;
public enum ErrorCorrectionLevel public enum ErrorCorrectionLevel
{ {
L, L,
M, M,
Q, Q,
H H
} }
@@ -1,5 +1,3 @@
using System;
namespace Gma.QrCodeNet.Encoding; namespace Gma.QrCodeNet.Encoding;
/// <summary> /// <summary>
@@ -7,11 +5,11 @@ namespace Gma.QrCodeNet.Encoding;
/// </summary> /// </summary>
public class InputOutOfBoundaryException : Exception public class InputOutOfBoundaryException : Exception
{ {
public InputOutOfBoundaryException() : base() public InputOutOfBoundaryException() : base()
{ {
} }
public InputOutOfBoundaryException(string message) : base(message) public InputOutOfBoundaryException(string message) : base(message)
{ {
} }
} }
@@ -2,12 +2,12 @@ namespace Gma.QrCodeNet.Encoding.Masking;
public enum MaskPatternType public enum MaskPatternType
{ {
Type0 = 0, Type0 = 0,
Type1 = 1, Type1 = 1,
Type2 = 2, Type2 = 2,
Type3 = 3, Type3 = 3,
Type4 = 4, Type4 = 4,
Type5 = 5, Type5 = 5,
Type6 = 6, Type6 = 6,
Type7 = 7 Type7 = 7
} }
@@ -1,44 +1,43 @@
using System;
using Gma.QrCodeNet.Encoding.EncodingRegion; using Gma.QrCodeNet.Encoding.EncodingRegion;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
public static class MatrixExtensions public static class MatrixExtensions
{ {
public static TriStateMatrix Xor(this TriStateMatrix first, Pattern second, ErrorCorrectionLevel errorLevel) public static TriStateMatrix Xor(this TriStateMatrix first, Pattern second, ErrorCorrectionLevel errorLevel)
{ {
TriStateMatrix result = XorMatrix(first, second); TriStateMatrix result = XorMatrix(first, second);
result.EmbedFormatInformation(errorLevel, second); result.EmbedFormatInformation(errorLevel, second);
return result; return result;
} }
private static TriStateMatrix XorMatrix(TriStateMatrix first, BitMatrix second) private static TriStateMatrix XorMatrix(TriStateMatrix first, BitMatrix second)
{ {
int width = first.Width; int width = first.Width;
TriStateMatrix maskedMatrix = new(width); TriStateMatrix maskedMatrix = new(width);
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
for (int y = 0; y < width; y++) for (int y = 0; y < width; y++)
{ {
MatrixStatus states = first.MStatus(x, y); MatrixStatus states = first.MStatus(x, y);
switch (states) switch (states)
{ {
case MatrixStatus.NoMask: case MatrixStatus.NoMask:
maskedMatrix[x, y, MatrixStatus.NoMask] = first[x, y]; maskedMatrix[x, y, MatrixStatus.NoMask] = first[x, y];
break; break;
case MatrixStatus.Data: case MatrixStatus.Data:
maskedMatrix[x, y, MatrixStatus.Data] = first[x, y] ^ second[x, y]; maskedMatrix[x, y, MatrixStatus.Data] = first[x, y] ^ second[x, y];
break; break;
default: default:
throw new ArgumentException($"{nameof(TriStateMatrix)} has None value cell.", nameof(first)); throw new ArgumentException($"{nameof(TriStateMatrix)} has None value cell.", nameof(first));
} }
} }
} }
return maskedMatrix; return maskedMatrix;
} }
public static TriStateMatrix Apply(this TriStateMatrix matrix, Pattern pattern, ErrorCorrectionLevel errorLevel) => matrix.Xor(pattern, errorLevel); public static TriStateMatrix Apply(this TriStateMatrix matrix, Pattern pattern, ErrorCorrectionLevel errorLevel) => matrix.Xor(pattern, errorLevel);
} }
@@ -1,13 +1,11 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
public abstract class Pattern : BitMatrix public abstract class Pattern : BitMatrix
{ {
public override int Width => throw new NotSupportedException(); public override int Width => throw new NotSupportedException();
public override int Height => throw new NotSupportedException(); public override int Height => throw new NotSupportedException();
public override bool[,] InternalArray => throw new NotImplementedException(); public override bool[,] InternalArray => throw new NotImplementedException();
public abstract MaskPatternType MaskPatternType { get; } public abstract MaskPatternType MaskPatternType { get; }
} }
@@ -1,14 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern0 : Pattern internal class Pattern0 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type0; public override MaskPatternType MaskPatternType => MaskPatternType.Type0;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => (j + i) % 2 == 0; get => (j + i) % 2 == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,14 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern1 : Pattern internal class Pattern1 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type1; public override MaskPatternType MaskPatternType => MaskPatternType.Type1;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => j % 2 == 0; get => j % 2 == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,14 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern2 : Pattern internal class Pattern2 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type2; public override MaskPatternType MaskPatternType => MaskPatternType.Type2;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => i % 3 == 0; get => i % 3 == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,14 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern3 : Pattern internal class Pattern3 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type3; public override MaskPatternType MaskPatternType => MaskPatternType.Type3;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => (j + i) % 3 == 0; get => (j + i) % 3 == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,14 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern4 : Pattern internal class Pattern4 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type4; public override MaskPatternType MaskPatternType => MaskPatternType.Type4;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => ((j / 2) + (i / 3)) % 2 == 0; get => ((j / 2) + (i / 3)) % 2 == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,14 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern5 : Pattern internal class Pattern5 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type5; public override MaskPatternType MaskPatternType => MaskPatternType.Type5;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => (((i * j) % 2) + ((i * j) % 3)) == 0; get => (((i * j) % 2) + ((i * j) % 3)) == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,13 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern6 : Pattern internal class Pattern6 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type6; public override MaskPatternType MaskPatternType => MaskPatternType.Type6;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => ((((i * j) % 2) + ((i * j) % 3)) % 2) == 0; get => ((((i * j) % 2) + ((i * j) % 3)) % 2) == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,14 +1,12 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class Pattern7 : Pattern internal class Pattern7 : Pattern
{ {
public override MaskPatternType MaskPatternType => MaskPatternType.Type7; public override MaskPatternType MaskPatternType => MaskPatternType.Type7;
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => (((i * j) % 3) + (((i + j) % 2) % 2)) == 0; get => (((i * j) % 3) + (((i + j) % 2) % 2)) == 0;
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
} }
@@ -1,31 +1,28 @@
using System;
using System.Collections.Generic;
namespace Gma.QrCodeNet.Encoding.Masking; namespace Gma.QrCodeNet.Encoding.Masking;
internal class PatternFactory internal class PatternFactory
{ {
internal Pattern CreateByType(MaskPatternType maskPatternType) internal Pattern CreateByType(MaskPatternType maskPatternType)
{ {
return maskPatternType switch return maskPatternType switch
{ {
MaskPatternType.Type0 => new Pattern0(), MaskPatternType.Type0 => new Pattern0(),
MaskPatternType.Type1 => new Pattern1(), MaskPatternType.Type1 => new Pattern1(),
MaskPatternType.Type2 => new Pattern2(), MaskPatternType.Type2 => new Pattern2(),
MaskPatternType.Type3 => new Pattern3(), MaskPatternType.Type3 => new Pattern3(),
MaskPatternType.Type4 => new Pattern4(), MaskPatternType.Type4 => new Pattern4(),
MaskPatternType.Type5 => new Pattern5(), MaskPatternType.Type5 => new Pattern5(),
MaskPatternType.Type6 => new Pattern6(), MaskPatternType.Type6 => new Pattern6(),
MaskPatternType.Type7 => new Pattern7(), MaskPatternType.Type7 => new Pattern7(),
_ => throw new NotSupportedException("This should never happen.") _ => throw new NotSupportedException("This should never happen.")
}; };
} }
internal IEnumerable<Pattern> AllPatterns() internal IEnumerable<Pattern> AllPatterns()
{ {
foreach (MaskPatternType patternType in Enum.GetValues(typeof(MaskPatternType))) foreach (MaskPatternType patternType in Enum.GetValues(typeof(MaskPatternType)))
{ {
yield return CreateByType(patternType); yield return CreateByType(patternType);
} }
} }
} }
@@ -1,36 +1,34 @@
using System.Linq;
namespace Gma.QrCodeNet.Encoding.Masking.Scoring; namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
internal static class MatrixScoreCalculator internal static class MatrixScoreCalculator
{ {
internal static BitMatrix GetLowestPenaltyMatrix(this TriStateMatrix matrix, ErrorCorrectionLevel errorLevel) internal static BitMatrix GetLowestPenaltyMatrix(this TriStateMatrix matrix, ErrorCorrectionLevel errorLevel)
{ {
PatternFactory patternFactory = new(); PatternFactory patternFactory = new();
int score = int.MaxValue; int score = int.MaxValue;
int tempScore; int tempScore;
TriStateMatrix result = new(matrix.Width); TriStateMatrix result = new(matrix.Width);
TriStateMatrix triMatrix; TriStateMatrix triMatrix;
foreach (Pattern pattern in patternFactory.AllPatterns()) foreach (Pattern pattern in patternFactory.AllPatterns())
{ {
triMatrix = matrix.Apply(pattern, errorLevel); triMatrix = matrix.Apply(pattern, errorLevel);
tempScore = triMatrix.PenaltyScore(); tempScore = triMatrix.PenaltyScore();
if (tempScore < score) if (tempScore < score)
{ {
score = tempScore; score = tempScore;
result = triMatrix; result = triMatrix;
} }
} }
return result; return result;
} }
internal static int PenaltyScore(this BitMatrix matrix) internal static int PenaltyScore(this BitMatrix matrix)
{ {
PenaltyFactory penaltyFactory = new(); PenaltyFactory penaltyFactory = new();
return return
penaltyFactory penaltyFactory
.AllRules() .AllRules()
.Sum(penalty => penalty.PenaltyCalculate(matrix)); .Sum(penalty => penalty.PenaltyCalculate(matrix));
} }
} }
@@ -2,5 +2,5 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
public abstract class Penalty public abstract class Penalty
{ {
internal abstract int PenaltyCalculate(BitMatrix matrix); internal abstract int PenaltyCalculate(BitMatrix matrix);
} }
@@ -5,81 +5,81 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
/// </summary> /// </summary>
internal class Penalty1 : Penalty internal class Penalty1 : Penalty
{ {
/// <summary> /// <summary>
/// Calculate penalty value for first rule. /// Calculate penalty value for first rule.
/// </summary> /// </summary>
internal override int PenaltyCalculate(BitMatrix matrix) internal override int PenaltyCalculate(BitMatrix matrix)
{ {
int penaltyValue = PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false); int penaltyValue = PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false);
return penaltyValue; return penaltyValue;
} }
private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal) private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal)
{ {
int penalty = 0; int penalty = 0;
int width = matrix.Width; int width = matrix.Width;
int i = 0; int i = 0;
int j = 0; int j = 0;
while (i < width) while (i < width)
{ {
while (j < width - 4) while (j < width - 4)
{ {
bool preBit = isHorizontal bool preBit = isHorizontal
? matrix[j + 4, i] ? matrix[j + 4, i]
: matrix[i, j + 4]; : matrix[i, j + 4];
int numSameBitCell = 1; int numSameBitCell = 1;
for (int x = 1; x <= 4; x++) for (int x = 1; x <= 4; x++)
{ {
bool bit = isHorizontal bool bit = isHorizontal
? matrix[j + 4 - x, i] ? matrix[j + 4 - x, i]
: matrix[i, j + 4 - x]; : matrix[i, j + 4 - x];
if (bit == preBit) if (bit == preBit)
{ {
numSameBitCell++; numSameBitCell++;
} }
else else
{ {
break; break;
} }
} }
if (numSameBitCell == 1) if (numSameBitCell == 1)
{ {
j += 4; j += 4;
} }
else else
{ {
int x = 5; int x = 5;
while ((j + x) < width) while ((j + x) < width)
{ {
bool bit = isHorizontal bool bit = isHorizontal
? matrix[j + x, i] ? matrix[j + x, i]
: matrix[i, j + x]; : matrix[i, j + x];
if (bit == preBit) if (bit == preBit)
{ {
numSameBitCell++; numSameBitCell++;
} }
else else
{ {
break; break;
} }
x++; x++;
} }
if (numSameBitCell >= 5) if (numSameBitCell >= 5)
{ {
penalty += (3 + (numSameBitCell - 5)); penalty += (3 + (numSameBitCell - 5));
} }
j += x; j += x;
} }
} }
j = 0; j = 0;
i++; i++;
} }
return penalty; return penalty;
} }
} }
@@ -5,47 +5,47 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
/// </summary> /// </summary>
internal class Penalty2 : Penalty internal class Penalty2 : Penalty
{ {
internal override int PenaltyCalculate(BitMatrix matrix) internal override int PenaltyCalculate(BitMatrix matrix)
{ {
int width = matrix.Width; int width = matrix.Width;
int x = 0; int x = 0;
int y = 0; int y = 0;
int penalty = 0; int penalty = 0;
while (y < (width - 1)) while (y < (width - 1))
{ {
while (x < (width - 1)) while (x < (width - 1))
{ {
bool topR = matrix[x + 1, y]; bool topR = matrix[x + 1, y];
if (topR == matrix[x + 1, y + 1]) // Bottom Right if (topR == matrix[x + 1, y + 1]) // Bottom Right
{ {
if (topR == matrix[x, y + 1]) // Bottom Left if (topR == matrix[x, y + 1]) // Bottom Left
{ {
if (topR == matrix[x, y]) // Top Left if (topR == matrix[x, y]) // Top Left
{ {
penalty += 3; penalty += 3;
x += 1; x += 1;
} }
else else
{ {
x += 1; x += 1;
} }
} }
else else
{ {
x += 1; x += 1;
} }
} }
else else
{ {
x += 2; x += 2;
} }
} }
x = 0; x = 0;
y++; y++;
} }
return penalty; return penalty;
} }
} }
@@ -5,137 +5,137 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
/// </summary> /// </summary>
internal class Penalty3 : Penalty internal class Penalty3 : Penalty
{ {
/// <summary> /// <summary>
/// Calculate penalty value for Third rule. /// Calculate penalty value for Third rule.
/// </summary> /// </summary>
internal override int PenaltyCalculate(BitMatrix matrix) => PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false); internal override int PenaltyCalculate(BitMatrix matrix) => PenaltyCalculation(matrix, true) + PenaltyCalculation(matrix, false);
private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal) private int PenaltyCalculation(BitMatrix matrix, bool isHorizontal)
{ {
int i = 0; int i = 0;
int j = 1; int j = 1;
int penalty = 0; int penalty = 0;
int width = matrix.Width; int width = matrix.Width;
bool bit; bool bit;
while (i < width) while (i < width)
{ {
while (j < width - 5) while (j < width - 5)
{ {
bit = isHorizontal bit = isHorizontal
? matrix[j + 4, i] ? matrix[j + 4, i]
: matrix[i, j + 4]; : matrix[i, j + 4];
if (!bit) if (!bit)
{ {
bit = isHorizontal bit = isHorizontal
? matrix[j, i] ? matrix[j, i]
: matrix[i, j]; : matrix[i, j];
if (!bit) if (!bit)
{ {
penalty += PatternCheck(matrix, i, j, isHorizontal); penalty += PatternCheck(matrix, i, j, isHorizontal);
j += 4; j += 4;
} }
else else
{ {
j += 4; j += 4;
} }
} }
else else
{ {
for (int num = 4; num > 0; num--) for (int num = 4; num > 0; num--)
{ {
bit = isHorizontal bit = isHorizontal
? matrix[j + num, i] ? matrix[j + num, i]
: matrix[i, j + num]; : matrix[i, j + num];
if (!bit) if (!bit)
{ {
j += num; j += num;
break; break;
} }
if (num == 1) if (num == 1)
{ {
j += 5; j += 5;
} }
} }
} }
} }
j = 0; j = 0;
i++; i++;
} }
return penalty; return penalty;
} }
private int PatternCheck(BitMatrix matrix, int i, int j, bool isHorizontal) private int PatternCheck(BitMatrix matrix, int i, int j, bool isHorizontal)
{ {
bool bit; bool bit;
for (int num = 3; num >= 1; num--) for (int num = 3; num >= 1; num--)
{ {
bit = isHorizontal bit = isHorizontal
? matrix[j + num, i] ? matrix[j + num, i]
: matrix[i, j + num]; : matrix[i, j + num];
if (!bit) if (!bit)
{ {
return 0; return 0;
} }
} }
// Check for left side and right side x ( xoxxxox ). // Check for left side and right side x ( xoxxxox ).
if ((j - 1) < 0 || (j + 1) >= matrix.Width) if ((j - 1) < 0 || (j + 1) >= matrix.Width)
{ {
return 0; return 0;
} }
bit = isHorizontal bit = isHorizontal
? matrix[j + 5, i] ? matrix[j + 5, i]
: matrix[i, j + 5]; : matrix[i, j + 5];
if (!bit) if (!bit)
{ {
return 0; return 0;
} }
bit = isHorizontal bit = isHorizontal
? matrix[j - 1, i] ? matrix[j - 1, i]
: matrix[i, j - 1]; : matrix[i, j - 1];
if (!bit) if (!bit)
{ {
return 0; return 0;
} }
if ((j - 5) >= 0) if ((j - 5) >= 0)
{ {
for (int num = -2; num >= -5; num--) for (int num = -2; num >= -5; num--)
{ {
bit = isHorizontal bit = isHorizontal
? matrix[j + num, i] ? matrix[j + num, i]
: matrix[i, j + num]; : matrix[i, j + num];
if (bit) if (bit)
{ {
break; break;
} }
if (num == -5) if (num == -5)
{ {
return 40; return 40;
} }
} }
} }
if ((j + 9) < matrix.Width) if ((j + 9) < matrix.Width)
{ {
for (int num = 6; num <= 9; num++) for (int num = 6; num <= 9; num++)
{ {
bit = isHorizontal bit = isHorizontal
? matrix[j + num, i] ? matrix[j + num, i]
: matrix[i, j + num]; : matrix[i, j + num];
if (bit) if (bit)
{ {
return 0; return 0;
} }
} }
return 40; return 40;
} }
else else
{ {
return 0; return 0;
} }
} }
} }
@@ -1,5 +1,3 @@
using System;
namespace Gma.QrCodeNet.Encoding.Masking.Scoring; namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
/// <summary> /// <summary>
@@ -7,30 +5,30 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
/// </summary> /// </summary>
internal class Penalty4 : Penalty internal class Penalty4 : Penalty
{ {
/// <summary> /// <summary>
/// Calculate penalty value for Fourth rule. /// Calculate penalty value for Fourth rule.
/// Perform O(n) search for available x modules /// Perform O(n) search for available x modules
/// </summary> /// </summary>
internal override int PenaltyCalculate(BitMatrix matrix) internal override int PenaltyCalculate(BitMatrix matrix)
{ {
int width = matrix.Width; int width = matrix.Width;
int darkBitCount = 0; int darkBitCount = 0;
for (int j = 0; j < width; j++) for (int j = 0; j < width; j++)
{ {
for (int i = 0; i < width; i++) for (int i = 0; i < width; i++)
{ {
if (matrix[i, j]) if (matrix[i, j])
{ {
darkBitCount++; darkBitCount++;
} }
} }
} }
int matrixCount = width * width; int matrixCount = width * width;
double ratio = (double)darkBitCount / matrixCount; double ratio = (double)darkBitCount / matrixCount;
return Math.Abs((int)((ratio * 100) - 50)) / 5 * 10; return Math.Abs((int)((ratio * 100) - 50)) / 5 * 10;
} }
} }
@@ -1,6 +1,3 @@
using System;
using System.Collections.Generic;
namespace Gma.QrCodeNet.Encoding.Masking.Scoring; namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
/// <summary> /// <summary>
@@ -8,23 +5,23 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
/// </summary> /// </summary>
internal class PenaltyFactory internal class PenaltyFactory
{ {
internal Penalty CreateByRule(PenaltyRules penaltyRule) internal Penalty CreateByRule(PenaltyRules penaltyRule)
{ {
return penaltyRule switch return penaltyRule switch
{ {
PenaltyRules.Rule01 => new Penalty1(), PenaltyRules.Rule01 => new Penalty1(),
PenaltyRules.Rule02 => new Penalty2(), PenaltyRules.Rule02 => new Penalty2(),
PenaltyRules.Rule03 => new Penalty3(), PenaltyRules.Rule03 => new Penalty3(),
PenaltyRules.Rule04 => new Penalty4(), PenaltyRules.Rule04 => new Penalty4(),
_ => throw new ArgumentException($"Unsupport penalty rule: {penaltyRule}", nameof(penaltyRule)) _ => throw new ArgumentException($"Unsupport penalty rule: {penaltyRule}", nameof(penaltyRule))
}; };
} }
internal IEnumerable<Penalty> AllRules() internal IEnumerable<Penalty> AllRules()
{ {
foreach (PenaltyRules penaltyRule in Enum.GetValues(typeof(PenaltyRules))) foreach (PenaltyRules penaltyRule in Enum.GetValues(typeof(PenaltyRules)))
{ {
yield return CreateByRule(penaltyRule); yield return CreateByRule(penaltyRule);
} }
} }
} }
@@ -2,8 +2,8 @@ namespace Gma.QrCodeNet.Encoding.Masking.Scoring;
public enum PenaltyRules public enum PenaltyRules
{ {
Rule01 = 1, Rule01 = 1,
Rule02 = 2, Rule02 = 2,
Rule03 = 3, Rule03 = 3,
Rule04 = 4 Rule04 = 4
} }
@@ -2,19 +2,19 @@ namespace Gma.QrCodeNet.Encoding;
public struct MatrixPoint public struct MatrixPoint
{ {
internal MatrixPoint(int x, int y) internal MatrixPoint(int x, int y)
: this() : this()
{ {
X = x; X = x;
Y = y; Y = y;
} }
public int X { get; private set; } public int X { get; private set; }
public int Y { get; private set; } public int Y { get; private set; }
public MatrixPoint Offset(MatrixPoint offset) => new(offset.X + X, offset.Y + Y); public MatrixPoint Offset(MatrixPoint offset) => new(offset.X + X, offset.Y + Y);
internal MatrixPoint Offset(int offsetX, int offsetY) => Offset(new MatrixPoint(offsetX, offsetY)); internal MatrixPoint Offset(int offsetX, int offsetY) => Offset(new MatrixPoint(offsetX, offsetY));
public override string ToString() => $"Point({X};{Y})"; public override string ToString() => $"Point({X};{Y})";
} }
@@ -1,32 +1,31 @@
using System.Collections; using System.Collections;
using System.Collections.Generic;
namespace Gma.QrCodeNet.Encoding; namespace Gma.QrCodeNet.Encoding;
internal struct MatrixRectangle : IEnumerable<MatrixPoint> internal struct MatrixRectangle : IEnumerable<MatrixPoint>
{ {
internal MatrixRectangle(MatrixPoint location, MatrixSize size) : internal MatrixRectangle(MatrixPoint location, MatrixSize size) :
this() this()
{ {
Location = location; Location = location;
Size = size; Size = size;
} }
public MatrixPoint Location { get; private set; } public MatrixPoint Location { get; private set; }
public MatrixSize Size { get; private set; } public MatrixSize Size { get; private set; }
public IEnumerator<MatrixPoint> GetEnumerator() public IEnumerator<MatrixPoint> GetEnumerator()
{ {
for (int j = Location.Y; j < Location.Y + Size.Height; j++) for (int j = Location.Y; j < Location.Y + Size.Height; j++)
{ {
for (int i = Location.X; i < Location.X + Size.Width; i++) for (int i = Location.X; i < Location.X + Size.Width; i++)
{ {
yield return new MatrixPoint(i, j); yield return new MatrixPoint(i, j);
} }
} }
} }
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public override string ToString() => $"Rectangle({Location.X};{Location.Y}):({Size.Width} x {Size.Height})"; public override string ToString() => $"Rectangle({Location.X};{Location.Y}):({Size.Width} x {Size.Height})";
} }
@@ -2,18 +2,18 @@ namespace Gma.QrCodeNet.Encoding;
public struct MatrixSize public struct MatrixSize
{ {
internal MatrixSize(int width, int height) internal MatrixSize(int width, int height)
: this() : this()
{ {
Width = width; Width = width;
Height = height; Height = height;
} }
public int Width { get; private set; } public int Width { get; private set; }
public int Height { get; private set; } public int Height { get; private set; }
public override string ToString() public override string ToString()
{ {
return $"Size({Width};{Height})"; return $"Size({Width};{Height})";
} }
} }
@@ -2,7 +2,7 @@ namespace Gma.QrCodeNet.Encoding;
public enum MatrixStatus public enum MatrixStatus
{ {
None, None,
NoMask, NoMask,
Data Data
} }
@@ -4,11 +4,11 @@ namespace Gma.QrCodeNet.Encoding.Positioning;
internal static class PositioningPatternBuilder internal static class PositioningPatternBuilder
{ {
internal static void EmbedBasicPatterns(int version, TriStateMatrix matrix) internal static void EmbedBasicPatterns(int version, TriStateMatrix matrix)
{ {
new PositionDetectionPattern(version).ApplyTo(matrix); new PositionDetectionPattern(version).ApplyTo(matrix);
new DarkDotAtLeftBottom(version).ApplyTo(matrix); new DarkDotAtLeftBottom(version).ApplyTo(matrix);
new AlignmentPattern(version).ApplyTo(matrix); new AlignmentPattern(version).ApplyTo(matrix);
new TimingPattern(version).ApplyTo(matrix); new TimingPattern(version).ApplyTo(matrix);
} }
} }
@@ -1,105 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; namespace Gma.QrCodeNet.Encoding.Positioning.Stencils;
internal class AlignmentPattern : PatternStencilBase internal class AlignmentPattern : PatternStencilBase
{ {
public AlignmentPattern(int version) public AlignmentPattern(int version)
: base(version) : base(version)
{ {
} }
private static bool[,] AlignmentPatternArray { get; } = private static bool[,] AlignmentPatternArray { get; } =
new[,] new[,]
{ {
{ X, X, X, X, X }, { X, X, X, X, X },
{ X, O, O, O, X }, { X, O, O, O, X },
{ X, O, X, O, X }, { X, O, X, O, X },
{ X, O, O, O, X }, { X, O, O, O, X },
{ X, X, X, X, X } { X, X, X, X, X }
}; };
public override bool[,] Stencil => AlignmentPatternArray; public override bool[,] Stencil => AlignmentPatternArray;
// Table E.1 — Row/column coordinates of center module of Alignment Patterns // Table E.1 — Row/column coordinates of center module of Alignment Patterns
private static byte[][] AlignmentPatternCoordinatesByVersion { get; } = private static byte[][] AlignmentPatternCoordinatesByVersion { get; } =
new[] new[]
{ {
Array.Empty<byte>(), Array.Empty<byte>(),
Array.Empty<byte>(), Array.Empty<byte>(),
new byte[] { 6, 18 }, new byte[] { 6, 18 },
new byte[] { 6, 22 }, new byte[] { 6, 22 },
new byte[] { 6, 26 }, new byte[] { 6, 26 },
new byte[] { 6, 30 }, new byte[] { 6, 30 },
new byte[] { 6, 34 }, new byte[] { 6, 34 },
new byte[] { 6, 22, 38 }, new byte[] { 6, 22, 38 },
new byte[] { 6, 24, 42 }, new byte[] { 6, 24, 42 },
new byte[] { 6, 26, 46 }, new byte[] { 6, 26, 46 },
new byte[] { 6, 28, 50 }, new byte[] { 6, 28, 50 },
new byte[] { 6, 30, 54 }, new byte[] { 6, 30, 54 },
new byte[] { 6, 32, 58 }, new byte[] { 6, 32, 58 },
new byte[] { 6, 34, 62 }, new byte[] { 6, 34, 62 },
new byte[] { 6, 26, 46, 66 }, new byte[] { 6, 26, 46, 66 },
new byte[] { 6, 26, 48, 70 }, new byte[] { 6, 26, 48, 70 },
new byte[] { 6, 26, 50, 74 }, new byte[] { 6, 26, 50, 74 },
new byte[] { 6, 30, 54, 78 }, new byte[] { 6, 30, 54, 78 },
new byte[] { 6, 30, 56, 82 }, new byte[] { 6, 30, 56, 82 },
new byte[] { 6, 30, 58, 86 }, new byte[] { 6, 30, 58, 86 },
new byte[] { 6, 34, 62, 90 }, new byte[] { 6, 34, 62, 90 },
new byte[] { 6, 28, 50, 72, 94 }, new byte[] { 6, 28, 50, 72, 94 },
new byte[] { 6, 26, 50, 74, 98 }, new byte[] { 6, 26, 50, 74, 98 },
new byte[] { 6, 30, 54, 78, 102 }, new byte[] { 6, 30, 54, 78, 102 },
new byte[] { 6, 28, 54, 80, 106 }, new byte[] { 6, 28, 54, 80, 106 },
new byte[] { 6, 32, 58, 84, 110 }, new byte[] { 6, 32, 58, 84, 110 },
new byte[] { 6, 30, 58, 86, 114 }, new byte[] { 6, 30, 58, 86, 114 },
new byte[] { 6, 34, 62, 90, 118 }, new byte[] { 6, 34, 62, 90, 118 },
new byte[] { 6, 26, 50, 74, 98, 122 }, new byte[] { 6, 26, 50, 74, 98, 122 },
new byte[] { 6, 30, 54, 78, 102, 126 }, new byte[] { 6, 30, 54, 78, 102, 126 },
new byte[] { 6, 26, 52, 78, 104, 130 }, new byte[] { 6, 26, 52, 78, 104, 130 },
new byte[] { 6, 30, 56, 82, 108, 134 }, new byte[] { 6, 30, 56, 82, 108, 134 },
new byte[] { 6, 34, 60, 86, 112, 138 }, new byte[] { 6, 34, 60, 86, 112, 138 },
new byte[] { 6, 30, 58, 86, 114, 142 }, new byte[] { 6, 30, 58, 86, 114, 142 },
new byte[] { 6, 34, 62, 90, 118, 146 }, new byte[] { 6, 34, 62, 90, 118, 146 },
new byte[] { 6, 30, 54, 78, 102, 126, 150 }, new byte[] { 6, 30, 54, 78, 102, 126, 150 },
new byte[] { 6, 24, 50, 76, 102, 128, 154 }, new byte[] { 6, 24, 50, 76, 102, 128, 154 },
new byte[] { 6, 28, 54, 80, 106, 132, 158 }, new byte[] { 6, 28, 54, 80, 106, 132, 158 },
new byte[] { 6, 32, 58, 84, 110, 136, 162 }, new byte[] { 6, 32, 58, 84, 110, 136, 162 },
new byte[] { 6, 26, 54, 82, 110, 138, 166 }, new byte[] { 6, 26, 54, 82, 110, 138, 166 },
new byte[] { 6, 30, 58, 86, 114, 142, 170 } new byte[] { 6, 30, 58, 86, 114, 142, 170 }
}; };
public override void ApplyTo(TriStateMatrix matrix) public override void ApplyTo(TriStateMatrix matrix)
{ {
foreach (MatrixPoint coordinatePair in GetNonColidingCoordinatePairs(matrix)) foreach (MatrixPoint coordinatePair in GetNonColidingCoordinatePairs(matrix))
{ {
CopyTo(matrix, coordinatePair, MatrixStatus.NoMask); CopyTo(matrix, coordinatePair, MatrixStatus.NoMask);
} }
} }
public IEnumerable<MatrixPoint> GetNonColidingCoordinatePairs(TriStateMatrix matrix) public IEnumerable<MatrixPoint> GetNonColidingCoordinatePairs(TriStateMatrix matrix)
{ {
return return
GetAllCoordinatePairs() GetAllCoordinatePairs()
.Where(point => matrix.MStatus(point.Offset(2, 2)) == MatrixStatus.None); .Where(point => matrix.MStatus(point.Offset(2, 2)) == MatrixStatus.None);
} }
private IEnumerable<MatrixPoint> GetAllCoordinatePairs() private IEnumerable<MatrixPoint> GetAllCoordinatePairs()
{ {
IEnumerable<byte> coordinates = GetPatternCoordinatesByVersion(Version); IEnumerable<byte> coordinates = GetPatternCoordinatesByVersion(Version);
foreach (byte centerX in coordinates) foreach (byte centerX in coordinates)
{ {
foreach (byte centerY in coordinates) foreach (byte centerY in coordinates)
{ {
MatrixPoint location = new(centerX - 2, centerY - 2); MatrixPoint location = new(centerX - 2, centerY - 2);
yield return location; yield return location;
} }
} }
} }
private static IEnumerable<byte> GetPatternCoordinatesByVersion(int version) private static IEnumerable<byte> GetPatternCoordinatesByVersion(int version)
{ {
return AlignmentPatternCoordinatesByVersion[version]; return AlignmentPatternCoordinatesByVersion[version];
} }
} }
@@ -1,17 +1,15 @@
using System;
namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; namespace Gma.QrCodeNet.Encoding.Positioning.Stencils;
internal class DarkDotAtLeftBottom : PatternStencilBase internal class DarkDotAtLeftBottom : PatternStencilBase
{ {
public DarkDotAtLeftBottom(int version) : base(version) public DarkDotAtLeftBottom(int version) : base(version)
{ {
} }
public override bool[,] Stencil => throw new NotImplementedException(); public override bool[,] Stencil => throw new NotImplementedException();
public override void ApplyTo(TriStateMatrix matrix) public override void ApplyTo(TriStateMatrix matrix)
{ {
matrix[8, matrix.Width - 8, MatrixStatus.NoMask] = true; matrix[8, matrix.Width - 8, MatrixStatus.NoMask] = true;
} }
} }
@@ -1,32 +1,30 @@
using System;
namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; namespace Gma.QrCodeNet.Encoding.Positioning.Stencils;
internal abstract class PatternStencilBase : BitMatrix internal abstract class PatternStencilBase : BitMatrix
{ {
protected const bool O = false; protected const bool O = false;
protected const bool X = true; protected const bool X = true;
internal PatternStencilBase(int version) internal PatternStencilBase(int version)
{ {
Version = version; Version = version;
} }
public int Version { get; private set; } public int Version { get; private set; }
public abstract bool[,] Stencil { get; } public abstract bool[,] Stencil { get; }
public override int Width => Stencil.GetLength(0); public override int Width => Stencil.GetLength(0);
public override int Height => Stencil.GetLength(1); public override int Height => Stencil.GetLength(1);
public override bool[,] InternalArray => throw new NotImplementedException(); public override bool[,] InternalArray => throw new NotImplementedException();
public override bool this[int i, int j] public override bool this[int i, int j]
{ {
get => Stencil[i, j]; get => Stencil[i, j];
set => throw new NotSupportedException(); set => throw new NotSupportedException();
} }
public abstract void ApplyTo(TriStateMatrix matrix); public abstract void ApplyTo(TriStateMatrix matrix);
} }
@@ -2,40 +2,40 @@ namespace Gma.QrCodeNet.Encoding.Positioning.Stencils;
internal class PositionDetectionPattern : PatternStencilBase internal class PositionDetectionPattern : PatternStencilBase
{ {
public PositionDetectionPattern(int version) public PositionDetectionPattern(int version)
: base(version) : base(version)
{ {
} }
private static bool[,] PositionDetection { get; } = private static bool[,] PositionDetection { get; } =
new[,] new[,]
{ {
{ O, O, O, O, O, O, O, O, O }, { O, O, O, O, O, O, O, O, O },
{ O, X, X, X, X, X, X, X, O }, { O, X, X, X, X, X, X, X, O },
{ O, X, O, O, O, O, O, X, O }, { O, X, O, O, O, O, O, X, O },
{ O, X, O, X, X, X, O, X, O }, { O, X, O, X, X, X, O, X, O },
{ O, X, O, X, X, X, O, X, O }, { O, X, O, X, X, X, O, X, O },
{ O, X, O, X, X, X, O, X, O }, { O, X, O, X, X, X, O, X, O },
{ O, X, O, O, O, O, O, X, O }, { O, X, O, O, O, O, O, X, O },
{ O, X, X, X, X, X, X, X, O }, { O, X, X, X, X, X, X, X, O },
{ O, O, O, O, O, O, O, O, O } { O, O, O, O, O, O, O, O, O }
}; };
public override bool[,] Stencil => PositionDetection; public override bool[,] Stencil => PositionDetection;
public override void ApplyTo(TriStateMatrix matrix) public override void ApplyTo(TriStateMatrix matrix)
{ {
MatrixSize size = GetSizeOfSquareWithSeparators(); MatrixSize size = GetSizeOfSquareWithSeparators();
MatrixPoint leftTopCorner = new(0, 0); MatrixPoint leftTopCorner = new(0, 0);
CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 1), size), leftTopCorner, MatrixStatus.NoMask); CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 1), size), leftTopCorner, MatrixStatus.NoMask);
MatrixPoint rightTopCorner = new(matrix.Width - Width + 1, 0); MatrixPoint rightTopCorner = new(matrix.Width - Width + 1, 0);
CopyTo(matrix, new MatrixRectangle(new MatrixPoint(0, 1), size), rightTopCorner, MatrixStatus.NoMask); CopyTo(matrix, new MatrixRectangle(new MatrixPoint(0, 1), size), rightTopCorner, MatrixStatus.NoMask);
MatrixPoint leftBottomCorner = new(0, matrix.Width - Width + 1); MatrixPoint leftBottomCorner = new(0, matrix.Width - Width + 1);
CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 0), size), leftBottomCorner, MatrixStatus.NoMask); CopyTo(matrix, new MatrixRectangle(new MatrixPoint(1, 0), size), leftBottomCorner, MatrixStatus.NoMask);
} }
private MatrixSize GetSizeOfSquareWithSeparators() => new(Width - 1, Height - 1); private MatrixSize GetSizeOfSquareWithSeparators() => new(Width - 1, Height - 1);
} }
@@ -1,35 +1,33 @@
using System;
namespace Gma.QrCodeNet.Encoding.Positioning.Stencils; namespace Gma.QrCodeNet.Encoding.Positioning.Stencils;
internal class TimingPattern : PatternStencilBase internal class TimingPattern : PatternStencilBase
{ {
public TimingPattern(int version) public TimingPattern(int version)
: base(version) : base(version)
{ {
} }
public override bool[,] Stencil => throw new NotImplementedException(); public override bool[,] Stencil => throw new NotImplementedException();
public override void ApplyTo(TriStateMatrix matrix) public override void ApplyTo(TriStateMatrix matrix)
{ {
// -8 is for skipping position detection patterns (size 7), and two horizontal/vertical // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical
// separation patterns (size 1). Thus, 8 = 7 + 1. // separation patterns (size 1). Thus, 8 = 7 + 1.
for (int i = 8; i < matrix.Width - 8; ++i) for (int i = 8; i < matrix.Width - 8; ++i)
{ {
bool value = (sbyte)((i + 1) % 2) == 1; bool value = (sbyte)((i + 1) % 2) == 1;
// Horizontal line. // Horizontal line.
if (matrix.MStatus(6, i) == MatrixStatus.None) if (matrix.MStatus(6, i) == MatrixStatus.None)
{ {
matrix[6, i, MatrixStatus.NoMask] = value; matrix[6, i, MatrixStatus.NoMask] = value;
} }
// Vertical line. // Vertical line.
if (matrix.MStatus(i, 6) == MatrixStatus.None) if (matrix.MStatus(i, 6) == MatrixStatus.None)
{ {
matrix[i, 6, MatrixStatus.NoMask] = value; matrix[i, 6, MatrixStatus.NoMask] = value;
} }
} }
} }
} }
@@ -5,48 +5,48 @@ namespace Gma.QrCodeNet.Encoding;
/// </summary> /// </summary>
public static class QRCodeConstantVariable public static class QRCodeConstantVariable
{ {
public const int MinVersion = 1; public const int MinVersion = 1;
public const int MaxVersion = 40; public const int MaxVersion = 40;
public const string DefaultEncoding = "iso-8859-1"; public const string DefaultEncoding = "iso-8859-1";
public const string UTF8Encoding = "utf-8"; public const string UTF8Encoding = "utf-8";
/// <summary> /// <summary>
/// ISO/IEC 18004:2006(E) Page 45 Chapter Generating the error correction codewords /// ISO/IEC 18004:2006(E) Page 45 Chapter Generating the error correction codewords
/// Primative Polynomial = Bin 100011101 = Dec 285 /// Primative Polynomial = Bin 100011101 = Dec 285
/// </summary> /// </summary>
public const int QRCodePrimitive = 285; public const int QRCodePrimitive = 285;
internal const int TerminatorNPaddingBit = 0; internal const int TerminatorNPaddingBit = 0;
internal const int TerminatorLength = 4; internal const int TerminatorLength = 4;
/// <summary> /// <summary>
/// 0xEC /// 0xEC
/// </summary> /// </summary>
internal const int PadeCodewordsOdd = 0xec; internal const int PadeCodewordsOdd = 0xec;
/// <summary> /// <summary>
/// 0x11 /// 0x11
/// </summary> /// </summary>
internal const int PadeCodewordsEven = 0x11; internal const int PadeCodewordsEven = 0x11;
internal const int PositionStencilWidth = 7; internal const int PositionStencilWidth = 7;
internal static bool[] PadeOdd = new bool[] internal static bool[] PadeOdd = new bool[]
{ {
true, true, true, false, true, true, true, false,
true, true, false, false true, true, false, false
}; };
internal static bool[] PadeEven = new bool[] internal static bool[] PadeEven = new bool[]
{ {
false, false, false, true, false, false, false, true,
false, false, false, true false, false, false, true
}; };
/// <summary> /// <summary>
/// URL:http://en.wikipedia.org/wiki/Byte-order_mark /// URL:http://en.wikipedia.org/wiki/Byte-order_mark
/// </summary> /// </summary>
public static byte[] UTF8ByteOrderMark => new byte[] { 0xEF, 0xBB, 0xBF }; public static byte[] UTF8ByteOrderMark => new byte[] { 0xEF, 0xBB, 0xBF };
} }
@@ -9,24 +9,24 @@ namespace Gma.QrCodeNet.Encoding;
internal static class QRCodeEncode internal static class QRCodeEncode
{ {
internal static BitMatrix Encode(string content, ErrorCorrectionLevel errorLevel) internal static BitMatrix Encode(string content, ErrorCorrectionLevel errorLevel)
{ {
EncodationStruct encodeStruct = DataEncode.Encode(content, errorLevel); EncodationStruct encodeStruct = DataEncode.Encode(content, errorLevel);
return ProcessEncodationResult(encodeStruct, errorLevel); return ProcessEncodationResult(encodeStruct, errorLevel);
} }
private static BitMatrix ProcessEncodationResult(EncodationStruct encodeStruct, ErrorCorrectionLevel errorLevel) private static BitMatrix ProcessEncodationResult(EncodationStruct encodeStruct, ErrorCorrectionLevel errorLevel)
{ {
BitList codewords = ECGenerator.FillECCodewords(encodeStruct.DataCodewords, encodeStruct.VersionDetail); BitList codewords = ECGenerator.FillECCodewords(encodeStruct.DataCodewords, encodeStruct.VersionDetail);
TriStateMatrix triMatrix = new(encodeStruct.VersionDetail.MatrixWidth); TriStateMatrix triMatrix = new(encodeStruct.VersionDetail.MatrixWidth);
PositioningPatternBuilder.EmbedBasicPatterns(encodeStruct.VersionDetail.Version, triMatrix); PositioningPatternBuilder.EmbedBasicPatterns(encodeStruct.VersionDetail.Version, triMatrix);
triMatrix.EmbedVersionInformation(encodeStruct.VersionDetail.Version); triMatrix.EmbedVersionInformation(encodeStruct.VersionDetail.Version);
triMatrix.EmbedFormatInformation(errorLevel, new Pattern0()); triMatrix.EmbedFormatInformation(errorLevel, new Pattern0());
triMatrix.TryEmbedCodewords(codewords); triMatrix.TryEmbedCodewords(codewords);
return triMatrix.GetLowestPenaltyMatrix(errorLevel); return triMatrix.GetLowestPenaltyMatrix(errorLevel);
} }
} }
@@ -8,21 +8,21 @@ namespace Gma.QrCodeNet.Encoding;
/// </summary> /// </summary>
public class QrCode public class QrCode
{ {
internal QrCode(BitMatrix matrix) internal QrCode(BitMatrix matrix)
{ {
Matrix = matrix; Matrix = matrix;
IsContainMatrix = true; IsContainMatrix = true;
} }
public bool IsContainMatrix public bool IsContainMatrix
{ {
get; get;
private set; private set;
} }
public BitMatrix Matrix public BitMatrix Matrix
{ {
get; get;
private set; private set;
} }
} }
@@ -2,38 +2,38 @@ namespace Gma.QrCodeNet.Encoding;
public class QrEncoder public class QrEncoder
{ {
/// <summary> /// <summary>
/// Default QrEncoder will set ErrorCorrectionLevel as M /// Default QrEncoder will set ErrorCorrectionLevel as M
/// </summary> /// </summary>
public QrEncoder() public QrEncoder()
: this(ErrorCorrectionLevel.M) : this(ErrorCorrectionLevel.M)
{ {
} }
/// <summary> /// <summary>
/// QrEncoder with parameter ErrorCorrectionLevel. /// QrEncoder with parameter ErrorCorrectionLevel.
/// </summary> /// </summary>
public QrEncoder(ErrorCorrectionLevel errorCorrectionLevel) public QrEncoder(ErrorCorrectionLevel errorCorrectionLevel)
{ {
ErrorCorrectionLevel = errorCorrectionLevel; ErrorCorrectionLevel = errorCorrectionLevel;
} }
public ErrorCorrectionLevel ErrorCorrectionLevel { get; set; } public ErrorCorrectionLevel ErrorCorrectionLevel { get; set; }
/// <summary> /// <summary>
/// Encode string content to QrCode matrix /// Encode string content to QrCode matrix
/// </summary> /// </summary>
/// <exception cref="InputOutOfBoundaryException"> /// <exception cref="InputOutOfBoundaryException">
/// This exception for string content is null, empty or too large</exception> /// This exception for string content is null, empty or too large</exception>
public QrCode Encode(string content) public QrCode Encode(string content)
{ {
if (string.IsNullOrEmpty(content)) if (string.IsNullOrEmpty(content))
{ {
throw new InputOutOfBoundaryException("Input cannot be null or empty."); throw new InputOutOfBoundaryException("Input cannot be null or empty.");
} }
else else
{ {
return new QrCode(QRCodeEncode.Encode(content, ErrorCorrectionLevel)); return new QrCode(QRCodeEncode.Encode(content, ErrorCorrectionLevel));
} }
} }
} }
@@ -1,5 +1,3 @@
using System;
namespace Gma.QrCodeNet.Encoding.ReedSolomon; namespace Gma.QrCodeNet.Encoding.ReedSolomon;
/// <summary> /// <summary>
@@ -7,116 +5,116 @@ namespace Gma.QrCodeNet.Encoding.ReedSolomon;
/// </summary> /// </summary>
internal sealed class GaloisField256 internal sealed class GaloisField256
{ {
internal GaloisField256(int primitive) internal GaloisField256(int primitive)
{ {
AntiLogTable = new int[256]; AntiLogTable = new int[256];
LogTable = new int[256]; LogTable = new int[256];
Primitive = primitive; Primitive = primitive;
int gfx = 1; int gfx = 1;
// Power cycle is from 0 to 254. 2^255 = 1 = 2^0 // Power cycle is from 0 to 254. 2^255 = 1 = 2^0
// Value cycle is from 1 to 255. Thus there should not have Log(0). // Value cycle is from 1 to 255. Thus there should not have Log(0).
for (int powers = 0; powers < 256; powers++) for (int powers = 0; powers < 256; powers++)
{ {
AntiLogTable[powers] = gfx; AntiLogTable[powers] = gfx;
if (powers != 255) if (powers != 255)
{ {
LogTable[gfx] = powers; LogTable[gfx] = powers;
} }
gfx <<= 1; // gfx = gfx * 2 where alpha is 2. gfx <<= 1; // gfx = gfx * 2 where alpha is 2.
if (gfx > 255) if (gfx > 255)
{ {
gfx ^= primitive; gfx ^= primitive;
} }
} }
} }
private int[] AntiLogTable { get; } private int[] AntiLogTable { get; }
private int[] LogTable { get; } private int[] LogTable { get; }
internal int Primitive { get; } internal int Primitive { get; }
internal static GaloisField256 QRCodeGaloisField => new(QRCodeConstantVariable.QRCodePrimitive); internal static GaloisField256 QRCodeGaloisField => new(QRCodeConstantVariable.QRCodePrimitive);
/// <returns> /// <returns>
/// Powers of a in GF table. Where a = 2 /// Powers of a in GF table. Where a = 2
/// </returns> /// </returns>
internal int Exponent(int powersOfa) => AntiLogTable[powersOfa]; internal int Exponent(int powersOfa) => AntiLogTable[powersOfa];
/// <returns> /// <returns>
/// Log (power of a) in GF table. Where a = 2 /// Log (power of a) in GF table. Where a = 2
/// </returns> /// </returns>
internal int Log(int gfValue) internal int Log(int gfValue)
{ {
if (gfValue == 0) if (gfValue == 0)
{ {
throw new ArgumentException("GaloisField value will not be equal to 0, Log method."); throw new ArgumentException("GaloisField value will not be equal to 0, Log method.");
} }
return LogTable[gfValue]; return LogTable[gfValue];
} }
internal int Inverse(int gfValue) internal int Inverse(int gfValue)
{ {
if (gfValue == 0) if (gfValue == 0)
{ {
throw new ArgumentException("GaloisField value will not be equal to 0, Inverse method."); throw new ArgumentException("GaloisField value will not be equal to 0, Inverse method.");
} }
return Exponent(255 - Log(gfValue)); return Exponent(255 - Log(gfValue));
} }
internal int Addition(int gfValueA, int gfValueB) => gfValueA ^ gfValueB; internal int Addition(int gfValueA, int gfValueB) => gfValueA ^ gfValueB;
internal int Subtraction(int gfValueA, int gfValueB) => Addition(gfValueA, gfValueB); // Subtraction is same as addition. internal int Subtraction(int gfValueA, int gfValueB) => Addition(gfValueA, gfValueB); // Subtraction is same as addition.
/// <returns> /// <returns>
/// Product of two values. /// Product of two values.
/// In other words. a multiply b /// In other words. a multiply b
/// </returns> /// </returns>
internal int Product(int gfValueA, int gfValueB) internal int Product(int gfValueA, int gfValueB)
{ {
if (gfValueA == 0 || gfValueB == 0) if (gfValueA == 0 || gfValueB == 0)
{ {
return 0; return 0;
} }
if (gfValueA == 1) if (gfValueA == 1)
{ {
return gfValueB; return gfValueB;
} }
if (gfValueB == 1) if (gfValueB == 1)
{ {
return gfValueA; return gfValueA;
} }
return Exponent((Log(gfValueA) + Log(gfValueB)) % 255); return Exponent((Log(gfValueA) + Log(gfValueB)) % 255);
} }
/// <returns> /// <returns>
/// Quotient of two values. /// Quotient of two values.
/// In other words. a divided b /// In other words. a divided b
/// </returns> /// </returns>
internal int Quotient(int gfValueA, int gfValueB) internal int Quotient(int gfValueA, int gfValueB)
{ {
if (gfValueA == 0) if (gfValueA == 0)
{ {
return 0; return 0;
} }
if (gfValueB == 0) if (gfValueB == 0)
{ {
throw new ArgumentException($"{nameof(gfValueB)} cannot be zero."); throw new ArgumentException($"{nameof(gfValueB)} cannot be zero.");
} }
if (gfValueB == 1) if (gfValueB == 1)
{ {
return gfValueA; return gfValueA;
} }
return Exponent(Math.Abs(Log(gfValueA) - Log(gfValueB)) % 255); return Exponent(Math.Abs(Log(gfValueA) - Log(gfValueB)) % 255);
} }
} }

Some files were not shown because too many files have changed in this diff Show More