image_data !== null and imagedestroy($this->image_data); $this->image_data = null; } // Check if the function exists if (function_exists('imagecreatefrom'.$image_extension)) { // Create a new transparent image. $sizes = $this->sizes($image_fullpath); $tmpImage = call_user_func('imagecreatefrom'.$image_extension, $image_fullpath); $image = $this->create_transparent_image($sizes->width, $sizes->height, $tmpImage); if ( ! $return_data) { $this->image_data = $image; $return = true; } else { $return = $image; } $this->debug('', "Loaded ".$image_fullpath." with size of ".$sizes->width."x".$sizes->height); } else { throw new \RuntimeException("Function imagecreatefrom".$image_extension."() does not exist (Missing GD?)"); } return $return_data ? $return : $this; } protected function _crop($x1, $y1, $x2, $y2) { extract(parent::_crop($x1, $y1, $x2, $y2)); $width = $x2 - $x1; $height = $y2 - $y1; $this->debug("Cropping image ".$width."x".$height."+$x1+$y1 based on coords ($x1, $y1), ($x2, $y2)"); $image = $this->create_transparent_image($width, $height); imagecopy($image, $this->image_data, 0, 0, $x1, $y1, $width, $height); $this->image_data = $image; } protected function _resize($width, $height = null, $keepar = true, $pad = true) { extract(parent::_resize($width, $height, $keepar, $pad)); $sizes = $this->sizes(); $this->debug("Resizing image to $width, $height with" . ($keepar ? '' : 'out') . " keeping AR and with" . ($pad ? '' : 'out') . " padding."); // Add the original image. $image = $this->create_transparent_image($cwidth, $cheight); call_user_func($this->gdresizefunc, $image, $this->image_data, $x, $y, 0, 0, $width, $height, $sizes->width, $sizes->height); $this->image_data = $image; } protected function _rotate($degrees) { extract(parent::_rotate($degrees)); $degrees = 360 - $degrees; $bgcolor = $this->config['bgcolor'] !== null ? $this->config['bgcolor'] : '#000'; $color = $this->create_color($this->image_data, $bgcolor, 100); $this->image_data = imagerotate($this->image_data, $degrees, $color, false); } protected function _watermark($filename, $position, $padding = 5) { $values = parent::_watermark($filename, $position, $padding); if ($values == false) { throw new \InvalidArgumentException("Watermark image not found or invalid filetype."); } else { extract($values); $wsizes = $this->sizes($filename); $sizes = $this->sizes(); // Load the watermark preserving transparency $watermark = $this->load($filename, true); // Below is to prevent glitch in GD with negative $x coords if ($x < 0 || $y < 0) { $this->debug("Modifying watermark to remove negative coords."); // Generate a new width and height for the watermark. $newwidth = ($x < 0 ? $wsizes->width + $x : $wsizes->width); $newheight = ($y < 0 ? $wsizes->height + $y : $wsizes->height); // Create a transparent image the size of the new watermark. $tmpwatermark = $this->create_transparent_image($newwidth, $newheight); $this->debug("New size is $newwidth x $newheight and coords are $x , $y"); // Call the resize function based on image format imagecopy( $tmpwatermark, $watermark, // Copy the new image into the tmp watermark 0, 0, $x < 0 ? abs($x) : 0, $y < 0 ? abs($y) : 0, $newwidth, $newheight ); // Set the variables for the image_merge $watermark = $tmpwatermark; $x = $x < 0 ? 0 : $x; $y = $y < 0 ? 0 : $y; } // Used as a workaround for lack of alpha support in imagecopymerge. $this->debug("Coords for watermark are $x , $y"); $this->image_merge($this->image_data, $watermark, $x, $y, $this->config['watermark_alpha']); } } protected function _flip($mode) { $sizes = (array) $this->sizes(); $source = array_merge($sizes, array('x' => 0, 'y' => 0)); switch ($mode) { case 'vertical': $source['y'] = $sizes['height'] - 1; $source['height'] = -$sizes['height']; break; case 'horizontal': $source['x'] = $sizes['width'] - 1; $source['width'] = -$sizes['width']; break; case 'both': $source['y'] = $sizes['height'] - 1; $source['x'] = $sizes['width'] - 1; $source['height'] = -$sizes['height']; $source['width'] = -$sizes['width']; break; default: return false; } $image = imagecreatetruecolor($sizes['width'], $sizes['height']); imagecopyresampled( $image, $this->image_data, 0, 0, $source['x'], $source['y'], $sizes['width'], $sizes['height'], $source['width'], $source['height'] ); $this->image_data = $image; } protected function _border($size, $color = null) { extract(parent::_border($size, $color)); $sizes = $this->sizes(); $image = $this->create_transparent_image($sizes->width + ($size * 2), $sizes->height + ($size * 2)); $color = $this->create_color($image, $color, 100); $this->image_merge($image, $this->image_data, $size, $size, 100); for ($s = 0; $s < $size; $s++) { imagerectangle($image, $s, $s, $sizes->width + ($size * 2) - $s - 1, $sizes->height + ($size * 2) - $s - 1, $color); } $this->image_data = $image; } protected function _mask($maskimage) { extract(parent::_mask($maskimage)); // Get size and width of image $sizes = $this->sizes(); $masksizes = $this->sizes($maskimage); // Create new blank image $image = $this->create_transparent_image($sizes->width, $sizes->height); if (is_resource($maskimage)) { $maskim = $maskimage; } else { $maskim = $this->load($maskimage, true); } $masksizes->width > $sizes->width and $masksizes->width = $sizes->width; $masksizes->height > $sizes->width and $masksizes->height = $sizes->height; // Loop through all the pixels for ($x = 0; $x < $masksizes->width; $x++) { for ($y = 0; $y < $masksizes->height; $y++) { $maskcolor = imagecolorat($maskim, $x, $y); $maskcolor = imagecolorsforindex($maskim, $maskcolor); $maskalpha = 127 - floor(($maskcolor['red'] + $maskcolor['green'] + $maskcolor['blue']) / 6); if ($maskalpha == 127) { continue; } if ($maskalpha == 0) { $ourcolor = array( 'red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 0, ); } else { $ourcolor = imagecolorat($this->image_data, $x, $y); $ourcolor = imagecolorsforindex($this->image_data, $ourcolor); } $ouralpha = 127 - $ourcolor['alpha']; if ($ouralpha == 0) { continue; } $newalpha = floor($ouralpha - (($maskalpha / 127) * $ouralpha)); $newcolor = imagecolorallocatealpha($image, $ourcolor['red'], $ourcolor['green'], $ourcolor['blue'], 127 - $newalpha); imagesetpixel($image, $x, $y, $newcolor); } } $this->image_data = $image; } protected function _rounded($radius, $sides, $antialias) { extract(parent::_rounded($radius, $sides, $antialias)); $tl and $this->round_corner($this->image_data, $radius, $antialias, true, true); $tr and $this->round_corner($this->image_data, $radius, $antialias, true, false); $bl and $this->round_corner($this->image_data, $radius, $antialias, false, true); $br and $this->round_corner($this->image_data, $radius, $antialias, false, false); } protected function _grayscale() { $sizes = $this->sizes(); // Create the 256 color palette $bwpalette = array(); for ($i = 0; $i < 256; $i++) { $bwpalette[$i] = imagecolorallocate($this->image_data, $i, $i, $i); } for ($x = 0; $x < $sizes->width; $x++) { for ($y = 0; $y < $sizes->height; $y++) { $color = imagecolorat($this->image_data, $x, $y); $red = ($color >> 16) & 0xFF; $green = ($color >> 8) & 0xFF; $blue = $color & 0xFF; // If its black or white, theres no use in setting the pixel if (($red == 0 && $green == 0 && $blue == 0) || ($red == 255 && $green == 255 && $blue == 255)) { continue; } // Now set the color $shade = (($red*0.299)+($green*0.587)+($blue*0.114)); imagesetpixel($this->image_data, $x, $y, $bwpalette[$shade]); } } } public function sizes($filename = null) { if (empty($filename) && !empty($this->image_fullpath)) { $filename = $this->image_fullpath; } if ($filename == $this->image_fullpath && is_resource($this->image_data)) { $width = imagesx($this->image_data); $height = imagesy($this->image_data); } elseif (is_resource($filename)) { $width = imagesx($filename); $height = imagesy($filename); } else { list($width, $height) = getimagesize($filename); } return (object) array('width' => $width, 'height' => $height); } public function save($filename = null, $permissions = null) { extract(parent::save($filename, $permissions)); $this->run_queue(); $this->add_background(); $vars = array(&$this->image_data, $filename); $filetype = $this->image_extension; if ($filetype == 'jpg' || $filetype == 'jpeg') { $vars[] = $this->config['quality']; $filetype = 'jpeg'; } elseif ($filetype == 'png') { $vars[] = floor(($this->config['quality'] / 100) * 9); } call_fuel_func_array('image'.$filetype, $vars); if ($this->config['persistence'] === false) { $this->reload(); } return $this; } public function output($filetype = null) { $this->gdresizefunc = ($filetype == 'gif') ? 'imagecopyresized' : $this->gdresizefunc = 'imagecopyresampled'; extract(parent::output($filetype)); $this->run_queue(); $this->add_background(); $vars = array($this->image_data, null); if ($filetype == 'jpg' || $filetype == 'jpeg') { $vars[] = $this->config['quality']; $filetype = 'jpeg'; } elseif ($filetype == 'png') { $vars[] = floor(($this->config['quality'] / 100) * 9); } call_fuel_func_array('image'.$filetype, $vars); if ($this->config['persistence'] === false) { $this->reload(); } return $this; } /** * Creates a new color usable by GD. * * @param resource $image The image to create the color from * @param string $hex The hex code of the color * @param integer $alpha The alpha of the color, 0 (trans) to 100 (opaque) * @return integer The color */ protected function create_color(&$image, $hex, $alpha) { extract($this->create_hex_color($hex)); // Handling alpha is different among drivers if ($hex == null) { $alpha = 127; } else { $alpha = 127 - floor($alpha * 1.27); } // Check if the transparency is allowed return imagecolorallocatealpha($image, $red, $green, $blue, $alpha); } protected function add_background() { if ($this->config['bgcolor'] != null || ($this->new_extension == 'jpg' || $this->new_extension == 'jpeg')) { $bgcolor = $this->config['bgcolor'] == null ? '#000' : $this->config['bgcolor']; $this->debug("Adding background color $bgcolor"); $sizes = $this->sizes(); $bgimg = $this->create_transparent_image($sizes->width, $sizes->height); $color = $this->create_color($bgimg, $bgcolor, 100); imagefill($bgimg, 0, 0, $color); $this->image_merge($bgimg, $this->image_data, 0, 0, 100); $this->image_data = $bgimg; } } /** * Creates a new transparent image. * * @param integer $width The width of the image. * @param integer $height The height of the image. * @param resource $resource Optionally add an image to the new transparent image. * @return resource Returns the image in resource form. */ protected function create_transparent_image($width, $height, $resource = null) { $image = imagecreatetruecolor($width, $height); $bgcolor = $this->config['bgcolor'] == null ? '#000' : $this->config['bgcolor']; $color = $this->create_color($image, $bgcolor, 0); imagesavealpha($image, true); if ($this->image_extension == 'gif' || $this->image_extension == 'png') { // Get the current transparent color if possible... $transcolor = imagecolortransparent($image); if ($transcolor > 0) { $color = $transcolor; } imagecolortransparent($image, $color); } // Set the blending mode to false, add the bgcolor, then switch it back. imagealphablending($image, false); imagefilledrectangle($image, 0, 0, $width, $height, $color); imagealphablending($image, true); if (is_resource($resource)) { imagecopy($image, $resource, 0, 0, 0, 0, $width, $height); } return $image; } /** * Creates a rounded corner on the image. * * @param resource $image * @param integer $radius * @param integer $antialias * @param boolean $top * @param boolean $left */ protected function round_corner(&$image, $radius, $antialias, $top, $left) { $this->debug("Rounding ".($top ? 'top' : 'bottom')." ".($left ? 'left' : 'right')." corner with a radius of ".$radius."px."); $sX = $left ? -$radius : 0; $sY = $top ? -$radius : 0; $eX = $left ? 0 : $radius; $eY = $top ? 0 : $radius; // Get this images size $sizes = $this->sizes(); $offsetX = ($left ? $radius : $sizes->width - $radius - 1); $offsetY = ($top ? $radius : $sizes->height - $radius - 1); // Set the images alpha blend to false imagealphablending($image, false); // Make this color ahead time $transparent = $this->create_color($image, null, 0); for ($x = $sX; $x <= $eX; $x++) { for ($y = $sY; $y <= $eY; $y++) { $dist = sqrt(($x * $x) + ($y * $y)); if ($dist <= $radius + $antialias) { // Decide if anything needs to be changed // We subtract from antialias so the transparency makes sense. $fromCirc = $dist - $radius; if ($fromCirc > 0) { if ($fromCirc == 0) { imagesetpixel($image, $x + $offsetX, $y + $offsetY, $transparent); } else { // Get color information from this spot on the image $rgba = imagecolorat($image, $x + $offsetX, $y + $offsetY); $tmpColor = imagecolorallocatealpha( $image, ($rgba >> 16) & 0xFF, // Red ($rgba >> 8) & 0xFF, // Green $rgba & 0xFF, // Blue (127 - (($rgba >> 24) & 0xFF)) * ($fromCirc / $antialias) // Alpha ); imagesetpixel($image, $x + $offsetX, $y + $offsetY, $tmpColor); } } } else { // Clear this area out... imagesetpixel($image, $x + $offsetX, $y + $offsetY, $transparent); } } } // Reset alpha blending imagealphablending($image, true); } /** * Merges to images together, using a fix for transparency * * @param resource $image The bottom image * @param resource $watermark The image to be placed on top * @param integer $x The position of the watermark on the X-axis * @param integer $y The position of the watermark on the Y-axis * @param integer $alpha The transparency of the watermark, 0 (trans) to 100 (opaque) */ protected function image_merge(&$image, $watermark, $x, $y, $alpha) { $wsizes = $this->sizes($watermark); $tmpimage = $this->create_transparent_image($wsizes->width, $wsizes->height); imagecopy($tmpimage, $image, 0, 0, $x, $y, $wsizes->width, $wsizes->height); imagecopy($tmpimage, $watermark, 0, 0, 0, 0, $wsizes->width, $wsizes->height); imagealphablending($image, false); imagecopymerge($image, $tmpimage, $x, $y, 0, 0, $wsizes->width, $wsizes->height, $alpha); imagealphablending($image, true); } }