Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

827 lines
20 KiB

  1. <?php
  2. if(! class_exists("Photo")) {
  3. class Photo {
  4. private $image;
  5. /**
  6. * Put back gd stuff, not everybody have Imagick
  7. */
  8. private $imagick;
  9. private $width;
  10. private $height;
  11. private $valid;
  12. private $type;
  13. private $types;
  14. /**
  15. * supported mimetypes and corresponding file extensions
  16. */
  17. static function supportedTypes() {
  18. if(class_exists('Imagick')) {
  19. /**
  20. * Imagick::queryFormats won't help us a lot there...
  21. * At least, not yet, other parts of friendica uses this array
  22. */
  23. $t = array(
  24. 'image/jpeg' => 'jpg',
  25. 'image/png' => 'png',
  26. 'image/gif' => 'gif'
  27. );
  28. } else {
  29. $t = array();
  30. $t['image/jpeg'] ='jpg';
  31. if (imagetypes() & IMG_PNG) $t['image/png'] = 'png';
  32. }
  33. return $t;
  34. }
  35. public function __construct($data, $type=null) {
  36. $this->imagick = class_exists('Imagick');
  37. $this->types = $this->supportedTypes();
  38. if (!array_key_exists($type,$this->types)){
  39. $type='image/jpeg';
  40. }
  41. $this->type = $type;
  42. if($this->is_imagick() && $this->load_data($data)) {
  43. return true;
  44. } else {
  45. // Failed to load with Imagick, fallback
  46. $this->imagick = false;
  47. }
  48. return $this->load_data($data);
  49. }
  50. public function __destruct() {
  51. if($this->image) {
  52. if($this->is_imagick()) {
  53. $this->image->clear();
  54. $this->image->destroy();
  55. return;
  56. }
  57. imagedestroy($this->image);
  58. }
  59. }
  60. public function is_imagick() {
  61. return $this->imagick;
  62. }
  63. /**
  64. * Maps Mime types to Imagick formats
  65. */
  66. public function get_FormatsMap() {
  67. $m = array(
  68. 'image/jpeg' => 'JPG',
  69. 'image/png' => 'PNG',
  70. 'image/gif' => 'GIF'
  71. );
  72. return $m;
  73. }
  74. private function load_data($data) {
  75. if($this->is_imagick()) {
  76. $this->image = new Imagick();
  77. try {
  78. $this->image->readImageBlob($data);
  79. }
  80. catch (Exception $e) {
  81. // Imagick couldn't use the data
  82. return false;
  83. }
  84. /**
  85. * Setup the image to the format it will be saved to
  86. */
  87. $map = $this->get_FormatsMap();
  88. $format = $map[$type];
  89. $this->image->setFormat($format);
  90. // Always coalesce, if it is not a multi-frame image it won't hurt anyway
  91. $this->image = $this->image->coalesceImages();
  92. /**
  93. * setup the compression here, so we'll do it only once
  94. */
  95. switch($this->getType()){
  96. case "image/png":
  97. $quality = get_config('system','png_quality');
  98. if((! $quality) || ($quality > 9))
  99. $quality = PNG_QUALITY;
  100. /**
  101. * From http://www.imagemagick.org/script/command-line-options.php#quality:
  102. *
  103. * 'For the MNG and PNG image formats, the quality value sets
  104. * the zlib compression level (quality / 10) and filter-type (quality % 10).
  105. * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering,
  106. * unless the image has a color map, in which case it means compression level 7 with no PNG filtering'
  107. */
  108. $quality = $quality * 10;
  109. $this->image->setCompressionQuality($quality);
  110. break;
  111. case "image/jpeg":
  112. $quality = get_config('system','jpeg_quality');
  113. if((! $quality) || ($quality > 100))
  114. $quality = JPEG_QUALITY;
  115. $this->image->setCompressionQuality($quality);
  116. }
  117. // The 'width' and 'height' properties are only used by non-Imagick routines.
  118. $this->width = $this->image->getImageWidth();
  119. $this->height = $this->image->getImageHeight();
  120. $this->valid = true;
  121. return true;
  122. }
  123. $this->valid = false;
  124. $this->image = @imagecreatefromstring($data);
  125. if($this->image !== FALSE) {
  126. $this->width = imagesx($this->image);
  127. $this->height = imagesy($this->image);
  128. $this->valid = true;
  129. imagealphablending($this->image, false);
  130. imagesavealpha($this->image, true);
  131. return true;
  132. }
  133. return false;
  134. }
  135. public function is_valid() {
  136. if($this->is_imagick())
  137. return ($this->image !== FALSE);
  138. return $this->valid;
  139. }
  140. public function getWidth() {
  141. if(!$this->is_valid())
  142. return FALSE;
  143. if($this->is_imagick())
  144. return $this->image->getImageWidth();
  145. return $this->width;
  146. }
  147. public function getHeight() {
  148. if(!$this->is_valid())
  149. return FALSE;
  150. if($this->is_imagick())
  151. return $this->image->getImageHeight();
  152. return $this->height;
  153. }
  154. public function getImage() {
  155. if(!$this->is_valid())
  156. return FALSE;
  157. if($this->is_imagick()) {
  158. /* Clean it */
  159. $this->image = $this->image->deconstructImages();
  160. return $this->image;
  161. }
  162. return $this->image;
  163. }
  164. public function getType() {
  165. if(!$this->is_valid())
  166. return FALSE;
  167. return $this->type;
  168. }
  169. public function getExt() {
  170. if(!$this->is_valid())
  171. return FALSE;
  172. return $this->types[$this->getType()];
  173. }
  174. public function scaleImage($max) {
  175. if(!$this->is_valid())
  176. return FALSE;
  177. $width = $this->getWidth();
  178. $height = $this->getHeight();
  179. $dest_width = $dest_height = 0;
  180. if((! $width)|| (! $height))
  181. return FALSE;
  182. if($width > $max && $height > $max) {
  183. // very tall image (greater than 16:9)
  184. // constrain the width - let the height float.
  185. if((($height * 9) / 16) > $width) {
  186. $dest_width = $max;
  187. $dest_height = intval(( $height * $max ) / $width);
  188. }
  189. // else constrain both dimensions
  190. elseif($width > $height) {
  191. $dest_width = $max;
  192. $dest_height = intval(( $height * $max ) / $width);
  193. }
  194. else {
  195. $dest_width = intval(( $width * $max ) / $height);
  196. $dest_height = $max;
  197. }
  198. }
  199. else {
  200. if( $width > $max ) {
  201. $dest_width = $max;
  202. $dest_height = intval(( $height * $max ) / $width);
  203. }
  204. else {
  205. if( $height > $max ) {
  206. // very tall image (greater than 16:9)
  207. // but width is OK - don't do anything
  208. if((($height * 9) / 16) > $width) {
  209. $dest_width = $width;
  210. $dest_height = $height;
  211. }
  212. else {
  213. $dest_width = intval(( $width * $max ) / $height);
  214. $dest_height = $max;
  215. }
  216. }
  217. else {
  218. $dest_width = $width;
  219. $dest_height = $height;
  220. }
  221. }
  222. }
  223. if($this->is_imagick()) {
  224. /**
  225. * If it is not animated, there will be only one iteration here,
  226. * so don't bother checking
  227. */
  228. // Don't forget to go back to the first frame
  229. $this->image->setFirstIterator();
  230. do {
  231. // FIXME - implement horizantal bias for scaling as in followin GD functions
  232. // to allow very tall images to be constrained only horizontally.
  233. $this->image->scaleImage($dest_width, $dest_height);
  234. } while ($this->image->nextImage());
  235. // These may not be necessary any more
  236. $this->width = $this->image->getImageWidth();
  237. $this->height = $this->image->getImageHeight();
  238. return;
  239. }
  240. $dest = imagecreatetruecolor( $dest_width, $dest_height );
  241. imagealphablending($dest, false);
  242. imagesavealpha($dest, true);
  243. if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
  244. imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
  245. if($this->image)
  246. imagedestroy($this->image);
  247. $this->image = $dest;
  248. $this->width = imagesx($this->image);
  249. $this->height = imagesy($this->image);
  250. }
  251. public function rotate($degrees) {
  252. if(!$this->is_valid())
  253. return FALSE;
  254. if($this->is_imagick()) {
  255. $this->image->setFirstIterator();
  256. do {
  257. $this->image->rotateImage(new ImagickPixel(), -$degrees); // ImageMagick rotates in the opposite direction of imagerotate()
  258. } while ($this->image->nextImage());
  259. return;
  260. }
  261. $this->image = imagerotate($this->image,$degrees,0);
  262. $this->width = imagesx($this->image);
  263. $this->height = imagesy($this->image);
  264. }
  265. public function flip($horiz = true, $vert = false) {
  266. if(!$this->is_valid())
  267. return FALSE;
  268. if($this->is_imagick()) {
  269. $this->image->setFirstIterator();
  270. do {
  271. if($horiz) $this->image->flipImage();
  272. if($vert) $this->image->flopImage();
  273. } while ($this->image->nextImage());
  274. return;
  275. }
  276. $w = imagesx($this->image);
  277. $h = imagesy($this->image);
  278. $flipped = imagecreate($w, $h);
  279. if($horiz) {
  280. for ($x = 0; $x < $w; $x++) {
  281. imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
  282. }
  283. }
  284. if($vert) {
  285. for ($y = 0; $y < $h; $y++) {
  286. imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
  287. }
  288. }
  289. $this->image = $flipped;
  290. }
  291. public function orient($filename) {
  292. // based off comment on http://php.net/manual/en/function.imagerotate.php
  293. if(!$this->is_valid())
  294. return FALSE;
  295. if( (! function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg') )
  296. return;
  297. $exif = @exif_read_data($filename);
  298. if(! $exif)
  299. return;
  300. $ort = $exif['Orientation'];
  301. switch($ort)
  302. {
  303. case 1: // nothing
  304. break;
  305. case 2: // horizontal flip
  306. $this->flip();
  307. break;
  308. case 3: // 180 rotate left
  309. $this->rotate(180);
  310. break;
  311. case 4: // vertical flip
  312. $this->flip(false, true);
  313. break;
  314. case 5: // vertical flip + 90 rotate right
  315. $this->flip(false, true);
  316. $this->rotate(-90);
  317. break;
  318. case 6: // 90 rotate right
  319. $this->rotate(-90);
  320. break;
  321. case 7: // horizontal flip + 90 rotate right
  322. $this->flip();
  323. $this->rotate(-90);
  324. break;
  325. case 8: // 90 rotate left
  326. $this->rotate(90);
  327. break;
  328. }
  329. }
  330. public function scaleImageUp($min) {
  331. if(!$this->is_valid())
  332. return FALSE;
  333. $width = $this->getWidth();
  334. $height = $this->getHeight();
  335. $dest_width = $dest_height = 0;
  336. if((! $width)|| (! $height))
  337. return FALSE;
  338. if($width < $min && $height < $min) {
  339. if($width > $height) {
  340. $dest_width = $min;
  341. $dest_height = intval(( $height * $min ) / $width);
  342. }
  343. else {
  344. $dest_width = intval(( $width * $min ) / $height);
  345. $dest_height = $min;
  346. }
  347. }
  348. else {
  349. if( $width < $min ) {
  350. $dest_width = $min;
  351. $dest_height = intval(( $height * $min ) / $width);
  352. }
  353. else {
  354. if( $height < $min ) {
  355. $dest_width = intval(( $width * $min ) / $height);
  356. $dest_height = $min;
  357. }
  358. else {
  359. $dest_width = $width;
  360. $dest_height = $height;
  361. }
  362. }
  363. }
  364. if($this->is_imagick())
  365. return $this->scaleImage($dest_width,$dest_height);
  366. $dest = imagecreatetruecolor( $dest_width, $dest_height );
  367. imagealphablending($dest, false);
  368. imagesavealpha($dest, true);
  369. if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
  370. imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
  371. if($this->image)
  372. imagedestroy($this->image);
  373. $this->image = $dest;
  374. $this->width = imagesx($this->image);
  375. $this->height = imagesy($this->image);
  376. }
  377. public function scaleImageSquare($dim) {
  378. if(!$this->is_valid())
  379. return FALSE;
  380. if($this->is_imagick()) {
  381. $this->image->setFirstIterator();
  382. do {
  383. $this->image->scaleImage($dim, $dim);
  384. } while ($this->image->nextImage());
  385. return;
  386. }
  387. $dest = imagecreatetruecolor( $dim, $dim );
  388. imagealphablending($dest, false);
  389. imagesavealpha($dest, true);
  390. if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
  391. imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dim, $dim, $this->width, $this->height);
  392. if($this->image)
  393. imagedestroy($this->image);
  394. $this->image = $dest;
  395. $this->width = imagesx($this->image);
  396. $this->height = imagesy($this->image);
  397. }
  398. public function cropImage($max,$x,$y,$w,$h) {
  399. if(!$this->is_valid())
  400. return FALSE;
  401. if($this->is_imagick()) {
  402. $this->image->setFirstIterator();
  403. do {
  404. $this->image->cropImage($w, $h, $x, $y);
  405. /**
  406. * We need to remove the canva,
  407. * or the image is not resized to the crop:
  408. * http://php.net/manual/en/imagick.cropimage.php#97232
  409. */
  410. $this->image->setImagePage(0, 0, 0, 0);
  411. } while ($this->image->nextImage());
  412. return $this->scaleImage($max);
  413. }
  414. $dest = imagecreatetruecolor( $max, $max );
  415. imagealphablending($dest, false);
  416. imagesavealpha($dest, true);
  417. if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
  418. imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
  419. if($this->image)
  420. imagedestroy($this->image);
  421. $this->image = $dest;
  422. $this->width = imagesx($this->image);
  423. $this->height = imagesy($this->image);
  424. }
  425. public function saveImage($path) {
  426. if(!$this->is_valid())
  427. return FALSE;
  428. $string = $this->imageString();
  429. file_put_contents($path, $string);
  430. }
  431. public function imageString() {
  432. if(!$this->is_valid())
  433. return FALSE;
  434. if($this->is_imagick()) {
  435. /* Clean it */
  436. $this->image = $this->image->deconstructImages();
  437. $string = $this->image->getImagesBlob();
  438. return $string;
  439. }
  440. $quality = FALSE;
  441. ob_start();
  442. // Enable interlacing
  443. imageinterlace($this->image, true);
  444. switch($this->getType()){
  445. case "image/png":
  446. $quality = get_config('system','png_quality');
  447. if((! $quality) || ($quality > 9))
  448. $quality = PNG_QUALITY;
  449. imagepng($this->image,NULL, $quality);
  450. break;
  451. case "image/jpeg":
  452. $quality = get_config('system','jpeg_quality');
  453. if((! $quality) || ($quality > 100))
  454. $quality = JPEG_QUALITY;
  455. imagejpeg($this->image,NULL,$quality);
  456. }
  457. $string = ob_get_contents();
  458. ob_end_clean();
  459. return $string;
  460. }
  461. public function store($uid, $cid, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') {
  462. $r = q("select `guid` from photo where `resource-id` = '%s' and `guid` != '' limit 1",
  463. dbesc($rid)
  464. );
  465. if(count($r))
  466. $guid = $r[0]['guid'];
  467. else
  468. $guid = get_guid();
  469. $x = q("select id from photo where `resource-id` = '%s' and uid = %d and `contact-id` = %d and `scale` = %d limit 1",
  470. dbesc($rid),
  471. intval($uid),
  472. intval($cid),
  473. intval($scale)
  474. );
  475. if(count($x)) {
  476. $r = q("UPDATE `photo`
  477. set `uid` = %d,
  478. `contact-id` = %d,
  479. `guid` = '%s',
  480. `resource-id` = '%s',
  481. `created` = '%s',
  482. `edited` = '%s',
  483. `filename` = '%s',
  484. `type` = '%s',
  485. `album` = '%s',
  486. `height` = %d,
  487. `width` = %d,
  488. `datasize` = %d,
  489. `data` = '%s',
  490. `scale` = %d,
  491. `profile` = %d,
  492. `allow_cid` = '%s',
  493. `allow_gid` = '%s',
  494. `deny_cid` = '%s',
  495. `deny_gid` = '%s'
  496. where id = %d",
  497. intval($uid),
  498. intval($cid),
  499. dbesc($guid),
  500. dbesc($rid),
  501. dbesc(datetime_convert()),
  502. dbesc(datetime_convert()),
  503. dbesc(basename($filename)),
  504. dbesc($this->getType()),
  505. dbesc($album),
  506. intval($this->getHeight()),
  507. intval($this->getWidth()),
  508. dbesc(strlen($this->imageString())),
  509. dbesc($this->imageString()),
  510. intval($scale),
  511. intval($profile),
  512. dbesc($allow_cid),
  513. dbesc($allow_gid),
  514. dbesc($deny_cid),
  515. dbesc($deny_gid),
  516. intval($x[0]['id'])
  517. );
  518. }
  519. else {
  520. $r = q("INSERT INTO `photo`
  521. ( `uid`, `contact-id`, `guid`, `resource-id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `datasize`, `data`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` )
  522. VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s' )",
  523. intval($uid),
  524. intval($cid),
  525. dbesc($guid),
  526. dbesc($rid),
  527. dbesc(datetime_convert()),
  528. dbesc(datetime_convert()),
  529. dbesc(basename($filename)),
  530. dbesc($this->getType()),
  531. dbesc($album),
  532. intval($this->getHeight()),
  533. intval($this->getWidth()),
  534. dbesc(strlen($this->imageString())),
  535. dbesc($this->imageString()),
  536. intval($scale),
  537. intval($profile),
  538. dbesc($allow_cid),
  539. dbesc($allow_gid),
  540. dbesc($deny_cid),
  541. dbesc($deny_gid)
  542. );
  543. }
  544. return $r;
  545. }
  546. }}
  547. /**
  548. * Guess image mimetype from filename or from Content-Type header
  549. *
  550. * @arg $filename string Image filename
  551. * @arg $fromcurl boolean Check Content-Type header from curl request
  552. */
  553. function guess_image_type($filename, $fromcurl=false) {
  554. logger('Photo: guess_image_type: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG);
  555. $type = null;
  556. if ($fromcurl) {
  557. $a = get_app();
  558. $headers=array();
  559. $h = explode("\n",$a->get_curl_headers());
  560. foreach ($h as $l) {
  561. list($k,$v) = array_map("trim", explode(":", trim($l), 2));
  562. $headers[$k] = $v;
  563. }
  564. if (array_key_exists('Content-Type', $headers))
  565. $type = $headers['Content-Type'];
  566. }
  567. if (is_null($type)){
  568. // Guessing from extension? Isn't that... dangerous?
  569. if(class_exists('Imagick') && file_exists($filename) && is_readable($filename)) {
  570. /**
  571. * Well, this not much better,
  572. * but at least it comes from the data inside the image,
  573. * we won't be tricked by a manipulated extension
  574. */
  575. $image = new Imagick($filename);
  576. $type = $image->getImageMimeType();
  577. $image->setInterlaceScheme(Imagick::INTERLACE_PLANE);
  578. } else {
  579. $ext = pathinfo($filename, PATHINFO_EXTENSION);
  580. $types = Photo::supportedTypes();
  581. $type = "image/jpeg";
  582. foreach ($types as $m=>$e){
  583. if ($ext==$e) $type = $m;
  584. }
  585. }
  586. }
  587. logger('Photo: guess_image_type: type='.$type, LOGGER_DEBUG);
  588. return $type;
  589. }
  590. function import_profile_photo($photo,$uid,$cid) {
  591. $a = get_app();
  592. $r = q("select `resource-id` from photo where `uid` = %d and `contact-id` = %d and `scale` = 4 and `album` = 'Contact Photos' limit 1",
  593. intval($uid),
  594. intval($cid)
  595. );
  596. if(count($r) && strlen($r[0]['resource-id'])) {
  597. $hash = $r[0]['resource-id'];
  598. }
  599. else {
  600. $hash = photo_new_resource();
  601. }
  602. $photo_failure = false;
  603. $filename = basename($photo);
  604. $img_str = fetch_url($photo,true);
  605. $type = guess_image_type($photo,true);
  606. $img = new Photo($img_str, $type);
  607. if($img->is_valid()) {
  608. $img->scaleImageSquare(175);
  609. $r = $img->store($uid, $cid, $hash, $filename, 'Contact Photos', 4 );
  610. if($r === false)
  611. $photo_failure = true;
  612. $img->scaleImage(80);
  613. $r = $img->store($uid, $cid, $hash, $filename, 'Contact Photos', 5 );
  614. if($r === false)
  615. $photo_failure = true;
  616. $img->scaleImage(48);
  617. $r = $img->store($uid, $cid, $hash, $filename, 'Contact Photos', 6 );
  618. if($r === false)
  619. $photo_failure = true;
  620. $photo = $a->get_baseurl() . '/photo/' . $hash . '-4.' . $img->getExt();
  621. $thumb = $a->get_baseurl() . '/photo/' . $hash . '-5.' . $img->getExt();
  622. $micro = $a->get_baseurl() . '/photo/' . $hash . '-6.' . $img->getExt();
  623. }
  624. else
  625. $photo_failure = true;
  626. if($photo_failure) {
  627. $photo = $a->get_baseurl() . '/images/person-175.jpg';
  628. $thumb = $a->get_baseurl() . '/images/person-80.jpg';
  629. $micro = $a->get_baseurl() . '/images/person-48.jpg';
  630. }
  631. return(array($photo,$thumb,$micro));
  632. }
  633. function get_photo_info($url) {
  634. $data = array();
  635. $data = Cache::get($url);
  636. if (is_null($data)) {
  637. $img_str = fetch_url($url, true, $redirects, 4);
  638. $tempfile = tempnam(get_temppath(), "cache");
  639. file_put_contents($tempfile, $img_str);
  640. $data = getimagesize($tempfile);
  641. unlink($tempfile);
  642. Cache::set($url, serialize($data));
  643. } else
  644. $data = unserialize($data);
  645. return $data;
  646. }
  647. function scale_image($width, $height, $max) {
  648. $dest_width = $dest_height = 0;
  649. if((!$width) || (!$height))
  650. return FALSE;
  651. if($width > $max && $height > $max) {
  652. // very tall image (greater than 16:9)
  653. // constrain the width - let the height float.
  654. if((($height * 9) / 16) > $width) {
  655. $dest_width = $max;
  656. $dest_height = intval(( $height * $max ) / $width);
  657. } elseif($width > $height) {
  658. // else constrain both dimensions
  659. $dest_width = $max;
  660. $dest_height = intval(( $height * $max ) / $width);
  661. } else {
  662. $dest_width = intval(( $width * $max ) / $height);
  663. $dest_height = $max;
  664. }
  665. } else {
  666. if( $width > $max ) {
  667. $dest_width = $max;
  668. $dest_height = intval(( $height * $max ) / $width);
  669. } else {
  670. if( $height > $max ) {
  671. // very tall image (greater than 16:9)
  672. // but width is OK - don't do anything
  673. if((($height * 9) / 16) > $width) {
  674. $dest_width = $width;
  675. $dest_height = $height;
  676. } else {
  677. $dest_width = intval(( $width * $max ) / $height);
  678. $dest_height = $max;
  679. }
  680. } else {
  681. $dest_width = $width;
  682. $dest_height = $height;
  683. }
  684. }
  685. }
  686. return array("width" => $dest_width, "height" => $dest_height);
  687. }