File Coverage

blib/lib/Device/PaPiRus.pm
Criterion Covered Total %
statement 27 29 93.1
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 37 39 94.8


line stmt bran cond sub pod time code
1             package Device::PaPiRus;
2             #---AUTOPRAGMASTART---
3 1     1   13354 use 5.012;
  1         2  
4 1     1   4 use strict;
  1         1  
  1         22  
5 1     1   3 use warnings;
  1         3  
  1         30  
6 1     1   540 use diagnostics;
  1         131800  
  1         7  
7 1     1   799 use mro 'c3';
  1         643  
  1         3  
8 1     1   487 use English qw( -no_match_vars );
  1         2581  
  1         5  
9 1     1   289 use Carp;
  1         2  
  1         60  
10             our $VERSION = 1.1;
11 1     1   480 no if $] >= 5.017011, warnings => 'experimental::smartmatch';
  1         8  
  1         5  
12 1     1   615 use Fatal qw( close );
  1         9915  
  1         4  
13             #---AUTOPRAGMAEND---
14              
15 1     1   639 use GD;
  0            
  0            
16              
17             sub new {
18             my ($proto, %config) = @_;
19             my $class = ref($proto) || $proto;
20              
21             my $self = {};
22              
23             bless $self, $class; # bless with our class
24              
25             # Let's load the display size
26             open(my $ifh, '<', '/dev/epd/panel') or croak($OS_ERROR);
27             my $line = <$ifh>;
28             close $ifh;
29             if($line =~ /\ (\d+)x(\d+)\ /) {
30             ($self->{width}, $self->{height}) = ($1, $2);
31             } else {
32             croak("Can't read panel dimensions!");
33             }
34              
35             # Default
36             $self->{threshold} = 150;
37             $self->{randomize_white} = 0;
38             $self->{randomize_black} = 0;
39             $self->{dithering} = 0;
40              
41             return $self;
42             }
43              
44             sub getWidth {
45             my ($self) = @_;
46              
47             return $self->{width};
48             }
49              
50             sub getHeight {
51             my ($self) = @_;
52              
53             return $self->{height};
54             }
55              
56             sub randomizeWhite {
57             my ($self, $val) = @_;
58              
59             $self->{randomize_white} = $val;
60             return;
61             }
62              
63             sub randomizeBlack {
64             my ($self, $val) = @_;
65              
66             $self->{randomize_black} = $val;
67             return;
68             }
69              
70             sub useDithering {
71             my ($self, $val) = @_;
72              
73             $self->{dithering} = $val;
74             return;
75             }
76              
77             sub setThreshold {
78             my ($self, $threshold) = @_;
79              
80             $threshold = 0 + $threshold;
81             if($threshold < 0) {
82             croak("Threshold can not be less than zero");
83             } elsif($threshold > 255) {
84             croak("Threshold can not be larger than 255");
85             }
86             $self->{threshold} = $threshold;
87             return;
88             }
89              
90             sub fullUpdate {
91             my ($self, $img) = @_;
92              
93             my $panelImage;
94             if(!$self->{dithering}) {
95             $panelImage = $self->calculateImage($img);
96             } else {
97             $panelImage = $self->calculateDitheringImage($img);
98             }
99             return $self->writeImage($panelImage, 'U');
100             }
101              
102             sub partialUpdate {
103             my ($self, $img) = @_;
104              
105             my $panelImage;
106             if(!$self->{dithering}) {
107             $panelImage = $self->calculateImage($img);
108             } else {
109             $panelImage = $self->calculateDitheringImage($img);
110             }
111             return $self->writeImage($panelImage, 'P');
112             }
113              
114             sub writeImage {
115             my ($self, $img, $mode) = @_;
116              
117             open(my $ofh, '>', '/dev/epd/display') or croak($!);
118             binmode $ofh;
119             print $ofh $img;
120             close $ofh;
121              
122             open(my $cfh, '>', '/dev/epd/command') or croak($!);
123             print $cfh $mode;
124             close $cfh;
125              
126             return;
127             }
128              
129             sub calculateImage {
130             my ($self, $img) = @_;
131             my $outimg = '';
132              
133             my ($sourcewidth, $sourceheight) = $img->getBounds();
134             if($sourcewidth != $self->{width} || $sourceheight != $self->{height}) {
135             croak('Image dimensions (' . $sourcewidth . 'x' . $sourceheight . ') do not match panel size (' . $self->{width} . 'x' . $self->{height} . ')!');
136             }
137              
138             # We need to read 8 pixels of the image in one go, turn them into pure black&white bits and stuff the 8 of them into a single byte,
139             # correcting for endianess and all that...
140             for(my $y = 0; $y < $self->{height}; $y++) {
141             for(my $x = 0; $x < ($self->{width} / 8); $x++) {
142             my $buf = '';
143             for(my $offs = 0; $offs < 8; $offs++) {
144             my $index = $img->getPixel(($x*8) + $offs,$y);
145             my ($r,$g,$b) = $img->rgb($index);
146             my $grey = int(($r+$g+$b)/3);
147             if($grey > $self->{threshold}) {
148             if($self->{randomize_white} && int(rand(10000)) % 4 == 0) {
149             $buf .= "1";
150             } else {
151             $buf .= "0";
152             }
153             } else {
154             if($self->{randomize_black} && int(rand(10000)) % 4 == 0) {
155             $buf .= "0";
156             } else {
157             $buf .= "1";
158             }
159             }
160             }
161             my $byte = pack('b8', $buf);
162             $outimg .= $byte;
163             }
164             }
165              
166             return $outimg;
167             }
168              
169             sub calculateDitheringImage {
170             my ($self, $img) = @_;
171             my $outimg = '';
172              
173             my ($sourcewidth, $sourceheight) = $img->getBounds();
174             if($sourcewidth != $self->{width} || $sourceheight != $self->{height}) {
175             croak('Image dimensions (' . $sourcewidth . 'x' . $sourceheight . ') do not match panel size (' . $self->{width} . 'x' . $self->{height} . ')!');
176             }
177              
178             # Init array with the greyscale pixel value of the image
179             my @npixel;
180             for(my $x = 0; $x < $self->{width}; $x++) {
181             my @nline;
182             for(my $y = 0; $y < $self->{height}; $y++) {
183             my $index = $img->getPixel($x, $y);
184             my ($r,$g,$b) = $img->rgb($index);
185             my $oldpixel = int(($r+$g+$b)/3);
186             my $newpixel = $oldpixel / 255;
187             push @nline, $newpixel;
188             }
189             $npixel[$x] = \@nline;
190             }
191              
192              
193             # Run dithering
194             for(my $y = 0; $y < $self->{height}; $y++) {
195             for(my $x = 0; $x < $self->{width}; $x++) {
196             my $newpixel = $npixel[$x]->[$y];
197              
198             my $quant_error = (0.5 - $newpixel);
199              
200             # Correct neighboring pixels (if they exist)
201             if(($x + 1) < $self->{width}) {
202             $npixel[$x + 1]->[$y] = $npixel[$x + 1]->[$y] + ($quant_error * 7/16);
203             }
204             if($x > 0 && ($y + 1) < $self->{height}) {
205             $npixel[$x - 1]->[$y + 1] = $npixel[$x - 1]->[$y + 1] + ($quant_error * 3/16);
206             }
207             if(($y + 1) < $self->{height}) {
208             $npixel[$x]->[$y + 1] = $npixel[$x]->[$y + 1] + ($quant_error * 5/16);
209             }
210             if(($x + 1) < $self->{width} && ($y + 1) < $self->{height}) {
211             $npixel[$x + 1]->[$y + 1] = $npixel[$x + 1]->[$y + 1] + ($quant_error * 1/16);
212             }
213             }
214             }
215              
216             # We need to read 8 pixels of the image in one go, turn them into pure black&white bits and stuff the 8 of them into a single byte,
217             # correcting for endianess and all that...
218             for(my $y = 0; $y < $self->{height}; $y++) {
219             for(my $x = 0; $x < ($self->{width} / 8); $x++) {
220             my $buf = '';
221             for(my $offs = 0; $offs < 8; $offs++) {
222             my $raw = $npixel[($x * 8) + $offs]->[$y];
223             if($raw >= 0.5) {
224             $buf .= '1';
225             } else {
226             $buf .= '0';
227             }
228             }
229             my $byte = pack('b8', $buf);
230             $outimg .= $byte;
231             }
232             }
233              
234             return $outimg;
235             }
236              
237              
238             1;
239             __END__