0' );
}
if ( !is_int( $height ) || $height < 1 )
{
throw new ezcBaseValueException( 'height', $height, 'int > 0' );
}
$resource = $this->getActiveResource();
$oldDim = array( 'x' => imagesx( $resource ), 'y' => imagesy( $resource ) );
$widthRatio = $width / $oldDim['x'];
$heighRatio = $height / $oldDim['y'];
$ratio = min( $widthRatio, $heighRatio );
switch ( $direction )
{
case self::SCALE_DOWN:
$ratio = $ratio < 1 ? $ratio : 1;
break;
case self::SCALE_UP:
$ratio = $ratio > 1 ? $ratio : 1;
break;
case self::SCALE_BOTH:
break;
default:
throw new ezcBaseValueException( 'direction', $direction, 'self::SCALE_BOTH, self::SCALE_UP, self::SCALE_DOWN' );
break;
}
$newDim = array(
'x' => round( $oldDim['x'] * $ratio ),
'y' => round( $oldDim['y'] * $ratio ),
);
$this->performScale( $newDim );
}
/**
* Scale after width filter.
* Scales the image to a give width, measured in pixel. Scales the height
* automatically while keeping the ratio. The direction dictates, if an
* image may only be scaled {@link self::SCALE_UP}, {@link self::SCALE_DOWN}
* or if the scale may work in {@link self::SCALE_BOTH} directions.
*
* ATTENTION: Using this filter method directly results in the filter being
* applied to the image which is internally marked as "active" (most
* commonly this is the last recently loaded one). It is highly recommended
* to apply filters through the {@link ezcImageGdHandler::applyFilter()} method,
* which enables you to specify the image a filter is applied to.
*
* @param int $width Scale to width
* @param int $direction Scale to which direction
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
*/
public function scaleWidth( $width, $direction )
{
if ( !is_int( $width ) || $width < 1 )
{
throw new ezcBaseValueException( 'width', $width, 'int > 0' );
}
$resource = $this->getActiveResource();
$oldDim = array(
'x' => imagesx( $resource ),
'y' => imagesy( $resource ),
);
switch ( $direction )
{
case self::SCALE_BOTH:
$newDim = array(
'x' => $width,
'y' => $width / $oldDim['x'] * $oldDim['y']
);
break;
case self::SCALE_UP:
$newDim = array(
'x' => max( $width, $oldDim['x'] ),
'y' => $width > $oldDim['x'] ? round( $width / $oldDim['x'] * $oldDim['y'] ) : $oldDim['y'],
);
break;
case self::SCALE_DOWN:
$newDim = array(
'x' => min( $width, $oldDim['x'] ),
'y' => $width < $oldDim['x'] ? round( $width / $oldDim['x'] * $oldDim['y'] ) : $oldDim['y'],
);
break;
default:
throw new ezcBaseValueException( 'direction', $direction, 'self::SCALE_BOTH, self::SCALE_UP, self::SCALE_DOWN' );
break;
}
$this->performScale( $newDim );
}
/**
* Scale after height filter.
* Scales the image to a give height, measured in pixel. Scales the width
* automatically while keeping the ratio. The direction dictates, if an
* image may only be scaled {@link self::SCALE_UP}, {@link self::SCALE_DOWN}
* or if the scale may work in {@link self::SCALE_BOTH} directions.
*
* ATTENTION: Using this filter method directly results in the filter being
* applied to the image which is internally marked as "active" (most
* commonly this is the last recently loaded one). It is highly recommended
* to apply filters through the {@link ezcImageGdHandler::applyFilter()} method,
* which enables you to specify the image a filter is applied to.
*
* @param int $height Scale to height
* @param int $direction Scale to which direction
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
*/
public function scaleHeight( $height, $direction )
{
if ( !is_int( $height ) || $height < 1 )
{
throw new ezcBaseValueException( 'height', $height, 'int > 0' );
}
$resource = $this->getActiveResource();
$oldDim = array(
'x' => imagesx( $resource ),
'y' => imagesy( $resource ),
);
switch ( $direction )
{
case self::SCALE_BOTH:
$newDim = array(
'x' => $height / $oldDim['y'] * $oldDim['x'],
'y' => $height,
);
break;
case self::SCALE_UP:
$newDim = array(
'x' => $height > $oldDim['y'] ? round( $height / $oldDim['y'] * $oldDim['x'] ) : $oldDim['x'],
'y' => max( $height, $oldDim['y'] ),
);
break;
case self::SCALE_DOWN:
$newDim = array(
'x' => $height < $oldDim['y'] ? round( $height / $oldDim['y'] * $oldDim['x'] ) : $oldDim['x'],
'y' => min( $height, $oldDim['y'] ),
);
break;
default:
throw new ezcBaseValueException( 'direction', $direction, 'self::SCALE_BOTH, self::SCALE_UP, self::SCALE_DOWN' );
break;
}
$this->performScale( $newDim );
}
/**
* Scale percent measures filter.
* Scale an image to a given percentage value size.
*
* ATTENTION: Using this filter method directly results in the filter being
* applied to the image which is internally marked as "active" (most
* commonly this is the last recently loaded one). It is highly recommended
* to apply filters through the {@link ezcImageGdHandler::applyFilter()} method,
* which enables you to specify the image a filter is applied to.
*
* @param int $width Scale to width
* @param int $height Scale to height
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
*/
public function scalePercent( $width, $height )
{
if ( !is_int( $height ) || $height < 1 )
{
throw new ezcBaseValueException( 'height', $height, 'int > 0' );
}
if ( !is_int( $width ) || $width < 1 )
{
throw new ezcBaseValueException( 'width', $width, 'int > 0' );
}
$resource = $this->getActiveResource();
$newDim = array(
'x' => round( imagesx( $resource ) * $width / 100 ),
'y' => round( imagesy( $resource ) * $height / 100 ),
);
$this->performScale( $newDim );
}
/**
* Scale exact filter.
* Scale the image to a fixed given pixel size, no matter to which
* direction.
*
* ATTENTION: Using this filter method directly results in the filter being
* applied to the image which is internally marked as "active" (most
* commonly this is the last recently loaded one). It is highly recommended
* to apply filters through the {@link ezcImageGdHandler::applyFilter()} method,
* which enables you to specify the image a filter is applied to.
*
* @param int $width Scale to width
* @param int $height Scale to height
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
*/
public function scaleExact( $width, $height )
{
if ( !is_int( $height ) || $height < 1 )
{
throw new ezcBaseValueException( 'height', $height, 'int > 0' );
}
if ( !is_int( $width ) || $width < 1 )
{
throw new ezcBaseValueException( 'width', $width, 'int > 0' );
}
$this->performScale( array( 'x' => $width, 'y' => $height ) );
}
/**
* Crop filter.
* Crop an image to a given size. This takes cartesian coordinates of a
* rect area to crop from the image. The cropped area will replace the old
* image resource (not the input image immediately, if you use the
* {@link ezcImageConverter}). Coordinates are given as integer values and
* are measured from the top left corner.
*
* ATTENTION: Using this filter method directly results in the filter being
* applied to the image which is internally marked as "active" (most
* commonly this is the last recently loaded one). It is highly recommended
* to apply filters through the {@link ezcImageGdHandler::applyFilter()} method,
* which enables you to specify the image a filter is applied to.
*
* @param int $x X offset of the cropping area.
* @param int $y Y offset of the cropping area.
* @param int $width Width of cropping area.
* @param int $height Height of cropping area.
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
*/
public function crop( $x, $y, $width, $height )
{
if ( !is_int( $x ) || $x < 0 )
{
throw new ezcBaseValueException( 'x', $x, 'int >= 0' );
}
if ( !is_int( $y ) || $y < 0 )
{
throw new ezcBaseValueException( 'y', $y, 'int >= 0' );
}
if ( !is_int( $height ) )
{
throw new ezcBaseValueException( 'height', $height, 'int' );
}
if ( !is_int( $width ) )
{
throw new ezcBaseValueException( 'width', $width, 'int' );
}
$x = min( $x, $x + $width );
$y = min( $y, $y + $height );
$width = abs( $width );
$height = abs( $height );
$oldResource = $this->getActiveResource();
$sourceWidth = imagesx( $oldResource );
$sourceHeight = imagesy( $oldResource );
if ( $x + $width > $sourceWidth )
{
$width = $sourceWidth - $x;
}
if ( $y + $height > $sourceHeight )
{
$height = $sourceHeight - $y;
}
if ( imageistruecolor( $oldResource ) )
{
$newResource = imagecreatetruecolor( $width, $height );
}
else
{
$newResource = imagecreate( $width, $height );
}
// Save transparency, if image has it
$bgColor = imagecolorallocatealpha( $newResource, 255, 255, 255, 127 );
imagealphablending( $newResource, true );
imagesavealpha( $newResource, true );
imagefill( $newResource, 1, 1, $bgColor );
$res = imagecopyresampled(
$newResource, // destination resource
$oldResource, // source resource
0, // destination x coord
0, // destination y coord
$x, // source x coord
$y, // source y coord
$width, // destination width
$height, // destination height
$width, // source witdh
$height // source height
);
if ( $res === false )
{
throw new ezcImageFilterFailedException( 'crop', 'Resampling of image failed.' );
}
imagedestroy( $oldResource );
$this->setActiveResource( $newResource );
}
/**
* Colorspace filter.
* Transform the colorspace of the picture. The following colorspaces are
* supported:
*
* - {@link self::COLORSPACE_GREY} - 255 grey colors
* - {@link self::COLORSPACE_SEPIA} - Sepia colors
* - {@link self::COLORSPACE_MONOCHROME} - 2 colors black and white
*
* ATTENTION: Using this filter method directly results in the filter being
* applied to the image which is internally marked as "active" (most
* commonly this is the last recently loaded one). It is highly recommended
* to apply filters through the {@link ezcImageGdHandler::applyFilter()} method,
* which enables you to specify the image a filter is applied to.
*
* @param int $space Colorspace, one of self::COLORSPACE_* constants.
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcBaseValueException
* If the parameter submitted as the colorspace was not within the
* self::COLORSPACE_* constants.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
*/
public function colorspace( $space )
{
switch ( $space )
{
case self::COLORSPACE_GREY:
$this->luminanceColorScale( array( 1.0, 1.0, 1.0 ) );
break;
case self::COLORSPACE_MONOCHROME:
$this->thresholdColorScale(
array(
127 => array( 0, 0, 0 ),
255 => array( 255, 255, 255 ),
)
);
break;
return;
case self::COLORSPACE_SEPIA:
$this->luminanceColorScale( array( 1.0, 0.89, 0.74 ) );
break;
default:
throw new ezcBaseValueException( 'space', $space, 'self::COLORSPACE_GREY, self::COLORSPACE_SEPIA, self::COLORSPACE_MONOCHROME' );
break;
}
}
/**
* Watermark filter.
* Places a watermark on the image. The file to use as the watermark image
* is given as $image. The $posX, $posY and $size values are given in
* percent, related to the destination image. A $size value of 10 will make
* the watermark appear in 10% of the destination image size.
* $posX = $posY = 10 will make the watermark appear in the top left corner
* of the destination image, 10% of its size away from its borders. If
* $size is ommitted, the watermark image will not be resized.
*
* @param string $image The image file to use as the watermark
* @param int $posX X position in the destination image in percent.
* @param int $posY Y position in the destination image in percent.
* @param int $size Percentage size of the watermark.
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
*/
public function watermarkPercent( $image, $posX, $posY, $size = false )
{
if ( !is_string( $image ) || !file_exists( $image ) || !is_readable( $image ) )
{
throw new ezcBaseValueException( 'image', $image, 'string, path to an image file' );
}
if ( !is_int( $posX ) || $posX < 0 || $posX > 100 )
{
throw new ezcBaseValueException( 'posX', $posX, 'int percentage value' );
}
if ( !is_int( $posY ) || $posY < 0 || $posY > 100 )
{
throw new ezcBaseValueException( 'posY', $posY, 'int percentage value' );
}
if ( !is_bool( $size ) && ( !is_int( $size ) || $size < 0 || $size > 100 ) )
{
throw new ezcBaseValueException( 'size', $size, 'int percentage value / bool' );
}
$imgWidth = imagesx( $this->getActiveResource() );
$imgHeight = imagesy( $this->getActiveResource() );
$watermarkWidth = false;
$watermarkHeight = false;
if ( $size !== false )
{
$watermarkWidth = (int) round( $imgWidth * $size / 100 );
$watermarkHeight = (int) round( $imgHeight * $size / 100 );
}
$watermarkPosX = (int) round( $imgWidth * $posX / 100 );
$watermarkPosY = (int) round( $imgWidth * $posY / 100 );
$this->watermarkAbsolute( $image, $watermarkPosX, $watermarkPosY, $watermarkWidth, $watermarkHeight );
}
/**
* Watermark filter.
* Places a watermark on the image. The file to use as the watermark image
* is given as $image. The $posX, $posY and $size values are given in
* pixel. The watermark appear at $posX, $posY in the destination image
* with a size of $size pixel. If $size is ommitted, the watermark image
* will not be resized.
*
* @param string $image The image file to use as the watermark
* @param int $posX X position in the destination image in pixel.
* @param int $posY Y position in the destination image in pixel.
* @param int $size Pixel size of the watermark.
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
* @throws ezcBaseValueException
* If a submitted parameter was out of range or type.
* @throws ezcImageFileNotProcessableException
* If the given watermark image could not be loaded.
*/
public function watermarkAbsolute( $image, $posX, $posY, $width = false, $height = false )
{
if ( !is_string( $image ) || !file_exists( $image ) || !is_readable( $image ) )
{
throw new ezcBaseValueException( 'image', $image, 'string, path to an image file' );
}
if ( !is_int( $posX ) )
{
throw new ezcBaseValueException( 'posX', $posX, 'int' );
}
if ( !is_int( $posY ) )
{
throw new ezcBaseValueException( 'posY', $posY, 'int' );
}
if ( !is_int( $width ) && !is_bool( $width ) )
{
throw new ezcBaseValueException( 'width', $width, 'int/bool' );
}
if ( !is_int( $height ) && !is_bool( $height ) )
{
throw new ezcBaseValueException( 'height', $height, 'int/bool' );
}
// Backup original image reference
$originalRef = $this->getActiveReference();
$watermarkRef = $this->load( $image );
if ( $width !== false && $height !== false && ( imagesx( $this->getActiveResource() ) !== $width || imagesy( $this->getActiveResource() ) !== $height ) )
{
$this->scale( $width, $height, ezcImageGeometryFilters::SCALE_BOTH );
}
imagecopy(
$this->getReferenceData( $originalRef, "resource" ), // resource $dst_im
$this->getReferenceData( $watermarkRef, "resource" ), // resource $src_im
$posX, // int $dst_x
$posY, // int $dst_y
0, // int $src_x
0, // int $src_y
imagesx( $this->getReferenceData( $watermarkRef, "resource" ) ), // int $src_w
imagesy( $this->getReferenceData( $watermarkRef, "resource" ) ) // int $src_h
);
$this->close( $watermarkRef );
// Restore original image reference
$this->setActiveReference( $originalRef );
}
// private
/**
* Retrieve luminance value for a specific pixel.
*
* @param resource(GD) $resource Image resource
* @param int $x Pixel x coordinate.
* @param int $y Pixel y coordinate.
* @return float Luminance value.
*/
private function getLuminanceAt( $resource, $x, $y )
{
$currentColor = imagecolorat( $resource, $x, $y );
$rgbValues = array(
'r' => ( $currentColor >> 16 ) & 0xff,
'g' => ( $currentColor >> 8 ) & 0xff,
'b' => $currentColor & 0xff,
);
return $rgbValues['r'] * 0.299 + $rgbValues['g'] * 0.587 + $rgbValues['b'] * 0.114;
}
/**
* Scale colors by threshold values.
* Thresholds are defined by the following array structures:
*
*
* array(
* => array(
* 0 => ,
* 1 => ,
* 2 => ,
* ),
* )
*
*
* @param array $thresholds
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
*
* @todo Optimization as described here: http://lists.ez.no/pipermail/components/2005-November/000566.html
*/
protected function thresholdColorScale( $thresholds )
{
$resource = $this->getActiveResource();
$dimensions = array( 'x' => imagesx( $resource ), 'y' => imagesy( $resource ) );
// Check for GIFs and convert them to work properly here.
if ( !imageistruecolor( $resource ) )
{
$resource = $this->paletteToTruecolor( $resource, $dimensions );
}
foreach ( $thresholds as $threshold => $colors )
{
$thresholds[$threshold] = array_merge(
$colors,
array( 'color' => $this->getColor( $resource, $colors[0], $colors[1], $colors[2] ) )
);
}
// Default
if ( !isset( $thresholds[255] ) )
{
$thresholds[255] = end( $thresholds );
reset( $thresholds );
}
$colorCache = array();
for ( $x = 0; $x < $dimensions['x']; $x++ )
{
for ( $y = 0; $y < $dimensions['y']; $y++ )
{
$luminance = $this->getLuminanceAt( $resource, $x, $y );
$color = end( $thresholds );
foreach ( $thresholds as $threshold => $colorValues )
{
if ( $luminance <= $threshold )
{
$color = $colorValues;
break;
}
}
imagesetpixel( $resource, $x, $y, $color['color'] );
}
}
$this->setActiveResource( $resource );
}
/**
* Perform luminance color scale.
*
* @param array $scale Array of RGB values (numeric index).
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed
*
* @todo Optimization as described here: http://lists.ez.no/pipermail/components/2005-November/000566.html
*/
protected function luminanceColorScale( $scale )
{
$resource = $this->getActiveResource();
$dimensions = array( 'x' => imagesx( $resource ), 'y' => imagesy( $resource ) );
// Check for GIFs and convert them to work properly here.
if ( !imageistruecolor( $resource ) )
{
$resource = $this->paletteToTruecolor( $resource, $dimensions );
}
for ( $x = 0; $x < $dimensions['x']; $x++ )
{
for ( $y = 0; $y < $dimensions['y']; $y++ )
{
$luminance = $this->getLuminanceAt( $resource, $x, $y );
$newRgbValues = array(
'r' => $luminance * $scale[0],
'g' => $luminance * $scale[1],
'b' => $luminance * $scale[2],
);
$color = $this->getColor( $resource, $newRgbValues['r'], $newRgbValues['g'], $newRgbValues['b'] );
imagesetpixel( $resource, $x, $y, $color );
}
}
$this->setActiveResource( $resource );
}
/**
* Convert a palette based image resource to a true color one.
* Takes a GD resource that does not represent a true color image and
* converts it to a true color based resource. Do not forget, to replace
* the actual resource in the handler, if you use this ,method!
*
* @param resource(GD) $resource The image resource to convert
* @return resource(GD) The converted resource.
*/
protected function paletteToTruecolor( $resource, $dimensions )
{
$newResource = imagecreatetruecolor( $dimensions['x'], $dimensions['y'] );
imagecopy( $newResource, $resource, 0, 0, 0, 0, $dimensions['x'], $dimensions['y'] );
imagedestroy( $resource );
return $newResource;
}
/**
* Common color determination method.
* Returns a color identifier for an RGB value. Avoids problems with palette images.
*
* @param reource(GD) $resource The image resource to get a color for.
* @param int $r Red value.
* @param int $g Green value.
* @param int $b Blue value.
*
* @return int The color identifier.
*
* @throws ezcImageFilterFailedException
* If the operation performed by the the filter failed.
*/
protected function getColor( $resource, $r, $g, $b )
{
if ( ( $res = imagecolorexact( $resource, $r, $g, $b ) ) !== -1 )
{
return $res;
}
if ( ( $res = imagecolorallocate( $resource, $r, $g, $b ) ) !== -1 )
{
return $res;
}
if ( ( $res = imagecolorclosest( $resource, $r, $g, $b ) ) !== -1 )
{
return $res;
}
throw new ezcImageFilterFailedException( 'colorspace', "Color allocation failed for color r: '{$r}', g: '{$g}', b: '{$b}'." );
}
/**
* General scaling method to perform actual scale to new dimensions.
*
* @param array $dimensions Dimensions as array('x' => , 'y' => ).
* @return void
*
* @throws ezcImageInvalidReferenceException
* If no valid resource for the active reference could be found.
* @throws ezcImageFilterFailedException.
* If the operation performed by the the filter failed.
*/
protected function performScale( $dimensions )
{
$oldResource = $this->getActiveResource();
if ( imageistruecolor( $oldResource ) )
{
$newResource = imagecreatetruecolor( $dimensions['x'], $dimensions['y'] );
}
else
{
$newResource = imagecreate( $dimensions['x'], $dimensions['y'] );
}
// Save transparency, if image has it
$bgColor = imagecolorallocatealpha( $newResource, 255, 255, 255, 127 );
imagealphablending( $newResource, true );
imagesavealpha( $newResource, true );
imagefill( $newResource, 1, 1, $bgColor );
$res = imagecopyresampled(
$newResource,
$oldResource,
0, 0, 0, 0,
$dimensions['x'],
$dimensions['y'],
imagesx( $this->getActiveResource() ),
imagesy( $this->getActiveResource() )
);
if ( $res === false )
{
throw new ezcImageFilterFailedException( 'scale', 'Resampling of image failed.' );
}
imagedestroy( $oldResource );
$this->setActiveResource( $newResource );
}
}
?>