Commit of qrcode encoder and supporting modules.
Includes demo module with basic png and crypto for a full-cyle demo with a mobile phone.
This commit is contained in:
		
							
								
								
									
										79
									
								
								src/base32.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/base32.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
-module(base32).
 | 
			
		||||
 | 
			
		||||
-export([encode/1, decode/1]).
 | 
			
		||||
 | 
			
		||||
-define(BASE32_ALPHABET, {
 | 
			
		||||
	$A, $B, $C, $D, $E, $F, $G, $H, 
 | 
			
		||||
	$I, $J, $K, $L, $M, $N, $O, $P, 
 | 
			
		||||
	$Q, $R, $S, $T, $U, $V, $W, $X, 
 | 
			
		||||
	$Y, $Z, $2, $3, $4, $5, $6, $7
 | 
			
		||||
}).
 | 
			
		||||
 | 
			
		||||
%% RFC 4648
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
encode(Bin) when is_binary(Bin) ->
 | 
			
		||||
    Split = 5 * (byte_size(Bin) div 5),
 | 
			
		||||
    <<Main0:Split/binary, Rest/binary>> = Bin,
 | 
			
		||||
    Main = << <<(b32e(C))>> || <<C:5>> <= Main0 >>,
 | 
			
		||||
	encode0(Rest, Main).
 | 
			
		||||
 | 
			
		||||
encode0(<<>>, Acc) ->
 | 
			
		||||
	Acc;
 | 
			
		||||
encode0(<<A:5, B:3>>, Acc) ->
 | 
			
		||||
	<<Acc/binary, (b32e(A)), (b32e(B bsl 2)), "======">>;
 | 
			
		||||
encode0(<<A:5, B:5, C:5, D:1>>, Acc) ->
 | 
			
		||||
	<<Acc/binary, (b32e(A)), (b32e(B)), (b32e(C)), (b32e(D bsl 4)), "====">>;
 | 
			
		||||
encode0(<<A:5, B:5, C:5, D:5, E:4>>, Acc) ->
 | 
			
		||||
	<<Acc/binary, (b32e(A)), (b32e(B)), (b32e(C)), (b32e(D)), (b32e(E bsl 1)), "===">>;
 | 
			
		||||
encode0(<<A:5, B:5, C:5, D:5, E:5, F:5, G:2>>, Acc) ->
 | 
			
		||||
	<<Acc/binary, (b32e(A)), (b32e(B)), (b32e(C)), (b32e(D)), (b32e(E)), (b32e(F)), (b32e(G bsl 3)), "=">>.
 | 
			
		||||
	
 | 
			
		||||
%%
 | 
			
		||||
decode(Bin) when is_binary(Bin) ->
 | 
			
		||||
	Result = decode(Bin, <<>>),
 | 
			
		||||
	true = is_binary(Result),
 | 
			
		||||
	Result.
 | 
			
		||||
	
 | 
			
		||||
decode(<<X, "======">>, Acc) ->
 | 
			
		||||
	Bits = decode0(X) bsr 2,
 | 
			
		||||
	<<Acc/bits, Bits:3>>;
 | 
			
		||||
decode(<<X, "====">>, Acc) ->
 | 
			
		||||
	Bits = decode0(X) bsr 4,
 | 
			
		||||
	<<Acc/bits, Bits:1>>;
 | 
			
		||||
decode(<<X, "===">>, Acc) ->
 | 
			
		||||
	Bits = decode0(X) bsr 1,
 | 
			
		||||
	<<Acc/bits, Bits:4>>;
 | 
			
		||||
decode(<<X, "=">>, Acc) ->
 | 
			
		||||
	Bits = decode0(X) bsr 3,
 | 
			
		||||
	<<Acc/bits, Bits:2>>;
 | 
			
		||||
decode(<<A, Bin/binary>>, Acc) ->
 | 
			
		||||
	Bits = decode0(A),
 | 
			
		||||
	decode(Bin, <<Acc/bits, Bits:5>>);
 | 
			
		||||
decode(<<>>, Acc) ->
 | 
			
		||||
	true = is_binary(Acc),
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
decode0(X) when X >= $A, X =< $Z ->
 | 
			
		||||
	X - $A;
 | 
			
		||||
decode0(X) when X >= $2, X =< $7 ->
 | 
			
		||||
	X - $2 + 26.
 | 
			
		||||
 | 
			
		||||
b32e(X) ->
 | 
			
		||||
    element(X + 1, ?BASE32_ALPHABET).
 | 
			
		||||
 | 
			
		||||
			
 | 
			
		||||
							
								
								
									
										69
									
								
								src/bits.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/bits.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
-module(bits).
 | 
			
		||||
 | 
			
		||||
-export([reverse/1, duplicate/2, append/1, bitlist/1, bitstring/1, stringbits/1]).
 | 
			
		||||
-compile(export_all).
 | 
			
		||||
 | 
			
		||||
reverse(Bin) ->
 | 
			
		||||
	reverse(Bin, <<>>).
 | 
			
		||||
reverse(<<X:1, Bin/bits>>, Acc) ->
 | 
			
		||||
	reverse(Bin, <<X:1, Acc/bits>>);
 | 
			
		||||
reverse(<<>>, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
duplicate(Bin, N) ->
 | 
			
		||||
	duplicate(Bin, N, <<>>).
 | 
			
		||||
duplicate(Bin, N, Acc) when N > 0 ->
 | 
			
		||||
	duplicate(Bin, N - 1, <<Acc/bits, Bin/bits>>);
 | 
			
		||||
duplicate(_, 0, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
append(List) ->
 | 
			
		||||
	append(List, <<>>).
 | 
			
		||||
append([H|T], Acc) ->
 | 
			
		||||
	append(T, <<Acc/bits, H/bits>>);
 | 
			
		||||
append([], Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
bitlist(Bin) ->
 | 
			
		||||
	bitlist(Bin, []).
 | 
			
		||||
bitlist(<<X:1, Bin/bits>>, Acc) ->
 | 
			
		||||
	bitlist(Bin, [X|Acc]);
 | 
			
		||||
bitlist(<<>>, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
bitstring(Bin) ->
 | 
			
		||||
	bitstring(Bin, <<>>).
 | 
			
		||||
bitstring(<<0:1, Bin/bits>>, Acc) ->
 | 
			
		||||
	bitstring(Bin, <<Acc/binary, $0>>);
 | 
			
		||||
bitstring(<<1:1, Bin/bits>>, Acc) ->
 | 
			
		||||
	bitstring(Bin, <<Acc/binary, $1>>);
 | 
			
		||||
bitstring(<<>>, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
stringbits(Bin) ->
 | 
			
		||||
	stringbits(Bin, <<>>).
 | 
			
		||||
stringbits(<<$0, Bin/binary>>, Acc) ->
 | 
			
		||||
	stringbits(Bin, <<Acc/bits, 0:1>>);
 | 
			
		||||
stringbits(<<$1, Bin/binary>>, Acc) ->
 | 
			
		||||
	stringbits(Bin, <<Acc/bits, 1:1>>);
 | 
			
		||||
stringbits(<<>>, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
							
								
								
									
										192
									
								
								src/gf256.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								src/gf256.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,192 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
%% NOTE: This module implements finite field arithmetic over the galois field 
 | 
			
		||||
% GF(256) with a specified prime modulus. 
 | 
			
		||||
 | 
			
		||||
-module(gf256).
 | 
			
		||||
 | 
			
		||||
-export([field/1, add/3, subtract/3, multiply/3]).
 | 
			
		||||
-export([exponent/2, log/2, inverse/2, value/3]).
 | 
			
		||||
-export([monomial_product/4, polynomial_product/3, divide/3]).
 | 
			
		||||
 | 
			
		||||
-record(gf256, {exponent, log}).
 | 
			
		||||
 | 
			
		||||
% UNUSED
 | 
			
		||||
%-record(gf256poly, {field, coefficients}).
 | 
			
		||||
% NOTE: Implementation and use are greatly simplified by expressing polynomials
 | 
			
		||||
% simply as lists of coefficient values,  rather than explicit reification of 
 | 
			
		||||
% polynomial "objects".
 | 
			
		||||
 | 
			
		||||
-define(RANGE, 255).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
field(PrimeModulus) ->
 | 
			
		||||
	Exponent = exponent_table(1, PrimeModulus, []),
 | 
			
		||||
	Log = log_table(Exponent, 1, [0]),
 | 
			
		||||
	#gf256{exponent = Exponent, log = Log}.
 | 
			
		||||
%	
 | 
			
		||||
exponent_table(X, Modulus, Acc) when length(Acc) =< ?RANGE ->
 | 
			
		||||
	case X bsl 1 of
 | 
			
		||||
	V when V > ?RANGE ->
 | 
			
		||||
		X0 = V bxor Modulus;
 | 
			
		||||
	V ->
 | 
			
		||||
		X0 = V
 | 
			
		||||
	end,
 | 
			
		||||
	exponent_table(X0, Modulus, [X|Acc]);
 | 
			
		||||
exponent_table(_, _, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
%	
 | 
			
		||||
log_table(E, Count, Acc) when Count =< ?RANGE ->
 | 
			
		||||
	X = index_of(Count, 0, E),
 | 
			
		||||
	log_table(E, Count + 1, [X|Acc]);
 | 
			
		||||
log_table(_, _, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
%
 | 
			
		||||
index_of(X, Count, [X|_]) ->
 | 
			
		||||
	Count;
 | 
			
		||||
index_of(X, Count, [_|T]) ->
 | 
			
		||||
	index_of(X, Count + 1, T).
 | 
			
		||||
 | 
			
		||||
%% 
 | 
			
		||||
add(#gf256{}, A, B) when is_integer(A), is_integer(B) ->
 | 
			
		||||
	A bxor B;
 | 
			
		||||
add(#gf256{}, [0], B) when is_list(B) ->
 | 
			
		||||
	B;
 | 
			
		||||
add(#gf256{}, A, [0]) when is_list(A) ->
 | 
			
		||||
	A;
 | 
			
		||||
add(F = #gf256{}, A, B) when is_list(A), is_list(B) ->
 | 
			
		||||
	add(F, lists:reverse(A), lists:reverse(B), []).
 | 
			
		||||
	
 | 
			
		||||
add(F, [H|T], [H0|T0], Acc) ->
 | 
			
		||||
	add(F, T, T0, [H bxor H0 | Acc]);
 | 
			
		||||
add(F, [H|T], [], Acc) ->
 | 
			
		||||
	add(F, T, [], [H|Acc]);
 | 
			
		||||
add(F, [], [H|T], Acc) ->
 | 
			
		||||
	add(F, [], T, [H|Acc]);
 | 
			
		||||
add(F, [], [], [0|Acc]) ->
 | 
			
		||||
	add(F, [], [], Acc);
 | 
			
		||||
add(_, [], [], Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%% NOTE: Subtraction is the same as addition over a galois field.
 | 
			
		||||
subtract(F = #gf256{}, A, B) ->
 | 
			
		||||
	add(F, A, B).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
multiply(#gf256{}, 0, _) ->
 | 
			
		||||
	0;
 | 
			
		||||
multiply(#gf256{}, _, 0) ->
 | 
			
		||||
	0;
 | 
			
		||||
multiply(#gf256{}, 1, B) ->
 | 
			
		||||
	B;
 | 
			
		||||
multiply(#gf256{}, A, 1) ->
 | 
			
		||||
	A;
 | 
			
		||||
multiply(#gf256{exponent = E, log = L}, A, B) ->
 | 
			
		||||
	X = (lists:nth(A + 1, L) + lists:nth(B + 1, L)) rem ?RANGE,
 | 
			
		||||
	lists:nth(X + 1, E).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
exponent(#gf256{exponent = E}, X) ->
 | 
			
		||||
	lists:nth(X + 1, E).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
log(#gf256{log = L}, X) ->
 | 
			
		||||
	lists:nth(X + 1, L).
 | 
			
		||||
	
 | 
			
		||||
%%
 | 
			
		||||
inverse(#gf256{exponent = E, log = L}, X) ->
 | 
			
		||||
	lists:nth(256 - lists:nth(X + 1, L), E).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
value(#gf256{}, Poly, 0) ->
 | 
			
		||||
	lists:last(Poly);
 | 
			
		||||
value(F = #gf256{}, Poly, 1) ->
 | 
			
		||||
	lists:foldl(fun(X, Sum) -> gf256:add(F, X, Sum) end, 0, Poly);
 | 
			
		||||
value(F = #gf256{}, [H|T], X) ->
 | 
			
		||||
	value(F, T, X, H).
 | 
			
		||||
%
 | 
			
		||||
value(F, [H|T], X, Acc) ->
 | 
			
		||||
	Acc0 = multiply(F, X, Acc),
 | 
			
		||||
	Acc1 = add(F, Acc0, H),
 | 
			
		||||
	value(F, T, X, Acc1);
 | 
			
		||||
value(_, [], _, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%
 | 
			
		||||
monomial(#gf256{}, 0, Degree) when Degree >= 0 ->
 | 
			
		||||
	[0];
 | 
			
		||||
monomial(#gf256{}, Coeff, Degree) when Degree >= 0 ->
 | 
			
		||||
	[Coeff|lists:duplicate(Degree, 0)].
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
monomial_product(#gf256{}, _, 0, _) ->
 | 
			
		||||
	[0];
 | 
			
		||||
monomial_product(F, Poly, Coeff, Degree) ->
 | 
			
		||||
	monomial_product(F, Poly, Coeff, Degree, []).
 | 
			
		||||
%	
 | 
			
		||||
monomial_product(F, [H|T], C, D, Acc) ->
 | 
			
		||||
	P = gf256:multiply(F, H, C),
 | 
			
		||||
	monomial_product(F, T, C, D, [P|Acc]);
 | 
			
		||||
monomial_product(F, [], C, D, Acc) when D > 0 ->
 | 
			
		||||
	monomial_product(F, [], C, D - 1, [0|Acc]);
 | 
			
		||||
monomial_product(_, [], _, 0, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
polynomial_product(_, [0], _) ->
 | 
			
		||||
	[0];
 | 
			
		||||
polynomial_product(_, _, [0]) ->
 | 
			
		||||
	[0];
 | 
			
		||||
polynomial_product(F, P0, P1) ->
 | 
			
		||||
	polynomial_product0(F, P0, P1, [], []).
 | 
			
		||||
%	
 | 
			
		||||
polynomial_product0(F, [H|T], P1, P2, Acc) ->
 | 
			
		||||
	[H0|T0] = polynomial_product1(F, H, P1, P2, []),
 | 
			
		||||
	polynomial_product0(F, T, P1, T0, [H0|Acc]);
 | 
			
		||||
polynomial_product0(F, [], P1, [H|T], Acc) ->
 | 
			
		||||
	polynomial_product0(F, [], P1, T, [H|Acc]);
 | 
			
		||||
polynomial_product0(_, [], _, [], Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
%
 | 
			
		||||
polynomial_product1(_, _, [], [], Acc) ->
 | 
			
		||||
	lists:reverse(Acc);
 | 
			
		||||
polynomial_product1(F, X, [H|T], [], Acc) ->
 | 
			
		||||
	Coeff = polynomial_product2(F, X, H, 0),
 | 
			
		||||
	polynomial_product1(F, X, T, [], [Coeff|Acc]);
 | 
			
		||||
polynomial_product1(F, X, [H|T], [H0|T0], Acc) ->
 | 
			
		||||
	Coeff = polynomial_product2(F, X, H, H0),
 | 
			
		||||
	polynomial_product1(F, X, T, T0, [Coeff|Acc]).
 | 
			
		||||
 | 
			
		||||
polynomial_product2(F, X, H, H0) ->
 | 
			
		||||
	Coeff = multiply(F, X, H),
 | 
			
		||||
	add(F, H0, Coeff).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
divide(F = #gf256{}, A, B = [H|_]) when B =/= [0] ->
 | 
			
		||||
	IDLT = inverse(F, H),
 | 
			
		||||
	divide(F, IDLT, B, [0], A).
 | 
			
		||||
%
 | 
			
		||||
divide(F, IDLT, B, Q, R = [H|_]) when length(R) >= length(B), R =/= [0] ->
 | 
			
		||||
	Diff = length(R) - length(B),
 | 
			
		||||
	Scale = multiply(F, H, IDLT),
 | 
			
		||||
	M = monomial(F, Scale, Diff),
 | 
			
		||||
	Q0 = add(F, Q, M),
 | 
			
		||||
	Coeffs = monomial_product(F, B, Scale, Diff),
 | 
			
		||||
	R0 = add(F, R, Coeffs),
 | 
			
		||||
	divide(F, IDLT, B, Q0, R0);
 | 
			
		||||
divide(_, _, _, Q, R) ->
 | 
			
		||||
	{Q, R}.
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										194
									
								
								src/qrcode.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								src/qrcode.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,194 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
-module(qrcode).
 | 
			
		||||
 | 
			
		||||
-include("qrcode.hrl").
 | 
			
		||||
-include("qrcode_params.hrl").
 | 
			
		||||
 | 
			
		||||
-export([encode/1, decode/1]).
 | 
			
		||||
	
 | 
			
		||||
%%
 | 
			
		||||
decode(_Bin) -> 
 | 
			
		||||
	{error, not_implemented}.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
encode(Bin) ->
 | 
			
		||||
	encode(Bin, 'M').
 | 
			
		||||
%
 | 
			
		||||
encode(Bin, ECC) when is_binary(Bin) ->
 | 
			
		||||
	Params = choose_qr_params(Bin, ECC),
 | 
			
		||||
	Content = encode_content(Params, Bin),
 | 
			
		||||
	BlocksWithECC = generate_ecc_blocks(Params, Content),
 | 
			
		||||
	Codewords = interleave_blocks(BlocksWithECC),
 | 
			
		||||
	Matrix = qrcode_matrix:embed_data(Params, Codewords),
 | 
			
		||||
	MaskedMatrices = qrcode_mask:generate(Params, Matrix),
 | 
			
		||||
	Candidates = [qrcode_matrix:overlay_static(Params, M) || M <- MaskedMatrices],
 | 
			
		||||
	{MaskType, SelectedMatrix} = qrcode_mask:select(Candidates),
 | 
			
		||||
	Params0 = Params#qr_params{mask = MaskType},
 | 
			
		||||
	FMT = format_info_bits(Params0),
 | 
			
		||||
	VSN = version_info_bits(Params0),
 | 
			
		||||
	#qr_params{version = Version, dimension = Dim, ec_level = _ECC} = Params0,
 | 
			
		||||
	QRCode = qrcode_matrix:finalize(Dim, FMT, VSN, ?QUIET_ZONE, SelectedMatrix),
 | 
			
		||||
	%% NOTE: Added "API" record
 | 
			
		||||
	#qrcode{version = Version, ecc = ECC, dimension = Dim + ?QUIET_ZONE * 2, data = QRCode}.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
choose_qr_params(Bin, ECLevel) ->
 | 
			
		||||
	Mode = choose_encoding(Bin),
 | 
			
		||||
	{Mode, Version, ECCBlockDefs, Remainder} = choose_version(Mode, ECLevel, byte_size(Bin)),
 | 
			
		||||
	AlignmentCoords = alignment_patterns(Version),
 | 
			
		||||
	Dim = qrcode_matrix:dimension(Version),
 | 
			
		||||
	#qr_params{mode = Mode, version = Version, dimension = Dim, ec_level = ECLevel,
 | 
			
		||||
		block_defs = ECCBlockDefs, align_coords = AlignmentCoords, remainder = Remainder, data = Bin}.
 | 
			
		||||
	
 | 
			
		||||
%% NOTE: byte mode only (others removed)
 | 
			
		||||
choose_encoding(_Bin) ->
 | 
			
		||||
	byte.
 | 
			
		||||
	
 | 
			
		||||
%%
 | 
			
		||||
choose_version(Type, ECC, Length) ->
 | 
			
		||||
	choose_version(Type, ECC, Length, ?TABLES).
 | 
			
		||||
%
 | 
			
		||||
choose_version(byte, ECC, Length, [{{ECC, Version}, {_, _, Capacity, _}, ECCBlocks, Remainder}|_]) 
 | 
			
		||||
		when Capacity >= Length ->
 | 
			
		||||
	{byte, Version, ECCBlocks, Remainder};
 | 
			
		||||
choose_version(Type, ECC, Length, [_|T]) ->
 | 
			
		||||
	choose_version(Type, ECC, Length, T).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
encode_content(#qr_params{mode = Mode, version = Version}, Bin) ->
 | 
			
		||||
	encode_content(Mode, Version, Bin).
 | 
			
		||||
%
 | 
			
		||||
encode_content(byte, Version, Bin) ->
 | 
			
		||||
	encode_bytes(Version, Bin).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
generate_ecc_blocks(#qr_params{block_defs = ECCBlockDefs}, Bin) ->
 | 
			
		||||
	generate_ecc(Bin, ECCBlockDefs, []).
 | 
			
		||||
%
 | 
			
		||||
generate_ecc(Bin, [{C, L, D}|T], Acc) ->
 | 
			
		||||
	{Result, Bin0} = generate_ecc0(Bin, C, L, D, []),
 | 
			
		||||
	generate_ecc(Bin0, T, [Result|Acc]);
 | 
			
		||||
generate_ecc(<<>>, [], Acc) ->
 | 
			
		||||
	lists:flatten(lists:reverse(Acc)).
 | 
			
		||||
%	
 | 
			
		||||
generate_ecc0(Bin, Count, TotalLength, BlockLength, Acc) when byte_size(Bin) >= BlockLength, Count > 0 ->
 | 
			
		||||
	<<Block:BlockLength/binary, Bin0/binary>> = Bin,
 | 
			
		||||
	EC = qrcode_reedsolomon:encode(Block, TotalLength - BlockLength),
 | 
			
		||||
	generate_ecc0(Bin0, Count, TotalLength, BlockLength, [{Block, EC}|Acc]);
 | 
			
		||||
generate_ecc0(Bin, Count, TotalLength, BlockLength, Acc) when Count > 0 ->
 | 
			
		||||
	Block = pad_block(Bin, BlockLength),
 | 
			
		||||
	EC = qrcode_reedsolomon:encode(Block, TotalLength - BlockLength),
 | 
			
		||||
	{lists:reverse([{Block, EC}|Acc]), <<>>}; 
 | 
			
		||||
generate_ecc0(Bin, 0, _, _, Acc) ->
 | 
			
		||||
	{lists:reverse(Acc), Bin}.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
interleave_blocks(Blocks) ->
 | 
			
		||||
	Data = interleave_data(Blocks, <<>>),
 | 
			
		||||
	interleave_ecc(Blocks, Data).
 | 
			
		||||
 | 
			
		||||
interleave_data(Blocks, Bin) ->
 | 
			
		||||
	Data = [X || {X, _} <- Blocks],
 | 
			
		||||
	interleave_blocks(Data, [], Bin).
 | 
			
		||||
	
 | 
			
		||||
interleave_ecc(Blocks, Bin) ->
 | 
			
		||||
	Data = [X || {_, X} <- Blocks],
 | 
			
		||||
	interleave_blocks(Data, [], Bin).
 | 
			
		||||
	
 | 
			
		||||
interleave_blocks([], [], Bin) ->
 | 
			
		||||
	Bin;
 | 
			
		||||
interleave_blocks([], Acc, Bin) ->
 | 
			
		||||
	Acc0 = [X || X <- Acc, X =/= <<>>],
 | 
			
		||||
	interleave_blocks(lists:reverse(Acc0), [], Bin);
 | 
			
		||||
interleave_blocks([<<X, Data/binary>>|T], Acc, Bin) ->
 | 
			
		||||
	interleave_blocks(T, [Data|Acc], <<Bin/binary, X>>).
 | 
			
		||||
 | 
			
		||||
%
 | 
			
		||||
encode_bytes(Version, Bin) when is_binary(Bin) ->
 | 
			
		||||
	Size = size(Bin),
 | 
			
		||||
	CharacterCountBitSize = cci(?BYTE_MODE, Version),
 | 
			
		||||
	<<?BYTE_MODE:4, Size:CharacterCountBitSize, Bin/binary, 0:4>>.
 | 
			
		||||
%
 | 
			
		||||
pad_block(Bin, BlockLength) ->
 | 
			
		||||
	PadCount = BlockLength - byte_size(Bin),
 | 
			
		||||
	Pad = binary:copy(<<?DATA_PAD_0, ?DATA_PAD_1>>, PadCount bsr 1),
 | 
			
		||||
	case PadCount band 1 of
 | 
			
		||||
	0 ->
 | 
			
		||||
		Padding = Pad;
 | 
			
		||||
	1 ->
 | 
			
		||||
		Padding = <<Pad/binary, ?DATA_PAD_0>>
 | 
			
		||||
	end,
 | 
			
		||||
	<<Bin/binary, Padding/binary>>.
 | 
			
		||||
 | 
			
		||||
%% Table 25. Error correction level indicators
 | 
			
		||||
ecc('L') -> 1;
 | 
			
		||||
ecc('M') -> 0;
 | 
			
		||||
ecc('Q') -> 3;
 | 
			
		||||
ecc('H') -> 2.
 | 
			
		||||
 | 
			
		||||
% Table 5. Charset encoder
 | 
			
		||||
% NOTE: removed
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
alignment_patterns(Version) ->
 | 
			
		||||
	D = qrcode_matrix:dimension(Version),
 | 
			
		||||
	L = element(Version, ?ALIGNMENT_COORDINATES),
 | 
			
		||||
	L0 = [{X, Y} || X <- L, Y <- L],
 | 
			
		||||
	L1 = [{X, Y} || {X, Y} <- L0, is_finder_region(D, X, Y) =:= false],
 | 
			
		||||
	% Change the natural sort order so that rows have greater weight than columns
 | 
			
		||||
	F = fun 
 | 
			
		||||
		({_, Y}, {_, Y0}) when Y < Y0 ->
 | 
			
		||||
			true;
 | 
			
		||||
		({X, Y}, {X0, Y0}) when Y =:= Y0 andalso X =< X0 ->
 | 
			
		||||
			true;
 | 
			
		||||
		(_, _) ->
 | 
			
		||||
			false
 | 
			
		||||
		end,
 | 
			
		||||
	lists:sort(F, L1).
 | 
			
		||||
%
 | 
			
		||||
is_finder_region(D, X, Y) 
 | 
			
		||||
		when (X =< 8 andalso Y =< 8) 
 | 
			
		||||
		orelse (X =< 8 andalso Y >= D - 8)
 | 
			
		||||
		orelse (X >= D - 8 andalso Y =< 8) ->
 | 
			
		||||
	true;
 | 
			
		||||
is_finder_region(_, _, _) ->
 | 
			
		||||
	false.
 | 
			
		||||
 | 
			
		||||
%% Table 3. Number of bits in Character Count Indicator
 | 
			
		||||
cci(Mode, Version) when Version >= 1 andalso Version =< 40->
 | 
			
		||||
	{Mode, CC} = lists:keyfind(Mode, 1, ?CCI_BITSIZE),
 | 
			
		||||
	cci0(CC, Version).
 | 
			
		||||
%
 | 
			
		||||
cci0([X, _, _], Version) when Version =< 9 ->
 | 
			
		||||
	X;
 | 
			
		||||
cci0([_, X, _], Version) when Version =< 26 ->
 | 
			
		||||
	X;
 | 
			
		||||
cci0([_, _, X], _) ->
 | 
			
		||||
	X.
 | 
			
		||||
 | 
			
		||||
version_info_bits(#qr_params{version = Version}) when Version < 7 ->
 | 
			
		||||
	<<>>;
 | 
			
		||||
version_info_bits(#qr_params{version = Version}) when Version =< 40 ->
 | 
			
		||||
	BCH = qrcode_reedsolomon:bch_code(Version, ?VERSION_INFO_POLY),
 | 
			
		||||
	<<Version:6, BCH:12>>.
 | 
			
		||||
 | 
			
		||||
format_info_bits(#qr_params{ec_level = ECLevel, mask = MaskType}) ->
 | 
			
		||||
	Info = (ecc(ECLevel) bsl 3) bor MaskType,
 | 
			
		||||
	BCH = qrcode_reedsolomon:bch_code(Info, ?FORMAT_INFO_POLY),
 | 
			
		||||
	InfoWithEC = (Info bsl 10) bor BCH,
 | 
			
		||||
	Value = InfoWithEC bxor ?FORMAT_INFO_MASK,
 | 
			
		||||
	<<Value:15>>.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								src/qrcode.hrl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/qrcode.hrl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
%
 | 
			
		||||
% qrcode.hrl
 | 
			
		||||
 | 
			
		||||
%% API Record
 | 
			
		||||
-record(qrcode, {version, ecc, dimension, data}).
 | 
			
		||||
							
								
								
									
										136
									
								
								src/qrcode_demo.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/qrcode_demo.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,136 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
-module(qrcode_demo).
 | 
			
		||||
 | 
			
		||||
%% Shows how to achieve HOTP/SHA1 with a mobile phone using Google Authenticator.
 | 
			
		||||
%%
 | 
			
		||||
%% This module is a rag-bag of supporting functions, many of which are simplified 
 | 
			
		||||
%% extracts from the core libs (?_common, ?_crypto, ?_math, ?_image). This is to 
 | 
			
		||||
%% allow a full-cycle demo without requiring open-sourcing of the entire platform.
 | 
			
		||||
%% 
 | 
			
		||||
%% @ref QR Code: ISO/IEC 18004 (2000, 1st Edition)
 | 
			
		||||
 | 
			
		||||
%% Google Authenticator Phone App 
 | 
			
		||||
%% iPhone:  <http://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8>
 | 
			
		||||
%% Android: <https://market.android.com/details?id=com.google.android.apps.authenticator>
 | 
			
		||||
 | 
			
		||||
%% Google Authenticator URL Specification 
 | 
			
		||||
% @ref <http://code.google.com/p/google-authenticator/wiki/KeyUriFormat>
 | 
			
		||||
%  otpauth://TYPE/LABEL?PARAMETERS
 | 
			
		||||
%  TYPE: hotp | totp
 | 
			
		||||
%  LABEL: string() (usually email address)
 | 
			
		||||
%  PARAMETERS:
 | 
			
		||||
%    digits = 6 | 8 (default 6)
 | 
			
		||||
%    counter = integer() (hotp only, default 0?)
 | 
			
		||||
%    period = integer() (in seconds, totp only, default 30)
 | 
			
		||||
%    secret = binary() base32 encoded
 | 
			
		||||
%    algorithm = MD5 | SHA1 | SHA256 | SHA512 (default SHA1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-include("qrcode.hrl").
 | 
			
		||||
 | 
			
		||||
-compile(export_all).
 | 
			
		||||
 | 
			
		||||
-define(TTY(Term), io:format(user, "[~p] ~p~n", [?MODULE, Term])).
 | 
			
		||||
 | 
			
		||||
run() ->
 | 
			
		||||
	Passcode = crypto:sha(<<"password">>),
 | 
			
		||||
	run(<<"demo@mydomain.com">>, Passcode, 60).
 | 
			
		||||
 | 
			
		||||
run(Domain, Passcode, Seconds) ->
 | 
			
		||||
	PasscodeBase32 = base32:encode(Passcode),
 | 
			
		||||
	Period = list_to_binary(integer_to_list(Seconds)),
 | 
			
		||||
	Token = <<"otpauth://totp/", Domain/binary, "?period=", Period/binary, "&secret=", PasscodeBase32/binary>>,
 | 
			
		||||
	?TTY({token, Token}),
 | 
			
		||||
	QRCode = qrcode:encode(Token),
 | 
			
		||||
	Image = simple_png_encode(QRCode),
 | 
			
		||||
	Filename = "qrcode.png",
 | 
			
		||||
	ok = file:write_file(Filename, Image),
 | 
			
		||||
	?TTY({image, filename:absname(Filename)}),
 | 
			
		||||
	QRCode.
 | 
			
		||||
 | 
			
		||||
%% Very simple PNG encoder for demo purposes
 | 
			
		||||
simple_png_encode(#qrcode{dimension = Dim, data = Data}) ->
 | 
			
		||||
	MAGIC = <<137, 80, 78, 71, 13, 10, 26, 10>>,
 | 
			
		||||
	Size = Dim * 8,
 | 
			
		||||
	IHDR = png_chunk(<<"IHDR">>, <<Size:32, Size:32, 8:8, 2:8, 0:24>>), 
 | 
			
		||||
	PixelData = get_pixel_data(Dim, Data),
 | 
			
		||||
	IDAT = png_chunk(<<"IDAT">>, PixelData),
 | 
			
		||||
	IEND = png_chunk(<<"IEND">>, <<>>),
 | 
			
		||||
	<<MAGIC/binary, IHDR/binary, IDAT/binary, IEND/binary>>.
 | 
			
		||||
 | 
			
		||||
png_chunk(Type, Bin) ->
 | 
			
		||||
	Length = byte_size(Bin),
 | 
			
		||||
	CRC = erlang:crc32(<<Type/binary, Bin/binary>>),
 | 
			
		||||
	<<Length:32, Type/binary, Bin/binary, CRC:32>>.
 | 
			
		||||
 | 
			
		||||
get_pixel_data(Dim, Data) ->
 | 
			
		||||
	Pixels = get_pixels(Data, 0, Dim, <<>>),
 | 
			
		||||
	zlib:compress(Pixels).
 | 
			
		||||
 | 
			
		||||
get_pixels(<<>>, Dim, Dim, Acc) ->
 | 
			
		||||
	Acc;
 | 
			
		||||
get_pixels(Bin, Count, Dim, Acc) ->
 | 
			
		||||
	<<RowBits:Dim/bits, Bits/bits>> = Bin,
 | 
			
		||||
	Row = get_pixels0(RowBits, <<0>>), % row filter byte
 | 
			
		||||
	FullRow = binary:copy(Row, 8),
 | 
			
		||||
	get_pixels(Bits, Count + 1, Dim, <<Acc/binary, FullRow/binary>>).
 | 
			
		||||
	
 | 
			
		||||
get_pixels0(<<1:1, Bits/bits>>, Acc) ->
 | 
			
		||||
	Black = binary:copy(<<0>>, 24),
 | 
			
		||||
	get_pixels0(Bits, <<Acc/binary, Black/binary>>);
 | 
			
		||||
get_pixels0(<<0:1, Bits/bits>>, Acc) ->
 | 
			
		||||
	White = binary:copy(<<255>>, 24),
 | 
			
		||||
	get_pixels0(Bits, <<Acc/binary, White/binary>>);
 | 
			
		||||
get_pixels0(<<>>, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
totp() ->
 | 
			
		||||
	Key = crypto:sha(<<"password">>),	
 | 
			
		||||
	totp(Key, 60).
 | 
			
		||||
totp(Key, Period) ->
 | 
			
		||||
	T = unow() div Period,
 | 
			
		||||
	{hotp(Key, T - 1), hotp(Key, T), hotp(Key, T + 1)}.
 | 
			
		||||
%% RFC-4226 "HOTP: An HMAC-Based One-Time Password Algorithm"
 | 
			
		||||
%% @ref <http://tools.ietf.org/html/rfc4226>
 | 
			
		||||
hotp(Key, Count) when is_binary(Key), is_integer(Count) ->
 | 
			
		||||
	HS = crypto:sha_mac(Key, <<Count:64>>),
 | 
			
		||||
	<<_:19/binary, _:4, Offset:4>> = HS,
 | 
			
		||||
	<<_:Offset/binary, _:1, P:31, _/binary>> = HS,
 | 
			
		||||
	HOTP = integer_to_list(P rem 1000000),
 | 
			
		||||
	Pad = lists:duplicate(6 - length(HOTP), $0),
 | 
			
		||||
	list_to_binary([Pad, HOTP]).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-define(UNIX_TIME_ZERO, 62167219200).
 | 
			
		||||
 | 
			
		||||
unow() ->
 | 
			
		||||
	calendar:datetime_to_gregorian_seconds(calendar:universal_time()) - ?UNIX_TIME_ZERO.
 | 
			
		||||
	
 | 
			
		||||
hex(Bin) when is_binary(Bin) ->
 | 
			
		||||
	hex(Bin, <<>>).	
 | 
			
		||||
 | 
			
		||||
hex(<<A:4, B:4, Rest/binary>>, Acc) ->
 | 
			
		||||
	U = hex_digit(A),
 | 
			
		||||
	L = hex_digit(B),
 | 
			
		||||
	hex(Rest, <<Acc/binary, U, L>>);
 | 
			
		||||
hex(<<>>, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
hex_digit(D) when D >= 0, D =< 9 -> 
 | 
			
		||||
	$0 + D;
 | 
			
		||||
hex_digit(D) when D >= 10, D =< 16 -> 
 | 
			
		||||
	$a + D - 10.
 | 
			
		||||
							
								
								
									
										267
									
								
								src/qrcode_mask.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								src/qrcode_mask.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,267 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
-module(qrcode_mask).
 | 
			
		||||
 | 
			
		||||
-include("qrcode_params.hrl").
 | 
			
		||||
 | 
			
		||||
-export([generate/2, select/1]).
 | 
			
		||||
 | 
			
		||||
-define(PENALTY_RULE_1, 3).
 | 
			
		||||
-define(PENALTY_RULE_2, 3).
 | 
			
		||||
-define(PENALTY_RULE_3, 40).
 | 
			
		||||
-define(PENALTY_RULE_4, 10).
 | 
			
		||||
 | 
			
		||||
%% Generates all eight masked versions of the bit matrix
 | 
			
		||||
generate(#qr_params{dimension = Dim}, Matrix) ->
 | 
			
		||||
	Sequence = lists:seq(0, 7),
 | 
			
		||||
	Functions = [mask(X) || X <- Sequence],
 | 
			
		||||
	Masks = [generate_mask(Dim, MF) || MF <- Functions],
 | 
			
		||||
	[apply_mask(Matrix, Mask, []) || Mask <- Masks].
 | 
			
		||||
	
 | 
			
		||||
%% Selects the lowest penalty candidate from a list of bit matrices
 | 
			
		||||
select([H|T]) ->
 | 
			
		||||
	Score = score_candidate(H),
 | 
			
		||||
	select_candidate(T, 0, 0, Score, H).
 | 
			
		||||
 | 
			
		||||
%% Internal
 | 
			
		||||
 | 
			
		||||
%
 | 
			
		||||
generate_mask(Max, MF) ->
 | 
			
		||||
	Sequence = lists:seq(0, Max - 1),
 | 
			
		||||
	[generate_mask(Sequence, Y, MF) || Y <- Sequence].
 | 
			
		||||
generate_mask(Sequence, Y, MF) ->
 | 
			
		||||
	[case MF(X, Y) of true -> 1; false -> 0 end || X <- Sequence].
 | 
			
		||||
 | 
			
		||||
apply_mask([H|T], [H0|T0], Acc) ->
 | 
			
		||||
	Row = apply_mask0(H, H0, []),
 | 
			
		||||
	apply_mask(T, T0, [Row|Acc]);
 | 
			
		||||
apply_mask([], [], Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
	
 | 
			
		||||
apply_mask0([H|T], [H0|T0], Acc) when is_integer(H) ->
 | 
			
		||||
	apply_mask0(T, T0, [H bxor H0|Acc]);
 | 
			
		||||
apply_mask0([H|T], [_|T0], Acc) ->
 | 
			
		||||
	apply_mask0(T, T0, [H|Acc]);
 | 
			
		||||
apply_mask0([], [], Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
 | 
			
		||||
% (i + j) mod 2 = 0
 | 
			
		||||
mask(0) -> 
 | 
			
		||||
	fun(X, Y) -> (X + Y) rem 2 =:= 0 end;
 | 
			
		||||
% i mod 2 = 0
 | 
			
		||||
mask(1) -> 
 | 
			
		||||
	fun(_X, Y) -> Y rem 2 =:= 0 end;
 | 
			
		||||
% j mod 3 = 0
 | 
			
		||||
mask(2) -> 
 | 
			
		||||
	fun(X, _Y) -> X rem 3 =:= 0 end;
 | 
			
		||||
% (i + j) mod 3 = 0
 | 
			
		||||
mask(3) -> 
 | 
			
		||||
	fun(X, Y) -> (X + Y) rem 3 =:= 0 end;
 | 
			
		||||
% ((i div 2) + (j div 3)) mod 2 = 0
 | 
			
		||||
mask(4) -> 
 | 
			
		||||
	fun(X, Y) -> (X div 3 + Y div 2) rem 2 =:= 0 end;
 | 
			
		||||
%101 (i * j) mod 2 + (i *j) mod 3 = 0
 | 
			
		||||
mask(5) -> 
 | 
			
		||||
	fun(X, Y) -> Sum = X * Y, Sum rem 2 + Sum rem 3 =:= 0 end;
 | 
			
		||||
% ((i * j) mod 2 + (i* j) mod 3) mod 2 = 0
 | 
			
		||||
mask(6) -> 
 | 
			
		||||
	fun(X, Y) -> Sum = X * Y, (Sum rem 2 + Sum rem 3) rem 2 =:= 0 end;
 | 
			
		||||
%((i * j) mod 3 + (i + j) mod 2) mod 2 = 0
 | 
			
		||||
mask(7) -> 
 | 
			
		||||
	fun(X, Y) -> ((X * Y rem 3) + ((X + Y) rem 2)) rem 2 =:= 0 end.
 | 
			
		||||
	
 | 
			
		||||
select_candidate([H|T], Count, Mask, Score, C) ->
 | 
			
		||||
	case score_candidate(H) of
 | 
			
		||||
	X when X < Score ->
 | 
			
		||||
		select_candidate(T, Count + 1, Count + 1, X, H);
 | 
			
		||||
	_ ->
 | 
			
		||||
		select_candidate(T, Count + 1, Mask, Score, C)
 | 
			
		||||
	end;
 | 
			
		||||
select_candidate([], _, Mask, _Score, C) ->
 | 
			
		||||
	%?TTY({selected, Mask, {score, Score}}),
 | 
			
		||||
	{Mask, C}.
 | 
			
		||||
 | 
			
		||||
score_candidate(C) ->
 | 
			
		||||
	Rule1 = apply_penalty_rule_1(C),
 | 
			
		||||
	Rule2 = apply_penalty_rule_2(C),
 | 
			
		||||
	Rule3 = apply_penalty_rule_3(C),
 | 
			
		||||
	Rule4 = apply_penalty_rule_4(C),
 | 
			
		||||
	Total = Rule1 + Rule2 + Rule3 + Rule4,
 | 
			
		||||
	%?TTY({score, Total, [Rule1, Rule2, Rule3, Rule4]}),
 | 
			
		||||
	Total.
 | 
			
		||||
	
 | 
			
		||||
%% Section 8.2.2
 | 
			
		||||
apply_penalty_rule_1(Candidate) ->
 | 
			
		||||
	ScoreRows = rule1(Candidate, 0),
 | 
			
		||||
	ScoreCols = rule1(rows_to_columns(Candidate), 0),
 | 
			
		||||
	ScoreRows + ScoreCols.
 | 
			
		||||
%
 | 
			
		||||
rule1([Row|T], Score) ->
 | 
			
		||||
	Score0 = rule1_row(Row, Score),
 | 
			
		||||
	rule1(T, Score0);
 | 
			
		||||
rule1([], Score) ->
 | 
			
		||||
	Score.
 | 
			
		||||
%
 | 
			
		||||
rule1_row(L = [H|_], Score) ->
 | 
			
		||||
	F = fun
 | 
			
		||||
		(1) when H =:= 1 ->
 | 
			
		||||
			true;
 | 
			
		||||
		(1) ->
 | 
			
		||||
			false;
 | 
			
		||||
		(_) when H =:= 0 orelse is_integer(H) =:= false ->
 | 
			
		||||
			true;
 | 
			
		||||
		(_) ->
 | 
			
		||||
			false
 | 
			
		||||
		end,
 | 
			
		||||
	{H0,T0} = lists:splitwith(F, L),
 | 
			
		||||
	case length(H0) of 
 | 
			
		||||
	Repeats when Repeats >= 5 ->
 | 
			
		||||
		Penalty = ?PENALTY_RULE_1 + Repeats - 5,
 | 
			
		||||
		rule1_row(T0, Score + Penalty);
 | 
			
		||||
	_ ->
 | 
			
		||||
		rule1_row(T0, Score)
 | 
			
		||||
	end;
 | 
			
		||||
rule1_row([], Score) ->
 | 
			
		||||
	Score.
 | 
			
		||||
 | 
			
		||||
%% TODO
 | 
			
		||||
apply_penalty_rule_2(_M = [H, H0|T]) ->
 | 
			
		||||
%	?TTY(M),
 | 
			
		||||
	Blocks = rule2(1, 1, H, H0, [H0|T], []),
 | 
			
		||||
	Blocks0 = composite_blocks(Blocks, []),
 | 
			
		||||
	Blocks1 = composite_blocks(Blocks0, []),
 | 
			
		||||
%	?TTY(Blocks1),
 | 
			
		||||
	score_blocks(Blocks1, 0).
 | 
			
		||||
 | 
			
		||||
score_blocks([{_, {M, N}, _}|T], Acc) ->
 | 
			
		||||
	Score = ?PENALTY_RULE_2 * (M - 1) * (N - 1),
 | 
			
		||||
	score_blocks(T, Acc + Score);
 | 
			
		||||
score_blocks([], Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
	
 | 
			
		||||
rule2(X, Y, [H, H|T], [H, H|T0], Rows, Acc) ->
 | 
			
		||||
	rule2(X + 1, Y, [H|T], [H|T0], Rows, [{{X, Y}, {2, 2}, H}|Acc]);
 | 
			
		||||
rule2(X, Y, [_|T], [_|T0], Rows, Acc) ->
 | 
			
		||||
	rule2(X + 1, Y, T, T0, Rows, Acc);
 | 
			
		||||
rule2(_, Y, [], [], [H, H0|T], Acc) ->
 | 
			
		||||
	rule2(1, Y + 1, H, H0, [H0|T], Acc);
 | 
			
		||||
rule2(_, _, [], [], [_], Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
 | 
			
		||||
composite_blocks([H|T], Acc) ->
 | 
			
		||||
	{H0, T0} = composite_block(H, T, []),
 | 
			
		||||
	composite_blocks(T0, [H0|Acc]);
 | 
			
		||||
composite_blocks([], Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
 | 
			
		||||
composite_block(B, [H|T], Acc) ->
 | 
			
		||||
	case combine_block(B, H) of
 | 
			
		||||
	false ->
 | 
			
		||||
		composite_block(B, T, [H|Acc]);
 | 
			
		||||
	B0 ->
 | 
			
		||||
		composite_block(B0, T, Acc)
 | 
			
		||||
	end;
 | 
			
		||||
composite_block(B, [], Acc) ->
 | 
			
		||||
	{B, lists:reverse(Acc)}.
 | 
			
		||||
 | 
			
		||||
% Does Block 0 contain the Block 1 coordinate?
 | 
			
		||||
combine_block(B = {{X, Y}, {SX, SY}, _}, B0 = {{X0, Y0}, _, _}) 
 | 
			
		||||
		when X0 < X + SX orelse Y0 < Y + SY  ->
 | 
			
		||||
	combine_block0(B, B0);
 | 
			
		||||
combine_block(_, _) ->
 | 
			
		||||
	false.
 | 
			
		||||
	
 | 
			
		||||
% are they same valued?
 | 
			
		||||
combine_block0(B = {_, _, V}, B0 = {_, _, V0}) 
 | 
			
		||||
	when V =:= V0 orelse (V =/= 1 andalso V0 =/= 1) ->
 | 
			
		||||
	combine_block1(B, B0);
 | 
			
		||||
combine_block0(_, _) ->
 | 
			
		||||
	false.
 | 
			
		||||
	
 | 
			
		||||
% is B extended by B0 horizontally?
 | 
			
		||||
combine_block1({{X, Y}, {SX, SY}, V}, {{X0, Y}, {SX0, SY}, _}) when X0 =:= X + SX - 1 ->
 | 
			
		||||
	{{X, Y}, {SX + SX0 - 1, SY}, V};
 | 
			
		||||
% is B extended by B0 vertically?
 | 
			
		||||
combine_block1({{X, Y}, {SX, SY}, V}, {{X, Y0}, {SX, SY0}, _}) when Y0 =:= Y + SY - 1 ->
 | 
			
		||||
	{{X, Y}, {SX, SY + SY0 - 1}, V};
 | 
			
		||||
combine_block1(_, _) ->
 | 
			
		||||
	false.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
apply_penalty_rule_3(Candidate) -> 
 | 
			
		||||
	RowScores = [rule3(Row, 0) || Row <- Candidate],
 | 
			
		||||
	ColumnScores = [rule3(Col, 0) || Col <- rows_to_columns(Candidate)],
 | 
			
		||||
	lists:sum(RowScores) + lists:sum(ColumnScores).
 | 
			
		||||
%
 | 
			
		||||
rule3(Row = [1|T], Score) ->
 | 
			
		||||
	Ones = lists:takewhile(fun(X) -> X =:= 1 end, Row),
 | 
			
		||||
	Scale = length(Ones),
 | 
			
		||||
	case Scale * 7 of
 | 
			
		||||
	Length when Length > length(Row) ->
 | 
			
		||||
		rule3(T, Score);
 | 
			
		||||
	Length ->
 | 
			
		||||
		case is_11311_pattern(lists:sublist(Row, Length), Scale) of
 | 
			
		||||
		true ->
 | 
			
		||||
			rule3(T, Score + ?PENALTY_RULE_3);
 | 
			
		||||
		false ->
 | 
			
		||||
			rule3(T, Score)
 | 
			
		||||
		end
 | 
			
		||||
	end;
 | 
			
		||||
rule3([_|T], Score) ->
 | 
			
		||||
	rule3(T, Score);
 | 
			
		||||
rule3([], Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
%
 | 
			
		||||
is_11311_pattern(List, Scale) ->
 | 
			
		||||
	List0 = lists:map(fun(X) when X =:= 1 -> 1; (_) -> 0 end, List), 
 | 
			
		||||
	Result = condense(List0, Scale, []),
 | 
			
		||||
	Result =:= [1,0,1,1,1,0,1].
 | 
			
		||||
%
 | 
			
		||||
condense([], _, Acc) ->
 | 
			
		||||
	lists:reverse(Acc);
 | 
			
		||||
condense(L, Scale, Acc) ->
 | 
			
		||||
	{H, T} = lists:split(Scale, L),
 | 
			
		||||
	case lists:sum(H) of
 | 
			
		||||
	Scale ->
 | 
			
		||||
		condense(T, Scale, [1|Acc]);
 | 
			
		||||
	0 ->
 | 
			
		||||
		condense(T, Scale, [0|Acc]);
 | 
			
		||||
	_ ->
 | 
			
		||||
		undefined
 | 
			
		||||
	end.
 | 
			
		||||
	
 | 
			
		||||
%%
 | 
			
		||||
apply_penalty_rule_4(Candidate) ->
 | 
			
		||||
	Proportion = rule4(Candidate, 0, 0),
 | 
			
		||||
	%?TTY({proportion, Proportion}),
 | 
			
		||||
	?PENALTY_RULE_4 * (trunc(abs(Proportion * 100 - 50)) div 5).
 | 
			
		||||
%	
 | 
			
		||||
rule4([H|T], Dark, All) ->
 | 
			
		||||
	All0 = All + length(H),
 | 
			
		||||
	Dark0 = Dark + length([X || X <- H, X =:= 1]),
 | 
			
		||||
	rule4(T, Dark0, All0);
 | 
			
		||||
rule4([], Dark, All) ->
 | 
			
		||||
	Dark / All.
 | 
			
		||||
	
 | 
			
		||||
%
 | 
			
		||||
rows_to_columns(L) ->
 | 
			
		||||
	rows_to_columns(L, []).
 | 
			
		||||
rows_to_columns([[]|_], Acc) ->
 | 
			
		||||
	lists:reverse(Acc);
 | 
			
		||||
rows_to_columns(L, Acc) ->
 | 
			
		||||
	Heads = [H || [H|_] <- L],
 | 
			
		||||
	Tails = [T || [_|T] <- L],
 | 
			
		||||
	rows_to_columns(Tails, [Heads|Acc]).
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										263
									
								
								src/qrcode_matrix.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/qrcode_matrix.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,263 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
-module(qrcode_matrix).
 | 
			
		||||
 | 
			
		||||
-include("qrcode_params.hrl").
 | 
			
		||||
 | 
			
		||||
-export([dimension/1, template/1, embed_data/2, overlay_static/2, finalize/5]).
 | 
			
		||||
 | 
			
		||||
-define(FINDER_BITS, <<6240274796270654599595212063015969838585429452563217548030:192>>).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
dimension(Version) 
 | 
			
		||||
		when Version > 0 
 | 
			
		||||
		andalso Version < 41 ->
 | 
			
		||||
	17 + (Version * 4).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
template(#qr_params{version = Version, align_coords = AC}) ->
 | 
			
		||||
	template(Version, AC).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
embed_data(#qr_params{version = Version, align_coords = AC, remainder = Rem}, Codewords) ->
 | 
			
		||||
	FlippedTemplate = flip(template(Version, AC)),
 | 
			
		||||
	FlippedMatrix = embed_data(FlippedTemplate, <<Codewords/binary, 0:Rem>>, []),
 | 
			
		||||
	flip(FlippedMatrix).
 | 
			
		||||
	
 | 
			
		||||
%%
 | 
			
		||||
overlay_static(#qr_params{version = Version, align_coords = AC}, Matrix) ->
 | 
			
		||||
	F = finder_bits(),
 | 
			
		||||
	T = timing_bits(Version, AC),
 | 
			
		||||
	A = alignment_bits(AC),
 | 
			
		||||
	overlay_static(Matrix, F, T, A, []).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
finalize(Dim, FMT, VSN, QZ, Matrix) ->
 | 
			
		||||
	M = format_bits(FMT),
 | 
			
		||||
	V = version_bits(VSN),
 | 
			
		||||
	FinalMatrix = overlay_format(Matrix, M, V, []),
 | 
			
		||||
	QBitLength = (Dim + QZ * 2) * QZ,
 | 
			
		||||
	Q = <<0:QBitLength>>,
 | 
			
		||||
	Bin = encode_bits(FinalMatrix, QZ, Q),
 | 
			
		||||
	<<Bin/bits, Q/bits>>.
 | 
			
		||||
 | 
			
		||||
%% Internal
 | 
			
		||||
 | 
			
		||||
%% 
 | 
			
		||||
template(Version, AC) ->
 | 
			
		||||
	Dim = dimension(Version),
 | 
			
		||||
	template(1, Dim, AC, []).
 | 
			
		||||
%
 | 
			
		||||
template(Y, Max, AC, Acc) when Y =< Max->
 | 
			
		||||
	Row = template_row(1, Y, Max, AC, []),
 | 
			
		||||
	template(Y + 1, Max, AC, [Row|Acc]);
 | 
			
		||||
template(_, _, _, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
%
 | 
			
		||||
template_row(X, Y, Max, AC, Acc) when X =< Max ->
 | 
			
		||||
	Ref = template_ref(X, Y, Max, AC),
 | 
			
		||||
	template_row(X + 1, Y, Max, AC, [Ref|Acc]);
 | 
			
		||||
template_row(_, _, _, _, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
%	
 | 
			
		||||
template_ref(X, Y, Max, _AC) 
 | 
			
		||||
		when (X =< 8 andalso Y =< 8)
 | 
			
		||||
		orelse (X =< 8 andalso Y > Max - 8)
 | 
			
		||||
		orelse (X > Max - 8 andalso Y =< 8) ->
 | 
			
		||||
	f;
 | 
			
		||||
template_ref(X, Y, Max, _AC) 
 | 
			
		||||
		when (X =:= 9 andalso Y =/= 7 andalso (Y =< 9 orelse Max - Y =< 7))
 | 
			
		||||
		orelse (Y =:= 9 andalso X =/= 7 andalso (X =< 9 orelse Max - X =< 7)) ->
 | 
			
		||||
	m;
 | 
			
		||||
template_ref(X, Y, Max, _AC) 
 | 
			
		||||
		when Max >= 45 
 | 
			
		||||
		andalso ((X < 7 andalso Max - Y =< 10) 
 | 
			
		||||
		orelse (Max - X =< 10 andalso Y < 7)) ->
 | 
			
		||||
	v;
 | 
			
		||||
template_ref(X, Y, Max, AC) ->
 | 
			
		||||
	case is_alignment_bit(X, Y, AC) of
 | 
			
		||||
	true -> 
 | 
			
		||||
		a;
 | 
			
		||||
	false ->
 | 
			
		||||
		template_ref0(X, Y, Max)
 | 
			
		||||
	end.
 | 
			
		||||
%
 | 
			
		||||
template_ref0(X, Y, _)
 | 
			
		||||
		when X =:= 7 
 | 
			
		||||
		orelse Y =:= 7 ->
 | 
			
		||||
	t;
 | 
			
		||||
template_ref0(_, _, _) ->
 | 
			
		||||
	d.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
is_alignment_bit(X, Y, [{Xa, Ya}|_]) 
 | 
			
		||||
		when (X >= Xa - 2 
 | 
			
		||||
		andalso X =< Xa + 2 
 | 
			
		||||
		andalso Y >= Ya - 2 
 | 
			
		||||
		andalso Y =< Ya + 2) ->
 | 
			
		||||
	true;
 | 
			
		||||
is_alignment_bit(X, Y, [_|T]) ->
 | 
			
		||||
	is_alignment_bit(X, Y, T);
 | 
			
		||||
is_alignment_bit(_X, _Y, []) ->
 | 
			
		||||
	false.
 | 
			
		||||
	
 | 
			
		||||
% deal with row 7 exceptional case
 | 
			
		||||
embed_data([HA, HB, H, HC, HD|T], Codewords, Acc) when length(T) =:= 4 -> % skip row 7
 | 
			
		||||
	{HA0, HB0, Codewords0} = embed_data(HA, HB, Codewords, [], []),	
 | 
			
		||||
	{HC0, HD0, Codewords1} = embed_data_reversed(HC, HD, Codewords0),	
 | 
			
		||||
	embed_data(T, Codewords1, [HD0, HC0, H, HB0, HA0|Acc]);
 | 
			
		||||
% normal case
 | 
			
		||||
embed_data([HA, HB, HC, HD|T], Codewords, Acc) ->
 | 
			
		||||
	{HA0, HB0, Codewords0} = embed_data(HA, HB, Codewords, [], []),	
 | 
			
		||||
	{HC0, HD0, Codewords1} = embed_data_reversed(HC, HD, Codewords0),	
 | 
			
		||||
	embed_data(T, Codewords1, [HD0, HC0, HB0, HA0|Acc]);
 | 
			
		||||
embed_data([], <<>>, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
	
 | 
			
		||||
embed_data([d|T0], [d|T1], <<A:1, B:1, Codewords/bits>>, StreamA, StreamB) ->
 | 
			
		||||
	embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]);
 | 
			
		||||
embed_data([d|T0], [B|T1], <<A:1, Codewords/bits>>, StreamA, StreamB) ->
 | 
			
		||||
	embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]);
 | 
			
		||||
embed_data([A|T0], [d|T1], <<B:1, Codewords/bits>>, StreamA, StreamB) ->
 | 
			
		||||
	embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]);
 | 
			
		||||
embed_data([A|T0], [B|T1], Codewords, StreamA, StreamB) ->
 | 
			
		||||
	embed_data(T0, T1, Codewords, [A|StreamA], [B|StreamB]);
 | 
			
		||||
embed_data([], [], Codewords, StreamA, StreamB) ->
 | 
			
		||||
	{lists:reverse(StreamA), lists:reverse(StreamB), Codewords}.
 | 
			
		||||
	
 | 
			
		||||
embed_data_reversed(A, B, Codewords) ->
 | 
			
		||||
	{A0, B0, Codewords0} = embed_data(lists:reverse(A), lists:reverse(B), Codewords, [], []),
 | 
			
		||||
	{lists:reverse(A0), lists:reverse(B0), Codewords0}.
 | 
			
		||||
 | 
			
		||||
%
 | 
			
		||||
overlay_static([H|L], F, T, A, Acc) ->
 | 
			
		||||
	{F0, T0, A0, Row} = overlay0(H, F, T, A, []),
 | 
			
		||||
	overlay_static(L, F0, T0, A0, [Row|Acc]);
 | 
			
		||||
overlay_static([], <<>>, <<>>, <<>>, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
%
 | 
			
		||||
overlay0([f|L], <<F0:1, F/bits>>, T, A, Acc) ->
 | 
			
		||||
	overlay0(L, F, T, A, [F0|Acc]);	
 | 
			
		||||
overlay0([t|L], F, <<T0:1, T/bits>>, A, Acc) ->
 | 
			
		||||
	overlay0(L, F, T, A, [T0|Acc]);
 | 
			
		||||
overlay0([a|L], F, T, <<A0:1, A/bits>>, Acc) ->
 | 
			
		||||
	overlay0(L, F, T, A, [A0|Acc]);
 | 
			
		||||
overlay0([H|L], F, T, A, Acc) ->
 | 
			
		||||
	overlay0(L, F, T, A, [H|Acc]);
 | 
			
		||||
overlay0([], F, T, A, Acc) ->
 | 
			
		||||
	{F, T, A, lists:reverse(Acc)}.
 | 
			
		||||
 | 
			
		||||
%
 | 
			
		||||
encode_bits([H|T], QZ, Acc) ->
 | 
			
		||||
	Acc0 = encode_bits0(H, <<Acc/bits, 0:QZ>>),
 | 
			
		||||
	encode_bits(T, QZ, <<Acc0/bits, 0:QZ>>);
 | 
			
		||||
encode_bits([], _, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
	
 | 
			
		||||
encode_bits0([H|T], Acc) when is_integer(H) ->
 | 
			
		||||
	encode_bits0(T, <<Acc/bits, H:1>>);
 | 
			
		||||
encode_bits0([], Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
%
 | 
			
		||||
overlay_format([H|L], M, V, Acc) ->
 | 
			
		||||
	{M0, V0, Row} = overlay1(H, M, V, []),
 | 
			
		||||
	overlay_format(L, M0, V0, [Row|Acc]);
 | 
			
		||||
overlay_format([], <<>>, <<>>, Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
%
 | 
			
		||||
overlay1([m|L], <<M0:1, M/bits>>, V, Acc) ->
 | 
			
		||||
	overlay1(L, M, V, [M0|Acc]);	
 | 
			
		||||
overlay1([v|L], M, <<V0:1, V/bits>>, Acc) ->
 | 
			
		||||
	overlay1(L, M, V, [V0|Acc]);
 | 
			
		||||
overlay1([H|L], M, V, Acc) ->
 | 
			
		||||
	overlay1(L, M, V, [H|Acc]);
 | 
			
		||||
overlay1([], M, V, Acc) ->
 | 
			
		||||
	{M, V, lists:reverse(Acc)}.
 | 
			
		||||
 | 
			
		||||
%
 | 
			
		||||
flip(L) ->
 | 
			
		||||
	flip(L, []).
 | 
			
		||||
flip([[]|T], Acc) ->
 | 
			
		||||
	[[] || [] <- T], % guard check
 | 
			
		||||
	[lists:reverse(L) || L <- Acc];
 | 
			
		||||
flip(L, Acc) ->
 | 
			
		||||
	Heads = [H || [H|_] <- L],
 | 
			
		||||
	Tails = [T || [_|T] <- L],
 | 
			
		||||
	flip(Tails, [Heads|Acc]).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
finder_bits() ->
 | 
			
		||||
	?FINDER_BITS.
 | 
			
		||||
%%	
 | 
			
		||||
alignment_bits(AC) ->
 | 
			
		||||
	Repeats = composite_ac(AC, []),
 | 
			
		||||
	alignment_bits(Repeats, <<>>).
 | 
			
		||||
alignment_bits([H|T], Acc) ->
 | 
			
		||||
	Bits0 = bits:duplicate(<<31:5>>, H),
 | 
			
		||||
	Bits1 = bits:duplicate(<<17:5>>, H),
 | 
			
		||||
	Bits2 = bits:duplicate(<<21:5>>, H),
 | 
			
		||||
	Bits = bits:append([Bits0, Bits1, Bits2, Bits1, Bits0]),
 | 
			
		||||
	alignment_bits(T, <<Acc/bits, Bits/bits>>);
 | 
			
		||||
alignment_bits([], Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
%
 | 
			
		||||
composite_ac([{_, Row}|T], Acc) ->
 | 
			
		||||
	N = 1 + length([{X, Y} || {X, Y} <- T, Y =:= Row]),
 | 
			
		||||
	T0 = [{X, Y} || {X, Y} <- T, Y =/= Row],
 | 
			
		||||
	composite_ac(T0, [N|Acc]);
 | 
			
		||||
composite_ac([], Acc) ->
 | 
			
		||||
	lists:reverse(Acc).
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
timing_bits(Version, AC) ->
 | 
			
		||||
	Length = dimension(Version) - 16,
 | 
			
		||||
	% alignment pattern start coordinates, to trigger bit skipping
 | 
			
		||||
	TH = timing_bits(1, Length, [X - 8 - 2 || {X, 7} <- AC], <<>>),
 | 
			
		||||
	TV = timing_bits(1, Length, [Y - 8 - 2 || {7, Y} <- AC], <<>>),
 | 
			
		||||
	<<TH/bits, TV/bits>>.
 | 
			
		||||
%	
 | 
			
		||||
timing_bits(N, Max, A, Acc) when N =< Max ->
 | 
			
		||||
	case lists:member(N, A) of
 | 
			
		||||
	true -> % skip the alignment pattern
 | 
			
		||||
		timing_bits(N + 5, Max, A, Acc);
 | 
			
		||||
	false ->
 | 
			
		||||
		Bit = N band 1,
 | 
			
		||||
		timing_bits(N + 1, Max, A, <<Acc/bits, Bit:1>>)
 | 
			
		||||
	end;
 | 
			
		||||
timing_bits(_, _, _, Acc) ->
 | 
			
		||||
	Acc.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
format_bits(Bin) ->
 | 
			
		||||
	<<A:7, C:1, E:7>> = bits:reverse(Bin),
 | 
			
		||||
	<<B:8, D:7>> = Bin,
 | 
			
		||||
	<<A:7, B:8, C:1, D:7, 1:1, E:7>>.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
version_bits(Bin) ->
 | 
			
		||||
	VTop = bits:reverse(Bin),
 | 
			
		||||
	VLeft = version_bits(VTop, []),
 | 
			
		||||
	<<VTop/bits, VLeft/bits>>.
 | 
			
		||||
%
 | 
			
		||||
version_bits(<<X:3/bits, Bin/bits>>, Acc) ->
 | 
			
		||||
	version_bits(Bin, [X|Acc]);
 | 
			
		||||
version_bits(<<>>, Acc) ->
 | 
			
		||||
	version_bits(lists:reverse(Acc), <<>>, <<>>, <<>>).
 | 
			
		||||
%
 | 
			
		||||
version_bits([<<A:1, B:1, C:1>>|T], RowA, RowB, RowC) ->
 | 
			
		||||
	version_bits(T, <<RowA/bits, A:1>>, <<RowB/bits, B:1>>, <<RowC/bits, C:1>>);
 | 
			
		||||
version_bits([], RowA, RowB, RowC) ->
 | 
			
		||||
	bits:append([RowA, RowB, RowC]).
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										264
									
								
								src/qrcode_params.hrl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								src/qrcode_params.hrl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,264 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
%
 | 
			
		||||
% qrcode_params.hrl
 | 
			
		||||
 | 
			
		||||
-record(qr_params, {mode, version, dimension, ec_level, block_defs, align_coords, remainder, mask, data}).
 | 
			
		||||
 | 
			
		||||
-define(QR_GF256_PRIME_MODULUS, 285). % 16#011D -> 2^8 + 2^4 + 2^3 + 2^2 + 1
 | 
			
		||||
 | 
			
		||||
-define(VERSION_INFO_POLY, 7973). % 16#1f25 -> 0001 1111 0010 0101
 | 
			
		||||
-define(FORMAT_INFO_POLY, 1335).  % 16#0537 -> 0000 0101 0011 1110
 | 
			
		||||
-define(FORMAT_INFO_MASK, 21522). % 16#5412 -> 0101 0100 0001 0010
 | 
			
		||||
 | 
			
		||||
-define(QUIET_ZONE, 4). % recommended value
 | 
			
		||||
 | 
			
		||||
%% Table 2. Mode Indicator
 | 
			
		||||
-define(TERMINATOR, 0).
 | 
			
		||||
-define(NUMERIC_MODE, 1).
 | 
			
		||||
-define(ALPHANUMERIC_MODE, 2).
 | 
			
		||||
-define(STRUCTURED_APPEND_MODE, 3).
 | 
			
		||||
-define(FNC1_FIRST_POSITION_MODE, 5).
 | 
			
		||||
-define(BYTE_MODE, 4).
 | 
			
		||||
-define(ECI_MODE, 7).
 | 
			
		||||
-define(KANJI_MODE, 8).
 | 
			
		||||
-define(FNC1_SECOND_POSITION_MODE, 9).
 | 
			
		||||
 | 
			
		||||
%% Table 3. Number of bits in Character Count Indicator
 | 
			
		||||
%% {Mode, [v0-v9, v10-v26, v27-v40]}
 | 
			
		||||
-define(CCI_BITSIZE, [
 | 
			
		||||
	{?NUMERIC_MODE, [10, 12, 14]},
 | 
			
		||||
	{?ALPHANUMERIC_MODE, [9, 11, 13]},
 | 
			
		||||
	{?BYTE_MODE, [8, 16, 16]},
 | 
			
		||||
	{?KANJI_MODE, [8, 16, 16]}
 | 
			
		||||
]).
 | 
			
		||||
 | 
			
		||||
% Table 5. Alphanumeric charset - see also char/1 
 | 
			
		||||
-define(CHARSET, <<"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:">>).
 | 
			
		||||
-define(ALPHANUMERIC_REGEX, <<"[", ?CHARSET/binary, "]+">>).
 | 
			
		||||
-define(NUMERIC_REGEX, <<"[0123456789]+">>).
 | 
			
		||||
 | 
			
		||||
% Section 8.4.9
 | 
			
		||||
-define(DATA_PAD_0, 236). % 11101100
 | 
			
		||||
-define(DATA_PAD_1, 17).  % 00010001
 | 
			
		||||
 | 
			
		||||
%% Annex E. Table E.1 - Origin at 1, 1 rather than 0, 0
 | 
			
		||||
-define(ALIGNMENT_COORDINATES, {
 | 
			
		||||
	[],
 | 
			
		||||
	[7, 19],
 | 
			
		||||
	[7, 23],
 | 
			
		||||
	[7, 27],
 | 
			
		||||
	[7, 31],
 | 
			
		||||
	[7, 35], 
 | 
			
		||||
	[7, 23, 39], % Version 7
 | 
			
		||||
	[7, 25, 43], 
 | 
			
		||||
	[7, 27, 47], 
 | 
			
		||||
	[7, 29, 51],
 | 
			
		||||
	[7, 31, 55], 
 | 
			
		||||
	[7, 33, 59], 
 | 
			
		||||
	[7, 35, 63], 
 | 
			
		||||
	[7, 27, 47, 67], % Version 14 
 | 
			
		||||
	[7, 27, 49, 71],
 | 
			
		||||
	[7, 27, 51, 75], 
 | 
			
		||||
	[7, 31, 55, 79], 
 | 
			
		||||
	[7, 31, 57, 83], 
 | 
			
		||||
	[7, 31, 59, 87], 
 | 
			
		||||
	[7, 35, 63, 91],
 | 
			
		||||
	[7, 29, 51, 73, 95], % Version 21
 | 
			
		||||
	[7, 27, 51, 75, 99], 
 | 
			
		||||
	[7, 31, 55, 79, 103], 
 | 
			
		||||
	[7, 29, 55, 81, 107], 
 | 
			
		||||
	[7, 33, 59, 85, 111],
 | 
			
		||||
	[7, 31, 59, 87, 115], 
 | 
			
		||||
	[7, 35, 63, 91, 119], 
 | 
			
		||||
	[7, 27, 51, 75, 99, 123], % Version 28
 | 
			
		||||
	[7, 31, 55, 79, 103, 127],
 | 
			
		||||
	[7, 27, 53, 79, 105, 131],
 | 
			
		||||
	[7, 31, 57, 83, 109, 135],
 | 
			
		||||
	[7, 35, 61, 87, 113, 139],
 | 
			
		||||
	[7, 31, 59, 87, 115, 143],
 | 
			
		||||
	[7, 35, 63, 91, 119, 147],
 | 
			
		||||
	[7, 31, 55, 79, 103, 127, 151], % Version 35
 | 
			
		||||
	[7, 25, 51, 77, 103, 129, 155],
 | 
			
		||||
	[7, 29, 55, 81, 107, 133, 159],
 | 
			
		||||
	[7, 33, 59, 85, 111, 137, 163],
 | 
			
		||||
	[7, 27, 55, 83, 111, 139, 167],
 | 
			
		||||
	[7, 31, 59, 87, 115, 143, 171] % Version 40
 | 
			
		||||
}).
 | 
			
		||||
 | 
			
		||||
% Composite of Tables 1, 7-11, 13-22
 | 
			
		||||
% {{level, version}, {numeric_capacity, alpha_capacity, byte_capacity, kanji_capacity}, ecc_blocks[{number_of_blocks, total_bytes, data_bytes], remainder_bits}
 | 
			
		||||
-define(TABLES, [
 | 
			
		||||
	{{'L',1},{41,25,17,10},[{1,26,19}],0},
 | 
			
		||||
	{{'L',2},{77,47,32,20},[{1,44,34}],7},
 | 
			
		||||
	{{'L',3},{127,77,53,32},[{1,70,55}],7},
 | 
			
		||||
	{{'L',4},{187,114,78,48},[{1,100,80}],7},
 | 
			
		||||
	{{'L',5},{255,154,106,65},[{1,134,108}],7},
 | 
			
		||||
	{{'L',6},{322,195,134,82},[{2,86,68}],7},
 | 
			
		||||
	{{'L',7},{370,224,154,95},[{2,98,78}],0},
 | 
			
		||||
	{{'L',8},{461,279,192,118},[{2,121,97}],0},
 | 
			
		||||
	{{'L',9},{552,335,230,141},[{2,146,116}],0},
 | 
			
		||||
	{{'L',10},{652,395,271,167},[{2,86,68},{2,87,69}],0},
 | 
			
		||||
	{{'L',11},{772,468,321,198},[{4,101,81}],0},
 | 
			
		||||
	{{'L',12},{883,535,367,226},[{2,116,92},{2,117,93}],0},
 | 
			
		||||
	{{'L',13},{1022,619,425,262},[{4,133,107}],0},
 | 
			
		||||
	{{'L',14},{1101,667,458,282},[{3,145,115},{1,146,116}],3},
 | 
			
		||||
	{{'L',15},{1250,758,520,320},[{5,109,87},{1,110,88}],3},
 | 
			
		||||
	{{'L',16},{1408,854,586,361},[{5,122,98},{1,123,99}],3},
 | 
			
		||||
	{{'L',17},{1548,938,644,397},[{1,135,107},{5,136,108}],3},
 | 
			
		||||
	{{'L',18},{1725,1046,718,442},[{5,150,120},{1,151,121}],3},
 | 
			
		||||
	{{'L',19},{1903,1153,792,488},[{3,141,113},{4,142,114}],3},
 | 
			
		||||
	{{'L',20},{2061,1249,858,528},[{3,135,107},{5,136,108}],3},
 | 
			
		||||
	{{'L',21},{2232,1352,929,572},[{4,144,116},{4,145,117}],4},
 | 
			
		||||
	{{'L',22},{2409,1460,1003,618},[{2,139,111},{7,140,112}],4},
 | 
			
		||||
	{{'L',23},{2620,1588,1091,672},[{4,151,121},{5,152,122}],4},
 | 
			
		||||
	{{'L',24},{2812,1704,1171,721},[{6,147,117},{4,148,118}],4},
 | 
			
		||||
	{{'L',25},{3057,1853,1273,784},[{8,132,106},{4,133,107}],4},
 | 
			
		||||
	{{'L',26},{3283,1990,1367,842},[{10,142,114},{2,143,115}],4},
 | 
			
		||||
	{{'L',27},{3517,2132,1465,902},[{8,152,122},{4,153,123}],4},
 | 
			
		||||
	{{'L',28},{3669,2223,1528,940},[{3,147,117},{10,148,118}],3},
 | 
			
		||||
	{{'L',29},{3909,2369,1628,1002},[{7,146,116},{7,147,117}],3},
 | 
			
		||||
	{{'L',30},{4158,2520,1732,1066},[{5,145,115},{10,146,116}],3},
 | 
			
		||||
	{{'L',31},{4417,2677,1840,1132},[{13,145,115},{3,146,116}],3},
 | 
			
		||||
	{{'L',32},{4686,2840,1952,1201},[{17,145,115}],3},
 | 
			
		||||
	{{'L',33},{4965,3009,2068,1273},[{17,145,115},{1,146,116}],3},
 | 
			
		||||
	{{'L',34},{5253,3183,2188,1347},[{13,145,115},{6,146,116}],3},
 | 
			
		||||
	{{'L',35},{5529,3351,2303,1417},[{12,151,121},{7,152,122}],0},
 | 
			
		||||
	{{'L',36},{5836,3537,2431,1496},[{6,151,121},{14,152,122}],0},
 | 
			
		||||
	{{'L',37},{6153,3729,2563,1577},[{17,152,122},{4,153,123}],0},
 | 
			
		||||
	{{'L',38},{6479,3927,2699,1661},[{4,152,122},{18,153,123}],0},
 | 
			
		||||
	{{'L',39},{6743,4087,2809,1729},[{20,147,117},{4,148,118}],0},
 | 
			
		||||
	{{'L',40},{7089,4296,2953,1817},[{19,148,118},{6,149,119}],0},
 | 
			
		||||
	{{'M',1},{34,20,14,8},[{1,26,16}],0},
 | 
			
		||||
	{{'M',2},{63,38,26,16},[{1,44,28}],7},
 | 
			
		||||
	{{'M',3},{101,61,42,26},[{1,70,44}],7},
 | 
			
		||||
	{{'M',4},{149,90,62,38},[{2,50,32}],7},
 | 
			
		||||
	{{'M',5},{202,122,84,52},[{2,67,43}],7},
 | 
			
		||||
	{{'M',6},{255,154,106,65},[{4,43,27}],7},
 | 
			
		||||
	{{'M',7},{293,178,122,75},[{4,49,31}],0},
 | 
			
		||||
	{{'M',8},{365,221,152,93},[{2,60,38},{2,61,39}],0},
 | 
			
		||||
	{{'M',9},{432,262,180,111},[{3,58,36},{2,59,37}],0},
 | 
			
		||||
	{{'M',10},{513,311,213,131},[{4,69,43},{1,70,44}],0},
 | 
			
		||||
	{{'M',11},{604,366,251,155},[{1,80,50},{4,81,51}],0},
 | 
			
		||||
	{{'M',12},{691,419,287,177},[{6,58,36},{2,59,37}],0},
 | 
			
		||||
	{{'M',13},{796,483,331,204},[{8,59,37},{1,60,38}],0},
 | 
			
		||||
	{{'M',14},{871,528,362,223},[{4,64,40},{5,65,41}],3},
 | 
			
		||||
	{{'M',15},{991,600,412,254},[{5,65,41},{5,66,42}],3},
 | 
			
		||||
	{{'M',16},{1082,656,450,277},[{7,73,45},{3,74,46}],3},
 | 
			
		||||
	{{'M',17},{1212,734,504,310},[{10,74,46},{1,75,47}],3},
 | 
			
		||||
	{{'M',18},{1346,816,560,345},[{9,69,43},{4,70,44}],3},
 | 
			
		||||
	{{'M',19},{1500,909,624,384},[{3,70,44},{11,71,45}],3},
 | 
			
		||||
	{{'M',20},{1600,970,666,410},[{3,67,41},{13,68,42}],3},
 | 
			
		||||
	{{'M',21},{1708,1035,711,438},[{17,68,42}],4},
 | 
			
		||||
	{{'M',22},{1872,1134,779,480},[{17,74,46}],4},
 | 
			
		||||
	{{'M',23},{2059,1248,857,528},[{4,75,47},{14,76,48}],4},
 | 
			
		||||
	{{'M',24},{2188,1326,911,561},[{6,73,45},{14,74,46}],4},
 | 
			
		||||
	{{'M',25},{2395,1451,997,614},[{8,75,47},{13,76,48}],4},
 | 
			
		||||
	{{'M',26},{2544,1542,1059,652},[{19,74,46},{4,75,47}],4},
 | 
			
		||||
	{{'M',27},{2701,1637,1125,692},[{22,73,45},{3,74,46}],4},
 | 
			
		||||
	{{'M',28},{2857,1732,1190,732},[{3,73,45},{23,74,46}],3},
 | 
			
		||||
	{{'M',29},{3035,1839,1264,778},[{21,73,45},{7,74,46}],3},
 | 
			
		||||
	{{'M',30},{3289,1994,1370,843},[{19,75,47},{10,76,48}],3},
 | 
			
		||||
	{{'M',31},{3486,2113,1452,894},[{2,74,46},{29,75,47}],3},
 | 
			
		||||
	{{'M',32},{3693,2238,1538,947},[{10,74,46},{23,75,47}],3},
 | 
			
		||||
	{{'M',33},{3909,2369,1628,1002},[{14,74,46},{21,75,47}],3},
 | 
			
		||||
	{{'M',34},{4134,2506,1722,1060},[{14,74,46},{23,75,47}],3},
 | 
			
		||||
	{{'M',35},{4343,2632,1809,1113},[{12,75,47},{26,76,48}],0},
 | 
			
		||||
	{{'M',36},{4588,2780,1911,1176},[{6,75,47},{34,76,48}],0},
 | 
			
		||||
	{{'M',37},{4775,2894,1989,1224},[{29,74,46},{14,75,47}],0},
 | 
			
		||||
	{{'M',38},{5039,3054,2099,1292},[{13,74,46},{32,75,47}],0},
 | 
			
		||||
	{{'M',39},{5313,3220,2213,1362},[{40,75,47},{7,76,48}],0},
 | 
			
		||||
	{{'M',40},{5596,3391,2331,1435},[{18,75,47},{31,76,48}],0},
 | 
			
		||||
	{{'Q',1},{27,16,11,7},[{1,26,13}],0},
 | 
			
		||||
	{{'Q',2},{48,29,20,12},[{1,44,22}],7},
 | 
			
		||||
	{{'Q',3},{77,47,32,20},[{2,35,17}],7},
 | 
			
		||||
	{{'Q',4},{111,67,46,28},[{2,50,24}],7},
 | 
			
		||||
	{{'Q',5},{144,87,60,37},[{2,33,15},{2,34,16}],7},
 | 
			
		||||
	{{'Q',6},{178,108,74,45},[{4,43,19}],7},
 | 
			
		||||
	{{'Q',7},{207,125,86,53},[{2,32,14},{4,33,15}],0},
 | 
			
		||||
	{{'Q',8},{259,157,108,66},[{4,40,18},{2,41,19}],0},
 | 
			
		||||
	{{'Q',9},{312,189,130,80},[{4,36,16},{4,37,17}],0},
 | 
			
		||||
	{{'Q',10},{364,221,151,93},[{6,43,19},{2,44,20}],0},
 | 
			
		||||
	{{'Q',11},{427,259,177,109},[{4,50,22},{4,51,23}],0},
 | 
			
		||||
	{{'Q',12},{489,296,203,125},[{4,46,20},{6,47,21}],0},
 | 
			
		||||
	{{'Q',13},{580,352,241,149},[{8,44,20},{4,45,21}],0},
 | 
			
		||||
	{{'Q',14},{621,376,258,159},[{11,36,16},{5,37,17}],3},
 | 
			
		||||
	{{'Q',15},{703,426,292,180},[{5,54,24},{7,55,25}],3},
 | 
			
		||||
	{{'Q',16},{775,470,322,198},[{15,43,19},{2,44,20}],3},
 | 
			
		||||
	{{'Q',17},{876,531,364,224},[{1,50,22},{15,51,23}],3},
 | 
			
		||||
	{{'Q',18},{948,574,394,243},[{17,50,22},{1,51,23}],3},
 | 
			
		||||
	{{'Q',19},{1063,644,442,272},[{17,47,21},{4,48,22}],3},
 | 
			
		||||
	{{'Q',20},{1159,702,482,297},[{15,54,24},{5,55,25}],3},
 | 
			
		||||
	{{'Q',21},{1224,742,509,314},[{17,50,22},{6,51,23}],4},
 | 
			
		||||
	{{'Q',22},{1358,823,565,348},[{7,54,24},{16,55,25}],4},
 | 
			
		||||
	{{'Q',23},{1468,890,611,376},[{11,54,24},{14,55,25}],4},
 | 
			
		||||
	{{'Q',24},{1588,963,661,407},[{11,54,24},{16,55,25}],4},
 | 
			
		||||
	{{'Q',25},{1718,1041,715,440},[{7,54,24},{22,55,25}],4},
 | 
			
		||||
	{{'Q',26},{1804,1094,751,462},[{28,50,22},{6,51,23}],4},
 | 
			
		||||
	{{'Q',27},{1933,1172,805,496},[{8,53,23},{26,54,24}],4},
 | 
			
		||||
	{{'Q',28},{2085,1263,868,534},[{4,54,24},{31,55,25}],3},
 | 
			
		||||
	{{'Q',29},{2181,1322,908,559},[{1,53,23},{37,54,24}],3},
 | 
			
		||||
	{{'Q',30},{2358,1429,982,604},[{15,54,24},{25,55,25}],3},
 | 
			
		||||
	{{'Q',31},{2473,1499,1030,634},[{42,54,24},{1,55,25}],3},
 | 
			
		||||
	{{'Q',32},{2670,1618,1112,684},[{10,54,24},{35,55,25}],3},
 | 
			
		||||
	{{'Q',33},{2805,1700,1168,719},[{29,54,24},{19,55,25}],3},
 | 
			
		||||
	{{'Q',34},{2949,1787,1228,756},[{44,54,24},{7,55,25}],3},
 | 
			
		||||
	{{'Q',35},{3081,1867,1283,790},[{39,54,24},{14,55,25}],0},
 | 
			
		||||
	{{'Q',36},{3244,1966,1351,832},[{46,54,24},{10,55,25}],0},
 | 
			
		||||
	{{'Q',37},{3417,2071,1423,876},[{49,54,24},{10,55,25}],0},
 | 
			
		||||
	{{'Q',38},{3599,2181,1499,923},[{48,54,24},{14,55,25}],0},
 | 
			
		||||
	{{'Q',39},{3791,2298,1579,972},[{43,54,24},{22,55,25}],0},
 | 
			
		||||
	{{'Q',40},{3993,2420,1663,1024},[{34,54,24},{34,55,25}],0},
 | 
			
		||||
	{{'H',1},{17,10,7,4},[{1,26,9}],0},
 | 
			
		||||
	{{'H',2},{34,20,14,8},[{1,44,16}],7},
 | 
			
		||||
	{{'H',3},{58,35,24,15},[{2,35,13}],7},
 | 
			
		||||
	{{'H',4},{82,50,34,21},[{4,25,9}],7},
 | 
			
		||||
	{{'H',5},{106,64,44,27},[{2,33,11},{2,34,12}],7},
 | 
			
		||||
	{{'H',6},{139,84,58,36},[{4,43,15}],7},
 | 
			
		||||
	{{'H',7},{154,93,64,39},[{4,39,13},{1,40,14}],0},
 | 
			
		||||
	{{'H',8},{202,122,84,52},[{4,40,14},{2,41,15}],0},
 | 
			
		||||
	{{'H',9},{235,143,98,60},[{4,36,12},{4,37,13}],0},
 | 
			
		||||
	{{'H',10},{288,174,119,74},[{6,43,15},{2,44,16}],0},
 | 
			
		||||
	{{'H',11},{331,200,137,85},[{3,36,12},{8,37,13}],0},
 | 
			
		||||
	{{'H',12},{374,227,155,96},[{7,42,14},{4,43,15}],0},
 | 
			
		||||
	{{'H',13},{427,259,177,109},[{12,33,11},{4,34,12}],0},
 | 
			
		||||
	{{'H',14},{468,283,194,120},[{11,36,12},{5,37,13}],3},
 | 
			
		||||
	{{'H',15},{530,321,220,136},[{11,36,12},{7,37,13}],3},
 | 
			
		||||
	{{'H',16},{602,365,250,154},[{3,45,15},{13,46,16}],3},
 | 
			
		||||
	{{'H',17},{674,408,280,173},[{2,42,14},{17,43,15}],3},
 | 
			
		||||
	{{'H',18},{746,452,310,191},[{2,42,14},{19,43,15}],3},
 | 
			
		||||
	{{'H',19},{813,493,338,208},[{9,39,13},{16,40,14}],3},
 | 
			
		||||
	{{'H',20},{919,557,382,235},[{15,43,15},{10,44,16}],3},
 | 
			
		||||
	{{'H',21},{969,587,403,248},[{19,46,16},{6,47,17}],4},
 | 
			
		||||
	{{'H',22},{1056,640,439,270},[{34,37,13}],4},
 | 
			
		||||
	{{'H',23},{1108,672,461,284},[{16,45,15},{14,46,16}],4},
 | 
			
		||||
	{{'H',24},{1228,744,511,315},[{30,46,16},{2,47,17}],4},
 | 
			
		||||
	{{'H',25},{1286,779,535,330},[{22,45,15},{13,46,16}],4},
 | 
			
		||||
	{{'H',26},{1425,864,593,365},[{33,46,16},{4,47,17}],4},
 | 
			
		||||
	{{'H',27},{1501,910,625,385},[{12,45,15},{28,46,16}],4},
 | 
			
		||||
	{{'H',28},{1581,958,658,405},[{11,45,15},{31,46,16}],3},
 | 
			
		||||
	{{'H',29},{1677,1016,698,430},[{19,45,15},{26,46,16}],3},
 | 
			
		||||
	{{'H',30},{1782,1080,742,457},[{23,45,15},{25,46,16}],3},
 | 
			
		||||
	{{'H',31},{1897,1150,790,486},[{23,45,15},{28,46,16}],3},
 | 
			
		||||
	{{'H',32},{2022,1226,842,518},[{19,45,15},{35,46,16}],3},
 | 
			
		||||
	{{'H',33},{2157,1307,898,553},[{11,45,15},{46,46,16}],3},
 | 
			
		||||
	{{'H',34},{2301,1394,958,590},[{59,46,16},{1,47,17}],3},
 | 
			
		||||
	{{'H',35},{2361,1431,983,605},[{22,45,15},{41,46,16}],0},
 | 
			
		||||
	{{'H',36},{2524,1530,1051,647},[{2,45,15},{64,46,16}],0},
 | 
			
		||||
	{{'H',37},{2625,1591,1093,673},[{24,45,15},{46,46,16}],0},
 | 
			
		||||
	{{'H',38},{2735,1658,1139,701},[{42,45,15},{32,46,16}],0},
 | 
			
		||||
	{{'H',39},{2927,1774,1219,750},[{10,45,15},{67,46,16}],0},
 | 
			
		||||
	{{'H',40},{3057,1852,1273,784},[{20,45,15},{61,46,16}],0}
 | 
			
		||||
]).
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										69
									
								
								src/qrcode_reedsolomon.erl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/qrcode_reedsolomon.erl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
			
		||||
%% Copyright 2011 Steve Davis <steve@simulacity.com>
 | 
			
		||||
%
 | 
			
		||||
% Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
% you may not use this file except in compliance with the License.
 | 
			
		||||
% You may obtain a copy of the License at
 | 
			
		||||
% 
 | 
			
		||||
% http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
% 
 | 
			
		||||
% Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
% distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
% See the License for the specific language governing permissions and
 | 
			
		||||
% limitations under the License.
 | 
			
		||||
 | 
			
		||||
-module(qrcode_reedsolomon).
 | 
			
		||||
 | 
			
		||||
-export([encode/2, bch_code/2]).
 | 
			
		||||
 | 
			
		||||
-define(QRCODE_GF256_PRIME_MODULUS, 285). % 16#011D = 2^8 + 2^4 + 2^3 + 2^2 + 2^0
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
encode(Bin, Degree) when Degree > 0 ->
 | 
			
		||||
	Field = gf256:field(?QRCODE_GF256_PRIME_MODULUS),
 | 
			
		||||
	Generator = generator(Field, Degree),
 | 
			
		||||
	Data = binary_to_list(Bin),
 | 
			
		||||
	Coeffs = gf256:monomial_product(Field, Data, 1, Degree),
 | 
			
		||||
	{_Quotient, Remainder} = gf256:divide(Field, Coeffs, Generator),
 | 
			
		||||
	ErrorCorrectionBytes = list_to_binary(Remainder),
 | 
			
		||||
	<<ErrorCorrectionBytes/binary>>.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
bch_code(Byte, Poly) ->
 | 
			
		||||
	MSB = msb(Poly),
 | 
			
		||||
	Byte0 = Byte bsl (MSB - 1),
 | 
			
		||||
	bch_code(Byte0, Poly, MSB).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%% Internal
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
generator(F, D) when D > 0 ->
 | 
			
		||||
	generator(F, [1], D, 0).
 | 
			
		||||
%	
 | 
			
		||||
generator(_, P, D, D) ->
 | 
			
		||||
	P;
 | 
			
		||||
generator(F, P, D, Count) ->
 | 
			
		||||
	P0 = gf256:polynomial_product(F, P, [1, gf256:exponent(F, Count)]),
 | 
			
		||||
	generator(F, P0, D, Count + 1).
 | 
			
		||||
	
 | 
			
		||||
%
 | 
			
		||||
bch_code(Byte, Poly, MSB) ->
 | 
			
		||||
	case msb(Byte) >= MSB of
 | 
			
		||||
	true ->
 | 
			
		||||
		Byte0 = Byte bxor (Poly bsl (msb(Byte) - MSB)),
 | 
			
		||||
		bch_code(Byte0, Poly, MSB);
 | 
			
		||||
	false ->
 | 
			
		||||
		Byte
 | 
			
		||||
	end.
 | 
			
		||||
 | 
			
		||||
%%
 | 
			
		||||
msb(0) ->
 | 
			
		||||
	0;
 | 
			
		||||
msb(Byte) ->
 | 
			
		||||
	msb(Byte, 0).
 | 
			
		||||
msb(0, Count) ->
 | 
			
		||||
	Count;
 | 
			
		||||
msb(Byte, Count) ->
 | 
			
		||||
	msb(Byte bsr 1, Count + 1).
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user