1 2/* 3 +------------------------------------------------------------------------+ 4 | Phalcon Framework | 5 +------------------------------------------------------------------------+ 6 | Copyright (c) 2011-2017 Phalcon Team (https://phalconphp.com) | 7 +------------------------------------------------------------------------+ 8 | This source file is subject to the New BSD License that is bundled | 9 | with this package in the file LICENSE.txt. | 10 | | 11 | If you did not receive a copy of the license and are unable to | 12 | obtain it through the world-wide-web, please send an email | 13 | to license@phalconphp.com so we can send you a copy immediately. | 14 +------------------------------------------------------------------------+ 15 | Authors: Andres Gutierrez <andres@phalconphp.com> | 16 | Eduar Carvajal <eduar@phalconphp.com> | 17 +------------------------------------------------------------------------+ 18 */ 19 20namespace Phalcon\Image; 21 22use Phalcon\Image; 23 24/** 25 * Phalcon\Image\Adapter 26 * 27 * All image adapters must use this class 28 */ 29abstract class Adapter implements AdapterInterface 30{ 31 32 protected _image { get }; 33 34 protected _file; 35 36 protected _realpath { get }; 37 38 /** 39 * Image width 40 * 41 * @var int 42 */ 43 protected _width { get }; 44 45 /** 46 * Image height 47 * 48 * @var int 49 */ 50 protected _height { get }; 51 52 /** 53 * Image type 54 * 55 * Driver dependent 56 * 57 * @var int 58 */ 59 protected _type { get }; 60 61 /** 62 * Image mime type 63 * 64 * @var string 65 */ 66 protected _mime { get }; 67 68 protected static _checked = false; 69 70 /** 71 * Resize the image to the given size 72 */ 73 public function resize(int width = null, int height = null, int master = Image::AUTO) -> <Adapter> 74 { 75 var ratio; 76 77 if master == Image::TENSILE { 78 79 if !width || !height { 80 throw new Exception("width and height must be specified"); 81 } 82 83 } else { 84 85 if master == Image::AUTO { 86 87 if !width || !height { 88 throw new Exception("width and height must be specified"); 89 } 90 91 let master = (this->_width / width) > (this->_height / height) ? Image::WIDTH : Image::HEIGHT; 92 } 93 94 if master == Image::INVERSE { 95 96 if !width || !height { 97 throw new Exception("width and height must be specified"); 98 } 99 100 let master = (this->_width / width) > (this->_height / height) ? Image::HEIGHT : Image::WIDTH; 101 } 102 103 switch master { 104 105 case Image::WIDTH: 106 if !width { 107 throw new Exception("width must be specified"); 108 } 109 let height = this->_height * width / this->_width; 110 break; 111 112 case Image::HEIGHT: 113 if !height { 114 throw new Exception("height must be specified"); 115 } 116 let width = this->_width * height / this->_height; 117 break; 118 119 case Image::PRECISE: 120 if !width || !height { 121 throw new Exception("width and height must be specified"); 122 } 123 let ratio = this->_width / this->_height; 124 125 if (width / height) > ratio { 126 let height = this->_height * width / this->_width; 127 } else { 128 let width = this->_width * height / this->_height; 129 } 130 break; 131 132 case Image::NONE: 133 if !width { 134 let width = (int) this->_width; 135 } 136 137 if !height { 138 let width = (int) this->_height; 139 } 140 break; 141 } 142 } 143 144 let width = (int) max(round(width), 1); 145 let height = (int) max(round(height), 1); 146 147 this->{"_resize"}(width, height); 148 149 return this; 150 } 151 152 /** 153 * This method scales the images using liquid rescaling method. Only support Imagick 154 * 155 * @param int $width new width 156 * @param int $height new height 157 * @param int $deltaX How much the seam can traverse on x-axis. Passing 0 causes the seams to be straight. 158 * @param int $rigidity Introduces a bias for non-straight seams. This parameter is typically 0. 159 */ 160 public function liquidRescale(int width, int height, int deltaX = 0, int rigidity = 0) -> <Adapter> 161 { 162 this->{"_liquidRescale"}(width, height, deltaX, rigidity); 163 return this; 164 } 165 166 /** 167 * Crop an image to the given size 168 */ 169 public function crop(int width, int height, int offsetX = null, int offsetY = null) -> <Adapter> 170 { 171 if is_null(offsetX) { 172 let offsetX = ((this->_width - width) / 2); 173 } else { 174 if offsetX < 0 { 175 let offsetX = this->_width - width + offsetX; 176 } 177 178 if offsetX > this->_width { 179 let offsetX = (int) this->_width; 180 } 181 } 182 183 if is_null(offsetY) { 184 let offsetY = ((this->_height - height) / 2); 185 } else { 186 if offsetY < 0 { 187 let offsetY = this->_height - height + offsetY; 188 } 189 190 if offsetY > this->_height { 191 let offsetY = (int) this->_height; 192 } 193 } 194 195 if width > (this->_width - offsetX) { 196 let width = this->_width - offsetX; 197 } 198 199 if height > (this->_height - offsetY) { 200 let height = this->_height - offsetY; 201 } 202 203 this->{"_crop"}(width, height, offsetX, offsetY); 204 205 return this; 206 } 207 208 /** 209 * Rotate the image by a given amount 210 */ 211 public function rotate(int degrees) -> <Adapter> 212 { 213 if degrees > 180 { 214 // FIXME: Fix Zephir Parser to allow use let degrees %= 360 215 let degrees = degrees % 360; 216 if degrees > 180 { 217 let degrees -= 360; 218 } 219 } else { 220 while degrees < -180 { 221 let degrees += 360; 222 } 223 } 224 225 this->{"_rotate"}(degrees); 226 return this; 227 } 228 229 /** 230 * Flip the image along the horizontal or vertical axis 231 */ 232 public function flip(int direction) -> <Adapter> 233 { 234 if direction != Image::HORIZONTAL && direction != Image::VERTICAL { 235 let direction = Image::HORIZONTAL; 236 } 237 238 this->{"_flip"}(direction); 239 return this; 240 } 241 242 /** 243 * Sharpen the image by a given amount 244 */ 245 public function sharpen(int amount) -> <Adapter> 246 { 247 if amount > 100 { 248 let amount = 100; 249 } elseif amount < 1 { 250 let amount = 1; 251 } 252 253 this->{"_sharpen"}(amount); 254 return this; 255 } 256 257 /** 258 * Add a reflection to an image 259 */ 260 public function reflection(int height, int opacity = 100, boolean fadeIn = false) -> <Adapter> 261 { 262 if height <= 0 || height > this->_height { 263 let height = (int) this->_height; 264 } 265 266 if opacity < 0 { 267 let opacity = 0; 268 } elseif opacity > 100 { 269 let opacity = 100; 270 } 271 272 this->{"_reflection"}(height, opacity, fadeIn); 273 274 return this; 275 } 276 277 /** 278 * Add a watermark to an image with the specified opacity 279 */ 280 public function watermark(<Adapter> watermark, int offsetX = 0, int offsetY = 0, int opacity = 100) -> <Adapter> 281 { 282 int tmp; 283 284 let tmp = this->_width - watermark->getWidth(); 285 286 if offsetX < 0 { 287 let offsetX = 0; 288 } elseif offsetX > tmp { 289 let offsetX = tmp; 290 } 291 292 let tmp = this->_height - watermark->getHeight(); 293 294 if offsetY < 0 { 295 let offsetY = 0; 296 } elseif offsetY > tmp { 297 let offsetY = tmp; 298 } 299 300 if opacity < 0 { 301 let opacity = 0; 302 } elseif opacity > 100 { 303 let opacity = 100; 304 } 305 306 this->{"_watermark"}(watermark, offsetX, offsetY, opacity); 307 308 return this; 309 } 310 311 /** 312 * Add a text to an image with a specified opacity 313 */ 314 public function text(string text, var offsetX = false, var offsetY = false, int opacity = 100, string color = "000000", int size = 12, string fontfile = null) -> <Adapter> 315 { 316 var colors; 317 318 if opacity < 0 { 319 let opacity = 0; 320 } else { 321 if opacity > 100 { 322 let opacity = 100; 323 } 324 } 325 326 if strlen(color) > 1 && substr(color, 0, 1) === "#" { 327 let color = substr(color, 1); 328 } 329 330 if strlen(color) === 3 { 331 let color = preg_replace("/./", "$0$0", color); 332 } 333 334 let colors = array_map("hexdec", str_split(color, 2)); 335 336 this->{"_text"}(text, offsetX, offsetY, opacity, colors[0], colors[1], colors[2], size, fontfile); 337 338 return this; 339 } 340 341 /** 342 * Composite one image onto another 343 */ 344 public function mask(<Adapter> watermark) -> <Adapter> 345 { 346 this->{"_mask"}(watermark); 347 return this; 348 } 349 350 /** 351 * Set the background color of an image 352 */ 353 public function background(string color, int opacity = 100) -> <Adapter> 354 { 355 var colors; 356 357 if strlen(color) > 1 && substr(color, 0, 1) === "#" { 358 let color = substr(color, 1); 359 } 360 361 if strlen(color) === 3 { 362 let color = preg_replace("/./", "$0$0", color); 363 } 364 365 let colors = array_map("hexdec", str_split(color, 2)); 366 367 this->{"_background"}(colors[0], colors[1], colors[2], opacity); 368 return this; 369 } 370 371 /** 372 * Blur image 373 */ 374 public function blur(int radius) -> <Adapter> 375 { 376 if radius < 1 { 377 let radius = 1; 378 } elseif radius > 100 { 379 let radius = 100; 380 } 381 382 this->{"_blur"}(radius); 383 return this; 384 } 385 386 /** 387 * Pixelate image 388 */ 389 public function pixelate(int amount) -> <Adapter> 390 { 391 if amount < 2 { 392 let amount = 2; 393 } 394 395 this->{"_pixelate"}(amount); 396 return this; 397 } 398 399 /** 400 * Save the image 401 */ 402 public function save(string file = null, int quality = -1) -> <Adapter> 403 { 404 if !file { 405 let file = (string) this->_realpath; 406 } 407 408 this->{"_save"}(file, quality); 409 return this; 410 } 411 412 /** 413 * Render the image and return the binary string 414 */ 415 public function render(string ext = null, int quality = 100) -> string 416 { 417 if !ext { 418 let ext = (string) pathinfo(this->_file, PATHINFO_EXTENSION); 419 } 420 421 if empty ext { 422 let ext = "png"; 423 } 424 425 if quality < 1 { 426 let quality = 1; 427 } elseif quality > 100 { 428 let quality = 100; 429 } 430 431 return this->{"_render"}(ext, quality); 432 } 433} 434