Completed
Push — v3.2.x ( cba5a1...08b96c )
by smiley
01:23
created

QRImage   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 178
rs 10
c 0
b 0
f 0
wmc 23
lcom 1
cbo 5

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A setModuleValues() 0 17 5
B dump() 0 32 7
A setPixel() 0 10 1
A dumpImage() 0 26 3
A png() 0 9 2
A gif() 0 3 1
A jpg() 0 9 2
1
<?php
2
/**
3
 * Class QRImage
4
 *
5
 * @filesource   QRImage.php
6
 * @created      05.12.2015
7
 * @package      chillerlan\QRCode\Output
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2015 Smiley
10
 * @license      MIT
11
 *
12
 * @noinspection PhpComposerExtensionStubsInspection
13
 */
14
15
namespace chillerlan\QRCode\Output;
16
17
use chillerlan\QRCode\Data\QRMatrix;
18
use chillerlan\QRCode\{QRCode, QRCodeException};
19
use chillerlan\Settings\SettingsContainerInterface;
20
use Exception;
21
22
use function array_values, base64_encode, call_user_func, count, imagecolorallocate, imagecolortransparent,
23
	imagecreatetruecolor, imagedestroy, imagefilledrectangle, imagegif, imagejpeg, imagepng, in_array,
24
	is_array, ob_end_clean, ob_get_contents, ob_start, range, sprintf;
25
26
/**
27
 * Converts the matrix into GD images, raw or base64 output
28
 * requires ext-gd
29
 * @link http://php.net/manual/book.image.php
30
 */
31
class QRImage extends QROutputAbstract{
32
33
	protected const TRANSPARENCY_TYPES = [
34
		QRCode::OUTPUT_IMAGE_PNG,
35
		QRCode::OUTPUT_IMAGE_GIF,
36
	];
37
38
	/**
39
	 * @var string
40
	 */
41
	protected $defaultMode = QRCode::OUTPUT_IMAGE_PNG;
42
43
	/**
44
	 * @see imagecreatetruecolor()
45
	 * @var resource
46
	 */
47
	protected $image;
48
49
	/**
50
	 * @inheritDoc
51
	 *
52
	 * @throws \chillerlan\QRCode\QRCodeException
53
	 */
54
	public function __construct(SettingsContainerInterface $options, QRMatrix $matrix){
55
56
		if(!extension_loaded('gd')){
57
			throw new QRCodeException('ext-gd not loaded'); // @codeCoverageIgnore
58
		}
59
60
		parent::__construct($options, $matrix);
61
	}
62
63
	/**
64
	 * @inheritDoc
65
	 */
66
	protected function setModuleValues():void{
67
68
		foreach($this::DEFAULT_MODULE_VALUES as $M_TYPE => $defaultValue){
69
			$v = $this->options->moduleValues[$M_TYPE] ?? null;
70
71
			if(!is_array($v) || count($v) < 3){
72
				$this->moduleValues[$M_TYPE] = $defaultValue
73
					? [0, 0, 0]
74
					: [255, 255, 255];
75
			}
76
			else{
77
				$this->moduleValues[$M_TYPE] = array_values($v);
78
			}
79
80
		}
81
82
	}
83
84
	/**
85
	 * @inheritDoc
86
	 *
87
	 * @return string|resource
88
	 */
89
	public function dump(string $file = null){
90
		$this->image = imagecreatetruecolor($this->length, $this->length);
91
92
		// avoid: Indirect modification of overloaded property $imageTransparencyBG has no effect
93
		// https://stackoverflow.com/a/10455217
94
		$tbg = $this->options->imageTransparencyBG;
95
		$background  = imagecolorallocate($this->image, ...$tbg);
96
97
		if((bool)$this->options->imageTransparent && in_array($this->options->outputType, $this::TRANSPARENCY_TYPES, true)){
98
			imagecolortransparent($this->image, $background);
99
		}
100
101
		imagefilledrectangle($this->image, 0, 0, $this->length, $this->length, $background);
102
103
		foreach($this->matrix->matrix() as $y => $row){
104
			foreach($row as $x => $M_TYPE){
105
				$this->setPixel($x, $y, $this->moduleValues[$M_TYPE]);
106
			}
107
		}
108
109
		if($this->options->returnResource){
110
			return $this->image;
111
		}
112
113
		$imageData = $this->dumpImage($file);
114
115
		if($this->options->imageBase64){
116
			$imageData = sprintf('data:image/%s;base64,%s', $this->options->outputType, base64_encode($imageData));
117
		}
118
119
		return $imageData;
120
	}
121
122
	/**
123
	 * @param int   $x
124
	 * @param int   $y
125
	 * @param array $rgb
126
	 *
127
	 * @return void
128
	 */
129
	protected function setPixel(int $x, int $y, array $rgb):void{
130
		imagefilledrectangle(
131
			$this->image,
132
			$x * $this->scale,
133
			$y * $this->scale,
134
			($x + 1) * $this->scale,
135
			($y + 1) * $this->scale,
136
			imagecolorallocate($this->image, ...$rgb)
137
		);
138
	}
139
140
	/**
141
	 * @param string|null $file
142
	 *
143
	 * @return string
144
145
	 * @throws \chillerlan\QRCode\Output\QRCodeOutputException
146
	 */
147
	protected function dumpImage(string $file = null):string{
148
		$file = $file ?? $this->options->cachefile;
149
150
		ob_start();
151
152
		try{
153
			call_user_func([$this, $this->outputMode ?? $this->defaultMode]);
154
		}
155
		// not going to cover edge cases
156
		// @codeCoverageIgnoreStart
157
		catch(Exception $e){
158
			throw new QRCodeOutputException($e->getMessage());
159
		}
160
		// @codeCoverageIgnoreEnd
161
162
		$imageData = ob_get_contents();
163
		imagedestroy($this->image);
164
165
		ob_end_clean();
166
167
		if($file !== null){
168
			$this->saveToFile($imageData, $file);
169
		}
170
171
		return $imageData;
172
	}
173
174
	/**
175
	 * @return void
176
	 */
177
	protected function png():void{
178
		imagepng(
179
			$this->image,
180
			null,
181
			in_array($this->options->pngCompression, range(-1, 9), true)
182
				? $this->options->pngCompression
183
				: -1
184
		);
185
	}
186
187
	/**
188
	 * Jiff - like... JitHub!
189
	 * @return void
190
	 */
191
	protected function gif():void{
192
		imagegif($this->image);
193
	}
194
195
	/**
196
	 * @return void
197
	 */
198
	protected function jpg():void{
199
		imagejpeg(
200
			$this->image,
201
			null,
202
			in_array($this->options->jpegQuality, range(0, 100), true)
203
				? $this->options->jpegQuality
204
				: 85
205
		);
206
	}
207
208
}
209