Passed
Push — main ( de27a9...f13300 )
by smiley
02:02
created

QRImagick::setBgColor()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 13
rs 10
1
<?php
2
/**
3
 * Class QRImagick
4
 *
5
 * @created      04.07.2018
6
 * @author       smiley <[email protected]>
7
 * @copyright    2018 smiley
8
 * @license      MIT
9
 *
10
 * @noinspection PhpComposerExtensionStubsInspection
11
 */
12
13
namespace chillerlan\QRCode\Output;
14
15
use chillerlan\QRCode\Data\QRMatrix;
16
use chillerlan\Settings\SettingsContainerInterface;
17
use finfo, Imagick, ImagickDraw, ImagickPixel;
18
use function extension_loaded, is_string;
19
use const FILEINFO_MIME_TYPE;
20
21
/**
22
 * ImageMagick output module (requires ext-imagick)
23
 *
24
 * @see http://php.net/manual/book.imagick.php
25
 * @see http://phpimagick.com
26
 */
27
class QRImagick extends QROutputAbstract{
28
29
	/**
30
	 * The main image instance
31
	 */
32
	protected Imagick $imagick;
33
34
	/**
35
	 * The main draw instance
36
	 */
37
	protected ImagickDraw $imagickDraw;
38
39
	/**
40
	 * The allocated background color
41
	 */
42
	protected ImagickPixel $background;
43
44
	/**
45
	 * @inheritDoc
46
	 *
47
	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
48
	 */
49
	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
50
51
		if(!extension_loaded('imagick')){
52
			throw new QRCodeOutputException('ext-imagick not loaded'); // @codeCoverageIgnore
53
		}
54
55
		if(!extension_loaded('fileinfo')){
56
			throw new QRCodeOutputException('ext-fileinfo not loaded'); // @codeCoverageIgnore
57
		}
58
59
		parent::__construct($options, $matrix);
60
	}
61
62
	/**
63
	 * @todo: check/validate possible values
64
	 * @see https://www.php.net/manual/imagickpixel.construct.php
65
	 * @inheritDoc
66
	 */
67
	protected function moduleValueIsValid($value):bool{
68
		return is_string($value);
69
	}
70
71
	/**
72
	 * @inheritDoc
73
	 */
74
	protected function getModuleValue($value):ImagickPixel{
75
		return new ImagickPixel($value);
76
	}
77
78
	/**
79
	 * @inheritDoc
80
	 */
81
	protected function getDefaultModuleValue(bool $isDark):ImagickPixel{
82
		return $this->getModuleValue(($isDark) ? $this->options->markupDark : $this->options->markupLight);
83
	}
84
85
	/**
86
	 * @inheritDoc
87
	 *
88
	 * @return string|\Imagick
89
	 */
90
	public function dump(string $file = null){
91
		$this->imagick = new Imagick;
92
93
		$this->setBgColor();
94
95
		$this->imagick->newImage($this->length, $this->length, $this->background, $this->options->imagickFormat);
96
97
		$this->drawImage();
98
		// set transparency color after all operations
99
		$this->setTransparencyColor();
100
101
		if($this->options->returnResource){
102
			return $this->imagick;
103
		}
104
105
		$imageData = $this->imagick->getImageBlob();
106
107
		$this->imagick->destroy();
108
109
		$this->saveToFile($imageData, $file);
110
111
		if($this->options->imageBase64){
112
			$imageData = $this->toBase64DataURI($imageData, (new finfo(FILEINFO_MIME_TYPE))->buffer($imageData));
113
		}
114
115
		return $imageData;
116
	}
117
118
	/**
119
	 * Sets the background color
120
	 */
121
	protected function setBgColor():void{
122
123
		if(isset($this->background)){
124
			return;
125
		}
126
127
		if($this->moduleValueIsValid($this->options->bgColor)){
128
			$this->background = $this->getModuleValue($this->options->bgColor);
129
130
			return;
131
		}
132
133
		$this->background = $this->getModuleValue('white');
134
	}
135
136
	/**
137
	 * Sets the transparency color
138
	 */
139
	protected function setTransparencyColor():void{
140
141
		if(!$this->options->imageTransparent){
142
			return;
143
		}
144
145
		$transparencyColor = $this->background;
146
147
		if($this->moduleValueIsValid($this->options->transparencyColor)){
148
			$transparencyColor = $this->getModuleValue($this->options->transparencyColor);
149
		}
150
151
		$this->imagick->transparentPaintImage($transparencyColor, 0.0, 10, false);
152
	}
153
154
	/**
155
	 * Creates the QR image via ImagickDraw
156
	 */
157
	protected function drawImage():void{
158
		$this->imagickDraw = new ImagickDraw;
159
		$this->imagickDraw->setStrokeWidth(0);
160
161
		foreach($this->matrix->matrix() as $y => $row){
162
			foreach($row as $x => $M_TYPE){
163
				$this->setPixel($x, $y, $M_TYPE);
164
			}
165
		}
166
167
		$this->imagick->drawImage($this->imagickDraw);
168
	}
169
170
	/**
171
	 * draws a single pixel at the given position
172
	 */
173
	protected function setPixel(int $x, int $y, int $M_TYPE):void{
174
175
		if(!$this->options->drawLightModules && !$this->matrix->check($x, $y)){
176
			return;
177
		}
178
179
		$this->imagickDraw->setFillColor($this->moduleValues[$M_TYPE]);
180
181
		$this->options->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, $this->options->keepAsSquare)
0 ignored issues
show
Bug introduced by
It seems like $this->options->keepAsSquare can also be of type null; however, parameter $M_TYPES of chillerlan\QRCode\Data\QRMatrix::checkTypeIn() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

181
		$this->options->drawCircularModules && !$this->matrix->checkTypeIn($x, $y, /** @scrutinizer ignore-type */ $this->options->keepAsSquare)
Loading history...
182
			? $this->imagickDraw->circle(
183
				(($x + 0.5) * $this->scale),
184
				(($y + 0.5) * $this->scale),
185
				(($x + 0.5 + $this->options->circleRadius) * $this->scale),
186
				(($y + 0.5) * $this->scale)
187
			)
188
			: $this->imagickDraw->rectangle(
189
				($x * $this->scale),
190
				($y * $this->scale),
191
				(($x + 1) * $this->scale),
192
				(($y + 1) * $this->scale)
193
			);
194
	}
195
196
}
197