RllCodec::compressRuns()   B
last analyzed

Complexity

Conditions 7
Paths 20

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 18
c 1
b 0
f 0
nc 20
nop 1
dl 0
loc 32
ccs 0
cts 25
cp 0
crap 56
rs 8.8333
1
<?php
2
3
/**
4
 * This file is part of PhpAidc LabelPrinter package.
5
 *
6
 * © Appwilio (https://appwilio.com)
7
 * © JhaoDa (https://github.com/jhaoda)
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
declare(strict_types=1);
14
15
namespace PhpAidc\LabelPrinter\Emulation;
16
17
final class RllCodec
18
{
19
    private const MAX_RUN_LENGTH = 127;
20
    private const MAX_LINE_REPETITIONS = 128;
21
22
    /** @var \Imagick */
23
    private $image;
24
25
    public static function create(\Imagick $image): self
26
    {
27
        return new self($image);
28
    }
29
30
    public function __construct(\Imagick $image)
31
    {
32
        $this->image = $image;
33
    }
34
35
    public function encode(): string
36
    {
37
        return \pack('C*', ...\array_merge(...$this->compressLines($this->scan())));
38
    }
39
40
    private function scan(): \Generator
41
    {
42
        foreach ((new \ImagickPixelIterator($this->image)) as $y => $line) {
43
            $buffer = \array_reduce($line, static function ($carry, $pixel) {
44
                $color = $pixel->getColor();
45
46
                $carry[] = 1 - ((int) (($color['r'] + $color['g'] + $color['b']) / 3) >> 7);
47
48
                return $carry;
49
            }, []);
50
51
            yield $this->compressRuns($buffer);
52
        }
53
    }
54
55
    private function compressRuns(array $line): array
56
    {
57
        $prev = null;
58
        $buffer = [];
59
        $runLength = 0;
60
        $size = \count($line);
61
62
        foreach ($line as $i => $byte) {
63
            if ($prev === null || $prev === $byte) {
64
                $runLength++;
65
            } else {
66
                $this->encodeRun($buffer, $runLength);
67
68
                $runLength = 1;
69
            }
70
71
            if ($i + 1 === $size) {
72
                $this->encodeRun($buffer, $runLength);
73
            }
74
75
            $prev = $byte;
76
        }
77
78
        if ($line[0] === 1) {
79
            \array_unshift($buffer, 0);
80
        }
81
82
        if (\end($line) === 1) {
83
            $buffer[] = 0;
84
        }
85
86
        return $buffer;
87
    }
88
89
    private function encodeRun(array &$buffer, int $length)
90
    {
91
        if ($length < self::MAX_RUN_LENGTH) {
92
            $buffer[] = \max($length, 1);
93
94
            return;
95
        }
96
97
        \array_map(static function () use (&$buffer) {
98
            $buffer[] = self::MAX_RUN_LENGTH;
99
            $buffer[] = 0;
100
        }, \range(1, \intdiv($length, self::MAX_RUN_LENGTH)));
101
102
        $buffer[] = $length % self::MAX_RUN_LENGTH;
103
    }
104
105
    private function compressLines(\Generator $lines): array
106
    {
107
        $count = 1;
108
        $line = [];
109
        $buffer = [];
110
        $prev = $lines->current();
111
112
        $lines->next();
113
114
        while ($lines->valid()) {
115
            $line = $lines->current();
116
117
            if ($prev === $line) {
118
                $count++;
119
120
                $prev = $line;
121
122
                $lines->next();
123
124
                continue;
125
            }
126
127
            $this->encodeLineRepetitions($buffer, $prev, $count);
128
129
            $count = 1;
130
131
            $prev = $line;
132
133
            $lines->next();
134
        }
135
136
        $this->encodeLineRepetitions($buffer, $line, $count);
137
138
        return $buffer;
139
    }
140
141
    private function encodeLineRepetitions(array &$buffer, array $line, int $count): void
142
    {
143
        if ($count <= 1) {
144
            $buffer[] = $line;
145
        } elseif ($count > 1 && $count <= self::MAX_LINE_REPETITIONS) {
146
            $buffer[] = $this->buildLineRepetition($line, $count);
147
        } else {
148
            \array_map(function () use (&$buffer, $line) {
149
                $buffer[] = $this->buildLineRepetition($line, self::MAX_LINE_REPETITIONS);
150
            }, \range(1, \intdiv($count, self::MAX_LINE_REPETITIONS)));
151
152
            $buffer[] = $this->buildLineRepetition($line, $count % self::MAX_LINE_REPETITIONS);
153
        }
154
    }
155
156
    private function buildLineRepetition(array $line, int $count): array
157
    {
158
        return \array_merge([-$count], $line, [-$count]);
159
    }
160
}
161