mirror of
https://github.com/chillerlan/php-qrcode.git
synced 2026-08-17 23:01:45 +00:00
158 lines
4.5 KiB
PHP
158 lines
4.5 KiB
PHP
<?php
|
|
/**
|
|
* php-gd realization of QR code with rounded modules
|
|
*
|
|
* @see https://github.com/chillerlan/php-qrcode/pull/215
|
|
* @see https://github.com/chillerlan/php-qrcode/issues/127
|
|
*
|
|
* @created 17.09.2023
|
|
* @author livingroot
|
|
* @copyright 2023 livingroot
|
|
* @license MIT
|
|
*
|
|
* @noinspection PhpComposerExtensionStubsInspection
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
use chillerlan\QRCode\{QRCode, QROptions};
|
|
use chillerlan\QRCode\Common\EccLevel;
|
|
use chillerlan\QRCode\Data\QRMatrix;
|
|
use chillerlan\QRCode\Output\QRGdImagePNG;
|
|
use chillerlan\Settings\SettingsContainerInterface;
|
|
|
|
require_once __DIR__.'/../vendor/autoload.php';
|
|
|
|
// --------------------
|
|
// Class definition
|
|
// --------------------
|
|
|
|
class QRGdRounded extends QRGdImagePNG{
|
|
|
|
public function __construct(SettingsContainerInterface|QROptions|iterable $options, QRMatrix $matrix){
|
|
|
|
if(is_iterable($options)){
|
|
$options = new QROptions($options);
|
|
}
|
|
|
|
// enable the internal scaling for better rounding results at scale < 20
|
|
// we need to do this before calling the parent constructor as these values are used there
|
|
$options->gdImageUseUpscale = true;
|
|
$options->drawCircularModules = true;
|
|
|
|
parent::__construct($options, $matrix);
|
|
}
|
|
|
|
protected function module(int $x, int $y, int $M_TYPE):void{
|
|
|
|
/**
|
|
* The bit order (starting from 0):
|
|
*
|
|
* 0 1 2
|
|
* 7 # 3
|
|
* 6 5 4
|
|
*/
|
|
$neighbours = $this->matrix->checkNeighbours($x, $y);
|
|
|
|
$x1 = ($x * $this->scale);
|
|
$y1 = ($y * $this->scale);
|
|
$x2 = (($x + 1) * $this->scale);
|
|
$y2 = (($y + 1) * $this->scale);
|
|
$rectsize = (int)($this->scale / 2);
|
|
|
|
$light = $this->getModuleValue($M_TYPE);
|
|
$dark = $this->getModuleValue($M_TYPE | QRMatrix::IS_DARK);
|
|
|
|
// ------------------
|
|
// Outer rounding
|
|
// ------------------
|
|
|
|
if(($neighbours & (1 << 7))){ // neighbour left
|
|
// top left
|
|
imagefilledrectangle($this->image, $x1, $y1, ($x1 + $rectsize), ($y1 + $rectsize), $light);
|
|
// bottom left
|
|
imagefilledrectangle($this->image, $x1, ($y2 - $rectsize), ($x1 + $rectsize), $y2, $light);
|
|
}
|
|
|
|
if(($neighbours & (1 << 3))){ // neighbour right
|
|
// top right
|
|
imagefilledrectangle($this->image, ($x2 - $rectsize), $y1, $x2, ($y1 + $rectsize), $light);
|
|
// bottom right
|
|
imagefilledrectangle($this->image, ($x2 - $rectsize), ($y2 - $rectsize), $x2, $y2, $light);
|
|
}
|
|
|
|
if(($neighbours & (1 << 1))){ // neighbour top
|
|
// top left
|
|
imagefilledrectangle($this->image, $x1, $y1, ($x1 + $rectsize), ($y1 + $rectsize), $light);
|
|
// top right
|
|
imagefilledrectangle($this->image, ($x2 - $rectsize), $y1, $x2, ($y1 + $rectsize), $light);
|
|
}
|
|
|
|
if(($neighbours & (1 << 5))){ // neighbour bottom
|
|
// bottom left
|
|
imagefilledrectangle($this->image, $x1, ($y2 - $rectsize), ($x1 + $rectsize), $y2, $light);
|
|
// bottom right
|
|
imagefilledrectangle($this->image, ($x2 - $rectsize), ($y2 - $rectsize), $x2, $y2, $light);
|
|
}
|
|
|
|
// ---------------------
|
|
// inner rounding
|
|
// ---------------------
|
|
|
|
if(!$this->matrix->check($x, $y)){
|
|
|
|
if(($neighbours & 1) && ($neighbours & (1 << 7)) && ($neighbours & (1 << 1))){
|
|
// top left
|
|
imagefilledrectangle($this->image, $x1, $y1, ($x1 + $rectsize), ($y1 + $rectsize), $dark);
|
|
}
|
|
|
|
if(($neighbours & (1 << 1)) && ($neighbours & (1 << 2)) && ($neighbours & (1 << 3))){
|
|
// top right
|
|
imagefilledrectangle($this->image, ($x2 - $rectsize), $y1, $x2, ($y1 + $rectsize), $dark);
|
|
}
|
|
|
|
if(($neighbours & (1 << 7)) && ($neighbours & (1 << 6)) && ($neighbours & (1 << 5))){
|
|
// bottom left
|
|
imagefilledrectangle($this->image, $x1, ($y2 - $rectsize), ($x1 + $rectsize), $y2, $dark);
|
|
}
|
|
|
|
if(($neighbours & (1 << 3)) && ($neighbours & (1 << 4)) && ($neighbours & (1 << 5))){
|
|
// bottom right
|
|
imagefilledrectangle($this->image, ($x2 - $rectsize), ($y2 - $rectsize), $x2, $y2, $dark);
|
|
}
|
|
}
|
|
|
|
imagefilledellipse(
|
|
$this->image,
|
|
(int)($x * $this->scale + $this->scale / 2),
|
|
(int)($y * $this->scale + $this->scale / 2),
|
|
($this->scale - 1),
|
|
($this->scale - 1),
|
|
$light,
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// --------------------
|
|
// Example
|
|
// --------------------
|
|
|
|
$options = new QROptions([
|
|
'version' => 7,
|
|
'eccLevel' => EccLevel::H,
|
|
'outputInterface' => QRGdRounded::class,
|
|
'outputBase64' => false,
|
|
'scale' => 30,
|
|
'addLogoSpace' => true,
|
|
'logoSpaceWidth' => 13,
|
|
'logoSpaceHeight' => 13,
|
|
]);
|
|
|
|
|
|
$img = new QRCode($options)->render('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
|
|
|
|
header('Content-type: image/png');
|
|
|
|
echo $img;
|