Overview

Packages

  • Image
  • None

Classes

  • Horde_Image
  • Horde_Image_Base
  • Horde_Image_Effect
  • Horde_Image_Effect_Border
  • Horde_Image_Effect_Gd_DropShadow
  • Horde_Image_Effect_Gd_RoundCorners
  • Horde_Image_Effect_Gd_TextWatermark
  • Horde_Image_Effect_Gd_Unsharpmask
  • Horde_Image_Effect_Im_Border
  • Horde_Image_Effect_Im_CenterCrop
  • Horde_Image_Effect_Im_Composite
  • Horde_Image_Effect_Im_DropShadow
  • Horde_Image_Effect_Im_LiquidResize
  • Horde_Image_Effect_Im_PhotoStack
  • Horde_Image_Effect_Im_PolaroidImage
  • Horde_Image_Effect_Im_RoundCorners
  • Horde_Image_Effect_Im_TextWatermark
  • Horde_Image_Effect_Im_Unsharpmask
  • Horde_Image_Effect_Imagick_Border
  • Horde_Image_Effect_Imagick_CenterCrop
  • Horde_Image_Effect_Imagick_Composite
  • Horde_Image_Effect_Imagick_DropShadow
  • Horde_Image_Effect_Imagick_LiquidResize
  • Horde_Image_Effect_Imagick_PhotoStack
  • Horde_Image_Effect_Imagick_PolaroidImage
  • Horde_Image_Effect_Imagick_RoundCorners
  • Horde_Image_Effect_Imagick_SmartCrop
  • Horde_Image_Effect_Imagick_TextWatermark
  • Horde_Image_Effect_Imagick_Unsharpmask
  • Horde_Image_Exception
  • Horde_Image_Exif
  • Horde_Image_Exif_Base
  • Horde_Image_Exif_Bundled
  • Horde_Image_Exif_Exiftool
  • Horde_Image_Exif_Parser_Base
  • Horde_Image_Exif_Php
  • Horde_Image_Gd
  • Horde_Image_Im
  • Horde_Image_Imagick
  • Horde_Image_Png
  • Horde_Image_Svg
  • Horde_Image_Swf
  • Horde_Image_Translation
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Effect for composing multiple images into a single image.
  4:  *
  5:  * Copyright 2007-2012 Horde LLC (http://www.horde.org/)
  6:  *
  7:  * The technique for the Polaroid-like stack using the Imagick extension is
  8:  * credited to Mikko Koppanen and is documented at http://valokuva.org
  9:  *
 10:  * @author  Michael J. Rubinsky <mrubinsk@horde.org>
 11:  * @package Image
 12:  */
 13: class Horde_Image_Effect_Imagick_PhotoStack extends Horde_Image_Effect
 14: {
 15:     /**
 16:      * Valid parameters for the stack effect
 17:      *
 18:      * images           -    An array of Horde_Image objects to stack. Images
 19:      *                       are stacked in a FIFO manner, so that the top-most
 20:      *                       image is the last one in this array.
 21:      *
 22:      * type             -    Determines the style for the composition.
 23:      *                       'plain' or 'polaroid' are supported.
 24:      *
 25:      * resize_height    -    The height that each individual thumbnail
 26:      *                       should be resized to before composing on the image.
 27:      *
 28:      * padding          -    How much padding should we ensure is left around
 29:      *                       the active image area?
 30:      *
 31:      * background       -    The background canvas color - this is used as the
 32:      *                       color to set any padding to.
 33:      *
 34:      * bordercolor      -    If using type 'plain' this sets the color of the
 35:      *                       border that each individual thumbnail gets.
 36:      *
 37:      * borderwidth      -    If using type 'plain' this sets the width of the
 38:      *                       border on each individual thumbnail.
 39:      *
 40:      * offset           -    If using type 'plain' this determines the amount of
 41:      *                       x and y offset to give each successive image when
 42:      *                       it is placed on the top of the stack.
 43:      *
 44:      * @var array
 45:      */
 46:     protected $_params = array('type' => 'plain',
 47:                                'resize_height' => '150',
 48:                                'padding' => 0,
 49:                                'background' => 'none',
 50:                                'bordercolor' => '#333',
 51:                                'borderwidth' => 1,
 52:                                'borderrounding' => 10,
 53:                                'offset' => 5);
 54: 
 55:     /**
 56:      * Create the photo_stack
 57:      *
 58:      */
 59:     public function apply()
 60:     {
 61:         $i = 1;
 62:         $cnt = count($this->_params['images']);
 63:         if ($cnt <=0) {
 64:             throw new Horde_Image_Exception('No Images provided.');
 65:         }
 66:         if (!method_exists($this->_image->imagick, 'polaroidImage') ||
 67:             !method_exists($this->_image->imagick, 'trimImage')) {
 68:                 throw new Horde_Image_Exception('Your version of Imagick is not compiled against a recent enough ImageMagick library to use the PhotoStack effect.');
 69:         }
 70: 
 71:         $imgs = array();
 72:         $length = 0;
 73: 
 74:         switch ($this->_params['type']) {
 75:         case 'plain':
 76:         case 'rounded':
 77:             $haveBottom = false;
 78:             // First, we need to resize the top image to get the dimensions
 79:             // for the rest of the stack.
 80:             $topimg = new Imagick();
 81:             $topimg->clear();
 82:             $topimg->readImageBlob($this->_params['images'][$cnt - 1]->raw());
 83:             $topimg->thumbnailImage(
 84:                 $this->_params['resize_height'],
 85:                 $this->_params['resize_height'],
 86:                 true);
 87:             if ($this->_params['type'] == 'rounded') {
 88:                 $topimg = $this->_roundBorder($topimg);
 89:             }
 90: 
 91:             $size = $topimg->getImageGeometry();
 92:             foreach ($this->_params['images'] as $image) {
 93:                 $imgk= new Imagick();
 94:                 $imgk->clear();
 95:                 $imgk->readImageBlob($image->raw());
 96:                 // Either resize the thumbnail to match the top image or we *are*
 97:                 // the top image already.
 98:                 if ($i++ <= $cnt) {
 99:                     $imgk->thumbnailImage($size['width'], $size['height'], false);
100:                 } else {
101:                     $imgk->destroy();
102:                     $imgk = $topimg->clone();
103:                 }
104:                 if ($this->_params['type'] == 'rounded') {
105:                     $imgk = $this->_roundBorder($imgk);
106:                 } else {
107:                     $imgk->borderImage($this->_params['bordercolor'],
108:                                        $this->_params['borderwidth'],
109:                                        $this->_params['borderwidth']);
110:                 }
111:                 // Only shadow the bottom image for 'plain' stacks
112:                 if (!$haveBottom) {
113:                     $shad = $imgk->clone();
114:                     $shad->setImageBackgroundColor(new ImagickPixel('black'));
115:                     $shad->shadowImage(80, 4, 0, 0);
116:                     $shad->compositeImage($imgk, Imagick::COMPOSITE_OVER, 0, 0);
117:                     $imgk->clear();
118:                     $imgk->addImage($shad);
119:                     $shad->destroy();
120:                     $haveBottom = true;
121:                 }
122:                 // Get the geometry of the image and remember the largest.
123:                 $geo = $imgk->getImageGeometry();
124:                 $length = max(
125:                     $length,
126:                     sqrt(pow($geo['height'], 2) + pow($geo['width'], 2)));
127: 
128:                 $imgs[] = $imgk;
129:             }
130:             break;
131:         case 'polaroid':
132:             foreach ($this->_params['images'] as $image) {
133:                 //@TODO: instead of doing $image->raw(), we might be able to clone
134:                 //         the imagick object if we can do it cleanly might
135:                 //         be faster, less memory intensive?
136:                 $imgk= new Imagick();
137:                 $imgk->clear();
138:                 $imgk->readImageBlob($image->raw());
139:                 $imgk->thumbnailImage($this->_params['resize_height'],
140:                                       $this->_params['resize_height'],
141:                                       true);
142:                 $imgk->setImageBackgroundColor('black');
143:                 if ($i++ == $cnt) {
144:                     $angle = 0;
145:                 } else {
146:                     $angle = mt_rand(1, 45);
147:                     if (mt_rand(1, 2) % 2 === 0) {
148:                         $angle = $angle * -1;
149:                     }
150:                 }
151:                 $result = $imgk->polaroidImage(new ImagickDraw(), $angle);
152:    
153:                  // Get the geometry of the image and remember the largest.
154:                 $geo = $imgk->getImageGeometry();
155:                 $length = max(
156:                     $length,
157:                     sqrt(pow($geo['height'], 2) + pow($geo['width'], 2)));
158: 
159:                 $imgs[] = $imgk;
160:             }
161:             break;
162:         }
163: 
164:         // Make sure the background canvas is large enough to hold it all.
165:         $this->_image->imagick->thumbnailImage($length * 1.5 + 20,
166:                                                $length * 1.5 + 20);
167: 
168:         // x and y offsets.
169:         $xo = $yo = (count($imgs) + 1) * $this->_params['offset'];
170:         foreach ($imgs as $image) {
171:             if ($this->_params['type'] == 'polaroid') {
172:                 $xo = mt_rand(1, $this->_params['resize_height'] / 2);
173:                 $yo = mt_rand(1, $this->_params['resize_height'] / 2);
174:             } elseif ($this->_params['type'] == 'plain' ||
175:                       $this->_params['type'] == 'rounded') {
176:                 $xo -= $this->_params['offset'];
177:                 $yo -= $this->_params['offset'];
178:             }
179:             $this->_image->imagick->compositeImage($image, Imagick::COMPOSITE_OVER, $xo, $yo);
180:             $image->removeImage();
181:             $image->destroy();
182:         }
183: 
184:         // Trim the canvas before resizing to keep the thumbnails as large
185:         // as possible.
186:         $this->_image->imagick->trimImage(0);
187:         if ($this->_params['padding'] || $this->_params['background'] != 'none') {
188:             $this->_image->imagick->borderImage(
189:                 new ImagickPixel($this->_params['background']),
190:                 $this->_params['padding'],
191:                 $this->_params['padding']);
192:         }
193: 
194:         return true;
195:     }
196: 
197:     private function _roundBorder($image)
198:     {
199:         $context = array('tmpdir' => $this->_image->getTmpDir());
200:         $size = $image->getImageGeometry();
201:         $new = new Horde_Image_Imagick(array(), $context);
202:         $new->loadString($image->getImageBlob());
203:         $image->destroy();
204:         $new->addEffect('RoundCorners', array('border' => 2, 'bordercolor' => '#111'));
205:         $new->applyEffects();
206:         $return = new Imagick();
207:         $return->newImage($size['width'] + $this->_params['borderwidth'],
208:                           $size['height'] + $this->_params['borderwidth'],
209:                           $this->_params['bordercolor']);
210:         $return->setImageFormat($this->_image->getType());
211:         $return->clear();
212:         $return->readImageBlob($new->raw());
213: 
214:         return $return;
215:     }
216: 
217: }
API documentation generated by ApiGen