Skip to content

Commit

Permalink
Merge pull request #63 from sitegeist/feature/dataSrc
Browse files Browse the repository at this point in the history
FEATURE: Add dataSrc method to image source interface
  • Loading branch information
mficzel committed Aug 29, 2023
2 parents 8874601 + df39bd6 commit dab3160
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 286 deletions.
17 changes: 7 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,15 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0']
neos-versions: ['7.3']
php-versions: ['8.0', '8.1']
neos-versions: ['8.3']
include:
- php-versions: '7.3'
neos-versions: '5.3'
- php-versions: '7.3'
neos-versions: '7.0'
- php-versions: '7.3'
neos-versions: '7.1'
- php-versions: '7.3'
neos-versions: '7.2'

neos-versions: '7.3'
- php-versions: '7.4'
neos-versions: '7.3'
- php-versions: '8.0'
neos-versions: '8.0'
runs-on: ubuntu-latest

steps:
Expand Down
307 changes: 33 additions & 274 deletions Classes/Controller/DummyImageController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,34 @@

namespace Sitegeist\Kaleidoscope\Controller;

use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
use Imagine\Image\ImagineInterface;
use Imagine\Image\Palette;
use Imagine\Image\Palette\Color\ColorInterface;
use Imagine\Image\Point;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Log\Utility\LogEnvironment;
use Neos\Flow\Mvc\Controller\ActionController;
use Neos\Flow\Package\Exception\UnknownPackageException;
use Neos\Flow\Package\PackageManager;
use Neos\Flow\ResourceManagement\ResourceManager;
use Neos\Media\Domain\Model\ImageInterface;
use Psr\Log\LoggerInterface;
use Sitegeist\Kaleidoscope\Domain\DummyImageGenerator;

class DummyImageController extends ActionController
{
/**
* @var ImagineInterface
* @var DummyImageGenerator
*
* @Flow\Inject
*/
protected $imagineService;
protected $dummyImageService;

/**
* @var ResourceManager
*
* @Flow\Inject
*/
protected $resourceManager;

/**
* @var PackageManager
*
* @Flow\Inject
*/
protected $packageManager;
Expand All @@ -52,19 +50,6 @@ class DummyImageController extends ActionController
*/
protected $settings;

/*
* Override the default Imagine driver from Neos.Imagine
* with a static Imagick instance,
* because Vips does not yet support draw
*/
public function initializeObject(): void
{
if (isset($this->settings['dummyImage']['overrideImagineDriver']) && $this->settings['dummyImage']['overrideImagineDriver'] !== false) {
$className = 'Imagine\\'.$this->settings['dummyImage']['overrideImagineDriver'].'\\Imagine';
$this->imagineService = new $className();
}
}

/**
* Get a dummy-image.
*
Expand All @@ -79,77 +64,37 @@ public function initializeObject(): void
*/
public function imageAction(int $w = 600, int $h = 400, string $bg = '#000', string $fg = '#fff', string $t = null, string $f = 'png'): string
{
// limit input arguments
if ($w > 9999) {
$w = 9999;
} elseif ($w < 10) {
$w = 10;
}

if ($h > 9999) {
$h = 9999;
} elseif ($h < 10) {
$h = 10;
}

$width = $w;
$height = $h;

try {
$palette = new Palette\RGB();
$backgroundColor = $palette->color($bg);
$foregroundColor = $palette->color($fg);

// create image
$imageBox = new Box($width, $height);
$image = $this->imagineService->create($imageBox);
$image->usePalette($palette);

$renderBorder = ($width >= 70 && $height >= 70);
$renderShape = ($width >= 200 && $height >= 100);
$renderText = ($width >= 50 && $height >= 30);
$renderPattern = ($width >= 20 && $height >= 20);

$this->renderBackground($image, $foregroundColor, $backgroundColor, $width, $height);

if ($renderShape) {
$this->renderShape($image, $foregroundColor, $backgroundColor, $width, $height);
}

if ($renderBorder) {
$this->renderBorder($image, $foregroundColor, $backgroundColor, $width, $height);
}

if ($renderText) {
$text = trim((string) $t) ?: sprintf('%s×%s', $width, $height);
$this->renderText($image, $foregroundColor, $width, $height, $text, $renderShape ? false : true);
}
$dummyImage = $this->dummyImageService->createDummyImage($w, $h, $bg, $fg, $t, $f);

if ($dummyImage instanceof ImageInterface) {
// render image
try {
$result = $dummyImage->get($f);
} catch (\RuntimeException $e) {
// Render image as png if get() method fails
$result = $dummyImage->get($this->settings['dummyImage']['fallbackFormat']);
}
if (!$result) {
throw new \RuntimeException('Something went wrong without throwing an exception');
}

if ($renderPattern) {
$this->renderPattern($image, $renderShape ? $backgroundColor : $foregroundColor, $width, $height);
}
// build result
/** @phpstan-ignore-next-line */
if (method_exists($this->response, 'setHttpHeader')) {
$this->response->setHttpHeader('Cache-Control', 'max-age=883000000');
} elseif (method_exists($this->response, 'setComponentParameter') && class_exists('\Neos\Flow\Http\Component\SetHeaderComponent')) {
$this->response->setComponentParameter(\Neos\Flow\Http\Component\SetHeaderComponent::class, 'Cache-Control', 'max-age=883000000');
}
$this->response->setContentType('image/'.$f);

// render image
try {
$result = $image->get($f);
} catch (\RuntimeException $e) {
// Render image as png if get() method fails
$result = $image->get($this->settings['dummyImage']['fallbackFormat']);
}
if (!$result) {
throw new \RuntimeException('Something went wrong without throwing an exception');
}
return $result;
} else {
$this->response->setStatusCode(500);
$this->response->setContentType('image/png');

// build result
/** @phpstan-ignore-next-line */
if (method_exists($this->response, 'setHttpHeader')) {
$this->response->setHttpHeader('Cache-Control', 'max-age=883000000');
} elseif (method_exists($this->response, 'setComponentParameter') && class_exists('\Neos\Flow\Http\Component\SetHeaderComponent')) {
$this->response->setComponentParameter(\Neos\Flow\Http\Component\SetHeaderComponent::class, 'Cache-Control', 'max-age=883000000');
return file_get_contents('resource://Sitegeist.Kaleidoscope/Public/Images/imageError.png') ?: '';
}
$this->response->setContentType('image/'.$f);

return $result;
} catch (\Exception $exception) {
$this->logger->error($exception->getMessage(), LogEnvironment::fromMethodName(__METHOD__));

Expand All @@ -160,190 +105,4 @@ public function imageAction(int $w = 600, int $h = 400, string $bg = '#000', str
return file_get_contents('resource://Sitegeist.Kaleidoscope/Public/Images/imageError.png') ?: '';
}
}

/**
* @param ImageInterface $image
* @param ColorInterface $foregroundColor
* @param ColorInterface $backgroundColor
* @param int $width
* @param int $height
*/
protected function renderBackground(ImageInterface $image, ColorInterface $foregroundColor, ColorInterface $backgroundColor, int $width, int $height): void
{
$image->draw()->polygon(
[
new Point(0, 0),
new Point($width, 0),
new Point($width, $height),
new Point(0, $height),
],
$backgroundColor,
true,
1
);
}

/**
* @param ImageInterface $image
* @param ColorInterface $foregroundColor
* @param ColorInterface $backgroundColor
* @param int $width
* @param int $height
*/
protected function renderShape(ImageInterface $image, ColorInterface $foregroundColor, ColorInterface $backgroundColor, int $width, int $height): void
{
$imageAspectRatio = $width / $height;
$baseShapeWidth = 600;
$baseShapeHeight = 400;
$baseShapeAspectRatio = $baseShapeWidth / $baseShapeHeight;

/**
* @var Point[] $baseShape
*/
$baseShape = [
new Point(0, 250), // left ground
new Point(0, 0), // left top
new Point(600, 0), // right top
new Point(600, 250), // right ground
new Point(580, 250),

new Point(440, 110), // small mountain
new Point(360, 190), // saddle
new Point(220, 50), // big mountain

new Point(20, 250),
];

// transform shape to center of the image
$factor = ($imageAspectRatio > $baseShapeAspectRatio) ? (float) $height / (float) $baseShapeHeight : (float) $width / (float) $baseShapeWidth;
$xOffset = ($imageAspectRatio > $baseShapeAspectRatio) ? ($width - ($baseShapeWidth * $factor)) / 2.0 : 0.0;
$yOffset = ($imageAspectRatio < $baseShapeAspectRatio) ? ($height - ($baseShapeHeight * $factor)) / 2.0 : 0.0;

/**
* @var Point[] $transformedShape
*/
$transformedShape = array_map(
static function (Point $point) use ($factor, $xOffset, $yOffset) {
return new Point((int) ($point->getX() * $factor + $xOffset), (int) ($point->getY() * $factor + $yOffset));
},
$baseShape
);

// adjust some points based on aspect ratio
$transformedShape[0] = new Point(0, $transformedShape[0]->getY());
$transformedShape[1] = new Point(0, 0);
$transformedShape[2] = new Point($width, 0);
$transformedShape[3] = new Point($width, $transformedShape[3]->getY());

// draw shape
$image->draw()->polygon(
$transformedShape,
$foregroundColor,
true,
1
);
}

/**
* @param ImageInterface $image
* @param ColorInterface $foregroundColor
* @param ColorInterface $backgroundColor
* @param int $width
* @param int $height
*/
protected function renderBorder(ImageInterface $image, ColorInterface $foregroundColor, ColorInterface $backgroundColor, int $width, int $height): void
{
$borderWidth = 10;

for ($i = 0; $i <= $borderWidth; $i++) {
$x1 = $i;
$x2 = $width - $i;
$y1 = $i;
$y2 = $height - $i;
$image->draw()->polygon(
[
new Point($x1, $y1),
new Point($x2, $y1),
new Point($x2, $y2),
new Point($x1, $y2),
],
($i > $borderWidth / 2 ? $foregroundColor : $backgroundColor),
false,
1
);
}
}

/**
* @param ImageInterface $image
* @param ColorInterface $textColor
* @param int $width
* @param int $height
* @param string $text
* @param bool $center
*
* @throws UnknownPackageException
*/
protected function renderText(ImageInterface $image, ColorInterface $textColor, int $width, int $height, string $text, bool $center = false): void
{
$initialFontSize = 10;
$fontFile = $this->packageManager->getPackage('Sitegeist.Kaleidoscope')->getPackagePath().'Resources/Private/Font/NotoSans-Regular.ttf';
$initialFont = $this->imagineService->font($fontFile, $initialFontSize, $textColor);

// scale text to fit the image
$initialFontBox = $initialFont->box($text);
$targetFontWidth = $width * .5;
$targetFontHeight = $center ? $height * .5 : $height * .20;
$correctedFontSizeByWidth = $targetFontWidth * $initialFontSize / $initialFontBox->getWidth();
$correctedFontSizeByHeight = $targetFontHeight * $initialFontSize / $initialFontBox->getHeight();

// render actual text
$actualFont = $this->imagineService->font($fontFile, (int) min([$correctedFontSizeByWidth, $correctedFontSizeByHeight]), $textColor);
$actualFontBox = $actualFont->box($text);
$imageCenterPosition = new Point($width / 2, $height / 2);
$textCenterPosition = new Point\Center($actualFontBox);
if ($center) {
$centeredTextPosition = new Point($imageCenterPosition->getX() - $textCenterPosition->getX(), (int) ($height * .5 - $actualFontBox->getHeight() * .5));
} else {
$centeredTextPosition = new Point($imageCenterPosition->getX() - $textCenterPosition->getX(), (int) ($height * .78 - $actualFontBox->getHeight() * .5));
}
/** @phpstan-ignore-next-line */
$image->draw()->text($text, $actualFont, $centeredTextPosition);
}

/**
* @param ImageInterface $image
* @param ColorInterface $patternColor
* @param int $width
* @param int $height
*
* @return void
*/
protected function renderPattern(ImageInterface $image, ColorInterface $patternColor, int $width, int $height): void
{
$borderWidth = 5;
$patternSize = 50;

$limitingDimension = $width > $height ? $height : $width;

if ($limitingDimension < ($patternSize + $borderWidth + $borderWidth)) {
$patternSize = $limitingDimension - $borderWidth - $borderWidth;
}

for ($i = 0; $i < $patternSize; $i++) {
for ($k = 0; $k < $patternSize; $k++) {
if ($k > $patternSize - $i || $i > $patternSize - $k) {
continue;
}

if (
$i === $k ||
($i % 2 && $k % 2)

) {
$image->draw()->dot(new Point($borderWidth + $i, $borderWidth + $k), $patternColor);
}
}
}
}
}
Loading

0 comments on commit dab3160

Please sign in to comment.