From 7b996a3b73671ad2e80efd4fc1cc2a6b870077be Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 14:58:59 +0100 Subject: [PATCH 01/25] Styling --- src/ThumbnailCreator.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index abf1c84..aa02b49 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -347,20 +347,19 @@ public static function imageFlip($imgsrc, $mode = 0) $src_width = $width; $src_height = $height; - switch ( $mode ) - { + switch ($mode) { default: - case '0': //vertical - $src_y = $height -1; + case '0': // Vertical + $src_y = $height - 1; $src_height= -$height; break; - case '1': //horizontal - $src_x = $width -1; + case '1': // Horizontal + $src_x = $width - 1; $src_width = -$width; break; - case '2': //both - $src_x = $width -1; - $src_y = $height -1; + case '2': // Both + $src_x = $width - 1; + $src_y = $height - 1; $src_width = -$width; $src_height = -$height; break; @@ -368,8 +367,7 @@ public static function imageFlip($imgsrc, $mode = 0) $imgdest = imagecreatetruecolor ( $width, $height ); - if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $src_x, $src_y , $width, $height, $src_width, $src_height)) - { + if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $src_x, $src_y , $width, $height, $src_width, $src_height)) { return $imgdest; } From bb74ab0f26d0ab3acaffb7ca5c79ee172a8b31c1 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 15:45:21 +0100 Subject: [PATCH 02/25] Camelcase variables --- src/ThumbnailCreator.php | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index aa02b49..d15f733 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -342,32 +342,32 @@ public static function imageFlip($imgsrc, $mode = 0) $width = imagesx($imgsrc); $height = imagesy($imgsrc); - $src_x = 0; - $src_y = 0; - $src_width = $width; - $src_height = $height; + $srcX = 0; + $srcY = 0; + $srcWidth = $width; + $srcHeight = $height; switch ($mode) { default: case '0': // Vertical - $src_y = $height - 1; - $src_height= -$height; + $srcY = $height - 1; + $srcHeight= -$height; break; case '1': // Horizontal - $src_x = $width - 1; - $src_width = -$width; + $srcX = $width - 1; + $srcWidth = -$width; break; case '2': // Both - $src_x = $width - 1; - $src_y = $height - 1; - $src_width = -$width; - $src_height = -$height; + $srcX = $width - 1; + $srcY = $height - 1; + $srcWidth = -$width; + $srcHeight = -$height; break; } $imgdest = imagecreatetruecolor ( $width, $height ); - if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $src_x, $src_y , $width, $height, $src_width, $src_height)) { + if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $srcX, $srcY , $width, $height, $srcWidth, $srcHeight)) { return $imgdest; } From b46b58a892e2cdf3e14914ca624cfe4f58d1a030 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 15:45:45 +0100 Subject: [PATCH 03/25] Styling --- src/ThumbnailCreator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index d15f733..8806850 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -351,7 +351,7 @@ public static function imageFlip($imgsrc, $mode = 0) default: case '0': // Vertical $srcY = $height - 1; - $srcHeight= -$height; + $srcHeight = -$height; break; case '1': // Horizontal $srcX = $width - 1; @@ -365,9 +365,9 @@ public static function imageFlip($imgsrc, $mode = 0) break; } - $imgdest = imagecreatetruecolor ( $width, $height ); + $imgdest = imagecreatetruecolor($width, $height); - if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $srcX, $srcY , $width, $height, $srcWidth, $srcHeight)) { + if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight)) { return $imgdest; } From f70a55430d3d94a926f62a62f4a567c45ab81757 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 15:49:51 +0100 Subject: [PATCH 04/25] Use 'V', 'H' and 'HV' for image flip instead of numbers --- src/ThumbnailCreator.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index 8806850..a29b66c 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -300,23 +300,23 @@ public static function imageSetOrientationFromExif($img, $orientation) { switch ($orientation) { case 2: // horizontal flip - $img = self::imageFlip($img, 1); + $img = self::imageFlip($img, 'H'); break; case 3: // 180 rotate left $img = imagerotate($img, 180, 0); break; case 4: // vertical flip - $img = self::imageFlip($img, 0); + $img = self::imageFlip($img, 'V'); break; case 5: // vertical flip + 90 rotate right - $img = self::imageFlip($img, 0); + $img = self::imageFlip($img, 'V'); $img = imagerotate($img, -90, 0); break; case 6: // 90 rotate right $img = imagerotate($img, -90, 0); break; case 7: // horizontal flip + 90 rotate right - $img = self::imageFlip($img, 1); + $img = self::imageFlip($img, 'H'); $img = imagerotate($img, -90, 0); break; case 8: // 90 rotate left @@ -334,10 +334,10 @@ public static function imageSetOrientationFromExif($img, $orientation) * Thanks Jon Grant * * @param $imgsrc (image to flip) - * @param $mode (0 = vertical, 1 = horizontal, 2 = both) - defaults to vertical flip + * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both) - defaults to vertical flip * */ - public static function imageFlip($imgsrc, $mode = 0) + public static function imageFlip($imgsrc, $mode = 'V') { $width = imagesx($imgsrc); $height = imagesy($imgsrc); @@ -349,15 +349,15 @@ public static function imageFlip($imgsrc, $mode = 0) switch ($mode) { default: - case '0': // Vertical + case 'V': // Vertical $srcY = $height - 1; $srcHeight = -$height; break; - case '1': // Horizontal + case 'H': // Horizontal $srcX = $width - 1; $srcWidth = -$width; break; - case '2': // Both + case 'HV': // Both $srcX = $width - 1; $srcY = $height - 1; $srcWidth = -$width; From 8439454c5cb507f076d4d04efcb2a7472d837608 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 15:55:18 +0100 Subject: [PATCH 05/25] Rename parameter --- src/ThumbnailCreator.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index a29b66c..4f1ee0b 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -333,14 +333,14 @@ public static function imageSetOrientationFromExif($img, $orientation) * Based on http://stackoverflow.com/a/10001884/1136593 * Thanks Jon Grant * - * @param $imgsrc (image to flip) + * @param $img (image to flip) * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both) - defaults to vertical flip * */ - public static function imageFlip($imgsrc, $mode = 'V') + public static function imageFlip($img, $mode = 'V') { - $width = imagesx($imgsrc); - $height = imagesy($imgsrc); + $width = imagesx($img); + $height = imagesy($img); $srcX = 0; $srcY = 0; @@ -367,10 +367,10 @@ public static function imageFlip($imgsrc, $mode = 'V') $imgdest = imagecreatetruecolor($width, $height); - if (imagecopyresampled($imgdest, $imgsrc, 0, 0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight)) { + if (imagecopyresampled($imgdest, $img, 0, 0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight)) { return $imgdest; } - return $imgsrc; + return $img; } } From 3138301c5736fe49616888f7a0d468ab7a2f94d0 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 15:59:16 +0100 Subject: [PATCH 06/25] Change way returning flipped image --- src/ThumbnailCreator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index 4f1ee0b..bc43a47 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -368,7 +368,7 @@ public static function imageFlip($img, $mode = 'V') $imgdest = imagecreatetruecolor($width, $height); if (imagecopyresampled($imgdest, $img, 0, 0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight)) { - return $imgdest; + $img = $imgdest; } return $img; From 6d1c2389fa536c3e62e86a5a5c17a48d1f2f4f0e Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 16:02:18 +0100 Subject: [PATCH 07/25] Default is now flipping --- src/ThumbnailCreator.php | 61 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index bc43a47..cadccc1 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -334,41 +334,42 @@ public static function imageSetOrientationFromExif($img, $orientation) * Thanks Jon Grant * * @param $img (image to flip) - * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both) - defaults to vertical flip + * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both) - defaults to no flip * */ - public static function imageFlip($img, $mode = 'V') + public static function imageFlip($img, $mode = '') { - $width = imagesx($img); - $height = imagesy($img); - - $srcX = 0; - $srcY = 0; - $srcWidth = $width; - $srcHeight = $height; - - switch ($mode) { - default: - case 'V': // Vertical - $srcY = $height - 1; - $srcHeight = -$height; - break; - case 'H': // Horizontal - $srcX = $width - 1; - $srcWidth = -$width; - break; - case 'HV': // Both - $srcX = $width - 1; - $srcY = $height - 1; - $srcWidth = -$width; - $srcHeight = -$height; - break; - } + if ($mode === 'V' || $mode === 'H' || $mode === 'HV') { + $width = imagesx($img); + $height = imagesy($img); + + $srcX = 0; + $srcY = 0; + $srcWidth = $width; + $srcHeight = $height; + + switch ($mode) { + case 'V': // Vertical + $srcY = $height - 1; + $srcHeight = -$height; + break; + case 'H': // Horizontal + $srcX = $width - 1; + $srcWidth = -$width; + break; + case 'HV': // Both + $srcX = $width - 1; + $srcY = $height - 1; + $srcWidth = -$width; + $srcHeight = -$height; + break; + } - $imgdest = imagecreatetruecolor($width, $height); + $imgdest = imagecreatetruecolor($width, $height); - if (imagecopyresampled($imgdest, $img, 0, 0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight)) { - $img = $imgdest; + if (imagecopyresampled($imgdest, $img, 0, 0, $srcX, $srcY, $width, $height, $srcWidth, $srcHeight)) { + $img = $imgdest; + } } return $img; From ed08e3e5874b94547879d3a62998d20dcace5adb Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 16:18:42 +0100 Subject: [PATCH 08/25] Add rotation to imageFlip() resulting in imageFlipRotate() --- src/ThumbnailCreator.php | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index cadccc1..10ecd98 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -300,27 +300,25 @@ public static function imageSetOrientationFromExif($img, $orientation) { switch ($orientation) { case 2: // horizontal flip - $img = self::imageFlip($img, 'H'); + $img = self::imageFlipRotate($img, 'H', 0); break; case 3: // 180 rotate left - $img = imagerotate($img, 180, 0); + $img = self::imageFlipRotate($img, '', 180); break; case 4: // vertical flip - $img = self::imageFlip($img, 'V'); + $img = self::imageFlipRotate($img, 'V', 0); break; case 5: // vertical flip + 90 rotate right - $img = self::imageFlip($img, 'V'); - $img = imagerotate($img, -90, 0); + $img = self::imageFlipRotate($img, 'V', -90); break; case 6: // 90 rotate right - $img = imagerotate($img, -90, 0); + $img = self::imageFlipRotate($img, '', -90); break; case 7: // horizontal flip + 90 rotate right - $img = self::imageFlip($img, 'H'); - $img = imagerotate($img, -90, 0); + $img = self::imageFlipRotate($img, 'H', -90); break; case 8: // 90 rotate left - $img = imagerotate($img, 90, 0); + $img = self::imageFlipRotate($img, '', 90); break; } @@ -328,17 +326,19 @@ public static function imageSetOrientationFromExif($img, $orientation) } /** - * Image Flip + * Image flip and rotate * * Based on http://stackoverflow.com/a/10001884/1136593 * Thanks Jon Grant * - * @param $img (image to flip) - * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both) - defaults to no flip + * @param $img (image to flip and/or rotate) + * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both, '' = none) + * @param $angle * */ - public static function imageFlip($img, $mode = '') + public static function imageFlipRotate($img, $mode, $angle) { + // Flip the image if ($mode === 'V' || $mode === 'H' || $mode === 'HV') { $width = imagesx($img); $height = imagesy($img); @@ -372,6 +372,11 @@ public static function imageFlip($img, $mode = '') } } + // Rotate the image + if ($rotation !== 0) { + $img = imagerotate($img, $angle, 0); + } + return $img; } } From 0529aac313f9c34efc92f8eba1ae09d4933e092d Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 16:24:44 +0100 Subject: [PATCH 09/25] Change exif orientation value detection --- src/ThumbnailCreator.php | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index 10ecd98..ded726d 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -298,31 +298,22 @@ protected function getOutput($imageContent, $type) **/ public static function imageSetOrientationFromExif($img, $orientation) { - switch ($orientation) { - case 2: // horizontal flip - $img = self::imageFlipRotate($img, 'H', 0); - break; - case 3: // 180 rotate left - $img = self::imageFlipRotate($img, '', 180); - break; - case 4: // vertical flip - $img = self::imageFlipRotate($img, 'V', 0); - break; - case 5: // vertical flip + 90 rotate right - $img = self::imageFlipRotate($img, 'V', -90); - break; - case 6: // 90 rotate right - $img = self::imageFlipRotate($img, '', -90); - break; - case 7: // horizontal flip + 90 rotate right - $img = self::imageFlipRotate($img, 'H', -90); - break; - case 8: // 90 rotate left - $img = self::imageFlipRotate($img, '', 90); - break; - } + $orient = array( + 2 => array('H', 0), // horizontal flip + 3 => array('', 180), // 180 rotate left + 4 => array('V', 0), // vertical flip + 5 => array('V', -90), // vertical flip + 90 rotate right + 6 => array('', -90), // 90 rotate right + 7 => array('H', -90), // horizontal flip + 90 rotate right + 8 => array('', 90), // 90 rotate left + ); + if (isset($orient[$orientation])) { + list($mode, $angle) = $orient[$orientation]; - return $img; + return self::imageFlipRotate($img, $mode, $angle); + } else { + return $img; + } } /** From ebc5fbe089c2aeda98cc583a1e836051e091d06f Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 16:29:56 +0100 Subject: [PATCH 10/25] Fix wrong variable name --- src/ThumbnailCreator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index ded726d..e255648 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -364,7 +364,7 @@ public static function imageFlipRotate($img, $mode, $angle) } // Rotate the image - if ($rotation !== 0) { + if ($angle !== 0) { $img = imagerotate($img, $angle, 0); } From 69edb8daf527aff3f80b057780fa02b7602b4de6 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 16:40:01 +0100 Subject: [PATCH 11/25] Eliminate imageSetOrientationFromExif() --- src/ThumbnailCreator.php | 47 +++++++++++++++------------------------- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index e255648..b691d22 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -174,11 +174,24 @@ protected function doResize($src, $width, $height, $crop = false, $fit = false, break; case 'jpg': $img = imagecreatefromjpeg($src); + // Handle exif orientation $exif = $this->exifOrientation ? exif_read_data($src) : false; - if ($exif !== false && isset($exif['Orientation'])) { - $img = self::imageSetOrientationFromExif($img, $exif['Orientation']); - $w = imagesx($img); - $h = imagesy($img); + if ($exif !== false) { + $exifMode = array( + 2 => array('H', 0), + 3 => array('', 180), + 4 => array('V', 0), + 5 => array('V', -90), + 6 => array('', -90), + 7 => array('H', -90), + 8 => array('', 90), + ); + if (isset($exifMode[$exif['Orientation']])) { + list($mode, $angle) = $orient[$orientation]; + $img = self::imageFlipRotate($img, $mode, $angle); + $w = imagesx($img); + $h = imagesy($img); + } } break; case 'png': @@ -290,32 +303,6 @@ protected function getOutput($imageContent, $type) return false; } - /** - * Sets orientation of image based on exif data - * - * @param $img image resource - * @param $orientation the exif image orientation - **/ - public static function imageSetOrientationFromExif($img, $orientation) - { - $orient = array( - 2 => array('H', 0), // horizontal flip - 3 => array('', 180), // 180 rotate left - 4 => array('V', 0), // vertical flip - 5 => array('V', -90), // vertical flip + 90 rotate right - 6 => array('', -90), // 90 rotate right - 7 => array('H', -90), // horizontal flip + 90 rotate right - 8 => array('', 90), // 90 rotate left - ); - if (isset($orient[$orientation])) { - list($mode, $angle) = $orient[$orientation]; - - return self::imageFlipRotate($img, $mode, $angle); - } else { - return $img; - } - } - /** * Image flip and rotate * From e154aecf71ee3834091829d000043970c5bb8bae Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 16:50:21 +0100 Subject: [PATCH 12/25] Only allow distinct rotation angles --- src/ThumbnailCreator.php | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index b691d22..d7b7d50 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -178,16 +178,16 @@ protected function doResize($src, $width, $height, $crop = false, $fit = false, $exif = $this->exifOrientation ? exif_read_data($src) : false; if ($exif !== false) { $exifMode = array( - 2 => array('H', 0), - 3 => array('', 180), - 4 => array('V', 0), - 5 => array('V', -90), - 6 => array('', -90), - 7 => array('H', -90), - 8 => array('', 90), + 2 => array('H', ''), + 3 => array('', 'T'), + 4 => array('V', ''), + 5 => array('V', 'L'), + 6 => array('', 'L'), + 7 => array('H', 'L'), + 8 => array('', 'R'), ); if (isset($exifMode[$exif['Orientation']])) { - list($mode, $angle) = $orient[$orientation]; + list($mode, $angle) = $exifMode[$exif['Orientation']]; $img = self::imageFlipRotate($img, $mode, $angle); $w = imagesx($img); $h = imagesy($img); @@ -310,8 +310,8 @@ protected function getOutput($imageContent, $type) * Thanks Jon Grant * * @param $img (image to flip and/or rotate) - * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both, '' = none) - * @param $angle + * @param $mode ('V' = vertical, 'H' = horizontal, 'HV' = both) + * @param $angle ('L' = -90°, 'R' = +90°, 'T' = 180°) * */ public static function imageFlipRotate($img, $mode, $angle) @@ -351,8 +351,9 @@ public static function imageFlipRotate($img, $mode, $angle) } // Rotate the image - if ($angle !== 0) { - $img = imagerotate($img, $angle, 0); + if ($angle === 'L' || $angle === 'R' || $angle === 'T') { + $rotate = array('L' => -90, 'R' => 90, 'T' => 180); + $img = imagerotate($img, $rotate[$angle], 0); } return $img; From c753c4e5530eb2e82e2bb47b668441c42b60460c Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 16:55:14 +0100 Subject: [PATCH 13/25] Simplify mode handling --- src/ThumbnailCreator.php | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index d7b7d50..6ceb768 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -176,22 +176,12 @@ protected function doResize($src, $width, $height, $crop = false, $fit = false, $img = imagecreatefromjpeg($src); // Handle exif orientation $exif = $this->exifOrientation ? exif_read_data($src) : false; - if ($exif !== false) { - $exifMode = array( - 2 => array('H', ''), - 3 => array('', 'T'), - 4 => array('V', ''), - 5 => array('V', 'L'), - 6 => array('', 'L'), - 7 => array('H', 'L'), - 8 => array('', 'R'), - ); - if (isset($exifMode[$exif['Orientation']])) { - list($mode, $angle) = $exifMode[$exif['Orientation']]; - $img = self::imageFlipRotate($img, $mode, $angle); - $w = imagesx($img); - $h = imagesy($img); - } + $modes = array(2 => 'H-', 3 => '-T', 4 => 'V-', 5 => 'VL', 6 => '-L', 7 => 'HL', 8 => '-R'); + if ($exif !== false && isset($modes[$exif['Orientation']])) { + $mode = $modes[$exif['Orientation']]; + $img = self::imageFlipRotate($img, $mode[0], $mode[1]); + $w = imagesx($img); + $h = imagesy($img); } break; case 'png': From 2b3df1e7893a32af50a9be02aa50f402a07d953f Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 18:08:30 +0100 Subject: [PATCH 14/25] Require bolt/bolt again --- composer.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 7f9efe4..947aa20 100644 --- a/composer.json +++ b/composer.json @@ -8,23 +8,27 @@ "email": "riley.ross@gmail.com" } ], - + "minimum-stability": "dev", - + "require": { "symfony/http-foundation": "~2.5", "ext-gd": "*" }, - - + + "require-dev": { + "bolt/bolt": "~2.0" + }, + "autoload": { "psr-4": { "Bolt\\Thumbs\\": "src", "Bolt\\Thumbs\\Tests\\": "tests" } }, + "extra": { "branch-alias": { "dev-master": "2.1-dev" } } - + } From 68990d13e379d1432d0fb51e27a43f25e9adb977 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 18:29:30 +0100 Subject: [PATCH 15/25] Typo --- phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0d11db1..1a282a1 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -7,7 +7,7 @@ verbose="false"> - + tests/ From 725e6b78f1a2d097aacee8296cd0fda49643f251 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 19:24:09 +0100 Subject: [PATCH 16/25] Don't require "bolt/app/lib.php" anymore --- tests/ThumbnailCreatorTest.php | 1 - tests/ThumbnailResponderTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/ThumbnailCreatorTest.php b/tests/ThumbnailCreatorTest.php index ce5de1a..46b20ae 100644 --- a/tests/ThumbnailCreatorTest.php +++ b/tests/ThumbnailCreatorTest.php @@ -23,7 +23,6 @@ public function setup() $this->jpg = __DIR__ . '/images/generic-logo.jpg'; $this->gif = __DIR__ . '/images/generic-logo.gif'; $this->png = __DIR__ . '/images/generic-logo.png'; - require_once __DIR__ . '/../vendor/bolt/bolt/app/lib.php'; } public function testSetup() diff --git a/tests/ThumbnailResponderTest.php b/tests/ThumbnailResponderTest.php index d70fbbd..22fc8fe 100644 --- a/tests/ThumbnailResponderTest.php +++ b/tests/ThumbnailResponderTest.php @@ -15,7 +15,6 @@ class ThumbnailResponderTest extends \PHPUnit_Framework_TestCase public function setup() { @mkdir(__DIR__ . '/tmp/cache/', 0777, true); - require_once __DIR__ . '/../vendor/bolt/bolt/app/lib.php'; } public function testBasicRequestParsing() From 0efd3e66ab19cf62a4eccb472e4940b676611cfb Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 19:42:28 +0100 Subject: [PATCH 17/25] Changed buggy orientation handling --- src/ThumbnailCreator.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index 6ceb768..742ff04 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -177,8 +177,9 @@ protected function doResize($src, $width, $height, $crop = false, $fit = false, // Handle exif orientation $exif = $this->exifOrientation ? exif_read_data($src) : false; $modes = array(2 => 'H-', 3 => '-T', 4 => 'V-', 5 => 'VL', 6 => '-L', 7 => 'HL', 8 => '-R'); - if ($exif !== false && isset($modes[$exif['Orientation']])) { - $mode = $modes[$exif['Orientation']]; + $orientation = isset($exif['Orientation']) ? $exif['Orientation'] : 0; + if (isset($modes[$orientation])) { + $mode = $modes[$orientation]; $img = self::imageFlipRotate($img, $mode[0], $mode[1]); $w = imagesx($img); $h = imagesy($img); From be7f35889f2a1ae8cc417658d81d99a429a907e6 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 20:05:13 +0100 Subject: [PATCH 18/25] Add a correction in testPortraitResize() for different gd libs --- tests/ThumbnailCreatorTest.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/ThumbnailCreatorTest.php b/tests/ThumbnailCreatorTest.php index 46b20ae..c5d6c88 100644 --- a/tests/ThumbnailCreatorTest.php +++ b/tests/ThumbnailCreatorTest.php @@ -170,8 +170,15 @@ public function testPortraitResize() $result = $creator->resize(array('width' => 200, 'height' => 500)); $compare = __DIR__ . '/images/timthumbs/resize_sample2_200_500.jpg'; file_put_contents(__DIR__ . '/tmp/test.jpg', $result); + + // Original compare image is with v80, v90 creates a 2 byte smaller image (perhaps only on windows?) + $correction = 0; + if (preg_match('%CREATOR: gd-jpeg v1\.0 \(using IJG JPEG v(\d+)\)%', $result, $pm) && $pm[1] == '90') { + $correction = 2; + } + $this->assertEquals(getimagesize($compare), getimagesize(__DIR__ . '/tmp/test.jpg')); - $this->assertEquals(filesize($compare), filesize(__DIR__ . '/tmp/test.jpg')); + $this->assertEquals(filesize($compare), filesize(__DIR__ . '/tmp/test.jpg') + $correction); } public function testPortraitFit() From ff73bcb13c463db77f0838a0cb73c7bc5cddd2c8 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 20:09:57 +0100 Subject: [PATCH 19/25] Fix uses --- tests/ThumbnailCreatorTest.php | 6 ------ tests/ThumbnailResponderTest.php | 2 -- 2 files changed, 8 deletions(-) diff --git a/tests/ThumbnailCreatorTest.php b/tests/ThumbnailCreatorTest.php index c5d6c88..6196ac0 100644 --- a/tests/ThumbnailCreatorTest.php +++ b/tests/ThumbnailCreatorTest.php @@ -1,13 +1,7 @@ Date: Sun, 4 Jan 2015 20:43:28 +0100 Subject: [PATCH 20/25] Fix ThumbnailResponderTest --- tests/ThumbnailResponderTest.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/ThumbnailResponderTest.php b/tests/ThumbnailResponderTest.php index cd8e878..03dedcd 100644 --- a/tests/ThumbnailResponderTest.php +++ b/tests/ThumbnailResponderTest.php @@ -1,11 +1,16 @@ __DIR__, + 'pathmanager' => new PlatformFileSystemPathFactory() + ) + ); + + $config = new ResourceManager($container); $config->setPath('cache', 'tmp/cache'); $config->setPath('files', 'images'); $config->compat(); $app = new Application(array('resources' => $config)); - $app->register(new \Bolt\Provider\CacheServiceProvider()); + $app->register(new CacheServiceProvider()); $responder = new ThumbnailResponder($app, $request); $responder->initialize(); @@ -81,7 +93,7 @@ public function tearDown() protected function rmdir($dir) { $iterator = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS), + new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST ); foreach ($iterator as $file) { From 9f1dd443efed41152ecfaa4be84483cc17640b35 Mon Sep 17 00:00:00 2001 From: rarila Date: Sun, 4 Jan 2015 21:08:35 +0100 Subject: [PATCH 21/25] Tabs to spaces --- composer.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 947aa20..423df53 100644 --- a/composer.json +++ b/composer.json @@ -16,9 +16,9 @@ "ext-gd": "*" }, - "require-dev": { - "bolt/bolt": "~2.0" - }, + "require-dev": { + "bolt/bolt": "~2.0" + }, "autoload": { "psr-4": { @@ -30,5 +30,4 @@ "extra": { "branch-alias": { "dev-master": "2.1-dev" } } - } From f5cc404c01e6cbc3289b0d68b0dc967fc4ae25c0 Mon Sep 17 00:00:00 2001 From: rarila Date: Mon, 5 Jan 2015 15:02:47 +0100 Subject: [PATCH 22/25] Add exif orientation test images --- tests/images/exif-orientation/1-top-left.jpg | Bin 0 -> 5555 bytes tests/images/exif-orientation/2-top-right.jpg | Bin 0 -> 7023 bytes tests/images/exif-orientation/3-bottom-right.jpg | Bin 0 -> 7416 bytes tests/images/exif-orientation/4-bottom-left.jpg | Bin 0 -> 7143 bytes tests/images/exif-orientation/5-left-top.jpg | Bin 0 -> 6768 bytes tests/images/exif-orientation/6-right-top.jpg | Bin 0 -> 6997 bytes tests/images/exif-orientation/7-right-bottom.jpg | Bin 0 -> 7310 bytes tests/images/exif-orientation/8-left-bottom.jpg | Bin 0 -> 7192 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/images/exif-orientation/1-top-left.jpg create mode 100644 tests/images/exif-orientation/2-top-right.jpg create mode 100644 tests/images/exif-orientation/3-bottom-right.jpg create mode 100644 tests/images/exif-orientation/4-bottom-left.jpg create mode 100644 tests/images/exif-orientation/5-left-top.jpg create mode 100644 tests/images/exif-orientation/6-right-top.jpg create mode 100644 tests/images/exif-orientation/7-right-bottom.jpg create mode 100644 tests/images/exif-orientation/8-left-bottom.jpg diff --git a/tests/images/exif-orientation/1-top-left.jpg b/tests/images/exif-orientation/1-top-left.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27a0d8bf5448974fbca2305993e4e21184db1edb GIT binary patch literal 5555 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 DM>!#! literal 0 HcmV?d00001 diff --git a/tests/images/exif-orientation/2-top-right.jpg b/tests/images/exif-orientation/2-top-right.jpg new file mode 100644 index 0000000000000000000000000000000000000000..85cac3d1956abbe0bc761317fded8f9bbd262b0c GIT binary patch literal 7023 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 Zz2m;)P-sNdY{1pje*w<}?(UcPF^J6FkA4;K6z1 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 literal 0 HcmV?d00001 diff --git a/tests/images/exif-orientation/5-left-top.jpg b/tests/images/exif-orientation/5-left-top.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a1671ba5295a4bcc10266cb2ea0f34b198d4d09 GIT binary patch literal 6768 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 literal 0 HcmV?d00001 diff --git a/tests/images/exif-orientation/6-right-top.jpg b/tests/images/exif-orientation/6-right-top.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fdc9e2c956931ea0fbe70d96fcfca408cd60d3fc GIT binary patch literal 6997 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 literal 0 HcmV?d00001 diff --git a/tests/images/exif-orientation/8-left-bottom.jpg b/tests/images/exif-orientation/8-left-bottom.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d9044eee191b91fa27ffc4a6db46a36df0455422 GIT binary patch literal 7192 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 bW6d&OC7fEFZB*y1K}gH|=kMfy@#Vh&c=#hc literal 0 HcmV?d00001 From 4aae3668bb4e8bf75157ef009b2b934653ead6cb Mon Sep 17 00:00:00 2001 From: rarila Date: Mon, 5 Jan 2015 15:43:48 +0100 Subject: [PATCH 23/25] =?UTF-8?q?Use=20270=C2=B0=20instead=20of=20-90?= =?UTF-8?q?=C2=B0=20to=20avoid=20a=20rounding=20problem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ThumbnailCreator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ThumbnailCreator.php b/src/ThumbnailCreator.php index 742ff04..3070d6f 100644 --- a/src/ThumbnailCreator.php +++ b/src/ThumbnailCreator.php @@ -343,7 +343,7 @@ public static function imageFlipRotate($img, $mode, $angle) // Rotate the image if ($angle === 'L' || $angle === 'R' || $angle === 'T') { - $rotate = array('L' => -90, 'R' => 90, 'T' => 180); + $rotate = array('L' => 270, 'R' => 90, 'T' => 180); $img = imagerotate($img, $rotate[$angle], 0); } From 03d468271b4769b52ffcd0e26dc4a7444f52c937 Mon Sep 17 00:00:00 2001 From: rarila Date: Mon, 5 Jan 2015 16:53:24 +0100 Subject: [PATCH 24/25] Add exif orientation tests --- tests/ThumbnailCreatorTest.php | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/ThumbnailCreatorTest.php b/tests/ThumbnailCreatorTest.php index 6196ac0..025b9b4 100644 --- a/tests/ThumbnailCreatorTest.php +++ b/tests/ThumbnailCreatorTest.php @@ -199,6 +199,39 @@ public function testPortraitBorder() $this->assertEquals(filesize($compare), filesize(__DIR__ . '/tmp/test.jpg')); } + public function testExifOrientation() + { + $images = array( + '1-top-left', + '2-top-right', + '3-bottom-right', + '4-bottom-left', + '5-left-top', + '6-right-top', + '7-right-bottom', + '8-left-bottom', + ); + $resize = array('width' => 200, 'height' => 100); + + foreach ($images as $name) { + $path = __DIR__ . '/tmp/' . $name . '.jpg'; + // Create test image + $creator = new ThumbnailCreator(); + $creator->setSource(new File(__DIR__ . '/images/exif-orientation/' . $name . '.jpg')); + $result = $creator->resize($resize); + file_put_contents($path, $result); + // Read test image + $img = imagecreatefromjpeg($path); + $width = imagesx($img); + $height = imagesy($img); + $rgb = imagecolorsforindex($img, imagecolorat($img, 0, 0)); + // Assert image size and red color (fuzzy) in the upper left corner + $this->assertEquals($resize['width'], $width, 'Wrong width!'); + $this->assertEquals($resize['height'], $height, 'Wrong height!'); + $this->assertTrue($rgb['red'] > 250 && $rgb['green'] < 5 && $rgb['blue'] < 5, 'Wrong orientation!'); + } + } + public function tearDown() { $tmp = __DIR__ . '/tmp/test.jpg'; From f389d377a360e4b1b79b35e0a92bdb5f9dee3169 Mon Sep 17 00:00:00 2001 From: rarila Date: Mon, 5 Jan 2015 16:59:19 +0100 Subject: [PATCH 25/25] Code styling --- src/ThumbnailResponder.php | 8 ++++---- tests/ThumbnailCreatorTest.php | 19 +++++++++---------- tests/ThumbnailResponderTest.php | 4 +--- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/ThumbnailResponder.php b/src/ThumbnailResponder.php index 944f188..ac897dc 100644 --- a/src/ThumbnailResponder.php +++ b/src/ThumbnailResponder.php @@ -51,12 +51,12 @@ public function __construct(Application $app, Request $request, ResizeInterface public function initialize() { if (null !== $this->app['config']->get('general/thumbnails/notfound_image')) { - $file = $this->app['resources']->getPath('app'). '/' .$this->app['config']->get('general/thumbnails/notfound_image'); + $file = $this->app['resources']->getPath('app') . '/' . $this->app['config']->get('general/thumbnails/notfound_image'); $this->resizer->setDefaultSource(new File($file, false)); } if (null !== $this->app['config']->get('general/thumbnails/error_image')) { - $file = $this->app['resources']->getPath('app'). '/' .$this->app['config']->get('general/thumbnails/error_image'); + $file = $this->app['resources']->getPath('app') . '/' . $this->app['config']->get('general/thumbnails/error_image'); $this->resizer->setErrorSource(new File($file, false)); } @@ -184,11 +184,11 @@ public function saveStatic($imageContent) $path = urldecode($this->request->getPathInfo()); try { $webroot = dirname($this->request->server->get('SCRIPT_FILENAME')); - $savePath = dirname($webroot.$path); + $savePath = dirname($webroot . $path); if (!is_dir($savePath)) { mkdir($savePath, 0777, true); } - file_put_contents($webroot.$path, $imageContent); + file_put_contents($webroot . $path, $imageContent); } catch (\Exception $e) { } diff --git a/tests/ThumbnailCreatorTest.php b/tests/ThumbnailCreatorTest.php index 025b9b4..644ee11 100644 --- a/tests/ThumbnailCreatorTest.php +++ b/tests/ThumbnailCreatorTest.php @@ -34,23 +34,23 @@ public function testFallbacksForBadDimensions() $creator = new ThumbnailCreator(); $creator->setSource($src); - $ok_width = 624; - $ok_height = 351; + $okWidth = 624; + $okHeight = 351; $testcases = array( array(), - array('width' => $ok_width, 'height' => -20), - array('width' => $ok_width), - array('height' => $ok_height), - array('width' => 'A', 'height' => $ok_height), - array('width' => 123.456, 'height' => $ok_height), + array('width' => $okWidth, 'height' => -20), + array('width' => $okWidth), + array('height' => $okHeight), + array('width' => 'A', 'height' => $okHeight), + array('width' => 123.456, 'height' => $okHeight), array('width' => 'both', 'height' => 'wrong'), ); foreach ($testcases as $parameters) { $creator->verify($parameters); - $this->assertEquals($ok_width, $creator->targetWidth); - $this->assertEquals($ok_height, $creator->targetHeight); + $this->assertEquals($okWidth, $creator->targetWidth); + $this->assertEquals($okHeight, $creator->targetHeight); } } @@ -238,6 +238,5 @@ public function tearDown() if (is_readable($tmp)) { unlink($tmp); } - } } diff --git a/tests/ThumbnailResponderTest.php b/tests/ThumbnailResponderTest.php index 03dedcd..d949a5b 100644 --- a/tests/ThumbnailResponderTest.php +++ b/tests/ThumbnailResponderTest.php @@ -34,7 +34,6 @@ public function testBasicRequestParsing() $this->assertEquals('240', $responder->height); $this->assertEquals('crop', $responder->action); $this->assertEquals('generic-logo.jpg', $responder->file); - } public function testParseWithSubdirectory() @@ -46,7 +45,6 @@ public function testParseWithSubdirectory() $responder = $this->initializeResponder($request); $this->assertEquals('subdir/generic-logo.jpg', $responder->file); - } public function testResponse() @@ -87,7 +85,7 @@ protected function initializeResponder($request) public function tearDown() { $this->rmdir(__DIR__ . '/tmp'); - @rmdir(__DIR__ .'/tmp'); + @rmdir(__DIR__ . '/tmp'); } protected function rmdir($dir)