QRCode-Encoder
view release on metacpan or search on metacpan
lib/QRCode/Encoder/Matrix.pm view on Meta::CPAN
my $matrix = $data->{matrix};
my $ecstart = $data->{eside_size} - 4 - 7 - 4;
for my $i (4 .. 9) {
for my $j ($ecstart .. ($ecstart + 2)) {
$matrix->[$i][$j] = $matrix->[$j][$i] = 0x32;
}
}
return $data;
}
sub add_timing ($data) {
my $matrix = $data->{matrix};
my $es = $data->{eside_size};
for my $i (12 .. ($es - 4 - 8 - 1)) {
$matrix->[$i][10] = $matrix->[10][$i] = 0x35 ^ ($i & 1);
}
return $data;
}
sub try_add_alignment_pattern ($data, $x, $y) {
state $shape = [
[ 0x31, 0x31, 0x31, 0x31, 0x31 ],
[ 0x31, 0x30, 0x30, 0x30, 0x31 ],
[ 0x31, 0x30, 0x31, 0x30, 0x31 ],
[ 0x31, 0x30, 0x30, 0x30, 0x31 ],
[ 0x31, 0x31, 0x31, 0x31, 0x31 ],
];
$x += 4; # offset by quiet zone
$y += 4; # offset by quiet zone
my $matrix = $data->{matrix};
return if $matrix->[$y][$x] < 0x34
|| $matrix->[$y][$x + 4] < 0x34
|| $matrix->[$y + 4][$x] < 0x34;
for my $i (0 .. 4) {
for my $j (0 .. 4) {
$matrix->[$y + $i][$x + $j] = $shape->[$i][$j];
}
}
}
sub add_alignments ($data) {
my @offset = qrspec_alignment_patterns($data->{version});
for my $y_center (@offset) {
for my $x_center (@offset) {
try_add_alignment_pattern($data, $x_center - 2, $y_center - 2);
}
}
return $data;
}
sub bits_iterator ($data) {
my $n_expanded = length($data->{expanded});
my $rem = $data->{remainder};
my $i = 0;
my @queue;
return sub {
if (! @queue) {
if ($i < $n_expanded) {
push @queue, split m{}mxs, unpack 'B*', substr($data->{expanded}, $i++, 1);
}
else {
push @queue, ('0') x $rem;
$rem = 0;
}
}
return shift(@queue);
};
}
sub add_codewords ($data) {
my $it = bits_iterator($data);
my $matrix = $data->{matrix};
my $side_size = $data->{side_size};
# start from a fake position that would be the last bit of a
# hypothetical "-1" codeword
my $x = $side_size - 2;
my $y = $side_size;
my $left = 1;
my $d = -1; # direction
while (defined(my $bit = $it->())) {
while ('necessary') {
if ($x % 2 == $left) {
++$x;
$y += $d;
}
else {
--$x;
}
if ($d < 0 && $y < 0) { # reset condition
$x -= 2;
$y = 0;
$d = 1;
}
elsif ($d > 0 && $y >= $side_size) { # other reset condition
$x -= 2;
$y = $side_size - 1;
$d = -1;
}
if ($x == 6) { # left timing column, skip a column entirely
$x = 5;
$left = 0;
}
last if $matrix->[$y + 4][$x + 4] > 0x37; # found suitable position
}
$matrix->[$y + 4][$x + 4] = $bit ? 0x37 : 0x36;
}
return $data;
}
sub evaluate_matrix ($matrix) {
return 0
+ evaluate_matrix_adjacents_and_11311($matrix)
+ evaluate_matrix_blocks($matrix)
+ evaluate_matrix_proportion($matrix);
}
sub __row ($matrix, $i) {
my $max_idx = $matrix->[0]->$#* - 4;
join('', map { $matrix->[$i + 4][$_] & 0x01 ? 1 : 0 } 4 .. $max_idx);
}
sub __col ($matrix, $i) {
my $max_idx = $matrix->[0]->$#* - 4;
join('', map { $matrix->[$_][$i + 4] & 0x01 ? 1 : 0 } 4 .. $max_idx);
}
sub evaluate_matrix_adjacents_and_11311 ($matrix) {
my $side_size = $matrix->[0]->@* - 8;
my $penalty = 0;
my $penalty2 = 0;
for my $i (0 .. ($side_size - 1)) {
for my $seq (__row($matrix, $i), __col($matrix, $i)) {
# adjacences
my @contributions =
map { $_ - 2 }
grep { $_ >= 5 }
map { length }
split m{(0+)}mxs, $seq;
$penalty += sum(@contributions) if @contributions;
# 000011311 | 113110000
my @matches = $seq =~ m{
(
(?: (?<=0000) 1011101 ) # look behind...
| (?: 1011101 (?=0000) ) # or look ahead
)
}gmxs;
$penalty2 += 40 * scalar(@matches);
}
}
return $penalty + $penalty2;
}
sub evaluate_matrix_blocks ($matrix) {
my $side_size = $matrix->[0]->@* - 8;
my $penalty = 0;
for my $i (0 .. ($side_size - 2)) {
for my $j (0 .. ($side_size - 2)) {
my $count = 0;
for my $offset ([0, 0], [0, 1], [1, 0], [1, 1]) {
my ($oi, $oj) = $offset->@*;
$count++ if $matrix->[$i + $oi + 4][$j + $oj + 4] & 1;
}
$penalty += 3 if ($count == 0) || ($count == 4);
}
}
return $penalty;
}
sub evaluate_matrix_proportion ($matrix) {
my $count = sum( map { map { $_ & 0x1 ? 1 : 0 } $_->@* } $matrix->@* );
my $side_size = $matrix->[0]->@* - 8;
my $total = $side_size * $side_size;
my $percentage = 100 * $count / $total;
my $deviation = abs($percentage - 50);
my $penalty = 10 * int($deviation / 5);
return $penalty;
}
sub masked_matrix ($data, $mask_id) {
state $mask_for = {
0 => sub ($i, $j) { (($i + $j) % 2) == 0 },
1 => sub ($i, $j) { ($i % 2) == 0 },
2 => sub ($i, $j) { ($j % 3) == 0 },
3 => sub ($i, $j) { (($i + $j) % 3) == 0 },
4 => sub ($i, $j) { ((int($i / 2) + int($j / 3)) % 2) == 0 },
5 => sub ($i, $j) { ((($i * $j) % 2) + (($i * $j) % 3)) == 0 },
6 => sub ($i, $j) { (((($i * $j) % 2) + (($i * $j) % 3)) % 2) == 0 },
7 => sub ($i, $j) { (((($i + $j) % 2) + (($i * $j) % 3)) % 2) == 0 },
};
my $matrix = $data->{matrix};
my @masked;
my $eside_size = $data->{eside_size};
my $mask = $mask_for->{$mask_id};
for my $i (0 .. ($eside_size - 1)) {
for my $j (0 .. ($eside_size - 1)) {
if (($matrix->[$i][$j] >= 0x36) && $mask->($i - 4, $j - 4)) {
( run in 1.757 second using v1.01-cache-2.11-cpan-71847e10f99 )