vendor/gregwar/image/Gregwar/Image/Adapter/Common.php line 276

Open in your IDE?
  1. <?php
  2. namespace Gregwar\Image\Adapter;
  3. abstract class Common extends Adapter
  4. {
  5. /**
  6. * {@inheritdoc}
  7. */
  8. public function zoomCrop($width, $height, $background = 'transparent', $xPosLetter = 'center', $yPosLetter = 'center')
  9. {
  10. $originalWidth = $this->width();
  11. $originalHeight = $this->height();
  12. // Calculate the different ratios
  13. $originalRatio = $originalWidth / $originalHeight;
  14. $newRatio = $width / $height;
  15. // Compare ratios
  16. if ($originalRatio > $newRatio) {
  17. // Original image is wider
  18. $newHeight = $height;
  19. $newWidth = (int) $height * $originalRatio;
  20. } else {
  21. // Equal width or smaller
  22. $newHeight = (int) $width / $originalRatio;
  23. $newWidth = $width;
  24. }
  25. // Perform resize
  26. $this->resize($newWidth, $newHeight, $background, true);
  27. // Define x position
  28. switch ($xPosLetter) {
  29. case 'L':
  30. case 'left':
  31. $xPos = 0;
  32. break;
  33. case 'R':
  34. case 'right':
  35. $xPos = (int) $newWidth - $width;
  36. break;
  37. case 'center':
  38. $xPos = (int) ($newWidth - $width) / 2;
  39. break;
  40. default:
  41. $factorW = $newWidth / $originalWidth;
  42. $xPos = $xPosLetter * $factorW;
  43. // If the desired cropping position goes beyond the width then
  44. // set the crop to be within the correct bounds.
  45. if ($xPos + $width > $newWidth) {
  46. $xPos = (int) $newWidth - $width;
  47. }
  48. }
  49. // Define y position
  50. switch ($yPosLetter) {
  51. case 'T':
  52. case 'top':
  53. $yPos = 0;
  54. break;
  55. case 'B':
  56. case 'bottom':
  57. $yPos = (int) $newHeight - $height;
  58. break;
  59. case 'center':
  60. $yPos = (int) ($newHeight - $height) / 2;
  61. break;
  62. default:
  63. $factorH = $newHeight / $originalHeight;
  64. $yPos = $yPosLetter * $factorH;
  65. // If the desired cropping position goes beyond the height then
  66. // set the crop to be within the correct bounds.
  67. if ($yPos + $height > $newHeight) {
  68. $yPos = (int) $newHeight - $height;
  69. }
  70. }
  71. // Crop image to reach desired size
  72. $this->crop($xPos, $yPos, $width, $height);
  73. return $this;
  74. }
  75. /**
  76. * Resizes the image forcing the destination to have exactly the
  77. * given width and the height.
  78. *
  79. * @param int $w the width
  80. * @param int $h the height
  81. * @param int $bg the background
  82. */
  83. public function forceResize($width = null, $height = null, $background = 'transparent')
  84. {
  85. return $this->resize($width, $height, $background, true);
  86. }
  87. /**
  88. * {@inheritdoc}
  89. */
  90. public function scaleResize($width = null, $height = null, $background = 'transparent', $crop = false)
  91. {
  92. return $this->resize($width, $height, $background, false, true, $crop);
  93. }
  94. /**
  95. * {@inheritdoc}
  96. */
  97. public function cropResize($width = null, $height = null, $background = 'transparent')
  98. {
  99. return $this->resize($width, $height, $background, false, false, true);
  100. }
  101. /**
  102. * Read exif rotation from file and apply it.
  103. */
  104. public function fixOrientation()
  105. {
  106. if (!in_array(exif_imagetype($this->source->getInfos()), array(
  107. IMAGETYPE_JPEG,
  108. IMAGETYPE_TIFF_II,
  109. IMAGETYPE_TIFF_MM,
  110. ))) {
  111. return $this;
  112. }
  113. if (!extension_loaded('exif')) {
  114. throw new \RuntimeException('You need to EXIF PHP Extension to use this function');
  115. }
  116. $exif = @exif_read_data($this->source->getInfos());
  117. if ($exif === false || !array_key_exists('Orientation', $exif)) {
  118. return $this;
  119. }
  120. return $this->applyExifOrientation($exif['Orientation']);
  121. }
  122. /**
  123. * Apply orientation using Exif orientation value.
  124. */
  125. public function applyExifOrientation($exif_orienation)
  126. {
  127. switch ($exif_orienation) {
  128. case 1:
  129. break;
  130. case 2:
  131. $this->flip(false, true);
  132. break;
  133. case 3: // 180 rotate left
  134. $this->rotate(180);
  135. break;
  136. case 4: // vertical flip
  137. $this->flip(true, false);
  138. break;
  139. case 5: // vertical flip + 90 rotate right
  140. $this->flip(true, false);
  141. $this->rotate(-90);
  142. break;
  143. case 6: // 90 rotate right
  144. $this->rotate(-90);
  145. break;
  146. case 7: // horizontal flip + 90 rotate right
  147. $this->flip(false, true);
  148. $this->rotate(-90);
  149. break;
  150. case 8: // 90 rotate left
  151. $this->rotate(90);
  152. break;
  153. }
  154. return $this;
  155. }
  156. /**
  157. * Opens the image.
  158. */
  159. abstract protected function openGif($file);
  160. abstract protected function openJpeg($file);
  161. abstract protected function openPng($file);
  162. abstract protected function openWebp($file);
  163. /**
  164. * Creates an image.
  165. */
  166. abstract protected function createImage($width, $height);
  167. /**
  168. * Creating an image using $data.
  169. */
  170. abstract protected function createImageFromData($data);
  171. /**
  172. * Loading image from $resource.
  173. */
  174. protected function loadResource($resource)
  175. {
  176. $this->resource = $resource;
  177. }
  178. protected function loadFile($file, $type)
  179. {
  180. if (!$this->supports($type)) {
  181. throw new \RuntimeException('Type '.$type.' is not supported by GD');
  182. }
  183. if ($type == 'jpeg') {
  184. $this->openJpeg($file);
  185. }
  186. if ($type == 'gif') {
  187. $this->openGif($file);
  188. }
  189. if ($type == 'png') {
  190. $this->openPng($file);
  191. }
  192. if ($type == 'webp') {
  193. $this->openWebp($file);
  194. }
  195. if (false === $this->resource) {
  196. throw new \UnexpectedValueException('Unable to open file ('.$file.')');
  197. } else {
  198. $this->convertToTrueColor();
  199. }
  200. }
  201. /**
  202. * {@inheritdoc}
  203. */
  204. public function init()
  205. {
  206. $source = $this->source;
  207. if ($source instanceof \Gregwar\Image\Source\File) {
  208. $this->loadFile($source->getFile(), $source->guessType());
  209. } elseif ($source instanceof \Gregwar\Image\Source\Create) {
  210. $this->createImage($source->getWidth(), $source->getHeight());
  211. } elseif ($source instanceof \Gregwar\Image\Source\Data) {
  212. $this->createImageFromData($source->getData());
  213. } elseif ($source instanceof \Gregwar\Image\Source\Resource) {
  214. $this->loadResource($source->getResource());
  215. } else {
  216. throw new \Exception('Unsupported image source type '.get_class($source));
  217. }
  218. return $this;
  219. }
  220. /**
  221. * {@inheritdoc}
  222. */
  223. public function deinit()
  224. {
  225. $this->resource = null;
  226. }
  227. /**
  228. * {@inheritdoc}
  229. */
  230. public function resize($width = null, $height = null, $background = 'transparent', $force = false, $rescale = false, $crop = false)
  231. {
  232. $current_width = $this->width();
  233. $current_height = $this->height();
  234. $new_width = 0;
  235. $new_height = 0;
  236. $scale = 1.0;
  237. if ($height === null && preg_match('#^(.+)%$#mUsi', $width, $matches)) {
  238. $width = round($current_width * ((float) $matches[1] / 100.0));
  239. $height = round($current_height * ((float) $matches[1] / 100.0));
  240. }
  241. if (!$rescale && (!$force || $crop)) {
  242. if ($width != null && $current_width > $width) {
  243. $scale = $current_width / $width;
  244. }
  245. if ($height != null && $current_height > $height) {
  246. if ($current_height / $height > $scale) {
  247. $scale = $current_height / $height;
  248. }
  249. }
  250. } else {
  251. if ($width != null) {
  252. $scale = $current_width / $width;
  253. $new_width = $width;
  254. }
  255. if ($height != null) {
  256. if ($width != null && $rescale) {
  257. $scale = max($scale, $current_height / $height);
  258. } else {
  259. $scale = $current_height / $height;
  260. }
  261. $new_height = $height;
  262. }
  263. }
  264. if (!$force || $width == null || $rescale) {
  265. $new_width = round($current_width / $scale);
  266. }
  267. if (!$force || $height == null || $rescale) {
  268. $new_height = round($current_height / $scale);
  269. }
  270. if ($width == null || $crop) {
  271. $width = $new_width;
  272. }
  273. if ($height == null || $crop) {
  274. $height = $new_height;
  275. }
  276. $this->doResize($background, $width, $height, $new_width, $new_height);
  277. }
  278. /**
  279. * Trim background color arround the image.
  280. *
  281. * @param int $bg the background
  282. */
  283. protected function _trimColor($background = 'transparent')
  284. {
  285. $width = $this->width();
  286. $height = $this->height();
  287. $b_top = 0;
  288. $b_lft = 0;
  289. $b_btm = $height - 1;
  290. $b_rt = $width - 1;
  291. //top
  292. for (; $b_top < $height; ++$b_top) {
  293. for ($x = 0; $x < $width; ++$x) {
  294. if ($this->getColor($x, $b_top) != $background) {
  295. break 2;
  296. }
  297. }
  298. }
  299. // bottom
  300. for (; $b_btm >= 0; --$b_btm) {
  301. for ($x = 0; $x < $width; ++$x) {
  302. if ($this->getColor($x, $b_btm) != $background) {
  303. break 2;
  304. }
  305. }
  306. }
  307. // left
  308. for (; $b_lft < $width; ++$b_lft) {
  309. for ($y = $b_top; $y <= $b_btm; ++$y) {
  310. if ($this->getColor($b_lft, $y) != $background) {
  311. break 2;
  312. }
  313. }
  314. }
  315. // right
  316. for (; $b_rt >= 0; --$b_rt) {
  317. for ($y = $b_top; $y <= $b_btm; ++$y) {
  318. if ($this->getColor($b_rt, $y) != $background) {
  319. break 2;
  320. }
  321. }
  322. }
  323. ++$b_btm;
  324. ++$b_rt;
  325. $this->crop($b_lft, $b_top, $b_rt - $b_lft, $b_btm - $b_top);
  326. }
  327. /**
  328. * Resizes the image to an image having size of $target_width, $target_height, using
  329. * $new_width and $new_height and padding with $bg color.
  330. */
  331. abstract protected function doResize($bg, $target_width, $target_height, $new_width, $new_height);
  332. /**
  333. * Gets the color of the $x, $y pixel.
  334. */
  335. abstract protected function getColor($x, $y);
  336. /**
  337. * {@inheritdoc}
  338. */
  339. public function enableProgressive()
  340. {
  341. throw new \Exception('The Adapter '.$this->getName().' does not support Progressive Image loading');
  342. }
  343. /**
  344. * This does nothing, but can be used to tag a ressource for instance (having a final image hash
  345. * for the cache different depending on the tag)
  346. */
  347. public function tag($tag)
  348. {
  349. }
  350. }