You are here

imagecache_reflect.module in ImageCache Reflect 7

Same filename and directory in other branches
  1. 8 imagecache_reflect.module

Adds a reflection action for images.

File

imagecache_reflect.module
View source
<?php

/**
 * @file
 * Adds a reflection action for images.
 */

/**
 * Implements hook_image_effect_info().
 */
function imagecache_reflect_image_effect_info() {
  $effects = array();
  $effects['imagecache_reflect'] = array(
    'label' => t('Reflect'),
    'help' => t('Creates a reflection-like effect.'),
    'effect callback' => 'imagecache_reflect_image',
    'form callback' => 'imagecache_reflect_form',
    'summary theme' => 'imagecache_reflect_summary',
  );
  return $effects;
}

/**
 * Implements hook_theme().
 */
function imagecache_reflect_theme() {
  return array(
    'imagecache_reflect_summary' => array(
      'variables' => array(
        'data' => NULL,
      ),
    ),
  );
}

/**
 * Implements imagecache_hook_form()
 * 
 * Config form for reflect action.
 */
function imagecache_reflect_form($action) {
  $action = (array) $action;
  $default = array(
    'imagecache_reflect_color' => 'White',
    'imagecache_reflect_position' => 'bottom',
    'imagecache_reflect_size' => '50%',
    'imagecache_reflect_transparent_source' => FALSE,
  );
  $action = array_merge($default, (array) $action);
  $form = array();
  $form['imagecache_reflect_color'] = array(
    '#type' => 'textfield',
    '#title' => t('Background color'),
    '#description' => t('A hexadecimal color code preceded by a # or the name of the color can be used. eg. <em>white, blue, green, black, etc</em>'),
    '#default_value' => $action['imagecache_reflect_color'],
    '#size' => 10,
    '#maxlength' => 20,
    '#suffix' => '<div class="imagecache-reflect-color"></div>',
    '#attributes' => array(
      'class' => array(
        'edit-imagecache-reflect-colorpicker',
      ),
    ),
    '#element_validate' => array(
      '_imagecache_reflect_validate_color',
    ),
    '#attached' => array(
      'library' => array(
        array(
          'system',
          'farbtastic',
        ),
      ),
      'js' => array(
        drupal_get_path('module', 'imagecache_reflect') . '/imagecache_reflect.js',
      ),
    ),
  );
  $form['imagecache_reflect_transparent_source'] = array(
    '#type' => 'checkbox',
    '#title' => t('Transparent source image'),
    '#default_value' => $action['imagecache_reflect_transparent_source'],
    '#description' => t('If the image that you are reflecting uses alpha transparency, optionally use a much slower algorithm for creating the images, but one that will preserve the transparency.'),
  );
  $form['imagecache_reflect_position'] = array(
    '#type' => 'radios',
    '#title' => t('Position'),
    '#default_value' => $action['imagecache_reflect_position'],
    '#options' => array(
      'top' => t('Top'),
      'right' => t('Right'),
      'bottom' => t('Bottom'),
      'left' => t('Left'),
    ),
    '#description' => t('The position of the image reflection. Default is bottom.'),
    '#required' => TRUE,
  );
  $form['imagecache_reflect_size'] = array(
    '#type' => 'textfield',
    '#title' => t('Size'),
    '#default_value' => $action['imagecache_reflect_size'],
    '#description' => t('The size of the reflection in pixels. You may append % to the integer to represent percentages.'),
    '#required' => TRUE,
  );
  return $form;
}

/**
 * Validates the form color.
 */
function _imagecache_reflect_validate_color($form, &$form_state) {
  $c = $form_state['values']['data']['imagecache_reflect_color'];
  if (!empty($c) || $c == '0') {
    if (drupal_substr($c, 0, 1) == '#') {
      if (!preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/i', $c)) {
        form_error($form, t('Not a valid hex color.'));
      }
    }
    else {
      $c = strtolower($c);
      $colors = (array) array_change_key_case(_imagecache_reflect_colors());
      if (!array_key_exists($c, $colors)) {
        form_error($form, t('Not a valid color name.'));
      }
    }
  }
}

/**
 * Formats a summary of the reflection-like effect.
 * 
 * @param array $variables
 *   An associative array containing:
 *   - data: The current configuration for this reflection-like effect.
 */
function theme_imagecache_reflect_summary($variables) {
  $data = $variables['data'];
  return t('Background Color: !color, Preserve transparency: @preserve, Position: !position, Size: @size', array(
    '!color' => !empty($data['imagecache_reflect_color']) ? $data['imagecache_reflect_color'] : t('White'),
    '@preserve' => $data['imagecache_reflect_transparent_source'] ? t('Yes') : t('No'),
    '!position' => $data['imagecache_reflect_position'],
    '@size' => $data['imagecache_reflect_size'],
  ));
}

/**
 * Implements hook_image().
 * 
 * Creates the reflection-like effect.
 */
function imagecache_reflect_image(&$image, $data) {
  $data = array_merge(array(
    'imagecache_reflect_color' => 'white',
    'imagecache_reflect_position' => 'bottom',
    'imagecache_reflect_transparent_source' => FALSE,
    'imagecache_reflect_size' => '50%',
  ), $data);
  $is_vertical = in_array($data['imagecache_reflect_position'], array(
    'top',
    'bottom',
  ));
  if (preg_match('/^\\d{1,3}%$/', $data['imagecache_reflect_size'])) {
    $image_size = $is_vertical ? $image->info['height'] : $image->info['width'];
    $data['imagecache_reflect_size'] = $image_size * (floatval($data['imagecache_reflect_size']) / 100);
  }
  else {
    $data['imagecache_reflect_size'] = intval($data['imagecache_reflect_size']);
  }
  if (!empty($data['imagecache_reflect_color'])) {
    $data['imagecache_reflect_color'] = _imagecache_reflect_color($data['imagecache_reflect_color']);
  }
  $width = $image->info['width'] + ($is_vertical ? 0 : $data['imagecache_reflect_size']);
  $height = $image->info['height'] + ($is_vertical ? $data['imagecache_reflect_size'] : 0);
  $x = $data['imagecache_reflect_position'] == 'left' ? $data['imagecache_reflect_size'] : 0;
  $y = $data['imagecache_reflect_position'] == 'top' ? $data['imagecache_reflect_size'] : 0;
  $background = imagecreatetruecolor($width, $height);

  // If $data['imagecache_reflect_color'] is empty,
  // we're trying for a transparent canvas:
  if (empty($data['imagecache_reflect_color'])) {
    imagesavealpha($background, TRUE);
    imagealphablending($background, FALSE);
  }
  else {
    imagefill($background, 0, 0, $data['imagecache_reflect_color']);
  }
  imagecopy($background, $image->resource, $x, $y, 0, 0, $image->info['width'], $image->info['height']);

  // If the user has said that the source image might contain
  // transparency, use the slower, but working algorithm to merge
  // the images:
  if ($data['imagecache_reflect_transparent_source']) {
    $min_alpha = _imagecache_reflect_alpha_get_min($image->resource);
  }
  else {
    $min_alpha = 0;
  }
  $return = FALSE;
  switch ($data['imagecache_reflect_position']) {
    case 'top':
      $steps = min($data['imagecache_reflect_size'], $image->info['height']);
      for ($i = 0, $opacity = 50; $i < $steps; ++$i, $opacity = ceil(($steps - $i) / $steps * 100 / 2)) {
        _imagecache_reflect_imagecopymerge_alpha($background, $image->resource, 0, $y - $i, 0, $i, $image->info['width'], 1, $opacity, $min_alpha);
      }
      $return = TRUE;
      break;
    case 'bottom':
      $steps = min($data['imagecache_reflect_size'], $image->info['height']);
      for ($i = 0, $opacity = 50; $i < $steps; ++$i, $opacity = ceil(($steps - $i) / $steps * 100 / 2)) {
        _imagecache_reflect_imagecopymerge_alpha($background, $image->resource, 0, $image->info['height'] + $i, 0, $image->info['height'] - $i - 1, $image->info['width'], 1, $opacity, $min_alpha);
      }
      $return = TRUE;
      break;
    case 'left':
      $steps = min($data['imagecache_reflect_size'], $image->info['width']);
      for ($i = 0, $opacity = 50; $i < $steps; ++$i, $opacity = ceil(($steps - $i) / $steps * 100 / 2)) {
        _imagecache_reflect_imagecopymerge_alpha($background, $image->resource, $x - $i, 0, $i, 0, 1, $image->info['height'], $opacity, $min_alpha);
      }
      $return = TRUE;
      break;
    case 'right':
      $steps = min($data['imagecache_reflect_size'], $image->info['height']);
      for ($i = 0, $opacity = 50; $i < $steps; ++$i, $opacity = ceil(($steps - $i) / $steps * 100 / 2)) {
        _imagecache_reflect_imagecopymerge_alpha($background, $image->resource, $image->info['width'] + $i, 0, $image->info['width'] - $i - 1, 0, 1, $image->info['height'], $opacity, $min_alpha);
      }
      $return = TRUE;
      break;
  }
  if ($return) {
    imagedestroy($image->resource);
    $image->resource = $background;
    $image->info['width'] = $width;
    $image->info['height'] = $height;
  }
  else {
    imagedestroy($background);
  }
  return $return;
}

/**
 * A fix to get a function like imagecopymerge with alpha blending.
 *
 * Main script by aiden dot mail at freemail dot hu.
 * Transformed to imagecopymerge_alpha() by rodrigo dot polo at gmail dot com
 * @see http://uk3.php.net/manual/en/function.imagecopymerge.php#88456
 */
function _imagecache_reflect_imagecopymerge_alpha(&$dst_im, &$src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct, $minalpha = NULL) {
  if (!isset($pct)) {
    return FALSE;
  }
  $pct /= 100;

  // Get image width and height.
  $w = imagesx($src_im);
  $h = imagesy($src_im);

  // Turn alpha blending off.
  imagealphablending($src_im, FALSE);
  if (is_null($minalpha)) {

    // Find the most opaque pixel in the image (smallest alpha value).
    $minalpha = 127;
    for ($x = 0; $x < $w; $x++) {
      for ($y = 0; $y < $h; $y++) {
        $alpha = imagecolorat($src_im, $x, $y) >> 24 & 0xff;
        if ($alpha < $minalpha) {
          $minalpha = $alpha;
        }
      }
    }
  }

  // Loop through image pixels and modify alpha for each.
  for ($x = $src_x; $x < $src_x + $src_w; $x++) {
    for ($y = $src_y; $y < $src_y + $src_h; $y++) {

      // Get current alpha value (represents the transparency).
      $colorxy = imagecolorat($src_im, $x, $y);
      $alpha = $colorxy >> 24 & 0xff;

      // Calculate new alpha.
      if ($minalpha !== 127) {
        $alpha = 127 + 127 * $pct * ($alpha - 127) / (127 - $minalpha);
      }
      else {
        $alpha += 127 * $pct;
      }

      // Get the color index with new alpha.
      $alphacolorxy = imagecolorallocatealpha($src_im, $colorxy >> 16 & 0xff, $colorxy >> 8 & 0xff, $colorxy & 0xff, $alpha);

      // Set pixel with the new color + opacity.
      if (!imagesetpixel($src_im, $x, $y, $alphacolorxy)) {
        return FALSE;
      }
    }
  }

  // The image copy.
  imagecopy($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h);
}

/**
 * Compute the minimum alpha value of the pixels of a given image resource.
 *
 * Warning, this function is very expensive.
 */
function _imagecache_reflect_alpha_get_min($src_im) {
  $w = imagesx($src_im);
  $h = imagesy($src_im);

  // Turn alpha blending off.
  imagealphablending($src_im, FALSE);

  // Find the most opaque pixel in the image (smallest alpha value).
  $minalpha = 127;
  for ($x = 0; $x < $w; $x++) {
    for ($y = 0; $y < $h; $y++) {
      $alpha = imagecolorat($src_im, $x, $y) >> 24 & 0xff;
      if ($alpha < $minalpha) {
        $minalpha = $alpha;
      }
    }
  }
  return $minalpha;
}

/**
 * Get color indentifier to use in imagefill function.
 *
 * @param string $c
 *   The hex value (3 or 6 characters prepended with '#') or name of the color.
 * 
 * @return color
 *   Returns a color identifier representing the color hex value or name.
 */
function _imagecache_reflect_color($c) {
  if (!empty($c)) {
    if (drupal_substr($c, 0, 1) == '#') {
      $c = drupal_substr($c, 1);
      if (drupal_strlen($c) == 6) {
        $c = drupal_substr($c, 0, 6);
        $c = str_pad($c, 6, '0');
        $color = '0x' . $c;
      }
      else {
        $c = drupal_substr($c, 0, 3);
        $color = '0x' . $c[0] . $c[0] . $c[1] . $c[1] . $c[2] . $c[2];
      }
    }
    else {
      $c = drupal_strtolower(trim($c));
      foreach (_imagecache_reflect_colors() as $name => $value) {
        if ($c == drupal_strtolower($name)) {
          $color = $value;
        }
      }
    }
  }
  else {
    $color = 0xffffff;
  }
  return $color;
}

/**
 * Gets an array of colors with hex equivalent.
 */
function _imagecache_reflect_colors() {
  static $colors = array(
    'AliceBlue' => '0xF0F8FF',
    'AntiqueWhite' => '0xFAEBD7',
    'Aqua' => '0x00FFFF',
    'Aquamarine' => '0x7FFFD4',
    'Azure' => '0xF0FFFF',
    'Beige' => '0xF5F5DC',
    'Bisque' => '0xFFE4C4',
    'Black' => '0x000000',
    'BlanchedAlmond' => '0xFFEBCD',
    'Blue' => '0x0000FF',
    'BlueViolet' => '0x8A2BE2',
    'Brown' => '0xA52A2A',
    'BurlyWood' => '0xDEB887',
    'CadetBlue' => '0x5F9EA0',
    'Chartreuse' => '0x7FFF00',
    'Chocolate' => '0xD2691E',
    'Coral' => '0xFF7F50',
    'CornflowerBlue' => '0x6495ED',
    'Cornsilk' => '0xFFF8DC',
    'Crimson' => '0xDC143C',
    'Cyan' => '0x00FFFF',
    'DarkBlue' => '0x00008B',
    'DarkCyan' => '0x008B8B',
    'DarkGoldenRod' => '0xB8860B',
    'DarkGray' => '0xA9A9A9',
    'DarkGrey' => '0xA9A9A9',
    'DarkGreen' => '0x006400',
    'DarkKhaki' => '0xBDB76B',
    'DarkMagenta' => '0x8B008B',
    'DarkOliveGreen' => '0x556B2F',
    'Darkorange' => '0xFF8C00',
    'DarkOrchid' => '0x9932CC',
    'DarkRed' => '0x8B0000',
    'DarkSalmon' => '0xE9967A',
    'DarkSeaGreen' => '0x8FBC8F',
    'DarkSlateBlue' => '0x483D8B',
    'DarkSlateGray' => '0x2F4F4F',
    'DarkSlateGrey' => '0x2F4F4F',
    'DarkTurquoise' => '0x00CED1',
    'DarkViolet' => '0x9400D3',
    'DeepPink' => '0xFF1493',
    'DeepSkyBlue' => '0x00BFFF',
    'DimGray' => '0x696969',
    'DimGrey' => '0x696969',
    'DodgerBlue' => '0x1E90FF',
    'FireBrick' => '0xB22222',
    'FloralWhite' => '0xFFFAF0',
    'ForestGreen' => '0x228B22',
    'Fuchsia' => '0xFF00FF',
    'Gainsboro' => '0xDCDCDC',
    'GhostWhite' => '0xF8F8FF',
    'Gold' => '0xFFD700',
    'GoldenRod' => '0xDAA520',
    'Gray' => '0x808080',
    'Grey' => '0x808080',
    'Green' => '0x008000',
    'GreenYellow' => '0xADFF2F',
    'HoneyDew' => '0xF0FFF0',
    'HotPink' => '0xFF69B4',
    'IndianRed' => '0xCD5C5C',
    'Indigo' => '0x4B0082',
    'Ivory' => '0xFFFFF0',
    'Khaki' => '0xF0E68C',
    'Lavender' => '0xE6E6FA',
    'LavenderBlush' => '0xFFF0F5',
    'LawnGreen' => '0x7CFC00',
    'LemonChiffon' => '0xFFFACD',
    'LightBlue' => '0xADD8E6',
    'LightCoral' => '0xF08080',
    'LightCyan' => '0xE0FFFF',
    'LightGoldenRodYellow' => '0xFAFAD2',
    'LightGray' => '0xD3D3D3',
    'LightGrey' => '0xD3D3D3',
    'LightGreen' => '0x90EE90',
    'LightPink' => '0xFFB6C1',
    'LightSalmon' => '0xFFA07A',
    'LightSeaGreen' => '0x20B2AA',
    'LightSkyBlue' => '0x87CEFA',
    'LightSlateGray' => '0x778899',
    'LightSlateGrey' => '0x778899',
    'LightSteelBlue' => '0xB0C4DE',
    'LightYellow' => '0xFFFFE0',
    'Lime' => '0x00FF00',
    'LimeGreen' => '0x32CD32',
    'Linen' => '0xFAF0E6',
    'Magenta' => '0xFF00FF',
    'Maroon' => '0x800000',
    'MediumAquaMarine' => '0x66CDAA',
    'MediumBlue' => '0x0000CD',
    'MediumOrchid' => '0xBA55D3',
    'MediumPurple' => '0x9370D8',
    'MediumSeaGreen' => '0x3CB371',
    'MediumSlateBlue' => '0x7B68EE',
    'MediumSpringGreen' => '0x00FA9A',
    'MediumTurquoise' => '0x48D1CC',
    'MediumVioletRed' => '0xC71585',
    'MidnightBlue' => '0x191970',
    'MintCream' => '0xF5FFFA',
    'MistyRose' => '0xFFE4E1',
    'Moccasin' => '0xFFE4B5',
    'NavajoWhite' => '0xFFDEAD',
    'Navy' => '0x000080',
    'OldLace' => '0xFDF5E6',
    'Olive' => '0x808000',
    'OliveDrab' => '0x6B8E23',
    'Orange' => '0xFFA500',
    'OrangeRed' => '0xFF4500',
    'Orchid' => '0xDA70D6',
    'PaleGoldenRod' => '0xEEE8AA',
    'PaleGreen' => '0x98FB98',
    'PaleTurquoise' => '0xAFEEEE',
    'PaleVioletRed' => '0xD87093',
    'PapayaWhip' => '0xFFEFD5',
    'PeachPuff' => '0xFFDAB9',
    'Peru' => '0xCD853F',
    'Pink' => '0xFFC0CB',
    'Plum' => '0xDDA0DD',
    'PowderBlue' => '0xB0E0E6',
    'Purple' => '0x800080',
    'Red' => '0xFF0000',
    'RosyBrown' => '0xBC8F8F',
    'RoyalBlue' => '0x041690',
    'SaddleBrown' => '0x8B4513',
    'Salmon' => '0xFA8072',
    'SandyBrown' => '0xF4A460',
    'SeaGreen' => '0x2E8B57',
    'SeaShell' => '0xFFF5EE',
    'Sienna' => '0xA0522D',
    'Silver' => '0xC0C0C0',
    'SkyBlue' => '0x87CEEB',
    'SlateBlue' => '0x6A5ACD',
    'SlateGray' => '0x708090',
    'SlateGrey' => '0x708090',
    'Snow' => '0xFFFAFA',
    'SpringGreen' => '0x00FF7F',
    'SteelBlue' => '0x4682B4',
    'Tan' => '0xD2B48C',
    'Teal' => '0x008080',
    'Thistle' => '0xD8BFD8',
    'Tomato' => '0xFF6347',
    'Turquoise' => '0x40E0D0',
    'Violet' => '0xEE82EE',
    'Wheat' => '0xF5DEB3',
    'White' => '0xFFFFFF',
    'WhiteSmoke' => '0xF5F5F5',
    'Yellow' => '0xFFFF00',
    'YellowGreen' => '0x9ACD32',
  );
  return $colors;
}

Functions

Namesort descending Description
imagecache_reflect_form Implements imagecache_hook_form()
imagecache_reflect_image Implements hook_image().
imagecache_reflect_image_effect_info Implements hook_image_effect_info().
imagecache_reflect_theme Implements hook_theme().
theme_imagecache_reflect_summary Formats a summary of the reflection-like effect.
_imagecache_reflect_alpha_get_min Compute the minimum alpha value of the pixels of a given image resource.
_imagecache_reflect_color Get color indentifier to use in imagefill function.
_imagecache_reflect_colors Gets an array of colors with hex equivalent.
_imagecache_reflect_imagecopymerge_alpha A fix to get a function like imagecopymerge with alpha blending.
_imagecache_reflect_validate_color Validates the form color.