Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Add dataSrc method to image source interface #63

Merged
merged 1 commit into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading