Rectangle.php

Same filename in other branches
  1. 9 core/lib/Drupal/Component/Utility/Rectangle.php
  2. 8.9.x core/lib/Drupal/Component/Utility/Rectangle.php
  3. 11.x core/lib/Drupal/Component/Utility/Rectangle.php

Namespace

Drupal\Component\Utility

File

core/lib/Drupal/Component/Utility/Rectangle.php

View source
<?php

namespace Drupal\Component\Utility;


/**
 * Rectangle rotation algebra class.
 *
 * This class is used by the image system to abstract, from toolkit
 * implementations, the calculation of the expected dimensions resulting from
 * an image rotate operation.
 *
 * Different versions of PHP for the GD toolkit, and alternative toolkits, use
 * different algorithms to perform the rotation of an image and result in
 * different dimensions of the output image. This prevents predictability of
 * the final image size for instance by the image rotate effect, or by image
 * toolkit rotate operations.
 *
 * This class implements a calculation algorithm that returns, given input
 * width, height and rotation angle, dimensions of the expected image after
 * rotation that are consistent with those produced by the GD rotate image
 * toolkit operation using PHP 5.5 and above.
 *
 * @see \Drupal\system\Plugin\ImageToolkit\Operation\gd\Rotate
 */
class Rectangle {
    
    /**
     * The width of the rectangle.
     *
     * @var int
     */
    protected $width;
    
    /**
     * The height of the rectangle.
     *
     * @var int
     */
    protected $height;
    
    /**
     * The width of the rotated rectangle.
     *
     * @var int
     */
    protected $boundingWidth;
    
    /**
     * The height of the rotated rectangle.
     *
     * @var int
     */
    protected $boundingHeight;
    
    /**
     * Constructs a new Rectangle object.
     *
     * @param int $width
     *   The width of the rectangle.
     * @param int $height
     *   The height of the rectangle.
     */
    public function __construct($width, $height) {
        if ($width > 0 && $height > 0) {
            $this->width = $width;
            $this->height = $height;
            $this->boundingWidth = $width;
            $this->boundingHeight = $height;
        }
        else {
            throw new \InvalidArgumentException("Invalid dimensions ({$width}x{$height}) specified for a Rectangle object");
        }
    }
    
    /**
     * Rotates the rectangle.
     *
     * @param float $angle
     *   Rotation angle.
     *
     * @return $this
     */
    public function rotate($angle) {
        // PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148: To prevent buggy
        // behavior on negative multiples of 30 degrees we convert any negative
        // angle to a positive one between 0 and 360 degrees.
        $angle -= floor($angle / 360) * 360;
        // For some rotations that are multiple of 30 degrees, we need to correct
        // an imprecision between GD that uses C floats internally, and PHP that
        // uses C doubles. Also, for rotations that are not multiple of 90 degrees,
        // we need to introduce a correction factor of 0.5 to match the GD
        // algorithm used in PHP 5.5 (and above) to calculate the width and height
        // of the rotated image.
        if ((int) $angle == $angle && $angle % 90 == 0) {
            $imprecision = 0;
            $correction = 0;
        }
        else {
            $imprecision = -1.0E-5;
            $correction = 0.5;
        }
        // Do the trigonometry, applying imprecision fixes where needed.
        $rad = deg2rad($angle);
        $cos = cos($rad);
        $sin = sin($rad);
        $a = $this->width * $cos;
        $b = $this->height * $sin + $correction;
        $c = $this->width * $sin;
        $d = $this->height * $cos + $correction;
        if ((int) $angle == $angle && in_array($angle, [
            60,
            150,
            300,
        ])) {
            $a = $this->fixImprecision($a, $imprecision);
            $b = $this->fixImprecision($b, $imprecision);
            $c = $this->fixImprecision($c, $imprecision);
            $d = $this->fixImprecision($d, $imprecision);
        }
        // This is how GD on PHP5.5 calculates the new dimensions.
        $this->boundingWidth = abs((int) $a) + abs((int) $b);
        $this->boundingHeight = abs((int) $c) + abs((int) $d);
        return $this;
    }
    
    /**
     * Performs an imprecision check on the input value and fixes it if needed.
     *
     * GD that uses C floats internally, whereas we at PHP level use C doubles.
     * In some cases, we need to compensate imprecision.
     *
     * @param float $input
     *   The input value.
     * @param float $imprecision
     *   The imprecision factor.
     *
     * @return float
     *   A value, where imprecision is added to input if the delta part of the
     *   input is lower than the absolute imprecision.
     */
    protected function fixImprecision($input, $imprecision) {
        if ($this->delta($input) < abs($imprecision)) {
            return $input + $imprecision;
        }
        return $input;
    }
    
    /**
     * Returns the fractional part of a float number, unsigned.
     *
     * @param float $input
     *   The input value.
     *
     * @return float
     *   The fractional part of the input number, unsigned.
     */
    protected function fraction($input) {
        return abs((int) $input - $input);
    }
    
    /**
     * Returns the difference of a fraction from the closest between 0 and 1.
     *
     * @param float $input
     *   The input value.
     *
     * @return float
     *   the difference of a fraction from the closest between 0 and 1.
     */
    protected function delta($input) {
        $fraction = $this->fraction($input);
        return $fraction > 0.5 ? 1 - $fraction : $fraction;
    }
    
    /**
     * Gets the bounding width of the rectangle.
     *
     * @return int
     *   The bounding width of the rotated rectangle.
     */
    public function getBoundingWidth() {
        return $this->boundingWidth;
    }
    
    /**
     * Gets the bounding height of the rectangle.
     *
     * @return int
     *   The bounding height of the rotated rectangle.
     */
    public function getBoundingHeight() {
        return $this->boundingHeight;
    }

}

Classes

Title Deprecated Summary
Rectangle Rectangle rotation algebra class.

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