X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2FPhoto.php;h=69b08f62919bc7fe81855238ff9fea847dff556c;hb=1b74e3ff100426b86f0f7cdd2f967b7781d6f8fa;hp=06b90bb88af147a8cc3d49ed8b315c80818c68e9;hpb=c0d3d50fb58667e03a80a25676092afd3cf630d4;p=friendica.git diff --git a/include/Photo.php b/include/Photo.php index 06b90bb88a..69b08f6291 100644 --- a/include/Photo.php +++ b/include/Photo.php @@ -41,34 +41,18 @@ class Photo { public function __construct($data, $type=null) { $this->imagick = class_exists('Imagick'); $this->types = $this->supportedTypes(); - - if($this->is_imagick()) { - $this->image = new Imagick(); - $this->image->readImageBlob($data); - - /** - * Setup the image to the format of the one we just loaded, - * we'll change it to something else if we need to at the time we save it - */ - $this->image->setFormat($this->image->getImageFormat()); - - // If it is a gif, it may be animated, get it ready for any future operations - if($this->image->getFormat() !== "GIF") $this->image = $this->image->coalesceImages(); - } else { - if (!array_key_exists($type,$this->types)){ - $type='image/jpeg'; - } - $this->valid = false; - $this->type = $type; - $this->image = @imagecreatefromstring($data); - if($this->image !== FALSE) { - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - $this->valid = true; - imagealphablending($this->image, false); - imagesavealpha($this->image, true); - } + if (!array_key_exists($type,$this->types)){ + $type='image/jpeg'; } + $this->type = $type; + + if($this->is_imagick() && $this->load_data($data)) { + return true; + } else { + // Failed to load with Imagick, fallback + $this->imagick = false; + } + return $this->load_data($data); } public function __destruct() { @@ -86,6 +70,88 @@ class Photo { return $this->imagick; } + /** + * Maps Mime types to Imagick formats + */ + public function get_FormatsMap() { + $m = array( + 'image/jpeg' => 'JPG', + 'image/png' => 'PNG', + 'image/gif' => 'GIF' + ); + return $m; + } + + private function load_data($data) { + if($this->is_imagick()) { + $this->image = new Imagick(); + try { + $this->image->readImageBlob($data); + } + catch (Exception $e) { + // Imagick couldn't use the data + return false; + } + + /** + * Setup the image to the format it will be saved to + */ + $map = $this->get_FormatsMap(); + $format = $map[$type]; + $this->image->setFormat($format); + + // Always coalesce, if it is not a multi-frame image it won't hurt anyway + $this->image = $this->image->coalesceImages(); + + /** + * setup the compression here, so we'll do it only once + */ + switch($this->getType()){ + case "image/png": + $quality = get_config('system','png_quality'); + if((! $quality) || ($quality > 9)) + $quality = PNG_QUALITY; + /** + * From http://www.imagemagick.org/script/command-line-options.php#quality: + * + * 'For the MNG and PNG image formats, the quality value sets + * the zlib compression level (quality / 10) and filter-type (quality % 10). + * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering, + * unless the image has a color map, in which case it means compression level 7 with no PNG filtering' + */ + $quality = $quality * 10; + $this->image->setCompressionQuality($quality); + break; + case "image/jpeg": + $quality = get_config('system','jpeg_quality'); + if((! $quality) || ($quality > 100)) + $quality = JPEG_QUALITY; + $this->image->setCompressionQuality($quality); + } + + // The 'width' and 'height' properties are only used by non-Imagick routines. + $this->width = $this->image->getImageWidth(); + $this->height = $this->image->getImageHeight(); + $this->valid = true; + + return true; + } + + $this->valid = false; + $this->image = @imagecreatefromstring($data); + if($this->image !== FALSE) { + $this->width = imagesx($this->image); + $this->height = imagesy($this->image); + $this->valid = true; + imagealphablending($this->image, false); + imagesavealpha($this->image, true); + + return true; + } + + return false; + } + public function is_valid() { if($this->is_imagick()) return ($this->image !== FALSE); @@ -126,9 +192,6 @@ class Photo { if(!$this->is_valid()) return FALSE; - if($this->is_imagick()) { - return $this->image->getImageMimeType(); - } return $this->type; } @@ -143,21 +206,8 @@ class Photo { if(!$this->is_valid()) return FALSE; - if($this->is_imagick()) { - /** - * If it is not animated, there will be only one iteration here, - * so don't bother checking - */ - // Don't forget to go back to the first frame - $this->image->setFirstIterator(); - do { - $this->image->resizeImage($max, $max, imagick::FILTER_LANCZOS, 1, true); - } while ($this->image->nextImage()); - return; - } - - $width = $this->width; - $height = $this->height; + $width = $this->getWidth(); + $height = $this->getHeight(); $dest_width = $dest_height = 0; @@ -165,7 +215,18 @@ class Photo { return FALSE; if($width > $max && $height > $max) { - if($width > $height) { + + // very tall image (greater than 16:9) + // constrain the width - let the height float. + + if((($height * 9) / 16) > $width) { + $dest_width = $max; + $dest_height = intval(( $height * $max ) / $width); + } + + // else constrain both dimensions + + elseif($width > $height) { $dest_width = $max; $dest_height = intval(( $height * $max ) / $width); } @@ -181,8 +242,18 @@ class Photo { } else { if( $height > $max ) { - $dest_width = intval(( $width * $max ) / $height); - $dest_height = $max; + + // very tall image (greater than 16:9) + // but width is OK - don't do anything + + if((($height * 9) / 16) > $width) { + $dest_width = $width; + $dest_height = $height; + } + else { + $dest_width = intval(( $width * $max ) / $height); + $dest_height = $max; + } } else { $dest_width = $width; @@ -192,6 +263,29 @@ class Photo { } + if($this->is_imagick()) { + /** + * If it is not animated, there will be only one iteration here, + * so don't bother checking + */ + // Don't forget to go back to the first frame + $this->image->setFirstIterator(); + do { + + // FIXME - implement horizantal bias for scaling as in followin GD functions + // to allow very tall images to be constrained only horizontally. + + $this->image->scaleImage($dest_width, $dest_height); + } while ($this->image->nextImage()); + + // These may not be necessary any more + $this->width = $this->image->getImageWidth(); + $this->height = $this->image->getImageHeight(); + + return; + } + + $dest = imagecreatetruecolor( $dest_width, $dest_height ); imagealphablending($dest, false); imagesavealpha($dest, true); @@ -211,7 +305,7 @@ class Photo { if($this->is_imagick()) { $this->image->setFirstIterator(); do { - $this->image->rotateImage(new ImagickPixel(), $degrees); + $this->image->rotateImage(new ImagickPixel(), -$degrees); // ImageMagick rotates in the opposite direction of imagerotate() } while ($this->image->nextImage()); return; } @@ -259,7 +353,11 @@ class Photo { if( (! function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg') ) return; - $exif = exif_read_data($filename); + $exif = @exif_read_data($filename); + + if(! $exif) + return; + $ort = $exif['Orientation']; switch($ort) @@ -305,11 +403,9 @@ class Photo { if(!$this->is_valid()) return FALSE; - if($this->is_imagick()) - return $this->scaleImage($min); - $width = $this->width; - $height = $this->height; + $width = $this->getWidth(); + $height = $this->getHeight(); $dest_width = $dest_height = 0; @@ -343,6 +439,8 @@ class Photo { } } + if($this->is_imagick()) + return $this->scaleImage($dest_width,$dest_height); $dest = imagecreatetruecolor( $dest_width, $dest_height ); imagealphablending($dest, false); @@ -365,7 +463,7 @@ class Photo { if($this->is_imagick()) { $this->image->setFirstIterator(); do { - $this->image->resizeImage($dim, $dim, imagick::FILTER_LANCZOS, 1, false); + $this->image->scaleImage($dim, $dim); } while ($this->image->nextImage()); return; } @@ -387,19 +485,19 @@ class Photo { if(!$this->is_valid()) return FALSE; - if($this->is_imagick()) { - $this->image->setFirstIterator(); - do { - $this->image->cropImage($w, $h, $x, $y); - /** - * We need to remove the canva, - * or the image is not resized to the crop: - * http://php.net/manual/en/imagick.cropimage.php#97232 - */ - $this->image->setImagePage(0, 0, 0, 0); - } while ($this->image->nextImage()); - return $this->scaleImage($max); - } + if($this->is_imagick()) { + $this->image->setFirstIterator(); + do { + $this->image->cropImage($w, $h, $x, $y); + /** + * We need to remove the canva, + * or the image is not resized to the crop: + * http://php.net/manual/en/imagick.cropimage.php#97232 + */ + $this->image->setImagePage(0, 0, 0, 0); + } while ($this->image->nextImage()); + return $this->scaleImage($max); + } $dest = imagecreatetruecolor( $max, $max ); imagealphablending($dest, false); @@ -425,63 +523,32 @@ class Photo { if(!$this->is_valid()) return FALSE; - $quality = FALSE; + if($this->is_imagick()) { + /* Clean it */ + $this->image = $this->image->deconstructImages(); + $string = $this->image->getImagesBlob(); + return $string; + } - /** - * Hmmm, for Imagick - * we should do the conversion/compression at the initialisation i think - * This method may be called several times, - * and there is no need to do that more than once - */ + $quality = FALSE; - if(!$this->is_imagick()) ob_start(); + ob_start(); switch($this->getType()){ case "image/png": $quality = get_config('system','png_quality'); if((! $quality) || ($quality > 9)) $quality = PNG_QUALITY; - if($this->is_imagick()) { - /** - * From http://www.imagemagick.org/script/command-line-options.php#quality: - * - * 'For the MNG and PNG image formats, the quality value sets - * the zlib compression level (quality / 10) and filter-type (quality % 10). - * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering, - * unless the image has a color map, in which case it means compression level 7 with no PNG filtering' - */ - $quality = $quality * 10; - } else imagepng($this->image,NULL, $quality); - break; - case "image/gif": - // We change nothing here, do we? + imagepng($this->image,NULL, $quality); break; - default: - // Convert to jpeg by default + case "image/jpeg": $quality = get_config('system','jpeg_quality'); if((! $quality) || ($quality > 100)) $quality = JPEG_QUALITY; - if($this->is_imagick()) { - $this->image->setFormat('jpeg'); - logger('Photo: imageString: Unhandled mime type ('. $this->getType() .'), Imagick format is "'. $this->image->getFormat() .'"', LOGGER_DEBUG); - } - else imagejpeg($this->image,NULL,$quality); - } - - if($this->is_imagick()) { - if($quality !== FALSE) { - // Do we need to iterate for animations? - $this->image->setCompressionQuality($quality); - $this->image->stripImage(); - } - - /* Clean it */ - $this->image = $this->image->deconstructImages(); - $string = $this->image->getImagesBlob(); - } else { - $string = ob_get_contents(); - ob_end_clean(); + imagejpeg($this->image,NULL,$quality); } + $string = ob_get_contents(); + ob_end_clean(); return $string; } @@ -517,6 +584,7 @@ class Photo { `album` = '%s', `height` = %d, `width` = %d, + `datasize` = %d, `data` = '%s', `scale` = %d, `profile` = %d, @@ -537,6 +605,7 @@ class Photo { dbesc($album), intval($this->getHeight()), intval($this->getWidth()), + dbesc(strlen($this->imageString())), dbesc($this->imageString()), intval($scale), intval($profile), @@ -549,8 +618,8 @@ class Photo { } else { $r = q("INSERT INTO `photo` - ( `uid`, `contact-id`, `guid`, `resource-id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s' )", + ( `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` ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s' )", intval($uid), intval($cid), dbesc($guid), @@ -562,6 +631,7 @@ class Photo { dbesc($album), intval($this->getHeight()), intval($this->getWidth()), + dbesc(strlen($this->imageString())), dbesc($this->imageString()), intval($scale), intval($profile), @@ -598,7 +668,7 @@ function guess_image_type($filename, $fromcurl=false) { } if (is_null($type)){ // Guessing from extension? Isn't that... dangerous? - if($this->is_imagick()) { + if(class_exists('Imagick') && file_exists($filename) && is_readable($filename)) { /** * Well, this not much better, * but at least it comes from the data inside the image, @@ -628,7 +698,7 @@ function import_profile_photo($photo,$uid,$cid) { intval($uid), intval($cid) ); - if(count($r)) { + if(count($r) && strlen($r[0]['resource-id'])) { $hash = $r[0]['resource-id']; } else { @@ -640,11 +710,7 @@ function import_profile_photo($photo,$uid,$cid) { $filename = basename($photo); $img_str = fetch_url($photo,true); - if($this->is_imagick()) $type = null; - else { - // guess mimetype from headers or filename - $type = guess_image_type($photo,true); - } + $type = guess_image_type($photo,true); $img = new Photo($img_str, $type); if($img->is_valid()) {