Simpletest Coverage - includes/image.inc

1 <?php
2 // $Id: image.inc,v 1.33 2009/04/20 20:02:30 dries Exp $
3
4 /**
5 * @file
6 * API for manipulating images.
7 */
8
9 /**
10 * @defgroup image Image toolkits
11 * @{
12 * Drupal's image toolkits provide an abstraction layer for common image file
13 * manipulations like scaling, cropping, and rotating. The abstraction frees
14 * module authors from the need to support multiple image libraries, and it
15 * allows site administrators to choose the library that's best for them.
16 *
17 * PHP includes the GD library by default so a GD toolkit is installed with
18 * Drupal. Other toolkits like ImageMagic are available from contrib modules.
19 * GD works well for small images, but using it with larger files may cause PHP
20 * to run out of memory. In contrast the ImageMagick library does not suffer
21 * from this problem, but it requires the ISP to have installed additional
22 * software.
23 *
24 * Image toolkits are discovered based on the associated module's
25 * hook_image_toolkits. Additionally the image toolkit include file
26 * must be identified in the files array in the module.info file. The
27 * toolkit must then be enabled using the admin/settings/image-toolkit
28 * form.
29 *
30 * Only one toolkit may be selected at a time. If a module author wishes to call
31 * a specific toolkit they can check that it is installed by calling
32 * image_get_available_toolkits(), and then calling its functions directly.
33 */
34
35 /**
36 * Return a list of available toolkits.
37 *
38 * @return
39 * An array with the toolkit names as keys and the descriptions as values.
40 */
41 function image_get_available_toolkits() {
42 // hook_image_toolkits returns an array of toolkit names.
43 $toolkits = module_invoke_all('image_toolkits');
44
45 $output = array();
46 foreach ($toolkits as $name => $info) {
47 // Only allow modules that aren't marked as unavailable.
48 if ($info['available']) {
49 $output[$name] = $info['title'];
50 }
51 }
52
53 return $output;
54 }
55
56 /**
57 * Retrieve the name of the currently used toolkit.
58 *
59 * @return
60 * String containing the name of the selected toolkit, or FALSE on error.
61 */
62 function image_get_toolkit() {
63 static $toolkit;
64
65 if (!isset($toolkit)) {
66 $toolkits = image_get_available_toolkits();
67 $toolkit = variable_get('image_toolkit', 'gd');
68 if (!isset($toolkits[$toolkit]) || !drupal_function_exists('image_' . $toolkit . '_load')) {
69 // The selected toolkit isn't available so return the first one found. If
70 // none are available this will return FALSE.
71 reset($toolkits);
72 $toolkit = key($toolkits);
73 }
74 }
75
76 return $toolkit;
77 }
78
79 /**
80 * Invokes the given method using the currently selected toolkit.
81 *
82 * @param $method
83 * A string containing the method to invoke.
84 * @param $image
85 * An image object returned by image_load().
86 * @param $params
87 * An optional array of parameters to pass to the toolkit method.
88 * @return
89 * Mixed values (typically Boolean indicating successful operation).
90 */
91 function image_toolkit_invoke($method, stdClass $image, array $params = array()) {
92 $function = 'image_' . $image->toolkit . '_' . $method;
93 if (drupal_function_exists($function)) {
94 array_unshift($params, $image);
95 return call_user_func_array($function, $params);
96 }
97 watchdog('image', 'The selected image handling toolkit %toolkit can not correctly process %function.', array('%toolkit' => $image->toolkit, '%function' => $function), WATCHDOG_ERROR);
98 return FALSE;
99 }
100
101 /**
102 * Get details about an image.
103 *
104 * Drupal only supports GIF, JPG and PNG file formats.
105 *
106 * @param $filepath
107 * String specifying the path of the image file.
108 * @return
109 * FALSE, if the file could not be found or is not an image. Otherwise, a
110 * keyed array containing information about the image:
111 * 'width' - Width, in pixels.
112 * 'height' - Height, in pixels.
113 * 'extension' - Commonly used file extension for the image.
114 * 'mime_type' - MIME type ('image/jpeg', 'image/gif', 'image/png').
115 * 'file_size' - File size in bytes.
116 */
117 function image_get_info($filepath) {
118 if (!is_file($filepath)) {
119 return FALSE;
120 }
121
122 $details = FALSE;
123 $data = @getimagesize($filepath);
124 $file_size = @filesize($filepath);
125
126 if (isset($data) && is_array($data)) {
127 $extensions = array('1' => 'gif', '2' => 'jpg', '3' => 'png');
128 $extension = array_key_exists($data[2], $extensions) ? $extensions[$data[2]] : '';
129 $details = array('width' => $data[0],
130 'height' => $data[1],
131 'extension' => $extension,
132 'file_size' => $file_size,
133 'mime_type' => $data['mime']);
134 }
135
136 return $details;
137 }
138
139 /**
140 * Scales an image to the exact width and height given.
141 *
142 * This function achieves the target aspect ratio by cropping the original image
143 * equally on both sides, or equally on the top and bottom. This function is
144 * useful to create uniform sized avatars from larger images.
145 *
146 * The resulting image always has the exact target dimensions.
147 *
148 * @param $image
149 * An image object returned by image_load().
150 * @param $width
151 * The target width, in pixels.
152 * @param $height
153 * The target height, in pixels.
154 * @return
155 * TRUE or FALSE, based on success.
156 *
157 * @see image_load()
158 * @see image_resize()
159 * @see image_crop()
160 */
161 function image_scale_and_crop(stdClass $image, $width, $height) {
162 $scale = max($width / $image->info['width'], $height / $image->info['height']);
163 $x = ($image->info['width'] * $scale - $width) / 2;
164 $y = ($image->info['height'] * $scale - $height) / 2;
165
166 if (image_resize($image, $image->info['width'] * $scale, $image->info['height'] * $scale)) {
167 return image_crop($image, $x, $y, $width, $height);
168 }
169 return FALSE;
170 }
171
172 /**
173 * Scales an image to the given width and height while maintaining aspect ratio.
174 *
175 * The resulting image can be smaller for one or both target dimensions.
176 *
177 * @param $image
178 * An image object returned by image_load().
179 * @param $width
180 * The target width, in pixels. This value is omitted then the scaling will
181 * based only on the height value.
182 * @param $height
183 * The target height, in pixels. This value is omitted then the scaling will
184 * based only on the width value.
185 * @param $upscale
186 * Boolean indicating that files smaller than the dimensions will be scalled
187 * up. This generally results in a low quality image.
188 * @return
189 * TRUE or FALSE, based on success.
190 *
191 * @see image_load()
192 * @see image_scale_and_crop()
193 */
194 function image_scale(stdClass $image, $width = NULL, $height = NULL, $upscale = FALSE) {
195 $aspect = $image->info['height'] / $image->info['width'];
196
197 if ($upscale) {
198 // Set width/height according to aspect ratio if either is empty.
199 $width = !empty($width) ? $width : $height / $aspect;
200 $height = !empty($height) ? $height : $width / $aspect;
201 }
202 else {
203 // Set impossibly large values if the width and height aren't set.
204 $width = !empty($width) ? $width : 9999999;
205 $height = !empty($height) ? $height : 9999999;
206
207 // Don't scale up.
208 if (round($width) >= $image->info['width'] && round($height) >= $image->info['height']) {
209 return TRUE;
210 }
211 }
212
213 if ($aspect < $height / $width) {
214 $height = $width * $aspect;
215 }
216 else {
217 $width = $height / $aspect;
218 }
219
220 return image_resize($image, $width, $height);
221 }
222
223 /**
224 * Resize an image to the given dimensions (ignoring aspect ratio).
225 *
226 * @param $image
227 * An image object returned by image_load().
228 * @param $width
229 * The target width, in pixels.
230 * @param $height
231 * The target height, in pixels.
232 * @return
233 * TRUE or FALSE, based on success.
234 *
235 * @see image_load()
236 * @see image_gd_resize()
237 */
238 function image_resize(stdClass $image, $width, $height) {
239 $width = (int) round($width);
240 $height = (int) round($height);
241
242 return image_toolkit_invoke('resize', $image, array($width, $height));
243 }
244
245 /**
246 * Rotate an image by the given number of degrees.
247 *
248 * @param $image
249 * An image object returned by image_load().
250 * @param $degrees
251 * The number of (clockwise) degrees to rotate the image.
252 * @param $background
253 * An hexadecimal integer specifying the background color to use for the
254 * uncovered area of the image after the rotation. E.g. 0x000000 for black,
255 * 0xff00ff for magenta, and 0xffffff for white. For images that support
256 * transparency, this will default to transparent. Otherwise it will
257 * be white.
258 * @return
259 * TRUE or FALSE, based on success.
260 *
261 * @see image_load()
262 * @see image_gd_rotate()
263 */
264 function image_rotate(stdClass $image, $degrees, $background = NULL) {
265 return image_toolkit_invoke('rotate', $image, array($degrees, $background));
266 }
267
268 /**
269 * Crop an image to the rectangle specified by the given rectangle.
270 *
271 * @param $image
272 * An image object returned by image_load().
273 * @param $x
274 * The top left coordinate, in pixels, of the crop area (x axis value).
275 * @param $y
276 * The top left coordinate, in pixels, of the crop area (y axis value).
277 * @param $width
278 * The target width, in pixels.
279 * @param $height
280 * The target height, in pixels.
281 * @return
282 * TRUE or FALSE, based on success.
283 *
284 * @see image_load()
285 * @see image_scale_and_crop()
286 * @see image_gd_crop()
287 */
288 function image_crop(stdClass $image, $x, $y, $width, $height) {
289 $aspect = $image->info['height'] / $image->info['width'];
290 if (empty($height)) $height = $width / $aspect;
291 if (empty($width)) $width = $height * $aspect;
292
293 $width = (int) round($width);
294 $height = (int) round($height);
295
296 return image_toolkit_invoke('crop', $image, array($x, $y, $width, $height));
297 }
298
299 /**
300 * Convert an image to grayscale.
301 *
302 * @param $image
303 * An image object returned by image_load().
304 * @return
305 * TRUE or FALSE, based on success.
306 *
307 * @see image_load()
308 * @see image_gd_desaturate()
309 */
310 function image_desaturate(stdClass $image) {
311 return image_toolkit_invoke('desaturate', $image);
312 }
313
314
315 /**
316 * Load an image file and return an image object.
317 *
318 * Any changes to the file are not saved until image_save() is called.
319 *
320 * @param $file
321 * Path to an image file.
322 * @param $toolkit
323 * An optional, image toolkit name to override the default.
324 * @return
325 * An image object or FALSE if there was a problem loading the file. The
326 * image object has the following properties:
327 * - 'source' - The original file path.
328 * - 'info' - The array of information returned by image_get_info()
329 * - 'toolkit' - The name of the image toolkit requested when the image was
330 * loaded.
331 * Image tookits may add additional properties. The caller is advised not to
332 * monkey about with them.
333 *
334 * @see image_save()
335 * @see image_get_info()
336 * @see image_get_available_toolkits()
337 * @see image_gd_load()
338 */
339 function image_load($file, $toolkit = FALSE) {
340 if (!$toolkit) {
341 $toolkit = image_get_toolkit();
342 }
343 if ($toolkit) {
344 $image = new stdClass();
345 $image->source = $file;
346 $image->info = image_get_info($file);
347 $image->toolkit = $toolkit;
348 if (image_toolkit_invoke('load', $image)) {
349 return $image;
350 }
351 }
352 return FALSE;
353 }
354
355 /**
356 * Close the image and save the changes to a file.
357 *
358 * @param $image
359 * An image object returned by image_load(). The object's 'info' property
360 * will be updated if the file is saved successfully.
361 * @param $destination
362 * Destination path where the image should be saved. If it is empty the
363 * original image file will be overwritten.
364 * @return
365 * TRUE or FALSE, based on success.
366 *
367 * @see image_load()
368 * @see image_gd_save()
369 */
370 function image_save(stdClass $image, $destination = NULL) {
371 if (empty($destination)) {
372 $destination = $image->source;
373 }
374 if ($return = image_toolkit_invoke('save', $image, array($destination))) {
375 // Clear the cached file size and refresh the image information.
376 clearstatcache();
377 $image->info = image_get_info($destination);
378
379 if (drupal_chmod($destination)) {
380 return $return;
381 }
382 }
383 return FALSE;
384 }
385
386 /**
387 * @} End of "defgroup image".
388 */
389

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.