Skip to content

Commit 763fb4f

Browse files
committed
qrcode: Precompute packed function masks, merging data in during walk
1 parent ed67829 commit 763fb4f

1 file changed

Lines changed: 87 additions & 48 deletions

File tree

‎src/qrcode.ps.src‎

Lines changed: 87 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -541,31 +541,31 @@ begin
541541
/qrcode.modeANbeforeE [ 7 8 9 e 3 4 5 4 5 5 5 5 5 5 5 5 6 4 5 5 5 6 6 5 5 5 5 6 6 5 5 6 6 6 5 6 6 6 6 ] readonly def
542542

543543
%
544-
% Mask functions for each symbol format
544+
% Mask functions
545545
%
546-
/qrcode.formatmaskfuncs <<
547-
(full) [
548-
{add 2 mod} bind
549-
{exch pop 2 mod} bind
550-
{pop 3 mod} bind
551-
{add 3 mod} bind
552-
{2 idiv exch 3 idiv add 2 mod} bind
553-
{mul dup 2 mod exch 3 mod add} bind
554-
{mul dup 2 mod exch 3 mod add 2 mod} bind
555-
{2 copy mul 3 mod 3 1 roll add 2 mod add 2 mod} bind
556-
]
557-
(micro) [
558-
{exch pop 2 mod} bind
559-
{2 idiv exch 3 idiv add 2 mod} bind
560-
{mul dup 2 mod exch 3 mod add 2 mod} bind
561-
{2 copy mul 3 mod 3 1 roll add 2 mod add 2 mod} bind
562-
]
563-
(rmqr) [
564-
{2 idiv exch 3 idiv add 2 mod} bind
565-
]
546+
/qrcode.maskfuncs [
547+
{add 2 mod} bind % 0: (i+j) % 2
548+
{exch pop 2 mod} bind % 1: j % 2
549+
{pop 3 mod} bind % 2: i % 3
550+
{add 3 mod} bind % 3: (i+j) % 3
551+
{2 idiv exch 3 idiv add 2 mod} bind % 4: (j/2 + i/3) % 2
552+
{mul dup 2 mod exch 3 mod add} bind % 5: (i*j) % 2 + (i*j) % 3
553+
{mul dup 2 mod exch 3 mod add 2 mod} bind % 6: ((i*j) % 2 + (i*j) % 3) % 2
554+
{2 copy mul 3 mod 3 1 roll add 2 mod add 2 mod} bind % 7: ((i*j) % 3 + (i+j) % 2) % 2
555+
] readonly def
556+
557+
%
558+
% Mask bit positions in funcpatlayers for each format
559+
%
560+
% Entries relate to the numbered mask functions above.
561+
%
562+
/qrcode.formatmaskbits <<
563+
(full) [ 0 1 2 3 4 5 6 7 ]
564+
(micro) [ 1 4 6 7 ]
565+
(rmqr) [ 4 ]
566566
>> readonly def
567567

568-
/qrcode.latevars dup 3 dict def load /init {
568+
/qrcode.latevars dup 4 dict def load /init {
569569

570570
currentglobal
571571
true setglobal
@@ -587,6 +587,25 @@ begin
587587
1 1 255 {dup rsalog exch get exch rslog 3 1 roll put} for
588588
/rslog rslog readonly def
589589

590+
%
591+
% The mask functions have 6x12 periodicity, so we compute an 8-layer array
592+
% representing all mask function patterns at bit positions
593+
%
594+
/funcpatlayers 6 12 mul array def
595+
0 1 11 {
596+
/j exch def
597+
0 1 5 {
598+
/i exch def
599+
/bits 0 def
600+
0 1 7 {
601+
/m exch def
602+
i j //qrcode.maskfuncs m get exec 0 eq { /bits bits 1 m bitshift or def } if
603+
} for
604+
funcpatlayers j 6 mul i add bits put
605+
} for
606+
} for
607+
/funcpatlayers funcpatlayers readonly def
608+
590609
/init { //qrcode.latevars {def} forall } def
591610

592611
end
@@ -624,7 +643,9 @@ begin
624643
/bwipp.qrcodeEmptyData (The data must not be empty) //raiseerror exec
625644
} if
626645

646+
%
627647
% If version is supplied and format is not given then set format to correspond
648+
%
628649
version (unset) ne {
629650
format (unset) eq {
630651
(full)
@@ -644,7 +665,9 @@ begin
644665
/bwipp.qrcodeRMQRwithoutVersion (A version must be provided for RMQR) //raiseerror exec
645666
} if
646667

668+
%
647669
% Default error correction level
670+
%
648671
eclevel (unset) eq {/eclevel format (micro) ne {(M)} {(L)} ifelse def} if
649672

650673
eclevel (L) ne eclevel (M) ne eclevel (Q) ne eclevel (H) ne and and and {
@@ -661,7 +684,9 @@ begin
661684
} if
662685
} if
663686

687+
%
664688
% Parse the input
689+
%
665690
/fncvals <<
666691
/parse parse
667692
/parsefnc parsefnc
@@ -671,7 +696,9 @@ begin
671696
/msg barcode fncvals //parseinput exec def
672697
/msglen msg length def
673698

699+
%
674700
% If FNC1 in first position then escape % as %%
701+
%
675702
/fnc1first false def
676703
msglen 0 gt {
677704
msg 0 get //qrcode.fn1 eq {
@@ -681,7 +708,9 @@ begin
681708
} if
682709
} if
683710

711+
%
684712
% Encoding functions
713+
%
685714
/tobin {
686715
string dup length 1 sub 0 exch 1 exch {1 index exch 48 put} for
687716
dup 3 -1 roll 2 2 index length string cvrs dup length 2 index length exch sub exch putinterval
@@ -1406,31 +1435,37 @@ begin
14061435
} if
14071436

14081437
%
1409-
% Calculate the mask patterns
1438+
% Lookup the mask bits corresponding to the mask functions applicable to
1439+
% this format
14101440
%
1411-
/maskfuncs //qrcode.formatmaskfuncs format get def
1412-
mask -1 ne { % User specifies a mask
1413-
/maskfuncs [maskfuncs mask 1 sub get] def
1441+
mask -1 eq {
1442+
/maskbits //qrcode.formatmaskbits format get def
1443+
} { % Just the indicated function when given a user-specified mask
1444+
/maskbits [ //qrcode.formatmaskbits format get mask 1 sub get ] def
14141445
/bestmaskval mask 1 sub def
1415-
} if
1416-
/masks maskfuncs length array def
1417-
0 1 masks length 1 sub {
1418-
/m exch def
1419-
/mask rows cols mul array def
1420-
0 1 rows 1 sub {
1421-
/j exch def
1422-
0 1 cols 1 sub {
1423-
/i exch def
1424-
i j maskfuncs m get exec 0 eq
1425-
pixs i j qmv get -1 eq and {1} {0} ifelse
1426-
mask i j qmv 3 -1 roll put
1427-
} for
1446+
} ifelse
1447+
/nmasks maskbits length def
1448+
1449+
%
1450+
% Build masklayers from the extruded funcpatlayers, with function cell
1451+
% values included (as 0 or 255)
1452+
%
1453+
/masklayers rows cols mul array def
1454+
0 1 rows 1 sub {
1455+
/j exch def
1456+
0 1 cols 1 sub {
1457+
/i exch def
1458+
/pos i j qmv def
1459+
masklayers pos pixs pos get dup -1 eq {
1460+
pop funcpatlayers j 6 mul 72 mod i 6 mod add get
1461+
} {
1462+
255 mul
1463+
} ifelse put
14281464
} for
1429-
masks m mask put
14301465
} for
14311466

14321467
%
1433-
% Walk the symbol placing the bitstream
1468+
% Walk the symbol placing the bitstream by merging it into masklayers
14341469
%
14351470
/posx cols format (rmqr) ne {1} {2} ifelse sub def
14361471
/posy rows 1 sub def
@@ -1439,9 +1474,10 @@ begin
14391474
/num 0 def
14401475
{ % loop
14411476
posx 0 lt {exit} if
1442-
pixs posx posy qmv get -1 eq {
1443-
cws num 8 idiv get 7 num 8 mod sub neg bitshift 1 and
1444-
pixs posx posy qmv 3 -1 roll put
1477+
/pos posx posy qmv def
1478+
pixs pos get -1 eq {
1479+
/bit cws num 8 idiv get 7 num 8 mod sub neg bitshift 1 and def
1480+
masklayers pos masklayers pos get bit 255 mul xor put
14451481
/num num 1 add def
14461482
} if
14471483
col 1 eq {
@@ -1565,17 +1601,19 @@ begin
15651601
} def
15661602

15671603
%
1568-
% Evaluate the masked symbols to find the most suitable
1604+
% Extract each masked symbol from the required set and evaluate them to
1605+
% find the most suitable
15691606
%
15701607
/bestscore 999999999 def
1571-
0 1 masks length 1 sub {
1608+
0 1 nmasks 1 sub {
15721609
/m exch def
1610+
/maskbit maskbits m get def
15731611
/masksym rows cols mul array def
15741612
0 1 rows cols mul 1 sub {
15751613
/i exch def
1576-
masksym i pixs i get masks m get i get xor put
1614+
masksym i masklayers i get maskbit neg bitshift 1 and put
15771615
} for
1578-
masks length 1 ne { % Not rMQR nor user-specified mask
1616+
nmasks 1 ne { % Not rMQR nor user-specified mask
15791617
/score masksym format (full) eq {evalfull} {evalmicro} ifelse def
15801618
score bestscore lt {
15811619
/bestsym masksym def
@@ -1584,6 +1622,7 @@ begin
15841622
} if
15851623
} {
15861624
/bestsym masksym def
1625+
% Note: bestmaskval never set for rMQR, but is not required
15871626
} ifelse
15881627
} for
15891628
/pixs bestsym def

0 commit comments

Comments
 (0)