function ImageToolkitGdTestCase::testManipulations

Since PHP can't visually check that our images have been manipulated properly, build a list of expected color values for each of the corners and the expected height and widths for the final images.

File

modules/simpletest/tests/image.test, line 264

Class

ImageToolkitGdTestCase
Test the core GD image manipulation functions.

Code

function testManipulations() {
    // If GD isn't available don't bother testing this.
    module_load_include('inc', 'system', 'image.gd');
    if (!function_exists('image_gd_check_settings') || !image_gd_check_settings()) {
        $this->pass(t('Image manipulations for the GD toolkit were skipped because the GD toolkit is not available.'));
        return;
    }
    // Typically the corner colors will be unchanged. These colors are in the
    // order of top-left, top-right, bottom-right, bottom-left.
    $default_corners = array(
        $this->red,
        $this->green,
        $this->blue,
        $this->transparent,
    );
    // A list of files that will be tested.
    $files = array(
        'image-test.png',
        'image-test.gif',
        'image-test-no-transparency.gif',
        'image-test.jpg',
    );
    // Setup a list of tests to perform on each type.
    $operations = array(
        'resize' => array(
            'function' => 'resize',
            'arguments' => array(
                20,
                10,
            ),
            'width' => 20,
            'height' => 10,
            'corners' => $default_corners,
        ),
        'scale_x' => array(
            'function' => 'scale',
            'arguments' => array(
                20,
                NULL,
            ),
            'width' => 20,
            'height' => 10,
            'corners' => $default_corners,
        ),
        'scale_y' => array(
            'function' => 'scale',
            'arguments' => array(
                NULL,
                10,
            ),
            'width' => 20,
            'height' => 10,
            'corners' => $default_corners,
        ),
        'upscale_x' => array(
            'function' => 'scale',
            'arguments' => array(
                80,
                NULL,
                TRUE,
            ),
            'width' => 80,
            'height' => 40,
            'corners' => $default_corners,
        ),
        'upscale_y' => array(
            'function' => 'scale',
            'arguments' => array(
                NULL,
                40,
                TRUE,
            ),
            'width' => 80,
            'height' => 40,
            'corners' => $default_corners,
        ),
        'crop' => array(
            'function' => 'crop',
            'arguments' => array(
                12,
                4,
                16,
                12,
            ),
            'width' => 16,
            'height' => 12,
            'corners' => array_fill(0, 4, $this->white),
        ),
        'scale_and_crop' => array(
            'function' => 'scale_and_crop',
            'arguments' => array(
                10,
                8,
            ),
            'width' => 10,
            'height' => 8,
            'corners' => array_fill(0, 4, $this->black),
        ),
    );
    // Systems using non-bundled GD2 don't have imagerotate. Test if available.
    // @todo Remove the version check once https://www.drupal.org/node/2918570
    //   is resolved.
    if (function_exists('imagerotate') && (version_compare(PHP_VERSION, '7.0.26', '<') || version_compare(PHP_VERSION, '7.1', '>=') && version_compare(PHP_VERSION, '7.1.12', '<'))) {
        $operations += array(
            'rotate_90' => array(
                'function' => 'rotate',
                'arguments' => array(
                    90,
                    0xff00ff,
                ),
                // Fuchsia background.
'width' => 20,
                'height' => 40,
                'corners' => array(
                    $this->fuchsia,
                    $this->red,
                    $this->green,
                    $this->blue,
                ),
            ),
            'rotate_transparent_90' => array(
                'function' => 'rotate',
                'arguments' => array(
                    90,
                ),
                'width' => 20,
                'height' => 40,
                'corners' => array(
                    $this->transparent,
                    $this->red,
                    $this->green,
                    $this->blue,
                ),
            ),
        );
        // As of PHP version 5.5, GD uses a different algorithm to rotate images
        // than version 5.4 and below, resulting in different dimensions.
        // See https://bugs.php.net/bug.php?id=65148.
        // For the 40x20 test images, the dimensions resulting from rotation will
        // be 1 pixel smaller in both width and height in PHP 5.5 and above.
        // @todo: The PHP bug was fixed in PHP 7.0.26 and 7.1.12. Change the code
        //   below to reflect that in https://www.drupal.org/node/2918570.
        if (version_compare(PHP_VERSION, '5.5', '>=')) {
            $operations += array(
                'rotate_5' => array(
                    'function' => 'rotate',
                    'arguments' => array(
                        5,
                        0xff00ff,
                    ),
                    // Fuchsia background.
'width' => 41,
                    'height' => 23,
                    'corners' => array_fill(0, 4, $this->fuchsia),
                ),
                'rotate_transparent_5' => array(
                    'function' => 'rotate',
                    'arguments' => array(
                        5,
                    ),
                    'width' => 41,
                    'height' => 23,
                    'corners' => array_fill(0, 4, $this->rotate_transparent),
                ),
            );
        }
        else {
            $operations += array(
                'rotate_5' => array(
                    'function' => 'rotate',
                    'arguments' => array(
                        5,
                        0xff00ff,
                    ),
                    // Fuchsia background.
'width' => 42,
                    'height' => 24,
                    'corners' => array_fill(0, 4, $this->fuchsia),
                ),
                'rotate_transparent_5' => array(
                    'function' => 'rotate',
                    'arguments' => array(
                        5,
                    ),
                    'width' => 42,
                    'height' => 24,
                    'corners' => array_fill(0, 4, $this->rotate_transparent),
                ),
            );
        }
    }
    // Systems using non-bundled GD2 don't have imagefilter. Test if available.
    if (function_exists('imagefilter')) {
        $operations += array(
            'desaturate' => array(
                'function' => 'desaturate',
                'arguments' => array(),
                'height' => 20,
                'width' => 40,
                // Grayscale corners are a bit funky. Each of the corners are a shade of
                // gray. The values of these were determined simply by looking at the
                // final image to see what desaturated colors end up being.
'corners' => array(
                    array_fill(0, 3, 76) + array(
                        3 => 0,
                    ),
                    array_fill(0, 3, 149) + array(
                        3 => 0,
                    ),
                    array_fill(0, 3, 29) + array(
                        3 => 0,
                    ),
                    array_fill(0, 3, 225) + array(
                        3 => 127,
                    ),
                ),
            ),
        );
    }
    foreach ($files as $file) {
        foreach ($operations as $op => $values) {
            // Load up a fresh image.
            $image = image_load(drupal_get_path('module', 'simpletest') . '/files/' . $file, 'gd');
            if (!$image) {
                $this->fail(t('Could not load image %file.', array(
                    '%file' => $file,
                )));
                continue 2;
            }
            // All images should be converted to truecolor when loaded.
            $image_truecolor = imageistruecolor($image->resource);
            $this->assertTrue($image_truecolor, format_string('Image %file after load is a truecolor image.', array(
                '%file' => $file,
            )));
            if ($image->info['extension'] == 'gif') {
                if ($op == 'desaturate') {
                    // Transparent GIFs and the imagefilter function don't work together.
                    $values['corners'][3][3] = 0;
                }
            }
            // Perform our operation.
            $function = 'image_' . $values['function'];
            $arguments = array();
            $arguments[] =& $image;
            $arguments = array_merge($arguments, $values['arguments']);
            call_user_func_array($function, $arguments);
            // To keep from flooding the test with assert values, make a general
            // value for whether each group of values fail.
            $correct_dimensions_real = TRUE;
            $correct_dimensions_object = TRUE;
            $correct_colors = TRUE;
            // Check the real dimensions of the image first.
            if (imagesy($image->resource) != $values['height'] || imagesx($image->resource) != $values['width']) {
                $correct_dimensions_real = FALSE;
            }
            // Check that the image object has an accurate record of the dimensions.
            if ($image->info['width'] != $values['width'] || $image->info['height'] != $values['height']) {
                $correct_dimensions_object = FALSE;
            }
            // 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:
                        $x = 0;
                        $y = 0;
                        break;
                    case 1:
                        $x = $values['width'] - 1;
                        $y = 0;
                        break;
                    case 2:
                        $x = $values['width'] - 1;
                        $y = $values['height'] - 1;
                        break;
                    case 3:
                        $x = 0;
                        $y = $values['height'] - 1;
                        break;
                }
                $color = $this->getPixelColor($image, $x, $y);
                $correct_colors = $this->colorsAreEqual($color, $corner);
            }
            $directory = file_default_scheme() . '://imagetests';
            file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
            $file_path = $directory . '/' . $op . '.' . $image->info['extension'];
            image_save($image, $file_path);
            $this->assertTrue($correct_dimensions_real, format_string('Image %file after %action action has proper dimensions.', array(
                '%file' => $file,
                '%action' => $op,
            )));
            $this->assertTrue($correct_dimensions_object, format_string('Image %file object after %action action is reporting the proper height and width values.', array(
                '%file' => $file,
                '%action' => $op,
            )));
            // JPEG colors will always be messed up due to compression.
            if ($image->info['extension'] != 'jpg') {
                $this->assertTrue($correct_colors, format_string('Image %file object after %action action has the correct color placement.', array(
                    '%file' => $file,
                    '%action' => $op,
                )));
            }
        }
        // Check that saved image reloads without raising PHP errors.
        $image_reloaded = image_load($file_path);
    }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.