You are here

class ctools_stylizer_image_processor in Chaos Tool Suite (ctools) 7

Same name and namespace in other branches
  1. 6 includes/stylizer.inc \ctools_stylizer_image_processor

Hierarchy

Expanded class hierarchy of ctools_stylizer_image_processor

File

includes/stylizer.inc, line 234
Create customized CSS and images from palettes created by user input.

View source
class ctools_stylizer_image_processor {
  var $workspace = NULL;
  var $name = NULL;
  var $workspaces = array();
  var $message_log = array();
  var $error_log = array();
  function execute($path, $plugin, $settings) {
    $this->path = $path;
    $this->plugin = $plugin;
    $this->settings = $settings;
    $this->palette = $settings['palette'];
    if (is_string($plugin['actions']) && function_exists($plugin['actions'])) {
      $actions = $plugin['actions']($plugin, $settings);
    }
    elseif (is_array($plugin['actions'])) {
      $actions = $plugin['actions'];
    }
    if (!empty($actions) && is_array($actions)) {
      foreach ($plugin['actions'] as $action) {
        $command = 'command_' . array_shift($action);
        if (method_exists($this, $command)) {
          call_user_func_array(array(
            $this,
            $command,
          ), $action);
        }
      }
    }

    // Clean up buffers.
    foreach ($this->workspaces as $name => $workspace) {
      imagedestroy($this->workspaces[$name]);
    }
  }
  function log($message, $type = 'normal') {
    $this->message_log[] = $message;
    if ($type == 'error') {
      $this->error_log[] = $message;
    }
  }
  function set_current_workspace($workspace) {
    $this
      ->log("Set current workspace: {$workspace}");
    $this->workspace =& $this->workspaces[$workspace];
    $this->name = $workspace;
  }

  /**
   * Create a new workspace.
   */
  function command_new($name, $width, $height) {
    $this
      ->log("New workspace: {$name} ({$width} x {$height})");

    // Clean up if there was already a workspace there.
    if (isset($this->workspaces[$name])) {
      imagedestroy($this->workspaces[$name]);
    }
    $this->workspaces[$name] = imagecreatetruecolor($width, $height);
    $this
      ->set_current_workspace($name);

    // Make sure the new workspace has a transparent color.
    // Turn off transparency blending (temporarily)
    imagealphablending($this->workspace, FALSE);

    // Create a new transparent color for image
    $color = imagecolorallocatealpha($this->workspace, 0, 0, 0, 127);

    // Completely fill the background of the new image with allocated color.
    imagefill($this->workspace, 0, 0, $color);

    // Restore transparency blending
    imagesavealpha($this->workspace, TRUE);
  }

  /**
   * Create a new workspace a file.
   *
   * This will make the new workspace the current workspace.
   */
  function command_load($name, $file) {
    $this
      ->log("New workspace: {$name} (from {$file})");
    if (!file_exists($file)) {

      // Try it relative to the plugin.
      $file = $this->plugin['path'] . '/' . $file;
      if (!file_exists($file)) {
        $this
          ->log("Unable to open {$file}");
        return;
      }
    }

    // Clean up if there was already a workspace there.
    if (isset($this->workspaces[$name])) {
      imagedestroy($this->workspaces[$name]);
    }
    $this->workspaces[$name] = imagecreatefrompng($file);
    $this
      ->set_current_workspace($name);
  }

  /**
   * Create a new workspace using the properties of an existing workspace.
   */
  function command_new_from($name, $workspace) {
    $this
      ->log("New workspace: {$name} from existing {$workspace}");
    if (empty($this->workspaces[$workspace])) {
      $this
        ->log("Workspace {$name} does not exist.", 'error');
      return;
    }

    // Clean up if there was already a workspace there.
    if (isset($this->workspaces[$name])) {
      imagedestroy($this->workspaces[$name]);
    }
    $this->workspaces[$name] = $this
      ->new_image($this->workspace[$workspace]);
    $this
      ->set_current_workspace($name);
  }

  /**
   * Set the current workspace.
   */
  function command_workspace($name) {
    $this
      ->log("Set workspace: {$name}");
    if (empty($this->workspaces[$name])) {
      $this
        ->log("Workspace {$name} does not exist.", 'error');
      return;
    }
    $this
      ->set_current_workspace($name);
  }

  /**
   * Copy the contents of one workspace into the current workspace.
   */
  function command_merge_from($workspace, $x = 0, $y = 0) {
    $this
      ->log("Merge from: {$workspace} ({$x}, {$y})");
    if (empty($this->workspaces[$workspace])) {
      $this
        ->log("Workspace {$workspace} does not exist.", 'error');
      return;
    }
    $this
      ->merge($this->workspaces[$workspace], $this->workspace, $x, $y);
  }
  function command_merge_to($workspace, $x = 0, $y = 0) {
    $this
      ->log("Merge to: {$workspace} ({$x}, {$y})");
    if (empty($this->workspaces[$workspace])) {
      $this
        ->log("Workspace {$workspace} does not exist.", 'error');
      return;
    }
    $this
      ->merge($this->workspace, $this->workspaces[$workspace], $x, $y);
    $this
      ->set_current_workspace($workspace);
  }

  /**
   * Blend an image into the current workspace.
   */
  function command_merge_from_file($file, $x = 0, $y = 0) {
    $this
      ->log("Merge from file: {$file} ({$x}, {$y})");
    if (!file_exists($file)) {

      // Try it relative to the plugin
      $file = $this->plugin['path'] . '/' . $file;
      if (!file_exists($file)) {
        $this
          ->log("Unable to open {$file}");
        return;
      }
    }
    $source = imagecreatefrompng($file);
    $this
      ->merge($source, $this->workspace, $x, $y);
    imagedestroy($source);
  }
  function command_fill($color, $x, $y, $width, $height) {
    $this
      ->log("Fill: {$color} ({$x}, {$y}, {$width}, {$height})");
    imagefilledrectangle($this->workspace, $x, $y, $x + $width, $y + $height, _color_gd($this->workspace, $this->palette[$color]));
  }
  function command_gradient($from, $to, $x, $y, $width, $height, $direction = 'down') {
    $this
      ->log("Gradient: {$from} to {$to} ({$x}, {$y}, {$width}, {$height}) {$direction}");
    if ($direction == 'down') {
      for ($i = 0; $i < $height; ++$i) {
        $color = _color_blend($this->workspace, $this->palette[$from], $this->palette[$to], $i / ($height - 1));
        imagefilledrectangle($this->workspace, $x, $y + $i, $x + $width, $y + $i + 1, $color);
      }
    }
    else {
      for ($i = 0; $i < $width; ++$i) {
        $color = _color_blend($this->workspace, $this->palette[$from], $this->palette[$to], $i / ($width - 1));
        imagefilledrectangle($this->workspace, $x + $i, $y, $x + $i + 1, $y + $height, $color);
      }
    }
  }

  /**
   * Colorize the current workspace with the given location.
   *
   * This uses simple color blending to colorize the image.
   *
   * @todo it is possible that this colorize could allow different methods for
   * determining how to blend colors?
   */
  function command_colorize($color, $x = NULL, $y = NULL, $width = NULL, $height = NULL) {
    if (!isset($x)) {
      $whole_image = TRUE;
      $x = $y = 0;
      $width = imagesx($this->workspace);
      $height = imagesy($this->workspace);
    }
    $this
      ->log("Colorize: {$color} ({$x}, {$y}, {$width}, {$height})");
    $c = _color_unpack($this->palette[$color]);
    imagealphablending($this->workspace, FALSE);
    imagesavealpha($this->workspace, TRUE);

    // If PHP 5 use the nice imagefilter which is faster.
    if (!empty($whole_image) && version_compare(phpversion(), '5.2.5', '>=') && function_exists('imagefilter')) {
      imagefilter($this->workspace, IMG_FILTER_COLORIZE, $c[0], $c[1], $c[2]);
    }
    else {

      // Otherwise we can do it the brute force way.
      for ($j = 0; $j < $height; $j++) {
        for ($i = 0; $i < $width; $i++) {
          $current = imagecolorsforindex($this->workspace, imagecolorat($this->workspace, $i, $j));
          $new_index = imagecolorallocatealpha($this->workspace, $c[0], $c[1], $c[2], $current['alpha']);
          imagesetpixel($this->workspace, $i, $j, $new_index);
        }
      }
    }
  }

  /**
   * Colorize the current workspace with the given location.
   *
   * This uses a color replacement algorithm that retains luminosity but
   * turns replaces all color with the specified color.
   */
  function command_hue($color, $x = NULL, $y = NULL, $width = NULL, $height = NULL) {
    if (!isset($x)) {
      $whole_image = TRUE;
      $x = $y = 0;
      $width = imagesx($this->workspace);
      $height = imagesy($this->workspace);
    }
    $this
      ->log("Hue: {$color} ({$x}, {$y}, {$width}, {$height})");
    list($red, $green, $blue) = _color_unpack($this->palette[$color]);

    // We will create a monochromatic palette based on the input color
    // which will go from black to white.
    // Input color luminosity: this is equivalent to the position of the
    // input color in the monochromatic palette
    $luminosity_input = round(255 * ($red + $green + $blue) / 765);

    // 765 = 255 * 3
    // We fill the palette entry with the input color at itscorresponding position
    $palette[$luminosity_input]['red'] = $red;
    $palette[$luminosity_input]['green'] = $green;
    $palette[$luminosity_input]['blue'] = $blue;

    // Now we complete the palette, first we'll do it to the black, and then to
    // the white.
    // From input to black
    $steps_to_black = $luminosity_input;

    // The step size for each component
    if ($steps_to_black) {
      $step_size_red = $red / $steps_to_black;
      $step_size_green = $green / $steps_to_black;
      $step_size_blue = $blue / $steps_to_black;
      for ($i = $steps_to_black; $i >= 0; $i--) {
        $palette[$steps_to_black - $i]['red'] = $red - round($step_size_red * $i);
        $palette[$steps_to_black - $i]['green'] = $green - round($step_size_green * $i);
        $palette[$steps_to_black - $i]['blue'] = $blue - round($step_size_blue * $i);
      }
    }

    // From input to white
    $steps_to_white = 255 - $luminosity_input;
    if ($steps_to_white) {
      $step_size_red = (255 - $red) / $steps_to_white;
      $step_size_green = (255 - $green) / $steps_to_white;
      $step_size_blue = (255 - $blue) / $steps_to_white;
    }
    else {
      $step_size_red = $step_size_green = $step_size_blue = 0;
    }

    // The step size for each component
    for ($i = $luminosity_input + 1; $i <= 255; $i++) {
      $palette[$i]['red'] = $red + round($step_size_red * ($i - $luminosity_input));
      $palette[$i]['green'] = $green + round($step_size_green * ($i - $luminosity_input));
      $palette[$i]['blue'] = $blue + round($step_size_blue * ($i - $luminosity_input));
    }

    // Go over the specified area of the image and update the colors.
    for ($j = $x; $j < $height; $j++) {
      for ($i = $y; $i < $width; $i++) {
        $color = imagecolorsforindex($this->workspace, imagecolorat($this->workspace, $i, $j));
        $luminosity = round(255 * ($color['red'] + $color['green'] + $color['blue']) / 765);
        $new_color = imagecolorallocatealpha($this->workspace, $palette[$luminosity]['red'], $palette[$luminosity]['green'], $palette[$luminosity]['blue'], $color['alpha']);
        imagesetpixel($this->workspace, $i, $j, $new_color);
      }
    }
  }

  /**
   * Take a slice out of the current workspace and save it as an image.
   */
  function command_slice($file, $x = NULL, $y = NULL, $width = NULL, $height = NULL) {
    if (!isset($x)) {
      $x = $y = 0;
      $width = imagesx($this->workspace);
      $height = imagesy($this->workspace);
    }
    $this
      ->log("Slice: {$file} ({$x}, {$y}, {$width}, {$height})");
    $base = basename($file);
    $image = $this->path . '/' . $base;
    $slice = $this
      ->new_image($this->workspace, $width, $height);
    imagecopy($slice, $this->workspace, 0, 0, $x, $y, $width, $height);

    // Make sure alphas are saved:
    imagealphablending($slice, FALSE);
    imagesavealpha($slice, TRUE);

    // Save image.
    $temp_name = drupal_tempnam('temporary://', 'file');
    imagepng($slice, drupal_realpath($temp_name));
    file_unmanaged_move($temp_name, $image);
    imagedestroy($slice);

    // Set standard file permissions for webserver-generated files
    @chmod(realpath($image), 0664);
    $this->paths[$file] = $image;
  }

  /**
   * Prepare a new image for being copied or worked on, preserving transparency.
   */
  function &new_image(&$source, $width = NULL, $height = NULL) {
    if (!isset($width)) {
      $width = imagesx($source);
    }
    if (!isset($height)) {
      $height = imagesy($source);
    }
    $target = imagecreatetruecolor($width, $height);
    imagealphablending($target, FALSE);
    imagesavealpha($target, TRUE);
    $transparency_index = imagecolortransparent($source);

    // If we have a specific transparent color
    if ($transparency_index >= 0) {

      // Get the original image's transparent color's RGB values
      $transparent_color = imagecolorsforindex($source, $transparency_index);

      // Allocate the same color in the new image resource
      $transparency_index = imagecolorallocate($target, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);

      // Completely fill the background of the new image with allocated color.
      imagefill($target, 0, 0, $transparency_index);

      // Set the background color for new image to transparent
      imagecolortransparent($target, $transparency_index);
    }
    else {

      // Create a new transparent color for image
      $color = imagecolorallocatealpha($target, 0, 0, 0, 127);

      // Completely fill the background of the new image with allocated color.
      imagefill($target, 0, 0, $color);
    }
    return $target;
  }

  /**
   * Merge two images together, preserving alpha transparency.
   */
  function merge(&$from, &$to, $x, $y) {

    // Blend over template.
    $width = imagesx($from);
    $height = imagesy($from);

    // Re-enable alpha blending to make sure transparency merges.
    imagealphablending($to, TRUE);
    imagecopy($to, $from, $x, $y, 0, 0, $width, $height);
    imagealphablending($to, FALSE);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ctools_stylizer_image_processor::$error_log property
ctools_stylizer_image_processor::$message_log property
ctools_stylizer_image_processor::$name property
ctools_stylizer_image_processor::$workspace property
ctools_stylizer_image_processor::$workspaces property
ctools_stylizer_image_processor::command_colorize function Colorize the current workspace with the given location.
ctools_stylizer_image_processor::command_fill function
ctools_stylizer_image_processor::command_gradient function
ctools_stylizer_image_processor::command_hue function Colorize the current workspace with the given location.
ctools_stylizer_image_processor::command_load function Create a new workspace a file.
ctools_stylizer_image_processor::command_merge_from function Copy the contents of one workspace into the current workspace.
ctools_stylizer_image_processor::command_merge_from_file function Blend an image into the current workspace.
ctools_stylizer_image_processor::command_merge_to function
ctools_stylizer_image_processor::command_new function Create a new workspace.
ctools_stylizer_image_processor::command_new_from function Create a new workspace using the properties of an existing workspace.
ctools_stylizer_image_processor::command_slice function Take a slice out of the current workspace and save it as an image.
ctools_stylizer_image_processor::command_workspace function Set the current workspace.
ctools_stylizer_image_processor::execute function
ctools_stylizer_image_processor::log function
ctools_stylizer_image_processor::merge function Merge two images together, preserving alpha transparency.
ctools_stylizer_image_processor::new_image function Prepare a new image for being copied or worked on, preserving transparency.
ctools_stylizer_image_processor::set_current_workspace function