]> git.mxchange.org Git - friendica.git/blob - src/Object/Image.php
Move Photos to xrc
[friendica.git] / src / Object / Image.php
1 <?php
2 /**
3  * @file src/Object/Image.php
4  * @brief This file contains the Image class for image processing
5  */
6 namespace Friendica\Object;
7
8 use Friendica\App;
9 use Friendica\Core\Cache;
10 use Friendica\Core\Config;
11 use Friendica\Core\System;
12 use Friendica\Database\DBM;
13 use Friendica\Model\Photo;
14 use Exception;
15 use Imagick;
16 use ImagickPixel;
17
18 /**
19  * Class to handle images
20  */
21 class Image
22 {
23         private $image;
24
25         /*
26          * Put back gd stuff, not everybody have Imagick
27          */
28         private $imagick;
29         private $width;
30         private $height;
31         private $valid;
32         private $type;
33         private $types;
34
35         /**
36          * @brief supported mimetypes and corresponding file extensions
37          * @return array
38          */
39         public static function supportedTypes()
40         {
41                 if (class_exists('Imagick')) {
42                         // Imagick::queryFormats won't help us a lot there...
43                         // At least, not yet, other parts of friendica uses this array
44                         $t = array(
45                                 'image/jpeg' => 'jpg',
46                                 'image/png' => 'png',
47                                 'image/gif' => 'gif'
48                         );
49                 } else {
50                         $t = array();
51                         $t['image/jpeg'] ='jpg';
52                         if (imagetypes() & IMG_PNG) {
53                                 $t['image/png'] = 'png';
54                         }
55                 }
56
57                 return $t;
58         }
59
60         /**
61          * @brief Constructor
62          * @param object  $data data
63          * @param boolean $type optional, default null
64          * @return object
65          */
66         public function __construct($data, $type = null)
67         {
68                 $this->imagick = class_exists('Imagick');
69                 $this->types = static::supportedTypes();
70                 if (!array_key_exists($type, $this->types)) {
71                         $type='image/jpeg';
72                 }
73                 $this->type = $type;
74
75                 if ($this->isImagick() && $this->loadData($data)) {
76                         return true;
77                 } else {
78                         // Failed to load with Imagick, fallback
79                         $this->imagick = false;
80                 }
81                 return $this->loadData($data);
82         }
83
84         /**
85          * @brief Destructor
86          * @return void
87          */
88         public function __destruct()
89         {
90                 if ($this->image) {
91                         if ($this->isImagick()) {
92                                 $this->image->clear();
93                                 $this->image->destroy();
94                                 return;
95                         }
96                         if (is_resource($this->image)) {
97                                 imagedestroy($this->image);
98                         }
99                 }
100         }
101
102         /**
103          * @return boolean
104          */
105         public function isImagick()
106         {
107                 return $this->imagick;
108         }
109
110         /**
111          * @brief Maps Mime types to Imagick formats
112          * @return array With with image formats (mime type as key)
113          */
114         public static function getFormatsMap()
115         {
116                 $m = array(
117                         'image/jpeg' => 'JPG',
118                         'image/png' => 'PNG',
119                         'image/gif' => 'GIF'
120                 );
121                 return $m;
122         }
123
124         /**
125          * @param object $data data
126          * @return boolean
127          */
128         private function loadData($data)
129         {
130                 if ($this->isImagick()) {
131                         $this->image = new Imagick();
132                         try {
133                                 $this->image->readImageBlob($data);
134                         } catch (Exception $e) {
135                                 // Imagick couldn't use the data
136                                 return false;
137                         }
138
139                         /*
140                          * Setup the image to the format it will be saved to
141                          */
142                         $map = self::getFormatsMap();
143                         $format = $map[$this->type];
144                         $this->image->setFormat($format);
145
146                         // Always coalesce, if it is not a multi-frame image it won't hurt anyway
147                         $this->image = $this->image->coalesceImages();
148
149                         /*
150                          * setup the compression here, so we'll do it only once
151                          */
152                         switch ($this->getType()) {
153                                 case "image/png":
154                                         $quality = Config::get('system', 'png_quality');
155                                         if ((! $quality) || ($quality > 9)) {
156                                                 $quality = PNG_QUALITY;
157                                         }
158                                         /*
159                                          * From http://www.imagemagick.org/script/command-line-options.php#quality:
160                                          *
161                                          * 'For the MNG and PNG image formats, the quality value sets
162                                          * the zlib compression level (quality / 10) and filter-type (quality % 10).
163                                          * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering,
164                                          * unless the image has a color map, in which case it means compression level 7 with no PNG filtering'
165                                          */
166                                         $quality = $quality * 10;
167                                         $this->image->setCompressionQuality($quality);
168                                         break;
169                                 case "image/jpeg":
170                                         $quality = Config::get('system', 'jpeg_quality');
171                                         if ((! $quality) || ($quality > 100)) {
172                                                 $quality = JPEG_QUALITY;
173                                         }
174                                         $this->image->setCompressionQuality($quality);
175                         }
176
177                         // The 'width' and 'height' properties are only used by non-Imagick routines.
178                         $this->width  = $this->image->getImageWidth();
179                         $this->height = $this->image->getImageHeight();
180                         $this->valid  = true;
181
182                         return true;
183                 }
184
185                 $this->valid = false;
186                 $this->image = @imagecreatefromstring($data);
187                 if ($this->image !== false) {
188                         $this->width  = imagesx($this->image);
189                         $this->height = imagesy($this->image);
190                         $this->valid  = true;
191                         imagealphablending($this->image, false);
192                         imagesavealpha($this->image, true);
193
194                         return true;
195                 }
196
197                 return false;
198         }
199
200         /**
201          * @return boolean
202          */
203         public function isValid()
204         {
205                 if ($this->isImagick()) {
206                         return ($this->image !== false);
207                 }
208                 return $this->valid;
209         }
210
211         /**
212          * @return mixed
213          */
214         public function getWidth()
215         {
216                 if (!$this->isValid()) {
217                         return false;
218                 }
219
220                 if ($this->isImagick()) {
221                         return $this->image->getImageWidth();
222                 }
223                 return $this->width;
224         }
225
226         /**
227          * @return mixed
228          */
229         public function getHeight()
230         {
231                 if (!$this->isValid()) {
232                         return false;
233                 }
234
235                 if ($this->isImagick()) {
236                         return $this->image->getImageHeight();
237                 }
238                 return $this->height;
239         }
240
241         /**
242          * @return mixed
243          */
244         public function getImage()
245         {
246                 if (!$this->isValid()) {
247                         return false;
248                 }
249
250                 if ($this->isImagick()) {
251                         /* Clean it */
252                         $this->image = $this->image->deconstructImages();
253                         return $this->image;
254                 }
255                 return $this->image;
256         }
257
258         /**
259          * @return mixed
260          */
261         public function getType()
262         {
263                 if (!$this->isValid()) {
264                         return false;
265                 }
266
267                 return $this->type;
268         }
269
270         /**
271          * @return mixed
272          */
273         public function getExt()
274         {
275                 if (!$this->isValid()) {
276                         return false;
277                 }
278
279                 return $this->types[$this->getType()];
280         }
281
282         /**
283          * @param integer $max max dimension
284          * @return mixed
285          */
286         public function scaleDown($max)
287         {
288                 if (!$this->isValid()) {
289                         return false;
290                 }
291
292                 $width = $this->getWidth();
293                 $height = $this->getHeight();
294
295                 $dest_width = $dest_height = 0;
296
297                 if ((! $width)|| (! $height)) {
298                         return false;
299                 }
300
301                 if ($width > $max && $height > $max) {
302                         // very tall image (greater than 16:9)
303                         // constrain the width - let the height float.
304
305                         if ((($height * 9) / 16) > $width) {
306                                 $dest_width = $max;
307                                 $dest_height = intval(($height * $max) / $width);
308                         } elseif ($width > $height) {
309                                 // else constrain both dimensions
310                                 $dest_width = $max;
311                                 $dest_height = intval(($height * $max) / $width);
312                         } else {
313                                 $dest_width = intval(($width * $max) / $height);
314                                 $dest_height = $max;
315                         }
316                 } else {
317                         if ($width > $max) {
318                                 $dest_width = $max;
319                                 $dest_height = intval(($height * $max) / $width);
320                         } else {
321                                 if ($height > $max) {
322                                         // very tall image (greater than 16:9)
323                                         // but width is OK - don't do anything
324
325                                         if ((($height * 9) / 16) > $width) {
326                                                 $dest_width = $width;
327                                                 $dest_height = $height;
328                                         } else {
329                                                 $dest_width = intval(($width * $max) / $height);
330                                                 $dest_height = $max;
331                                         }
332                                 } else {
333                                         $dest_width = $width;
334                                         $dest_height = $height;
335                                 }
336                         }
337                 }
338
339                 return $this->scale($dest_width, $dest_height);
340         }
341
342         /**
343          * @param integer $degrees degrees to rotate image
344          * @return mixed
345          */
346         public function rotate($degrees)
347         {
348                 if (!$this->isValid()) {
349                         return false;
350                 }
351
352                 if ($this->isImagick()) {
353                         $this->image->setFirstIterator();
354                         do {
355                                 $this->image->rotateImage(new ImagickPixel(), -$degrees); // ImageMagick rotates in the opposite direction of imagerotate()
356                         } while ($this->image->nextImage());
357                         return;
358                 }
359
360                 // if script dies at this point check memory_limit setting in php.ini
361                 $this->image  = imagerotate($this->image, $degrees, 0);
362                 $this->width  = imagesx($this->image);
363                 $this->height = imagesy($this->image);
364         }
365
366         /**
367          * @param boolean $horiz optional, default true
368          * @param boolean $vert  optional, default false
369          * @return mixed
370          */
371         public function flip($horiz = true, $vert = false)
372         {
373                 if (!$this->isValid()) {
374                         return false;
375                 }
376
377                 if ($this->isImagick()) {
378                         $this->image->setFirstIterator();
379                         do {
380                                 if ($horiz) {
381                                         $this->image->flipImage();
382                                 }
383                                 if ($vert) {
384                                         $this->image->flopImage();
385                                 }
386                         } while ($this->image->nextImage());
387                         return;
388                 }
389
390                 $w = imagesx($this->image);
391                 $h = imagesy($this->image);
392                 $flipped = imagecreate($w, $h);
393                 if ($horiz) {
394                         for ($x = 0; $x < $w; $x++) {
395                                 imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
396                         }
397                 }
398                 if ($vert) {
399                         for ($y = 0; $y < $h; $y++) {
400                                 imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
401                         }
402                 }
403                 $this->image = $flipped;
404         }
405
406         /**
407          * @param string $filename filename
408          * @return mixed
409          */
410         public function orient($filename)
411         {
412                 if ($this->isImagick()) {
413                         // based off comment on http://php.net/manual/en/imagick.getimageorientation.php
414                         $orientation = $this->image->getImageOrientation();
415                         switch ($orientation) {
416                                 case Imagick::ORIENTATION_BOTTOMRIGHT:
417                                         $this->image->rotateimage("#000", 180);
418                                         break;
419                                 case Imagick::ORIENTATION_RIGHTTOP:
420                                         $this->image->rotateimage("#000", 90);
421                                         break;
422                                 case Imagick::ORIENTATION_LEFTBOTTOM:
423                                         $this->image->rotateimage("#000", -90);
424                                         break;
425                         }
426
427                         $this->image->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
428                         return true;
429                 }
430                 // based off comment on http://php.net/manual/en/function.imagerotate.php
431
432                 if (!$this->isValid()) {
433                         return false;
434                 }
435
436                 if ((!function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg')) {
437                         return;
438                 }
439
440                 $exif = @exif_read_data($filename, null, true);
441                 if (!$exif) {
442                         return;
443                 }
444
445                 $ort = $exif['IFD0']['Orientation'];
446
447                 switch ($ort) {
448                         case 1: // nothing
449                                 break;
450
451                         case 2: // horizontal flip
452                                 $this->flip();
453                                 break;
454
455                         case 3: // 180 rotate left
456                                 $this->rotate(180);
457                                 break;
458
459                         case 4: // vertical flip
460                                 $this->flip(false, true);
461                                 break;
462
463                         case 5: // vertical flip + 90 rotate right
464                                 $this->flip(false, true);
465                                 $this->rotate(-90);
466                                 break;
467
468                         case 6: // 90 rotate right
469                                 $this->rotate(-90);
470                                 break;
471
472                         case 7: // horizontal flip + 90 rotate right
473                                 $this->flip();
474                                 $this->rotate(-90);
475                                 break;
476
477                         case 8: // 90 rotate left
478                                 $this->rotate(90);
479                                 break;
480                 }
481
482                 //      logger('exif: ' . print_r($exif,true));
483                 return $exif;
484         }
485
486         /**
487          * @param integer $min minimum dimension
488          * @return mixed
489          */
490         public function scaleUp($min)
491         {
492                 if (!$this->isValid()) {
493                         return false;
494                 }
495
496                 $width = $this->getWidth();
497                 $height = $this->getHeight();
498
499                 $dest_width = $dest_height = 0;
500
501                 if ((!$width)|| (!$height)) {
502                         return false;
503                 }
504
505                 if ($width < $min && $height < $min) {
506                         if ($width > $height) {
507                                 $dest_width = $min;
508                                 $dest_height = intval(($height * $min) / $width);
509                         } else {
510                                 $dest_width = intval(($width * $min) / $height);
511                                 $dest_height = $min;
512                         }
513                 } else {
514                         if ($width < $min) {
515                                 $dest_width = $min;
516                                 $dest_height = intval(($height * $min) / $width);
517                         } else {
518                                 if ($height < $min) {
519                                         $dest_width = intval(($width * $min) / $height);
520                                         $dest_height = $min;
521                                 } else {
522                                         $dest_width = $width;
523                                         $dest_height = $height;
524                                 }
525                         }
526                 }
527
528                 return $this->scale($dest_width, $dest_height);
529         }
530
531         /**
532          * @param integer $dim dimension
533          * @return mixed
534          */
535         public function scaleToSquare($dim)
536         {
537                 if (!$this->isValid()) {
538                         return false;
539                 }
540
541                 return $this->scale($dim, $dim);
542         }
543
544         /**
545          * @brief Scale image to target dimensions
546          *
547          * @param int $dest_width
548          * @param int $dest_height
549          * @return boolean
550          */
551         private function scale($dest_width, $dest_height)
552         {
553                 if (!$this->isValid()) {
554                         return false;
555                 }
556
557                 if ($this->isImagick()) {
558                         /*
559                          * If it is not animated, there will be only one iteration here,
560                          * so don't bother checking
561                          */
562                         // Don't forget to go back to the first frame
563                         $this->image->setFirstIterator();
564                         do {
565                                 // FIXME - implement horizontal bias for scaling as in following GD functions
566                                 // to allow very tall images to be constrained only horizontally.
567                                 $this->image->scaleImage($dest_width, $dest_height);
568                         } while ($this->image->nextImage());
569
570                         // These may not be necessary anymore
571                         $this->width  = $this->image->getImageWidth();
572                         $this->height = $this->image->getImageHeight();
573                 } else {
574                         $dest = imagecreatetruecolor($dest_width, $dest_height);
575                         imagealphablending($dest, false);
576                         imagesavealpha($dest, true);
577
578                         if ($this->type=='image/png') {
579                                 imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
580                         }
581
582                         imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $this->width, $this->height);
583
584                         if ($this->image) {
585                                 imagedestroy($this->image);
586                         }
587
588                         $this->image = $dest;
589                         $this->width  = imagesx($this->image);
590                         $this->height = imagesy($this->image);
591                 }
592
593                 return true;
594         }
595
596         /**
597          * @param integer $max maximum
598          * @param integer $x   x coordinate
599          * @param integer $y   y coordinate
600          * @param integer $w   width
601          * @param integer $h   height
602          * @return mixed
603          */
604         public function crop($max, $x, $y, $w, $h)
605         {
606                 if (!$this->isValid()) {
607                         return false;
608                 }
609
610                 if ($this->isImagick()) {
611                         $this->image->setFirstIterator();
612                         do {
613                                 $this->image->cropImage($w, $h, $x, $y);
614                                 /*
615                                  * We need to remove the canva,
616                                  * or the image is not resized to the crop:
617                                  * http://php.net/manual/en/imagick.cropimage.php#97232
618                                  */
619                                 $this->image->setImagePage(0, 0, 0, 0);
620                         } while ($this->image->nextImage());
621                         return $this->scaleDown($max);
622                 }
623
624                 $dest = imagecreatetruecolor($max, $max);
625                 imagealphablending($dest, false);
626                 imagesavealpha($dest, true);
627                 if ($this->type=='image/png') {
628                         imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
629                 }
630                 imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
631                 if ($this->image) {
632                         imagedestroy($this->image);
633                 }
634                 $this->image = $dest;
635                 $this->width  = imagesx($this->image);
636                 $this->height = imagesy($this->image);
637         }
638
639         /**
640          * @param string $path file path
641          * @return mixed
642          */
643         public function saveToFilePath($path)
644         {
645                 if (!$this->isValid()) {
646                         return false;
647                 }
648
649                 $string = $this->asString();
650
651                 $a = get_app();
652
653                 $stamp1 = microtime(true);
654                 file_put_contents($path, $string);
655                 $a->save_timestamp($stamp1, "file");
656         }
657
658         /**
659          * @brief Magic method allowing string casting of an Image object
660          *
661          * Ex: $data = $Image->asString();
662          * can be replaced by
663          * $data = (string) $Image;
664          *
665          * @return string
666          */
667         public function __toString() {
668                 return $this->asString();
669         }
670
671         /**
672          * @return mixed
673          */
674         public function asString()
675         {
676                 if (!$this->isValid()) {
677                         return false;
678                 }
679
680                 if ($this->isImagick()) {
681                         /* Clean it */
682                         $this->image = $this->image->deconstructImages();
683                         $string = $this->image->getImagesBlob();
684                         return $string;
685                 }
686
687                 $quality = false;
688
689                 ob_start();
690
691                 // Enable interlacing
692                 imageinterlace($this->image, true);
693
694                 switch ($this->getType()) {
695                         case "image/png":
696                                 $quality = Config::get('system', 'png_quality');
697                                 if ((!$quality) || ($quality > 9)) {
698                                         $quality = PNG_QUALITY;
699                                 }
700                                 imagepng($this->image, null, $quality);
701                                 break;
702                         case "image/jpeg":
703                                 $quality = Config::get('system', 'jpeg_quality');
704                                 if ((!$quality) || ($quality > 100)) {
705                                         $quality = JPEG_QUALITY;
706                                 }
707                                 imagejpeg($this->image, null, $quality);
708                 }
709                 $string = ob_get_contents();
710                 ob_end_clean();
711
712                 return $string;
713         }
714
715         /**
716          * Guess image mimetype from filename or from Content-Type header
717          *
718          * @param string  $filename Image filename
719          * @param boolean $fromcurl Check Content-Type header from curl request
720          *
721          * @return object
722          */
723         public static function guessType($filename, $fromcurl = false)
724         {
725                 logger('Image: guessType: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG);
726                 $type = null;
727                 if ($fromcurl) {
728                         $a = get_app();
729                         $headers=array();
730                         $h = explode("\n", $a->get_curl_headers());
731                         foreach ($h as $l) {
732                                 list($k,$v) = array_map("trim", explode(":", trim($l), 2));
733                                 $headers[$k] = $v;
734                         }
735                         if (array_key_exists('Content-Type', $headers))
736                                 $type = $headers['Content-Type'];
737                 }
738                 if (is_null($type)) {
739                         // Guessing from extension? Isn't that... dangerous?
740                         if (class_exists('Imagick') && file_exists($filename) && is_readable($filename)) {
741                                 /**
742                                  * Well, this not much better,
743                                  * but at least it comes from the data inside the image,
744                                  * we won't be tricked by a manipulated extension
745                                  */
746                                 $image = new Imagick($filename);
747                                 $type = $image->getImageMimeType();
748                                 $image->setInterlaceScheme(Imagick::INTERLACE_PLANE);
749                         } else {
750                                 $ext = pathinfo($filename, PATHINFO_EXTENSION);
751                                 $types = self::supportedTypes();
752                                 $type = "image/jpeg";
753                                 foreach ($types as $m => $e) {
754                                         if ($ext == $e) {
755                                                 $type = $m;
756                                         }
757                                 }
758                         }
759                 }
760                 logger('Image: guessType: type='.$type, LOGGER_DEBUG);
761                 return $type;
762         }
763
764         /**
765          * @param string $url url
766          * @return object
767          */
768         public static function getInfoFromURL($url)
769         {
770                 $data = array();
771
772                 $data = Cache::get($url);
773
774                 if (is_null($data) || !$data || !is_array($data)) {
775                         $img_str = fetch_url($url, true, $redirects, 4);
776                         $filesize = strlen($img_str);
777
778                         if (function_exists("getimagesizefromstring")) {
779                                 $data = getimagesizefromstring($img_str);
780                         } else {
781                                 $tempfile = tempnam(get_temppath(), "cache");
782
783                                 $a = get_app();
784                                 $stamp1 = microtime(true);
785                                 file_put_contents($tempfile, $img_str);
786                                 $a->save_timestamp($stamp1, "file");
787
788                                 $data = getimagesize($tempfile);
789                                 unlink($tempfile);
790                         }
791
792                         if ($data) {
793                                 $data["size"] = $filesize;
794                         }
795
796                         Cache::set($url, $data);
797                 }
798
799                 return $data;
800         }
801
802         /**
803          * @param integer $width  width
804          * @param integer $height height
805          * @param integer $max    max
806          * @return array
807          */
808         public static function getScalingDimensions($width, $height, $max)
809         {
810                 $dest_width = $dest_height = 0;
811
812                 if ((!$width) || (!$height)) {
813                         return false;
814                 }
815
816                 if ($width > $max && $height > $max) {
817                         // very tall image (greater than 16:9)
818                         // constrain the width - let the height float.
819
820                         if ((($height * 9) / 16) > $width) {
821                                 $dest_width = $max;
822                                 $dest_height = intval(($height * $max) / $width);
823                         } elseif ($width > $height) {
824                                 // else constrain both dimensions
825                                 $dest_width = $max;
826                                 $dest_height = intval(($height * $max) / $width);
827                         } else {
828                                 $dest_width = intval(($width * $max) / $height);
829                                 $dest_height = $max;
830                         }
831                 } else {
832                         if ($width > $max) {
833                                 $dest_width = $max;
834                                 $dest_height = intval(($height * $max) / $width);
835                         } else {
836                                 if ($height > $max) {
837                                         // very tall image (greater than 16:9)
838                                         // but width is OK - don't do anything
839
840                                         if ((($height * 9) / 16) > $width) {
841                                                 $dest_width = $width;
842                                                 $dest_height = $height;
843                                         } else {
844                                                 $dest_width = intval(($width * $max) / $height);
845                                                 $dest_height = $max;
846                                         }
847                                 } else {
848                                         $dest_width = $width;
849                                         $dest_height = $height;
850                                 }
851                         }
852                 }
853                 return array("width" => $dest_width, "height" => $dest_height);
854         }
855
856         /**
857          * @brief This function is used by the fromgplus addon
858          * @param object  $a         App
859          * @param integer $uid       user id
860          * @param string  $imagedata optional, default empty
861          * @param string  $url       optional, default empty
862          * @return array
863          */
864         public static function storePhoto(App $a, $uid, $imagedata = "", $url = "")
865         {
866                 $r = q(
867                         "SELECT `user`.`nickname`, `user`.`page-flags`, `contact`.`id` FROM `user` INNER JOIN `contact` on `user`.`uid` = `contact`.`uid`
868                         WHERE `user`.`uid` = %d AND `user`.`blocked` = 0 AND `contact`.`self` = 1 LIMIT 1",
869                         intval($uid)
870                 );
871
872                 if (!DBM::is_result($r)) {
873                         logger("Can't detect user data for uid ".$uid, LOGGER_DEBUG);
874                         return(array());
875                 }
876
877                 $page_owner_nick  = $r[0]['nickname'];
878
879                 /// @TODO
880                 /// $default_cid      = $r[0]['id'];
881                 /// $community_page   = (($r[0]['page-flags'] == PAGE_COMMUNITY) ? true : false);
882
883                 if ((strlen($imagedata) == 0) && ($url == "")) {
884                         logger("No image data and no url provided", LOGGER_DEBUG);
885                         return(array());
886                 } elseif (strlen($imagedata) == 0) {
887                         logger("Uploading picture from ".$url, LOGGER_DEBUG);
888
889                         $stamp1 = microtime(true);
890                         $imagedata = @file_get_contents($url);
891                         $a->save_timestamp($stamp1, "file");
892                 }
893
894                 $maximagesize = Config::get('system', 'maximagesize');
895
896                 if (($maximagesize) && (strlen($imagedata) > $maximagesize)) {
897                         logger("Image exceeds size limit of ".$maximagesize, LOGGER_DEBUG);
898                         return(array());
899                 }
900
901                 $tempfile = tempnam(get_temppath(), "cache");
902
903                 $stamp1 = microtime(true);
904                 file_put_contents($tempfile, $imagedata);
905                 $a->save_timestamp($stamp1, "file");
906
907                 $data = getimagesize($tempfile);
908
909                 if (!isset($data["mime"])) {
910                         unlink($tempfile);
911                         logger("File is no picture", LOGGER_DEBUG);
912                         return(array());
913                 }
914
915                 $Image = new Image($imagedata, $data["mime"]);
916
917                 if (!$Image->isValid()) {
918                         unlink($tempfile);
919                         logger("Picture is no valid picture", LOGGER_DEBUG);
920                         return(array());
921                 }
922
923                 $Image->orient($tempfile);
924                 unlink($tempfile);
925
926                 $max_length = Config::get('system', 'max_image_length');
927                 if (! $max_length) {
928                         $max_length = MAX_IMAGE_LENGTH;
929                 }
930
931                 if ($max_length > 0) {
932                         $Image->scaleDown($max_length);
933                 }
934
935                 $width = $Image->getWidth();
936                 $height = $Image->getHeight();
937
938                 $hash = photo_new_resource();
939
940                 $smallest = 0;
941
942                 // Pictures are always public by now
943                 //$defperm = '<'.$default_cid.'>';
944                 $defperm = "";
945                 $visitor = 0;
946
947                 $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, t('Wall Photos'), 0, 0, $defperm);
948
949                 if (!$r) {
950                         logger("Picture couldn't be stored", LOGGER_DEBUG);
951                         return(array());
952                 }
953
954                 $image = array("page" => System::baseUrl().'/photos/'.$page_owner_nick.'/image/'.$hash,
955                         "full" => System::baseUrl()."/photo/{$hash}-0.".$Image->getExt());
956
957                 if ($width > 800 || $height > 800) {
958                         $image["large"] = System::baseUrl()."/photo/{$hash}-0.".$Image->getExt();
959                 }
960
961                 if ($width > 640 || $height > 640) {
962                         $Image->scaleDown(640);
963                         $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, t('Wall Photos'), 1, 0, $defperm);
964                         if ($r) {
965                                 $image["medium"] = System::baseUrl()."/photo/{$hash}-1.".$Image->getExt();
966                         }
967                 }
968
969                 if ($width > 320 || $height > 320) {
970                         $Image->scaleDown(320);
971                         $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, t('Wall Photos'), 2, 0, $defperm);
972                         if ($r) {
973                                 $image["small"] = System::baseUrl()."/photo/{$hash}-2.".$Image->getExt();
974                         }
975                 }
976
977                 if ($width > 160 && $height > 160) {
978                         $x = 0;
979                         $y = 0;
980
981                         $min = $Image->getWidth();
982                         if ($min > 160) {
983                                 $x = ($min - 160) / 2;
984                         }
985
986                         if ($Image->getHeight() < $min) {
987                                 $min = $Image->getHeight();
988                                 if ($min > 160) {
989                                         $y = ($min - 160) / 2;
990                                 }
991                         }
992
993                         $min = 160;
994                         $Image->crop(160, $x, $y, $min, $min);
995
996                         $r = Photo::store($Image, $uid, $visitor, $hash, $tempfile, t('Wall Photos'), 3, 0, $defperm);
997                         if ($r) {
998                                 $image["thumb"] = System::baseUrl()."/photo/{$hash}-3.".$Image->getExt();
999                         }
1000                 }
1001
1002                 // Set the full image as preview image. This will be overwritten, if the picture is larger than 640.
1003                 $image["preview"] = $image["full"];
1004
1005                 // Deactivated, since that would result in a cropped preview, if the picture wasn't larger than 320
1006                 //if (isset($image["thumb"]))
1007                 //  $image["preview"] = $image["thumb"];
1008
1009                 // Unsure, if this should be activated or deactivated
1010                 //if (isset($image["small"]))
1011                 //  $image["preview"] = $image["small"];
1012
1013                 if (isset($image["medium"])) {
1014                         $image["preview"] = $image["medium"];
1015                 }
1016
1017                 return($image);
1018         }
1019 }