ImageRenderer.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. <?php
  2. declare(strict_types = 1);
  3. namespace BaconQrCode\Renderer;
  4. use BaconQrCode\Encoder\MatrixUtil;
  5. use BaconQrCode\Encoder\QrCode;
  6. use BaconQrCode\Exception\InvalidArgumentException;
  7. use BaconQrCode\Renderer\Image\ImageBackEndInterface;
  8. use BaconQrCode\Renderer\Path\Path;
  9. use BaconQrCode\Renderer\RendererStyle\EyeFill;
  10. use BaconQrCode\Renderer\RendererStyle\RendererStyle;
  11. final class ImageRenderer implements RendererInterface
  12. {
  13. /**
  14. * @var RendererStyle
  15. */
  16. private $rendererStyle;
  17. /**
  18. * @var ImageBackEndInterface
  19. */
  20. private $imageBackEnd;
  21. public function __construct(RendererStyle $rendererStyle, ImageBackEndInterface $imageBackEnd)
  22. {
  23. $this->rendererStyle = $rendererStyle;
  24. $this->imageBackEnd = $imageBackEnd;
  25. }
  26. /**
  27. * @throws InvalidArgumentException if matrix width doesn't match height
  28. */
  29. public function render(QrCode $qrCode) : string
  30. {
  31. $size = $this->rendererStyle->getSize();
  32. $margin = $this->rendererStyle->getMargin();
  33. $matrix = $qrCode->getMatrix();
  34. $matrixSize = $matrix->getWidth();
  35. if ($matrixSize !== $matrix->getHeight()) {
  36. throw new InvalidArgumentException('Matrix must have the same width and height');
  37. }
  38. $totalSize = $matrixSize + ($margin * 2);
  39. $moduleSize = $size / $totalSize;
  40. $fill = $this->rendererStyle->getFill();
  41. $this->imageBackEnd->new($size, $fill->getBackgroundColor());
  42. $this->imageBackEnd->scale((float) $moduleSize);
  43. $this->imageBackEnd->translate((float) $margin, (float) $margin);
  44. $module = $this->rendererStyle->getModule();
  45. $moduleMatrix = clone $matrix;
  46. MatrixUtil::removePositionDetectionPatterns($moduleMatrix);
  47. $modulePath = $this->drawEyes($matrixSize, $module->createPath($moduleMatrix));
  48. if ($fill->hasGradientFill()) {
  49. $this->imageBackEnd->drawPathWithGradient(
  50. $modulePath,
  51. $fill->getForegroundGradient(),
  52. 0,
  53. 0,
  54. $matrixSize,
  55. $matrixSize
  56. );
  57. } else {
  58. $this->imageBackEnd->drawPathWithColor($modulePath, $fill->getForegroundColor());
  59. }
  60. return $this->imageBackEnd->done();
  61. }
  62. private function drawEyes(int $matrixSize, Path $modulePath) : Path
  63. {
  64. $fill = $this->rendererStyle->getFill();
  65. $eye = $this->rendererStyle->getEye();
  66. $externalPath = $eye->getExternalPath();
  67. $internalPath = $eye->getInternalPath();
  68. $modulePath = $this->drawEye(
  69. $externalPath,
  70. $internalPath,
  71. $fill->getTopLeftEyeFill(),
  72. 3.5,
  73. 3.5,
  74. 0,
  75. $modulePath
  76. );
  77. $modulePath = $this->drawEye(
  78. $externalPath,
  79. $internalPath,
  80. $fill->getTopRightEyeFill(),
  81. $matrixSize - 3.5,
  82. 3.5,
  83. 90,
  84. $modulePath
  85. );
  86. $modulePath = $this->drawEye(
  87. $externalPath,
  88. $internalPath,
  89. $fill->getBottomLeftEyeFill(),
  90. 3.5,
  91. $matrixSize - 3.5,
  92. -90,
  93. $modulePath
  94. );
  95. return $modulePath;
  96. }
  97. private function drawEye(
  98. Path $externalPath,
  99. Path $internalPath,
  100. EyeFill $fill,
  101. float $xTranslation,
  102. float $yTranslation,
  103. int $rotation,
  104. Path $modulePath
  105. ) : Path {
  106. if ($fill->inheritsBothColors()) {
  107. return $modulePath
  108. ->append($externalPath->translate($xTranslation, $yTranslation))
  109. ->append($internalPath->translate($xTranslation, $yTranslation));
  110. }
  111. $this->imageBackEnd->push();
  112. $this->imageBackEnd->translate($xTranslation, $yTranslation);
  113. if (0 !== $rotation) {
  114. $this->imageBackEnd->rotate($rotation);
  115. }
  116. if ($fill->inheritsExternalColor()) {
  117. $modulePath = $modulePath->append($externalPath->translate($xTranslation, $yTranslation));
  118. } else {
  119. $this->imageBackEnd->drawPathWithColor($externalPath, $fill->getExternalColor());
  120. }
  121. if ($fill->inheritsInternalColor()) {
  122. $modulePath = $modulePath->append($internalPath->translate($xTranslation, $yTranslation));
  123. } else {
  124. $this->imageBackEnd->drawPathWithColor($internalPath, $fill->getInternalColor());
  125. }
  126. $this->imageBackEnd->pop();
  127. return $modulePath;
  128. }
  129. }