Files
server/usr/share/psa-pear/pear/php/Horde/Image/Gd.php
2026-01-07 20:52:11 +01:00

1110 lines
34 KiB
PHP

<?php
/**
* Copyright 2002-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file LICENSE for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Michael J. Rubinsky <mrubinsk@horde.org>
* @author Jan Schneider <jan@horde.org>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*/
/**
* This class implements the Horde_Image API for the PHP GD extension.
*
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Michael J. Rubinsky <mrubinsk@horde.org>
* @author Jan Schneider <jan@horde.org>
* @category Horde
* @copyright 2002-2017 Horde LLC
* @license http://www.horde.org/licenses/lgpl21 LGPL-2.1
* @package Image
*
* @property-read resource $_im The underlaying image resource.
*/
class Horde_Image_Gd extends Horde_Image_Base
{
/**
* Allocated color resources.
*
* @var int[]
*/
protected $_colors = array();
/**
* Capabilites of this driver.
*
* @var string[]
*/
protected $_capabilities = array(
'canvas',
'circle',
'crop',
'dashedLine',
'flip',
'grayscale',
'line',
'mirror',
'polygon',
'polyline',
'rectangle',
'resize',
'rotate',
'roundedRectangle',
'sepia',
'text',
'yellowize',
);
/**
* GD image resource for the current image data.
*
* @var resource
*/
protected $_im;
/**
* Constructor.
*
* @see Horde_Image_Base::_construct
*/
public function __construct($params, $context = array())
{
parent::__construct($params, $context);
if (!empty($params['filename'])) {
$this->loadFile($params['filename']);
} elseif (!empty($params['data'])) {
$this->loadString($params['data']);
} elseif (!empty($params['width'])) {
$this->_im = $this->create($this->_width, $this->_height);
$this->call(
'imageFill',
array(
$this->_im,
0, 0,
$this->_allocateColor($this->_background)
)
);
}
}
/**
*/
public function __get($property)
{
switch ($property) {
case '_im':
return $this->_im;
}
}
/**
* Displays the current image.
*/
public function display()
{
$this->headers();
$this->call('image' . $this->_type, array($this->_im));
}
/**
* Returns the raw data for this image.
*
* @param boolean $convert Ignored for Gd driver.
* @param array $options Array of options:
* - stream: If true, return as a stream resource.
* DEFAULT: false.
*
* @return string The raw image data.
*/
public function raw($convert = false, $options = array())
{
if (!is_resource($this->_im)) {
return '';
}
ob_start();
call_user_func('image' . $this->_type, $this->_im);
if (empty($options['stream'])) {
return ob_get_clean();
}
$s = new Horde_Stream_Temp();
$s->add(ob_get_clean(), true);
return $s->stream;
}
/**
* Resets the image data to defaults.
*/
public function reset()
{
parent::reset();
if (is_resource($this->_im)) {
$this->call('imageDestroy', array($this->_im));
}
}
/**
* Returns the height and width of the current image data.
*
* @return array A hash with 'width' containing the width, 'height'
* containing the height of the image.
*/
public function getDimensions()
{
if (is_resource($this->_im) &&
$this->_width == 0 &&
$this->_height == 0) {
$this->_width = $this->call('imageSX', array($this->_im));
$this->_height = $this->call('imageSY', array($this->_im));
}
return array(
'width' => $this->_width,
'height' => $this->_height
);
}
/**
* Creates a color that can be accessed in this object.
*
* When a color is set, the integer resource of it is returned.
*
* @param string $name The name of the color.
* @param integer $alpha Alpha transparency (0 - 127).
*
* @return integer The resource of the color that can be passed to GD.
*/
private function _allocateColor($name, $alpha = 0)
{
if (empty($this->_colors[$name])) {
if ($name == 'none') {
$this->_colors[$name] = $this->call(
'imageColorAllocateAlpha',
array($this->_im, 0, 0, 0, 127)
);
} else {
list($r, $g, $b) = Horde_Image::getRGB($name);
$this->_colors[$name] = $this->call(
'imageColorAllocateAlpha',
array($this->_im, $r, $g, $b, $alpha)
);
}
}
return $this->_colors[$name];
}
/**
* Returns the numeric font size a from textual description.
*
* @param string $font A textual size description.
*
* @return integer The font size supported by GD.
*/
private function _getFont($font)
{
switch ($font) {
case 'tiny':
return 1;
case 'medium':
return 3;
case 'large':
return 4;
case 'giant':
return 5;
case 'small':
default:
return 2;
}
}
/**
* Loads the image data from a string.
*
* @param string $image_data The data to use for the image.
*/
public function loadString($image_data)
{
$this->_im = $this->call('imageCreateFromString', array($image_data));
}
/**
* Loads the image data from a file.
*
* @param string $filename The full path and filename to the file to load
* the image data from.
*/
public function loadFile($filename)
{
$info = $this->call('getimagesize', array($filename));
if (is_array($info)) {
switch ($info[2]) {
case IMAGETYPE_GIF:
if (function_exists('imagecreatefromgif')) {
$this->_im = $this->call(
'imagecreatefromgif',
array($filename)
);
}
break;
case IMAGETYPE_JPEG:
$this->_im = $this->call(
'imagecreatefromjpeg',
array($filename)
);
break;
case IMAGETYPE_PNG:
$this->_im = $this->call(
'imagecreatefrompng',
array($filename)
);
break;
case IMAGETYPE_WBMP:
if (function_exists('imagecreatefromgwbmp')) {
$this->_im = $this->call(
'imagecreatefromgwbmp',
array($filename)
);
}
break;
case IMAGETYPE_XBM:
$this->_im = $this->call(
'imagecreatefromxbm',
array($filename)
);
break;
}
}
if (is_resource($this->_im)) {
return;
}
parent::loadFile($filename);
$this->_im = $this->call(
'imageCreateFromString',
array($this->_data->__toString())
);
}
/**
* Resizes the current image.
*
* @param integer $width The new width.
* @param integer $height The new height.
* @param boolean $ratio Maintain original aspect ratio.
* @param boolean $keepProfile Keep the image meta data (unused).
*/
public function resize($width, $height, $ratio = true, $keepProfile = false)
{
/* Abort if we're asked to divide by zero, truncate the image
* completely in either direction, or there is no image data. */
if (!$width || !$height || !is_resource($this->_im)) {
throw new Horde_Image_Exception('Unable to resize image.');
}
if ($ratio) {
if ($width / $height > $this->call('imageSX', array($this->_im)) / $this->call('imageSY', array($this->_im))) {
$width = $height
* $this->call('imageSX', array($this->_im))
/ $this->call('imageSY', array($this->_im));
} else {
$height = $width
* $this->call('imageSY', array($this->_im))
/ $this->call('imageSX', array($this->_im));
}
}
$im = $this->_im;
$this->_im = $this->create($width, $height);
/* Reset geometry since it will change. */
$this->_width = 0;
$this->_height = 0;
try {
$this->call(
'imageCopyResampled',
array(
$this->_im, $im,
0, 0, 0, 0,
$width, $height,
$this->call('imageSX', array($im)),
$this->call('imageSY', array($im))
)
);
} catch (Horde_Image_Exception $e) {
$this->call(
'imageCopyResized',
array(
$this->_im, $im,
0, 0, 0, 0,
$width, $height,
$this->call('imageSX', array($im)),
$this->call('imageSY', array($im))
)
);
}
}
/**
* Crops the current image.
*
* @param integer $x1 x for the top left corner.
* @param integer $y1 y for the top left corner.
* @param integer $x2 x for the bottom right corner.
* @param integer $y2 y for the bottom right corner.
*/
public function crop($x1, $y1, $x2, $y2)
{
$im = $this->_im;
$this->_im = $this->create($x2 - $x1, $y2 - $y1);
$this->_width = 0;
$this->_height = 0;
$this->call(
'imageCopy',
array($this->_im, $im, 0, 0, $x1, $y1, $x2 - $x1, $y2 - $y1)
);
}
/**
* Rotates the current image.
*
* @param integer $angle The angle to rotate the image by, in the
* clockwise direction.
* @param string $background The background color to fill any triangles.
*/
public function rotate($angle, $background = 'white')
{
$background = $this->_allocateColor($background);
$this->_width = 0;
$this->_height = 0;
switch ($angle) {
case '90':
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$xymax = max($x, $y);
$im = $this->create($xymax, $xymax);
$im = $this->call('imageRotate', array($im, 270, $background));
$this->_im = $im;
$im = $this->create($y, $x);
if ($x < $y) {
$this->call(
'imageCopy',
array($im, $this->_im, 0, 0, 0, 0, $xymax, $xymax)
);
} elseif ($x > $y) {
$this->call(
'imageCopy',
array(
$im, $this->_im,
0, 0, $xymax - $y, $xymax - $x,
$xymax, $xymax
)
);
}
$this->_im = $im;
break;
default:
$this->_im = $this->call(
'imageRotate',
array($this->_im, 360 - $angle, $background)
);
break;
}
}
/**
* Flips the current image.
*/
public function flip()
{
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$im = $this->create($x, $y);
for ($curY = 0; $curY < $y; $curY++) {
$this->call(
'imageCopy',
array($im, $this->_im, 0, $y - ($curY + 1), 0, $curY, $x, 1)
);
}
$this->_im = $im;
}
/**
* Mirrors the current image.
*/
public function mirror()
{
$x = $this->call('imageSX', array($this->_im));
$y = $this->call('imageSY', array($this->_im));
$im = $this->create($x, $y);
for ($curX = 0; $curX < $x; $curX++) {
$this->call(
'imageCopy',
array($im, $this->_im, $x - ($curX + 1), 0, $curX, 0, 1, $y)
);
}
$this->_im = $im;
}
/**
* Converts the current image to grayscale.
*/
public function grayscale()
{
$rateR = .229;
$rateG = .587;
$rateB = .114;
$whiteness = 3;
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = min(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$new = min(
255,
abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
$this->call('imageColorSet', array($this->_im, $x, $new, $new, $new));
}
}
/**
* Applies a sepia filter.
*
* @param integer $threshold Extent of sepia effect.
*/
public function sepia($threshold = 85)
{
$tintR = 80;
$tintG = 43;
$tintB = -23;
$rateR = .229;
$rateG = .587;
$rateB = .114;
$whiteness = 3;
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$new = min(
255,
abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness
);
$r = min(255, $new + $tintR);
$g = min(255, $new + $tintG);
$b = min(255, $new + $tintB);
$this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
}
}
/**
* Applies a yellow filter.
*
* Adds a layer of yellow that can be transparent or solid. If $intensityY
* is 255 the image will be 0% transparent (solid).
*
* @param integer $intensityY How strong should the yellow (red and green)
* be? (0-255)
* @param integer $intensityB How weak should the blue be? (>= 2, in the
* positive limit it will be make BLUE 0)
*/
public function yellowize($intensityY = 50, $intensityB = 3)
{
if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
$this->call('imageTrueColorToPalette', array($this->_im, true, 256));
}
$colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
for ($x = 0; $x < $colors; $x++) {
$src = $this->call('imageColorsForIndex', array($this->_im, $x));
$r = min($src['red'] + $intensityY, 255);
$g = min($src['green'] + $intensityY, 255);
$b = max(($r + $g) / max($intensityB, 2), 0);
$this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
}
}
/**
* Draws a text string on the image in a specified location, with the
* specified style information.
*
* @param string $text The text to draw.
* @param integer $x The left x coordinate of the start of the
* text string.
* @param integer $y The top y coordinate of the start of the text
* string.
* @param string $font The font identifier you want to use for the
* text.
* @param string $color The color that you want the text displayed in.
* @param integer $direction An integer that specifies the orientation of
* the text.
* @param string $fontsize Size of the font (small, medium, large, giant)
*/
public function text(
$string, $x, $y, $font = '', $color = 'black', $direction = 0,
$fontsize = 'small'
)
{
$c = $this->_allocateColor($color);
$f = $this->_getFont($fontsize);
switch ($direction) {
case -90:
case 270:
$this->call(
'imageStringUp',
array($this->_im, $f, $x, $y, $string, $c)
);
break;
case 0:
default:
$this->call(
'imageString',
array($this->_im, $f, $x, $y, $string, $c)
);
break;
}
}
/**
* Draws a circle.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the circle.
* @param string $color The line color of the circle.
* @param string $fill The color to fill the circle.
*/
public function circle($x, $y, $r, $color, $fill = null)
{
$c = $this->_allocateColor($color);
if (is_null($fill)) {
$result = $this->call(
'imageEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $c)
);
} else {
if ($fill !== $color) {
$fillColor = $this->_allocateColor($fill);
$this->call(
'imageFilledEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $fillColor)
);
$this->call(
'imageEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $c)
);
} else {
$this->call(
'imageFilledEllipse',
array($this->_im, $x, $y, $r * 2, $r * 2, $c)
);
}
}
}
/**
* Draws a polygon based on a set of vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the polygon with.
* @param string $fill The color to fill the polygon.
*/
public function polygon($verts, $color, $fill = 'none')
{
$vertices = array();
foreach ($verts as $vert) {
$vertices[] = $vert['x'];
$vertices[] = $vert['y'];
}
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call(
'imageFilledPolygon',
array($this->_im, $vertices, count($verts), $f)
);
}
if ($fill == 'none' || $fill != $color) {
$c = $this->_allocateColor($color);
$this->call(
'imagePolygon',
array($this->_im, $vertices, count($verts), $c)
);
}
}
/**
* Draws a rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rectangle.
*/
public function rectangle(
$x, $y, $width, $height, $color = 'black', $fill = 'none'
)
{
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call(
'imageFilledRectangle',
array($this->_im, $x, $y, $x + $width, $y + $height, $f)
);
}
if ($fill == 'none' || $fill != $color) {
$c = $this->_allocateColor($color);
$this->call(
'imageRectangle',
array($this->_im, $x, $y, $x + $width, $y + $height, $c)
);
}
}
/**
* Draws a rounded rectangle.
*
* @param integer $x The left x-coordinate of the rectangle.
* @param integer $y The top y-coordinate of the rectangle.
* @param integer $width The width of the rectangle.
* @param integer $height The height of the rectangle.
* @param integer $round The width of the corner rounding.
* @param string $color The line color of the rectangle.
* @param string $fill The color to fill the rounded rectangle with.
*/
public function roundedRectangle(
$x, $y, $width, $height, $round, $color = 'black', $fill = 'none'
)
{
if ($round <= 0) {
// Optimize out any calls with no corner rounding.
$this->rectangle($x, $y, $width, $height, $color, $fill);
return;
}
$c = $this->_allocateColor($color);
// Set corner points to avoid lots of redundant math.
$xul = $x + $round;
$yul = $y + $round;
$xur = $x + $width - $round;
$yur = $y + $round;
$xlr = $x + $width - $round;
$ylr = $y + $height - $round;
$xll = $x + $round;
$yll = $y + $height - $round;
$r = $round * 2;
// Calculate the upper left arc.
$pul = Horde_Image::arcPoints($round, 180, 270);
// Calculate the upper right arc.
$pur = Horde_Image::arcPoints($round, 270, 360);
// Calculate the lower right arc.
$plr = Horde_Image::arcPoints($round, 0, 90);
// Calculate the lower left arc.
$pll = Horde_Image::arcPoints($round, 90, 180);
// Draw the corners - upper left, upper right, lower right, lower left.
$this->call(
'imageArc',
array($this->_im, $xul, $yul, $r, $r, 180, 270, $c)
);
$this->call(
'imageArc',
array($this->_im, $xur, $yur, $r, $r, 270, 360, $c)
);
$this->call(
'imageArc',
array($this->_im, $xlr, $ylr, $r, $r, 0, 90, $c)
);
$this->call(
'imageArc',
array($this->_im, $xll, $yll, $r, $r, 90, 180, $c)
);
// Draw the connecting sides - top, right, bottom, left.
$this->call(
'imageLine',
array($this->_im, $xul, $y, $xur, $y, $c)
);
$this->call(
'imageLine',
array($this->_im, $x + $width, $yur, $x + $width, $ylr, $c)
);
$this->call(
'imageLine',
array($this->_im, $xlr, $y + $height, $xll, $y + $height, $c)
);
$this->call(
'imageLine',
array($this->_im, $x, $yll, $x, $yul, $c)
);
if ($fill != 'none') {
$f = $this->_allocateColor($fill);
$this->call(
'imageFillToBorder',
array(
$this->_im,
$x + ($width / 2), $y + ($height / 2),
$c, $f
)
);
}
}
/**
* Draws a line.
*
* @param integer $x0 The x coordinate of the start.
* @param integer $y0 The y coordinate of the start.
* @param integer $x1 The x coordinate of the end.
* @param integer $y1 The y coordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
*/
public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
{
$c = $this->_allocateColor($color);
// Don't need to do anything special for single-width lines.
if ($width == 1) {
$this->call('imageLine', array($this->_im, $x1, $y1, $x2, $y2, $c));
} elseif ($x1 == $x2) {
// For vertical lines, we can just draw a vertical rectangle.
$left = $x1 - floor(($width - 1) / 2);
$right = $x1 + floor($width / 2);
$this->call(
'imageFilledRectangle',
array($this->_im, $left, $y1, $right, $y2, $c)
);
} elseif ($y1 == $y2) {
// For horizontal lines, we can just draw a horizontal filled
// rectangle.
$top = $y1 - floor($width / 2);
$bottom = $y1 + floor(($width - 1) / 2);
$this->call(
'imageFilledRectangle',
array($this->_im, $x1, $top, $x2, $bottom, $c)
);
} else {
// Angled lines.
// Make sure that the end points of the line are perpendicular to
// the line itself.
$a = atan2($y1 - $y2, $x2 - $x1);
$dx = (sin($a) * $width / 2);
$dy = (cos($a) * $width / 2);
$verts = array(
$x2 + $dx, $y2 + $dy,
$x2 - $dx, $y2 - $dy,
$x1 - $dx, $y1 - $dy,
$x1 + $dx, $y1 + $dy
);
$this->call(
'imageFilledPolygon',
array($this->_im, $verts, count($verts) / 2, $c)
);
}
}
/**
* Draws a dashed line.
*
* @param integer $x0 The x co-ordinate of the start.
* @param integer $y0 The y co-ordinate of the start.
* @param integer $x1 The x co-ordinate of the end.
* @param integer $y1 The y co-ordinate of the end.
* @param string $color The line color.
* @param string $width The width of the line.
* @param integer $dash_length The length of a dash on the dashed line.
* @param integer $dash_space The length of a space in the dashed line.
*/
public function dashedLine(
$x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2,
$dash_space = 2
)
{
$c = $this->_allocateColor($color);
$w = $this->_allocateColor('white');
// Set up the style array according to the $dash_* parameters.
$style = array();
for ($i = 0; $i < $dash_length; $i++) {
$style[] = $c;
}
for ($i = 0; $i < $dash_space; $i++) {
$style[] = $w;
}
$this->call('imageSetStyle', array($this->_im, $style));
$this->call('imageSetThickness', array($this->_im, $width));
$this->call(
'imageLine',
array($this->_im, $x0, $y0, $x1, $y1, IMG_COLOR_STYLED)
);
}
/**
* Draws a polyline (a non-closed, non-filled polygon) based on a set of
* vertices.
*
* @param array $vertices An array of x and y labeled arrays
* (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
* @param string $color The color you want to draw the line with.
* @param string $width The width of the line.
*/
public function polyline($verts, $color, $width = 1)
{
$first = true;
foreach ($verts as $vert) {
if (!$first) {
$this->line(
$lastX, $lastY,
$vert['x'], $vert['y'],
$color, $width
);
} else {
$first = false;
}
$lastX = $vert['x'];
$lastY = $vert['y'];
}
}
/**
* Draws an arc.
*
* @param integer $x The x coordinate of the centre.
* @param integer $y The y coordinate of the centre.
* @param integer $r The radius of the arc.
* @param integer $start The start angle of the arc.
* @param integer $end The end angle of the arc.
* @param string $color The line color of the arc.
* @param string $fill The fill color of the arc (defaults to none).
*/
public function arc(
$x, $y, $r, $start, $end, $color = 'black', $fill = null
)
{
$c = $this->_allocateColor($color);
if (is_null($fill)) {
$this->call(
'imageArc',
array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c)
);
} else {
if ($fill !== $color) {
$f = $this->_allocateColor($fill);
$this->call(
'imageFilledArc',
array(
$this->_im,
$x, $y, $r * 2, $r * 2,
$start, $end,
$f, IMG_ARC_PIE
)
);
$this->call(
'imageFilledArc',
array(
$this->_im,
$x, $y, $r * 2, $r * 2,
$start, $end,
$c, IMG_ARC_EDGED | IMG_ARC_NOFILL
)
);
} else {
$this->call(
'imageFilledArc',
array(
$this->_im,
$x, $y, $r * 2, $r * 2,
$start, $end,
$c, IMG_ARC_PIE
)
);
}
}
}
/**
* Creates an image of the given size.
*
* If possible the function returns a true color image.
*
* @param integer $width The image width.
* @param integer $height The image height.
*
* @return resource The image handler.
* @throws Horde_Image_Exception
*/
public function create($width, $height)
{
$result = $this->call('imageCreateTrueColor', array($width, $height));
if (!is_resource($result)) {
throw new Horde_Image_Exception('Could not create image.');
}
$this->call('imagesavealpha', array($result, true));
$this->call('imagealphablending', array($result, false));
if (function_exists('imageantialias')) {
$this->call('imageantialias', array($result, true));
}
return $result;
}
/**
* Wraps a call to a function of the gd extension.
*
* @param string $function The name of the function to wrap.
* @param array $params An array with all parameters for that function.
*
* @return mixed The result of the function call.
* @throws Horde_Image_Exception
*/
public function call($function, $params = null)
{
unset($php_errormsg);
$track = ini_set('track_errors', 1);
$result = @call_user_func_array($function, $params);
if ($track !== false) {
ini_set('track_errors', $track);
}
if (!empty($php_errormsg)) {
$error_msg = $php_errormsg;
throw new Horde_Image_Exception($error_msg);
}
return $result;
}
/**
* Applies the specified mask to the image.
*
* @param resource $mask The gd image resource representing the mask
*/
public function applyMask($mask)
{
$imgX = round($this->call('imageSX', array($this->_im)));
$imgY = round($this->call('imageSY', array($this->_im)));
$mask_resized = $this->create($imgX, $imgY);
$this->call(
'imageCopyResampled',
array(
$mask_resized, $mask,
0, 0, 0, 0,
$imgX, $imgY,
$this->call('imageSX', array($mask)),
$this->call('imageSY', array($mask))
)
);
$mask_blendtemp = $this->create($imgX, $imgY);
$mbtX = $this->call('imageSX', array($mask_blendtemp));
$mbtY = $this->call('imageSY', array($mask_blendtemp));
$color_background = $this->call(
'imageColorAllocate',
array($mask_blendtemp, 0, 0, 0)
);
$this->call(
'imageFilledRectangle',
array($mask_blendtemp, 0, 0, $mbtX, $mbtY, $color_background)
);
$this->call('imageAlphaBlending', array($mask_blendtemp, false));
$this->call('imageSaveAlpha', array($mask_blendtemp, true));
for ($x = 0; $x < $imgX; $x++) {
for ($y = 0; $y < $imgY; $y++) {
$realPixel = $this->call(
'imageColorsForIndex',
array(
$this->_im,
$this->call('imageColorAt', array($this->_im, $x, $y))
)
);
$maskPixel = Horde_Image::grayscalePixel(
$this->call(
'imageColorsForIndex',
array(
$mask_resized,
$this->call(
'imageColorAt',
array($mask_resized, $x, $y)
)
)
)
);
$maskAlpha = 127
- floor($maskPixel['red'] / 2)
* (1 - ($realPixel['alpha'] / 127));
$newcolor = $this->call(
'imageColorAllocateAlpha',
array(
$mask_blendtemp,
$realPixel['red'],
$realPixel['green'],
$realPixel['blue'],
intval($maskAlpha)
)
);
$this->call(
'imageSetPixel',
array($mask_blendtemp, $x, $y, $newcolor)
);
}
}
$this->call('imageAlphaBlending', array($this->_im, false));
$this->call('imageSaveAlpha', array($this->_im, true));
$this->call(
'imageCopy',
array($this->_im, $mask_blendtemp, 0, 0, 0, 0, $mbtX, $mbtY)
);
$this->call('imageDestroy', array($mask_blendtemp));
$this->call('imageDestroy', array($mask_resized));
}
}