You are here

protected function GDOperationTrait::getEntropyCropByGridding in Image Effects 8

Same name and namespace in other branches
  1. 8.3 src/Plugin/ImageToolkit/Operation/gd/GDOperationTrait.php \Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait::getEntropyCropByGridding()
  2. 8.2 src/Plugin/ImageToolkit/Operation/gd/GDOperationTrait.php \Drupal\image_effects\Plugin\ImageToolkit\Operation\gd\GDOperationTrait::getEntropyCropByGridding()

Computes the entropy crop of an image, using recursive gridding.

Parameters

resource $src: The source image resource.

string $crop_width: The width of the crop.

string $crop_height: The height of the crop.

bool $simulate: If TRUE, the crop will be simulated, and image markers will be overlaid on the source image.

int $grid_width: The maximum width of the sub-grid window.

int $grid_height: The maximum height of the sub-grid window.

int $grid_rows: The number of rows of the sub-grid.

int $grid_columns: The number of columns of the sub-grid.

int $grid_sub_rows: The number of rows of the sub-grid for the sum calculation.

int $grid_sub_columns: The number of columns of the sub-grid for the sum calculation.

Return value

\Drupal\image_effects\Component\PositionedRectangle The PositionedRectangle object marking the crop area.

2 calls to GDOperationTrait::getEntropyCropByGridding()
SmartCrop::execute in src/Plugin/ImageToolkit/Operation/imagemagick/SmartCrop.php
SmartCrop::execute in src/Plugin/ImageToolkit/Operation/gd/SmartCrop.php
Performs the actual manipulation on the image.

File

src/Plugin/ImageToolkit/Operation/gd/GDOperationTrait.php, line 499

Class

GDOperationTrait
Trait for GD image toolkit operations.

Namespace

Drupal\image_effects\Plugin\ImageToolkit\Operation\gd

Code

protected function getEntropyCropByGridding($src, $crop_width, $crop_height, $simulate, $grid_width, $grid_height, $grid_rows, $grid_columns, $grid_sub_rows, $grid_sub_columns) {

  // Source image data.
  $source_rect = new PositionedRectangle(imagesx($src), imagesy($src));
  $source_image_aspect = imagesy($src) / imagesx($src);

  // If simulating, create an image serving as the layer for the visual
  // markers.
  if ($simulate) {

    // @todo when #2583041 is committed, add a check for memory
    // availability before creating the resource.
    $marker_layer_resource = imagecreatetruecolor(imagesx($src), imagesy($src));
    imagefill($marker_layer_resource, 0, 0, imagecolorallocatealpha($marker_layer_resource, 0, 0, 0, 127));
  }

  // Determine dimensions of the grid window. The window dimensions are
  // limited to reduce entropy compared to the source. It needs to respect the
  // aspect ratio of the source image.
  if ($source_image_aspect < $grid_height / $grid_width) {
    $grid_height = (int) round($grid_width * $source_image_aspect);
  }
  else {
    $grid_width = (int) round($grid_height / $source_image_aspect);
  }

  // Analyse the grid window to find the highest entropy sub-grid. Loop until
  // the window size is smaller than the crop area, nesting sub-windows.
  $window_nesting = 0;
  $window_x_offset = 0;
  $window_y_offset = 0;
  $window_width = imagesx($src);
  $window_height = imagesy($src);
  while ($window_width > $crop_width || $window_height > $crop_height) {

    // Determine the working size of the grid window. Resample the source
    // image grid area into the grid window, if it is bigger than the bounds
    // given.
    if ($window_width >= $grid_width && $window_height >= $grid_height) {
      $work_window_width = $grid_width;
      $work_window_height = $grid_height;
    }
    else {
      $work_window_width = $window_width;
      $work_window_height = $window_height;
    }

    // If the grid window is smaller than the matrix, it does not make sense
    // to dig deeper.
    if ($work_window_width < $grid_columns || $work_window_height < $grid_rows) {
      break;
    }

    // Add a grid to the source image rectangle.
    $source_rect
      ->addGrid('grid_' . $window_nesting, $window_x_offset, $window_y_offset, $window_width, $window_height, $grid_rows, $grid_columns);

    // Create the grid window.
    $grid_rect = new PositionedRectangle($work_window_width, $work_window_height);
    $grid_rect
      ->addGrid('grid_0', 0, 0, $work_window_width, $work_window_height, $grid_rows, $grid_columns);

    // @todo when #2583041 is committed, add a check for memory
    // availability before creating the resource.
    $grid_resource = imagecreatetruecolor($work_window_width, $work_window_height);
    imagefill($grid_resource, 0, 0, imagecolorallocatealpha($grid_resource, 0, 0, 0, 127));
    imagecopyresampled($grid_resource, $src, 0, 0, $window_x_offset, $window_y_offset, $work_window_width, $work_window_height, $window_width, $window_height);

    // Build the entropy matrix of the window.
    $entropy_matrix = [];
    for ($row = 0; $row < $grid_rows; $row++) {
      for ($column = 0; $column < $grid_columns; $column++) {
        $cell_top_left = $grid_rect
          ->getPoint('grid_0_' . $row . '_' . $column);
        $cell_dimensions = $grid_rect
          ->getSubGridDimensions('grid_0', $row, $column, 1, 1);
        $entropy_matrix[$row][$column] = $this
          ->getAreaEntropy($grid_resource, $cell_top_left[0], $cell_top_left[1], $cell_dimensions[0], $cell_dimensions[1]);
      }
    }

    // Find the sub-matrix that has the highest entropy sum.
    $entropy_sum_matrix = MatrixUtility::cumulativeSum($entropy_matrix);
    $max_sum_position = MatrixUtility::findMaxSumSubmatrix($entropy_sum_matrix, $grid_sub_rows, $grid_sub_columns);

    // Position the highest entropy sub-matrix on the source image.
    list($window_x_offset, $window_y_offset) = $source_rect
      ->getPoint('grid_' . $window_nesting . '_' . $max_sum_position[0] . '_' . $max_sum_position[1]);
    list($window_width, $window_height) = $source_rect
      ->getSubGridDimensions('grid_' . $window_nesting, $max_sum_position[0], $max_sum_position[1], $grid_sub_rows, $grid_sub_columns);
    if ($simulate) {
      switch ($window_nesting % 3) {
        case 0:
          $color = imagecolorallocatealpha($marker_layer_resource, 255, 0, 0, 0);
          break;
        case 1:
          $color = imagecolorallocatealpha($marker_layer_resource, 0, 255, 255, 0);
          break;
        case 2:
          $color = imagecolorallocatealpha($marker_layer_resource, 255, 255, 0, 0);
          break;
      }

      // Add grid points on marker layer.
      for ($row = 0; $row <= $grid_rows; $row++) {
        for ($column = 0; $column <= $grid_columns; $column++) {
          $coord = $source_rect
            ->getPoint('grid_' . $window_nesting . '_' . $row . '_' . $column);
          imagefilledellipse($marker_layer_resource, $coord[0], $coord[1], 6, 6, $color);
        }
      }

      // Polygon on marker layer.
      $rect = new PositionedRectangle($window_width, $window_height);
      $rect
        ->translate([
        $window_x_offset,
        $window_y_offset,
      ]);
      $rect
        ->translate([
        -2,
        -2,
      ]);
      for ($i = -2; $i <= 2; $i++) {
        imagepolygon($marker_layer_resource, $this
          ->getRectangleCorners($rect), 4, $color);
        $rect
          ->translate([
          1,
          1,
        ]);
      }
    }

    // Destroy the window.
    imagedestroy($grid_resource);
    $window_nesting++;
  }

  // Overlay marker layer onto source at 70% transparency.
  if ($simulate) {
    $this
      ->imageCopyMergeAlpha($src, $marker_layer_resource, 0, 0, 0, 0, imagesx($src), imagesy($src), 70);
    imagedestroy($marker_layer_resource);
  }

  // Determine the Rectangle containing the crop.
  $window_x_center = $window_x_offset + (int) ($window_width / 2);
  $window_y_center = $window_y_offset + (int) ($window_height / 2);
  $crop_x_offset = $window_x_center - (int) ($crop_width / 2);
  $crop_y_offset = $window_y_center - (int) ($crop_height / 2);
  $rect = new PositionedRectangle($crop_width, $crop_height);
  $rect
    ->translate([
    $crop_x_offset,
    $crop_y_offset,
  ]);
  if ($crop_x_offset < 0) {
    $rect
      ->translate([
      -$crop_x_offset,
      0,
    ]);
  }
  if ($crop_y_offset < 0) {
    $rect
      ->translate([
      0,
      -$crop_y_offset,
    ]);
  }
  if ($crop_x_offset + $crop_width > imagesx($src)) {
    $rect
      ->translate([
      -($crop_x_offset + $crop_width - imagesx($src)),
      0,
    ]);
  }
  if ($crop_y_offset + $crop_height > imagesy($src)) {
    $rect
      ->translate([
      0,
      -($crop_y_offset + $crop_height - imagesy($src)),
    ]);
  }
  return $rect;
}