File Coverage

blib/lib/App/optex/pingu/Picture.pm
Criterion Covered Total %
statement 29 109 26.6
branch 0 34 0.0
condition 0 30 0.0
subroutine 10 20 50.0
pod 0 8 0.0
total 39 201 19.4


$color_re)\g{col}*) }{
line stmt bran cond sub pod time code
1             package App::optex::pingu::Picture;
2              
3 1     1   10 use v5.24;
  1         3  
4 1     1   4 use warnings;
  1         1  
  1         57  
5 1     1   3 use utf8;
  1         3  
  1         14  
6              
7 1     1   28 use Exporter 'import';
  1         2  
  1         53  
8             our @EXPORT_OK = qw(&load);
9              
10 1     1   4 use Data::Dumper;
  1         1  
  1         78  
11 1     1   12 use List::Util qw(pairs zip reduce all any);
  1         2  
  1         225  
12 1     1   535 use Term::ANSIColor::Concise qw(ansi_color);
  1         56913  
  1         104  
13             $Term::ANSIColor::Concise::NO_RESET_EL = 1;
14              
15             use constant {
16 1     1   799 FB => "\N{FULL BLOCK}",
  1         7974  
  1         6  
  1         2089  
17             THB => "\N{UPPER HALF BLOCK}",
18             BHB => "\N{LOWER HALF BLOCK}",
19             LHB => "\N{LEFT HALF BLOCK}",
20             RHB => "\N{RIGHT HALF BLOCK}",
21             QUL => "\N{QUADRANT UPPER LEFT}",
22             QUR => "\N{QUADRANT UPPER RIGHT}",
23             QLL => "\N{QUADRANT LOWER LEFT}",
24             QLR => "\N{QUADRANT LOWER RIGHT}",
25             QULLR => "\N{QUADRANT UPPER LEFT AND LOWER RIGHT}",
26             QURLL => "\N{QUADRANT UPPER RIGHT AND LOWER LEFT}",
27             Q____ => "\N{SPACE}",
28             Qxx__ => "\N{UPPER HALF BLOCK}",
29             Q__xx => "\N{LOWER HALF BLOCK}",
30             Qx_x_ => "\N{LEFT HALF BLOCK}",
31             Q_x_x => "\N{RIGHT HALF BLOCK}",
32             Qx___ => "\N{QUADRANT UPPER LEFT}",
33             Q_x__ => "\N{QUADRANT UPPER RIGHT}",
34             Q__x_ => "\N{QUADRANT LOWER LEFT}",
35             Q___x => "\N{QUADRANT LOWER RIGHT}",
36             Qx__x => "\N{QUADRANT UPPER LEFT AND LOWER RIGHT}",
37             Q_xx_ => "\N{QUADRANT UPPER RIGHT AND LOWER LEFT}",
38             Q_xxx => "\N{QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT}",
39             Qx_xx => "\N{QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT}",
40             Qxx_x => "\N{QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT}",
41             Qxxx_ => "\N{QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT}",
42             Qxxxx => "\N{FULL BLOCK}",
43 1     1   35764 };
  1         2  
44             my $color_re = qr/[RGBCMYKW]/i;
45              
46             my %loader = (
47             asc => \&read_asc,
48             asc2 => sub { read_asc({ y => 2}, @_) },
49             asc4 => sub { read_asc({ x => 2, y => 2}, @_) },
50             );
51             $loader{default} //= $loader{asc};
52              
53             sub load {
54 0     0 0   my $file = shift;
55 0           my %opt = @_;
56 0 0         open my $fh, '<', $file or die "$file: $!\n";
57 0           my $data = do { local $/; <$fh> };
  0            
  0            
58 0 0         if ($file =~ /\.(\w+)$/) {
59 0   0       $opt{format} ||= $1;
60             }
61 0           load_data($data, %opt);
62             }
63              
64             sub load_data {
65 0     0 0   my $data = shift;
66 0           my %opt = @_;
67 0           for ($data) {
68 0           s/.*^__DATA__\n//ms;
69 0           s/^#.*\n//mg;
70             }
71 0   0       $opt{format} ||= 'default';
72 0   0       my $loader = $loader{$opt{format}} // die "$opt{format}: unknown format.\n";
73 0           $loader->($data);
74             }
75              
76             sub squash {
77             map @$_, reduce {
78 0     0     my $x = $a->[-1];
79 0 0 0       if ($x && all { $x->[$_] eq $b->[$_] } keys @$b) {
  0            
80 0           $x->[-1]++;
81             } else {
82 0           push @$a, [ @$b, 1 ];
83             }
84 0           $a;
85 0     0 0   } [], @_;
86             }
87              
88             my %element = (
89             "0" => Q____ , #
90             "1" => Qxxxx , # █
91             "00" => Q____ , #
92             "10" => Qxx__ , # ▀
93             "01" => Q__xx , # ▄
94             "11" => Qxxxx , # █
95             "0000" => Q____ , #
96             "0001" => Q___x , # ▗
97             "0010" => Q__x_ , # ▖
98             "0011" => Q__xx , # ▄
99             "0100" => Q_x__ , # ▝
100             "0101" => Q_x_x , # ▐
101             "0110" => Q_xx_ , # ▞
102             "0111" => Q_xxx , # ▟
103             "1000" => Qx___ , # ▘
104             "1001" => Qx__x , # ▚
105             "1010" => Qx_x_ , # ▄
106             "1011" => Qx_xx , # ▙
107             "1100" => Qxx__ , # ▀
108             "1101" => Qxx_x , # ▜
109             "1110" => Qxxx_ , # ▛
110             "1111" => Qxxxx , # █
111             );
112              
113             sub stringify {
114 0     0 0   my $vec = shift;
115 0           my $n = pop @$vec;
116 0           my $spec = join '', @$vec;
117 0   0       my $c1 = ($spec =~ /($color_re)/)[0] // '';
118 0   0       my $c2 = ($spec =~ /((?!$c1)$color_re)/)[0] // '';
119 0   0       my $ch = (state $cache = {})->{$spec} //= do {
120 0 0         if ($c1) {
121 0           my $bit = $spec =~ s/(.)/int($1 eq $c1)/ger;
  0            
122 0   0       $element{$bit} // die "$spec -> $bit";
123             } else {
124 0           substr $spec, 0, 1;
125             }
126             };
127 0   0       my $s = $ch x $n || 1;
128 0 0         $c1 ? ansi_color("$c1/$c2", $s) : $s;
129             }
130              
131             sub read_asc {
132 0 0   0 0   my $opt = ref $_[0] eq 'HASH' ? shift : {};
133 0   0       my $x = $opt->{x} // 1;
134 0   0       my $y = $opt->{y} // 1;
135 0           my $data = shift;
136 0           my @data = $data =~ /.+/g;
137 0 0         @data % $y and die "data format error.";
138 0 0   0     any { (length) % $x } @data and die "data format error.";
  0            
139 0           my @image;
140 0           while (my @y = splice @data, 0, $y) {
141 0           my @sequence = squash zip map [ /\X{$x}/g ], @y;
142 0           my $line = join '', map stringify($_), @sequence;
143 0           push @image, $line;
144             }
145 0 0         wantarray ? @image : join('', map "$_\n", @image);
146             }
147              
148             ######################################################################
149              
150             sub read_asc_1 {
151 0     0 0   local $_ = shift;
152 0           s/^#.*\n//mg;
153 0           s{ (?(?
154 0           ansi_color($+{col}, FB x length($+{str}))
155             }xge;
156 0           my @image = /.+/g;
157 0 0         wantarray ? @image : join('', map "$_\n", @image);
158             }
159              
160             my $use_FB = 0; # use FULL BLOCK when upper/lower are same
161             my $use_BHB = 0; # use LOWER HALF BLOCK to show lower part
162              
163             sub stringify_2 {
164 0     0 0   my($hi, $lo, $c) = @{+shift};
  0            
165 0   0       $c //= 1;
166 0 0         if ($hi =~ $color_re) {
    0          
167 0           my $color = $hi;
168 0 0 0       if ($use_FB and $lo eq $hi) {
169 0           ansi_color($color, FB x $c);
170             } else {
171 0 0         $color .= "/$lo" if $lo =~ $color_re;
172 0           ansi_color($color, THB x $c);
173             }
174             }
175             elsif ($lo =~ $color_re) {
176 0 0         if ($use_BHB) {
177 0           ansi_color($lo, BHB x $c);
178             } else {
179 0           ansi_color("S$lo", THB x $c);
180             }
181             }
182             else {
183 0           $hi x $c;
184             }
185             }
186              
187             sub read_asc_2 {
188 0     0 0   my $data = shift;
189 0           my @data = grep !/^\s*#/, $data =~ /.+/g;
190 0 0         @data % 2 and die "Data format error.\n";
191 0           my @image;
192 0           for (pairs @data) {
193 0           my($hi, $lo) = @$_;
194 0           my @data = squash zip [ $hi =~ /\X/g ], [ $lo =~ /\X/g ];
195 0           my $line = join '', map stringify_2($_), @data;
196 0           push @image, $line;
197             }
198 0 0         wantarray ? @image : join('', map "$_\n", @image);
199             }
200              
201             ######################################################################
202              
203             if (__FILE__ eq $0) {
204 1     1   8 use open IO => ':utf8', ':std';
  1         2  
  1         9  
205             local $/;
206             while (<>) {
207             my $suffix = ($ARGV =~ /\.(\w+)$/)[0] // 'default';
208             print scalar load_data($_, format => $suffix);
209             }
210             }
211              
212             1;