Skip to content

Commit 9b54d1d

Browse files
committed
:octocat: circular modules in GD output
1 parent e5948ad commit 9b54d1d

File tree

5 files changed

+75
-38
lines changed

5 files changed

+75
-38
lines changed

examples/image.php

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,51 +11,63 @@
1111
use chillerlan\QRCode\{QRCode, QROptions};
1212
use chillerlan\QRCode\Data\QRMatrix;
1313
use chillerlan\QRCode\Common\EccLevel;
14+
use Throwable;
1415

1516
require_once __DIR__.'/../vendor/autoload.php';
1617

1718
$data = 'https://www.youtube.com/watch?v=DLzxrzFCyOs&t=43s';
1819

1920
$options = new QROptions([
20-
'version' => 5,
21-
'outputType' => QRCode::OUTPUT_IMAGE_PNG,
22-
'eccLevel' => EccLevel::L,
23-
'scale' => 5,
24-
'imageBase64' => false,
25-
'moduleValues' => [
21+
'version' => 7,
22+
'outputType' => QRCode::OUTPUT_IMAGE_PNG,
23+
'eccLevel' => EccLevel::L,
24+
'scale' => 10,
25+
'imageBase64' => false,
26+
'imageTransparent' => false,
27+
'drawCircularModules' => true,
28+
'circleRadius' => 0.4,
29+
'keepAsSquare' => [QRMatrix::M_FINDER|QRMatrix::IS_DARK, QRMatrix::M_FINDER_DOT, QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK],
30+
'moduleValues' => [
2631
// finder
2732
QRMatrix::M_FINDER | QRMatrix::IS_DARK => [0, 63, 255], // dark (true)
28-
QRMatrix::M_FINDER => [255, 255, 255], // light (false), white is the transparency color and is enabled by default
29-
QRMatrix::M_FINDER_DOT | QRMatrix::IS_DARK => [241, 28, 163], // finder dot, dark (true)
33+
QRMatrix::M_FINDER => [233, 233, 233], // light (false), white is the transparency color and is enabled by default
34+
QRMatrix::M_FINDER_DOT | QRMatrix::IS_DARK => [0, 63, 255], // finder dot, dark (true)
3035
// alignment
3136
QRMatrix::M_ALIGNMENT | QRMatrix::IS_DARK => [255, 0, 255],
32-
QRMatrix::M_ALIGNMENT => [255, 255, 255],
37+
QRMatrix::M_ALIGNMENT => [233, 233, 233],
3338
// timing
3439
QRMatrix::M_TIMING | QRMatrix::IS_DARK => [255, 0, 0],
35-
QRMatrix::M_TIMING => [255, 255, 255],
40+
QRMatrix::M_TIMING => [233, 233, 233],
3641
// format
37-
QRMatrix::M_FORMAT | QRMatrix::IS_DARK => [67, 99, 84],
38-
QRMatrix::M_FORMAT => [255, 255, 255],
42+
QRMatrix::M_FORMAT | QRMatrix::IS_DARK => [67, 159, 84],
43+
QRMatrix::M_FORMAT => [233, 233, 233],
3944
// version
4045
QRMatrix::M_VERSION | QRMatrix::IS_DARK => [62, 174, 190],
41-
QRMatrix::M_VERSION => [255, 255, 255],
46+
QRMatrix::M_VERSION => [233, 233, 233],
4247
// data
4348
QRMatrix::M_DATA | QRMatrix::IS_DARK => [0, 0, 0],
44-
QRMatrix::M_DATA => [255, 255, 255],
49+
QRMatrix::M_DATA => [233, 233, 233],
4550
// darkmodule
4651
QRMatrix::M_DARKMODULE | QRMatrix::IS_DARK => [0, 0, 0],
4752
// separator
48-
QRMatrix::M_SEPARATOR => [255, 255, 255],
53+
QRMatrix::M_SEPARATOR => [233, 233, 233],
4954
// quietzone
50-
QRMatrix::M_QUIETZONE => [255, 255, 255],
55+
QRMatrix::M_QUIETZONE => [233, 233, 233],
5156
// logo (requires a call to QRMatrix::setLogoSpace()), see QRImageWithLogo
52-
QRMatrix::M_LOGO => [255, 255, 255],
57+
QRMatrix::M_LOGO => [233, 233, 233],
5358
],
5459
]);
5560

61+
try{
62+
$im = (new QRCode($options))->render($data);
63+
}
64+
catch(Throwable $e){
65+
exit($e->getMessage());
66+
}
67+
5668
header('Content-type: image/png');
5769

58-
echo (new QRCode($options))->render($data);
70+
echo $im;
5971

6072

6173

examples/svg.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
'markupDark' => '',
3030
'markupLight' => '',
3131
// draw the modules as circles isntead of squares
32-
'svgDrawCircularModules' => true,
33-
'svgCircleRadius' => 0.3,
32+
'drawCircularModules' => true,
33+
'circleRadius' => 0.4,
3434
// keep modules of thhese types as square
35-
'svgKeepAsSquare' => [QRMatrix::M_FINDER|QRMatrix::IS_DARK, QRMatrix::M_FINDER_DOT, QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK],
35+
'keepAsSquare' => [QRMatrix::M_FINDER|QRMatrix::IS_DARK, QRMatrix::M_FINDER_DOT, QRMatrix::M_ALIGNMENT|QRMatrix::IS_DARK],
3636
// connect
3737
'svgConnectPaths' => true,
3838
// https://developer.mozilla.org/en-US/docs/Web/SVG/Element/linearGradient

src/Output/QRImage.php

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
use chillerlan\Settings\SettingsContainerInterface;
1818
use Exception;
1919

20-
use function array_values, call_user_func, count, extension_loaded, imagecolorallocate, imagecolortransparent,
21-
imagecreatetruecolor, imagedestroy, imagefilledrectangle, imagegif, imagejpeg, imagepng, in_array,
20+
use function array_values, count, extension_loaded, imagecolorallocate, imagecolortransparent, imagecreatetruecolor,
21+
imagedestroy, imagefilledellipse, imagefilledrectangle, imagegif, imagejpeg, imagepng, imagescale, in_array,
2222
is_array, ob_end_clean, ob_get_contents, ob_start, range;
23+
use const IMG_BICUBIC;
2324

2425
/**
2526
* Converts the matrix into GD images, raw or base64 output (requires ext-gd)
@@ -93,6 +94,12 @@ protected function setModuleValues():void{
9394
public function dump(string $file = null){
9495
$file ??= $this->options->cachefile;
9596

97+
// we're scaling the image up in order to draw crisp round circles, otherwise they appear square-y on small scales
98+
if($this->options->drawCircularModules && $this->options->scale <= 20){
99+
$this->length = ($this->length + 2) * 10;
100+
$this->scale *= 10;
101+
}
102+
96103
$this->image = imagecreatetruecolor($this->length, $this->length);
97104

98105
// avoid: "Indirect modification of overloaded property $imageTransparencyBG has no effect"
@@ -113,6 +120,11 @@ public function dump(string $file = null){
113120
}
114121
}
115122

123+
// scale down to the expected size
124+
if($this->options->drawCircularModules && $this->options->scale <= 20){
125+
$this->image = imagescale($this->image, $this->length/10, $this->length/10, IMG_BICUBIC);
126+
}
127+
116128
if($this->options->returnResource){
117129
return $this->image;
118130
}
@@ -134,15 +146,26 @@ public function dump(string $file = null){
134146
* Creates a single QR pixel with the given settings
135147
*/
136148
protected function setPixel(int $x, int $y, int $M_TYPE):void{
137-
imagefilledrectangle(
138-
$this->image,
139-
$x * $this->scale,
140-
$y * $this->scale,
141-
($x + 1) * $this->scale,
142-
($y + 1) * $this->scale,
143-
/** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
144-
imagecolorallocate($this->image, ...$this->moduleValues[$M_TYPE])
145-
);
149+
/** @phan-suppress-next-line PhanParamTooFewInternalUnpack */
150+
$color = imagecolorallocate($this->image, ...$this->moduleValues[$M_TYPE]);
151+
152+
$this->options->drawCircularModules && !$this->matrix->checkTypes($x, $y, $this->options->keepAsSquare)
153+
? imagefilledellipse(
154+
$this->image,
155+
($x * $this->scale) + ($this->scale / 2),
156+
($y * $this->scale) + ($this->scale / 2),
157+
2 * $this->options->circleRadius * $this->scale,
158+
2 * $this->options->circleRadius * $this->scale,
159+
$color
160+
)
161+
: imagefilledrectangle(
162+
$this->image,
163+
$x * $this->scale,
164+
$y * $this->scale,
165+
($x + 1) * $this->scale,
166+
($y + 1) * $this->scale,
167+
$color
168+
);
146169
}
147170

148171
/**
@@ -154,7 +177,7 @@ protected function dumpImage():string{
154177
ob_start();
155178

156179
try{
157-
call_user_func([$this, $this->outputMode ?? $this->defaultMode]);
180+
$this->{$this->outputMode ?? $this->defaultMode}();
158181
}
159182
// not going to cover edge cases
160183
// @codeCoverageIgnoreStart

src/Output/QRMarkup.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ protected function svgModule(int $x, int $y):string{
189189
return '';
190190
}
191191

192-
if($this->options->svgDrawCircularModules && !$this->matrix->checkTypes($x, $y, $this->options->svgKeepAsSquare)){
193-
$r = $this->options->svgCircleRadius;
192+
if($this->options->drawCircularModules && !$this->matrix->checkTypes($x, $y, $this->options->keepAsSquare)){
193+
$r = $this->options->circleRadius;
194194

195195
return sprintf(
196196
'M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s,%3$s 0 1 0 -%4$s 0Z',

src/QROptionsTrait.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,18 +162,20 @@ trait QROptionsTrait{
162162

163163
/**
164164
* specify whether to draw the modules as filled circles
165+
*
166+
* @see https://github.com/chillerlan/php-qrcode/issues/23
165167
*/
166-
protected bool $svgDrawCircularModules = false;
168+
protected bool $drawCircularModules = false;
167169

168170
/**
169171
* specifies the radius of the modules when $svgDrawCircularModules is set to true
170172
*/
171-
protected float $svgCircleRadius = 0.45;
173+
protected float $circleRadius = 0.45;
172174

173175
/**
174176
* specifies which module types to exclude when $svgDrawCircularModules is set to true
175177
*/
176-
protected array $svgKeepAsSquare = [];
178+
protected array $keepAsSquare = [];
177179

178180
/**
179181
* string substitute for dark

0 commit comments

Comments
 (0)