<?php
/*
* Gallery - a web based photo album viewer and editor
* Copyright (C) 2000-2008 Bharat Mediratta
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
GalleryCoreApi::requireOnce('modules/core/classes/GalleryToolkit.class');
GalleryCoreApi::requireOnce('modules/gd/classes/GdToolkitHelper.class');
/**
* A Gd version of GalleryToolkit
* @package Gd
* @subpackage Classes
* @author Ernesto Baschny <ernst@baschny.de>
* @version $Revision: 20960 $
*/
class GdToolkit extends GalleryToolkit {
/**
* Our private GdFunctionality instance
* @var $_gdFunctionality
* @access private
*/
var $_gdFunctionality;
/**
* Does our GD library has a working imageCreateTrueColor?
* @access private
*/
var $_hasImageCreateTrueColor;
/**
* The color used to replace transparent areas is the output format doesn't support
* transparency. An RGB triple.
*
* @access private
*/
var $_transparencyReplacementColor = array(255, 255, 255);
/**
* Return the current set gdFunctionality.
* Default is GdFunctionality.class if no other is set.
*
* @return object implementation of GdFunctionality
* @access private
*/
function &_getGdFunctionality() {
if (!isset($this->_gdFunctionality)) {
GalleryCoreApi::requireOnce('modules/gd/classes/GdFunctionality.class');
$gd = new GdFunctionality();
$this->setGdFunctionality($gd);
}
return $this->_gdFunctionality;
}
/**
* Set a different GdFunctionality object to be used by this Toolkit.
* This is useful on our phpunit tests, which will make this Toolkit
* use a pseudo-gd-implementation that simulates different PHP versions.
* @return nothing
*/
function setGdFunctionality(&$obj) {
$this->_gdFunctionality =& $obj;
unset($this->_hasImageCreateTrueColor);
}
/**
* Do we have a working version of imageCreateTrueColor()?
*
* @return array GalleryStatus
* boolean true if this function should work
* @access private
*/
function _hasImageCreateTrueColor() {
if (!isset($this->_hasImageCreateTrueColor)) {
$gd =& $this->_getGdFunctionality();
/* Remember the info for later calls */
list ($ret, $this->_hasImageCreateTrueColor) =
GdToolkitHelper::hasImageCreateTrueColor($gd);
if ($ret) {
return array($ret, null);
}
}
return array(null, $this->_hasImageCreateTrueColor);
}
/**
* @see GalleryToolkit::getProperty
*/
function getProperty($mimeType, $propertyName, $sourceFilename) {
switch($propertyName) {
case 'dimensions':
list ($ret, $width, $height) = $this->_getImageDimensions($mimeType, $sourceFilename);
if ($ret) {
return array($ret, null);
}
$results = array((int)$width, (int)$height);
break;
default:
return array(GalleryCoreApi::error(ERROR_UNIMPLEMENTED), null);
}
return array(null, $results);
}
/**
* @see GalleryToolkit::performOperation
*/
function performOperation($mimeType, $operationName, $sourceFilename,
$destFilename, $parameters, $context=array()) {
$gd =& $this->_getGdFunctionality();
global $gallery;
if ($gallery->getDebug()) {
$gallery->debug(sprintf('GdToolkit::performOperation(%s,%s,%s,%s,%s)',
$mimeType, $operationName, $sourceFilename,
$destFilename, join('|', $parameters)));
}
$outputMimeType = $mimeType;
list ($ret, $sourceRes) = $this->_getImageResource($mimeType, $sourceFilename);
if ($ret) {
return array($ret, null, null);
}
$hasTransparency = $this->_canHaveTransparency($mimeType, $sourceRes);
if (in_array($operationName, array('thumbnail', 'scale', 'resize'))) {
$usePercent = substr($parameters[0], -1) == '%'
|| (isset($parameters[1]) && substr($parameters[1], -1) == '%');
if ($usePercent || $operationName != 'resize') {
list ($ret, $width, $height) = $this->_getImageDimensionsForResource($sourceRes);
if ($ret) {
$this->_free($sourceRes);
return array($ret, null, null);
}
}
if ($usePercent) {
/* Convert percentages to real image dimensions */
if (substr($parameters[0], -1) == '%') {
$parameters[0] = (int)round($width * rtrim($parameters[0], '%') / 100);
}
if (isset($parameters[1]) && substr($parameters[1], -1) == '%') {
$parameters[1] = (int)round($height * rtrim($parameters[1], '%') / 100);
}
}
}
switch($operationName) {
case 'thumbnail':
case 'scale':
/* $parameters[0]: target width, [1]: optional target height */
$targetHeight = empty($parameters[1]) ? $parameters[0] : $parameters[1];
/* Don't enlarge images for a thumbnail */
if ($operationName == 'thumbnail'
&& $width <= $parameters[0] && $height <= $targetHeight) {
break;
}
list ($destWidth, $destHeight) = GalleryUtilities::scaleDimensionsToFit(
$width, $height, $parameters[0], $targetHeight);
list ($ret, $destRes) =
$this->_resizeImageResource($sourceRes, $destWidth, $destHeight, $hasTransparency);
$this->_free($sourceRes);
if ($ret) {
return array($ret, null, null);
}
if (isset($context['width'])) {
$context['width'] = $destWidth;
$context['height'] = $destHeight;
}
break;
case 'resize':
/*
* $parameters[0]: target width
* $parameters[1]: target height
*/
list ($ret, $destRes) = $this->_resizeImageResource(
$sourceRes, $parameters[0], $parameters[1], $hasTransparency);
$this->_free($sourceRes);
if ($ret) {
return array($ret, null, null);
}
if (isset($context['width'])) {
$context['width'] = $parameters[0];
$context['height'] = $parameters[1];
}
break;
case 'rotate':
/*
* PHP >= 4.3.0
* $parameters[0]: rotation degrees
*/
$degrees = 0 - $parameters[0];
list ($ret, $destRes) = $gd->imageRotate($sourceRes, $degrees, 0);
$this->_free($sourceRes);
if ($ret) {
return array($ret, null, null);
}
if (isset($context['width']) && ($degrees == 90 || $degrees == -90)) {
$tmp = $context['width'];
$context['width'] = $context['height'];
$context['height'] = $tmp;
}
break;
case 'crop':
/*
* $parameters[0]: left edge %
* $parameters[1]: top edge %
* $parameters[2]: width %
* $parameters[3]: height %
*/
/* source dimensions are required to convert from percentages to pixels */
list ($ret, $width, $height) = $this->_getImageDimensionsForResource($sourceRes);
if ($ret) {
$this->_free($sourceRes);
return array($ret, null, null);
}
$pixelX = round($parameters[0] / 100 * $width);
$pixelY = round($parameters[1] / 100 * $height);
$pixelWidth = round($parameters[2] / 100 * $width);
$pixelHeight = round($parameters[3] / 100 * $height);
list ($ret, $destRes) =
$this->_getTrueColorImageRes($pixelWidth, $pixelHeight, $hasTransparency);
if ($ret) {
$this->_free($sourceRes);
return array($ret, null, null);
}
$ret = $gd->imageCopy($destRes, $sourceRes,
0, 0, /* dst x,y */
$pixelX, $pixelY, $pixelWidth, $pixelHeight);
$this->_free($sourceRes);
if ($ret) {
$this->_free($destRes);
return array($ret, null, null);
}
if (isset($context['width'])) {
$context['width'] = $pixelWidth;
$context['height'] = $pixelHeight;
}
break;
case 'convert-to-image/jpeg':
$destRes = $sourceRes;
$outputMimeType = 'image/jpeg';
break;
case 'composite':
/*
* $parameters[0]: overlay path
* $parameters[1]: overlay mime type
* $parameters[2]: overlay width
* $parameters[3]: overlay height
* $parameters[6]: alignment type
* $parameters[4]: alignment x %
* $parameters[5]: alignment y %
*/
$cmd = 'composite';
$compositeOverlayPath = $parameters[0];
$compositeOverlayMimeType = $parameters[1];
$compositeWidth = $parameters[2];
$compositeHeight = $parameters[3];
$compositeAlignmentType = $parameters[4];
$compositeAlignX = $parameters[5];
$compositeAlignY = $parameters[6];
list ($ret, $sourceWidth, $sourceHeight) =
$this->_getImageDimensionsForResource($sourceRes);
if ($ret) {
$this->_free($sourceRes);
return array($ret, null, null);
}
switch ($compositeAlignmentType) {
case 'top-left':
$compositeAlignX = 0;
$compositeAlignY = 0;
break;
case 'top':
$compositeAlignX = 50;
$compositeAlignY = 0;
break;
case 'top-right':
$compositeAlignX = 100;
$compositeAlignY = 0;
break;
case 'left':
$compositeAlignX = 0;
$compositeAlignY = 50;
break;
case 'center':
$compositeAlignX = 50;
$compositeAlignY = 50;
break;
case 'right':
$compositeAlignX = 100;
$compositeAlignY = 50;
break;
case 'bottom-left':
$compositeAlignX = 0;
$compositeAlignY = 100;
break;
case 'bottom':
$compositeAlignX = 50;
$compositeAlignY = 100;
break;
case 'bottom-right':
$compositeAlignX = 100;
$compositeAlignY = 100;
break;
case 'manual':
/* Use the alignments we received */
break;
default:
$this->_free($sourceRes);
return array(GalleryCoreApi::error(ERROR_BAD_PARAMETER, __FILE__, __LINE__,
"Unknown composite alignment type: $compositeAlignmentType"),
null, null);
}
/* Convert from percentages to pixels */
$compositeAlignX = (int)($compositeAlignX / 100 * ($sourceWidth - $compositeWidth));
$compositeAlignY = (int)($compositeAlignY / 100 * ($sourceHeight - $compositeHeight));
/* Clip to our bounding box */
$compositeAlignX = min($compositeAlignX, $sourceWidth - $compositeWidth);
$compositeAlignX = max(0, $compositeAlignX);
$compositeAlignY = min($compositeAlignY, $sourceHeight - $compositeHeight);
$compositeAlignY = max(0, $compositeAlignY);
$dataDir = $gallery->getConfig('data.gallery.base');
list ($ret, $overlayRes) = $this->_getImageResource($compositeOverlayMimeType,
$dataDir . $compositeOverlayPath);
if ($ret) {
$this->_free($sourceRes);
return array($ret, null, null);
}
$destRes = $sourceRes;
/* Don't use imageCopyMerge here, as it will lose alpha-transparency */
$ret = $gd->imageCopy($destRes, $overlayRes,
$compositeAlignX, $compositeAlignY,
0, 0,
$compositeWidth, $compositeHeight);
$this->_free($overlayRes);
if ($ret) {
return array($ret, null, null);
}
$hasTransparency = true;
break;
case 'compress':
$targetSize = $parameters[0];
$fileSize = $gd->filesize($sourceFilename) >> 10; /* Size in KB */
if ($fileSize <= $targetSize) {
break;
}
/* Use module quality parameter as initial guess */
list ($ret, $quality) =
GalleryCoreApi::getPluginParameter('module', 'gd', 'jpegQuality');
if ($ret) {
$this->_free($sourceRes);
return array($ret, null, null);
}
$maxQuality = 100;
$minQuality = 5;
do {
/* _saveImageResourceToFile changes the resource. Get a fresh one. */
$this->_free($sourceRes);
list ($ret, $sourceRes) = $this->_getImageResource($mimeType, $sourceFilename);
if ($ret) {
return array($ret, null, null);
}
$ret = $this->_saveImageResourceToFile($sourceRes, $destFilename, $outputMimeType,
$hasTransparency, $quality);
if ($ret) {
$this->_free($sourceRes);
return array($ret, null, null);
}
clearstatcache();
$fileSize = $gd->filesize($destFilename) >> 10;
if ($fileSize >= $targetSize) {
$maxQuality = $quality;
}
if ($fileSize <= $targetSize) {
$minQuality = $quality;
}
$quality = round(($minQuality + $maxQuality) / 2);
} while ($maxQuality - $minQuality > 2
&& abs(($fileSize - $targetSize) / $targetSize) > 0.02);
$this->_free($sourceRes);
return array(null, $outputMimeType, $context);
default:
$this->_free($sourceRes);
return array(GalleryCoreApi::error(ERROR_UNSUPPORTED_OPERATION, __FILE__, __LINE__,
"$operationName $mimeType"), null, null);
}
if (isset($destRes)) {
$ret = $this->_saveImageResourceToFile(
$destRes, $destFilename, $outputMimeType, $hasTransparency);
$this->_free($destRes);
if ($ret) {
return array($ret, null, null);
}
} else {
/* Just copy the source to the destination */
if ($sourceFilename != $destFilename) {
if (!$gd->copy($sourceFilename, $destFilename)) {
$this->_free($sourceRes);
return array(GalleryCoreApi::error(ERROR_PLATFORM_FAILURE), null, null);
}
}
}
return array(null, $outputMimeType, $context);
}
/**
* Return a GD image resource for the given filename so we can perform other operations on it.
*
* @param string $mimeType the mime-type of the image
* @param string $filename the path to the file to read
* @return array GalleryStatus a status code
* resource the GD resource
* @access private
*/
function _getImageResource($mimeType, $filename) {
$gd =& $this->_getGdFunctionality();
$res = null;
switch ($mimeType) {
case 'image/gif':
list ($ret, $res) = $gd->imageCreateFromGif($filename);
break;
case 'image/jpeg':
list ($ret, $res) = $gd->imageCreateFromJpeg($filename);
break;
case 'image/png':
list ($ret, $res) = $gd->imageCreateFromPng($filename);
break;
case 'image/vnd.wap.wbmp':
list ($ret, $res) = $gd->imageCreateFromWbmp($filename);
break;
case 'image/x-xpixmap':
list ($ret, $res) = $gd->imageCreateFromXpm($filename);
break;
case 'image/x-xbitmap':
list ($ret, $res) = $gd->imageCreateFromXbm($filename);
break;
}
if ($ret) {
return array($ret, null);
}
if (!$res) {
return array(GalleryCoreApi::error(ERROR_TOOLKIT_FAILURE), null);
}
return array(null, $res);
}
/**
* Create a truecolor GD resource with the specified sizes.
*
* On GD >= 2.0.1 there is a function to do this directly. On earlier versions, we need to
* create a temporary JPEG image and create the image from that.
*
* @param int $width
* @param int $height
* @param bool $prepareAlpha (optional) whether the resource should be prepared to host any
* transparencies
* @return array GalleryStatus
* resource the GD resource
* @access private
*/
function _getTrueColorImageRes($width, $height, $prepareAlpha=false) {
$gd =& $this->_getGdFunctionality();
list ($ret, $hasImageCreateTrueColor) = $this->_hasImageCreateTrueColor();
if ($ret) {
return array($ret, null);
}
if ($hasImageCreateTrueColor) {
list ($ret, $res) = $gd->imageCreateTruecolor($width, $height);
if ($ret) {
return array($ret, null);
}
} else {
/* Do something else to get a truecolor GD resource */
global $gallery;
$tmpDir = $gallery->getConfig('data.gallery.tmp');
$tmpFile = $gd->tempnam($tmpDir, 'gd');
/* Create a temporary jpeg file ... */
list ($ret, $tmpRes) = $gd->imageCreate($width, $height);
if ($ret) {
return array($ret, null);
}
$ret = $gd->imageJpeg($tmpRes, $tmpFile);
if ($ret) {
return array($ret, null);
}
/* ... and load it back into a resource (now truecolor) */
list ($ret, $res) = $gd->imageCreateFromJpeg($tmpFile);
if ($ret) {
return array($ret, null);
}
$gd->unlink($tmpFile);
}
if (!$res) {
return array(GalleryCoreApi::error(ERROR_TOOLKIT_FAILURE), null);
}
if ($prepareAlpha) {
$this->_prepareResourceForAlphaChannel($res);
/* Ignore errors */
}
return array(null, $res);
}
/**
* Save a GD resource to a file with a certain mime-type
*
* @param resource $res
* @param string $filename the path to the file to read
* @param string $mimeType the mime-type of the image
* @param bool $hasTransparency (optional) set to false to prevent dithering
* @param string $jpegQuality (optional)
* @return GalleryStatus a status code
* @access private
*/
function _saveImageResourceToFile($res, $filename, $mimeType, $hasTransparency=true,
$jpegQuality=null) {
$gd =& $this->_getGdFunctionality();
if ($hasTransparency) {
$ret = $this->_flattenAlphaChannel($res, $mimeType);
if ($ret) {
return $ret;
}
}
switch ($mimeType) {
case 'image/gif':
$ret = $gd->imageGif($res, $filename);
break;
case 'image/jpeg':
if (!isset($jpegQuality)) {
list ($ret, $jpegQuality) =
GalleryCoreApi::getPluginParameter('module', 'gd', 'jpegQuality');
if ($ret) {
return $ret;
}
}
$ret = $gd->imageJpeg($res, $filename, $jpegQuality);
break;
case 'image/png':
$ret = $gd->imagePng($res, $filename);
break;
case 'image/vnd.wap.wbmp':
$ret = $gd->imageWbmp($res, $filename);
break;
case 'image/x-xpixmap':
$ret = $gd->imageXpm($res, $filename);
break;
case 'image/x-xbitmap':
$ret = $gd->imageXbm($res, $filename);
break;
}
if ($ret) {
return $ret;
}
$gd->chmod($filename);
return null;
}
/**
* Resizes an open GD-resource to the specified size.
*
* @param resource $sourceRes the source GD-resource
* @param int $destWidth the destination width
* @param int $destHeight the destination height
* @param bool $hasAlpha (optional) whether the source image has any transparency
* @return array GalleryStatus
* resource the destination GD-resource
* @access private
*/
function _resizeImageResource($sourceRes, $destWidth, $destHeight, $hasAlpha=false) {
$gd =& $this->_getGdFunctionality();
list ($ret, $sourceWidth) = $gd->imageSX($sourceRes);
if ($ret) {
return array($ret, null);
}
list ($ret, $sourceHeight) = $gd->imageSY($sourceRes);
if ($ret) {
return array($ret, null);
}
list ($ret, $destRes) = $this->_getTrueColorImageRes($destWidth, $destHeight, $hasAlpha);
if ($ret) {
return array($ret, null);
}
$result = false;
$ret = $this->_imageCopyResampled($destRes, $sourceRes,
0, 0, 0, 0, /* dst and src X,Y */
$destWidth, $destHeight, /* dst W,H */
$sourceWidth, $sourceHeight /* src W,H */
);
if ($ret) {
return array($ret, null);
}
return array(null, $destRes);
}
/**
* @see GalleryGraphics::getImageDimensions
* @access private
*/
function _getImageDimensions($mimeType, $filename) {
$gd =& $this->_getGdFunctionality();
/*
* Try PHPs getimagesize first. If it runs afoul of open_basedir it'll return false and we
* can try using the GD functions
*/
$results = $gd->getImageSize($filename);
if (($results != false) &&
(($results[0] > 1) && ($results[1] > 1))) {
return array(null, $results[0], $results[1]);
}
list ($ret, $res) = $this->_getImageResource($mimeType, $filename);
if ($ret) {
return array(GalleryCoreApi::error(ERROR_TOOLKIT_FAILURE), 0, 0);
}
list ($ret, $width, $height) = $this->_getImageDimensionsForResource($res);
$this->_free($res);
if ($ret) {
return array($ret, null, null);
}
return array(null, $width, $height);
}
/**
* Get the image dimentions for an already opened GD resource.
*
* @return array GalleryStatus
* int width
* int height
* @access private
*/
function _getImageDimensionsForResource($res) {
$gd =& $this->_getGdFunctionality();
list ($ret, $x) = $gd->imageSx($res);
if ($ret) {
return array($ret, null, null);
}
list ($ret, $y) = $gd->imageSy($res);
if ($ret) {
return array($ret, null, null);
}
if ($x > 0 && $y > 0) {
return array(null, $x, $y);
}
return array(GalleryCoreApi::error(ERROR_TOOLKIT_FAILURE), null, null);
}
/**
* Copy and resize part of an image with resampling or resizing.
*
* We will use GD's "resample" functionality and fallback to "resize"
* if "resample" is not available.
*
* @return GalleryStatus
* @access private
*/
function _imageCopyResampled($dstRes, $srcRes, $dstX, $dstY, $srcX, $srcY,
$dstW, $dstH, $srcW, $srcH) {
$gd =& $this->_getGdFunctionality();
if ($gd->functionExists('imageCopyResampled')) {
$ret = $gd->imageCopyResampled($dstRes, $srcRes, $dstX, $dstY, $srcX, $srcY,
$dstW, $dstH, $srcW, $srcH);
} else {
/* Fallback */
$ret = $gd->imageCopyResized($dstRes, $srcRes, $dstX, $dstY, $srcX, $srcY,
$dstW, $dstH, $srcW, $srcH);
}
if ($ret) {
return $ret;
}
return null;
}
/**
* Prepares a resource to handle alpha inherited from a source resource.
* Images that have transparency must have the background filled with the indexed color for
* transparency, otherwise the transparent color will appear as black.
*
* The code is based on phpthumb by James Heinrich <info@silisoftware.com>.
* This version only works for PHP >= 4.3.2
*
* @param resource $res
* @param int $width
* @param int $height
* @return GalleryStatus
*/
function _prepareResourceForAlphaChannel(&$res) {
$gd =& $this->_getGdFunctionality();
list ($ret, $success) = $gd->imagesavealpha($res, true);
if ($ret) {
return $ret;
}
if (!$success) {
return GalleryCoreApi::error(ERROR_TOOLKIT_FAILURE);
}
list ($ret, $width, $height) = $this->_getImageDimensionsForResource($res);
if ($ret) {
return $ret;
}
$success = $gd->imagealphablending($res, false);
if (!$success) {
return GalleryCoreApi::error(ERROR_TOOLKIT_FAILURE);
}
list ($ret, $alphaColor) = $gd->imagecolorallocatealpha($res, 255, 255, 255, 127);
if ($ret) {
return $ret;
}
$success = $gd->imagefilledrectangle($res, 0, 0, $width, $height, $alphaColor);
if (!$success) {
return GalleryCoreApi::error(ERROR_TOOLKIT_FAILURE);
}
return null;
}
/**
* Handle the resouce according to the capabilities of the output format.
* - For formats that support an alpha channel, don't do anything.
* - For formats that support a single transparent color, interpret everything that is more
* than 50% opaque as transparent.
* - For formats that don't support any form of transparency, fill the transparent area
* with a predefined background color.
*
* The code is based on phpthumb by James Heinrich <info@silisoftware.com>.
*
* @param resource $res
* @param string $outputMimeType
* @return GalleryStatus
*/
function _flattenAlphaChannel($res, $outputMimeType) {
$gd =& $this->_getGdFunctionality();
if ($this->_canHaveAlphaChannel($outputMimeType)) {
/* Input and output have transparency, no need to flatten. */
return null;
} else if ($this->_canHaveTransparency($outputMimeType)) {
/* The output can only have a single transparency color. */
$transparentColor = $gd->imagecolortransparent($res);
/* No operation if the transparent color is already defined */
if ($transparentColor == -1) {
/* No transparency color defined */
list ($ret, $width, $height) = $this->_getImageDimensionsForResource($res);
if ($ret) {
return $ret;
}
list ($ret, $alphaMixdownDitherImage) =
$this->_getTrueColorImageRes($width, $height, true);
if ($ret) {
return $ret;
}
for ($i = 0; $i <= 255; $i++) {
$ditherColors[$i] =
$gd->imagecolorallocate($alphaMixdownDitherImage, $i, $i, $i);
}
/*
* Scan through the true-color image and the copy alpha channel to the temp image as
* grayscale.
*/
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$pixelColor =
$gd->imagecolorsforindex($res, $gd->imagecolorat($res, $x, $y));
/* The alpha channel has a max value of 127 */
$gd->imagesetpixel($alphaMixdownDitherImage, $x, $y,
$ditherColors[($pixelColor['alpha'] * 2)]);
}
}
/* Dither alpha channel grayscale version down to 2 colors */
$gd->imagetruecolortopalette($alphaMixdownDitherImage, true, 2);
/*
* Reduce color palette to 256-1 colors (leave one palette position for transparent
* color).
*/
$gd->imagetruecolortopalette($res, true, 255);
/* Allocate a new (random, hopefully unused) color for transparent color index.*/
$transparentColor = $gd->imagecolorallocate($res, 1, 254, 253);
$gd->imagecolortransparent($res, $transparentColor);
/*
* Scan through the alpha channel image and replace all pixels with > 50% alpha
* with the transparency color.
*/
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$alphaChannelPixel = $gd->imagecolorsforindex($alphaMixdownDitherImage,
$gd->imagecolorat($alphaMixdownDitherImage, $x, $y));
if ($alphaChannelPixel['red'] > 127) {
$gd->imagesetpixel($res, $x, $y, $transparentColor);
}
}
}
$this->_free($alphaMixdownDitherImage);
}
} else {
/* The output image can't store any transparencies. Use a background color instead. */
list ($ret, $width, $height) = $this->_getImageDimensionsForResource($res);
if ($ret) {
return $ret;
}
list ($ret, $tempImage) = $this->_getTrueColorImageRes($width, $height, false);
if ($ret) {
return $ret;
}
$backgroundColor = $gd->imagecolorallocate($tempImage,
$this->_transparencyReplacementColor[0],
$this->_transparencyReplacementColor[1],
$this->_transparencyReplacementColor[2]);
$gd->imagefilledrectangle($tempImage, 0, 0, $width, $height, $backgroundColor);
$ret = $gd->imageCopy($tempImage, $res, 0, 0, 0, 0, $width, $height);
if ($ret) {
$this->_free($tempImage);
return $ret;
}
$gd->imagealphablending($res, true);
$gd->imagesavealpha($res, false);
/* Ignore errors */
$gd->imagecolortransparent($res, -1);
$ret = $gd->imageCopy($res, $tempImage, 0, 0, 0, 0, $width, $height);
$this->_free($tempImage);
if ($ret) {
return $ret;
}
}
return null;
}
/**
* @see GdFunctionality::imagedestroy
*/
function _free($resource) {
if ($resource) {
$gd =& $this->_getGdFunctionality();
$gd->imagedestroy($resource);
}
}
/**
* Checks whether files of the given mime-type can have transparencies.
* @param string $mimeType
* @param resource $res (optional) If specified, additional checks are executed
* @return bool
*/
function _canHaveTransparency($mimeType, $res=null) {
$canHaveTransparency =
in_array($mimeType, array('image/gif', 'image/png', 'image/tiff', 'image/ico'));
if ($mimeType == 'image/gif' && $res) {
/* Don't unnecessarily dither */
$gd =& $this->_getGdFunctionality();
$transparentColor = $gd->imagecolortransparent($res);
$canHaveTransparency = $transparentColor != -1;
}
return $canHaveTransparency;
}
/**
* Checks whether files of the given mime-type can have a whole alpha channel.
* @param string $mimeType
* @return bool
*/
function _canHaveAlphaChannel($mimeType) {
return in_array($mimeType, array('image/png', 'image/tiff', 'image/ico'));
}
}
?>