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:  * This class implements the Horde_Image:: API for the PHP GD
  4:  * extension.
  5:  *
  6:  * Copyright 2002-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file COPYING for license information (LGPL). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 10:  *
 11:  * @author  Chuck Hagenbuch <chuck@horde.org>
 12:  * @author  Michael J. Rubinsky <mrubinsk@horde.org>
 13:  * @package Image
 14:  */
 15: class Horde_Image_Gd extends Horde_Image_Base
 16: {
 17: 
 18:     /**
 19:      * Capabilites of this driver.
 20:      *
 21:      * @var array
 22:      */
 23:     protected $_capabilities = array('resize',
 24:                                      'crop',
 25:                                      'rotate',
 26:                                      'flip',
 27:                                      'mirror',
 28:                                      'grayscale',
 29:                                      'sepia',
 30:                                      'yellowize',
 31:                                      'canvas');
 32: 
 33:     /**
 34:      * GD Image resource for the current image data.
 35:      *
 36:      * @var resource
 37:      */
 38:     protected $_im;
 39: 
 40:     /**
 41:      * Const'r
 42:      *
 43:      * @param $params
 44:      *
 45:      * @return Horde_Image_gd
 46:      */
 47:     public function __construct($params, $context = array())
 48:     {
 49:         parent::__construct($params, $context);
 50:         if (!empty($params['filename'])) {
 51:             $this->loadFile($params['filename']);
 52:         } elseif (!empty($params['data'])) {
 53:             $this->loadString($params['data']);
 54:         } elseif (!empty($params['width'])) {
 55:             $this->_im = $this->create($this->_width, $this->_height);
 56:             $this->call('imageFill', array($this->_im, 0, 0, $this->_allocateColor($this->_background)));
 57:         }
 58:     }
 59: 
 60:     public function __get($property)
 61:     {
 62:         switch ($property) {
 63:         case '_im':
 64:              return $this->_im;
 65:         }
 66:     }
 67: 
 68:     /**
 69:      * Display the current image.
 70:      */
 71:     public function display()
 72:     {
 73:         $this->headers();
 74: 
 75:         return $this->call('image' . $this->_type, array($this->_im));
 76:     }
 77: 
 78:     /**
 79:      * Returns the raw data for this image.
 80:      *
 81:      * @param boolean $convert (ignored)
 82:      *
 83:      * @return string  The raw image data.
 84:      */
 85:     public function raw($convert = false)
 86:     {
 87:         if (!is_resource($this->_im)) {
 88:             return '';
 89:         }
 90: 
 91:         ob_start();
 92:         call_user_func('image' . $this->_type, $this->_im);
 93:         return ob_get_clean();
 94:     }
 95: 
 96:     /**
 97:      * Reset the image data.
 98:      */
 99:     public function reset()
100:     {
101:         parent::reset();
102:         if (is_resource($this->_im)) {
103:             return $this->call('imageDestroy', array($this->_im));
104:         }
105: 
106:         return true;
107:     }
108: 
109:     /**
110:      * Get the height and width of the current image.
111:      *
112:      * @return array  An hash with 'width' containing the width,
113:      *                'height' containing the height of the image.
114:      */
115:     public function getDimensions()
116:     {
117:         if (is_resource($this->_im) && $this->_width == 0 && $this->_height ==0) {
118:             $this->_width = $this->call('imageSX', array($this->_im));
119:             $this->_height = $this->call('imageSY', array($this->_im));
120:             return array('width' => $this->_width,
121:                          'height' => $this->_height);
122:         } else {
123:             return array('width' => $this->_width,
124:                          'height' => $this->_height);
125:         }
126:     }
127: 
128:     /**
129:      * Creates a color that can be accessed in this object. When a
130:      * color is set, the integer resource of it is returned.
131:      *
132:      * @param string $name  The name of the color.
133:      * @param int $alpha    Alpha transparency (0 - 127)
134:      *
135:      * @return integer  The resource of the color that can be passed to GD.
136:      */
137:     private function _allocateColor($name, $alpha = 0)
138:     {
139:         static $colors = array();
140: 
141:         if (empty($colors[$name])) {
142:             list($r, $g, $b) = self::getRGB($name);
143:             $colors[$name] = $this->call('imageColorAllocateAlpha', array($this->_im, $r, $g, $b, $alpha));
144:         }
145: 
146:         return $colors[$name];
147:     }
148: 
149:     /**
150:      * Get numeric font size from textual description
151:      *
152:      * @param string $font  The textual size description
153:      *
154:      * @return integer
155:      */
156:     private function _getFont($font)
157:     {
158:         switch ($font) {
159:         case 'tiny':
160:             return 1;
161: 
162:         case 'medium':
163:             return 3;
164: 
165:         case 'large':
166:             return 4;
167: 
168:         case 'giant':
169:             return 5;
170: 
171:         case 'small':
172:         default:
173:             return 2;
174:         }
175:     }
176: 
177:     /**
178:      * Load the image data from a string.
179:      *
180:      * @param string $image_data  The data to use for the image.
181:      *
182:      * @return void
183:      */
184:     public function loadString($image_data)
185:     {
186:         $this->_im = $this->call('imageCreateFromString', array($image_data));
187:     }
188: 
189:     /**
190:      * Load the image data from a file.
191:      *
192:      * @param string $filename  The full path and filename to the file to load
193:      *                          the image data from. The filename will also be
194:      *                          used for the image id.
195:      *
196:      * @return boolean
197:      * @throws Horde_Image_Exception
198:      */
199:     public function loadFile($filename)
200:     {
201:         $info = $this->call('getimagesize', array($filename));
202:         if (is_array($info)) {
203:             switch ($info[2]) {
204:             case 1:
205:                 if (function_exists('imagecreatefromgif')) {
206:                     $this->_im = $this->call('imagecreatefromgif', array($filename));
207:                 }
208:                 break;
209:             case 2:
210:                 $this->_im = $this->call('imagecreatefromjpeg', array($filename));
211:                 break;
212:             case 3:
213:                 $this->_im = $this->call('imagecreatefrompng', array($filename));
214:                 break;
215:             case 15:
216:                 if (function_exists('imagecreatefromgwbmp')) {
217:                     $this->_im = $this->call('imagecreatefromgwbmp', array($filename));
218:                 }
219:                 break;
220:             case 16:
221:                 $this->_im = $this->call('imagecreatefromxbm', array($filename));
222:                 break;
223:             }
224:         }
225: 
226:         if (is_resource($this->_im)) {
227:             return true;
228:         }
229: 
230:         $result = parent::loadFile($filename);
231: 
232:         $this->_im = $this->call('imageCreateFromString', array($this->_data));
233:     }
234: 
235:     /**
236:      * Resize the current image.
237:      *
238:      * @param integer $width      The new width.
239:      * @param integer $height     The new height.
240:      * @param boolean $ratio      Maintain original aspect ratio.
241:      *
242:      * @return boolean
243:      */
244:     public function resize($width, $height, $ratio = true)
245:     {
246:         /* Abort if we're asked to divide by zero, truncate the image
247:          * completely in either direction, or there is no image data.
248:          */
249:         if (!$width || !$height || !is_resource($this->_im)) {
250:             throw new Horde_Image_Exception('Unable to resize image.');
251:         }
252: 
253:         if ($ratio) {
254:             if ($width / $height > $this->call('imageSX', array($this->_im)) / $this->call('imageSY', array($this->_im))) {
255:                 $width = $height * $this->call('imageSX', array($this->_im)) / $this->call('imageSY', array($this->_im));
256:             } else {
257:                 $height = $width * $this->call('imageSY', array($this->_im)) / $this->call('imageSX', array($this->_im));
258:             }
259:         }
260: 
261:         $im = $this->_im;
262:         $this->_im = $this->create($width, $height);
263: 
264:         /* Reset geometry since it will change */
265:         $this->_width = 0;
266:         $this->_height = 0;
267: 
268:         $this->call('imageFill', array($this->_im, 0, 0, $this->call('imageColorAllocate', array($this->_im, 255, 255, 255))));
269:         try {
270:             $this->call('imageCopyResampled', array($this->_im, $im, 0, 0, 0, 0, $width, $height, $this->call('imageSX', array($im)), $this->call('imageSY', array($im))));
271:         } catch (Horde_Image_Exception $e) {
272:             $this->call('imageCopyResized', array($this->_im, $im, 0, 0, 0, 0, $width, $height, $this->call('imageSX', array($im)), $this->call('imageSY', array($im))));
273:         }
274:     }
275: 
276:     /**
277:      * Crop the current image.
278:      *
279:      * @param integer $x1  The top left corner of the cropped image.
280:      * @param integer $y1  The top right corner of the cropped image.
281:      * @param integer $x2  The bottom left corner of the cropped image.
282:      * @param integer $y2  The bottom right corner of the cropped image.
283:      */
284:     public function crop($x1, $y1, $x2, $y2)
285:     {
286:         $im = $this->_im;
287:         $this->_im = $this->create($x2 - $x1, $y2 - $y1);
288:         $this->_width = 0;
289:         $this->_height = 0;
290:         $this->call('imageCopy', array($this->_im, $im, 0, 0, $x1, $y1, $x2 - $x1, $y2 - $y1));
291:     }
292: 
293:     /**
294:      * Rotate the current image.
295:      *
296:      * @param integer $angle       The angle to rotate the image by,
297:      *                             in the clockwise direction
298:      * @param integer $background  The background color to fill any triangles
299:      */
300:     public function rotate($angle, $background = 'white')
301:     {
302:         $background = $this->_allocateColor($background);
303: 
304:         $this->_width = 0;
305:         $this->_height = 0;
306: 
307:         switch ($angle) {
308:         case '90':
309:             $x = $this->call('imageSX', array($this->_im));
310:             $y = $this->call('imageSY', array($this->_im));
311:             $xymax = max($x, $y);
312: 
313:             $im = $this->create($xymax, $xymax);
314:             $im = $this->call('imageRotate', array($im, 270, $background));
315:             $this->_im = $im;
316:             $im = $this->create($y, $x);
317:             if ($x < $y) {
318:                 $this->call('imageCopy', array($im, $this->_im, 0, 0, 0, 0, $xymax, $xymax));
319:             } elseif ($x > $y) {
320:                 $this->call('imageCopy', array($im, $this->_im, 0, 0, $xymax - $y, $xymax - $x, $xymax, $xymax));
321:             }
322:             $this->_im = $im;
323:             break;
324: 
325:         default:
326:             $this->_im = $this->call('imageRotate', array($this->_im, 360 - $angle, $background));
327:         }
328:     }
329: 
330:     /**
331:      * Flip the current image.
332:      */
333:     public function flip()
334:     {
335:         $x = $this->call('imageSX', array($this->_im));
336:         $y = $this->call('imageSY', array($this->_im));
337: 
338:         $im = $this->create($x, $y);
339:         for ($curY = 0; $curY < $y; $curY++) {
340:             $this->call('imageCopy', array($im, $this->_im, 0, $y - ($curY + 1), 0, $curY, $x, 1));
341:         }
342: 
343:         $this->_im = $im;
344:     }
345: 
346:     /**
347:      * Mirror the current image.
348:      */
349:     public function mirror()
350:     {
351:         $x = $this->call('imageSX', array($this->_im));
352:         $y = $this->call('imageSY', array($this->_im));
353: 
354:         $im = $this->create($x, $y);
355:         for ($curX = 0; $curX < $x; $curX++) {
356:             $this->call('imageCopy', array($im, $this->_im, $x - ($curX + 1), 0, $curX, 0, 1, $y));
357:         }
358: 
359:         $this->_im = $im;
360:     }
361: 
362:     /**
363:      * Convert the current image to grayscale.
364:      */
365:     public function grayscale()
366:     {
367:         $rateR = .229;
368:         $rateG = .587;
369:         $rateB = .114;
370:         $whiteness = 3;
371:         if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
372:             $this->call('imageTrueColorToPalette', array($this->_im, true, 256));
373:         }
374:         $colors = min(256, $this->call('imageColorsTotal', array($this->_im)));
375:         for ($x = 0; $x < $colors; $x++) {
376:             $src = $this->call('imageColorsForIndex', array($this->_im, $x));
377:             $new = min(255, abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
378:             $this->call('imageColorSet', array($this->_im, $x, $new, $new, $new));
379:         }
380:     }
381: 
382:     /**
383:      * Sepia filter.
384:      *
385:      * Basically turns the image to grayscale and then adds some
386:      * defined tint on it (R += 30, G += 43, B += -23) so it will
387:      * appear to be a very old picture.
388:      *
389:      * @param integer $threshold  (Ignored in GD driver for now)
390:      */
391:     public function sepia($threshold = 85)
392:     {
393:         $tintR = 80;
394:         $tintG = 43;
395:         $tintB = -23;
396:         $rateR = .229;
397:         $rateG = .587;
398:         $rateB = .114;
399:         $whiteness = 3;
400: 
401:         if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
402:             $this->call('imageTrueColorToPalette', array($this->_im, true, 256));
403:         }
404: 
405:         $colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
406:         for ($x = 0; $x < $colors; $x++) {
407:             $src = $this->call('imageColorsForIndex', array($this->_im, $x));
408:             $new = min(255, abs($src['red'] * $rateR + $src['green'] * $rateG + $src['blue'] * $rateB) + $whiteness);
409:             $r = min(255, $new + $tintR);
410:             $g = min(255, $new + $tintG);
411:             $b = min(255, $new + $tintB);
412:             $this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
413:         }
414:     }
415: 
416:     /**
417:      * Yellowize filter.
418:      *
419:      * Adds a layer of yellow that can be transparent or solid. If
420:      * $intensityA is 255 the image will be 0% transparent (solid).
421:      *
422:      * @param integer $intensityY  How strong should the yellow (red and green) be? (0-255)
423:      * @param integer $intensityB  How weak should the blue be? (>= 2, in the positive limit it will be make BLUE 0)
424:      */
425:     public function yellowize($intensityY = 50, $intensityB = 3)
426:     {
427:         if ($this->call('imageIsTrueColor', array($this->_im)) === true) {
428:             $this->call('imageTrueColorToPalette', array($this->_im, true, 256));
429:         }
430: 
431:         $colors = max(256, $this->call('imageColorsTotal', array($this->_im)));
432:         for ($x = 0; $x < $colors; $x++) {
433:             $src = $this->call('imageColorsForIndex', array($this->_im, $x));
434:             $r = min($src['red'] + $intensityY, 255);
435:             $g = min($src['green'] + $intensityY, 255);
436:             $b = max(($r + $g) / max($intensityB, 2), 0);
437:             $this->call('imageColorSet', array($this->_im, $x, $r, $g, $b));
438:         }
439:     }
440: 
441:     /**
442:      * Draws a text string on the image in a specified location, with
443:      * the specified style information.
444:      *
445:      * @param string  $string     The text to draw.
446:      * @param integer $x          The left x coordinate of the start of the
447:      *                            text string.
448:      * @param integer $y          The top y coordinate of the start of the text
449:      *                            string.
450:      * @param string  $font       The font identifier you want to use for the
451:      *                            text (ignored for GD - font determined by
452:      *                            $fontsize).
453:      * @param string  $color      The color that you want the text displayed in.
454:      * @param integer $direction  An integer that specifies the orientation of
455:      *                            the text.
456:      * @param string  $fontsize   The font (size) to use.
457:      *
458:      * @return boolean
459:      */
460:     public function text($string, $x, $y, $font = 'monospace', $color = 'black', $direction = 0, $fontsize = 'small')
461:     {
462:         $c = $this->_allocateColor($color);
463:         $f = $this->_getFont($fontsize);
464:         switch ($direction) {
465:         case -90:
466:         case 270:
467:             $result = $this->call('imageStringUp', array($this->_im, $f, $x, $y, $string, $c));
468:             break;
469: 
470:         case 0:
471:         default:
472:             $result = $this->call('imageString', array($this->_im, $f, $x, $y, $string, $c));
473:         }
474: 
475:         return $result;
476:     }
477: 
478:     /**
479:      * Draw a circle.
480:      *
481:      * @param integer $x     The x co-ordinate of the centre.
482:      * @param integer $y     The y co-ordinate of the centre.
483:      * @param integer $r     The radius of the circle.
484:      * @param string $color  The line color of the circle.
485:      * @param string $fill   The color to fill the circle.
486:      */
487:     public function circle($x, $y, $r, $color, $fill = null)
488:     {
489:         $c = $this->_allocateColor($color);
490:         if (is_null($fill)) {
491:             $result = $this->call('imageEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $c));
492:         } else {
493:             if ($fill !== $color) {
494:                 $fillColor = $this->_allocateColor($fill);
495:                 $this->call('imageFilledEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $fillColor));
496:                 $this->call('imageEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $c));
497:             } else {
498:                 $this->call('imageFilledEllipse', array($this->_im, $x, $y, $r * 2, $r * 2, $c));
499:             }
500:         }
501:     }
502: 
503:     /**
504:      * Draw a polygon based on a set of vertices.
505:      *
506:      * @param array $vertices  An array of x and y labeled arrays
507:      *                         (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
508:      * @param string $color    The color you want to draw the polygon with.
509:      * @param string $fill     The color to fill the polygon.
510:      */
511:     public function polygon($verts, $color, $fill = 'none')
512:     {
513:         $vertices = array();
514:         foreach ($verts as $vert) {
515:             $vertices[] = $vert['x'];
516:             $vertices[] = $vert['y'];
517:         }
518: 
519:         if ($fill != 'none') {
520:             $f = $this->_allocateColor($fill);
521:             $this->call('imageFilledPolygon', array($this->_im, $vertices, count($verts), $f));
522:         }
523: 
524:         if ($fill == 'none' || $fill != $color) {
525:             $c = $this->_allocateColor($color);
526:             $this->call('imagePolygon', array($this->_im, $vertices, count($verts), $c));
527:         }
528:     }
529: 
530:     /**
531:      * Draw a rectangle.
532:      *
533:      * @param integer $x       The left x-coordinate of the rectangle.
534:      * @param integer $y       The top y-coordinate of the rectangle.
535:      * @param integer $width   The width of the rectangle.
536:      * @param integer $height  The height of the rectangle.
537:      * @param string $color    The line color of the rectangle.
538:      * @param string $fill     The color to fill the rectangle with.
539:      */
540:     public function rectangle($x, $y, $width, $height, $color = 'black', $fill = 'none')
541:     {
542:         if ($fill != 'none') {
543:             $f = $this->_allocateColor($fill);
544:             $this->call('imageFilledRectangle', array($this->_im, $x, $y, $x + $width, $y + $height, $f));
545:         }
546: 
547:         if ($fill == 'none' || $fill != $color) {
548:             $c = $this->_allocateColor($color);
549:             $this->call('imageRectangle', array($this->_im, $x, $y, $x + $width, $y + $height, $c));
550:         }
551:     }
552: 
553:     /**
554:      * Draw a rounded rectangle.
555:      *
556:      * @param integer $x       The left x-coordinate of the rectangle.
557:      * @param integer $y       The top y-coordinate of the rectangle.
558:      * @param integer $width   The width of the rectangle.
559:      * @param integer $height  The height of the rectangle.
560:      * @param integer $round   The width of the corner rounding.
561:      * @param string $color    The line color of the rectangle.
562:      * @param string $fill     The color to fill the rounded rectangle with.
563:      */
564:     public function roundedRectangle($x, $y, $width, $height, $round, $color = 'black', $fill = 'none')
565:     {
566:         if ($round <= 0) {
567:             // Optimize out any calls with no corner rounding.
568:             return $this->rectangle($x, $y, $width, $height, $color, $fill);
569:         }
570: 
571:         $c = $this->_allocateColor($color);
572: 
573:         // Set corner points to avoid lots of redundant math.
574:         $x1 = $x + $round;
575:         $y1 = $y + $round;
576: 
577:         $x2 = $x + $width - $round;
578:         $y2 = $y + $round;
579: 
580:         $x3 = $x + $width - $round;
581:         $y3 = $y + $height - $round;
582: 
583:         $x4 = $x + $round;
584:         $y4 = $y + $height - $round;
585: 
586:         $r = $round * 2;
587: 
588:         // Calculate the upper left arc.
589:         $p1 = Horde_Image::arcPoints($round, 180, 225);
590:         $p2 = Horde_Image::arcPoints($round, 225, 270);
591: 
592:         // Calculate the upper right arc.
593:         $p3 = Horde_Image::arcPoints($round, 270, 315);
594:         $p4 = Horde_Image::arcPoints($round, 315, 360);
595: 
596:         // Calculate the lower right arc.
597:         $p5 = Horde_Image::arcPoints($round, 0, 45);
598:         $p6 = Horde_Image::arcPoints($round, 45, 90);
599: 
600:         // Calculate the lower left arc.
601:         $p7 = Horde_Image::arcPoints($round, 90, 135);
602:         $p8 = Horde_Image::arcPoints($round, 135, 180);
603: 
604:         // Draw the corners - upper left, upper right, lower right,
605:         // lower left.
606:         $this->call('imageArc', array($this->_im, $x1, $y1, $r, $r, 180, 270, $c));
607:         $this->call('imageArc', array($this->_im, $x2, $y2, $r, $r, 270, 360, $c));
608:         $this->call('imageArc', array($this->_im, $x3, $y3, $r, $r, 0, 90, $c));
609:         $this->call('imageArc', array($this->_im, $x4, $y4, $r, $r, 90, 180, $c));
610: 
611:         // Draw the connecting sides - top, right, bottom, left.
612:         $this->call('imageLine', array($this->_im, $x1 + $p2['x2'], $y1 + $p2['y2'], $x2 + $p3['x1'], $y2 + $p3['y1'], $c));
613:         $this->call('imageLine', array($this->_im, $x2 + $p4['x2'], $y2 + $p4['y2'], $x3 + $p5['x1'], $y3 + $p5['y1'], $c));
614:         $this->call('imageLine', array($this->_im, $x3 + $p6['x2'], $y3 + $p6['y2'], $x4 + $p7['x1'], $y4 + $p7['y1'], $c));
615:         $this->call('imageLine', array($this->_im, $x4 + $p8['x2'], $y4 + $p8['y2'], $x1 + $p1['x1'], $y1 + $p1['y1'], $c));
616: 
617: 
618:         if ($fill != 'none') {
619:             $f = $this->_allocateColor($fill);
620:             $this->call('imageFillToBorder', array($this->_im, $x + ($width / 2), $y + ($height / 2), $c, $f));
621:         }
622:     }
623: 
624:     /**
625:      * Draw a line.
626:      *
627:      * @param integer $x0    The x co-ordinate of the start.
628:      * @param integer $y0    The y co-ordinate of the start.
629:      * @param integer $x1    The x co-ordinate of the end.
630:      * @param integer $y1    The y co-ordinate of the end.
631:      * @param string $color  The line color.
632:      * @param string $width  The width of the line.
633:      */
634:     public function line($x1, $y1, $x2, $y2, $color = 'black', $width = 1)
635:     {
636:         $c = $this->_allocateColor($color);
637: 
638:         // Don't need to do anything special for single-width lines.
639:         if ($width == 1) {
640:             $this->call('imageLine', array($this->_im, $x1, $y1, $x2, $y2, $c));
641:         } elseif ($x1 == $x2) {
642:             // For vertical lines, we can just draw a vertical
643:             // rectangle.
644:             $left = $x1 - floor(($width - 1) / 2);
645:             $right = $x1 + floor($width / 2);
646:             $this->call('imageFilledRectangle', array($this->_im, $left, $y1, $right, $y2, $c));
647:         } elseif ($y1 == $y2) {
648:             // For horizontal lines, we can just draw a horizontal
649:             // filled rectangle.
650:             $top = $y1 - floor($width / 2);
651:             $bottom = $y1 + floor(($width - 1) / 2);
652:             $this->call('imageFilledRectangle', array($this->_im, $x1, $top, $x2, $bottom, $c));
653:         } else {
654:             // Angled lines.
655: 
656:             // Make sure that the end points of the line are
657:             // perpendicular to the line itself.
658:             $a = atan2($y1 - $y2, $x2 - $x1);
659:             $dx = (sin($a) * $width / 2);
660:             $dy = (cos($a) * $width / 2);
661: 
662:             $verts = array($x2 + $dx, $y2 + $dy, $x2 - $dx, $y2 - $dy, $x1 - $dx, $y1 - $dy, $x1 + $dx, $y1 + $dy);
663:             $this->call('imageFilledPolygon', array($this->_im, $verts, count($verts) / 2, $c));
664:         }
665:     }
666: 
667:     /**
668:      * Draw a dashed line.
669:      *
670:      * @param integer $x0           The x co-ordinate of the start.
671:      * @param integer $y0           The y co-ordinate of the start.
672:      * @param integer $x1           The x co-ordinate of the end.
673:      * @param integer $y1           The y co-ordinate of the end.
674:      * @param string $color         The line color.
675:      * @param string $width         The width of the line.
676:      * @param integer $dash_length  The length of a dash on the dashed line
677:      * @param integer $dash_space   The length of a space in the dashed line
678:      */
679:     public function dashedLine($x0, $y0, $x1, $y1, $color = 'black', $width = 1, $dash_length = 2, $dash_space = 2)
680:     {
681:         $c = $this->_allocateColor($color);
682:         $w = $this->_allocateColor('white');
683: 
684:         // Set up the style array according to the $dash_* parameters.
685:         $style = array();
686:         for ($i = 0; $i < $dash_length; $i++) {
687:             $style[] = $c;
688:         }
689:         for ($i = 0; $i < $dash_space; $i++) {
690:             $style[] = $w;
691:         }
692: 
693:         $this->call('imageSetStyle', array($this->_im, $style));
694:         $this->call('imageSetThickness', array($this->_im, $width));
695:         $this->call('imageLine', array($this->_im, $x0, $y0, $x1, $y1, IMG_COLOR_STYLED));
696:     }
697: 
698:     /**
699:      * Draw a polyline (a non-closed, non-filled polygon) based on a
700:      * set of vertices.
701:      *
702:      * @param array $vertices  An array of x and y labeled arrays
703:      *                         (eg. $vertices[0]['x'], $vertices[0]['y'], ...).
704:      * @param string $color    The color you want to draw the line with.
705:      * @param string $width    The width of the line.
706:      */
707:     public function polyline($verts, $color, $width = 1)
708:     {
709:         $first = true;
710:         foreach ($verts as $vert) {
711:             if (!$first) {
712:                 $this->line($lastX, $lastY, $vert['x'], $vert['y'], $color, $width);
713:             } else {
714:                 $first = false;
715:             }
716:             $lastX = $vert['x'];
717:             $lastY = $vert['y'];
718:         }
719:     }
720: 
721:     /**
722:      * Draw an arc.
723:      *
724:      * @param integer $x      The x co-ordinate of the centre.
725:      * @param integer $y      The y co-ordinate of the centre.
726:      * @param integer $r      The radius of the arc.
727:      * @param integer $start  The start angle of the arc.
728:      * @param integer $end    The end angle of the arc.
729:      * @param string  $color  The line color of the arc.
730:      * @param string  $fill   The fill color of the arc (defaults to none).
731:      */
732:     public function arc($x, $y, $r, $start, $end, $color = 'black', $fill = null)
733:     {
734:         $c = $this->_allocateColor($color);
735:         if (is_null($fill)) {
736:             $this->call('imageArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c));
737:         } else {
738:             if ($fill !== $color) {
739:                 $f = $this->_allocateColor($fill);
740:                 $this->call('imageFilledArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $f, IMG_ARC_PIE));
741:                 $this->call('imageFilledArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c, IMG_ARC_EDGED | IMG_ARC_NOFILL));
742:             } else {
743:                 $this->call('imageFilledArc', array($this->_im, $x, $y, $r * 2, $r * 2, $start, $end, $c, IMG_ARC_PIE));
744:             }
745:         }
746:     }
747: 
748:     /**
749:      * Creates an image of the given size.
750:      * If possible the function returns a true color image.
751:      *
752:      * @param integer $width   The image width.
753:      * @param integer $height  The image height.
754:      *
755:      * @return resource  The image handler.
756:      * @throws Horde_Image_Exception
757:      */
758:     public function create($width, $height)
759:     {
760:         $result = $this->call('imageCreateTrueColor', array($width, $height));
761:         if (!is_resource($result)) {
762:             throw new Horde_Image_Exception('Could not create image.');
763:         }
764: 
765:         return $result;
766:     }
767: 
768:     /**
769:      * Wraps a call to a function of the gd extension.
770:      *
771:      * @param string $function  The name of the function to wrap.
772:      * @param array $params     An array with all parameters for that function.
773:      *
774:      * @return mixed  The result of the function call
775:      * @throws Horde_Image_Exception
776:      */
777:     public function call($function, $params = null)
778:     {
779:         unset($php_errormsg);
780:         $track = ini_set('track_errors', 1);
781:         $result = @call_user_func_array($function, $params);
782:         if ($track !== false) {
783:             ini_set('track_errors', $track);
784:         }
785:         if (!empty($php_errormsg)) {
786:             $error_msg = $php_errormsg;
787:             throw new Horde_Image_Exception($error_msg);
788:         }
789:         return $result;
790:     }
791: 
792:     /**
793:      * Applies the specified mask to the image.
794:      *
795:      * @param resource $gdimg_mask  The gd image resource representing the mask
796:      *
797:      * @return boolean
798:      */
799:     public function applyMask($gdimg_mask)
800:     {
801:         $imgX = round($this->call('imageSX', array($this->_im)));
802:         $imgY = round($this->call('imageSY', array($this->_im)));
803:         $gdimg_mask_resized = $this->create($imgX, $imgY);
804:         $result = $this->call('imageCopyResampled',
805:                                array($gdimg_mask_resized,
806:                                      $gdimg_mask,
807:                                      0, 0, 0, 0,
808:                                      $imgX,
809:                                      $imgY,
810:                                      $this->call('imageSX', array($gdimg_mask)),
811:                                      $this->call('imageSY', array($gdimg_mask))));
812: 
813:         $gdimg_mask_blendtemp = $this->create($imgX, $imgY);
814:         $mbtX = $this->call('imageSX', array($gdimg_mask_blendtemp));
815:         $mbtY = $this->call('imageSY', array($gdimg_mask_blendtemp));
816: 
817:         $color_background = $this->call('imageColorAllocate',
818:                                          array($gdimg_mask_blendtemp, 0, 0, 0));
819: 
820:         $this->call('imageFilledRectangle', array($gdimg_mask_blendtemp,
821:                                                    0,
822:                                                    0,
823:                                                    $mbtX,
824:                                                    $mbtY,
825:                                                    $color_background));
826: 
827:         $this->call('imageAlphaBlending',
828:                      array($gdimg_mask_blendtemp, false));
829: 
830:         $this->call('imageSaveAlpha',
831:                      array($gdimg_mask_blendtemp, true));
832: 
833:         for ($x = 0; $x < $imgX; $x++) {
834:             for ($y = 0; $y < $imgY; $y++) {
835:                 $colorat = $this->call('imageColorAt', array($this->_im, $x, $y));
836:                 $realPixel = $this->call('imageColorsForIndex', array($this->_im, $colorat));
837:                 $colorat = $this->call('imageColorAt', array($gdimg_mask_resized, $x, $y));
838:                 $maskPixel = Horde_Image::grayscalePixel($this->call('imageColorsForIndex', array($gdimg_mask_resized, $colorat)));
839:                 $maskAlpha = 127 - (floor($maskPixel['red'] / 2) * (1 - ($realPixel['alpha'] / 127)));
840:                 $newcolor = $this->_allocateColorAlpha($gdimg_mask_blendtemp,
841:                                                        $realPixel['red'],
842:                                                        $realPixel['green'],
843:                                                        $realPixel['blue'],
844:                                                        intval($maskAlpha));
845:                 $this->call('imageSetPixel', array($gdimg_mask_blendtemp, $x, $y, $newcolor));
846:             }
847:         }
848:         $this->call('imageAlphaBlending', array($this->_im, false));
849:         $this->call('imageSaveAlpha', array($this->_im, true));
850:         $this->call('imageCopy', array($this->_im,
851:                                         $gdimg_mask_blendtemp,
852:                                         0, 0, 0, 0,
853:                                         $mbtX,
854:                                         $mbtY));
855: 
856:         $this->call('imageDestroy', array($gdimg_mask_blendtemp));
857: 
858:         $this->call('imageDestroy', array($gdimg_mask_resized));
859: 
860:         return true;
861:     }
862: 
863:     protected function _allocateColorAlpha($gdimg_hexcolorallocate, $r, $g, $b , $alpha = false)
864:     {
865:         $result = $this->call('imageColorAllocateAlpha', array($gdimg_hexcolorallocate, $r, $g, $b, intval($alpha)));
866:         $result = $this->call('imageColorAllocate', array($gdimg_hexcolorallocate, $r, $g, $b));
867:         return $result;
868:     }
869: 
870:     /**
871:      * Request a specific image from the collection of images.
872:      *
873:      * @param integer $index  The index to return
874:      *
875:      * @return Horde_Image_Base
876:      */
877:     public function getImageAtIndex($index)
878:     {
879:         if ($index > 0) {
880:             throw new Horde_Image_Exception('Image index out of bounds.');
881:         }
882:     }
883: 
884:     /**
885:      * Return the number of image pages available in the image object.
886:      *
887:      * @return integer
888:      */
889:     public function getImagePageCount()
890:     {
891:         return 1;
892:     }
893: 
894: }
895: 
API documentation generated by ApiGen