+
+ public function scaleToFit($maxWidth=null, $maxHeight=null, $crop=null)
+ {
+ return self::getScalingValues($this->width, $this->height,
+ $maxWidth, $maxHeight, $crop, $this->rotate);
+ }
+
+ /*
+ * Gets scaling values for images of various types. Cropping can be enabled.
+ *
+ * Values will scale _up_ to fit max values if cropping is enabled!
+ * With cropping disabled, the max value of each axis will be respected.
+ *
+ * @param $width int Original width
+ * @param $height int Original height
+ * @param $maxW int Resulting max width
+ * @param $maxH int Resulting max height
+ * @param $crop int Crop to the size (not preserving aspect ratio)
+ */
+ public static function getScalingValues($width, $height,
+ $maxW=null, $maxH=null,
+ $crop=null, $rotate=0)
+ {
+ $maxW = $maxW ?: common_config('thumbnail', 'width');
+ $maxH = $maxH ?: common_config('thumbnail', 'height');
+
+ if ($maxW < 1 || ($maxH !== null && $maxH < 1)) {
+ throw new ServerException('Bad parameters for ImageFile::getScalingValues');
+ } elseif ($maxH === null) {
+ // if maxH is null, we set maxH to equal maxW and enable crop
+ $maxH = $maxW;
+ $crop = true;
+ }
+
+ // Because GD doesn't understand EXIF orientation etc.
+ if (abs($rotate) == 90) {
+ $tmp = $width;
+ $width = $height;
+ $height = $tmp;
+ }
+
+ // Cropping data (for original image size). Default values, 0 and null,
+ // imply no cropping and with preserved aspect ratio (per axis).
+ $cx = 0; // crop x
+ $cy = 0; // crop y
+ $cw = null; // crop area width
+ $ch = null; // crop area height
+
+ if ($crop) {
+ $s_ar = $width / $height;
+ $t_ar = $maxW / $maxH;
+
+ $rw = $maxW;
+ $rh = $maxH;
+
+ // Source aspect ratio differs from target, recalculate crop points!
+ if ($s_ar > $t_ar) {
+ $cx = floor($width / 2 - $height * $t_ar / 2);
+ $cw = ceil($height * $t_ar);
+ } elseif ($s_ar < $t_ar) {
+ $cy = floor($height / 2 - $width / $t_ar / 2);
+ $ch = ceil($width / $t_ar);
+ }
+ } else {
+ $rw = $maxW;
+ $rh = ceil($height * $rw / $width);
+
+ // Scaling caused too large height, decrease to max accepted value
+ if ($rh > $maxH) {
+ $rh = $maxH;
+ $rw = ceil($width * $rh / $height);
+ }
+ }
+ return array(intval($rw), intval($rh),
+ intval($cx), intval($cy),
+ is_null($cw) ? $width : intval($cw),
+ is_null($ch) ? $height : intval($ch));
+ }
+
+ /**
+ * Animated GIF test, courtesy of frank at huddler dot com et al:
+ * http://php.net/manual/en/function.imagecreatefromgif.php#104473
+ * Modified so avoid landing inside of a header (and thus not matching our regexp).
+ */
+ protected function isAnimatedGif()
+ {
+ if (!($fh = @fopen($this->filepath, 'rb'))) {
+ return false;
+ }
+
+ $count = 0;
+ //an animated gif contains multiple "frames", with each frame having a
+ //header made up of:
+ // * a static 4-byte sequence (\x00\x21\xF9\x04)
+ // * 4 variable bytes
+ // * a static 2-byte sequence (\x00\x2C)
+ // In total the header is maximum 10 bytes.
+
+ // We read through the file til we reach the end of the file, or we've found
+ // at least 2 frame headers
+ while(!feof($fh) && $count < 2) {
+ $chunk = fread($fh, 1024 * 100); //read 100kb at a time
+ $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00\x2C#s', $chunk, $matches);
+ // rewind in case we ended up in the middle of the header, but avoid
+ // infinite loop (i.e. don't rewind if we're already in the end).
+ if (!feof($fh) && ftell($fh) >= 9) {
+ fseek($fh, -9, SEEK_CUR);
+ }
+ }
+
+ fclose($fh);
+ return $count > 1;
+ }