1: <?php
2:
3: /**
4: * library for image handling using the GD library of functions
5: * @package core
6: */
7: // force UTF-8 Ø
8:
9: $_zp_graphics_optionhandlers[] = new lib_GD_Options(); // register option handler
10: /**
11: * Option class for lib-GD
12: *
13: */
14:
15: class lib_GD_Options {
16:
17: function __construct() {
18:
19: }
20:
21: /**
22: * Standard option interface
23: *
24: * @return array
25: */
26: function getOptionsSupported() {
27: if (defined('GD_FREETYPE') && GD_FREETYPE) {
28: return array(gettext('GD TypeFace path') => array('key' => 'GD_FreeType_Path', 'type' => OPTION_TYPE_TEXTBOX,
29: 'desc' => gettext('Supply the full path to your TrueType fonts.')));
30: } else {
31: return array();
32: }
33: }
34:
35: function canLoadMsg() {
36: if (extension_loaded('gd')) {
37: return '';
38: } else {
39: return gettext('The <strong><em>GD</em></strong> extension is not available.');
40: }
41: }
42:
43: }
44:
45: if (!function_exists('zp_graphicsLibInfo')) {
46:
47: /**
48: * Zenphoto image manipulation functions using the PHP GD library
49: *
50: */
51: if (extension_loaded('gd')) { // only define the functions if we have the proper versions
52: $_lib_GD_info = array();
53: $info = gd_info();
54: $_lib_GD_info['Library'] = 'GD';
55: $_lib_GD_info['Library_desc'] = sprintf(gettext('PHP GD library <em>%s</em>'), $info['GD Version']);
56: $_lib_GD_info['FreeType'] = $info['FreeType Support'];
57: define('GD_FREETYPE', (bool) $_lib_GD_info['FreeType']);
58: unset($_lib_GD_info['FreeType']);
59: define('GD_FREETYPE_SAMPLE', 'The quick brown fox jumps over the lazy dog');
60: define('GD_FREETYPE_SAMPLE_CHARS', strlen('GD_FREETYPE_SAMPLE'));
61: $_gd_freetype_fonts = array(0);
62:
63: $imgtypes = imagetypes();
64: $_lib_GD_info['GIF'] = ($imgtypes & IMG_GIF) ? 'gif' : false;
65: $_lib_GD_info['JPG'] = ($imgtypes & IMG_JPG) ? 'jpg' : false;
66: $_lib_GD_info['JPEG'] = ($imgtypes & IMG_JPG) ? 'jpg' : false;
67: $_lib_GD_info['PNG'] = ($imgtypes & IMG_PNG) ? 'png' : false;
68: $_lib_GD_info['WBMP'] = ($imgtypes & IMG_WBMP) ? 'jpg' : false;
69: unset($imgtypes);
70: unset($info);
71:
72: if (DEBUG_IMAGE)
73: debugLog("Loading " . $_lib_GD_info['Library']);
74:
75: /**
76: * Takes an image filename and returns a GD Image using the correct function
77: * for the image's format (imagecreatefrom*). Supports JPEG, GIF, and PNG.
78: * @param string $imagefile the full path and filename of the image to load.
79: * @return image the loaded GD image object.
80: *
81: */
82: function zp_imageGet($imgfile) {
83: $ext = getSuffix($imgfile);
84: switch ($ext) {
85: case 'png':
86: return imagecreatefrompng($imgfile);
87: case 'wbmp':
88: return imagecreatefromwbmp($imgfile);
89: case 'jpeg':
90: case 'jpg':
91: return imagecreatefromjpeg($imgfile);
92: case 'gif':
93: return imagecreatefromgif($imgfile);
94: }
95: return false;
96: }
97:
98: /**
99: * outputs an image resource as a given type
100: *
101: * @param resource $im
102: * @param string $type
103: * @param string $filename
104: * @param int $qual
105: */
106: function zp_imageOutput($im, $type, $filename = NULL, $qual = 75) {
107: $qual = max(min($qual, 100), 0);
108: if (getOption('image_interlace')) {
109: imageinterlace($im, true);
110: }
111: switch ($type) {
112: case 'png':
113: $qual = max(0, 9 - round($qual / 10));
114: return imagepng($im, $filename, $qual);
115: case 'wbmp':
116: return imagewbmp($im, $filename);
117: case 'jpeg':
118: case 'jpg':
119: return imagejpeg($im, $filename, $qual);
120: case 'gif':
121: return imagegif($im, $filename);
122: }
123: return false;
124: }
125:
126: /**
127: * Creates a true color image
128: *
129: * @param int $w the width of the image
130: * @param int $h the height of the image
131: * @return image
132: */
133: function zp_createImage($w, $h) {
134: return imagecreatetruecolor($w, $h);
135: }
136:
137: /**
138: * Fills an image area
139: *
140: * @param image $image
141: * @param int $x
142: * @param int $y
143: * @param color $color
144: * @return bool
145: */
146: function zp_imageFill($image, $x, $y, $color) {
147: return imagefill($image, $x, $y, $color);
148: }
149:
150: /**
151: * Sets the transparency color
152: *
153: * @param image $image
154: * @param color $color
155: * @return bool
156: */
157: function zp_imageColorTransparent($image, $color) {
158: return imagecolortransparent($image, $color);
159: }
160:
161: /**
162: * copies an image canvas
163: *
164: * @param image $imgCanvas source canvas
165: * @param image $img destination canvas
166: * @param int $dest_x destination x
167: * @param int $dest_y destination y
168: * @param int $src_x source x
169: * @param int $src_y source y
170: * @param int $w width
171: * @param int $h height
172: */
173: function zp_copyCanvas($imgCanvas, $img, $dest_x, $dest_y, $src_x, $src_y, $w, $h) {
174: return imageCopy($imgCanvas, $img, $dest_x, $dest_y, $src_x, $src_y, $w, $h);
175: }
176:
177: /**
178: * resamples an image to a new copy
179: *
180: * @param resource $dst_image
181: * @param resource $src_image
182: * @param int $dst_x
183: * @param int $dst_y
184: * @param int $src_x
185: * @param int $src_y
186: * @param int $dst_w
187: * @param int $dst_h
188: * @param int $src_w
189: * @param int $src_h
190: * @return bool
191: */
192: function zp_resampleImage($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) {
193: return imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
194: }
195:
196: /**
197: * Sharpens an image using an Unsharp Mask filter.
198: *
199: * Original description from the author:
200: *
201: * WARNING ! Due to a known bug in PHP 4.3.2 this script is not working well in this
202: * version. The sharpened images get too dark. The bug is fixed in version 4.3.3.
203: *
204: * From version 2 (July 17 2006) the script uses the imageconvolution function in
205: * PHP version >= 5.1, which improves the performance considerably.
206: *
207: * Unsharp masking is a traditional darkroom technique that has proven very
208: * suitable for digital imaging. The principle of unsharp masking is to create a
209: * blurred copy of the image and compare it to the underlying original. The
210: * difference in colour values between the two images is greatest for the pixels
211: * near sharp edges. When this difference is subtracted from the original image,
212: * the edges will be accentuated.
213: *
214: * The Amount parameter simply says how much of the effect you want. 100 is
215: * 'normal'. Radius is the radius of the blurring circle of the mask. 'Threshold'
216: * is the least difference in colour values that is allowed between the original
217: * and the mask. In practice this means that low-contrast areas of the picture are
218: * left unrendered whereas edges are treated normally. This is good for pictures of
219: * e.g. skin or blue skies.
220: *
221: * Any suggenstions for improvement of the algorithm, expecially regarding the
222: * speed and the roundoff errors in the Gaussian blur process, are welcome.
223: *
224: * Permission to license this code under the GPL was granted by the author on 2/12/2007.
225: *
226: * @param image $img the GD format image to sharpen. This is not a URL string, but
227: * should be the result of a GD image function.
228: * @param int $amount the strength of the sharpening effect. Nominal values are between 0 and 100.
229: * @param int $radius the pixel radius of the sharpening mask. A smaller radius sharpens smaller
230: * details, and a larger radius sharpens larger details.
231: * @param int $threshold the color difference threshold required for sharpening. A low threshold
232: * sharpens all edges including faint ones, while a higher threshold only sharpens more distinct edges.
233: * @return image the input image with the specified sharpening applied.
234: */
235: function zp_imageUnsharpMask($img, $amount, $radius, $threshold) {
236: /*
237: Unsharp Mask for PHP - version 2.0
238: Unsharp mask algorithm by Torstein Hønsi 2003-06.
239: Please leave this notice.
240: */
241:
242: // $img is an image that is already created within php using
243: // imgcreatetruecolor. No url! $img must be a truecolor image.
244: // Attempt to calibrate the parameters to Photoshop:
245: if ($amount > 500)
246: $amount = 500;
247: $amount = $amount * 0.016;
248: if ($radius > 50)
249: $radius = 50;
250: $radius = $radius * 2;
251: if ($threshold > 255)
252: $threshold = 255;
253:
254: $radius = abs(round($radius)); // Only integers make sense.
255: if ($radius == 0)
256: return $img;
257: $w = imagesx($img);
258: $h = imagesy($img);
259: $imgCanvas = imagecreatetruecolor($w, $h);
260: $imgCanvas2 = imagecreatetruecolor($w, $h);
261: imagecopy($imgCanvas, $img, 0, 0, 0, 0, $w, $h);
262: imagecopy($imgCanvas2, $img, 0, 0, 0, 0, $w, $h);
263:
264: imageBlurGD($imgCanvas, $imgCanvas2, $radius, $w, $h);
265:
266: // Calculate the difference between the blurred pixels and the original
267: // and set the pixels
268: for ($x = 0; $x < $w; $x++) { // each row
269: for ($y = 0; $y < $h; $y++) { // each pixel
270: $rgbOrig = ImageColorAt($imgCanvas2, $x, $y);
271: $rOrig = (($rgbOrig >> 16) & 0xFF);
272: $gOrig = (($rgbOrig >> 8) & 0xFF);
273: $bOrig = ($rgbOrig & 0xFF);
274:
275: $rgbBlur = ImageColorAt($imgCanvas, $x, $y);
276:
277: $rBlur = (($rgbBlur >> 16) & 0xFF);
278: $gBlur = (($rgbBlur >> 8) & 0xFF);
279: $bBlur = ($rgbBlur & 0xFF);
280:
281: // When the masked pixels differ less from the original
282: // than the threshold specifies, they are set to their original value.
283: $rNew = (abs($rOrig - $rBlur) >= $threshold) ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig)) : $rOrig;
284: $gNew = (abs($gOrig - $gBlur) >= $threshold) ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig)) : $gOrig;
285: $bNew = (abs($bOrig - $bBlur) >= $threshold) ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig)) : $bOrig;
286:
287: if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
288: $pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
289: ImageSetPixel($img, $x, $y, $pixCol);
290: }
291: }
292: }
293: return $img;
294: }
295:
296: /**
297: * Resize a PNG file with transparency to given dimensions
298: * and still retain the alpha channel information
299: * Author: Alex Le - http://www.alexle.net
300: *
301: *
302: * @param image $src
303: * @param int $w
304: * @param int $h
305: * @return image
306: */
307: function zp_imageResizeAlpha(&$src, $w, $h) {
308: /* create a new image with the new width and height */
309: if ($temp = @imagecreatetruecolor($w, $h)) {
310:
311: /* making the new image transparent */
312: $background = imagecolorallocate($temp, 0, 0, 0);
313: imagecolortransparent($temp, $background); // make the new temp image all transparent
314: imagealphablending($temp, false); // turn off the alpha blending to keep the alpha channel
315:
316: /* Resize the PNG file */
317: /* use imagecopyresized to gain some performance but loose some quality */
318: imagecopyresampled($temp, $src, 0, 0, 0, 0, $w, $h, imagesx($src), imagesy($src));
319: /* use imagecopyresampled if you concern more about the quality */
320: //imagecopyresampled($temp, $src, 0, 0, 0, 0, $w, $h, imagesx($src), imagesy($src));
321: }
322: return $temp;
323: }
324:
325: /**
326: * Returns true if GD library is configued with image rotation suppord
327: *
328: * @return bool
329: */
330: function zp_imageCanRotate() {
331: return function_exists('imagerotate');
332: }
333:
334: /**
335: * Rotates an image resource according to its Orientation
336: * NB: requires the imagarotate function to be configured
337: *
338: * @param resource $im
339: * @param int $rotate
340: * @return resource
341: */
342: function zp_rotateImage($im, $rotate) {
343: $newim_rot = imagerotate($im, $rotate, 0);
344: imagedestroy($im);
345: return $newim_rot;
346: }
347:
348: /**
349: * Returns the image height and width
350: *
351: * @param string $filename
352: * @return array
353: */
354: function zp_imageDims($filename) {
355: $imageinfo = NULL;
356: $rslt = getimagesize($filename, $imageinfo);
357: if (is_array($rslt)) {
358: return array('width' => $rslt[0], 'height' => $rslt[1]);
359: } else {
360: return false;
361: }
362: }
363:
364: /**
365: * Returns the IPTC data of an image
366: *
367: * @param string $filename
368: * @return string
369: */
370: function zp_imageIPTC($filename) {
371: $imageinfo = NULL;
372: $rslt = getimagesize($filename, $imageinfo);
373: if (is_array($rslt) && isset($imageinfo['APP13'])) {
374: return $imageinfo['APP13'];
375: } else {
376: return false;
377: }
378: }
379:
380: /**
381: * Returns the width of an image resource
382: *
383: * @param resource $im
384: * @return int
385: */
386: function zp_imageWidth($im) {
387: return imagesx($im);
388: }
389:
390: /**
391: * Returns the height of an image resource
392: *
393: * @param resource $im
394: * @return int
395: */
396: function zp_imageHeight($im) {
397: return imagesy($im);
398: }
399:
400: /**
401: * Does a copy merge of two image resources
402: *
403: * @param resource $dst_im
404: * @param resource $src_im
405: * @param int $dst_x
406: * @param int $dst_y
407: * @param int $src_x
408: * @param int $src_y
409: * @param int $src_w
410: * @param int $src_h
411: * @param int $pct
412: * @return bool
413: */
414: function zp_imageMerge($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct) {
415: return imagecopymerge($dst_im, $src_im, $dst_x, $dst_y, $src_x, $src_y, $src_w, $src_h, $pct);
416: }
417:
418: /**
419: * Creates a grayscale image
420: *
421: * @param resource $image
422: * @return resource
423: */
424: function zp_imageGray($image) {
425: $img_height = imagesy($image);
426: $img_width = imagesx($image);
427: for ($y = 0; $y < $img_height; $y++) {
428: for ($x = 0; $x < $img_width; $x++) {
429: $gray = (ImageColorAt($image, $x, $y) >> 8) & 0xFF;
430: imagesetpixel($image, $x, $y, ImageColorAllocate($image, $gray, $gray, $gray));
431: }
432: }
433: }
434:
435: /**
436: * destroys an image resource
437: *
438: * @param resource $im
439: * @return bool
440: */
441: function zp_imageKill($im) {
442: return imagedestroy($im);
443: }
444:
445: /**
446: * Returns an RGB color identifier
447: *
448: * @param resource $image
449: * @param int $red
450: * @param int $green
451: * @param int $blue
452: * @return int
453: */
454: function zp_colorAllocate($image, $red, $green, $blue) {
455: return imagecolorallocate($image, $red, $green, $blue);
456: }
457:
458: /**
459: * Rencers a string into the image
460: *
461: * @param resource $image
462: * @param int $font
463: * @param int $x
464: * @param int $y
465: * @param string $string
466: * @param int $color
467: * @return bool
468: */
469: function zp_writeString($image, $font, $x, $y, $string, $color) {
470: global $_gd_freetype_fonts;
471: if ($font > 0) {
472: return imagestring($image, $font, $x, $y, $string, $color);
473: } else {
474: $font = abs($font);
475: $fontfile = $_gd_freetype_fonts[$font]['path'];
476: $size = $_gd_freetype_fonts[abs($font)]['size'];
477: $bbox = imagettfbbox($_gd_freetype_fonts[$font]['size'], 0, $_gd_freetype_fonts[$font]['path'], GD_FREETYPE_SAMPLE);
478: $w = (int) (($bbox[2] - $bbox[0]) / GD_FREETYPE_SAMPLE_CHARS);
479: $h = $bbox[1] - $bbox[7];
480: $rslt = imagettftext($image, $size, 0, $x + $w, $y + $h, $color, $fontfile, $string);
481: return is_array($rslt);
482: }
483: }
484:
485: /**
486: * Creates a rectangle
487: *
488: * @param resource $image
489: * @param int $x1
490: * @param int $y1
491: * @param int $x2
492: * @param int $y2
493: * @param int $color
494: * @return bool
495: */
496: function zp_drawRectangle($image, $x1, $y1, $x2, $y2, $color) {
497: return imagerectangle($image, $x1, $y1, $x2, $y2, $color);
498: }
499:
500: /**
501: * Returns array of graphics library info
502: *
503: * @return array
504: */
505: function zp_graphicsLibInfo() {
506: global $_lib_GD_info;
507: return $_lib_GD_info;
508: }
509:
510: /**
511: * Returns a list of available fonts
512: *
513: * @return array
514: */
515: function zp_getFonts() {
516: global $_gd_fontlist;
517: if (!is_array($_gd_fontlist)) {
518: $_gd_fontlist = array('system' => '');
519: $curdir = getcwd();
520: $basefile = SERVERPATH . '/' . USER_PLUGIN_FOLDER . 'gd_fonts/';
521: if (is_dir($basefile)) {
522: chdir($basefile);
523: $filelist = safe_glob('*.gdf');
524: foreach ($filelist as $file) {
525: $key = filesystemToInternal(str_replace('.gdf', '', $file));
526: $_gd_fontlist[$key] = $basefile . '/' . $file;
527: }
528: }
529: chdir($basefile = SERVERPATH . '/' . ZENFOLDER . '/gd_fonts');
530: $filelist = safe_glob('*.gdf');
531: foreach ($filelist as $file) {
532: $key = filesystemToInternal(preg_replace('/\.gdf/i', '', $file));
533: $_gd_fontlist[$key] = $basefile . '/' . $file;
534: }
535: if (GD_FREETYPE) {
536: $basefile = rtrim(getOption('GD_FreeType_Path') . '/');
537: if (is_dir($basefile)) {
538: chdir($basefile);
539: $filelist = safe_glob('*.ttf');
540: foreach ($filelist as $file) {
541: $key = filesystemToInternal($file);
542: $_gd_fontlist[$key] = $basefile . '/' . $file;
543: }
544: }
545: }
546: chdir($curdir);
547: }
548: return $_gd_fontlist;
549: }
550:
551: /**
552: * Loads a font and returns its font id
553: *
554: * @param string $font
555: * @return int
556: */
557: function zp_imageLoadFont($font = NULL, $size = 18) {
558: global $_gd_freetype_fonts;
559: if (!empty($font)) {
560: if (file_exists($font)) {
561: switch (getSuffix($font)) {
562: case 'gdf':
563: return imageloadfont($font);
564: case 'ttf':
565: $index = -count($_gd_freetype_fonts);
566: array_push($_gd_freetype_fonts, array('path' => $font, 'size' => $size));
567: return $index;
568: }
569: }
570: }
571: return 5; // default to the largest inbuilt font
572: }
573:
574: /**
575: * Returns the font width in pixels
576: *
577: * @param int $font
578: * @return int
579: */
580: function zp_imageFontWidth($font) {
581: global $_gd_freetype_fonts;
582: if ($font > 0) {
583: return imagefontwidth($font);
584: } else {
585: $font = abs($font);
586: $bbox = imagettfbbox($_gd_freetype_fonts[$font]['size'], 0, $_gd_freetype_fonts[$font]['path'], GD_FREETYPE_SAMPLE);
587: $w = (int) (($bbox[2] - $bbox[0]) / GD_FREETYPE_SAMPLE_CHARS);
588: return $w;
589: }
590: }
591:
592: /**
593: * Returns the font height in pixels
594: *
595: * @param int $font
596: * @return int
597: */
598: function zp_imageFontHeight($font) {
599: global $_gd_freetype_fonts;
600: if ($font > 0) {
601: return imagefontheight($font);
602: } else {
603: $font = abs($font);
604: $bbox = imagettfbbox($_gd_freetype_fonts[$font]['size'], 0, $_gd_freetype_fonts[$font]['path'], GD_FREETYPE_SAMPLE);
605: $h = $bbox[1] - $bbox[7];
606: return $h;
607: }
608: }
609:
610: /**
611: * provides image blur support for lib-GD:zp_imageUnsharpMask
612: *
613: * @param image $imgCanvas
614: * @param int $radius
615: * @param int $w
616: * @param int $h
617: */
618: function imageBlurGD($imgCanvas, $imgCanvas2, $radius, $w, $h) {
619: // Gaussian blur matrix:
620: // 1 2 1
621: // 2 4 2
622: // 1 2 1
623: //////////////////////////////////////////////////
624: for ($i = 0; $i < $radius; $i++) {
625: if (function_exists('imageconvolution')) { // PHP >= 5.1
626: $matrix = array(
627: array(1, 2, 1),
628: array(2, 4, 2),
629: array(1, 2, 1)
630: );
631: imageconvolution($imgCanvas, $matrix, 16, 0);
632: }
633: }
634: }
635:
636: /**
637: *
638: * creates an image from an image stream
639: * @param $string
640: */
641: function zp_imageFromString($string) {
642: return imagecreatefromstring($string);
643: }
644:
645: }
646: }
647: ?>