Commit b79faf63 authored by webchick's avatar webchick

Issue #2215369 by fietserwin, svanou: Fixed PHP 5.5 imagerotate() fails when...

Issue #2215369 by fietserwin, svanou: Fixed PHP 5.5 imagerotate() fails when incorrect color indices are passed in.
parent 80718556
......@@ -27,23 +27,9 @@ class RotateImageEffect extends ConfigurableImageEffectBase {
* {@inheritdoc}
*/
public function applyEffect(ImageInterface $image) {
// Convert short #FFF syntax to full #FFFFFF syntax.
if (strlen($this->configuration['bgcolor']) == 4) {
$c = $this->configuration['bgcolor'];
$this->configuration['bgcolor'] = $c[0] . $c[1] . $c[1] . $c[2] . $c[2] . $c[3] . $c[3];
}
// Convert #FFFFFF syntax to hexadecimal colors.
if ($this->configuration['bgcolor'] != '') {
$this->configuration['bgcolor'] = hexdec(str_replace('#', '0x', $this->configuration['bgcolor']));
}
else {
$this->configuration['bgcolor'] = NULL;
}
if (!empty($this->configuration['random'])) {
$degrees = abs((float) $this->configuration['degrees']);
$this->configuration['degrees'] = rand(-1 * $degrees, $degrees);
$this->configuration['degrees'] = rand(-$degrees, $degrees);
}
if (!$image->rotate($this->configuration['degrees'], $this->configuration['bgcolor'])) {
......
......@@ -130,7 +130,7 @@ function testRotateEffect() {
// Check the parameters.
$calls = $this->imageTestGetAllCalls();
$this->assertEqual($calls['rotate'][0][0], 90, 'Degrees were passed correctly');
$this->assertEqual($calls['rotate'][0][1], 0xffffff, 'Background color was passed correctly');
$this->assertEqual($calls['rotate'][0][1], '#fff', 'Background color was passed correctly');
}
/**
......
......@@ -7,6 +7,8 @@
namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
use Drupal\Component\Utility\Color;
/**
* Defines GD2 rotate operation.
*
......@@ -29,7 +31,7 @@ protected function arguments() {
'description' => 'The number of (clockwise) degrees to rotate the image',
),
'background' => array(
'description' => 'An hexadecimal integer specifying the background color to use for the uncovered area of the image after the rotation. E.g. 0x000000 for black, 0xff00ff for magenta, and 0xffffff for white. For images that support transparency, this will default to transparent. Otherwise it will be white',
'description' => "A string specifying the hexadecimal color code to use as background for the uncovered area of the image after the rotation. E.g. '#000000' for black, '#ff00ff' for magenta, and '#ffffff' for white. For images that support transparency, this will default to transparent white",
'required' => FALSE,
'default' => NULL,
),
......@@ -39,47 +41,71 @@ protected function arguments() {
/**
* {@inheritdoc}
*/
protected function execute(array $arguments) {
// PHP installations using non-bundled GD do not have imagerotate.
if (!function_exists('imagerotate')) {
$this->logger->notice('The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $this->getToolkit()->getImage()->getSource()));
return FALSE;
}
protected function validateArguments(array $arguments) {
// PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148: To prevent buggy
// behavior on negative multiples of 90 degrees we convert any negative
// angle to a positive one between 0 and 360 degrees.
$arguments['degrees'] -= floor($arguments['degrees'] / 360) * 360;
// Convert the hexadecimal background value to a color index value.
// Validate or set background color argument.
if (!empty($arguments['background'])) {
$rgb = array();
for ($i = 16; $i >= 0; $i -= 8) {
$rgb[] = (($arguments['background'] >> $i) & 0xFF);
}
$arguments['background'] = imagecolorallocatealpha($this->getToolkit()->getResource(), $rgb[0], $rgb[1], $rgb[2], 0);
// Validate the background color: Color::hexToRgb does so for us.
$background = Color::hexToRgb($arguments['background']) + array( 'alpha' => 0 );
}
// Set background color as transparent if $arguments['background'] is NULL.
else {
// Get the current transparent color.
$arguments['background'] = imagecolortransparent($this->getToolkit()->getResource());
// Background color is not specified: use transparent white as background.
$background = array('red' => 255, 'green' => 255, 'blue' => 255, 'alpha' => 127);
}
// Store the color index for the background as that is what GD uses.
$arguments['background_idx'] = imagecolorallocatealpha($this->getToolkit()->getResource(), $background['red'], $background['green'], $background['blue'], $background['alpha']);
// If no transparent colors, use white.
if ($arguments['background'] == 0) {
$arguments['background'] = imagecolorallocatealpha($this->getToolkit()->getResource(), 255, 255, 255, 0);
if ($this->getToolkit()->getType() === IMAGETYPE_GIF) {
// GIF does not work with a transparency channel, but can define 1 color
// in its palette to act as transparent.
// Get the current transparent color, if any.
$gif_transparent_id = imagecolortransparent($this->getToolkit()->getResource());
if ($gif_transparent_id !== -1) {
// The gif already has a transparent color set: remember it to set it on
// the rotated image as well.
$arguments['gif_transparent_color'] = imagecolorsforindex($this->getToolkit()->getResource(), $gif_transparent_id);
if ($background['alpha'] >= 127) {
// We want a transparent background: use the color already set to act
// as transparent, as background.
$arguments['background_idx'] = $gif_transparent_id;
}
}
else {
// The gif does not currently have a transparent color set.
if ($background['alpha'] >= 127) {
// But as the background is transparent, it should get one.
$arguments['gif_transparent_color'] = $background;
}
}
}
// Images are assigned a new color palette when rotating, removing any
// transparency flags. For GIF images, keep a record of the transparent color.
if ($this->getToolkit()->getType() == IMAGETYPE_GIF) {
$transparent_index = imagecolortransparent($this->getToolkit()->getResource());
if ($transparent_index != 0) {
$transparent_gif_color = imagecolorsforindex($this->getToolkit()->getResource(), $transparent_index);
}
return $arguments;
}
/**
* {@inheritdoc}
*/
protected function execute(array $arguments) {
// PHP installations using non-bundled GD do not have imagerotate.
if (!function_exists('imagerotate')) {
$this->logger->notice('The image %file could not be rotated because the imagerotate() function is not available in this PHP installation.', array('%file' => $this->getToolkit()->getImage()->getSource()));
return FALSE;
}
$this->getToolkit()->setResource(imagerotate($this->getToolkit()->getResource(), 360 - $arguments['degrees'], $arguments['background']));
$this->getToolkit()->setResource(imagerotate($this->getToolkit()->getResource(), 360 - $arguments['degrees'], $arguments['background_idx']));
// GIFs need to reassign the transparent color after performing the rotate.
if (isset($transparent_gif_color)) {
$arguments['background'] = imagecolorexactalpha($this->getToolkit()->getResource(), $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
imagecolortransparent($this->getToolkit()->getResource(), $arguments['background']);
// GIFs need to reassign the transparent color after performing the rotate,
// but only do so, if the image already had transparency of its own, or the
// rotate added a transparent background.
if (!empty($arguments['gif_transparent_color'])) {
$transparent_idx = imagecolorexactalpha($this->getToolkit()->getResource(), $arguments['gif_transparent_color']['red'], $arguments['gif_transparent_color']['green'], $arguments['gif_transparent_color']['blue'], $arguments['gif_transparent_color']['alpha']);
imagecolortransparent($this->getToolkit()->getResource(), $transparent_idx);
}
return TRUE;
......
......@@ -32,9 +32,11 @@ class ToolkitGdTest extends DrupalUnitTestBase {
protected $green = array(0, 255, 0, 0);
protected $blue = array(0, 0, 255, 0);
protected $yellow = array(255, 255, 0, 0);
protected $fuchsia = array(255, 0, 255, 0); // Used as background colors.
protected $transparent = array(0, 0, 0, 127);
protected $white = array(255, 255, 255, 0);
protected $transparent = array(0, 0, 0, 127);
// Used as rotate background colors.
protected $fuchsia = array(255, 0, 255, 0);
protected $rotate_transparent = array(255, 255, 255, 127);
protected $width = 40;
protected $height = 20;
......@@ -117,6 +119,7 @@ function testManipulations() {
$files = array(
'image-test.png',
'image-test.gif',
'image-test-no-transparency.gif',
'image-test.jpg',
);
......@@ -178,14 +181,14 @@ function testManipulations() {
$operations += array(
'rotate_5' => array(
'function' => 'rotate',
'arguments' => array('degrees' => 5, 'background' => 0xFF00FF), // Fuchsia background.
'arguments' => array('degrees' => 5, 'background' => '#FF00FF'), // Fuchsia background.
'width' => 42,
'height' => 24,
'corners' => array_fill(0, 4, $this->fuchsia),
),
'rotate_90' => array(
'function' => 'rotate',
'arguments' => array('degrees' => 90, 'background' => 0xFF00FF), // Fuchsia background.
'arguments' => array('degrees' => 90, 'background' => '#FF00FF'), // Fuchsia background.
'width' => 20,
'height' => 40,
'corners' => array($this->transparent, $this->red, $this->green, $this->blue),
......@@ -195,7 +198,7 @@ function testManipulations() {
'arguments' => array('degrees' => 5),
'width' => 42,
'height' => 24,
'corners' => array_fill(0, 4, $this->transparent),
'corners' => array_fill(0, 4, $this->rotate_transparent),
),
'rotate_transparent_90' => array(
'function' => 'rotate',
......@@ -257,7 +260,20 @@ function testManipulations() {
$correct_dimensions_real = TRUE;
$correct_dimensions_object = TRUE;
// Check the real dimensions of the image first.
// PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148. PHP 5.5 GD
// rotates differently then it did in PHP 5.4 resulting in different
// dimensions then what math teaches us. For the test images, the
// dimensions will be 1 pixel smaller in both dimensions (though other
// tests have shown a difference of 0 to 3 pixels in both dimensions.
// @todo: if and when the PHP bug gets solved, add an upper limit
// version check.
// @todo: in [#1551686] the dimension calculations for rotation are
// reworked. That issue should also check if these tests can be made
// more robust.
if (version_compare(PHP_VERSION, '5.5', '>=') && $values['function'] === 'rotate' && $values['arguments']['degrees'] % 90 != 0) {
$values['height']--;
$values['width']--;
}
if (imagesy($toolkit->getResource()) != $values['height'] || imagesx($toolkit->getResource()) != $values['width']) {
$correct_dimensions_real = FALSE;
}
......@@ -279,6 +295,11 @@ function testManipulations() {
if ($image->getToolkit()->getType() != IMAGETYPE_JPEG) {
// Now check each of the corners to ensure color correctness.
foreach ($values['corners'] as $key => $corner) {
// The test gif that does not have transparency has yellow where the
// others have transparent.
if ($file === 'image-test-no-transparency.gif' && $corner === $this->transparent) {
$corner = $this->yellow;
}
// Get the location of the corner.
switch ($key) {
case 0:
......@@ -286,16 +307,16 @@ function testManipulations() {
$y = 0;
break;
case 1:
$x = $values['width'] - 1;
$x = $image->getWidth() - 1;
$y = 0;
break;
case 2:
$x = $values['width'] - 1;
$y = $values['height'] - 1;
$x = $image->getWidth() - 1;
$y = $image->getHeight() - 1;
break;
case 3:
$x = 0;
$y = $values['height'] - 1;
$y = $image->getHeight() - 1;
break;
}
$color = $this->getPixelColor($image, $x, $y);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment