diff --git a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php index 5619535..56bc01d 100644 --- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php @@ -235,12 +235,12 @@ public function save($destination) { $success = $function($this->getResource(), $destination, $this->configFactory->get('system.image.gd')->get('jpeg_quality')); } else { - // Always save alpha supporting images with alpha information. - $alpha_types = [IMAGETYPE_PNG]; + // Image types that support alpha need to be saved accordingly. + $alpha_supporting_types = [IMAGETYPE_PNG]; if (static::isWebpSupported()) { - $alpha_types[] = IMAGETYPE_WEBP; + $alpha_supporting_types[] = IMAGETYPE_WEBP; } - if (in_array($this->getType(), $alpha_types)) { + if (in_array($this->getType(), $alpha_supporting_types)) { imagealphablending($this->getResource(), FALSE); imagesavealpha($this->getResource(), TRUE); } diff --git a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php index 4570104..1ff6a83 100644 --- a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php +++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php @@ -69,15 +69,31 @@ protected function checkRequirements() { */ protected function buildWebpTestImage() { $res = imagecreatetruecolor(40, 20); + imagealphablending($res, FALSE); + $transparency = imagecolorallocatealpha($res, 0, 0, 0, 127); + imagefill($res, 0, 0, $transparency); + imagealphablending($res, TRUE); + imagesavealpha($res, TRUE); + $red = imagecolorallocatealpha($res, 255, 0, 0, 0); $green = imagecolorallocatealpha($res, 0, 255, 0, 0); $blue = imagecolorallocatealpha($res, 0, 0, 255, 0); + $white = imagecolorallocatealpha($res, 255, 255, 255, 0); + $black = imagecolorallocatealpha($res, 0, 0, 0, 0); $yellow_transparent = imagecolorallocatealpha($res, 255, 255, 0, 127); + imagefilledrectangle($res, 0, 0, 19, 9, $red); imagefilledrectangle($res, 20, 0, 39, 9, $green); imagefilledrectangle($res, 20, 10, 39, 19, $blue); imagefilledrectangle($res, 0, 10, 19, 19, $yellow_transparent); - imagesavealpha($res, TRUE); + + imagepolygon($res, [12, 4, 27, 4, 27, 15, 12, 15], 4, $white); + imagepolygon($res, [11, 3, 28, 3, 28, 16, 11, 16], 4, $black); + imagepolygon($res, [10, 2, 29, 2, 29, 17, 10, 17], 4, $black); + imagepolygon($res, [9, 1, 30, 1, 30, 18, 9, 18], 4, $black); + imagepolygon($res, [8, 0, 31, 0, 31, 19, 8, 19], 4, $black); + imagepolygon($res, [7, 0, 32, 0, 32, 19, 7, 19], 4, $black); + imagewebp($res, Settings::get('file_public_path') . '/sourceimagetest/img-test.webp', 100); } @@ -100,6 +116,35 @@ function colorsAreEqual($color_a, $color_b) { } /** + * Function to compare two colors by RGBa, within a tolerance. + * + * Very basic, just compares the sum of the squared differences for each of + * the R, G, B, a components of two colors against a 'tolerance' value. + * + * @param int[] $color_a + * An RGBa array. + * @param int[] $color_b + * An RGBa array. + * @param int $tolerance + * The accepteable difference between the colors. + * + * @return bool + * TRUE if the colors differences are within tolerance, FALSE otherwise. + */ + protected function colorsAreClose(array $color_a, array $color_b, $tolerance) { + // Fully transparent colors are equal, regardless of RGB. + if ($color_a[3] == 127 && $color_b[3] == 127) { + return TRUE; + } + $distance = pow(($color_a[0] - $color_b[0]), 2) + pow(($color_a[1] - $color_b[1]), 2) + pow(($color_a[2] - $color_b[2]), 2) + pow(($color_a[3] - $color_b[3]), 2); + if ($distance > $tolerance) { + debug("Color A: {" . implode(',', $color_a) . "}, Color B: {" . implode(',', $color_b) . "}, Distance: " . $distance . ", Tolerance: " . $tolerance); + return FALSE; + } + return TRUE; + } + + /** * Function for finding a pixel's RGBa values. */ function getPixelColor(ImageInterface $image, $x, $y) { @@ -371,27 +416,23 @@ function testManipulations() { // these tests if the original or the result is in jpeg format. if ($image->getToolkit()->getType() != IMAGETYPE_JPEG && $image_original_type != IMAGETYPE_JPEG) { - // WebP test adjustments. - // @todo address these in a more general way. + // WebP colors are also compressed, resulting in non-exact match vs. + // the expected ones. We use the ::colorsAreClose method here, + // indicating a tolerance for the color difference. if ($file === 'img-test.webp') { - if (in_array($op, ['resize', 'scale_x', 'scale_y', 'upscale_x', 'upscale_y', 'convert_gif', 'convert_png', 'convert_webp'])) { - $values['corners'][0] = [255, 1, 0, 0]; - $values['corners'][1] = [0, 255, 1, 0]; - $values['corners'][3] = [0, 0, 0, 0]; - } - if (in_array($op, ['crop', 'scale_and_crop'])) { - $values['corners'][0] = [255, 1, 0, 0]; - $values['corners'][1] = [0, 255, 1, 0]; - $values['corners'][2] = [0, 0, 255, 0]; - $values['corners'][3] = [0, 0, 0, 0]; - } - if (in_array($op, ['rotate_90', 'rotate_transparent_90'])) { - $values['corners'][0] = [0, 0, 0, 0]; - $values['corners'][1] = [255, 1, 0, 0]; - $values['corners'][2] = [0, 255, 1, 0]; - } - if (in_array($op, ['desaturate'])) { - $values['corners'][3] = [0, 0, 0, 0]; + switch ($op) { + case 'crop': + $values['color_tolerance'] = 1100; + break; + + case 'scale_and_crop': + $values['color_tolerance'] = 600; + break; + + default: + $values['color_tolerance'] = 15; + break; + } } @@ -435,10 +476,15 @@ function testManipulations() { // We also skip the color test for transparency for gif <-> png // conversion. The convert operation cannot handle that correctly. if ($image->getToolkit()->getType() == $image_original_type || $corner != $this->transparent) { - $correct_colors = $this->colorsAreEqual($color, $corner); - $actual_color = implode(',', $color); - $expected_color = implode(',', $corner); - $this->assertTrue($correct_colors, "Color of image '{$file}' after '{$op}' operation at corner {$key} is ({$actual_color}), expected ({$expected_color})."); + if (!isset($values['color_tolerance'])) { + $correct_colors = $this->colorsAreEqual($color, $corner); + $actual_color = implode(',', $color); + $expected_color = implode(',', $corner); + $this->assertTrue($correct_colors, "Color of image '{$file}' after '{$op}' operation at corner {$key} is ({$actual_color}), expected ({$expected_color})."); + } + else { + $this->assertTrue($this->colorsAreClose($color, $corner, $values['color_tolerance'])); + } } } }