Total Complexity | 70 |
Total Lines | 550 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like Image_Transform_Driver_GD often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Image_Transform_Driver_GD, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
65 | class Image_Transform_Driver_GD extends Image_Transform |
||
66 | { |
||
67 | /** |
||
68 | * Holds the image resource for manipulation |
||
69 | * |
||
70 | * @var resource $imageHandle |
||
71 | * @access protected |
||
72 | */ |
||
73 | public $imageHandle = null; |
||
74 | |||
75 | /** |
||
76 | * Holds the original image file |
||
77 | * |
||
78 | * @var resource $imageHandle |
||
79 | * @access protected |
||
80 | */ |
||
81 | public $oldImage = null; |
||
82 | |||
83 | /** |
||
84 | * Check settings |
||
85 | */ |
||
86 | public function Image_Transform_Driver_GD() |
||
87 | { |
||
88 | $this->__construct(); |
||
89 | } // End function Image |
||
90 | |||
91 | /** |
||
92 | * Check settings |
||
93 | * |
||
94 | * @since PHP 5 |
||
95 | */ |
||
96 | public function __construct() |
||
97 | { |
||
98 | if (!PEAR::loadExtension('gd')) { |
||
99 | $this->isError(PEAR::raiseError("GD library is not available.", IMAGE_TRANSFORM_ERROR_UNSUPPORTED)); |
||
100 | } else { |
||
101 | $types = ImageTypes(); |
||
102 | if ($types & IMG_PNG) { |
||
103 | $this->_supported_image_types['png'] = 'rw'; |
||
104 | } |
||
105 | if (($types & IMG_GIF) |
||
106 | || function_exists('imagegif')) { |
||
107 | $this->_supported_image_types['gif'] = 'rw'; |
||
108 | } elseif (function_exists('imagecreatefromgif')) { |
||
109 | $this->_supported_image_types['gif'] = 'rw'; |
||
110 | } |
||
111 | if ($types & IMG_JPG) { |
||
112 | $this->_supported_image_types['jpeg'] = 'rw'; |
||
113 | } |
||
114 | if ($types & IMG_WBMP) { |
||
115 | $this->_supported_image_types['wbmp'] = 'rw'; |
||
116 | } |
||
117 | if (!$this->_supported_image_types) { |
||
118 | $this->isError(PEAR::raiseError("No supported image types available", IMAGE_TRANSFORM_ERROR_UNSUPPORTED)); |
||
119 | } |
||
120 | } |
||
121 | } // End function Image |
||
122 | |||
123 | /** |
||
124 | * Loads an image from file |
||
125 | * |
||
126 | * @param string $image filename |
||
127 | * @return bool|PEAR_Error TRUE or a PEAR_Error object on error |
||
128 | * @access public |
||
129 | */ |
||
130 | public function load($image) |
||
131 | { |
||
132 | $this->free(); |
||
133 | |||
134 | $this->image = $image; |
||
135 | $result = $this->_get_image_details($image); |
||
136 | if (PEAR::isError($result)) { |
||
137 | return $result; |
||
138 | } |
||
139 | if (!$this->supportsType($this->type, 'r')) { |
||
140 | return PEAR::raiseError('Image type not supported for input', IMAGE_TRANSFORM_ERROR_UNSUPPORTED); |
||
141 | } |
||
142 | |||
143 | $functionName = 'ImageCreateFrom' . $this->type; |
||
144 | $this->imageHandle = $functionName($this->image); |
||
145 | if (!$this->imageHandle) { |
||
146 | $this->imageHandle = null; |
||
147 | |||
148 | return PEAR::raiseError('Error while loading image file.', IMAGE_TRANSFORM_ERROR_IO); |
||
149 | } |
||
150 | |||
151 | return true; |
||
152 | } // End load |
||
153 | |||
154 | /** |
||
155 | * Returns the GD image handle |
||
156 | * |
||
157 | * @return resource |
||
158 | * |
||
159 | * @access public |
||
160 | */ |
||
161 | public function getHandle() |
||
162 | { |
||
163 | return $this->imageHandle; |
||
164 | }//function getHandle() |
||
165 | |||
166 | /** |
||
167 | * Adds a border of constant width around an image |
||
168 | * |
||
169 | * @param int $border_width Width of border to add |
||
170 | * @author Peter Bowyer |
||
171 | * @return bool TRUE |
||
172 | * @access public |
||
173 | */ |
||
174 | public function addBorder($border_width=null, $color = '') |
||
175 | { |
||
176 | $this->new_x = $this->img_x + 2 * $border_width; |
||
177 | $this->new_y = $this->img_y + 2 * $border_width; |
||
178 | |||
179 | $new_img = $this->_createImage($new_x, $new_y, $this->true_color); |
||
180 | |||
181 | $options = array('pencilColor', $color); |
||
182 | $color = $this->_getColor('pencilColor', $options, array(0, 0, 0)); |
||
183 | if ($color) { |
||
184 | if ($this->true_color) { |
||
185 | $c = imagecolorresolve($this->imageHandle, $color[0], $color[1], $color[2]); |
||
186 | imagefill($new_img, 0, 0, $c); |
||
187 | } else { |
||
188 | imagecolorset($new_img, imagecolorat($new_img, 0, 0), $color[0], $color[1], $color[2]); |
||
189 | } |
||
190 | } |
||
191 | ImageCopy($new_img, $this->imageHandle, $border_width, $border_width, 0, 0, $this->img_x, $this->img_y); |
||
192 | $this->imageHandle = $new_img; |
||
193 | $this->resized = true; |
||
194 | |||
195 | return true; |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * addText |
||
200 | * |
||
201 | * @param array $params Array contains options |
||
202 | * array( |
||
203 | * 'text' The string to draw |
||
204 | * 'x' Horizontal position |
||
205 | * 'y' Vertical Position |
||
206 | * 'color' Font color |
||
207 | * 'font' Font to be used |
||
208 | * 'size' Size of the fonts in pixel |
||
209 | * 'resize_first' Tell if the image has to be resized |
||
210 | * before drawing the text |
||
211 | * ) |
||
212 | * |
||
213 | * @return bool|PEAR_Error TRUE or a PEAR_Error object on error |
||
214 | */ |
||
215 | public function addText($params=null) |
||
216 | { |
||
217 | $this->oldImage = $this->imageHandle; |
||
218 | $params = array_merge($this->_get_default_text_params(), $params); |
||
219 | extract($params); |
||
220 | |||
221 | $options = array('fontColor' => $color); |
||
222 | $color = $this->_getColor('fontColor', $options, array(0, 0, 0)); |
||
223 | |||
224 | $c = imagecolorresolve($this->imageHandle, $color[0], $color[1], $color[2]); |
||
225 | |||
226 | if ('ttf' == substr($font, -3)) { |
||
227 | ImageTTFText($this->imageHandle, $size, $angle, $x, $y, $c, $font, $text); |
||
228 | } else { |
||
229 | ImagePSText($this->imageHandle, $size, $angle, $x, $y, $c, $font, $text); |
||
230 | } |
||
231 | |||
232 | return true; |
||
233 | } // End addText |
||
234 | |||
235 | /** |
||
236 | * Rotates image by the given angle |
||
237 | * |
||
238 | * Uses a fast rotation algorythm for custom angles |
||
239 | * or lines copy for multiple of 90 degrees |
||
240 | * |
||
241 | * @param int $angle Rotation angle |
||
242 | * @param array $options array( |
||
243 | * 'canvasColor' => array(r ,g, b), named color or #rrggbb |
||
244 | * ) |
||
245 | * @author Pierre-Alain Joye |
||
246 | * @return bool|PEAR_Error TRUE or a PEAR_Error object on error |
||
247 | * @access public |
||
248 | */ |
||
249 | public function rotate($angle, $options = null) |
||
250 | { |
||
251 | if (0 == ($angle % 360)) { |
||
252 | return true; |
||
253 | } |
||
254 | |||
255 | $color_mask = $this->_getColor('canvasColor', $options, array(255, 255, 255)); |
||
256 | |||
257 | $mask = imagecolorresolve($this->imageHandle, $color_mask[0], $color_mask[1], $color_mask[2]); |
||
258 | |||
259 | $this->oldImage = $this->imageHandle; |
||
260 | |||
261 | // Multiply by -1 to change the sign, so the image is rotated clockwise |
||
262 | $this->imageHandle = ImageRotate($this->imageHandle, $angle * -1, $mask); |
||
263 | |||
264 | return true; |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * Horizontal mirroring |
||
269 | * |
||
270 | * @return mixed TRUE or PEAR_Error object on error |
||
271 | * @access public |
||
272 | * @see flip() |
||
273 | **/ |
||
274 | public function mirror() |
||
275 | { |
||
276 | $new_img = $this->_createImage(); |
||
277 | for ($x = 0; $x < $this->new_x; ++$x) { |
||
278 | imagecopy($new_img, $this->imageHandle, $x, 0, $this->new_x - $x - 1, 0, 1, $this->new_y); |
||
279 | } |
||
280 | imagedestroy($this->imageHandle); |
||
281 | $this->imageHandle = $new_img; |
||
282 | |||
283 | return true; |
||
284 | } |
||
285 | |||
286 | /** |
||
287 | * Vertical mirroring |
||
288 | * |
||
289 | * @return TRUE or PEAR Error object on error |
||
290 | * @access public |
||
291 | * @see mirror() |
||
292 | **/ |
||
293 | public function flip() |
||
294 | { |
||
295 | $new_img = $this->_createImage(); |
||
296 | for ($y = 0; $y < $this->new_y; ++$y) { |
||
297 | imagecopy($new_img, $this->imageHandle, 0, $y, 0, $this->new_y - $y - 1, $this->new_x, 1); |
||
298 | } |
||
299 | imagedestroy($this->imageHandle); |
||
300 | $this->imageHandle = $new_img; |
||
301 | |||
302 | /* for very large images we may want to use the following |
||
303 | Needs to find out what is the threshhold |
||
304 | for ($x = 0; $x < $this->new_x; ++$x) { |
||
305 | for ($y1 = 0; $y1 < $this->new_y / 2; ++$y1) { |
||
306 | $y2 = $this->new_y - 1 - $y1; |
||
307 | $color1 = imagecolorat($this->imageHandle, $x, $y1); |
||
308 | $color2 = imagecolorat($this->imageHandle, $x, $y2); |
||
309 | imagesetpixel($this->imageHandle, $x, $y1, $color2); |
||
310 | imagesetpixel($this->imageHandle, $x, $y2, $color1); |
||
311 | } |
||
312 | } */ |
||
313 | |||
314 | return true; |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Crops image by size and start coordinates |
||
319 | * |
||
320 | * @param int width Cropped image width |
||
321 | * @param int height Cropped image height |
||
322 | * @param int x X-coordinate to crop at |
||
323 | * @param int y Y-coordinate to crop at |
||
324 | * @return bool|PEAR_Error TRUE or a PEAR_Error object on error |
||
325 | * @access public |
||
326 | */ |
||
327 | public function crop($width, $height, $x = 0, $y = 0) |
||
328 | { |
||
329 | // Sanity check |
||
330 | if (!$this->intersects($width, $height, $x, $y)) { |
||
331 | return PEAR::raiseError('Nothing to crop', IMAGE_TRANSFORM_ERROR_OUTOFBOUND); |
||
332 | } |
||
333 | $x = min($this->new_x, max(0, $x)); |
||
334 | $y = min($this->new_y, max(0, $y)); |
||
335 | $width = min($width, $this->new_x - $x); |
||
336 | $height = min($height, $this->new_y - $y); |
||
337 | $new_img = $this->_createImage($width, $height); |
||
338 | |||
339 | if (!imagecopy($new_img, $this->imageHandle, 0, 0, $x, $y, $width, $height)) { |
||
340 | imagedestroy($new_img); |
||
341 | |||
342 | return PEAR::raiseError('Failed transformation: crop()', IMAGE_TRANSFORM_ERROR_FAILED); |
||
343 | } |
||
344 | |||
345 | $this->oldImage = $this->imageHandle; |
||
346 | $this->imageHandle = $new_img; |
||
347 | $this->resized = true; |
||
348 | |||
349 | $this->new_x = $width; |
||
350 | $this->new_y = $height; |
||
351 | |||
352 | return true; |
||
353 | } |
||
354 | |||
355 | /** |
||
356 | * Converts the image to greyscale |
||
357 | * |
||
358 | * @return bool|PEAR_Error TRUE or a PEAR_Error object on error |
||
359 | * @access public |
||
360 | */ |
||
361 | public function greyscale() |
||
362 | { |
||
363 | imagecopymergegray($this->imageHandle, $this->imageHandle, 0, 0, 0, 0, $this->new_x, $this->new_y, 0); |
||
364 | |||
365 | return true; |
||
366 | } |
||
367 | |||
368 | /** |
||
369 | * Resize Action |
||
370 | * |
||
371 | * For GD 2.01+ the new copyresampled function is used |
||
372 | * It uses a bicubic interpolation algorithm to get far |
||
373 | * better result. |
||
374 | * |
||
375 | * @param int $new_x New width |
||
376 | * @param int $new_y New height |
||
377 | * @param array $options Optional parameters |
||
378 | * <ul> |
||
379 | * <li>'scaleMethod': "pixel" or "smooth"</li> |
||
380 | * </ul> |
||
381 | * |
||
382 | * @return bool|PEAR_Error TRUE on success or PEAR_Error object on error |
||
383 | * @access protected |
||
384 | */ |
||
385 | public function _resize($new_x=null, $new_y=null, $options = null) |
||
386 | { |
||
387 | if (true === $this->resized) { |
||
388 | return PEAR::raiseError('You have already resized the image without saving it. Your previous resizing will be overwritten', null, PEAR_ERROR_TRIGGER, E_USER_NOTICE); |
||
389 | } |
||
390 | |||
391 | if ($this->new_x == $new_x && $this->new_y == $new_y) { |
||
392 | return true; |
||
393 | } |
||
394 | |||
395 | $scaleMethod = $this->_getOption('scaleMethod', $options, 'smooth'); |
||
396 | |||
397 | // Make sure to get a true color image if doing resampled resizing |
||
398 | // otherwise get the same type of image |
||
399 | $trueColor = ('pixel' == $scaleMethod) ? null : true; |
||
400 | $new_img = $this->_createImage($new_x, $new_y, $trueColor); |
||
401 | |||
402 | $icr_res = null; |
||
403 | if ('pixel' != $scaleMethod && function_exists('ImageCopyResampled')) { |
||
404 | $icr_res = ImageCopyResampled($new_img, $this->imageHandle, 0, 0, 0, 0, $new_x, $new_y, $this->img_x, $this->img_y); |
||
405 | } |
||
406 | if (!$icr_res) { |
||
407 | ImageCopyResized($new_img, $this->imageHandle, 0, 0, 0, 0, $new_x, $new_y, $this->img_x, $this->img_y); |
||
408 | } |
||
409 | $this->oldImage = $this->imageHandle; |
||
410 | $this->imageHandle = $new_img; |
||
411 | $this->resized = true; |
||
412 | |||
413 | $this->new_x = $new_x; |
||
414 | $this->new_y = $new_y; |
||
415 | |||
416 | return true; |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Adjusts the image gamma |
||
421 | * |
||
422 | * @param float $outputgamma |
||
423 | * |
||
424 | * @return bool|PEAR_Error TRUE or a PEAR_Error object on error |
||
425 | * @access public |
||
426 | */ |
||
427 | public function gamma($outputgamma = 1.0) |
||
428 | { |
||
429 | if (1.0 != $outputgamma) { |
||
430 | ImageGammaCorrect($this->imageHandle, 1.0, $outputgamma); |
||
431 | } |
||
432 | |||
433 | return true; |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Helper method to save to a file or output the image |
||
438 | * |
||
439 | * @param string $filename the name of the file to write to (blank to output) |
||
440 | * @param string $types define the output format, default |
||
441 | * is the current used format |
||
442 | * @param int $quality output DPI, default is 75 |
||
443 | * |
||
444 | * @return bool|PEAR_Error TRUE on success or PEAR_Error object on error |
||
445 | * @access protected |
||
446 | */ |
||
447 | public function _generate($filename, $type = '', $quality = null) |
||
448 | { |
||
449 | $type = strtolower(('' == $type) ? $this->type : $type); |
||
450 | $options = (is_array($quality)) ? $quality : array(); |
||
451 | switch ($type) { |
||
452 | case 'jpg': |
||
453 | $type = 'jpeg'; |
||
454 | // no break |
||
455 | case 'jpeg': |
||
456 | if (is_numeric($quality)) { |
||
457 | $options['quality'] = $quality; |
||
458 | } |
||
459 | $quality = $this->_getOption('quality', $options, 75); |
||
460 | break; |
||
461 | } |
||
462 | if (!$this->supportsType($type, 'w')) { |
||
463 | return PEAR::raiseError('Image type not supported for output', IMAGE_TRANSFORM_ERROR_UNSUPPORTED); |
||
464 | } |
||
465 | |||
466 | if ('' == $filename) { |
||
467 | header('Content-type: ' . $this->getMimeType($type)); |
||
468 | $action = 'output image'; |
||
469 | } else { |
||
470 | $action = 'save image to file'; |
||
471 | } |
||
472 | |||
473 | $functionName = 'image' . $type; |
||
474 | switch ($type) { |
||
475 | case 'jpeg': |
||
476 | $result = $functionName($this->imageHandle, $filename, $quality); |
||
477 | break; |
||
478 | default: |
||
479 | if ('' == $filename) { |
||
480 | $result = $functionName($this->imageHandle); |
||
481 | } else { |
||
482 | $result = $functionName($this->imageHandle, $filename); |
||
483 | } |
||
484 | } |
||
485 | if (!$result) { |
||
486 | return PEAR::raiseError('Couldn\'t ' . $action, IMAGE_TRANSFORM_ERROR_IO); |
||
487 | } |
||
488 | $this->imageHandle = $this->oldImage; |
||
489 | if (!$this->keep_settings_on_save) { |
||
490 | $this->free(); |
||
491 | } |
||
492 | |||
493 | return true; |
||
494 | } // End save |
||
495 | |||
496 | /** |
||
497 | * Displays image without saving and lose changes. |
||
498 | * |
||
499 | * This method adds the Content-type HTTP header |
||
500 | * |
||
501 | * @param string $type (JPEG, PNG...); |
||
502 | * @param int $quality 75 |
||
503 | * |
||
504 | * @return bool|PEAR_Error TRUE or PEAR_Error object on error |
||
505 | * @access public |
||
506 | */ |
||
507 | public function display($type = '', $quality = null) |
||
508 | { |
||
509 | return $this->_generate('', $type, $quality); |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Saves the image to a file |
||
514 | * |
||
515 | * @param string $filename the name of the file to write to |
||
516 | * @param string $type the output format, default |
||
517 | * is the current used format |
||
518 | * @param int $quality default is 75 |
||
519 | * |
||
520 | * @return bool|PEAR_Error TRUE on success or PEAR_Error object on error |
||
521 | * @access public |
||
522 | */ |
||
523 | public function save($filename, $type = '', $quality = null) |
||
524 | { |
||
525 | if (!trim($filename)) { |
||
526 | return PEAR::raiseError('Filename missing', IMAGE_TRANSFORM_ERROR_ARGUMENT); |
||
527 | } |
||
528 | |||
529 | return $this->_generate($filename, $type, $quality); |
||
530 | } |
||
531 | |||
532 | /** |
||
533 | * Destroys image handle |
||
534 | * |
||
535 | * @access public |
||
536 | */ |
||
537 | public function free() |
||
548 | } |
||
549 | |||
550 | /** |
||
551 | * Returns a new image for temporary processing |
||
552 | * |
||
553 | * @param int $width width of the new image |
||
554 | * @param int $height height of the new image |
||
555 | * @param bool $trueColor force which type of image to create |
||
556 | * @return resource a GD image resource |
||
557 | * @access protected |
||
558 | */ |
||
559 | public function _createImage($width = -1, $height = -1, $trueColor = null) |
||
615 | } |
||
616 | } |
||
617 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.