From df0d9999b8e0bf2849cc938968f43432a1084713 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 31 Oct 2014 19:48:45 +0100 Subject: [PATCH 01/88] font work --- src/Intervention/Image/AbstractFont.php | 56 ++++++ src/Intervention/Image/Gd/Font.php | 254 ++++-------------------- src/Intervention/Image/Point.php | 31 +++ src/Intervention/Image/Size.php | 18 ++ tests/PointTest.php | 8 + 5 files changed, 148 insertions(+), 219 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 9e3cca92c..2bc5c2d85 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -53,6 +53,20 @@ abstract class AbstractFont */ public $file; + /** + * Width of textbox + * + * @var integer + */ + public $width; + + /** + * Height of textbox + * + * @var integer + */ + public $height; + /** * Draws font to given image on given position * @@ -220,6 +234,48 @@ public function getFile() return $this->file; } + /** + * Set width of textbox + * + * @param integer $width + * @return void + */ + public function width($width) + { + $this->width = $width; + } + + /** + * Get width of textbox + * + * @return integer + */ + public function getWidth() + { + return $this->width; + } + + /** + * Set height of textbox + * + * @param integer $height + * @return void + */ + public function height($height) + { + $this->height = $height; + } + + /** + * Get height of textbox + * + * @return integer + */ + public function getHeight() + { + return $this->height; + } + /** * Checks if current font has access to an applicable font file * diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index a01552498..3e77ed75a 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -16,240 +16,56 @@ protected function getPointSize() return intval(ceil($this->size * 0.75)); } - /** - * Filter function to access internal integer font values - * - * @return integer - */ - private function getInternalFont() - { - $internalfont = is_null($this->file) ? 1 : $this->file; - $internalfont = is_numeric($internalfont) ? $internalfont : false; - - if ( ! in_array($internalfont, array(1, 2, 3, 4, 5))) { - throw new \Intervention\Image\Exception\NotSupportedException( - sprintf('Internal GD font (%s) not available. Use only 1-5.', $internalfont) - ); - } - - return intval($internalfont); - } - - /** - * Get width of an internal font character - * - * @return integer - */ - private function getInternalFontWidth() - { - return $this->getInternalFont() + 4; - } - - /** - * Get height of an internal font character - * - * @return integer - */ - private function getInternalFontHeight() - { - switch ($this->getInternalFont()) { - case 1: - return 8; - - case 2: - return 14; - - case 3: - return 14; - - case 4: - return 16; - - case 5: - return 16; - } - } - - /** - * Calculates bounding box of current font setting - * - * @return Array - */ - public function getBoxSize() + public function applyToImage(Image $image, $posx = 0, $posy = 0) { - $box = array(); - - if ($this->hasApplicableFontFile()) { - - // get bounding box with angle 0 - $box = imagettfbbox($this->getPointSize(), 0, $this->file, $this->text); - - // rotate points manually - if ($this->angle != 0) { - - $angle = pi() * 2 - $this->angle * pi() * 2 / 360; - - for ($i=0; $i<4; $i++) { - $x = $box[$i * 2]; - $y = $box[$i * 2 + 1]; - $box[$i * 2] = cos($angle) * $x - sin($angle) * $y; - $box[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y; - } - } + // create empty resource + $canvas = imagecreatetruecolor(400, 400); - $box['width'] = intval(abs($box[4] - $box[0])); - $box['height'] = intval(abs($box[5] - $box[1])); + // set background color (2147483647) + imagefill($canvas, 0, 0, 3242424); - } else { - - // get current internal font size - $width = $this->getInternalFontWidth(); - $height = $this->getInternalFontHeight(); - - if (strlen($this->text) == 0) { - // no text -> no boxsize - $box['width'] = 0; - $box['height'] = 0; - } else { - // calculate boxsize - $box['width'] = strlen($this->text) * $width; - $box['height'] = $height; - } - } - - return $box; - } - - /** - * Draws font to given image at given position - * - * @param Image $image - * @param integer $posx - * @param integer $posy - * @return void - */ - public function applyToImage(Image $image, $posx = 0, $posy = 0) - { // parse text color $color = new Color($this->color); if ($this->hasApplicableFontFile()) { - if ($this->angle != 0 || is_string($this->align) || is_string($this->valign)) { - - $box = $this->getBoxSize(); - - $align = is_null($this->align) ? 'left' : strtolower($this->align); - $valign = is_null($this->valign) ? 'bottom' : strtolower($this->valign); - - // correction on position depending on v/h alignment - switch ($align.'-'.$valign) { - - case 'center-top': - $posx = $posx - round(($box[6]+$box[4])/2); - $posy = $posy - round(($box[7]+$box[5])/2); - break; - - case 'right-top': - $posx = $posx - $box[4]; - $posy = $posy - $box[5]; - break; - - case 'left-top': - $posx = $posx - $box[6]; - $posy = $posy - $box[7]; - break; - - case 'center-center': - case 'center-middle': - $posx = $posx - round(($box[0]+$box[4])/2); - $posy = $posy - round(($box[1]+$box[5])/2); - break; - - case 'right-center': - case 'right-middle': - $posx = $posx - round(($box[2]+$box[4])/2); - $posy = $posy - round(($box[3]+$box[5])/2); - break; - - case 'left-center': - case 'left-middle': - $posx = $posx - round(($box[0]+$box[6])/2); - $posy = $posy - round(($box[1]+$box[7])/2); - break; - - case 'center-bottom': - $posx = $posx - round(($box[0]+$box[2])/2); - $posy = $posy - round(($box[1]+$box[3])/2); - break; - - case 'right-bottom': - $posx = $posx - $box[2]; - $posy = $posy - $box[3]; - break; - - case 'left-bottom': - $posx = $posx - $box[0]; - $posy = $posy - $box[1]; - break; - } - } - // enable alphablending for imagettftext imagealphablending($image->getCore(), true); // draw ttf text - imagettftext($image->getCore(), $this->getPointSize(), $this->angle, $posx, $posy, $color->getInt(), $this->file, $this->text); - - } else { - - // get box size - $box = $this->getBoxSize(); - $width = $box['width']; - $height = $box['height']; - - // internal font specific position corrections - if ($this->getInternalFont() == 1) { - $top_correction = 1; - $bottom_correction = 2; - } elseif ($this->getInternalFont() == 3) { - $top_correction = 2; - $bottom_correction = 4; - } else { - $top_correction = 3; - $bottom_correction = 4; - } - - // x-position corrections for horizontal alignment - switch (strtolower($this->align)) { - case 'center': - $posx = ceil($posx - ($width / 2)); - break; - - case 'right': - $posx = ceil($posx - $width) + 1; - break; - } - - // y-position corrections for vertical alignment - switch (strtolower($this->valign)) { - case 'center': - case 'middle': - $posy = ceil($posy - ($height / 2)); - break; - - case 'top': - $posy = ceil($posy - $top_correction); - break; + imagettftext( + $canvas, + $this->getPointSize(), + 0, + 16, + 16, + $color->getInt(), + $this->file, + $this->text + ); - default: - case 'bottom': - $posy = round($posy - $height + $bottom_correction); - break; + if ($this->angle != 0) { + $canvas = imagerotate($canvas, $this->angle, 45454); } + + + // insert canvas + imagecopy( + $image->getCore(), + $canvas, + $posx, + $posy, + 0, + 0, + 400, + 400 + ); - // draw text - imagestring($image->getCore(), $this->getInternalFont(), $posx, $posy, $this->text, $color->getInt()); } } + + public function getBoxSize($text = null) + { + $text = is_null($text) ? $this->text : $text; + } } diff --git a/src/Intervention/Image/Point.php b/src/Intervention/Image/Point.php index bb17fb7c1..06063d68b 100644 --- a/src/Intervention/Image/Point.php +++ b/src/Intervention/Image/Point.php @@ -61,4 +61,35 @@ public function setPosition($x, $y) $this->setX($x); $this->setY($y); } + + /** + * Rotate point ccw around pivot + * + * @param float $angle + * @param Point $pivot + * @return Point + */ + public function rotate($angle, Point $pivot) + { + $sin = sin($angle); + $cos = cos($angle); + + // translate point + $this->x -= $pivot->x; + $this->y -= $pivot->y; + + // rotate point clockwise + // $x = $this->x * $cos - $this->y * $sin; + // $y = $this->x * $sin + $this->y * $cos; + + // rotate point counter-clockwise + $x = $this->x * $cos + $this->y * $sin; + $y = ($this->x * -1) * $sin + $this->y * $cos; + + // translate point back + $this->x = $x + $pivot->x; + $this->y = $y + $pivot->y; + + return $this; + } } diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index bfea4d41e..a68157e9b 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -354,6 +354,24 @@ public function align($position, $offset_x = 0, $offset_y = 0) return $this; } + public function rotate($angle) + { + if ($angle != 0) { + $angle = pi() * 2 - $angle * pi() * 2 / 360; + + /* + for ($i=0; $i<4; $i++) { + $x = $box[$i * 2]; + $y = $box[$i * 2 + 1]; + $box[$i * 2] = cos($angle) * $x - sin($angle) * $y; + $box[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y; + } + */ + } + + return $this; + } + /** * Runs constraints on current size * diff --git a/tests/PointTest.php b/tests/PointTest.php index aaac80d8b..028c67fec 100644 --- a/tests/PointTest.php +++ b/tests/PointTest.php @@ -43,4 +43,12 @@ public function testSetPosition() $this->assertEquals(100, $point->x); $this->assertEquals(200, $point->y); } + + public function testRotate() + { + $point = new Point(30, 0); + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(0, $point->x); + $this->assertEquals(30, $point->y); + } } From c02bcdecd7e6a8137c903a92b916664c6f9673ba Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 1 Nov 2014 17:45:36 +0100 Subject: [PATCH 02/88] added point rotation --- src/Intervention/Image/Gd/Font.php | 74 +++++++++++++++++++++++++----- src/Intervention/Image/Point.php | 30 ++++-------- src/Intervention/Image/Size.php | 53 +++++++++++++++++---- tests/PointTest.php | 9 ++++ tests/SizeTest.php | 69 ++++++++++++++++++++++++++++ 5 files changed, 194 insertions(+), 41 deletions(-) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 3e77ed75a..b0e0a71ce 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Gd; use \Intervention\Image\Image; +use \Intervention\Image\Size; class Font extends \Intervention\Image\AbstractFont { @@ -18,11 +19,19 @@ protected function getPointSize() public function applyToImage(Image $image, $posx = 0, $posy = 0) { + // box size + $box = $this->getBoxSize(); + + // draw box (debug) + // $image->rectangle($posx, $posy, $posx + $box->getWidth(), $posy + $box->getHeight(), function ($draw) { + // $draw->border(1, '555'); + // }); + // create empty resource - $canvas = imagecreatetruecolor(400, 400); + $canvas = imagecreatetruecolor($box->getWidth(), $box->getHeight()); // set background color (2147483647) - imagefill($canvas, 0, 0, 3242424); + imagefill($canvas, 0, 0, 2147483647); // parse text color $color = new Color($this->color); @@ -35,30 +44,30 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) // draw ttf text imagettftext( $canvas, - $this->getPointSize(), - 0, - 16, - 16, + $this->getPointSize(), // size + 0, // angle + 0, // x + 49, // y $color->getInt(), $this->file, $this->text ); if ($this->angle != 0) { - $canvas = imagerotate($canvas, $this->angle, 45454); + $canvas = imagerotate($canvas, $this->angle, 2147483647); + $box->rotate($this->angle); } - // insert canvas imagecopy( $image->getCore(), $canvas, - $posx, - $posy, + $posx - $box->pivot->x, + $posy - $box->pivot->y, 0, 0, - 400, - 400 + imagesx($canvas), + imagesy($canvas) ); } @@ -67,5 +76,46 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) public function getBoxSize($text = null) { $text = is_null($text) ? $this->text : $text; + + if ($this->hasApplicableFontFile()) { + + // get bounding box with angle 0 + $box = imagettfbbox($this->getPointSize(), 0, $this->file, $this->text); + + // rotate points manually + if ($this->angle != 0) { + /* + $angle = pi() * 2 - $this->angle * pi() * 2 / 360; + + for ($i=0; $i<4; $i++) { + $x = $box[$i * 2]; + $y = $box[$i * 2 + 1]; + $box[$i * 2] = cos($angle) * $x - sin($angle) * $y; + $box[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y; + } + */ + } + + $width = intval(abs($box[4] - $box[0])); + $height = intval(abs($box[5] - $box[1])); + + } else { + + // get current internal font size + $w = $this->getInternalFontWidth(); + $h = $this->getInternalFontHeight(); + + if (strlen($this->text) == 0) { + // no text -> no boxsize + $width = 0; + $height = 0; + } else { + // calculate boxsize + $width = strlen($this->text) * $w; + $height = $h; + } + } + + return new Size($width, $height); } } diff --git a/src/Intervention/Image/Point.php b/src/Intervention/Image/Point.php index 06063d68b..de42edfca 100644 --- a/src/Intervention/Image/Point.php +++ b/src/Intervention/Image/Point.php @@ -53,13 +53,16 @@ public function setY($y) /** * Sets both X and Y coordinate * - * @param integer $x - * @param integer $y + * @param integer $x + * @param integer $y + * @return Point */ public function setPosition($x, $y) { $this->setX($x); $this->setY($y); + + return $this; } /** @@ -71,25 +74,12 @@ public function setPosition($x, $y) */ public function rotate($angle, Point $pivot) { - $sin = sin($angle); - $cos = cos($angle); - - // translate point - $this->x -= $pivot->x; - $this->y -= $pivot->y; + $sin = round(sin(deg2rad($angle)), 6); + $cos = round(cos(deg2rad($angle)), 6); - // rotate point clockwise - // $x = $this->x * $cos - $this->y * $sin; - // $y = $this->x * $sin + $this->y * $cos; + $x = $cos * ($this->x - $pivot->x) - $sin * ($this->y - $pivot->y) + $pivot->x; + $y = $sin * ($this->x - $pivot->x) + $cos * ($this->y - $pivot->y) + $pivot->y; - // rotate point counter-clockwise - $x = $this->x * $cos + $this->y * $sin; - $y = ($this->x * -1) * $sin + $this->y * $cos; - - // translate point back - $this->x = $x + $pivot->x; - $this->y = $y + $pivot->y; - - return $this; + return $this->setPosition($x, $y); } } diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index a68157e9b..fedde6110 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -354,19 +354,54 @@ public function align($position, $offset_x = 0, $offset_y = 0) return $this; } + /** + * Rotate rectangular size and return bounding rectangle + * + * @param float $angle + * @return \Intervention\Image\Size + */ public function rotate($angle) { if ($angle != 0) { - $angle = pi() * 2 - $angle * pi() * 2 / 360; - - /* - for ($i=0; $i<4; $i++) { - $x = $box[$i * 2]; - $y = $box[$i * 2 + 1]; - $box[$i * 2] = cos($angle) * $x - sin($angle) * $y; - $box[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y; + + // recreate rectangle with 4 points + $points = array( + new Point(0, 0), + new Point($this->width, 0), + new Point($this->width, $this->height * (-1)), + new Point(0, $this->height * (-1)) + ); + + $x_values = array(); + $y_values = array(); + + $pivot = clone $this->pivot; + $pivot->y = $pivot->y * (-1); + + // rotate 4 points + foreach ($points as $point) { + $point->rotate($angle, $pivot); + $x_values[] = $point->x; + $y_values[] = $point->y; } - */ + + // find max/min x/y values + $max_x_value = max($x_values); + $max_y_value = max($y_values); + $min_x_value = min($x_values); + $min_y_value = min($y_values); + + // set new bounding box + $this->set( + abs($min_x_value - $max_x_value), + abs($min_y_value - $max_y_value) + ); + + // set new pivot + $this->setPivot($pivot->setPosition( + abs($pivot->x + $min_x_value * (-1)), + abs($pivot->y + $max_y_value * (-1)) + )); } return $this; diff --git a/tests/PointTest.php b/tests/PointTest.php index 028c67fec..73b2e7179 100644 --- a/tests/PointTest.php +++ b/tests/PointTest.php @@ -50,5 +50,14 @@ public function testRotate() $point->rotate(90, new Point(0, 0)); $this->assertEquals(0, $point->x); $this->assertEquals(30, $point->y); + + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(-30, $point->x); + $this->assertEquals(0, $point->y); + + $point = new Point(300, 200); + $point->rotate(90, new Point(0, 0)); + $this->assertEquals(-200, $point->x); + $this->assertEquals(300, $point->y); } } diff --git a/tests/SizeTest.php b/tests/SizeTest.php index 4be87ee09..0604a6d11 100644 --- a/tests/SizeTest.php +++ b/tests/SizeTest.php @@ -424,6 +424,75 @@ public function testFitsInto() $this->assertFalse($fits); } + public function testRotate() + { + $box = new Size(300, 200); + $box = $box->rotate(90); + $this->assertEquals(200, $box->width); + $this->assertEquals(300, $box->height); + $this->assertEquals(0, $box->pivot->x); + $this->assertEquals(300, $box->pivot->y); + + $box = new Size(300, 200); + $box = $box->rotate(180); + $this->assertEquals(300, $box->width); + $this->assertEquals(200, $box->height); + $this->assertEquals(300, $box->pivot->x); + $this->assertEquals(200, $box->pivot->y); + + $box = new Size(300, 200); + $box = $box->rotate(270); + $this->assertEquals(200, $box->width); + $this->assertEquals(300, $box->height); + $this->assertEquals(200, $box->pivot->x); + $this->assertEquals(0, $box->pivot->y); + + $box = new Size(300, 200); + $box = $box->rotate(360); + $this->assertEquals(300, $box->width); + $this->assertEquals(200, $box->height); + $this->assertEquals(0, $box->pivot->x); + $this->assertEquals(0, $box->pivot->y); + + $box = new Size(200, 200); + $box = $box->rotate(45); + $this->assertEquals(282, $box->width); + $this->assertEquals(282, $box->height); + $this->assertEquals(0, $box->pivot->x); + $this->assertEquals(141, $box->pivot->y); + + $box = new Size(300, 200); + $box = $box->rotate(45); + $this->assertEquals(353, $box->width); + $this->assertEquals(353, $box->height); + $this->assertEquals(0, $box->pivot->x); + $this->assertEquals(212, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('bottom-right'); + $box = $box->rotate(45); + $this->assertEquals(353, $box->width); + $this->assertEquals(354, $box->height); + $this->assertEquals(353, $box->pivot->x); + $this->assertEquals(142, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('center'); + $box = $box->rotate(100); + $this->assertEquals(249, $box->width); + $this->assertEquals(330, $box->height); + $this->assertEquals(125, $box->pivot->x); + $this->assertEquals(165, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('left-bottom'); + $box = $box->rotate(90); + $this->assertEquals(200, $box->width); + $this->assertEquals(300, $box->height); + $this->assertEquals(200, $box->pivot->x); + $this->assertEquals(300, $box->pivot->y); + } + /** * @expectedException \Intervention\Image\Exception\InvalidArgumentException */ From e7a64d54238cfd6ed93caea2813cea2c27fb529f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 10 Nov 2014 18:02:16 +0100 Subject: [PATCH 03/88] added lineHeight call --- src/Intervention/Image/AbstractFont.php | 28 +++++++++++++++++++++++++ tests/AbstractFontTest.php | 21 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 2bc5c2d85..32f72fcca 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -53,6 +53,13 @@ abstract class AbstractFont */ public $file; + /** + * Line height of the text + * + * @var float + */ + public $lineHeight; + /** * Width of textbox * @@ -234,6 +241,27 @@ public function getFile() return $this->file; } + /** + * Set line-height + * + * @param float $height + * @return void + */ + public function lineHeight($lineHeight) + { + $this->lineHeight = $lineHeight; + } + + /** + * Get line-height of instance + * + * @return float + */ + public function getLineHeight() + { + return $this->lineHeight; + } + /** * Set width of textbox * diff --git a/tests/AbstractFontTest.php b/tests/AbstractFontTest.php index cf1d03b5a..649989d40 100644 --- a/tests/AbstractFontTest.php +++ b/tests/AbstractFontTest.php @@ -62,6 +62,27 @@ public function testFile() $this->assertEquals('test.ttf', $font->file); } + public function testLineHeight() + { + $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); + $font->lineHeight(1.5); + $this->assertEquals(1.5, $font->lineHeight); + } + + public function testWidth() + { + $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); + $font->width(300); + $this->assertEquals(300, $font->width); + } + + public function testHeight() + { + $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); + $font->height(200); + $this->assertEquals(200, $font->height); + } + public function testCountLines() { $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); From a13d6cd17f382d2245a7c171d2381737565e6fd6 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 10 Nov 2014 18:09:54 +0100 Subject: [PATCH 04/88] added getLines method --- src/Intervention/Image/AbstractFont.php | 12 +++++++++++- src/Intervention/Image/Gd/Font.php | 15 ++++++++++++++- tests/AbstractFontTest.php | 7 +++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 32f72fcca..219c56d72 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -325,6 +325,16 @@ protected function hasApplicableFontFile() */ public function countLines() { - return count(explode(PHP_EOL, $this->text)); + return count($this->getLines()); + } + + /** + * Get array of lines to be written + * + * @return array + */ + public function getLines() + { + return explode(PHP_EOL, $this->text); } } diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index b0e0a71ce..6c12c602d 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -26,7 +26,7 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) // $image->rectangle($posx, $posy, $posx + $box->getWidth(), $posy + $box->getHeight(), function ($draw) { // $draw->border(1, '555'); // }); - + // create empty resource $canvas = imagecreatetruecolor($box->getWidth(), $box->getHeight()); @@ -79,6 +79,14 @@ public function getBoxSize($text = null) if ($this->hasApplicableFontFile()) { + $width_values = array(); + $height_values = array(); + + // cycle through each line + foreach ($this->getLines() as $line) { + + } + // get bounding box with angle 0 $box = imagettfbbox($this->getPointSize(), 0, $this->file, $this->text); @@ -118,4 +126,9 @@ public function getBoxSize($text = null) return new Size($width, $height); } + + public function getLines() + { + return explode(PHP_EOL, $this->text); + } } diff --git a/tests/AbstractFontTest.php b/tests/AbstractFontTest.php index 649989d40..3e03d5741 100644 --- a/tests/AbstractFontTest.php +++ b/tests/AbstractFontTest.php @@ -95,4 +95,11 @@ public function testCountLines() baz'); $this->assertEquals(3, $font->countLines()); } + + public function testGetLines() + { + $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); + $font->text('foo'.PHP_EOL.'bar'.PHP_EOL.'baz'); + $this->assertEquals(array('foo', 'bar', 'baz'), $font->getLines()); + } } From b2fd85d839821f0083e46eee2bb2c91762f6207c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 10 Nov 2014 18:17:25 +0100 Subject: [PATCH 05/88] getBoxSize with lineHeight --- src/Intervention/Image/AbstractFont.php | 2 +- src/Intervention/Image/Gd/Font.php | 85 +++++++++++++++---------- 2 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 219c56d72..0bbf50d8f 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -58,7 +58,7 @@ abstract class AbstractFont * * @var float */ - public $lineHeight; + public $lineHeight = 1; /** * Width of textbox diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 6c12c602d..2fbbbcc13 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -30,28 +30,38 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) // create empty resource $canvas = imagecreatetruecolor($box->getWidth(), $box->getHeight()); - // set background color (2147483647) + // set background color transparent (2147483647) imagefill($canvas, 0, 0, 2147483647); // parse text color $color = new Color($this->color); + $lines = $this->getLines(); + if ($this->hasApplicableFontFile()) { // enable alphablending for imagettftext imagealphablending($image->getCore(), true); - // draw ttf text - imagettftext( - $canvas, - $this->getPointSize(), // size - 0, // angle - 0, // x - 49, // y - $color->getInt(), - $this->file, - $this->text - ); + $baseline = $this->getGdBoxSize($lines[0]); + + $padding = $this->getPadding(); + + // write line by line + foreach ($lines as $count => $line) { + + // draw ttf text + imagettftext( + $canvas, + $this->getPointSize(), // size + 0, // angle + $padding, // x + $baseline->getHeight() + $count * $this->lineHeight * $this->size * 1.5, // y + $color->getInt(), + $this->file, + $line + ); + } if ($this->angle != 0) { $canvas = imagerotate($canvas, $this->angle, 2147483647); @@ -79,33 +89,25 @@ public function getBoxSize($text = null) if ($this->hasApplicableFontFile()) { + $lines = $this->getLines(); + $baseline = $this->getGdBoxSize($lines[0]); + $width_values = array(); - $height_values = array(); // cycle through each line - foreach ($this->getLines() as $line) { - + foreach ($lines as $line) { + $width_values[] = $this->getGdBoxSize($line)->getWidth(); } - // get bounding box with angle 0 - $box = imagettfbbox($this->getPointSize(), 0, $this->file, $this->text); + $padding = $this->getPadding(); - // rotate points manually - if ($this->angle != 0) { - /* - $angle = pi() * 2 - $this->angle * pi() * 2 / 360; - - for ($i=0; $i<4; $i++) { - $x = $box[$i * 2]; - $y = $box[$i * 2 + 1]; - $box[$i * 2] = cos($angle) * $x - sin($angle) * $y; - $box[$i * 2 + 1] = sin($angle) * $x + cos($angle) * $y; - } - */ - } + $width = max($width_values); + $width = $width + $padding * 2; + + $height = $baseline->getHeight() + (count($lines) - 1) * $this->lineHeight * $this->size * 1.5; + $height = $height + $baseline->getHeight() / 3; + $height = $height + $padding * 2; - $width = intval(abs($box[4] - $box[0])); - $height = intval(abs($box[5] - $box[1])); } else { @@ -127,8 +129,23 @@ public function getBoxSize($text = null) return new Size($width, $height); } - public function getLines() + private function getPadding() + { + $correct = $this->angle != 0 ? 2 : 0; + return ceil($this->size / 20) + $correct; + } + + private function getGdBoxSize($text = null) { - return explode(PHP_EOL, $this->text); + $text = is_null($text) ? $this->text : $text; + + // get boxsize + $box = imagettfbbox($this->getPointSize(), 0, $this->file, $text); + + // calculate width/height + $width = intval(abs($box[4] - $box[0])); + $height = intval(abs($box[5] - $box[1])); + + return new Size($width, $height); } } From 42a72ef54e703bde1afa1a09a2bf4b1e114c816a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 16 Nov 2014 12:20:08 +0100 Subject: [PATCH 06/88] added moveX/Y to Point class --- src/Intervention/Image/Point.php | 20 ++++++++++++++++++++ tests/PointTest.php | 16 ++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/Intervention/Image/Point.php b/src/Intervention/Image/Point.php index de42edfca..02d786618 100644 --- a/src/Intervention/Image/Point.php +++ b/src/Intervention/Image/Point.php @@ -50,6 +50,26 @@ public function setY($y) $this->y = intval($y); } + /** + * Move X coordinate + * + * @param integer $x + */ + public function moveX($x) + { + $this->x = $this->x + intval($x); + } + + /** + * Move Y coordinate + * + * @param integer $y + */ + public function moveY($y) + { + $this->y = $this->y + intval($y); + } + /** * Sets both X and Y coordinate * diff --git a/tests/PointTest.php b/tests/PointTest.php index 73b2e7179..e463bf245 100644 --- a/tests/PointTest.php +++ b/tests/PointTest.php @@ -36,6 +36,22 @@ public function testSetY() $this->assertEquals(100, $point->y); } + public function testmoveX() + { + $point = new Point(50, 50); + $point->moveX(100); + $this->assertEquals(150, $point->x); + $this->assertEquals(50, $point->y); + } + + public function testmoveY() + { + $point = new Point(50, 50); + $point->moveY(100); + $this->assertEquals(50, $point->x); + $this->assertEquals(150, $point->y); + } + public function testSetPosition() { $point = new Point(0, 0); From 2f4eeb4166ad4081a3e100d82944d181650e903d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 16 Nov 2014 12:44:11 +0100 Subject: [PATCH 07/88] added align/valign support --- src/Intervention/Image/Gd/Font.php | 34 +++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 2fbbbcc13..59bb1bec9 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -31,7 +31,7 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $canvas = imagecreatetruecolor($box->getWidth(), $box->getHeight()); // set background color transparent (2147483647) - imagefill($canvas, 0, 0, 2147483647); + imagefill($canvas, 0, 0, 1291845632); // parse text color $color = new Color($this->color); @@ -47,15 +47,20 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $padding = $this->getPadding(); + $box->align(sprintf('%s-%s', $this->align, 'top')); + // write line by line foreach ($lines as $count => $line) { + $linesize = $this->getGdBoxSize($line); + $relative = $box->relativePosition($linesize->align($this->align)); + // draw ttf text imagettftext( $canvas, $this->getPointSize(), // size 0, // angle - $padding, // x + $relative->x, // x $baseline->getHeight() + $count * $this->lineHeight * $this->size * 1.5, // y $color->getInt(), $this->file, @@ -63,11 +68,33 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) ); } + // valign + switch (strtolower($this->valign)) { + case 'top': + # nothing to do... + break; + + case 'center': + case 'middle': + $box->pivot->moveY(imagesy($canvas) / 2); + break; + + case 'bottom': + $box->pivot->moveY(imagesy($canvas)); + break; + + default: + case 'baseline': + $box->pivot->moveY($baseline->getHeight()); + break; + } + + // rotate canvas if ($this->angle != 0) { $canvas = imagerotate($canvas, $this->angle, 2147483647); $box->rotate($this->angle); } - + // insert canvas imagecopy( $image->getCore(), @@ -131,6 +158,7 @@ public function getBoxSize($text = null) private function getPadding() { + return 0; $correct = $this->angle != 0 ? 2 : 0; return ceil($this->size / 20) + $correct; } From ef3d6ffc0c85436d5180f77f0682925fa22a769c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 16 Nov 2014 12:53:31 +0100 Subject: [PATCH 08/88] added box() method --- src/Intervention/Image/AbstractFont.php | 71 ++++++++++++------------- src/Intervention/Image/Gd/Font.php | 68 +++++++++++++++++++---- 2 files changed, 92 insertions(+), 47 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index 0bbf50d8f..b2fc1b05f 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -61,18 +61,11 @@ abstract class AbstractFont public $lineHeight = 1; /** - * Width of textbox + * Textbox * - * @var integer - */ - public $width; - - /** - * Height of textbox - * - * @var integer + * @var Size */ - public $height; + public $box; /** * Draws font to given image on given position @@ -263,45 +256,25 @@ public function getLineHeight() } /** - * Set width of textbox + * Set size of boxed text * * @param integer $width - * @return void - */ - public function width($width) - { - $this->width = $width; - } - - /** - * Get width of textbox - * - * @return integer - */ - public function getWidth() - { - return $this->width; - } - - /** - * Set height of textbox - * * @param integer $height * @return void */ - public function height($height) + public function box($width, $height) { - $this->height = $height; + $this->box = new Size($width, $height); } /** - * Get height of textbox + * Get width of textbox * * @return integer */ - public function getHeight() + public function getBox() { - return $this->height; + return $this->box; } /** @@ -333,8 +306,30 @@ public function countLines() * * @return array */ - public function getLines() + public function getLines($text = null) + { + $text = is_null($text) ? $this->text : $text; + + return explode(PHP_EOL, $text); + } + + /** + * Get array of words to be written + * + * @return array + */ + public function getWords() + { + return explode(' ', $this->text); + } + + /** + * Determines if text has defined box size + * + * @return boolean + */ + protected function isBoxed() { - return explode(PHP_EOL, $this->text); + return is_a($this->box, 'Intervention\Image\Size'); } } diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 59bb1bec9..7c72bfcc9 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -19,24 +19,28 @@ protected function getPointSize() public function applyToImage(Image $image, $posx = 0, $posy = 0) { + // format text + $text = $this->getFormated(); + // box size - $box = $this->getBoxSize(); + $box = $this->isBoxed() ? $this->box : $this->getBoxSize($text); // draw box (debug) - // $image->rectangle($posx, $posy, $posx + $box->getWidth(), $posy + $box->getHeight(), function ($draw) { - // $draw->border(1, '555'); + // $dbox = $this->getBoxSize($text); + // $image->rectangle($posx, $posy, $posx + $dbox->getWidth(), $posy + $dbox->getHeight(), function ($draw) { + // $draw->border(1, 'ff0000'); // }); // create empty resource - $canvas = imagecreatetruecolor($box->getWidth(), $box->getHeight()); + $canvas = imagecreatetruecolor($box->getWidth()+1, $box->getHeight()+1); - // set background color transparent (2147483647) - imagefill($canvas, 0, 0, 1291845632); + // set background color transparent (2147483647 (1291845632)) + imagefill($canvas, 0, 0, 2147483647); // parse text color $color = new Color($this->color); - $lines = $this->getLines(); + $lines = $this->getLines($text); if ($this->hasApplicableFontFile()) { @@ -49,6 +53,21 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $box->align(sprintf('%s-%s', $this->align, 'top')); + $ystart = 0; + + if ($this->isBoxed()) { + switch (strtolower($this->valign)) { + case 'bottom': + $ystart = $box->getHeight() - $this->getBoxSize($text)->getHeight(); + break; + + case 'center': + case 'middle': + $ystart = ($box->getHeight() - $this->getBoxSize($text)->getHeight()) / 2; + break; + } + } + // write line by line foreach ($lines as $count => $line) { @@ -61,7 +80,7 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $this->getPointSize(), // size 0, // angle $relative->x, // x - $baseline->getHeight() + $count * $this->lineHeight * $this->size * 1.5, // y + $ystart + $baseline->getHeight() + $count * $this->lineHeight * $this->size * 1.5, // y $color->getInt(), $this->file, $line @@ -89,6 +108,10 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) break; } + if ($this->isBoxed()) { + $box->align('top-left'); + } + // rotate canvas if ($this->angle != 0) { $canvas = imagerotate($canvas, $this->angle, 2147483647); @@ -116,7 +139,7 @@ public function getBoxSize($text = null) if ($this->hasApplicableFontFile()) { - $lines = $this->getLines(); + $lines = $this->getLines($text); $baseline = $this->getGdBoxSize($lines[0]); $width_values = array(); @@ -176,4 +199,31 @@ private function getGdBoxSize($text = null) return new Size($width, $height); } + + private function getFormated() + { + if ($this->isBoxed()) { + + $line = array(); + $lines = array(); + + foreach ($this->getWords() as $word) { + + $linesize = $this->getGdBoxSize( + implode(' ', array_merge($line, array($word))) + ); + + if ($linesize->getWidth() <= $this->box->getWidth() - 4) { + $line[] = $word; + } else { + $lines[] = implode(' ', $line); + $line = array($word); + } + } + + $lines[] = $word; + + return implode(PHP_EOL, $lines); + } + } } From c0ccda4ee841d284839d84bceea3d17e0c90bb6b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 17 Nov 2014 17:11:16 +0100 Subject: [PATCH 09/88] added resizeCanvas() method to Size --- src/Intervention/Image/Size.php | 23 +++++++++++++++ tests/SizeTest.php | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/Intervention/Image/Size.php b/src/Intervention/Image/Size.php index fedde6110..03638a295 100644 --- a/src/Intervention/Image/Size.php +++ b/src/Intervention/Image/Size.php @@ -407,6 +407,29 @@ public function rotate($angle) return $this; } + /** + * Resize rectangular and keeps pivot point relative + * + * @param integer $width + * @param integer $height + * @param boolean $relative + * @return \Intervention\Image\Size + */ + public function resizeCanvas($width, $height, $relative = false) + { + $width = $relative ? $this->width + $width * 2 : $width; + $height = $relative ? $this->height + $height * 2 : $height; + + $px = $this->pivot->x == 0 ? 0 : $width / ($this->width / $this->pivot->x); + $py = $this->pivot->y == 0 ? 0 : $height / ($this->height / $this->pivot->y); + + $this->width = $width; + $this->height = $height; + $this->setPivot(new Point($px, $py)); + + return $this; + } + /** * Runs constraints on current size * diff --git a/tests/SizeTest.php b/tests/SizeTest.php index 0604a6d11..79c84cb32 100644 --- a/tests/SizeTest.php +++ b/tests/SizeTest.php @@ -493,6 +493,56 @@ public function testRotate() $this->assertEquals(300, $box->pivot->y); } + public function testResizeCanvas() + { + $box = new Size(300, 200); + $box->resizeCanvas(400, 500); + $this->assertEquals(400, $box->width); + $this->assertEquals(500, $box->height); + $this->assertEquals(0, $box->pivot->x); + $this->assertEquals(0, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('top'); + $box->resizeCanvas(50, 100, true); + $this->assertEquals(400, $box->width); + $this->assertEquals(400, $box->height); + $this->assertEquals(200, $box->pivot->x); + $this->assertEquals(0, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('center'); + $box->resizeCanvas(150, 120, true); + $this->assertEquals(600, $box->width); + $this->assertEquals(440, $box->height); + $this->assertEquals(300, $box->pivot->x); + $this->assertEquals(220, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('right'); + $box->resizeCanvas(300, 1000); + $this->assertEquals(300, $box->width); + $this->assertEquals(1000, $box->height); + $this->assertEquals(300, $box->pivot->x); + $this->assertEquals(500, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('right-bottom'); + $box->resizeCanvas(50, 100); + $this->assertEquals(50, $box->width); + $this->assertEquals(100, $box->height); + $this->assertEquals(50, $box->pivot->x); + $this->assertEquals(100, $box->pivot->y); + + $box = new Size(300, 200); + $box->align('bottom'); + $box->resizeCanvas(50, 100); + $this->assertEquals(50, $box->width); + $this->assertEquals(100, $box->height); + $this->assertEquals(25, $box->pivot->x); + $this->assertEquals(100, $box->pivot->y); + } + /** * @expectedException \Intervention\Image\Exception\InvalidArgumentException */ From e9c9b63b9cf63152740f8ffd8dab0250e09b76f4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 17 Nov 2014 17:18:37 +0100 Subject: [PATCH 10/88] reconstruction --- src/Intervention/Image/Gd/Font.php | 47 ++++++++++-------------------- 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 7c72bfcc9..325e6c0e6 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -7,6 +7,8 @@ class Font extends \Intervention\Image\AbstractFont { + const PADDING = 10; + /** * Get font size in points * @@ -20,19 +22,16 @@ protected function getPointSize() public function applyToImage(Image $image, $posx = 0, $posy = 0) { // format text - $text = $this->getFormated(); + $text = $this->format(); // box size $box = $this->isBoxed() ? $this->box : $this->getBoxSize($text); - // draw box (debug) - // $dbox = $this->getBoxSize($text); - // $image->rectangle($posx, $posy, $posx + $dbox->getWidth(), $posy + $dbox->getHeight(), function ($draw) { - // $draw->border(1, 'ff0000'); - // }); - // create empty resource - $canvas = imagecreatetruecolor($box->getWidth()+1, $box->getHeight()+1); + $canvas = imagecreatetruecolor( + $box->getWidth() + self::PADDING * 2, + $box->getHeight() + self::PADDING * 2 + ); // set background color transparent (2147483647 (1291845632)) imagefill($canvas, 0, 0, 2147483647); @@ -49,8 +48,6 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $baseline = $this->getGdBoxSize($lines[0]); - $padding = $this->getPadding(); - $box->align(sprintf('%s-%s', $this->align, 'top')); $ystart = 0; @@ -79,8 +76,8 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $canvas, $this->getPointSize(), // size 0, // angle - $relative->x, // x - $ystart + $baseline->getHeight() + $count * $this->lineHeight * $this->size * 1.5, // y + self::PADDING + $relative->x, // x + self::PADDING + $ystart + $baseline->getHeight() + $count * $this->lineHeight * $this->size * 1.5, // y $color->getInt(), $this->file, $line @@ -95,11 +92,11 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) case 'center': case 'middle': - $box->pivot->moveY(imagesy($canvas) / 2); + $box->pivot->moveY($box->getHeight() / 2); break; case 'bottom': - $box->pivot->moveY(imagesy($canvas)); + $box->pivot->moveY($box->getHeight()); break; default: @@ -122,8 +119,8 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) imagecopy( $image->getCore(), $canvas, - $posx - $box->pivot->x, - $posy - $box->pivot->y, + $posx - $box->pivot->x - self::PADDING, + $posy - $box->pivot->y - self::PADDING, 0, 0, imagesx($canvas), @@ -149,15 +146,10 @@ public function getBoxSize($text = null) $width_values[] = $this->getGdBoxSize($line)->getWidth(); } - $padding = $this->getPadding(); - $width = max($width_values); - $width = $width + $padding * 2; $height = $baseline->getHeight() + (count($lines) - 1) * $this->lineHeight * $this->size * 1.5; $height = $height + $baseline->getHeight() / 3; - $height = $height + $padding * 2; - } else { @@ -179,13 +171,6 @@ public function getBoxSize($text = null) return new Size($width, $height); } - private function getPadding() - { - return 0; - $correct = $this->angle != 0 ? 2 : 0; - return ceil($this->size / 20) + $correct; - } - private function getGdBoxSize($text = null) { $text = is_null($text) ? $this->text : $text; @@ -200,7 +185,7 @@ private function getGdBoxSize($text = null) return new Size($width, $height); } - private function getFormated() + private function format() { if ($this->isBoxed()) { @@ -213,7 +198,7 @@ private function getFormated() implode(' ', array_merge($line, array($word))) ); - if ($linesize->getWidth() <= $this->box->getWidth() - 4) { + if ($linesize->getWidth() <= $this->box->getWidth()) { $line[] = $word; } else { $lines[] = implode(' ', $line); @@ -221,7 +206,7 @@ private function getFormated() } } - $lines[] = $word; + $lines[] = implode(' ', $line); return implode(PHP_EOL, $lines); } From 63c96ad4ca4c764ab4137ed89bbb45d5bb704fe2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 17 Nov 2014 20:15:57 +0100 Subject: [PATCH 11/88] re-added GD internal font methods --- src/Intervention/Image/Gd/Font.php | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 325e6c0e6..eaf70c274 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -19,6 +19,60 @@ protected function getPointSize() return intval(ceil($this->size * 0.75)); } + /** + * Filter function to access internal integer font values + * + * @return integer + */ + private function getInternalFont() + { + $internalfont = is_null($this->file) ? 1 : $this->file; + $internalfont = is_numeric($internalfont) ? $internalfont : false; + + if ( ! in_array($internalfont, array(1, 2, 3, 4, 5))) { + throw new \Intervention\Image\Exception\NotSupportedException( + sprintf('Internal GD font (%s) not available. Use only 1-5.', $internalfont) + ); + } + + return intval($internalfont); + } + + /** + * Get width of an internal font character + * + * @return integer + */ + private function getInternalFontWidth() + { + return $this->getInternalFont() + 4; + } + + /** + * Get height of an internal font character + * + * @return integer + */ + private function getInternalFontHeight() + { + switch ($this->getInternalFont()) { + case 1: + return 8; + + case 2: + return 14; + + case 3: + return 14; + + case 4: + return 16; + + case 5: + return 16; + } + } + public function applyToImage(Image $image, $posx = 0, $posy = 0) { // format text From 791bc9b3bb481598640c26ae6279576fd0c0811e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 18 Nov 2014 16:30:26 +0100 Subject: [PATCH 12/88] gd internal font implementation --- src/Intervention/Image/Gd/Font.php | 227 +++++++++++++++++------------ 1 file changed, 134 insertions(+), 93 deletions(-) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index eaf70c274..748d01a29 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -73,6 +73,26 @@ private function getInternalFontHeight() } } + private function getInternalFontBaseline() + { + switch ($this->getInternalFont()) { + case 1: + return 6; + + case 2: + return 10; + + case 3: + return 10; + + case 4: + return 12; + + case 5: + return 12; + } + } + public function applyToImage(Image $image, $posx = 0, $posy = 0) { // format text @@ -95,35 +115,32 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $lines = $this->getLines($text); - if ($this->hasApplicableFontFile()) { + $baseline = $this->getCoreBoxSize($lines[0]); - // enable alphablending for imagettftext - imagealphablending($image->getCore(), true); + $box->align(sprintf('%s-%s', $this->align, 'top')); - $baseline = $this->getGdBoxSize($lines[0]); + $ystart = 0; - $box->align(sprintf('%s-%s', $this->align, 'top')); - - $ystart = 0; - - if ($this->isBoxed()) { - switch (strtolower($this->valign)) { - case 'bottom': - $ystart = $box->getHeight() - $this->getBoxSize($text)->getHeight(); - break; - - case 'center': - case 'middle': - $ystart = ($box->getHeight() - $this->getBoxSize($text)->getHeight()) / 2; - break; - } + if ($this->isBoxed()) { + switch (strtolower($this->valign)) { + case 'bottom': + $ystart = $box->getHeight() - $this->getBoxSize($text)->getHeight(); + break; + + case 'center': + case 'middle': + $ystart = ($box->getHeight() - $this->getBoxSize($text)->getHeight()) / 2; + break; } + } - // write line by line - foreach ($lines as $count => $line) { + // write line by line + foreach ($lines as $count => $line) { - $linesize = $this->getGdBoxSize($line); - $relative = $box->relativePosition($linesize->align($this->align)); + $linesize = $this->getCoreBoxSize(trim($line)); + $relative = $box->relativePosition($linesize->align($this->align)); + + if ($this->hasApplicableFontFile()) { // draw ttf text imagettftext( @@ -136,105 +153,129 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $this->file, $line ); - } - // valign - switch (strtolower($this->valign)) { - case 'top': - # nothing to do... - break; + } else { - case 'center': - case 'middle': - $box->pivot->moveY($box->getHeight() / 2); - break; + // draw text + imagestring( + $canvas, + $this->getInternalFont(), + self::PADDING + $relative->x, // x + self::PADDING + $ystart + $count * $this->lineHeight * $baseline->getHeight(), // y + trim($line), + $color->getInt() + ); - case 'bottom': - $box->pivot->moveY($box->getHeight()); - break; - - default: - case 'baseline': - $box->pivot->moveY($baseline->getHeight()); - break; } - if ($this->isBoxed()) { - $box->align('top-left'); - } + } - // rotate canvas - if ($this->angle != 0) { - $canvas = imagerotate($canvas, $this->angle, 2147483647); - $box->rotate($this->angle); - } + // valign + switch (strtolower($this->valign)) { + case 'top': + # nothing to do... + break; + + case 'center': + case 'middle': + $box->pivot->moveY($box->getHeight() / 2); + break; + + case 'bottom': + $box->pivot->moveY($box->getHeight()); + break; + + default: + case 'baseline': + $baseline = $this->hasApplicableFontFile() ? $baseline->getHeight() : $this->getInternalFontBaseline(); + $box->pivot->moveY($baseline); + break; + } - // insert canvas - imagecopy( - $image->getCore(), - $canvas, - $posx - $box->pivot->x - self::PADDING, - $posy - $box->pivot->y - self::PADDING, - 0, - 0, - imagesx($canvas), - imagesy($canvas) - ); + if ($this->isBoxed()) { + $box->align('top-left'); + } + // rotate canvas + if ($this->angle != 0) { + $canvas = imagerotate($canvas, $this->angle, 2147483647); + $box->rotate($this->angle); } + + // enable alphablending for imagecopy + imagealphablending($image->getCore(), true); + + // insert canvas + imagecopy( + $image->getCore(), + $canvas, + $posx - $box->pivot->x - self::PADDING, + $posy - $box->pivot->y - self::PADDING, + 0, + 0, + imagesx($canvas), + imagesy($canvas) + ); + + } public function getBoxSize($text = null) { $text = is_null($text) ? $this->text : $text; - if ($this->hasApplicableFontFile()) { + $lines = $this->getLines($text); + $baseline = $this->getCoreBoxSize($lines[0]); - $lines = $this->getLines($text); - $baseline = $this->getGdBoxSize($lines[0]); + $width_values = array(); - $width_values = array(); + // cycle through each line + foreach ($lines as $line) { + $width_values[] = $this->getCoreBoxSize($line)->getWidth(); + } - // cycle through each line - foreach ($lines as $line) { - $width_values[] = $this->getGdBoxSize($line)->getWidth(); - } + // maximal line width is box width + $width = max($width_values); - $width = max($width_values); + if ($this->hasApplicableFontFile()) { $height = $baseline->getHeight() + (count($lines) - 1) * $this->lineHeight * $this->size * 1.5; $height = $height + $baseline->getHeight() / 3; } else { + + $height = $baseline->getHeight() + (count($lines) - 1) * $this->lineHeight * $this->getInternalFontHeight(); - // get current internal font size - $w = $this->getInternalFontWidth(); - $h = $this->getInternalFontHeight(); - - if (strlen($this->text) == 0) { - // no text -> no boxsize - $width = 0; - $height = 0; - } else { - // calculate boxsize - $width = strlen($this->text) * $w; - $height = $h; - } } return new Size($width, $height); } - private function getGdBoxSize($text = null) + private function getCoreBoxSize($text = null) { $text = is_null($text) ? $this->text : $text; - // get boxsize - $box = imagettfbbox($this->getPointSize(), 0, $this->file, $text); + if ($this->hasApplicableFontFile()) { - // calculate width/height - $width = intval(abs($box[4] - $box[0])); - $height = intval(abs($box[5] - $box[1])); + // get boxsize + $box = imagettfbbox($this->getPointSize(), 0, $this->file, $text); + + // calculate width/height + $width = intval(abs($box[4] - $box[0])); + $height = intval(abs($box[5] - $box[1])); + + } else { + + if (strlen($text) == 0) { + // no text -> no boxsize + $width = 0; + $height = 0; + } else { + // calculate boxsize + $width = strlen($text) * $this->getInternalFontWidth(); + $height = $this->getInternalFontHeight(); + } + } return new Size($width, $height); } @@ -248,19 +289,19 @@ private function format() foreach ($this->getWords() as $word) { - $linesize = $this->getGdBoxSize( - implode(' ', array_merge($line, array($word))) + $linesize = $this->getCoreBoxSize( + implode(' ', array_merge($line, array(trim($word)))) ); if ($linesize->getWidth() <= $this->box->getWidth()) { - $line[] = $word; + $line[] = trim($word); } else { $lines[] = implode(' ', $line); - $line = array($word); + $line = array(trim($word)); } } - $lines[] = implode(' ', $line); + $lines[] = trim(implode(' ', $line)); return implode(PHP_EOL, $lines); } From f1ec5c68ae62bd2a6554de0dbd3d5c8ebcf72dae Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 18 Nov 2014 17:29:10 +0100 Subject: [PATCH 13/88] added tests --- tests/AbstractFontTest.php | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/AbstractFontTest.php b/tests/AbstractFontTest.php index 3e03d5741..9058ffb1b 100644 --- a/tests/AbstractFontTest.php +++ b/tests/AbstractFontTest.php @@ -69,18 +69,14 @@ public function testLineHeight() $this->assertEquals(1.5, $font->lineHeight); } - public function testWidth() + public function testBox() { $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->width(300); - $this->assertEquals(300, $font->width); - } - - public function testHeight() - { - $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); - $font->height(200); - $this->assertEquals(200, $font->height); + $this->assertEquals(null, $font->box); + $font->box(300, 200); + $this->assertInstanceOf('Intervention\Image\Size', $font->box); + $this->assertEquals(300, $font->box->width); + $this->assertEquals(200, $font->box->height); } public function testCountLines() @@ -102,4 +98,18 @@ public function testGetLines() $font->text('foo'.PHP_EOL.'bar'.PHP_EOL.'baz'); $this->assertEquals(array('foo', 'bar', 'baz'), $font->getLines()); } + + public function testGetWord() + { + $font = $this->getMockForAbstractClass('\Intervention\Image\AbstractFont'); + + $font->text('foo bar baz'); + $this->assertEquals(array('foo', 'bar', 'baz'), $font->getWords()); + + $font->text('foo bar'); + $this->assertEquals(array('foo', 'bar'), $font->getWords()); + + $font->text('foo'); + $this->assertEquals(array('foo'), $font->getWords()); + } } From fd3092b27234cda0349cc5550f29046af0aaa158 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 18 Nov 2014 17:35:22 +0100 Subject: [PATCH 14/88] Refactoring --- src/Intervention/Image/AbstractFont.php | 42 ++++++++++++++++++++++++ src/Intervention/Image/Gd/Font.php | 43 ++++++++----------------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index b2fc1b05f..f36509d21 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -4,6 +4,8 @@ abstract class AbstractFont { + const PADDING = 10; + /** * Text to be written * @@ -77,6 +79,14 @@ abstract class AbstractFont */ abstract public function applyToImage(Image $image, $posx = 0, $posy = 0); + /** + * Get raw boxsize without any non-core features + * + * @param string $text + * @return \Intervention\Image\Size + */ + abstract protected function getCoreBoxSize($text = null); + /** * Create a new instance of Font * @@ -332,4 +342,36 @@ protected function isBoxed() { return is_a($this->box, 'Intervention\Image\Size'); } + + /** + * Returns formated text according to box settings + * + * @return string + */ + protected function format() + { + if ($this->isBoxed()) { + + $line = array(); + $lines = array(); + + foreach ($this->getWords() as $word) { + + $linesize = $this->getCoreBoxSize( + implode(' ', array_merge($line, array(trim($word)))) + ); + + if ($linesize->getWidth() <= $this->box->getWidth()) { + $line[] = trim($word); + } else { + $lines[] = implode(' ', $line); + $line = array(trim($word)); + } + } + + $lines[] = trim(implode(' ', $line)); + + return implode(PHP_EOL, $lines); + } + } } diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 748d01a29..8ae9e41d9 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -7,8 +7,6 @@ class Font extends \Intervention\Image\AbstractFont { - const PADDING = 10; - /** * Get font size in points * @@ -220,6 +218,12 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) } + /** + * Calculate boxsize including own features + * + * @param string $text + * @return \Intervention\Image\Size + */ public function getBoxSize($text = null) { $text = is_null($text) ? $this->text : $text; @@ -251,7 +255,13 @@ public function getBoxSize($text = null) return new Size($width, $height); } - private function getCoreBoxSize($text = null) + /** + * Get raw boxsize without any non-core features + * + * @param string $text + * @return \Intervention\Image\Size + */ + protected function getCoreBoxSize($text = null) { $text = is_null($text) ? $this->text : $text; @@ -279,31 +289,4 @@ private function getCoreBoxSize($text = null) return new Size($width, $height); } - - private function format() - { - if ($this->isBoxed()) { - - $line = array(); - $lines = array(); - - foreach ($this->getWords() as $word) { - - $linesize = $this->getCoreBoxSize( - implode(' ', array_merge($line, array(trim($word)))) - ); - - if ($linesize->getWidth() <= $this->box->getWidth()) { - $line[] = trim($word); - } else { - $lines[] = implode(' ', $line); - $line = array(trim($word)); - } - } - - $lines[] = trim(implode(' ', $line)); - - return implode(PHP_EOL, $lines); - } - } } From e2ff7ed0e3085e918c1dfb36ba956b55bfac335e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 18 Nov 2014 18:04:21 +0100 Subject: [PATCH 15/88] Imagick font integration --- src/Intervention/Image/AbstractFont.php | 8 ++ src/Intervention/Image/Imagick/Font.php | 174 +++++++++++++++++++----- 2 files changed, 148 insertions(+), 34 deletions(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index f36509d21..fdb73887b 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -79,6 +79,14 @@ abstract class AbstractFont */ abstract public function applyToImage(Image $image, $posx = 0, $posy = 0); + /** + * Calculate boxsize including own features + * + * @param string $text + * @return \Intervention\Image\Size + */ + abstract public function getBoxSize($text = null); + /** * Get raw boxsize without any non-core features * diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index 1a91066c1..c7e09c118 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -3,6 +3,7 @@ namespace Intervention\Image\Imagick; use \Intervention\Image\Image; +use \Intervention\Image\Size; class Font extends \Intervention\Image\AbstractFont { @@ -16,63 +17,168 @@ class Font extends \Intervention\Image\AbstractFont */ public function applyToImage(Image $image, $posx = 0, $posy = 0) { - // build draw object $draw = new \ImagickDraw(); - $draw->setStrokeAntialias(true); - $draw->setTextAntialias(true); - // set font file if ($this->hasApplicableFontFile()) { + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + + // font file $draw->setFont($this->file); + + // font size + $draw->setFontSize($this->size); + + // parse text color + $color = new Color($this->color); + $draw->setFillColor($color->getPixel()); + } else { throw new \Intervention\Image\Exception\RuntimeException( "Font file must be provided to apply text to image." ); } - // parse text color - $color = new Color($this->color); + // format text + $text = $this->format(); - $draw->setFontSize($this->size); - $draw->setFillColor($color->getPixel()); + // box size + $box = $this->isBoxed() ? $this->box : $this->getBoxSize($text); + + // create empty canvas + $canvas = $image->getDriver()->newImage( + $box->getWidth() + self::PADDING * 2, + $box->getHeight() + self::PADDING * 2 + )->getCore(); + + $lines = $this->getLines($text); + + $baseline = $this->getCoreBoxSize($lines[0]); + + $box->align(sprintf('%s-%s', $this->align, 'top')); + + $ystart = 0; + + if ($this->isBoxed()) { + switch (strtolower($this->valign)) { + case 'bottom': + $ystart = $box->getHeight() - $this->getBoxSize($text)->getHeight(); + break; + + case 'center': + case 'middle': + $ystart = ($box->getHeight() - $this->getBoxSize($text)->getHeight()) / 2; + break; + } + } + + // write line by line + foreach ($lines as $count => $line) { + + $linesize = $this->getCoreBoxSize(trim($line)); + $relative = $box->relativePosition($linesize->align($this->align)); + + // write line of text + $canvas->annotateImage( + $draw, + self::PADDING + $relative->x, // x + self::PADDING + $ystart + $baseline->getHeight() + $count * $this->lineHeight * $this->size * 1.5, // y + 0, // angle + trim($line) + ); + } + + // valign + switch (strtolower($this->valign)) { + case 'top': + # nothing to do... + break; - // align horizontal - switch (strtolower($this->align)) { case 'center': - $align = \Imagick::ALIGN_CENTER; - break; + case 'middle': + $box->pivot->moveY($box->getHeight() / 2); + break; - case 'right': - $align = \Imagick::ALIGN_RIGHT; - break; + case 'bottom': + $box->pivot->moveY($box->getHeight()); + break; default: - $align = \Imagick::ALIGN_LEFT; - break; + case 'baseline': + $box->pivot->moveY($baseline->getHeight()); + break; + } + + if ($this->isBoxed()) { + $box->align('top-left'); + } + + // rotate canvas + if ($this->angle != 0) { + $canvas->rotateImage(new \ImagickPixel('none'), ($this->angle * -1)); + $box->rotate($this->angle); } - $draw->setTextAlignment($align); + // insert canvas + $image->getCore()->compositeImage( + $canvas, + \Imagick::COMPOSITE_DEFAULT, + $posx - $box->pivot->x - self::PADDING, + $posy - $box->pivot->y - self::PADDING + ); + } - // align vertical - if (strtolower($this->valign) != 'bottom') { + /** + * Calculate boxsize including own features + * + * @param string $text + * @return \Intervention\Image\Size + */ + public function getBoxSize($text = null) + { + $text = is_null($text) ? $this->text : $text; - // calculate box size - $dimensions = $image->getCore()->queryFontMetrics($draw, $this->text); + $lines = $this->getLines($text); + $baseline = $this->getCoreBoxSize($lines[0]); - // corrections on y-position - switch (strtolower($this->valign)) { - case 'center': - case 'middle': - $posy = $posy + $dimensions['textHeight'] * 0.65 / 2; - break; + $width_values = array(); - case 'top': - $posy = $posy + $dimensions['textHeight'] * 0.65; - break; - } + // cycle through each line + foreach ($lines as $line) { + $width_values[] = $this->getCoreBoxSize($line)->getWidth(); } - // apply to image - $image->getCore()->annotateImage($draw, $posx, $posy, $this->angle * (-1), $this->text); + // maximal line width is box width + $width = max($width_values); + + // calculate height + $height = $baseline->getHeight() + (count($lines) - 1) * $this->lineHeight * $this->size * 1.5; + $height = $height + $baseline->getHeight() / 3; + + return new Size($width, $height); + } + + /** + * Get raw boxsize without any non-core features + * + * @param string $text + * @return \Intervention\Image\Size + */ + protected function getCoreBoxSize($text = null) + { + $text = is_null($text) ? $this->text : $text; + + $imagick = new \Imagick(); + $draw = new \ImagickDraw(); + + $draw->setStrokeAntialias(true); + $draw->setTextAntialias(true); + $draw->setFontSize($this->size); + $draw->setFont($this->file); + + // get boxsize + $size = $imagick->queryFontMetrics($draw, $text); + + return new Size($size['textWidth'], $size['textHeight']); } } From 9cb203bded8ebdc57c5e1b6c32b6728611242446 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 18 Nov 2014 19:19:26 +0100 Subject: [PATCH 16/88] refactoring --- src/Intervention/Image/AbstractFont.php | 3 +++ src/Intervention/Image/Gd/Font.php | 2 ++ tests/TextCommandTest.php | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/AbstractFont.php b/src/Intervention/Image/AbstractFont.php index fdb73887b..f89a98de2 100644 --- a/src/Intervention/Image/AbstractFont.php +++ b/src/Intervention/Image/AbstractFont.php @@ -4,6 +4,9 @@ abstract class AbstractFont { + /** + * Security padding to prevent text being cut off + */ const PADDING = 10; /** diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index 8ae9e41d9..d62762a5b 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -105,6 +105,8 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) $box->getHeight() + self::PADDING * 2 ); + imagealphablending($canvas, true); + // set background color transparent (2147483647 (1291845632)) imagefill($canvas, 0, 0, 2147483647); diff --git a/tests/TextCommandTest.php b/tests/TextCommandTest.php index 53df1650d..8b193aecd 100644 --- a/tests/TextCommandTest.php +++ b/tests/TextCommandTest.php @@ -16,7 +16,7 @@ public function testGd() $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image->shouldReceive('getCore')->times(2)->andReturn($resource); $command = new TextCommand(array('test', 10, 20)); $result = $command->execute($image); $this->assertTrue($result); From a1a5749da440d2d3e1c2bf3ac5aeef884c983640 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 18 Nov 2014 19:31:17 +0100 Subject: [PATCH 17/88] added/edited tests --- tests/GdSystemTest.php | 22 ++++++++++++++++++---- tests/tmp/grrrr.png | Bin 0 -> 838 bytes 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 tests/tmp/grrrr.png diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index a64bc40c4..a5d10484b 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -1028,13 +1028,14 @@ public function testTextImage() $img = $this->manager()->canvas(16, 16, 'ffffff'); - $img = $img->text('0', 8, 2, function($font) { + $img = $img->text('0', 8, 8, function($font) { + $font->file(3); $font->align('center'); - $font->valign('top'); + $font->valign('center'); $font->color('000000'); }); $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('649f3f529d3931c56601155fd2680959', $img->checksum()); + $this->assertEquals('ccb8b439d7e327089682094939564934', $img->checksum()); $img = $this->manager()->canvas(16, 16, 'ffffff'); $img = $img->text('0', 8, 8, function($font) { @@ -1044,7 +1045,20 @@ public function testTextImage() $font->color('000000'); }); $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('c0dda67589c46a90d78a97b891a811ee', $img->checksum()); + $this->assertEquals('496be71507490098ead374f6d6c359fe', $img->checksum()); + + $img = $this->manager()->canvas(100, 100, 'ffffff'); + $txt = 'The quick brown fox jumps over the lazy dog'; + $img = $img->text($txt, 10, 10, function($font) { + $font->align('center'); + $font->valign('top'); + $font->file(2); + $font->color('000000'); + $font->lineHeight(1.4); + $font->box(80, 80); + })->save('tests/tmp/grrrr.png'); + $this->assertInstanceOf('Intervention\Image\Image', $img); + $this->assertEquals('7a6eafce0c071f3be91342d62e41fb9d', $img->checksum()); } public function testRectangleImage() diff --git a/tests/tmp/grrrr.png b/tests/tmp/grrrr.png new file mode 100644 index 0000000000000000000000000000000000000000..77c6dd96d5f8a3d0c420ac891de1a6009e99d692 GIT binary patch literal 838 zcmeAS@N?(olHy`uVBq!ia0vp^DIm^ zV9l*r@0zF5U-{;%%Hjd$X`iiCP}PhH>IxVR?)f01H*q4%jjT9v#pgBGPf6YpoW`M9onrU+*5pH5 zgEp-#s?rb+-22r{$nI~P_tmKR=|RiGW^T?sv;TQ+l<8y{ImK(9Pi|NHZ@K2|u6N1D zMKf8#W8Q>C=Xlz=74KQNpJ-nr+H&9dR7uwReLw$QOx_uquX}d2$h*@OS8bn2ql6nu zU_PE|S!%S<$t2&>Z^nd)dn$4*KWE(AyIgng&rjF>UCdw8^3>UFl2zEg!__ap-2H3& z>3jHsYZBA8T>CY5zOr7|)ro2Mm?cU3UtLBGK2 z>)WPj&A+{_Z>6mD?B(a{SLeRgy)XTJ&aUgd?>W8aq!;v0n7G~NNc@*;oIGb!4`RCW zn66^@1Xg+dS49eBBOjnUhvxIMSZ)<=e9Zw)>Igy&yk%c zb}8!7)2&Z-hL%V6h2KoQz1B$ioc7WMJqxcYtpB(pwD0+rzkMt>E{Uu=-n#0m$LsC4 zHr}gH-*Ttrjfck96FW|Ye4BRv*747K@;N_7e7d7lU$A0n!D*wlTRxm%-F#|))F#`a zINi9Z3%WWUt>3Y7az|aFqu{543;$Q{7sQcDQ9>J*bU(`fxnW$wr)KyHm_HaiUHx3v IIVCg!0H1AxIRF3v literal 0 HcmV?d00001 From 39b0cb819d6156523fae96039fd933f52ec76fea Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 18 Nov 2014 19:39:11 +0100 Subject: [PATCH 18/88] removed tmp file --- tests/GdSystemTest.php | 2 +- tests/tmp/grrrr.png | Bin 838 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 tests/tmp/grrrr.png diff --git a/tests/GdSystemTest.php b/tests/GdSystemTest.php index a5d10484b..0a04f3cc2 100644 --- a/tests/GdSystemTest.php +++ b/tests/GdSystemTest.php @@ -1056,7 +1056,7 @@ public function testTextImage() $font->color('000000'); $font->lineHeight(1.4); $font->box(80, 80); - })->save('tests/tmp/grrrr.png'); + }); $this->assertInstanceOf('Intervention\Image\Image', $img); $this->assertEquals('7a6eafce0c071f3be91342d62e41fb9d', $img->checksum()); } diff --git a/tests/tmp/grrrr.png b/tests/tmp/grrrr.png deleted file mode 100644 index 77c6dd96d5f8a3d0c420ac891de1a6009e99d692..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 838 zcmeAS@N?(olHy`uVBq!ia0vp^DIm^ zV9l*r@0zF5U-{;%%Hjd$X`iiCP}PhH>IxVR?)f01H*q4%jjT9v#pgBGPf6YpoW`M9onrU+*5pH5 zgEp-#s?rb+-22r{$nI~P_tmKR=|RiGW^T?sv;TQ+l<8y{ImK(9Pi|NHZ@K2|u6N1D zMKf8#W8Q>C=Xlz=74KQNpJ-nr+H&9dR7uwReLw$QOx_uquX}d2$h*@OS8bn2ql6nu zU_PE|S!%S<$t2&>Z^nd)dn$4*KWE(AyIgng&rjF>UCdw8^3>UFl2zEg!__ap-2H3& z>3jHsYZBA8T>CY5zOr7|)ro2Mm?cU3UtLBGK2 z>)WPj&A+{_Z>6mD?B(a{SLeRgy)XTJ&aUgd?>W8aq!;v0n7G~NNc@*;oIGb!4`RCW zn66^@1Xg+dS49eBBOjnUhvxIMSZ)<=e9Zw)>Igy&yk%c zb}8!7)2&Z-hL%V6h2KoQz1B$ioc7WMJqxcYtpB(pwD0+rzkMt>E{Uu=-n#0m$LsC4 zHr}gH-*Ttrjfck96FW|Ye4BRv*747K@;N_7e7d7lU$A0n!D*wlTRxm%-F#|))F#`a zINi9Z3%WWUt>3Y7az|aFqu{543;$Q{7sQcDQ9>J*bU(`fxnW$wr)KyHm_HaiUHx3v IIVCg!0H1AxIRF3v From a9ed201501c2217b5ddfba1fe8a289e7fc957d4c Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 1 Dec 2014 18:43:17 +0100 Subject: [PATCH 19/88] added GifDecoder --- src/Intervention/Image/Tools/Gif/Decoded.php | 312 +++++++++++++++++++ src/Intervention/Image/Tools/Gif/Decoder.php | 217 +++++++++++++ src/Intervention/Image/Tools/Gif/Encoder.php | 8 + src/Intervention/Image/Tools/Gif/Frame.php | 107 +++++++ tests/GifDecodedTest.php | 112 +++++++ tests/GifDecoderTest.php | 71 +++++ tests/GifFrameTest.php | 60 ++++ tests/images/animation.gif | Bin 0 -> 592 bytes 8 files changed, 887 insertions(+) create mode 100644 src/Intervention/Image/Tools/Gif/Decoded.php create mode 100644 src/Intervention/Image/Tools/Gif/Decoder.php create mode 100644 src/Intervention/Image/Tools/Gif/Encoder.php create mode 100644 src/Intervention/Image/Tools/Gif/Frame.php create mode 100644 tests/GifDecodedTest.php create mode 100644 tests/GifDecoderTest.php create mode 100644 tests/GifFrameTest.php create mode 100644 tests/images/animation.gif diff --git a/src/Intervention/Image/Tools/Gif/Decoded.php b/src/Intervention/Image/Tools/Gif/Decoded.php new file mode 100644 index 000000000..afe199b6d --- /dev/null +++ b/src/Intervention/Image/Tools/Gif/Decoded.php @@ -0,0 +1,312 @@ +header = $value; + + return $this; + } + + /** + * Gets GIF header of current instance + * + * @return string + */ + public function getHeader() + { + return $this->header; + } + + /** + * Set Logical Screen Descriptor + * + * @param string $value + * @return Decoded + */ + public function setlogicalScreenDescriptor($value) + { + $this->logicalScreenDescriptor = $value; + + return $this; + } + + /** + * Returns Logical Screen Descriptor of current instance + * + * @return string + */ + public function getlogicalScreenDescriptor() + { + return $this->logicalScreenDescriptor; + } + + public function setGlobalColorTable($value) + { + $this->globalColorTable = $value; + + return $this; + } + + public function getGlobalColorTable() + { + return $this->globalColorTable; + } + + public function setNetscapeExtension($value) + { + $this->netscapeExtension = $value; + + return $this; + } + + public function getNetscapeExtension() + { + return $this->netscapeExtension; + } + + public function setPlaintextExtension($value) + { + $this->plaintextExtension = $value; + + return $this; + } + + public function getPlaintextExtension() + { + return $this->plaintextExtension; + } + + public function setCommentExtension($value) + { + $this->commentExtension = $value; + + return $this; + } + + public function getCommentExtension() + { + return $this->commentExtension; + } + + public function getFrames() + { + return $this->frames; + } + + public function countFrames() + { + return count($this->frames); + } + + public function addGraphicsControlExtension($value) + { + $this->addToFirstFrameWithoutProperty($value, 'graphicsControlExtension'); + } + + public function addLocalColorTable($value) + { + $this->addToFirstFrameWithoutProperty($value, 'localColorTable'); + } + + public function addInterlaced($value) + { + $this->addToFirstFrameWithoutProperty($value, 'interlaced'); + } + + public function addOffset($left, $top) + { + $offset = new \StdClass; + $offset->left = $left; + $offset->top = $top; + + $this->addToFirstFrameWithoutProperty($offset, 'offset'); + } + + public function addSize($width, $height) + { + $size = new \StdClass; + $size->width = $width; + $size->height = $height; + + $this->addToFirstFrameWithoutProperty($size, 'size'); + } + + public function addImageDescriptors($value) + { + $this->addToFirstFrameWithoutProperty($value, 'imageDescriptor'); + } + + public function addImageData($value) + { + $this->addToFirstFrameWithoutProperty($value, 'imageData'); + } + + /** + * Get canvas width + * + * @return int + */ + public function getCanvasWidth() + { + if ($this->logicalScreenDescriptor) { + return (int) unpack('v', + substr($this->logicalScreenDescriptor, 0, 2) + )[1]; + } + + return false; + } + + /** + * Get canvas height + * + * @return int + */ + public function getCanvasHeight() + { + if ($this->logicalScreenDescriptor) { + return (int) unpack('v', + substr($this->logicalScreenDescriptor, 2, 2) + )[1]; + } + + return false; + } + + /** + * Returns loops of animation + * + * @return integer|null + */ + public function getLoops() + { + if ($this->netscapeExtension) { + $loops = substr($this->netscapeExtension, 14, 2); + $loops = unpack('C', $loops)[1]; + + return $loops; + } + + return null; + } + + /** + * Determines if image has global color table + * + * @return boolean + */ + public function hasGlobalColorTable() + { + $byte = substr($this->logicalScreenDescriptor, 4, 1); + + if (strlen($byte) == 1) { + $byte = unpack('C', $byte)[1]; + $bit = $byte & bindec('10000000'); + } + + return isset($bit) ? boolval($bit) : false; + } + + /** + * Get number colors in global color palette + * + * @return int + */ + public function countGlobalColors() + { + $byte = substr($this->logicalScreenDescriptor, 4, 1); + + if (strlen($byte) == 1) { + $byte = unpack('C', $byte)[1]; + $bit = $byte & bindec('00000111'); + } + + // length of the global color table is 2^(N+1) + return isset($bit) ? pow(2, $bit + 1) : 0; + } + + private function addToFirstFrameWithoutProperty($value, $property) + { + $added = false; + + if (count($this->frames)) { + foreach ($this->frames as $key => $frame) { + if ( ! $frame->hasProperty($property)) { + $this->frames[$key]->setProperty($property, $value); + $added = true; + break; + } + } + + if ( ! $added) { + $frame = new Frame; + $this->frames[] = $frame->setProperty($property, $value); + } + + } else { + // add frame if no frames have been added yet + $frame = new Frame; + $this->frames[] = $frame->setProperty($property, $value); + } + + return $added; + } +} diff --git a/src/Intervention/Image/Tools/Gif/Decoder.php b/src/Intervention/Image/Tools/Gif/Decoder.php new file mode 100644 index 000000000..18490a3d3 --- /dev/null +++ b/src/Intervention/Image/Tools/Gif/Decoder.php @@ -0,0 +1,217 @@ +handle = fopen($path, 'rb'); + } + + /** + * Close down GifDecoder instance + */ + public function __destruct() + { + fclose($this->handle); + } + + /** + * Read number of bytes and move file pointer + * + * @param int $length + * @return string + */ + protected function getNextBytes($length) + { + return fread($this->handle, $length); + } + + /** + * Decode image stream + * + * @return Decoded + */ + public function decode() + { + $gif = new Decoded; + + // read header + $gif->setHeader($this->getNextBytes(6)); + + // read logocal screen descriptor + $gif->setlogicalScreenDescriptor($this->getNextBytes(7)); + + // read global color table + if ($gif->hasGlobalColorTable()) { + $gif->setGlobalColorTable($this->getNextBytes( + $gif->countGlobalColors() * 3 + )); + } + + // read body + while ( ! feof($this->handle)) { + + switch ($this->getNextBytes(1)) { + + case self::EXTENSION_BLOCK_MARKER: + $this->decodeExtension($gif); + break; + + case self::IMAGE_SEPARATOR: + $this->decodeImageDescriptor($gif); + $this->decodeImageData($gif); + break; + + case self::TRAILER_MARKER: + # code... + break 2; + + default: + // throw new \Intervention\Image\Exception\NotReadableException( + // "Unable to decode GIF image." + // ); + break; + } + } + + return $gif; + } + + /** + * Decode extension in image stream + * + * @return void + */ + private function decodeExtension($gif) + { + switch ($this->getNextBytes(1)) { + + case self::GRAPHICS_CONTROL_EXTENSION_MARKER: + $gif->addGraphicsControlExtension($this->getNextBytes(6)); + break; + + case self::NETSCAPE_EXTENSION_MARKER: + $gif->setNetscapeExtension($this->getNextBytes(17)); + break; + + case self::PLAINTEXT_EXTENSION_MARKER: + $blocksize = $this->getNextBytes(1); + $blocksize = unpack('C', $blocksize); + $gif->setPlaintextExtension($this->getNextBytes($blocksize)); + $this->getNextBytes(1); // null byte + break; + + case self::COMMENT_EXTENSION_MARKER: + $blocksize = $this->getNextBytes(1); + $blocksize = unpack('C', $blocksize); + $gif->setCommentExtension($this->getNextBytes($blocksize)); + $this->getNextBytes(1); // null byte + break; + + default: + # code... + break; + } + } + + /** + * Decode Image Descriptor from image stream + * + * @return void + */ + private function decodeImageDescriptor($gif) + { + $descriptor = $this->getNextBytes(9); + + // determine if descriptor has local color table + $flag = substr($descriptor, 8, 1); + $flag = unpack('C', $flag)[1]; + $flag = (bool) ($flag & bindec('10000000')); + if ($flag) { + // read local color table + $byte = substr($descriptor, 8, 1); + $byte = unpack('C', $byte)[1]; + $size = (int) ($byte & bindec('00000111')); + $size = 3 * pow(2, $size + 1); + + $gif->addLocalColorTable($this->getNextBytes($size)); + + } else { + $gif->addLocalColorTable(null); + } + + // determine if image is marked as interlaced + $interlaced = substr($descriptor, 8, 1); + $interlaced = unpack('C', $interlaced)[1]; + $interlaced = (bool) ($interlaced & bindec('01000000')); + $gif->addInterlaced($interlaced); + + // decode image offsets + $left = substr($descriptor, 0, 2); + $left = unpack('C', $left)[1]; + $top = substr($descriptor, 2, 2); + $top = unpack('C', $top)[1]; + $gif->addOffset($left, $top); + + // decode image dimensions + $width = substr($descriptor, 4, 2); + $width = unpack('v', $width)[1]; + $height = substr($descriptor, 6, 2); + $height = unpack('v', $height)[1]; + $gif->addSize($width, $height); + + $gif->addImageDescriptors($descriptor); + } + + /** + * Decode Image data from image stream + * + * @return void + */ + private function decodeImageData($gif) + { + $data = ''; + + // LZW minimum code size + $data .= $this->getNextBytes(1); + + do { + + $byte = $this->getNextBytes(1); + + if ($byte !== self::BLOCK_TERMINATOR) { + $size = unpack('C', $byte)[1]; + $data .= $byte; + $data .= $this->getNextBytes($size); + } else { + $data .= self::BLOCK_TERMINATOR; + } + + } while ($byte !== self::BLOCK_TERMINATOR); + + $gif->addImageData($data); + } +} diff --git a/src/Intervention/Image/Tools/Gif/Encoder.php b/src/Intervention/Image/Tools/Gif/Encoder.php new file mode 100644 index 000000000..e38369fd7 --- /dev/null +++ b/src/Intervention/Image/Tools/Gif/Encoder.php @@ -0,0 +1,8 @@ +{$name} = $value; + + return $this; + } + + /** + * Determines if instance has local color table + * + * @return boolean + */ + public function hasLocalColorTable() + { + return ! is_null($this->localColorTable); + } + + /** + * Returns local color table data of instance + * + * @return string + */ + public function getLocalColorTable() + { + return $this->localColorTable; + } + + /** + * Returns delay of current instance + * + * @return int + */ + public function getDelay() + { + if (property_exists($this, 'graphicsControlExtension') && $this->graphicsControlExtension) { + $byte = substr($this->graphicsControlExtension, 2, 2); + return (int) unpack('v', $byte)[1]; + } + + return false; + } + + /** + * Determines if instance has transparent colors + * + * @return boolean + */ + public function hasTransparentColor() + { + if (property_exists($this, 'graphicsControlExtension') && $this->graphicsControlExtension) { + $byte = substr($this->graphicsControlExtension, 1, 1); + $byte = unpack('C', $byte)[1]; + $bit = $byte & bindec('00000001'); + + return (bool) $bit; + } + + return false; + } + + /** + * Returns index byte of transparent color + * + * @return string + */ + public function getTransparentColorIndex() + { + if (property_exists($this, 'graphicsControlExtension') && $this->graphicsControlExtension) { + return substr($this->graphicsControlExtension, 4, 1); + } + + return false; + } + + /** + * Determines if current frame is saved as interlaced + * + * @return boolean + */ + public function isInterlaced() + { + return $this->interlaced; + } +} diff --git a/tests/GifDecodedTest.php b/tests/GifDecodedTest.php new file mode 100644 index 000000000..6a2cafc01 --- /dev/null +++ b/tests/GifDecodedTest.php @@ -0,0 +1,112 @@ +setHeader('foo'); + $this->assertEquals('foo', $decoded->getHeader()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + } + + public function testSetGetLogicalScreenDescriptor() + { + $decoded = new Decoded; + $obj = $decoded->setLogicalScreenDescriptor('foo'); + $this->assertEquals('foo', $decoded->getLogicalScreenDescriptor()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + } + + public function testSetGetGlobalColorTable() + { + $decoded = new Decoded; + $obj = $decoded->setGlobalColorTable('foo'); + $this->assertEquals('foo', $decoded->getGlobalColorTable()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + } + + public function testSetGetNetscapeExtension() + { + $decoded = new Decoded; + $obj = $decoded->setNetscapeExtension('foo'); + $this->assertEquals('foo', $decoded->getNetscapeExtension()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + } + + public function testSetGetPlaintextExtension() + { + $decoded = new Decoded; + $obj = $decoded->setPlaintextExtension('foo'); + $this->assertEquals('foo', $decoded->getPlaintextExtension()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + } + + public function testSetGetCommentExtension() + { + $decoded = new Decoded; + $obj = $decoded->setCommentExtension('foo'); + $this->assertEquals('foo', $decoded->getCommentExtension()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + } + + public function testGetCanvasWidth() + { + $decoded = new Decoded; + $this->assertEquals(false, $decoded->getCanvasWidth()); + + $decoded = new Decoded; + $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x00\x00"); + $this->assertEquals(10, $decoded->getCanvasWidth()); + } + + public function testGetCanvasHeight() + { + $decoded = new Decoded; + $this->assertEquals(false, $decoded->getCanvasHeight()); + + $decoded = new Decoded; + $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x00\x00"); + $this->assertEquals(10, $decoded->getCanvasHeight()); + } + + public function testGetLoops() + { + $decoded = new Decoded; + $this->assertEquals(null, $decoded->getLoops()); + + $decoded = new Decoded; + $decoded->setNetscapeExtension("\x0B\x4E\x45\x54\x53\x43\x41\x50\x45\x32\x2E\x30\x03\x01\x05\x00\x00"); + $this->assertEquals(5, $decoded->getLoops()); + } + + public function testHasGlobalColorTable() + { + $decoded = new Decoded; + $this->assertFalse($decoded->hasGlobalColorTable()); + + $decoded = new Decoded; + $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x00\x00"); + $this->assertFalse($decoded->hasGlobalColorTable()); + + $decoded = new Decoded; + $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x91\x00\x00"); + $this->assertTrue($decoded->hasGlobalColorTable()); + } + + public function testCountGlobalColors() + { + $decoded = new Decoded; + $this->assertEquals(0, $decoded->countGlobalColors()); + + $decoded = new Decoded; + $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x91\x00\x00"); + $this->assertEquals(4, $decoded->countGlobalColors()); + + $decoded = new Decoded; + $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x91\x00\x00"); + $this->assertEquals(4, $decoded->countGlobalColors()); + } +} diff --git a/tests/GifDecoderTest.php b/tests/GifDecoderTest.php new file mode 100644 index 000000000..0b7ac8802 --- /dev/null +++ b/tests/GifDecoderTest.php @@ -0,0 +1,71 @@ +decoder = $this->getTestDecoder('tests/images/animation.gif'); + } + + public function tearDown() + { + # code... + } + + private function getTestDecoder($file) + { + return new Decoder($file); + } + + public function testDecode() + { + $decoded = $this->decoder->decode(); + + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $decoded); + $this->assertEquals(8, $decoded->countFrames()); + $this->assertTrue($decoded->hasGlobalColorTable()); + $this->assertEquals(32, $decoded->countGlobalColors()); + $this->assertEquals(2, $decoded->getLoops()); + + $offsets = array( + array('left' => 0, 'top' => 0), + array('left' => 5, 'top' => 2), + array('left' => 1, 'top' => 0), + array('left' => 0, 'top' => 0), + array('left' => 8, 'top' => 5), + array('left' => 5, 'top' => 2), + array('left' => 1, 'top' => 0), + array('left' => 0, 'top' => 0) + ); + + $sizes = array( + array('width' => 20, 'height' => 15), + array('width' => 10, 'height' => 10), + array('width' => 17, 'height' => 15), + array('width' => 20, 'height' => 15), + array('width' => 5, 'height' => 5), + array('width' => 10, 'height' => 10), + array('width' => 17, 'height' => 15), + array('width' => 20, 'height' => 15) + ); + + $delays = array(20, 20, 20, 20, 20, 20, 20, 20); + $interlaced = array(true, false, false, false, false, false, false, false); + $localColorTables = array(null, null, null, null, null, null, null, null); + + foreach ($decoded->getFrames() as $key => $frame) { + $this->assertEquals($sizes[$key]['width'], $frame->size->width); + $this->assertEquals($sizes[$key]['height'], $frame->size->height); + $this->assertEquals($offsets[$key]['left'], $frame->offset->left); + $this->assertEquals($offsets[$key]['top'], $frame->offset->top); + $this->assertEquals($delays[$key], $frame->getDelay()); + $this->assertEquals($interlaced[$key], $frame->isInterlaced()); + $this->assertEquals($localColorTables[$key], $frame->getLocalColorTable()); + $this->assertFalse($frame->hasLocalColorTable()); + } + } +} diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php new file mode 100644 index 000000000..115087907 --- /dev/null +++ b/tests/GifFrameTest.php @@ -0,0 +1,60 @@ +assertFalse($frame->hasProperty('foo')); + + $frame = new Frame; + $frame->foo = 'bar'; + $this->assertTrue($frame->hasProperty('foo')); + + $frame = new Frame; + $frame->foo = null; + $this->assertTrue($frame->hasProperty('foo')); + } + + public function testSetProperty() + { + $frame = new Frame; + $this->assertFalse($frame->hasProperty('foo')); + $test = $frame->setProperty('foo', 'bar'); + $this->assertEquals('bar', $frame->foo); + $this->assertTrue($frame->hasProperty('foo')); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $test); + } + + public function testGetDelay() + { + $frame = new Frame; + $this->assertFalse($frame->getDelay()); + + $frame = new Frame; + $frame->setProperty('graphicsControlExtension', "\x04\x00\x14\x00\x00\x00"); + $this->assertEquals(20, $frame->getDelay()); + } + + public function testHasTransparentColor() + { + $frame = new Frame; + $this->assertFalse($frame->hasTransparentColor()); + + $frame = new Frame; + $frame->setProperty('graphicsControlExtension', "\x04\x01\x14\x00\x00\x00"); + $this->assertTrue($frame->hasTransparentColor()); + } + + public function testGetTransparentColorIndex() + { + $frame = new Frame; + $this->assertFalse($frame->getTransparentColorIndex()); + + $frame = new Frame; + $frame->setProperty('graphicsControlExtension', "\x04\x01\x14\x00\xF1\x00"); + $this->assertEquals("\xF1", $frame->getTransparentColorIndex()); + } +} diff --git a/tests/images/animation.gif b/tests/images/animation.gif new file mode 100644 index 0000000000000000000000000000000000000000..c45bb7771e1e8d034cb0e17b5f73709407104851 GIT binary patch literal 592 zcmZ?wbhEHb6k*_J_`=R$>7D$48KaYbylY@=ZnEdI*}Pea-aFe=YEgQMnE8qO#; zSy89+Z4v9|1?)j#;ivng&rOi3&2sv)l({L_zQ5S=(*lmS^SFL2W+~5bxiCSZDAnD; zFJW?t*}+bQV?DBp|GE8KLxPsJYn8J)y_3d`<@*ND%i^x#vAXn^t$K?el|_sz}Az3R}J)-SsxlReDu}cq0a9R zz0PIb!k(k0i$c~2AWVe$gn?Bg0qm2ERqJv~Kd%uDMs*sUEhl7C?3>jGY6B0RE zIOZHHU~1kdr4-qMP=oNDMuI@i5d(qw5;_jA^%|d_Ye>Ja?%+e+?VsmJpI3NX$s`-& zo+`}Iu?Xf8pan>NOHh#GOql0T)bXZi-?Bq;1$(WWdDVFrU*|c*@20^U$bUpo)j*Gt b|DmwM2Vcn@>iXFH1~d`LZ;)6*^P4pQdA7Tj literal 0 HcmV?d00001 From a8e633a7bc9557271178e8d29fa75191d6b9ae3e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 3 Dec 2014 17:49:44 +0100 Subject: [PATCH 20/88] refactoring --- src/Intervention/Image/Tools/Gif/Decoded.php | 30 +++++++++----------- src/Intervention/Image/Tools/Gif/Frame.php | 12 ++++++-- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/Intervention/Image/Tools/Gif/Decoded.php b/src/Intervention/Image/Tools/Gif/Decoded.php index afe199b6d..d736c1d99 100644 --- a/src/Intervention/Image/Tools/Gif/Decoded.php +++ b/src/Intervention/Image/Tools/Gif/Decoded.php @@ -287,26 +287,24 @@ private function addToFirstFrameWithoutProperty($value, $property) { $added = false; - if (count($this->frames)) { - foreach ($this->frames as $key => $frame) { - if ( ! $frame->hasProperty($property)) { - $this->frames[$key]->setProperty($property, $value); - $added = true; - break; - } - } - - if ( ! $added) { - $frame = new Frame; - $this->frames[] = $frame->setProperty($property, $value); + foreach ($this->frames as $key => $frame) { + if ( ! $frame->propertyIsSet($property)) { + $frame->setProperty($property, $value); + $added = true; + break; } + } - } else { - // add frame if no frames have been added yet - $frame = new Frame; - $this->frames[] = $frame->setProperty($property, $value); + if ( ! $added) { + $this->newFrameWithProperty($property, $value); } return $added; } + + private function newFrameWithProperty($property, $value) + { + $frame = new Frame; + $this->frames[] = $frame->setProperty($property, $value); + } } diff --git a/src/Intervention/Image/Tools/Gif/Frame.php b/src/Intervention/Image/Tools/Gif/Frame.php index 7ea3fb229..9e1106c3c 100644 --- a/src/Intervention/Image/Tools/Gif/Frame.php +++ b/src/Intervention/Image/Tools/Gif/Frame.php @@ -4,15 +4,23 @@ class Frame { + public $graphicsControlExtension; + public $imageDescriptor; + public $imageData; + public $localColorTable; + public $interlaced; + public $offset; + public $size; + /** * Determines if property is already set * * @param string $name * @return boolean */ - public function hasProperty($name) + public function propertyIsSet($name) { - return property_exists($this, $name); + return $this->{$name} !== null; } /** From a2becff774d5defae8f4bac5043616c99e6aff04 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 3 Dec 2014 18:16:52 +0100 Subject: [PATCH 21/88] added disposal detection --- src/Intervention/Image/Tools/Gif/Frame.php | 25 +++++++++++++++---- tests/GifFrameTest.php | 28 ++++++++++++++++------ 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/Intervention/Image/Tools/Gif/Frame.php b/src/Intervention/Image/Tools/Gif/Frame.php index 9e1106c3c..ef010afa1 100644 --- a/src/Intervention/Image/Tools/Gif/Frame.php +++ b/src/Intervention/Image/Tools/Gif/Frame.php @@ -4,6 +4,10 @@ class Frame { + const DISPOSAL_METHOD_LEAVE = 1; + const DISPOSAL_METHOD_BACKGROUND = 2; + const DISPOSAL_METHOD_PREVIOUS = 3; + public $graphicsControlExtension; public $imageDescriptor; public $imageData; @@ -20,7 +24,7 @@ class Frame */ public function propertyIsSet($name) { - return $this->{$name} !== null; + return property_exists($this, $name) && ($this->{$name} !== null); } /** @@ -63,7 +67,7 @@ public function getLocalColorTable() */ public function getDelay() { - if (property_exists($this, 'graphicsControlExtension') && $this->graphicsControlExtension) { + if ($this->graphicsControlExtension) { $byte = substr($this->graphicsControlExtension, 2, 2); return (int) unpack('v', $byte)[1]; } @@ -78,7 +82,7 @@ public function getDelay() */ public function hasTransparentColor() { - if (property_exists($this, 'graphicsControlExtension') && $this->graphicsControlExtension) { + if ($this->graphicsControlExtension) { $byte = substr($this->graphicsControlExtension, 1, 1); $byte = unpack('C', $byte)[1]; $bit = $byte & bindec('00000001'); @@ -96,13 +100,26 @@ public function hasTransparentColor() */ public function getTransparentColorIndex() { - if (property_exists($this, 'graphicsControlExtension') && $this->graphicsControlExtension) { + if ($this->graphicsControlExtension) { return substr($this->graphicsControlExtension, 4, 1); } return false; } + public function getDisposalMethod() + { + if ($this->graphicsControlExtension) { + $byte = substr($this->graphicsControlExtension, 1, 1); + $byte = unpack('C', $byte)[1]; + $method = $byte >> 2 & bindec('00000111'); + + return $method; + } + + return 0; + } + /** * Determines if current frame is saved as interlaced * diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php index 115087907..584114f9f 100644 --- a/tests/GifFrameTest.php +++ b/tests/GifFrameTest.php @@ -4,27 +4,27 @@ class GifFrameTest extends PHPUnit_Framework_TestCase { - public function testHasProperty() + public function testPropertyIsSet() { $frame = new Frame; - $this->assertFalse($frame->hasProperty('foo')); + $this->assertFalse($frame->propertyIsSet('foo')); $frame = new Frame; $frame->foo = 'bar'; - $this->assertTrue($frame->hasProperty('foo')); + $this->assertTrue($frame->propertyIsSet('foo')); $frame = new Frame; - $frame->foo = null; - $this->assertTrue($frame->hasProperty('foo')); + $frame->foo = false; + $this->assertTrue($frame->propertyIsSet('foo')); } public function testSetProperty() { $frame = new Frame; - $this->assertFalse($frame->hasProperty('foo')); + $this->assertFalse($frame->propertyIsSet('foo')); $test = $frame->setProperty('foo', 'bar'); $this->assertEquals('bar', $frame->foo); - $this->assertTrue($frame->hasProperty('foo')); + $this->assertTrue($frame->propertyIsSet('foo')); $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $test); } @@ -57,4 +57,18 @@ public function testGetTransparentColorIndex() $frame->setProperty('graphicsControlExtension', "\x04\x01\x14\x00\xF1\x00"); $this->assertEquals("\xF1", $frame->getTransparentColorIndex()); } + + public function testGetDisposalMethod() + { + $frame = new Frame; + $this->assertEquals(0, $frame->getDisposalMethod()); + + $frame = new Frame; + $frame->setProperty('graphicsControlExtension', "\x04\x05\x14\x00\xF1\x00"); + $this->assertEquals(1, $frame->getDisposalMethod()); + + $frame = new Frame; + $frame->setProperty('graphicsControlExtension', "\x04\x0C\x14\x00\xF1\x00"); + $this->assertEquals(3, $frame->getDisposalMethod()); + } } From 62a57d0da2cef13131aedae4059f8dc7395d4653 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 3 Dec 2014 18:24:26 +0100 Subject: [PATCH 22/88] gif encoder --- src/Intervention/Image/Tools/Gif/Encoder.php | 243 +++++++++++++++++++ src/Intervention/Image/Tools/Gif/Frame.php | 5 + tests/GifEncoderTest.php | 31 +++ 3 files changed, 279 insertions(+) create mode 100644 tests/GifEncoderTest.php diff --git a/src/Intervention/Image/Tools/Gif/Encoder.php b/src/Intervention/Image/Tools/Gif/Encoder.php index e38369fd7..9d404f082 100644 --- a/src/Intervention/Image/Tools/Gif/Encoder.php +++ b/src/Intervention/Image/Tools/Gif/Encoder.php @@ -4,5 +4,248 @@ class Encoder { + /** + * Canvas width + * + * @var integer + */ + public $canvasWidth; + + /** + * Canvas height + * + * @var integer + */ + public $canvasHeight; + + /** + * Global color table + * + * @var string + */ + public $globalColorTable; + + /** + * Number of sequence loops + * + * @var integer + */ + public $loops; + + /** + * Image frames + * + * @var array + */ + public $frames = array(); + + /** + * Set canvas dimensions + * + * @param int $width + * @param int $height + * @return \Intervention\Image\Tools\Gif\Encoder + */ + public function setCanvas($width, $height) + { + $this->canvasWidth = $width; + $this->canvasHeight = $height; + + return $this; + } + + /** + * Set number of loops + * + * @param int $value + * @return \Intervention\Image\Tools\Gif\Encoder + */ + public function setLoops($value) + { + $this->loops = $value; + + return $this; + } + + /** + * Set global color table + * + * @param int $value + * @return \Intervention\Image\Tools\Gif\Encoder + */ + public function setGlobalColorTable($value) + { + $this->globalColorTable = $value; + + return $this; + } + + public function addFrame(Frame $frame) + { + $this->frames[] = $frame; + } + + public function newFrame() + { + $frame = new Frame; + + $this->frames[] = $frame; + } + + /** + * Encode image data + * + * @return string + */ + public function encode() + { + // create gif + $encoded = $this->buildLogicalScreenDescriptor(); + + // netscape extension + if ($this->isAnimated()) { + $encoded .= $this->buildNetscapeExtension(); + } + + // add frame(s) + foreach ($this->frames as $frame) { + $encoded .= $this->buildFrame($frame); + } + + // EOF + $encoded .= "\x3B"; + + return $encoded; + } + + /** + * Build logical screen descriptor + * + * @return string + */ + private function buildLogicalScreenDescriptor() + { + // gif header + $descriptor = 'GIF89a'; + + // canvas width/height + $descriptor .= pack('v*', $this->canvasWidth); + $descriptor .= pack('v*', $this->canvasHeight); + + // packed field + $descriptor .= "\x00"; + + // background color index + $descriptor .= "\x00"; + + // pixel aspect ratio + $descriptor .= "\x00"; + + return $descriptor; + } + + private function buildNetscapeExtension() + { + $extension = "\x21"; + $extension .= "\xFF"; + $extension .= "\x0B"; + $extension .= 'NETSCAPE2.0'; + $extension .= "\x03"; + $extension .= "\x01"; + $extension .= pack('v', $this->loops); + $extension .= "\x00"; + + return $extension; + } + + private function buildFrame(Frame $frame) + { + // graphics control extensions + $encoded = $this->buildGraphicsControlExtension($frame); + + // image descriptor + $encoded .= $this->buildImageDescriptor($frame); + + // add image data + $encoded .= $frame->getImageData(); + + return $encoded; + } + + private function buildGraphicsControlExtension(Frame $frame) + { + // start + $extension = "\x21\xF9"; + + // byte size + $extension .= "\x04"; + + // packed field + $extension .= "\x00"; + + // delay + $extension .= pack('v*', $frame->getDelay()); + + // transparent color index + $extension .= "\x00"; + + // block terminator + $extension .= "\x00"; + + return $extension; + } + + private function buildImageDescriptor(Frame $frame) + { + // seperator + $descriptor = "\x2C"; + + // image left/top + $descriptor .= pack('v*', + $frame->offset->left, + $frame->offset->top + ); + + // image width/height + $descriptor .= pack('v*', + $frame->size->width, + $frame->size->height + ); + + $interlacedFlag = $frame->isInterlaced() ? 1 : 0; + $sortFlag = 0; + $reserved1 = 0; + $reserved2 = 0; + + if ($frame->hasLocalColorTable()) { + $colorTableFlag = 1; + $colorTableSize = log(strlen($frame->getLocalColorTable()) / 3, 2) - 1; + $colorTableSize = decbin($colorTableSize); + } else { + $colorTableFlag = 0; + $colorTableSize = 0; + } + + // packed field + $packed = $colorTableFlag.$interlacedFlag.$sortFlag.$reserved1.$reserved2.$colorTableSize; + $descriptor .= pack('C', bindec($packed)); + + if ($frame->hasLocalColorTable()) { + // add local color table + $descriptor .= $frame->getLocalColorTable(); + } + + return $descriptor; + } + + private function hasGlobalColorTable() + { + return isset($this->globalColorTable); + } + + public function isAnimated() + { + return count($this->frames) > 1; + } } diff --git a/src/Intervention/Image/Tools/Gif/Frame.php b/src/Intervention/Image/Tools/Gif/Frame.php index ef010afa1..a46407365 100644 --- a/src/Intervention/Image/Tools/Gif/Frame.php +++ b/src/Intervention/Image/Tools/Gif/Frame.php @@ -129,4 +129,9 @@ public function isInterlaced() { return $this->interlaced; } + + public function getImageData() + { + return $this->imageData; + } } diff --git a/tests/GifEncoderTest.php b/tests/GifEncoderTest.php new file mode 100644 index 000000000..8699b97af --- /dev/null +++ b/tests/GifEncoderTest.php @@ -0,0 +1,31 @@ +setCanvas(300, 200); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Encoder', $result); + $this->assertEquals(300, $encoder->canvasWidth); + $this->assertEquals(200, $encoder->canvasHeight); + } + + public function testSetLoops() + { + $encoder = new Encoder; + $result = $encoder->setLoops(6); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Encoder', $result); + $this->assertEquals(6, $encoder->loops); + } + + public function testSetGlobalColorTable() + { + $encoder = new Encoder; + $result = $encoder->setGlobalColorTable('foo'); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Encoder', $result); + $this->assertEquals('foo', $encoder->globalColorTable); + } +} From af25225592abaf29427b3862a90d065f28d25167 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 5 Dec 2014 19:35:11 +0100 Subject: [PATCH 23/88] gif decoder/encoder work --- src/Intervention/Image/Tools/Gif/Decoder.php | 16 ++++- src/Intervention/Image/Tools/Gif/Encoder.php | 26 +++++++- src/Intervention/Image/Tools/Gif/Frame.php | 67 ++++++++++++++++++-- tests/GifDecoderTest.php | 15 +++++ tests/GifFrameTest.php | 59 ++++++++++++++--- 5 files changed, 163 insertions(+), 20 deletions(-) diff --git a/src/Intervention/Image/Tools/Gif/Decoder.php b/src/Intervention/Image/Tools/Gif/Decoder.php index 18490a3d3..bd1e77fe9 100644 --- a/src/Intervention/Image/Tools/Gif/Decoder.php +++ b/src/Intervention/Image/Tools/Gif/Decoder.php @@ -25,9 +25,13 @@ class Decoder * * @param string $path */ - public function __construct($path) + public function __construct($path = null) { - $this->handle = fopen($path, 'rb'); + if (is_null($path)) { + $this->handle = fopen('php://memory', 'r+'); + } else { + $this->handle = fopen($path, 'rb'); + } } /** @@ -38,6 +42,14 @@ public function __destruct() fclose($this->handle); } + public function initFromData($data) + { + fwrite($this->handle, $data); + rewind($this->handle); + + return $this; + } + /** * Read number of bytes and move file pointer * diff --git a/src/Intervention/Image/Tools/Gif/Encoder.php b/src/Intervention/Image/Tools/Gif/Encoder.php index 9d404f082..515a1bb89 100644 --- a/src/Intervention/Image/Tools/Gif/Encoder.php +++ b/src/Intervention/Image/Tools/Gif/Encoder.php @@ -85,13 +85,27 @@ public function addFrame(Frame $frame) $this->frames[] = $frame; } - public function newFrame() + public function createFrame() { $frame = new Frame; $this->frames[] = $frame; } + public function createFrameFromGdResource($resource, $delay = null) + { + // get imagedata from resource + $gifdata = $this->encodeGdResource($resource); + $decoder = new Decoder; + $gif = $decoder->initFromData($gifdata)->decode(); + + $frame = $gif->getFrames()[0]; + $frame->setLocalColorTable($gif->getGlobalColorTable()); + $frame->setDelay($delay); + + $this->frames[] = $frame; + } + /** * Encode image data * @@ -243,6 +257,16 @@ private function hasGlobalColorTable() return isset($this->globalColorTable); } + private function encodeGdResource($resource) + { + ob_start(); + imagegif($resource); + $buffer = ob_get_contents(); + ob_end_clean(); + + return $buffer; + } + public function isAnimated() { return count($this->frames) > 1; diff --git a/src/Intervention/Image/Tools/Gif/Frame.php b/src/Intervention/Image/Tools/Gif/Frame.php index a46407365..e9ac7fe32 100644 --- a/src/Intervention/Image/Tools/Gif/Frame.php +++ b/src/Intervention/Image/Tools/Gif/Frame.php @@ -12,9 +12,12 @@ class Frame public $imageDescriptor; public $imageData; public $localColorTable; + public $transparentColorIndex; + public $disposalMethod; public $interlaced; public $offset; public $size; + public $delay; /** * Determines if property is already set @@ -40,6 +43,18 @@ public function setProperty($name, $value) return $this; } + public function setImageData($value) + { + $this->imageData = $value; + + return $this; + } + + public function getImageData() + { + return $this->imageData; + } + /** * Determines if instance has local color table * @@ -60,12 +75,31 @@ public function getLocalColorTable() return $this->localColorTable; } + public function setLocalColorTable($value) + { + $this->localColorTable = $value; + + return $this; + } + + public function getDelay() + { + return $this->delay; + } + + public function setDelay($delay) + { + $this->delay = $delay; + + return $this; + } + /** * Returns delay of current instance * * @return int */ - public function getDelay() + public function decodeDelay() { if ($this->graphicsControlExtension) { $byte = substr($this->graphicsControlExtension, 2, 2); @@ -93,12 +127,24 @@ public function hasTransparentColor() return false; } + public function getTransparentColorIndex() + { + return $this->transparentColorIndex; + } + + public function setTransparentColorIndex($value) + { + $this->transparentColorIndex = $value; + + return $this; + } + /** * Returns index byte of transparent color * * @return string */ - public function getTransparentColorIndex() + public function decodeTransparentColorIndex() { if ($this->graphicsControlExtension) { return substr($this->graphicsControlExtension, 4, 1); @@ -108,6 +154,18 @@ public function getTransparentColorIndex() } public function getDisposalMethod() + { + return $this->disposalMethod; + } + + public function setDisposalMethod($value) + { + $this->disposalMethod = $value; + + return $this; + } + + public function decodeDisposalMethod() { if ($this->graphicsControlExtension) { $byte = substr($this->graphicsControlExtension, 1, 1); @@ -129,9 +187,4 @@ public function isInterlaced() { return $this->interlaced; } - - public function getImageData() - { - return $this->imageData; - } } diff --git a/tests/GifDecoderTest.php b/tests/GifDecoderTest.php index 0b7ac8802..6da72d52c 100644 --- a/tests/GifDecoderTest.php +++ b/tests/GifDecoderTest.php @@ -21,6 +21,21 @@ private function getTestDecoder($file) return new Decoder($file); } + public function testConstructorFromFile() + { + $decoder = new Decoder('tests/images/animation.gif'); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoder', $decoder); + } + + public function testInitFromData() + { + $data = file_get_contents('tests/images/animation.gif'); + + $decoder = new Decoder; + $decoder->initFromData($data); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoder', $decoder); + } + public function testDecode() { $decoded = $this->decoder->decode(); diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php index 584114f9f..cd3a36bd5 100644 --- a/tests/GifFrameTest.php +++ b/tests/GifFrameTest.php @@ -28,14 +28,31 @@ public function testSetProperty() $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $test); } - public function testGetDelay() + public function testSetGetImageData() { $frame = new Frame; - $this->assertFalse($frame->getDelay()); + $this->assertNull($frame->getImageData()); + $result = $frame->setImageData('foo'); + $this->assertEquals('foo', $frame->getImageData()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); + } + + public function testSetGetDelay() + { + $frame = new Frame; + $this->assertNull($frame->getDelay()); + + $frame = new Frame; + $result = $frame->setDelay(20); + $this->assertEquals(20, $frame->getDelay()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); + } + public function testDecodeDelay() + { $frame = new Frame; $frame->setProperty('graphicsControlExtension', "\x04\x00\x14\x00\x00\x00"); - $this->assertEquals(20, $frame->getDelay()); + $this->assertEquals(20, $frame->decodeDelay()); } public function testHasTransparentColor() @@ -48,27 +65,49 @@ public function testHasTransparentColor() $this->assertTrue($frame->hasTransparentColor()); } - public function testGetTransparentColorIndex() + public function testSetGetTransparentColorIndex() + { + $frame = new Frame; + $this->assertNull($frame->getTransparentColorIndex()); + + $frame = new Frame; + $result = $frame->setTransparentColorIndex('foo'); + $this->assertEquals('foo', $frame->getTransparentColorIndex()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); + } + + public function testDecodeTransparentColorIndex() { $frame = new Frame; - $this->assertFalse($frame->getTransparentColorIndex()); + $this->assertFalse($frame->decodeTransparentColorIndex()); $frame = new Frame; $frame->setProperty('graphicsControlExtension', "\x04\x01\x14\x00\xF1\x00"); - $this->assertEquals("\xF1", $frame->getTransparentColorIndex()); + $this->assertEquals("\xF1", $frame->decodeTransparentColorIndex()); + } + + public function testSetGetDisposalMethod() + { + $frame = new Frame; + $this->assertNull($frame->getDisposalMethod()); + + $frame = new Frame; + $result = $frame->setDisposalMethod('foo'); + $this->assertEquals('foo', $frame->getDisposalMethod()); + $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); } - public function testGetDisposalMethod() + public function testDecodeDisposalMethod() { $frame = new Frame; - $this->assertEquals(0, $frame->getDisposalMethod()); + $this->assertEquals(0, $frame->decodeDisposalMethod()); $frame = new Frame; $frame->setProperty('graphicsControlExtension', "\x04\x05\x14\x00\xF1\x00"); - $this->assertEquals(1, $frame->getDisposalMethod()); + $this->assertEquals(1, $frame->decodeDisposalMethod()); $frame = new Frame; $frame->setProperty('graphicsControlExtension', "\x04\x0C\x14\x00\xF1\x00"); - $this->assertEquals(3, $frame->getDisposalMethod()); + $this->assertEquals(3, $frame->decodeDisposalMethod()); } } From 9ec513f35d1e667d6f6c2f49ecc6f379412ada86 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 6 Dec 2014 12:09:16 +0100 Subject: [PATCH 24/88] added animation classes --- src/Intervention/Image/Animation.php | 94 ++++++++++++++++++++++++++++ src/Intervention/Image/Frame.php | 32 ++++++++++ tests/AnimationTest.php | 88 ++++++++++++++++++++++++++ tests/FrameTest.php | 22 +++++++ 4 files changed, 236 insertions(+) create mode 100644 src/Intervention/Image/Animation.php create mode 100644 src/Intervention/Image/Frame.php create mode 100644 tests/AnimationTest.php create mode 100644 tests/FrameTest.php diff --git a/src/Intervention/Image/Animation.php b/src/Intervention/Image/Animation.php new file mode 100644 index 000000000..18e8f4741 --- /dev/null +++ b/src/Intervention/Image/Animation.php @@ -0,0 +1,94 @@ +loops = $loops; + } + + /** + * Returns Iterator + * + * @return ArrayIterator + */ + public function getIterator() + { + return new ArrayIterator($this->frames); + } + + /** + * Set the number of loops + * + * @param integer $value + */ + public function setLoops($value) + { + $this->loops = intval($value); + } + + /** + * Add one frame to the Animation + * + * @param Frame $frame + */ + public function addFrame(Frame $frame) + { + $this->frames[] = $frame; + } + + /** + * Append an array of frames to the Animation + * + * @param Array $frames + */ + public function addFrames(Array $frames) + { + $this->frames = array_merge($this->frames, $frames); + } + + /** + * Overwrite the current frames with array of frames + * + * @param Array $frames + */ + public function setFrames(Array $frames) + { + $this->frames = $frames; + } + + /** + * Get the current set of frames + * + * @return Array + */ + public function getFrames() + { + return $this->frames; + } +} diff --git a/src/Intervention/Image/Frame.php b/src/Intervention/Image/Frame.php new file mode 100644 index 000000000..8daaf8e95 --- /dev/null +++ b/src/Intervention/Image/Frame.php @@ -0,0 +1,32 @@ +core = $core; + $this->delay = $delay; + } +} diff --git a/tests/AnimationTest.php b/tests/AnimationTest.php new file mode 100644 index 000000000..62186affb --- /dev/null +++ b/tests/AnimationTest.php @@ -0,0 +1,88 @@ +assertInstanceOf('Intervention\Image\Animation', $animation); + $this->assertNull($animation->loops); + } + + public function testConstructorWithParameters() + { + $animation = new Animation(12); + $this->assertInstanceOf('Intervention\Image\Animation', $animation); + $this->assertEquals(12, $animation->loops); + } + + public function testIterate() + { + $frame = Mockery::mock('Intervention\Image\Frame'); + $animation = new Animation; + $animation->addFrame($frame); + $animation->addFrame($frame); + $animation->addFrame($frame); + $counter = 0; + foreach ($animation as $frame) { + $counter++; + } + $this->assertEquals(3, $counter); + } + + public function testSetLoops() + { + $animation = new Animation(12); + $animation->setLoops(13); + $this->assertEquals(13, $animation->loops); + } + + public function testAddFrame() + { + $frame = Mockery::mock('Intervention\Image\Frame'); + $animation = new Animation; + $animation->addFrame($frame); + $this->assertHasFrames(1, $animation); + $animation->addFrame($frame); + $this->assertHasFrames(2, $animation); + } + + public function testAddFrames() + { + $frame = Mockery::mock('Intervention\Image\Frame'); + $animation = new Animation; + $animation->addFrames(array($frame, $frame)); + $this->assertHasFrames(2, $animation); + $animation->addFrames(array($frame, $frame, $frame)); + $this->assertHasFrames(5, $animation); + $animation->addFrames(array($frame, $frame)); + $this->assertHasFrames(7, $animation); + } + + public function testSetFrames() + { + $frame = Mockery::mock('Intervention\Image\Frame'); + $animation = new Animation; + $animation->setFrames(array($frame, $frame, $frame)); + $this->assertHasFrames(3, $animation); + } + + public function testGetFrames() + { + $animation = new Animation; + $this->assertEquals(array(), $animation->getFrames()); + } + + private function assertHasFrames($number, $animation) + { + $this->assertEquals($number, count($animation->getFrames())); + } + +} \ No newline at end of file diff --git a/tests/FrameTest.php b/tests/FrameTest.php new file mode 100644 index 000000000..4d14ff87d --- /dev/null +++ b/tests/FrameTest.php @@ -0,0 +1,22 @@ +assertInstanceOf('Intervention\Image\Frame', $frame); + $this->assertEquals('foo', $frame->core); + $this->assertEquals(0, $frame->delay); + + $frame = new Frame('foo', 250); + $this->assertEquals(250, $frame->delay); + } +} From 2fc3e9b434b296a9f3d238e8366f1f7cbb7178af Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 6 Dec 2014 12:21:56 +0100 Subject: [PATCH 25/88] animation method --- src/Intervention/Image/Gd/Driver.php | 17 +++++++++++++++++ src/Intervention/Image/ImageManager.php | 15 +++++++++++++++ src/Intervention/Image/ImageManagerStatic.php | 15 +++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index aaf01170e..b44140a55 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -45,6 +45,23 @@ public function newImage($width, $height, $background = null) return $image; } + /** + * Creates a new animation image instance + * + * @param integer $width + * @param integer $height + * @param \Closure $callback + * @param integer $loops + * + * @return \Intervention\Image\Image + */ + public function newAnimation($width, $height, $callback = null, $loops = null) + { + $image = new \Intervention\Image\Image(new self, $core); + + return $image; + } + /** * Reads given string into color object * diff --git a/src/Intervention/Image/ImageManager.php b/src/Intervention/Image/ImageManager.php index a1489f24e..48cceecdf 100644 --- a/src/Intervention/Image/ImageManager.php +++ b/src/Intervention/Image/ImageManager.php @@ -63,6 +63,21 @@ public function canvas($width, $height, $background = null) return $this->createDriver()->newImage($width, $height, $background); } + /** + * Creates a new animation + * + * @param integer $width + * @param integer $height + * @param \Closure $callback + * @param integer $loops + * + * @return \Intervention\Image\Image + */ + public function animate($width, $height, $callback = null, $loops = null) + { + return $this->createDriver()->newAnimation($width, $height, $callback, $loops); + } + /** * Create new cached image and run callback * (requires additional package intervention/imagecache) diff --git a/src/Intervention/Image/ImageManagerStatic.php b/src/Intervention/Image/ImageManagerStatic.php index 3088bf55b..94a67138b 100644 --- a/src/Intervention/Image/ImageManagerStatic.php +++ b/src/Intervention/Image/ImageManagerStatic.php @@ -71,6 +71,21 @@ public static function canvas($width, $height, $background = null) return self::getManager()->canvas($width, $height, $background); } + /** + * Creates a new animation + * + * @param integer $width + * @param integer $height + * @param \Closure $callback + * @param integer $loops + * + * @return \Intervention\Image\Image + */ + public static function animate($width, $height, $callback = null, $loops = null) + { + return self::getManager()->animate($width, $height, $callback, $loops); + } + /** * Create new cached image and run callback statically * From 7b99dd9ba41c60de78b7f332ae6c022bb3c9069b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 13 Dec 2014 12:15:37 +0100 Subject: [PATCH 26/88] disposal property --- src/Intervention/Image/Frame.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Frame.php b/src/Intervention/Image/Frame.php index 8daaf8e95..e1bc667d5 100644 --- a/src/Intervention/Image/Frame.php +++ b/src/Intervention/Image/Frame.php @@ -4,6 +4,10 @@ class Frame { + const DISPOSAL_METHOD_LEAVE = 1; + const DISPOSAL_METHOD_BACKGROUND = 2; + const DISPOSAL_METHOD_PREVIOUS = 3; + /** * Image data * @@ -18,15 +22,24 @@ class Frame */ public $delay; + /** + * Disposal method + * + * @var integer + */ + public $disposal; + /** * Create new frame instance * * @param mixed $core * @param integer $delay + * @param integer $disposal */ - public function __construct($core, $delay = 0) + public function __construct($core, $delay = 0, $disposal = DISPOSAL_METHOD_LEAVE) { $this->core = $core; $this->delay = $delay; + $this->disposal = $disposal; } } From 4dcf9cf53945804c9aa7fa0185a9b7663917cc8e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 30 Dec 2014 19:26:11 +0100 Subject: [PATCH 27/88] finally --- src/Intervention/Image/Frame.php | 4 ++-- tests/GifDecoderTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/Frame.php b/src/Intervention/Image/Frame.php index e1bc667d5..300abb337 100644 --- a/src/Intervention/Image/Frame.php +++ b/src/Intervention/Image/Frame.php @@ -36,10 +36,10 @@ class Frame * @param integer $delay * @param integer $disposal */ - public function __construct($core, $delay = 0, $disposal = DISPOSAL_METHOD_LEAVE) + public function __construct($core, $delay = 0, $disposal = null) { $this->core = $core; $this->delay = $delay; - $this->disposal = $disposal; + $this->disposal = is_null($disposal) ? self::DISPOSAL_METHOD_LEAVE : $disposal; } } diff --git a/tests/GifDecoderTest.php b/tests/GifDecoderTest.php index 6da72d52c..a4695c5ed 100644 --- a/tests/GifDecoderTest.php +++ b/tests/GifDecoderTest.php @@ -77,7 +77,7 @@ public function testDecode() $this->assertEquals($sizes[$key]['height'], $frame->size->height); $this->assertEquals($offsets[$key]['left'], $frame->offset->left); $this->assertEquals($offsets[$key]['top'], $frame->offset->top); - $this->assertEquals($delays[$key], $frame->getDelay()); + $this->assertEquals($delays[$key], $frame->decodeDelay()); $this->assertEquals($interlaced[$key], $frame->isInterlaced()); $this->assertEquals($localColorTables[$key], $frame->getLocalColorTable()); $this->assertFalse($frame->hasLocalColorTable()); From 8af0d5ea36c245f7f0b2dfc365bd75927a2c4316 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 12:48:31 +0100 Subject: [PATCH 28/88] added container --- src/Intervention/Image/AbstractDriver.php | 6 +-- src/Intervention/Image/Animation.php | 2 + src/Intervention/Image/ContainerInterface.php | 9 +++++ src/Intervention/Image/Frame.php | 10 +++++ src/Intervention/Image/Gd/Container.php | 22 ++++++++++ src/Intervention/Image/Gd/Decoder.php | 4 +- src/Intervention/Image/Gd/Driver.php | 40 ++++++++++++++++++- src/Intervention/Image/Image.php | 18 ++++----- src/Intervention/Image/Imagick/Container.php | 31 ++++++++++++++ src/Intervention/Image/Imagick/Decoder.php | 26 +++++------- src/Intervention/Image/Imagick/Driver.php | 2 +- 11 files changed, 140 insertions(+), 30 deletions(-) create mode 100644 src/Intervention/Image/ContainerInterface.php create mode 100644 src/Intervention/Image/Gd/Container.php create mode 100644 src/Intervention/Image/Imagick/Container.php diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php index dfa1649b8..fb530e545 100644 --- a/src/Intervention/Image/AbstractDriver.php +++ b/src/Intervention/Image/AbstractDriver.php @@ -44,13 +44,13 @@ abstract public function parseColor($value); abstract protected function coreAvailable(); /** - * Returns clone of given core + * Returns clone of given container * * @return mixed */ - public function cloneCore($core) + public function cloneContainer(ContainerInterface $container) { - return clone $core; + return clone $container; } /** diff --git a/src/Intervention/Image/Animation.php b/src/Intervention/Image/Animation.php index 18e8f4741..5b098514f 100644 --- a/src/Intervention/Image/Animation.php +++ b/src/Intervention/Image/Animation.php @@ -80,6 +80,8 @@ public function addFrames(Array $frames) public function setFrames(Array $frames) { $this->frames = $frames; + + return $this; } /** diff --git a/src/Intervention/Image/ContainerInterface.php b/src/Intervention/Image/ContainerInterface.php new file mode 100644 index 000000000..2e3997f0f --- /dev/null +++ b/src/Intervention/Image/ContainerInterface.php @@ -0,0 +1,9 @@ +delay = $delay; $this->disposal = is_null($disposal) ? self::DISPOSAL_METHOD_LEAVE : $disposal; } + + /** + * Gets core of current frame + * + * @return mixed + */ + public function getCore() + { + return $this->core; + } } diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php new file mode 100644 index 000000000..b5abdd244 --- /dev/null +++ b/src/Intervention/Image/Gd/Container.php @@ -0,0 +1,22 @@ +getFrames()[0]->getCore(); + } + + public function setCore($core) + { + $this->setFrames(array( + new Frame($core) + )); + } +} diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index fe10ebff4..0fcb8b87a 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -62,7 +62,9 @@ public function initFromPath($path) */ public function initFromGdResource($resource) { - return new Image(new Driver, $resource); + $driver = new Driver; + + return new Image($driver, $driver->newContainer($resource)); } /** diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index b44140a55..418222213 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -2,7 +2,9 @@ namespace Intervention\Image\Gd; +use \Intervention\Image\ContainerInterface; use \Intervention\Image\Size; +use \Intervention\Image\Frame; class Driver extends \Intervention\Image\AbstractDriver { @@ -36,7 +38,7 @@ public function newImage($width, $height, $background = null) { // create empty resource $core = imagecreatetruecolor($width, $height); - $image = new \Intervention\Image\Image(new self, $core); + $image = new \Intervention\Image\Image(new self, $this->newContainer($core)); // set background color $background = new Color($background); @@ -100,4 +102,40 @@ public function cloneCore($core) return $clone; } + + /** + * Returns clone of current container + * + * @param ContainerInterface $container + * @return \Intervention\Image\Gd\Container + */ + public function cloneContainer(ContainerInterface $container) + { + $cloneContainer = clone $container; + $cloneFrames = array(); + + // clone each resource of container + foreach ($container as $frame) { + $cloneFrames[] = new Frame($this->cloneCore($frame->getCore())); + } + + $cloneContainer->setFrames($cloneFrames); + + return $cloneContainer; + } + + /** + * Builds new container from GD resource + * + * @param resource $resource + * @return \Intervention\Image\Gd\Container + */ + public function newContainer($resource) + { + $container = new Container; + + $container->setFrames(array(new Frame($resource))); + + return $container; + } } diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 6b85bacbc..8ad814c2d 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -55,11 +55,11 @@ class Image extends File protected $driver; /** - * Image resource/object of current image processor + * Container object of resources/objects of current image processor * * @var mixed */ - protected $core; + protected $container; /** * Array of Image resource backups of current image processor @@ -78,13 +78,13 @@ class Image extends File /** * Creates a new Image instance * - * @param AbstractDriver $driver - * @param mixed $core + * @param AbstractDriver $driver + * @param ContainerInterface $container */ - public function __construct(AbstractDriver $driver = null, $core = null) + public function __construct(AbstractDriver $driver = null, ContainerInterface $container = null) { $this->driver = $driver; - $this->core = $core; + $this->container = $container; } /** @@ -184,7 +184,7 @@ public function setDriver(AbstractDriver $driver) */ public function getCore() { - return $this->core; + return $this->container->getCore(); } /** @@ -194,7 +194,7 @@ public function getCore() */ public function setCore($core) { - $this->core = $core; + $this->container->setCore($core); return $this; } @@ -356,6 +356,6 @@ public function __toString() */ public function __clone() { - $this->core = $this->driver->cloneCore($this->core); + $this->container = $this->driver->cloneContainer($this->container); } } diff --git a/src/Intervention/Image/Imagick/Container.php b/src/Intervention/Image/Imagick/Container.php new file mode 100644 index 000000000..05705792c --- /dev/null +++ b/src/Intervention/Image/Imagick/Container.php @@ -0,0 +1,31 @@ +imagick = $imagick; + } + + public function getIterator() + { + return $this->imagick; + } + + public function getCore() + { + return $this->imagick; + } + + public function setCore($core) + { + $this->imagick = $core; + } +} diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index 3f11dd4b9..55d3a976e 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -15,12 +15,12 @@ class Decoder extends \Intervention\Image\AbstractDecoder */ public function initFromPath($path) { - $core = new \Imagick; + $imagick = new \Imagick; try { - $core->readImage($path); - $core->setImageType(\Imagick::IMGTYPE_TRUECOLORMATTE); + $imagick->readImage($path); + $imagick->setImageType(\Imagick::IMGTYPE_TRUECOLORMATTE); } catch (\ImagickException $e) { throw new \Intervention\Image\Exception\NotReadableException( @@ -29,7 +29,7 @@ public function initFromPath($path) } // build image - $image = $this->initFromImagick($core); + $image = $this->initFromImagick($imagick); $image->setFileInfoFromPath($path); return $image; @@ -51,19 +51,15 @@ public function initFromGdResource($resource) /** * Initiates new image from Imagick object * - * @param Imagick $object + * @param Imagick $imagick * @return \Intervention\Image\Image */ - public function initFromImagick(\Imagick $object) + public function initFromImagick(\Imagick $imagick) { - // currently animations are not supported - // so all images are turned into static - $object = $this->removeAnimation($object); - // reset image orientation - $object->setImageOrientation(\Imagick::ORIENTATION_UNDEFINED); + $imagick->setImageOrientation(\Imagick::ORIENTATION_UNDEFINED); - return new Image(new Driver, $object); + return new Image(new Driver, new Container($imagick)); } /** @@ -74,11 +70,11 @@ public function initFromImagick(\Imagick $object) */ public function initFromBinary($binary) { - $core = new \Imagick; + $imagick = new \Imagick; try { - $core->readImageBlob($binary); + $imagick->readImageBlob($binary); } catch (\ImagickException $e) { throw new \Intervention\Image\Exception\NotReadableException( @@ -87,7 +83,7 @@ public function initFromBinary($binary) } // build image - $image = $this->initFromImagick($core); + $image = $this->initFromImagick($imagick); $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); return $image; diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php index b8bfef482..24b9ef927 100644 --- a/src/Intervention/Image/Imagick/Driver.php +++ b/src/Intervention/Image/Imagick/Driver.php @@ -44,7 +44,7 @@ public function newImage($width, $height, $background = null) $core->setColorspace(\Imagick::COLORSPACE_UNDEFINED); // build image - $image = new \Intervention\Image\Image(new self, $core); + $image = new \Intervention\Image\Image(new self, new Container($core)); return $image; } From d2622ca0e8f1975d8347b292d592547d3d52207f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 14:07:32 +0100 Subject: [PATCH 29/88] renaming --- src/Intervention/Image/AbstractDriver.php | 4 ++-- src/Intervention/Image/Gd/Driver.php | 6 +++--- src/Intervention/Image/Imagick/Driver.php | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Intervention/Image/AbstractDriver.php b/src/Intervention/Image/AbstractDriver.php index fb530e545..67e45d3f2 100644 --- a/src/Intervention/Image/AbstractDriver.php +++ b/src/Intervention/Image/AbstractDriver.php @@ -37,11 +37,11 @@ abstract public function newImage($width, $height, $background); abstract public function parseColor($value); /** - * Checks if core module installation is available + * Checks if image module installation is available * * @return boolean */ - abstract protected function coreAvailable(); + abstract protected function moduleAvailable(); /** * Returns clone of given container diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index 418222213..b8a16034e 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -16,7 +16,7 @@ class Driver extends \Intervention\Image\AbstractDriver */ public function __construct(Decoder $decoder = null, Encoder $encoder = null) { - if ( ! $this->coreAvailable()) { + if ( ! $this->moduleAvailable()) { throw new \Intervention\Image\Exception\NotSupportedException( "GD Library extension not available with this PHP installation." ); @@ -76,11 +76,11 @@ public function parseColor($value) } /** - * Checks if core module installation is available + * Checks if image module installation is available * * @return boolean */ - protected function coreAvailable() + protected function moduleAvailable() { return (extension_loaded('gd') && function_exists('gd_info')); } diff --git a/src/Intervention/Image/Imagick/Driver.php b/src/Intervention/Image/Imagick/Driver.php index 24b9ef927..55e005425 100644 --- a/src/Intervention/Image/Imagick/Driver.php +++ b/src/Intervention/Image/Imagick/Driver.php @@ -14,7 +14,7 @@ class Driver extends \Intervention\Image\AbstractDriver */ public function __construct(Decoder $decoder = null, Encoder $encoder = null) { - if ( ! $this->coreAvailable()) { + if ( ! $this->moduleAvailable()) { throw new \Intervention\Image\Exception\NotSupportedException( "ImageMagick module not available with this PHP installation." ); @@ -61,11 +61,11 @@ public function parseColor($value) } /** - * Checks if core module installation is available + * Checks if image module installation is available * * @return boolean */ - protected function coreAvailable() + protected function moduleAvailable() { return (extension_loaded('imagick') && class_exists('Imagick')); } From f9003fea9afa78157efd4ca3662a8519ee8b0edd Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 14:13:09 +0100 Subject: [PATCH 30/88] fix --- tests/ImageTest.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/ImageTest.php b/tests/ImageTest.php index ace7546d3..b0ca31082 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -12,14 +12,14 @@ public function tearDown() public function testGetCore() { $image = $this->getTestImage(); - $this->assertEquals('mock', $image->getCore()); + $this->assertEquals('core', $image->getCore()); } public function testCommandCall() { $image = $this->getTestImage(); $result = $image->test(1, 2, 3); - $this->assertEquals('mock', $result); + $this->assertEquals('result', $result); } public function testEncode() @@ -67,11 +67,13 @@ private function getTestImage() { $size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $driver = Mockery::mock('\Intervention\Image\AbstractDriver'); + $container = Mockery::mock('\Intervention\Image\ContainerInterface'); + $container->shouldReceive('getCore')->andReturn('core'); $command = Mockery::mock('\Intervention\Image\Commands\AbstractCommand'); $command->shouldReceive('hasOutput')->andReturn(true); - $command->shouldReceive('getOutput')->andReturn('mock'); + $command->shouldReceive('getOutput')->andReturn('result'); $driver->shouldReceive('executeCommand')->andReturn($command); - $image = new Image($driver, 'mock'); + $image = new Image($driver, $container); $image->mime = 'image/png'; $image->dirname = './tmp'; $image->basename = 'foo.png'; From 9da10bf357501eb16f5dcd2c28f0fd5fc9d3c791 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 14:20:52 +0100 Subject: [PATCH 31/88] container --- src/Intervention/Image/Image.php | 34 +++++++++++++++++++++++++++++++- tests/ImageTest.php | 16 +++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 8ad814c2d..1572f85c8 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -45,7 +45,7 @@ * @method \Intervention\Image\Image trim(string $base = 'top-left', array $away = array('top', 'bottom', 'left', 'right'), integer $tolerance = 0, integer $feather = 0) Trim away image space in given color. Define an optional base to pick a color at a certain position and borders that should be trimmed away. You can also set an optional tolerance level, to trim similar colors and add a feathering border around the trimed image. * @method \Intervention\Image\Image widen(integer $width, \Closure $callback = null) Resizes the current image to new width, constraining aspect ratio. Pass an optional Closure callback as third parameter, to apply additional constraints like preventing possible upsizing. */ -class Image extends File +class Image extends File implements \IteratorAggregate { /** * Instance of current image driver @@ -101,6 +101,16 @@ public function __call($name, $arguments) return $command->hasOutput() ? $command->getOutput() : $this; } + /** + * Returns Iterator + * + * @return \Intervention\Image\ContainerInterface + */ + public function getIterator() + { + return $this->getContainer(); + } + /** * Starts encoding of current image * @@ -199,6 +209,28 @@ public function setCore($core) return $this; } + /** + * Returns current image container + * + * @return \Intervention\Image\ContainerInterface + */ + public function getContainer() + { + return $this->container; + } + + /** + * Set current image container + * + * @param mixed $core + */ + public function setContainer(ContainerInterface $container) + { + $this->container = $container; + + return $this; + } + /** * Returns current image backup * diff --git a/tests/ImageTest.php b/tests/ImageTest.php index b0ca31082..0245d46b8 100644 --- a/tests/ImageTest.php +++ b/tests/ImageTest.php @@ -15,6 +15,21 @@ public function testGetCore() $this->assertEquals('core', $image->getCore()); } + public function testGetContainer() + { + $image = $this->getTestImage(); + $this->assertInstanceOf('\Intervention\Image\ContainerInterface', $image->getContainer()); + } + + public function testSetContainer() + { + $image = $this->getTestImage(); + $container = Mockery::mock('\Intervention\Image\ContainerInterface'); + $container->shouldReceive('isReplaced')->andReturn(true); + $image->setContainer($container); + $this->assertTrue($image->getContainer()->isReplaced()); + } + public function testCommandCall() { $image = $this->getTestImage(); @@ -68,6 +83,7 @@ private function getTestImage() $size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $driver = Mockery::mock('\Intervention\Image\AbstractDriver'); $container = Mockery::mock('\Intervention\Image\ContainerInterface'); + $container->shouldReceive('setCore'); $container->shouldReceive('getCore')->andReturn('core'); $command = Mockery::mock('\Intervention\Image\Commands\AbstractCommand'); $command->shouldReceive('hasOutput')->andReturn(true); From 4722d1e54a9395861f6492e70c1ed82aee16a811 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 18:01:07 +0100 Subject: [PATCH 32/88] coalesce and optimize --- src/Intervention/Image/Imagick/Decoder.php | 3 +++ src/Intervention/Image/Imagick/Encoder.php | 1 + 2 files changed, 4 insertions(+) diff --git a/src/Intervention/Image/Imagick/Decoder.php b/src/Intervention/Image/Imagick/Decoder.php index 55d3a976e..63e93dcb5 100644 --- a/src/Intervention/Image/Imagick/Decoder.php +++ b/src/Intervention/Image/Imagick/Decoder.php @@ -59,6 +59,9 @@ public function initFromImagick(\Imagick $imagick) // reset image orientation $imagick->setImageOrientation(\Imagick::ORIENTATION_UNDEFINED); + // coalesce possible animation + $imagick = $imagick->coalesceImages(); + return new Image(new Driver, new Container($imagick)); } diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index 2d6c235fa..833bfe1cc 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -62,6 +62,7 @@ protected function processGif() $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); + $imagick = $imagick->optimizeImageLayers(); return $imagick->getImagesBlob(); } From 85a334d909419e27d5efd9b0ccb5c2f15528d648 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 18:15:31 +0100 Subject: [PATCH 33/88] added init from container --- src/Intervention/Image/Gd/Decoder.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 0fcb8b87a..1331bd4a8 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -4,6 +4,7 @@ use \Intervention\Image\Image; use \Intervention\Image\Size; +use \Intervention\Image\ContainerInterface; class Decoder extends \Intervention\Image\AbstractDecoder { @@ -102,6 +103,17 @@ public function initFromBinary($binary) return $image; } + /** + * Initiates new image from container object + * + * @param ContainerInterface $container + * @return \Intervention\Image\Image + */ + public function initFromInterventionContainer(ContainerInterface $container) + { + return new Image(new Driver, $container); + } + /** * Transform GD resource into Truecolor version * From f1612cbfbfe96beef412e16efbc99b6c58603e47 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 18:21:11 +0100 Subject: [PATCH 34/88] fixes --- src/Intervention/Image/Imagick/Encoder.php | 1 - tests/EncoderTest.php | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Imagick/Encoder.php b/src/Intervention/Image/Imagick/Encoder.php index 833bfe1cc..2d6c235fa 100644 --- a/src/Intervention/Image/Imagick/Encoder.php +++ b/src/Intervention/Image/Imagick/Encoder.php @@ -62,7 +62,6 @@ protected function processGif() $imagick->setImageFormat($format); $imagick->setCompression($compression); $imagick->setImageCompression($compression); - $imagick = $imagick->optimizeImageLayers(); return $imagick->getImagesBlob(); } diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 0b7eba8b3..062adfe1a 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -239,6 +239,7 @@ public function getImagickMock($type) $imagick->shouldReceive('setimagecompressionquality'); $imagick->shouldReceive('setimagebackgroundcolor'); $imagick->shouldReceive('setbackgroundcolor'); + $imagick->shouldReceive('optimizeimagelayers')->andReturn($imagick); $imagick->shouldReceive('mergeimagelayers')->andReturn($imagick); $imagick->shouldReceive('getimagesblob')->once()->andReturn(sprintf('mock-%s', $type)); return $imagick; From 5ff27ddc9d0a8bf1897c74bb071684ccff90bf69 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 2 Jan 2015 20:03:31 +0100 Subject: [PATCH 35/88] moved gif encoder --- .../Image/{Tools => Gd}/Gif/Decoded.php | 2 +- .../Image/{Tools => Gd}/Gif/Decoder.php | 2 +- .../Image/{Tools => Gd}/Gif/Encoder.php | 2 +- src/Intervention/Image/{Tools => Gd}/Gif/Frame.php | 2 +- tests/GifDecodedTest.php | 14 +++++++------- tests/GifDecoderTest.php | 8 ++++---- tests/GifEncoderTest.php | 8 ++++---- tests/GifFrameTest.php | 12 ++++++------ 8 files changed, 25 insertions(+), 25 deletions(-) rename src/Intervention/Image/{Tools => Gd}/Gif/Decoded.php (99%) rename src/Intervention/Image/{Tools => Gd}/Gif/Decoder.php (99%) rename src/Intervention/Image/{Tools => Gd}/Gif/Encoder.php (99%) rename src/Intervention/Image/{Tools => Gd}/Gif/Frame.php (98%) diff --git a/src/Intervention/Image/Tools/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php similarity index 99% rename from src/Intervention/Image/Tools/Gif/Decoded.php rename to src/Intervention/Image/Gd/Gif/Decoded.php index d736c1d99..ebe33e91e 100644 --- a/src/Intervention/Image/Tools/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -1,6 +1,6 @@ setHeader('foo'); $this->assertEquals('foo', $decoded->getHeader()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); } public function testSetGetLogicalScreenDescriptor() @@ -17,7 +17,7 @@ public function testSetGetLogicalScreenDescriptor() $decoded = new Decoded; $obj = $decoded->setLogicalScreenDescriptor('foo'); $this->assertEquals('foo', $decoded->getLogicalScreenDescriptor()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); } public function testSetGetGlobalColorTable() @@ -25,7 +25,7 @@ public function testSetGetGlobalColorTable() $decoded = new Decoded; $obj = $decoded->setGlobalColorTable('foo'); $this->assertEquals('foo', $decoded->getGlobalColorTable()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); } public function testSetGetNetscapeExtension() @@ -33,7 +33,7 @@ public function testSetGetNetscapeExtension() $decoded = new Decoded; $obj = $decoded->setNetscapeExtension('foo'); $this->assertEquals('foo', $decoded->getNetscapeExtension()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); } public function testSetGetPlaintextExtension() @@ -41,7 +41,7 @@ public function testSetGetPlaintextExtension() $decoded = new Decoded; $obj = $decoded->setPlaintextExtension('foo'); $this->assertEquals('foo', $decoded->getPlaintextExtension()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); } public function testSetGetCommentExtension() @@ -49,7 +49,7 @@ public function testSetGetCommentExtension() $decoded = new Decoded; $obj = $decoded->setCommentExtension('foo'); $this->assertEquals('foo', $decoded->getCommentExtension()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $obj); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); } public function testGetCanvasWidth() diff --git a/tests/GifDecoderTest.php b/tests/GifDecoderTest.php index a4695c5ed..614caf3ad 100644 --- a/tests/GifDecoderTest.php +++ b/tests/GifDecoderTest.php @@ -1,6 +1,6 @@ assertInstanceOf('Intervention\Image\Tools\Gif\Decoder', $decoder); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoder', $decoder); } public function testInitFromData() @@ -33,14 +33,14 @@ public function testInitFromData() $decoder = new Decoder; $decoder->initFromData($data); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoder', $decoder); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoder', $decoder); } public function testDecode() { $decoded = $this->decoder->decode(); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Decoded', $decoded); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $decoded); $this->assertEquals(8, $decoded->countFrames()); $this->assertTrue($decoded->hasGlobalColorTable()); $this->assertEquals(32, $decoded->countGlobalColors()); diff --git a/tests/GifEncoderTest.php b/tests/GifEncoderTest.php index 8699b97af..7b9a54320 100644 --- a/tests/GifEncoderTest.php +++ b/tests/GifEncoderTest.php @@ -1,6 +1,6 @@ setCanvas(300, 200); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Encoder', $result); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); $this->assertEquals(300, $encoder->canvasWidth); $this->assertEquals(200, $encoder->canvasHeight); } @@ -17,7 +17,7 @@ public function testSetLoops() { $encoder = new Encoder; $result = $encoder->setLoops(6); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Encoder', $result); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); $this->assertEquals(6, $encoder->loops); } @@ -25,7 +25,7 @@ public function testSetGlobalColorTable() { $encoder = new Encoder; $result = $encoder->setGlobalColorTable('foo'); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Encoder', $result); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); $this->assertEquals('foo', $encoder->globalColorTable); } } diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php index cd3a36bd5..565eadfd2 100644 --- a/tests/GifFrameTest.php +++ b/tests/GifFrameTest.php @@ -1,6 +1,6 @@ setProperty('foo', 'bar'); $this->assertEquals('bar', $frame->foo); $this->assertTrue($frame->propertyIsSet('foo')); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $test); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $test); } public function testSetGetImageData() @@ -34,7 +34,7 @@ public function testSetGetImageData() $this->assertNull($frame->getImageData()); $result = $frame->setImageData('foo'); $this->assertEquals('foo', $frame->getImageData()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); } public function testSetGetDelay() @@ -45,7 +45,7 @@ public function testSetGetDelay() $frame = new Frame; $result = $frame->setDelay(20); $this->assertEquals(20, $frame->getDelay()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); } public function testDecodeDelay() @@ -73,7 +73,7 @@ public function testSetGetTransparentColorIndex() $frame = new Frame; $result = $frame->setTransparentColorIndex('foo'); $this->assertEquals('foo', $frame->getTransparentColorIndex()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); } public function testDecodeTransparentColorIndex() @@ -94,7 +94,7 @@ public function testSetGetDisposalMethod() $frame = new Frame; $result = $frame->setDisposalMethod('foo'); $this->assertEquals('foo', $frame->getDisposalMethod()); - $this->assertInstanceOf('Intervention\Image\Tools\Gif\Frame', $result); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); } public function testDecodeDisposalMethod() From f735b66acd0d43e22a6bb9648798504220d5cd32 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 5 Jan 2015 19:16:39 +0100 Subject: [PATCH 36/88] decoding --- src/Intervention/Image/Gd/Gif/Decoded.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index ebe33e91e..cfb47b04f 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -2,6 +2,9 @@ namespace Intervention\Image\Gd\Gif; +use Intervention\Image\Frame as ContainerFrame; +use Intervention\Image\Gd\Container; + class Decoded { /** @@ -307,4 +310,19 @@ private function newFrameWithProperty($property, $value) $frame = new Frame; $this->frames[] = $frame->setProperty($property, $value); } + + public function toContainer() + { + $container = new Container; + $container->setLoops($this->getLoops()); + + foreach ($this->frames as $frame) { + $container->addFrame(new ContainerFrame( + $frame->toResource(), + $frame->getDelay() + )); + } + + return $container; + } } From a22a2a01d75a836065df4e761a6dd7999f1af8ae Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 6 Jan 2015 18:14:37 +0100 Subject: [PATCH 37/88] adding methods --- src/Intervention/Image/Gd/Gif/Decoded.php | 25 +++++++- src/Intervention/Image/Gd/Gif/Frame.php | 73 +++++++++++++++++++++++ tests/GifFrameTest.php | 65 ++++++++++++++++++++ 3 files changed, 162 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index cfb47b04f..961d54903 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -317,8 +317,31 @@ public function toContainer() $container->setLoops($this->getLoops()); foreach ($this->frames as $frame) { + + // create empty canvas + $resource = imagecreatetruecolor( + $this->getCanvasWidth(), + $this->getCanvasHeight() + ); + + imagealphablending($resource, false); + imagesavealpha($resource, true); + + // insert frame image data into canvas + imagecopy( + $resource, + $frame->toResource(), + 0, + 0, + 0, + 0, + $frame->getSize()->width, + $frame->getSize()->height + ); + + // add frame to container $container->addFrame(new ContainerFrame( - $frame->toResource(), + $resource, $frame->getDelay() )); } diff --git a/src/Intervention/Image/Gd/Gif/Frame.php b/src/Intervention/Image/Gd/Gif/Frame.php index 1b0920b3f..a016ab74a 100644 --- a/src/Intervention/Image/Gd/Gif/Frame.php +++ b/src/Intervention/Image/Gd/Gif/Frame.php @@ -178,6 +178,68 @@ public function decodeDisposalMethod() return 0; } + public function decodeWidth() + { + if ($this->imageDescriptor) { + return (int) unpack('v', + substr($this->imageDescriptor, 4, 2) + )[1]; + } + + return false; + } + + public function decodeHeight() + { + if ($this->imageDescriptor) { + return (int) unpack('v', + substr($this->imageDescriptor, 6, 2) + )[1]; + } + + return false; + } + + public function getSize() + { + $size = new \StdClass; + $size->width = $this->decodeWidth(); + $size->height = $this->decodeHeight(); + + return $size; + } + + public function decodeOffsetLeft() + { + if ($this->imageDescriptor) { + return (int) unpack('v', + substr($this->imageDescriptor, 0, 2) + )[1]; + } + + return false; + } + + public function decodeOffsetTop() + { + if ($this->imageDescriptor) { + return (int) unpack('v', + substr($this->imageDescriptor, 2, 2) + )[1]; + } + + return false; + } + + public function getOffset() + { + $offset = new \StdClass; + $offset->left = $this->decodeOffsetLeft(); + $offset->top = $this->decodeOffsetTop(); + + return $offset; + } + /** * Determines if current frame is saved as interlaced * @@ -187,4 +249,15 @@ public function isInterlaced() { return $this->interlaced; } + + public function toResource() + { + $encoder = new Encoder; + $encoder->setCanvas($this->decodeWidth(), $this->decodeHeight()); + $encoder->addFrame($this); + + $data = $encoder->encode(); + + return imagecreatefromstring($data); + } } diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php index 565eadfd2..89a32f42e 100644 --- a/tests/GifFrameTest.php +++ b/tests/GifFrameTest.php @@ -110,4 +110,69 @@ public function testDecodeDisposalMethod() $frame->setProperty('graphicsControlExtension', "\x04\x0C\x14\x00\xF1\x00"); $this->assertEquals(3, $frame->decodeDisposalMethod()); } + + public function testDecodeWidth() + { + $frame = new Frame; + $this->assertFalse($frame->decodeWidth()); + + $frame = new Frame; + $frame->setProperty('imageDescriptor', "\x00\x00\x00\x00\x40\x01\xF0\x00\x00"); + $this->assertEquals(320, $frame->decodeWidth()); + } + + public function testDecodeHeight() + { + $frame = new Frame; + $this->assertFalse($frame->decodeHeight()); + + $frame = new Frame; + $frame->setProperty('imageDescriptor', "\x00\x00\x00\x00\x40\x01\xF0\x00\x00"); + $this->assertEquals(240, $frame->decodeHeight()); + } + + public function testGetSize() + { + $frame = new Frame; + $frame->setProperty('imageDescriptor', "\x00\x00\x00\x00\x40\x01\xF0\x00\x00"); + $size = $frame->getSize(); + $this->assertInstanceOf('StdClass', $size); + $this->assertEquals(320, $size->width); + $this->assertEquals(240, $size->height); + } + + public function testDecodeOffsetTop() + { + $frame = new Frame; + $this->assertFalse($frame->decodeOffsetTop()); + + $frame = new Frame; + $frame->setProperty('imageDescriptor', "\x18\x00\x0C\x00\x40\x01\xF0\x00\x00"); + $this->assertEquals(12, $frame->decodeOffsetTop()); + } + + public function testDecodeOffsetLeft() + { + $frame = new Frame; + $this->assertFalse($frame->decodeOffsetLeft()); + + $frame = new Frame; + $frame->setProperty('imageDescriptor', "\x18\x00\x0C\x00\x40\x01\xF0\x00\x00"); + $this->assertEquals(24, $frame->decodeOffsetLeft()); + } + + public function testGetOffset() + { + $frame = new Frame; + $frame->setProperty('imageDescriptor', "\x18\x00\x0C\x00\x40\x01\xF0\x00\x00"); + $offset = $frame->getOffset(); + $this->assertInstanceOf('StdClass', $offset); + $this->assertEquals(12, $offset->top); + $this->assertEquals(24, $offset->left); + } + + public function testToResource() + { + $frame = new Frame; + } } From b5b93b3e5b1121a702af1e52532a4ef3487b3bcb Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 9 Jan 2015 11:07:11 +0100 Subject: [PATCH 38/88] gif decoder encoder --- src/Intervention/Image/Gd/Gif/Decoded.php | 25 ++++- src/Intervention/Image/Gd/Gif/Decoder.php | 60 +++++++++-- src/Intervention/Image/Gd/Gif/Encoder.php | 122 ++++++++++++++++++++-- src/Intervention/Image/Gd/Gif/Frame.php | 47 ++++++--- tests/GifFrameTest.php | 6 +- 5 files changed, 222 insertions(+), 38 deletions(-) diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index 961d54903..fbefeacb1 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -154,6 +154,17 @@ public function getFrames() return $this->frames; } + public function getFrame($index = 0) + { + if (array_key_exists($index, $this->frames)) { + return $this->frames[$index]; + } + + throw new \Intervention\Image\Exception\RuntimeException( + "Frame with index ({$index}) does not exists." + ); + } + public function countFrames() { return count($this->frames); @@ -286,6 +297,18 @@ public function countGlobalColors() return isset($bit) ? pow(2, $bit + 1) : 0; } + public function getBackgroundColorIndex() + { + if ($this->logicalScreenDescriptor) { + $index = substr($this->logicalScreenDescriptor, 5, 1); + $index = unpack('C', $index)[1]; + + return $index; + } + + return 0; + } + private function addToFirstFrameWithoutProperty($value, $property) { $added = false; @@ -330,7 +353,7 @@ public function toContainer() // insert frame image data into canvas imagecopy( $resource, - $frame->toResource(), + $resource_frame, 0, 0, 0, diff --git a/src/Intervention/Image/Gd/Gif/Decoder.php b/src/Intervention/Image/Gd/Gif/Decoder.php index aa36a4404..19f0e067a 100644 --- a/src/Intervention/Image/Gd/Gif/Decoder.php +++ b/src/Intervention/Image/Gd/Gif/Decoder.php @@ -7,7 +7,9 @@ class Decoder const IMAGE_SEPARATOR = "\x2C"; const EXTENSION_BLOCK_MARKER = "\x21"; const GRAPHICS_CONTROL_EXTENSION_MARKER = "\xF9"; - const NETSCAPE_EXTENSION_MARKER = "\xFF"; + const APPLICATION_EXTENSION_MARKER = "\xFF"; + const NETSCAPE_EXTENSION_MARKER = "NETSCAPE2.0"; + const XMP_EXTENSION_MARKER = "XMP DataXMP"; const PLAINTEXT_EXTENSION_MARKER = "\x01"; const COMMENT_EXTENSION_MARKER = "\xFE"; const BLOCK_TERMINATOR = "\x00"; @@ -102,9 +104,9 @@ public function decode() break 2; default: - // throw new \Intervention\Image\Exception\NotReadableException( - // "Unable to decode GIF image." - // ); + throw new \Intervention\Image\Exception\NotReadableException( + "Unable to decode GIF image." + ); break; } } @@ -115,9 +117,10 @@ public function decode() /** * Decode extension in image stream * + * @param Decoded $gif * @return void */ - private function decodeExtension($gif) + private function decodeExtension(Decoded $gif) { switch ($this->getNextBytes(1)) { @@ -125,13 +128,48 @@ private function decodeExtension($gif) $gif->addGraphicsControlExtension($this->getNextBytes(6)); break; - case self::NETSCAPE_EXTENSION_MARKER: - $gif->setNetscapeExtension($this->getNextBytes(17)); + case self::APPLICATION_EXTENSION_MARKER: + $application_block_size = $this->getNextBytes(1); + $application_block_size = unpack('C', $application_block_size)[1]; + $application_block = $this->getNextBytes($application_block_size); + + // only save netscape application extension + if ($application_block == self::NETSCAPE_EXTENSION_MARKER) { + + $data_block_size = $this->getNextBytes(1); + $data_block_size = unpack('C', $data_block_size)[1]; + $data_block = $this->getNextBytes($data_block_size); + + $extension = "\x0B"; + $extension .= self::NETSCAPE_EXTENSION_MARKER; + $extension .= "\x03"; + $extension .= $data_block; + $extension .= "\x00"; + $gif->setNetscapeExtension($extension); + + } elseif ($application_block == self::XMP_EXTENSION_MARKER) { + + do { + // skip xmp data for now + $byte = $this->getNextBytes(1); + } while ($byte != "\x00"); + + } else { + + $data_block_size = $this->getNextBytes(1); + $data_block_size = unpack('C', $data_block_size)[1]; + $data_block = $this->getNextBytes($data_block_size); + + } + + // subblock + $this->getNextBytes(1); + break; case self::PLAINTEXT_EXTENSION_MARKER: $blocksize = $this->getNextBytes(1); - $blocksize = unpack('C', $blocksize); + $blocksize = unpack('C', $blocksize)[1]; $gif->setPlaintextExtension($this->getNextBytes($blocksize)); $this->getNextBytes(1); // null byte break; @@ -152,9 +190,10 @@ private function decodeExtension($gif) /** * Decode Image Descriptor from image stream * + * @param Decoded $gif * @return void */ - private function decodeImageDescriptor($gif) + private function decodeImageDescriptor(Decoded $gif) { $descriptor = $this->getNextBytes(9); @@ -201,9 +240,10 @@ private function decodeImageDescriptor($gif) /** * Decode Image data from image stream * + * @param Decoded $gif * @return void */ - private function decodeImageData($gif) + private function decodeImageData(Decoded $gif) { $data = ''; diff --git a/src/Intervention/Image/Gd/Gif/Encoder.php b/src/Intervention/Image/Gd/Gif/Encoder.php index 52351992c..d041595c9 100644 --- a/src/Intervention/Image/Gd/Gif/Encoder.php +++ b/src/Intervention/Image/Gd/Gif/Encoder.php @@ -25,6 +25,13 @@ class Encoder */ public $globalColorTable; + /** + * Index of background color + * + * @var int + */ + public $backgroundColorIndex = 0; + /** * Number of sequence loops * @@ -44,7 +51,7 @@ class Encoder * * @param int $width * @param int $height - * @return \Intervention\Image\Tools\Gif\Encoder + * @return \Intervention\Image\Gd\Gif\Encoder */ public function setCanvas($width, $height) { @@ -58,7 +65,7 @@ public function setCanvas($width, $height) * Set number of loops * * @param int $value - * @return \Intervention\Image\Tools\Gif\Encoder + * @return \Intervention\Image\Gd\Gif\Encoder */ public function setLoops($value) { @@ -71,7 +78,7 @@ public function setLoops($value) * Set global color table * * @param int $value - * @return \Intervention\Image\Tools\Gif\Encoder + * @return \Intervention\Image\Gd\Gif\Encoder */ public function setGlobalColorTable($value) { @@ -80,6 +87,64 @@ public function setGlobalColorTable($value) return $this; } + public function getGlobalColorTable() + { + return $this->globalColorTable; + } + + public function setFrames(Array $frames) + { + $this->frames = $frames; + + return $this; + } + + public function setBackgroundColorIndex($index) + { + $this->backgroundColorIndex = $index; + + return $this; + } + + /** + * Setup encoder from Decoded object + * + * @param Decoded $decoded + * @param int|null $frameIndex + * @return \Intervention\Image\Gd\Gif\Encoder + */ + public function setFromDecoded(Decoded $decoded, $frameIndex = null) + { + if (is_null($frameIndex)) { + // setup from all decoded data + $width = $decoded->getCanvasWidth(); + $height = $decoded->getCanvasHeight(); + $colorTable = $decoded->getGlobalColorTable(); + $loops = $decoded->getLoops(); + $frames = $decoded->getFrames(); + } else { + // setup only one specific frame + $frame = $decoded->getFrame($frameIndex); + $frame->setOffset(0, 0); + $width = $frame->decodeWidth(); + $height = $frame->decodeHeight(); + $colorTable = $frame->hasLocalColorTable() ? $frame->getLocalColorTable() : $decoded->getGlobalColorTable(); + $loops = 0; + $frames = array($frame); + } + + // setup + $this->setCanvas($width, $height); + $this->setGlobalColorTable($colorTable); + $this->setBackgroundColorIndex($decoded->getBackgroundColorIndex()); + $this->setLoops($loops); + $this->setFrames($frames); + + + + return $this; + } + public function addFrame(Frame $frame) { $this->frames[] = $frame; @@ -116,8 +181,12 @@ public function encode() // create gif $encoded = $this->buildLogicalScreenDescriptor(); + if ($this->hasGlobalColorTable()) { + $encoded .= $this->getGlobalColorTable(); + } + // netscape extension - if ($this->isAnimated()) { + if ($this->isAnimated() && $this->doesLoop()) { $encoded .= $this->buildNetscapeExtension(); } @@ -147,10 +216,27 @@ private function buildLogicalScreenDescriptor() $descriptor .= pack('v*', $this->canvasHeight); // packed field - $descriptor .= "\x00"; + $colorResolution = 111; + $sortFlag = 0; + + if ($this->hasGlobalColorTable()) { + + $globalColorTableFlag = 1; + $globalColorTableSize = log(strlen($this->getGlobalColorTable()) / 3, 2) - 1; + $globalColorTableSize = decbin($globalColorTableSize); + $globalColorTableSize = str_pad($globalColorTableSize, 3, 0, STR_PAD_LEFT); + + } else { + $globalColorTableFlag = 0; + $globalColorTableSize = 0; + } + + $packed = $globalColorTableFlag.$colorResolution.$sortFlag.$globalColorTableSize; + + $descriptor .= pack('C', bindec($packed)); // background color index - $descriptor .= "\x00"; + $descriptor .= pack('C', $this->backgroundColorIndex); // pixel aspect ratio $descriptor .= "\x00"; @@ -195,13 +281,23 @@ private function buildGraphicsControlExtension(Frame $frame) $extension .= "\x04"; // packed field - $extension .= "\x00"; + $disposalMethod = decbin($frame->getDisposalMethod()); + $disposalMethod = str_pad($disposalMethod, 3, 0, STR_PAD_LEFT); + $userInputFlat = 0; + $transparentColorFlag = $frame->hasTransparentColor() ? 1 : 0; + $packed = $disposalMethod.$userInputFlat.$transparentColorFlag; + $packed = str_pad($packed, 3, 0, STR_PAD_LEFT); + $extension .= pack('C', bindec($packed)); // delay $extension .= pack('v*', $frame->getDelay()); // transparent color index - $extension .= "\x00"; + if ($frame->hasTransparentColor()) { + $extension .= $frame->getTransparentColorIndex(); + } else { + $extension .= "\x00"; + } // block terminator $extension .= "\x00"; @@ -227,7 +323,6 @@ private function buildImageDescriptor(Frame $frame) ); $interlacedFlag = $frame->isInterlaced() ? 1 : 0; - $sortFlag = 0; $reserved1 = 0; $reserved2 = 0; @@ -235,11 +330,15 @@ private function buildImageDescriptor(Frame $frame) $colorTableFlag = 1; $colorTableSize = log(strlen($frame->getLocalColorTable()) / 3, 2) - 1; $colorTableSize = decbin($colorTableSize); + $sortFlag = 0; } else { $colorTableFlag = 0; $colorTableSize = 0; + $sortFlag = 0; } + $colorTableSize = str_pad($colorTableSize, 3, 0, STR_PAD_LEFT); + // packed field $packed = $colorTableFlag.$interlacedFlag.$sortFlag.$reserved1.$reserved2.$colorTableSize; $descriptor .= pack('C', bindec($packed)); @@ -272,4 +371,9 @@ public function isAnimated() return count($this->frames) > 1; } + public function doesLoop() + { + return is_integer($this->loops); + } + } diff --git a/src/Intervention/Image/Gd/Gif/Frame.php b/src/Intervention/Image/Gd/Gif/Frame.php index a016ab74a..7428783eb 100644 --- a/src/Intervention/Image/Gd/Gif/Frame.php +++ b/src/Intervention/Image/Gd/Gif/Frame.php @@ -84,7 +84,9 @@ public function setLocalColorTable($value) public function getDelay() { - return $this->delay; + return $this->propertyIsSet('delay') + ? $this->delay + : $this->decodeDelay(); } public function setDelay($delay) @@ -129,7 +131,9 @@ public function hasTransparentColor() public function getTransparentColorIndex() { - return $this->transparentColorIndex; + return $this->propertyIsSet('transparentColorIndex') + ? $this->transparentColorIndex + : $this->decodeTransparentColorIndex(); } public function setTransparentColorIndex($value) @@ -155,7 +159,9 @@ public function decodeTransparentColorIndex() public function getDisposalMethod() { - return $this->disposalMethod; + return $this->propertyIsSet('disposalMethod') + ? $this->disposalMethod + : $this->decodeDisposalMethod(); } public function setDisposalMethod($value) @@ -240,6 +246,28 @@ public function getOffset() return $offset; } + public function setOffset($left, $top) + { + $offset = new \StdClass; + $offset->left = $left; + $offset->top = $top; + $this->offset = $offset; + + return $this; + } + + public function setInterlaced(boolean $flag) + { + $this->interlaced = $flag; + + return $this; + } + + public function getInterlaced() + { + return $this->propertyIsSet('interlaced') ? $this->interlaced : $this->decodeInterlaced(); + } + /** * Determines if current frame is saved as interlaced * @@ -247,17 +275,6 @@ public function getOffset() */ public function isInterlaced() { - return $this->interlaced; - } - - public function toResource() - { - $encoder = new Encoder; - $encoder->setCanvas($this->decodeWidth(), $this->decodeHeight()); - $encoder->addFrame($this); - - $data = $encoder->encode(); - - return imagecreatefromstring($data); + return (boolean) $this->getInterlaced(); } } diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php index 89a32f42e..d6c943e16 100644 --- a/tests/GifFrameTest.php +++ b/tests/GifFrameTest.php @@ -40,7 +40,7 @@ public function testSetGetImageData() public function testSetGetDelay() { $frame = new Frame; - $this->assertNull($frame->getDelay()); + $this->assertFalse($frame->getDelay()); $frame = new Frame; $result = $frame->setDelay(20); @@ -68,7 +68,7 @@ public function testHasTransparentColor() public function testSetGetTransparentColorIndex() { $frame = new Frame; - $this->assertNull($frame->getTransparentColorIndex()); + $this->assertFalse($frame->getTransparentColorIndex()); $frame = new Frame; $result = $frame->setTransparentColorIndex('foo'); @@ -89,7 +89,7 @@ public function testDecodeTransparentColorIndex() public function testSetGetDisposalMethod() { $frame = new Frame; - $this->assertNull($frame->getDisposalMethod()); + $this->assertEquals(0, $frame->getDisposalMethod()); $frame = new Frame; $result = $frame->setDisposalMethod('foo'); From d47ec923a8e77cb2c7ce4a0638c6fcccca435ed5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 11 Jan 2015 19:03:13 +0100 Subject: [PATCH 39/88] animation integration --- src/Intervention/Image/ContainerInterface.php | 1 + src/Intervention/Image/Gd/Container.php | 5 ++ src/Intervention/Image/Gd/Decoder.php | 37 ++++++++++--- src/Intervention/Image/Gd/Gif/Decoded.php | 52 ++++++++++++++----- src/Intervention/Image/Image.php | 10 ++++ 5 files changed, 83 insertions(+), 22 deletions(-) diff --git a/src/Intervention/Image/ContainerInterface.php b/src/Intervention/Image/ContainerInterface.php index 2e3997f0f..39593eb30 100644 --- a/src/Intervention/Image/ContainerInterface.php +++ b/src/Intervention/Image/ContainerInterface.php @@ -6,4 +6,5 @@ interface ContainerInterface { public function setCore($core); public function getCore(); + public function countFrames(); } \ No newline at end of file diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index b5abdd244..237198934 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -19,4 +19,9 @@ public function setCore($core) new Frame($core) )); } + + public function countFrames() + { + return count($this->getFrames()); + } } diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 1331bd4a8..158a831c9 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -68,6 +68,14 @@ public function initFromGdResource($resource) return new Image($driver, $driver->newContainer($resource)); } + + public function initFromContainer(Container $container) + { + $driver = new Driver; + + return new Image($driver, $container); + } + /** * Initiates new image from Imagick object * @@ -89,16 +97,29 @@ public function initFromImagick(\Imagick $object) */ public function initFromBinary($binary) { - $resource = @imagecreatefromstring($binary); + try { + + // try to custom decode gif + $gifDecoder = new Gif\Decoder; + $decoded = $gifDecoder->initFromData($binary)->decode(); - if ($resource === false) { - throw new \Intervention\Image\Exception\NotReadableException( - "Unable to init from given binary data." - ); - } + $image = $this->initFromContainer($decoded->createContainer()); + $image->mime = 'image/gif'; + + } catch (\Exception $e) { + + $resource = @imagecreatefromstring($binary); - $image = $this->initFromGdResource($resource); - $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); + if ($resource === false) { + throw new \Intervention\Image\Exception\NotReadableException( + "Unable to init from given binary data." + ); + } + + $image = $this->initFromGdResource($resource); + $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); + + } return $image; } diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index fbefeacb1..4f0467769 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -334,41 +334,65 @@ private function newFrameWithProperty($property, $value) $this->frames[] = $frame->setProperty($property, $value); } - public function toContainer() + public function createContainer() { $container = new Container; $container->setLoops($this->getLoops()); - foreach ($this->frames as $frame) { + // create empty canvas + $canvas = imagecreatetruecolor( + $this->getCanvasWidth(), + $this->getCanvasHeight() + ); - // create empty canvas - $resource = imagecreatetruecolor( - $this->getCanvasWidth(), - $this->getCanvasHeight() - ); + imagealphablending($canvas, false); + imagesavealpha($canvas, true); + + foreach ($this->frames as $key => $frame) { - imagealphablending($resource, false); - imagesavealpha($resource, true); + // create resource from frame + $encoder = new Encoder; + $encoder->setFromDecoded($this, $key); + $frame_resource = imagecreatefromstring($encoder->encode()); // insert frame image data into canvas imagecopy( - $resource, - $resource_frame, - 0, - 0, + $canvas, + $frame_resource, + $frame->getOffset()->left, + $frame->getOffset()->top, 0, 0, $frame->getSize()->width, $frame->getSize()->height ); + // destory frame resource + imagedestroy($frame_resource); + // add frame to container $container->addFrame(new ContainerFrame( - $resource, + $canvas, $frame->getDelay() )); + + // prepare next canvas + $canvas = $this->cloneResource($canvas); } return $container; } + + public function cloneResource($resource) + { + $width = imagesx($resource); + $height = imagesy($resource); + $clone = imagecreatetruecolor($width, $height); + imagealphablending($clone, false); + imagesavealpha($clone, true); + + imagecopy($clone, $resource, 0, 0, 0, 0, $width, $height); + + return $clone; + } } diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 1572f85c8..186f44805 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -277,6 +277,16 @@ private function backupExists($name) return array_key_exists($name, $this->backups); } + /** + * Determines if current image has multiple animation frames + * + * @return boolean + */ + public function isAnimated() + { + return ($this->container->countFrames() > 1); + } + /** * Checks if current image is already encoded * From 71189ba686a64e138789f0badb2e491bb9e6d733 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 12 Jan 2015 17:17:42 +0100 Subject: [PATCH 40/88] gif encoder implementation --- src/Intervention/Image/Animation.php | 10 ++++ src/Intervention/Image/Gd/Container.php | 17 ++++++ src/Intervention/Image/Gd/Decoder.php | 29 +++++++---- src/Intervention/Image/Gd/Driver.php | 9 +++- src/Intervention/Image/Gd/Encoder.php | 34 +++++++++--- src/Intervention/Image/Gd/Gif/Decoded.php | 16 +----- src/Intervention/Image/Gd/Helper.php | 55 ++++++++++++++++++++ src/Intervention/Image/Imagick/Container.php | 5 ++ 8 files changed, 145 insertions(+), 30 deletions(-) create mode 100644 src/Intervention/Image/Gd/Helper.php diff --git a/src/Intervention/Image/Animation.php b/src/Intervention/Image/Animation.php index 5b098514f..18c54dfed 100644 --- a/src/Intervention/Image/Animation.php +++ b/src/Intervention/Image/Animation.php @@ -52,6 +52,16 @@ public function setLoops($value) $this->loops = intval($value); } + /** + * Return number of loops + * + * @return integer + */ + public function getLoops() + { + return $this->loops; + } + /** * Add one frame to the Animation * diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index 237198934..11bfa6529 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -8,6 +8,13 @@ class Container extends Animation implements ContainerInterface { + public $driver; + + public function __construct($driver = null) + { + $this->driver = $driver; + } + public function getCore() { return $this->getFrames()[0]->getCore(); @@ -24,4 +31,14 @@ public function countFrames() { return count($this->getFrames()); } + + public function add($source, $delay = 0) + { + $core = $this->driver->init($source)->getCore(); + + $this->addFrame(new Frame( + $core, + $delay + )); + } } diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 158a831c9..a193dde45 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -24,31 +24,40 @@ public function initFromPath($path) ); } - // define core - switch ($info[2]) { - case IMAGETYPE_PNG: + // try to decode animated gif + if ($info['mime'] == 'image/gif') { + + return $this->initFromBinary(file_get_contents($path)); + + } else { + + // define core + switch ($info[2]) { + case IMAGETYPE_PNG: $core = imagecreatefrompng($path); $this->gdResourceToTruecolor($core); break; - case IMAGETYPE_JPEG: + case IMAGETYPE_JPEG: $core = imagecreatefromjpeg($path); $this->gdResourceToTruecolor($core); break; - case IMAGETYPE_GIF: + case IMAGETYPE_GIF: $core = imagecreatefromgif($path); $this->gdResourceToTruecolor($core); break; - default: + default: throw new \Intervention\Image\Exception\NotReadableException( "Unable to read image type. GD driver is only able to decode JPG, PNG or GIF files." - ); + ); + } + + // build image + $image = $this->initFromGdResource($core); } - // build image - $image = $this->initFromGdResource($core); $image->mime = $info['mime']; $image->setFileInfoFromPath($path); @@ -103,6 +112,7 @@ public function initFromBinary($binary) $gifDecoder = new Gif\Decoder; $decoded = $gifDecoder->initFromData($binary)->decode(); + // create image $image = $this->initFromContainer($decoded->createContainer()); $image->mime = 'image/gif'; @@ -116,6 +126,7 @@ public function initFromBinary($binary) ); } + // create image $image = $this->initFromGdResource($resource); $image->mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $binary); diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index b8a16034e..72a0bf3aa 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -59,7 +59,14 @@ public function newImage($width, $height, $background = null) */ public function newAnimation($width, $height, $callback = null, $loops = null) { - $image = new \Intervention\Image\Image(new self, $core); + $container = new Container($this); + $container->setLoops($loops); + + if (is_callable($callback)) { + $callback($container); + } + + $image = new \Intervention\Image\Image(new self, $container); return $image; } diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index 97a2c271e..5fa986219 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -46,13 +46,35 @@ protected function processPng() */ protected function processGif() { - ob_start(); - imagegif($this->image->getCore()); - $this->image->mime = image_type_to_mime_type(IMAGETYPE_GIF); - $buffer = ob_get_contents(); - ob_end_clean(); + $image = $this->image; - return $buffer; + $encoder = new Gif\Encoder; + $encoder->setCanvas($image->getWidth(), $image->getHeight()); + $encoder->setLoops($image->getContainer()->getLoops()); + + // set frames + foreach ($image as $frame) { + + // extract each frame + ob_start(); + imagegif($frame->getCore()); + $frame_data = ob_get_contents(); + ob_end_clean(); + + // decode frame + $decoder = new Gif\Decoder; + $decoder->initFromData($frame_data); + $decoded = $decoder->decode(); + + // add each frame + $encoder->addFrame( + $decoded->getFrame() + ->setLocalColorTable($decoded->getGlobalColorTable()) + ->setDelay($frame->delay) + ); + } + + return $encoder->encode(); } /** diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index 4f0467769..18d099159 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -4,6 +4,7 @@ use Intervention\Image\Frame as ContainerFrame; use Intervention\Image\Gd\Container; +use Intervention\Image\Gd\Helper; class Decoded { @@ -377,22 +378,9 @@ public function createContainer() )); // prepare next canvas - $canvas = $this->cloneResource($canvas); + $canvas = Helper::cloneResource($canvas); } return $container; } - - public function cloneResource($resource) - { - $width = imagesx($resource); - $height = imagesy($resource); - $clone = imagecreatetruecolor($width, $height); - imagealphablending($clone, false); - imagesavealpha($clone, true); - - imagecopy($clone, $resource, 0, 0, 0, 0, $width, $height); - - return $clone; - } } diff --git a/src/Intervention/Image/Gd/Helper.php b/src/Intervention/Image/Gd/Helper.php new file mode 100644 index 000000000..b91ec3ffe --- /dev/null +++ b/src/Intervention/Image/Gd/Helper.php @@ -0,0 +1,55 @@ +imagick = $core; } + + public function countFrames() + { + return $this->imagick->getNumberImages(); + } } From 0d9bbf8410394a2ee06bb66fe2de30db22829f01 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 15 Jan 2015 18:13:13 +0100 Subject: [PATCH 41/88] added tests and docblocks --- src/Intervention/Image/Gd/Container.php | 32 +++--- src/Intervention/Image/Gd/Gif/Decoded.php | 116 ++++++++++++++++++++++ src/Intervention/Image/Gd/Gif/Encoder.php | 81 +++++++++++++-- src/Intervention/Image/Gd/Gif/Frame.php | 98 +++++++++++++++++- tests/GdContainerTest.php | 25 +++++ tests/GdHelperTest.php | 26 +++++ tests/GifDecodedTest.php | 23 +++++ tests/GifEncodeDecodeTest.php | 11 ++ tests/GifEncoderTest.php | 75 ++++++++++++++ tests/GifFrameTest.php | 5 +- 10 files changed, 463 insertions(+), 29 deletions(-) create mode 100644 tests/GdContainerTest.php create mode 100644 tests/GdHelperTest.php create mode 100644 tests/GifEncodeDecodeTest.php diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index 11bfa6529..992285669 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -8,18 +8,21 @@ class Container extends Animation implements ContainerInterface { - public $driver; - - public function __construct($driver = null) - { - $this->driver = $driver; - } - + /** + * Return first image resource + * + * @return resource + */ public function getCore() { return $this->getFrames()[0]->getCore(); } + /** + * Setup image stack with new resource + * + * @param resource $core + */ public function setCore($core) { $this->setFrames(array( @@ -27,18 +30,13 @@ public function setCore($core) )); } + /** + * Return number of frames in container + * + * @return int + */ public function countFrames() { return count($this->getFrames()); } - - public function add($source, $delay = 0) - { - $core = $this->driver->init($source)->getCore(); - - $this->addFrame(new Frame( - $core, - $delay - )); - } } diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index 18d099159..16489ff18 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -102,6 +102,11 @@ public function getlogicalScreenDescriptor() return $this->logicalScreenDescriptor; } + /** + * Sets global color table + * + * @param string $value + */ public function setGlobalColorTable($value) { $this->globalColorTable = $value; @@ -109,11 +114,21 @@ public function setGlobalColorTable($value) return $this; } + /** + * Gets global color table + * + * @return string + */ public function getGlobalColorTable() { return $this->globalColorTable; } + /** + * Sets Netscape extension + * + * @param string $value + */ public function setNetscapeExtension($value) { $this->netscapeExtension = $value; @@ -121,11 +136,21 @@ public function setNetscapeExtension($value) return $this; } + /** + * Returns Netscape extension + * + * @return string + */ public function getNetscapeExtension() { return $this->netscapeExtension; } + /** + * Sets plain text extension + * + * @param string $value + */ public function setPlaintextExtension($value) { $this->plaintextExtension = $value; @@ -133,11 +158,21 @@ public function setPlaintextExtension($value) return $this; } + /** + * Returns plain text extension + * + * @return string + */ public function getPlaintextExtension() { return $this->plaintextExtension; } + /** + * Sets comment extension + * + * @param string $value + */ public function setCommentExtension($value) { $this->commentExtension = $value; @@ -145,16 +180,32 @@ public function setCommentExtension($value) return $this; } + /** + * Returns comment extension + * + * @return string + */ public function getCommentExtension() { return $this->commentExtension; } + /** + * Returns frames + * + * @return array + */ public function getFrames() { return $this->frames; } + /** + * Returns particular frame + * + * @param integer $index + * @return \Intervention\Image\Gd\Gif\Frame + */ public function getFrame($index = 0) { if (array_key_exists($index, $this->frames)) { @@ -166,26 +217,52 @@ public function getFrame($index = 0) ); } + /** + * Returns number of current frames + * + * @return integer + */ public function countFrames() { return count($this->frames); } + /** + * Adds graphic control extension to first frame without this extension + * + * @param string $value + */ public function addGraphicsControlExtension($value) { $this->addToFirstFrameWithoutProperty($value, 'graphicsControlExtension'); } + /** + * Adds local color table to first frame without local color table + * + * @param string $value + */ public function addLocalColorTable($value) { $this->addToFirstFrameWithoutProperty($value, 'localColorTable'); } + /** + * Adds interlaced flag to first frame without flag + * + * @param string $value + */ public function addInterlaced($value) { $this->addToFirstFrameWithoutProperty($value, 'interlaced'); } + /** + * Adds offset to first frame without offset + * + * @param integer $left + * @param integer $top + */ public function addOffset($left, $top) { $offset = new \StdClass; @@ -195,6 +272,12 @@ public function addOffset($left, $top) $this->addToFirstFrameWithoutProperty($offset, 'offset'); } + /** + * Adds size to first frame without size + * + * @param integer $width + * @param integer $height + */ public function addSize($width, $height) { $size = new \StdClass; @@ -204,11 +287,21 @@ public function addSize($width, $height) $this->addToFirstFrameWithoutProperty($size, 'size'); } + /** + * Adds image descriptor to first frame without this data + * + * @param string $value + */ public function addImageDescriptors($value) { $this->addToFirstFrameWithoutProperty($value, 'imageDescriptor'); } + /** + * Adds image data to first frame without this data + * + * @param string $value + */ public function addImageData($value) { $this->addToFirstFrameWithoutProperty($value, 'imageData'); @@ -298,6 +391,11 @@ public function countGlobalColors() return isset($bit) ? pow(2, $bit + 1) : 0; } + /** + * Returns background color index + * + * @return integer + */ public function getBackgroundColorIndex() { if ($this->logicalScreenDescriptor) { @@ -310,6 +408,12 @@ public function getBackgroundColorIndex() return 0; } + /** + * Adds value to first frame without this particular value + * + * @param mixed $value + * @param mixed $property + */ private function addToFirstFrameWithoutProperty($value, $property) { $added = false; @@ -329,12 +433,24 @@ private function addToFirstFrameWithoutProperty($value, $property) return $added; } + /** + * Adds new frame with given property + * + * @param mixed $property + * @param mixed $value + * @return void + */ private function newFrameWithProperty($property, $value) { $frame = new Frame; $this->frames[] = $frame->setProperty($property, $value); } + /** + * Returns Container object from decoded data + * + * @return \Intervention\Image\Gd\Container + */ public function createContainer() { $container = new Container; diff --git a/src/Intervention/Image/Gd/Gif/Encoder.php b/src/Intervention/Image/Gd/Gif/Encoder.php index d041595c9..e56ebe92b 100644 --- a/src/Intervention/Image/Gd/Gif/Encoder.php +++ b/src/Intervention/Image/Gd/Gif/Encoder.php @@ -87,11 +87,21 @@ public function setGlobalColorTable($value) return $this; } + /** + * Return global color table of encoder + * + * @return string + */ public function getGlobalColorTable() { return $this->globalColorTable; } + /** + * Setup frame stack of encoder + * + * @param Array $frames + */ public function setFrames(Array $frames) { $this->frames = $frames; @@ -99,6 +109,11 @@ public function setFrames(Array $frames) return $this; } + /** + * Set background color index for encoder + * + * @param string $index + */ public function setBackgroundColorIndex($index) { $this->backgroundColorIndex = $index; @@ -140,23 +155,26 @@ public function setFromDecoded(Decoded $decoded, $frameIndex = null) $this->setLoops($loops); $this->setFrames($frames); - - return $this; } + /** + * Add one frame to stack + * + * @param Frame $frame + */ public function addFrame(Frame $frame) { $this->frames[] = $frame; } - public function createFrame() - { - $frame = new Frame; - - $this->frames[] = $frame; - } - + /** + * Create and add new frame from GD resource + * + * @param resource $resource + * @param integer $delay + * @return void + */ public function createFrameFromGdResource($resource, $delay = null) { // get imagedata from resource @@ -244,6 +262,11 @@ private function buildLogicalScreenDescriptor() return $descriptor; } + /** + * Build Netscape extension + * + * @return string + */ private function buildNetscapeExtension() { $extension = "\x21"; @@ -258,6 +281,12 @@ private function buildNetscapeExtension() return $extension; } + /** + * Build encoded Frame + * + * @param Frame $frame + * @return string + */ private function buildFrame(Frame $frame) { // graphics control extensions @@ -272,6 +301,12 @@ private function buildFrame(Frame $frame) return $encoded; } + /** + * Build encoded graphics control extension for frame + * + * @param Frame $frame + * @return string + */ private function buildGraphicsControlExtension(Frame $frame) { // start @@ -305,6 +340,12 @@ private function buildGraphicsControlExtension(Frame $frame) return $extension; } + /** + * Build encoded image descriptor for frame + * + * @param Frame $frame + * @return string + */ private function buildImageDescriptor(Frame $frame) { // seperator @@ -351,11 +392,22 @@ private function buildImageDescriptor(Frame $frame) return $descriptor; } + /** + * Determines if encoder has global color table + * + * @return boolean + */ private function hasGlobalColorTable() { return isset($this->globalColorTable); } + /** + * Encode GD resource to GIF string + * + * @param resource $resource + * @return string + */ private function encodeGdResource($resource) { ob_start(); @@ -366,14 +418,23 @@ private function encodeGdResource($resource) return $buffer; } + /** + * Determines if current encoder is set up animated + * + * @return boolean + */ public function isAnimated() { return count($this->frames) > 1; } + /** + * Determines if current encoder is set up to loop animation + * + * @return boolean + */ public function doesLoop() { return is_integer($this->loops); } - } diff --git a/src/Intervention/Image/Gd/Gif/Frame.php b/src/Intervention/Image/Gd/Gif/Frame.php index 7428783eb..3c06824cc 100644 --- a/src/Intervention/Image/Gd/Gif/Frame.php +++ b/src/Intervention/Image/Gd/Gif/Frame.php @@ -43,6 +43,11 @@ public function setProperty($name, $value) return $this; } + /** + * Sets image data of fram + * + * @param string $value + */ public function setImageData($value) { $this->imageData = $value; @@ -50,6 +55,11 @@ public function setImageData($value) return $this; } + /** + * Returns image data + * + * @return string + */ public function getImageData() { return $this->imageData; @@ -75,6 +85,11 @@ public function getLocalColorTable() return $this->localColorTable; } + /** + * Sets local color table of frame + * + * @param string $value + */ public function setLocalColorTable($value) { $this->localColorTable = $value; @@ -82,6 +97,11 @@ public function setLocalColorTable($value) return $this; } + /** + * Returns delay of frame + * + * @return integer + */ public function getDelay() { return $this->propertyIsSet('delay') @@ -89,6 +109,11 @@ public function getDelay() : $this->decodeDelay(); } + /** + * Sets delay of frame + * + * @param integer $delay + */ public function setDelay($delay) { $this->delay = $delay; @@ -129,6 +154,11 @@ public function hasTransparentColor() return false; } + /** + * Returns transparent color index of frame + * + * @return integer + */ public function getTransparentColorIndex() { return $this->propertyIsSet('transparentColorIndex') @@ -136,6 +166,11 @@ public function getTransparentColorIndex() : $this->decodeTransparentColorIndex(); } + /** + * Sets transparent color index of frame + * + * @param integer $value + */ public function setTransparentColorIndex($value) { $this->transparentColorIndex = $value; @@ -157,6 +192,11 @@ public function decodeTransparentColorIndex() return false; } + /** + * Returns disposal method of frame + * + * @return integer + */ public function getDisposalMethod() { return $this->propertyIsSet('disposalMethod') @@ -164,6 +204,11 @@ public function getDisposalMethod() : $this->decodeDisposalMethod(); } + /** + * Defines disposal method of frame + * + * @param integer $value + */ public function setDisposalMethod($value) { $this->disposalMethod = $value; @@ -171,6 +216,11 @@ public function setDisposalMethod($value) return $this; } + /** + * Decodes disposal method of frame + * + * @return integer + */ public function decodeDisposalMethod() { if ($this->graphicsControlExtension) { @@ -184,6 +234,11 @@ public function decodeDisposalMethod() return 0; } + /** + * Decodes frame width + * + * @return integer + */ public function decodeWidth() { if ($this->imageDescriptor) { @@ -195,6 +250,11 @@ public function decodeWidth() return false; } + /** + * Decodes frame height + * + * @return integer + */ public function decodeHeight() { if ($this->imageDescriptor) { @@ -206,6 +266,11 @@ public function decodeHeight() return false; } + /** + * Decodes width and height of frame to size object + * + * @return StdClass + */ public function getSize() { $size = new \StdClass; @@ -215,6 +280,11 @@ public function getSize() return $size; } + /** + * Decodes left offset of frame + * + * @return integer + */ public function decodeOffsetLeft() { if ($this->imageDescriptor) { @@ -226,6 +296,11 @@ public function decodeOffsetLeft() return false; } + /** + * Decodes top offset of frame + * + * @return integer + */ public function decodeOffsetTop() { if ($this->imageDescriptor) { @@ -237,6 +312,11 @@ public function decodeOffsetTop() return false; } + /** + * Decodes offsets into object + * + * @return StdClass + */ public function getOffset() { $offset = new \StdClass; @@ -246,6 +326,12 @@ public function getOffset() return $offset; } + /** + * Sets frame offset + * + * @param integer $left + * @param integer $top + */ public function setOffset($left, $top) { $offset = new \StdClass; @@ -256,13 +342,23 @@ public function setOffset($left, $top) return $this; } - public function setInterlaced(boolean $flag) + /** + * Mark frame as interlaced + * + * @param boolean $flag + */ + public function setInterlaced($flag) { $this->interlaced = $flag; return $this; } + /** + * Returns interlaced mode + * + * @return boolean + */ public function getInterlaced() { return $this->propertyIsSet('interlaced') ? $this->interlaced : $this->decodeInterlaced(); diff --git a/tests/GdContainerTest.php b/tests/GdContainerTest.php new file mode 100644 index 000000000..f242c9a86 --- /dev/null +++ b/tests/GdContainerTest.php @@ -0,0 +1,25 @@ +setCore('foo'); + $this->assertEquals('foo', $container->getCore()); + } + + public function testCountFrames() + { + $container = new Container; + $container->addFrames(array('foo', 'bar', 'baz')); + $this->assertEquals(3, $container->countFrames()); + } +} diff --git a/tests/GdHelperTest.php b/tests/GdHelperTest.php new file mode 100644 index 000000000..c14f7bcd1 --- /dev/null +++ b/tests/GdHelperTest.php @@ -0,0 +1,26 @@ +assertFalse(imageistruecolor($resource)); + Helper::gdResourceToTruecolor($resource); + $this->assertTrue(imageistruecolor($resource)); + } + + public function testCloneResource() + { + $resource = imagecreate(10, 10); + $clone = Helper::cloneResource($resource); + $this->assertNotEquals($resource, $clone); + } +} diff --git a/tests/GifDecodedTest.php b/tests/GifDecodedTest.php index 4dc71558d..f58e221f0 100644 --- a/tests/GifDecodedTest.php +++ b/tests/GifDecodedTest.php @@ -109,4 +109,27 @@ public function testCountGlobalColors() $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x91\x00\x00"); $this->assertEquals(4, $decoded->countGlobalColors()); } + + public function testGetBackgroundColorIndex() + { + $decoded = new Decoded; + $this->assertEquals(0, $decoded->getBackgroundColorIndex()); + + $decoded = new Decoded; + $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x05\x00"); + $this->assertEquals(5, $decoded->getBackgroundColorIndex()); + } + + public function testGetFrame() + { + $decoded = new Decoded; + $decoded->addImageData('foo'); + $decoded->addImageData('bar'); + $frame = $decoded->getFrame(); + $this->assertInstanceOf('\Intervention\Image\Gd\Gif\Frame', $frame); + $this->assertEquals('foo', $frame->imageData); + $frame = $decoded->getFrame(1); + $this->assertInstanceOf('\Intervention\Image\Gd\Gif\Frame', $frame); + $this->assertEquals('bar', $frame->imageData); + } } diff --git a/tests/GifEncodeDecodeTest.php b/tests/GifEncodeDecodeTest.php new file mode 100644 index 000000000..6d390ffa5 --- /dev/null +++ b/tests/GifEncodeDecodeTest.php @@ -0,0 +1,11 @@ +assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); $this->assertEquals('foo', $encoder->globalColorTable); } + + public function testSetFrames() + { + $encoder = new Encoder; + $frames = array('foo', 'bar', 'baz'); + $encoder->setFrames($frames); + $this->assertEquals($frames, $encoder->frames); + } + + public function testSetBackgroundColorIndex() + { + $encoder = new Encoder; + $result = $encoder->setBackgroundColorIndex('foo'); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); + $this->assertEquals('foo', $encoder->backgroundColorIndex); + } + + public function testSetFromDecoded() + { + $encoder = new Encoder; + $decoded = Mockery::mock('Intervention\Image\Gd\Gif\Decoded'); + $decoded->shouldReceive('getCanvasWidth')->andReturn(300); + $decoded->shouldReceive('getCanvasHeight')->andReturn(200); + $decoded->shouldReceive('getGlobalColorTable')->andReturn('global_color_table'); + $decoded->shouldReceive('getBackgroundColorIndex')->andReturn('background_color_index'); + $decoded->shouldReceive('getLoops')->andReturn(2); + $decoded->shouldReceive('getFrames')->andReturn(array('frame1', 'frame2', 'frame3')); + $encoder->setFromDecoded($decoded); + $this->assertEquals(300, $encoder->canvasWidth); + $this->assertEquals(200, $encoder->canvasHeight); + $this->assertEquals('global_color_table', $encoder->globalColorTable); + $this->assertEquals('background_color_index', $encoder->backgroundColorIndex); + $this->assertEquals(2, $encoder->loops); + $this->assertEquals(3, count($encoder->frames)); + $this->assertTrue($encoder->doesLoop()); + } + + public function testAddFrame() + { + $encoder = new Encoder; + $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); + $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); + $this->assertEquals(2, count($encoder->frames)); + } + + public function testCreateFrameFromGdResource() + { + $encoder = new Encoder; + $resource = imagecreate(10, 10); + $encoder->createFrameFromGdResource($resource, 10); + $this->assertEquals(1, count($encoder->frames)); + } + + public function testIsAnimated() + { + $encoder = new Encoder; + $this->assertFalse($encoder->isAnimated()); + $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); + $this->assertFalse($encoder->isAnimated()); + $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); + $this->assertTrue($encoder->isAnimated()); + } + + public function testDoesLoop() + { + $encoder = new Encoder; + $this->assertFalse($encoder->doesLoop()); + $encoder->setLoops(10); + $this->assertTrue($encoder->doesLoop()); + } } diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php index d6c943e16..417083f18 100644 --- a/tests/GifFrameTest.php +++ b/tests/GifFrameTest.php @@ -171,8 +171,11 @@ public function testGetOffset() $this->assertEquals(24, $offset->left); } - public function testToResource() + public function testSetInterlaced() { $frame = new Frame; + $frame->setInterlaced(true); + $this->assertTrue($frame->interlaced); + $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $frame); } } From 64e74a047c1f2da78f46ebb3bdb3c976225f4c98 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 15 Jan 2015 18:25:40 +0100 Subject: [PATCH 42/88] fixes --- tests/EncoderTest.php | 10 +++++++++- tests/tmp/crc.png | Bin 170 -> 0 bytes 2 files changed, 9 insertions(+), 1 deletion(-) delete mode 100644 tests/tmp/crc.png diff --git a/tests/EncoderTest.php b/tests/EncoderTest.php index 062adfe1a..34ea46aa8 100644 --- a/tests/EncoderTest.php +++ b/tests/EncoderTest.php @@ -38,9 +38,17 @@ public function testProcessGifGd() { $core = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $encoder = new GdEncoder; + $frame = Mockery::mock('\Intervention\Image\Frame'); + $frame->shouldReceive('getCore')->andReturn($core); + $container = Mockery::mock('\Intervention\Image\Gd\Container'); + $container->shouldReceive('getLoops')->once()->andReturn(1); + $container->shouldReceive('getIterator')->andReturn(new ArrayIterator(array($frame))); $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image->shouldReceive('getWidth')->once()->andReturn(800); + $image->shouldReceive('getHeight')->once()->andReturn(600); + $image->shouldReceive('getContainer')->once()->andReturn($container); $image->shouldReceive('setEncoded')->once()->andReturn($image); + $image->shouldReceive('getIterator')->andReturn($container); $img = $encoder->process($image, 'gif', 90); $this->assertInstanceOf('Intervention\Image\Image', $img); $this->assertEquals('image/gif; charset=binary', $this->getMime($encoder->result)); diff --git a/tests/tmp/crc.png b/tests/tmp/crc.png deleted file mode 100644 index 919886063c17607467695fc90c1fd3c3ea2a314a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`g`O^sAr`%BgQJBGIIv9idtd+b zi}&%xH_olw@7lCM!Sm*ZZ1z1a3|~H0v!>iHeBiR+4nVE``>D01~jhz zu~XM4%l-17=?~spX8!x>-1KGbksZnxwrL4{yr5-h Date: Thu, 15 Jan 2015 18:52:51 +0100 Subject: [PATCH 43/88] added tests --- tests/GifDecodedTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/GifDecodedTest.php b/tests/GifDecodedTest.php index f58e221f0..2710ea726 100644 --- a/tests/GifDecodedTest.php +++ b/tests/GifDecodedTest.php @@ -132,4 +132,13 @@ public function testGetFrame() $this->assertInstanceOf('\Intervention\Image\Gd\Gif\Frame', $frame); $this->assertEquals('bar', $frame->imageData); } + + /** + * @expectedException \Intervention\Image\Exception\RuntimeException + */ + public function testGetFrameNotExisting() + { + $decoded = new Decoded; + $frame = $decoded->getFrame(); + } } From 04845b045c860da0307c2a5d1b0f6cc1949deac2 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 15 Jan 2015 20:20:13 +0100 Subject: [PATCH 44/88] added tests --- src/Intervention/Image/Gd/Encoder.php | 2 +- tests/GifEncodeDecodeTest.php | 73 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index 5fa986219..bcc032283 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -73,7 +73,7 @@ protected function processGif() ->setDelay($frame->delay) ); } - + return $encoder->encode(); } diff --git a/tests/GifEncodeDecodeTest.php b/tests/GifEncodeDecodeTest.php index 6d390ffa5..548c0b58c 100644 --- a/tests/GifEncodeDecodeTest.php +++ b/tests/GifEncodeDecodeTest.php @@ -1,6 +1,7 @@ decode(); + + // check before encoding + $this->assertEquals(20, $decoded->getCanvasWidth()); + $this->assertEquals(15, $decoded->getCanvasHeight()); + $this->assertEquals(8, $decoded->countFrames()); + $this->assertEquals(2, $decoded->getLoops()); + $this->assertEquals(32, $decoded->countGlobalColors()); + $this->assertTrue($decoded->hasGlobalColorTable()); + + foreach ($decoded->getFrames() as $frame) { + $this->assertEquals(20, $frame->getDelay()); + } + + // encode Decoded + $encoder = new Encoder; + $encoder->setFromDecoded($decoded); + $encoded = $encoder->encode(); + $decoder->initFromData($encoded); + $decoded = $decoder->decode(); + + // check after encoding + $this->assertEquals(20, $decoded->getCanvasWidth()); + $this->assertEquals(15, $decoded->getCanvasHeight()); + $this->assertEquals(8, $decoded->countFrames()); + $this->assertEquals(2, $decoded->getLoops()); + $this->assertEquals(32, $decoded->countGlobalColors()); + $this->assertTrue($decoded->hasGlobalColorTable()); + + foreach ($decoded->getFrames() as $frame) { + $this->assertEquals(20, $frame->getDelay()); + } + } + + public function testDecodeEncoded() + { + // create two resource + $res1 = imagecreatetruecolor(20, 15); + imagefill($res1, 0, 0, 850736919); + $res2 = imagecreatetruecolor(20, 15); + imagefill($res1, 0, 0, 11876119); + + // create encoded + $encoder = new Encoder; + $encoder->setCanvas(20, 15); + $encoder->setLoops(2); + $encoder->createFrameFromGdResource($res1, 100); + $encoder->createFrameFromGdResource($res2, 100); + $encoded = $encoder->encode(); + + // decode encoded + $decoder = new Decoder; + $decoder->initFromData($encoded); + $decoded = $decoder->decode(); + + // check after decoding + $this->assertEquals(20, $decoded->getCanvasWidth()); + $this->assertEquals(15, $decoded->getCanvasHeight()); + $this->assertEquals(2, $decoded->countFrames()); + $this->assertEquals(2, $decoded->getLoops()); + $this->assertEquals(32, $decoded->countGlobalColors()); + $this->assertFalse($decoded->hasGlobalColorTable()); + + foreach ($decoded->getFrames() as $frame) { + $this->assertEquals(100, $frame->getDelay()); + } + } } From c477c0996f15a7bc1524a3d96d8090835fa81d00 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 18 Jan 2015 14:13:20 +0100 Subject: [PATCH 45/88] gd --- src/Intervention/Image/Gd/Container.php | 40 +++++++++++++++++++++++ src/Intervention/Image/Gd/Decoder.php | 36 ++------------------ src/Intervention/Image/Gd/Driver.php | 4 +-- src/Intervention/Image/Gd/Gif/Decoded.php | 6 ++++ 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index 992285669..6d77e4fde 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -8,6 +8,13 @@ class Container extends Animation implements ContainerInterface { + /** + * Driver + * + * @var \Intervention\Image\Gd\Driver + */ + protected $driver; + /** * Return first image resource * @@ -22,12 +29,15 @@ public function getCore() * Setup image stack with new resource * * @param resource $core + * @return \Intervention\Image\Gd\Container */ public function setCore($core) { $this->setFrames(array( new Frame($core) )); + + return $this; } /** @@ -39,4 +49,34 @@ public function countFrames() { return count($this->getFrames()); } + + /** + * Add image source to Container + * + * @param mixed $source + * @param integer $delay + * @return \Intervention\Image\Gd\Container + */ + public function add($source, $delay = 0) + { + $this->addFrame(new Frame( + $this->driver->init($source)->getCore(), + $delay + )); + + return $this; + } + + /** + * Attach driver current instance + * + * @param Driver $driver + * @return \Intervention\Image\Gd\Container + */ + public function attachDriver(Driver $driver) + { + $this->driver = $driver; + + return $this; + } } diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index a193dde45..3ccf956c9 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -35,17 +35,17 @@ public function initFromPath($path) switch ($info[2]) { case IMAGETYPE_PNG: $core = imagecreatefrompng($path); - $this->gdResourceToTruecolor($core); + Helper::gdResourceToTruecolor($core); break; case IMAGETYPE_JPEG: $core = imagecreatefromjpeg($path); - $this->gdResourceToTruecolor($core); + Helper::gdResourceToTruecolor($core); break; case IMAGETYPE_GIF: $core = imagecreatefromgif($path); - $this->gdResourceToTruecolor($core); + Helper::gdResourceToTruecolor($core); break; default: @@ -145,34 +145,4 @@ public function initFromInterventionContainer(ContainerInterface $container) { return new Image(new Driver, $container); } - - /** - * Transform GD resource into Truecolor version - * - * @param resource $resource - * @return bool - */ - public function gdResourceToTruecolor(&$resource) - { - $width = imagesx($resource); - $height = imagesy($resource); - - // new canvas - $canvas = imagecreatetruecolor($width, $height); - - // fill with transparent color - imagealphablending($canvas, false); - $transparent = imagecolorallocatealpha($canvas, 255, 255, 255, 127); - imagefilledrectangle($canvas, 0, 0, $width, $height, $transparent); - imagecolortransparent($canvas, $transparent); - imagealphablending($canvas, true); - - // copy original - imagecopy($canvas, $resource, 0, 0, 0, 0, $width, $height); - imagedestroy($resource); - - $resource = $canvas; - - return true; - } } diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index 72a0bf3aa..f3c8b4d0e 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -59,11 +59,11 @@ public function newImage($width, $height, $background = null) */ public function newAnimation($width, $height, $callback = null, $loops = null) { - $container = new Container($this); + $container = new Container; $container->setLoops($loops); if (is_callable($callback)) { - $callback($container); + $callback($container->attachDriver($this)); } $image = new \Intervention\Image\Image(new self, $container); diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index 16489ff18..fd17e2240 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -464,6 +464,7 @@ public function createContainer() imagealphablending($canvas, false); imagesavealpha($canvas, true); + Helper::gdResourceToTruecolor($canvas); foreach ($this->frames as $key => $frame) { @@ -471,6 +472,11 @@ public function createContainer() $encoder = new Encoder; $encoder->setFromDecoded($this, $key); $frame_resource = imagecreatefromstring($encoder->encode()); + Helper::gdResourceToTruecolor($frame_resource); + + if ($key == 2) { + // Helper::display($frame_resource); + } // insert frame image data into canvas imagecopy( From 8b187e0927b6bffe20909fc15e5d218bbdecb66e Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 21 Jan 2015 18:11:17 +0100 Subject: [PATCH 46/88] gif --- src/Intervention/Image/Gd/Container.php | 29 +++++----------------- src/Intervention/Image/Gd/Driver.php | 12 ++++++++- src/Intervention/Image/Gd/Gif/Decoded.php | 5 ---- src/Intervention/Image/Image.php | 5 ++-- tests/images/frame1.png | Bin 0 -> 111 bytes tests/images/frame2.png | Bin 0 -> 137 bytes tests/images/frame3.png | Bin 0 -> 182 bytes 7 files changed, 20 insertions(+), 31 deletions(-) create mode 100644 tests/images/frame1.png create mode 100644 tests/images/frame2.png create mode 100644 tests/images/frame3.png diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index 6d77e4fde..e7421c203 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -8,21 +8,15 @@ class Container extends Animation implements ContainerInterface { - /** - * Driver - * - * @var \Intervention\Image\Gd\Driver - */ - protected $driver; - /** * Return first image resource * + * @param interger $index * @return resource */ - public function getCore() + public function getCore($index = 0) { - return $this->getFrames()[0]->getCore(); + return $this->getFrames()[$index]->getCore(); } /** @@ -59,24 +53,13 @@ public function countFrames() */ public function add($source, $delay = 0) { + $driver = new Driver; + $this->addFrame(new Frame( - $this->driver->init($source)->getCore(), + $driver->init($source)->getCore(), $delay )); return $this; } - - /** - * Attach driver current instance - * - * @param Driver $driver - * @return \Intervention\Image\Gd\Container - */ - public function attachDriver(Driver $driver) - { - $this->driver = $driver; - - return $this; - } } diff --git a/src/Intervention/Image/Gd/Driver.php b/src/Intervention/Image/Gd/Driver.php index f3c8b4d0e..e482cd387 100644 --- a/src/Intervention/Image/Gd/Driver.php +++ b/src/Intervention/Image/Gd/Driver.php @@ -59,13 +59,23 @@ public function newImage($width, $height, $background = null) */ public function newAnimation($width, $height, $callback = null, $loops = null) { + // create container $container = new Container; $container->setLoops($loops); + // create empty canvas + $canvas = $this->newImage($width, $height)->getCore(); + + // build frames from callback if (is_callable($callback)) { - $callback($container->attachDriver($this)); + + $callback($container); + + } else { + $container->setCore($canvas); } + // setup image instance $image = new \Intervention\Image\Image(new self, $container); return $image; diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index fd17e2240..db159feb3 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -472,11 +472,6 @@ public function createContainer() $encoder = new Encoder; $encoder->setFromDecoded($this, $key); $frame_resource = imagecreatefromstring($encoder->encode()); - Helper::gdResourceToTruecolor($frame_resource); - - if ($key == 2) { - // Helper::display($frame_resource); - } // insert frame image data into canvas imagecopy( diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 6b5c3bc06..2d5a1ab49 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -191,11 +191,12 @@ public function setDriver(AbstractDriver $driver) /** * Returns current image resource/obj * + * @param integer $index * @return mixed */ - public function getCore() + public function getCore($index = 0) { - return $this->container->getCore(); + return $this->container->getCore($index); } /** diff --git a/tests/images/frame1.png b/tests/images/frame1.png new file mode 100644 index 0000000000000000000000000000000000000000..ed59bbe4f7b2c56710129ee947df597316f8f845 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`W}YsNAr_~T6C_x-Fsim+UD~jN zv17fHVTZ137o!62|BD68nrtyglMaOLl5*H6uEBjI&_RlU;pQkh-+M{idX{qRBhV fIWC*XGcx?;zTI?g-7XuTLl`_={an^LB{Ts5M=n07 literal 0 HcmV?d00001 From 2505944ec989c942e8a28c76fe20e9effb083e57 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 21 Jan 2015 18:24:10 +0100 Subject: [PATCH 47/88] fixing bugs --- src/Intervention/Image/Gd/Decoder.php | 1 - src/Intervention/Image/Gd/Gif/Decoded.php | 11 +++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 3ccf956c9..873160a19 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -107,7 +107,6 @@ public function initFromImagick(\Imagick $object) public function initFromBinary($binary) { try { - // try to custom decode gif $gifDecoder = new Gif\Decoder; $decoded = $gifDecoder->initFromData($binary)->decode(); diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index db159feb3..501359f31 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -5,6 +5,7 @@ use Intervention\Image\Frame as ContainerFrame; use Intervention\Image\Gd\Container; use Intervention\Image\Gd\Helper; +use Intervention\Image\Gd\Driver; class Decoded { @@ -457,14 +458,8 @@ public function createContainer() $container->setLoops($this->getLoops()); // create empty canvas - $canvas = imagecreatetruecolor( - $this->getCanvasWidth(), - $this->getCanvasHeight() - ); - - imagealphablending($canvas, false); - imagesavealpha($canvas, true); - Helper::gdResourceToTruecolor($canvas); + $driver = new Driver; + $canvas = $driver->newImage($this->getCanvasWidth(), $this->getCanvasHeight())->getCore(); foreach ($this->frames as $key => $frame) { From 1c8ffffca859da1187f72322d350bba96315d049 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 21 Jan 2015 19:05:31 +0100 Subject: [PATCH 48/88] BlurCommand animation compatible --- .../Image/Gd/Commands/BlurCommand.php | 20 ++++++++++++++++--- .../Image/Imagick/Commands/BlurCommand.php | 6 +++++- tests/BlurCommandTest.php | 18 ++++++++++++++--- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/BlurCommand.php b/src/Intervention/Image/Gd/Commands/BlurCommand.php index d53f59d7c..6ec4a9935 100644 --- a/src/Intervention/Image/Gd/Commands/BlurCommand.php +++ b/src/Intervention/Image/Gd/Commands/BlurCommand.php @@ -5,7 +5,7 @@ class BlurCommand extends \Intervention\Image\Commands\AbstractCommand { /** - * Applies blur effect on image + * Apply blur effect on image frames * * @param \Intervention\Image\Image $image * @return boolean @@ -14,10 +14,24 @@ public function execute($image) { $amount = $this->argument(0)->between(0, 100)->value(1); - for ($i=0; $i < intval($amount); $i++) { - imagefilter($image->getCore(), IMG_FILTER_GAUSSIAN_BLUR); + foreach ($image as $frame) { + $this->applyBlur($frame->getCore(), $amount); } return true; } + + /** + * Apply blur effect on GD resource + * + * @param resource $resource + * @param integer $amount + * @return void + */ + private function applyBlur($resource, $amount) + { + for ($i=0; $i < intval($amount); $i++) { + imagefilter($resource, IMG_FILTER_GAUSSIAN_BLUR); + } + } } diff --git a/src/Intervention/Image/Imagick/Commands/BlurCommand.php b/src/Intervention/Image/Imagick/Commands/BlurCommand.php index b037c1516..57867d092 100644 --- a/src/Intervention/Image/Imagick/Commands/BlurCommand.php +++ b/src/Intervention/Image/Imagick/Commands/BlurCommand.php @@ -14,6 +14,10 @@ public function execute($image) { $amount = $this->argument(0)->between(0, 100)->value(1); - return $image->getCore()->blurImage(1 * $amount, 0.5 * $amount); + foreach ($image as $imagick) { + $imagick->blurImage(1 * $amount, 0.5 * $amount); + } + + return true; } } diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php index 2380c3fce..c007e499d 100644 --- a/tests/BlurCommandTest.php +++ b/tests/BlurCommandTest.php @@ -14,7 +14,12 @@ public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); + $frame = Mockery::mock('Intervention\Image\Frame'); + $container = Mockery::mock('Intervention\Image\Gd\Container'); + $iterator = new ArrayIterator(array($frame)); + $container->shouldReceive('getIterator')->andReturn($iterator); + $frame->shouldReceive('getCore')->andReturn($resource); + $image->shouldReceive('getIterator')->andReturn($container); $command = new BlurGd(array(2)); $result = $command->execute($image); $this->assertTrue($result); @@ -23,9 +28,16 @@ public function testGd() public function testImagick() { $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('blurimage')->with(2, 1)->andReturn(true); + $imagick->shouldReceive('blurimage')->with(2, 1)->once()->andReturn(true); + $imagick->shouldReceive('rewind'); + $imagick->shouldReceive('current')->andReturn($imagick); + $imagick->shouldReceive('valid')->andReturn(true); + $iterator = new ArrayIterator(array($imagick)); + $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $container = Mockery::mock('Intervention\Image\Imagick\Container'); + $container->shouldReceive('getIterator')->once()->andReturn($iterator); + $image->shouldReceive('getIterator')->once()->andReturn($container); $command = new BlurImagick(array(2)); $result = $command->execute($image); $this->assertTrue($result); From 418c2caceff710d08d3ec5d5bf6654fb95936798 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 21 Jan 2015 19:50:22 +0100 Subject: [PATCH 49/88] removed boolval --- src/Intervention/Image/Gd/Gif/Decoded.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php index 501359f31..a65407fed 100644 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ b/src/Intervention/Image/Gd/Gif/Decoded.php @@ -371,7 +371,7 @@ public function hasGlobalColorTable() $bit = $byte & bindec('10000000'); } - return isset($bit) ? boolval($bit) : false; + return isset($bit) ? (bool) $bit : false; } /** From ac3a36985f7d45584069a5a3837bf6ebf0a1a024 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 21 Jan 2015 19:59:49 +0100 Subject: [PATCH 50/88] ColorizeCommand animation compatible --- .../Image/Gd/Commands/ColorizeCommand.php | 6 +++++- .../Imagick/Commands/ColorizeCommand.php | 19 +++++++++++++------ tests/ColorizeCommandTest.php | 12 ++++++++++-- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php b/src/Intervention/Image/Gd/Commands/ColorizeCommand.php index 8f539638b..30f89b14a 100644 --- a/src/Intervention/Image/Gd/Commands/ColorizeCommand.php +++ b/src/Intervention/Image/Gd/Commands/ColorizeCommand.php @@ -22,6 +22,10 @@ public function execute($image) $blue = round($blue * 2.55); // apply filter - return imagefilter($image->getCore(), IMG_FILTER_COLORIZE, $red, $green, $blue); + foreach ($image as $frame) { + imagefilter($frame->getCore(), IMG_FILTER_COLORIZE, $red, $green, $blue); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php index 51142be27..2383287ce 100644 --- a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php @@ -21,16 +21,23 @@ public function execute($image) $green = $this->normalizeLevel($green); $blue = $this->normalizeLevel($blue); - $qrange = $image->getCore()->getQuantumRange(); - - // apply - $image->getCore()->levelImage(0, $red, $qrange['quantumRangeLong'], \Imagick::CHANNEL_RED); - $image->getCore()->levelImage(0, $green, $qrange['quantumRangeLong'], \Imagick::CHANNEL_GREEN); - $image->getCore()->levelImage(0, $blue, $qrange['quantumRangeLong'], \Imagick::CHANNEL_BLUE); + // apply on each frame + foreach ($image as $imagick) { + $qrange = $imagick->getQuantumRange(); + $imagick->levelImage(0, $red, $qrange['quantumRangeLong'], \Imagick::CHANNEL_RED); + $imagick->levelImage(0, $green, $qrange['quantumRangeLong'], \Imagick::CHANNEL_GREEN); + $imagick->levelImage(0, $blue, $qrange['quantumRangeLong'], \Imagick::CHANNEL_BLUE); + } return true; } + /** + * Return normalized level value + * + * @param numeric $level + * @return numeric + */ private function normalizeLevel($level) { if ($level > 0) { diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php index c89d0d4a9..8da0eb606 100644 --- a/tests/ColorizeCommandTest.php +++ b/tests/ColorizeCommandTest.php @@ -14,7 +14,13 @@ public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $frame = Mockery::mock('Intervention\Image\Frame'); + $container = Mockery::mock('Intervention\Image\Gd\Container'); + $iterator = new ArrayIterator(array($frame)); + $container->shouldReceive('getIterator')->andReturn($iterator); + $frame->shouldReceive('getCore')->andReturn($resource); + $image->shouldReceive('getIterator')->andReturn($container); + $command = new ColorizeGd(array(20, 0, -40)); $result = $command->execute($image); $this->assertTrue($result); @@ -23,12 +29,14 @@ public function testGd() public function testImagick() { $imagick = Mockery::mock('Imagick'); + $iterator = new ArrayIterator(array($imagick)); $imagick->shouldReceive('getquantumrange')->with()->once()->andReturn(array('quantumRangeLong' => 42)); $imagick->shouldReceive('levelimage')->with(0, 4, 42, \Imagick::CHANNEL_RED)->once()->andReturn(true); $imagick->shouldReceive('levelimage')->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->once()->andReturn(true); $imagick->shouldReceive('levelimage')->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->once()->andReturn(true); $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(4)->andReturn($imagick); + $image->shouldReceive('getIterator')->andReturn($iterator); + $command = new ColorizeImagick(array(20, 0, -40)); $result = $command->execute($image); $this->assertTrue($result); From 383950035dfc13d2cc52cd2f79bcaeb4912a244f Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 21 Jan 2015 20:23:12 +0100 Subject: [PATCH 51/88] added tests --- tests/ImagickContainerTest.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/ImagickContainerTest.php diff --git a/tests/ImagickContainerTest.php b/tests/ImagickContainerTest.php new file mode 100644 index 000000000..5cf68ff50 --- /dev/null +++ b/tests/ImagickContainerTest.php @@ -0,0 +1,27 @@ +setCore('foo'); + $this->assertEquals('foo', $container->getCore()); + } + + public function testCountFrames() + { + $imagick = Mockery::mock('Imagick'); + $imagick->shouldReceive('getnumberimages')->once()->andReturn(3); + $container = new Container($imagick); + $this->assertEquals(3, $container->countFrames()); + } +} From d888b840eb51b37a616a43a9099eceeb0b950541 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 22 Jan 2015 19:57:29 +0100 Subject: [PATCH 52/88] added base command test class --- composer.json | 3 +++ tests/BlurCommandTest.php | 29 ++++----------------- tests/ColorizeCommandTest.php | 29 +++++---------------- tests/CommandTestCase.php | 48 +++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 46 deletions(-) create mode 100644 tests/CommandTestCase.php diff --git a/composer.json b/composer.json index b8114a15b..20edb6bfc 100644 --- a/composer.json +++ b/composer.json @@ -29,5 +29,8 @@ "Intervention\\Image\\": "src/Intervention/Image" } }, + "autoload-dev": { + "classmap": ["tests"] + }, "minimum-stability": "stable" } diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php index c007e499d..feae2eebd 100644 --- a/tests/BlurCommandTest.php +++ b/tests/BlurCommandTest.php @@ -3,23 +3,12 @@ use Intervention\Image\Gd\Commands\BlurCommand as BlurGd; use Intervention\Image\Imagick\Commands\BlurCommand as BlurImagick; -class BlurCommandTest extends PHPUnit_Framework_TestCase +class BlurCommandTest extends CommandTestCase { - public function tearDown() - { - Mockery::close(); - } - public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $frame = Mockery::mock('Intervention\Image\Frame'); - $container = Mockery::mock('Intervention\Image\Gd\Container'); - $iterator = new ArrayIterator(array($frame)); - $container->shouldReceive('getIterator')->andReturn($iterator); - $frame->shouldReceive('getCore')->andReturn($resource); - $image->shouldReceive('getIterator')->andReturn($container); + $image = $this->getTestImage('gd'); + $command = new BlurGd(array(2)); $result = $command->execute($image); $this->assertTrue($result); @@ -27,17 +16,9 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('blurimage')->with(2, 1)->once()->andReturn(true); - $imagick->shouldReceive('rewind'); - $imagick->shouldReceive('current')->andReturn($imagick); - $imagick->shouldReceive('valid')->andReturn(true); - $iterator = new ArrayIterator(array($imagick)); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('blurimage')->with(2, 1)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $container = Mockery::mock('Intervention\Image\Imagick\Container'); - $container->shouldReceive('getIterator')->once()->andReturn($iterator); - $image->shouldReceive('getIterator')->once()->andReturn($container); $command = new BlurImagick(array(2)); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php index 8da0eb606..12cab822f 100644 --- a/tests/ColorizeCommandTest.php +++ b/tests/ColorizeCommandTest.php @@ -3,23 +3,11 @@ use Intervention\Image\Gd\Commands\ColorizeCommand as ColorizeGd; use Intervention\Image\Imagick\Commands\ColorizeCommand as ColorizeImagick; -class ColorizeCommandTest extends PHPUnit_Framework_TestCase +class ColorizeCommandTest extends CommandTestCase { - public function tearDown() - { - Mockery::close(); - } - public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $frame = Mockery::mock('Intervention\Image\Frame'); - $container = Mockery::mock('Intervention\Image\Gd\Container'); - $iterator = new ArrayIterator(array($frame)); - $container->shouldReceive('getIterator')->andReturn($iterator); - $frame->shouldReceive('getCore')->andReturn($resource); - $image->shouldReceive('getIterator')->andReturn($container); + $image = $this->getTestImage('gd'); $command = new ColorizeGd(array(20, 0, -40)); $result = $command->execute($image); @@ -28,14 +16,11 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $iterator = new ArrayIterator(array($imagick)); - $imagick->shouldReceive('getquantumrange')->with()->once()->andReturn(array('quantumRangeLong' => 42)); - $imagick->shouldReceive('levelimage')->with(0, 4, 42, \Imagick::CHANNEL_RED)->once()->andReturn(true); - $imagick->shouldReceive('levelimage')->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->once()->andReturn(true); - $imagick->shouldReceive('levelimage')->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getIterator')->andReturn($iterator); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('getquantumrange')->with()->once()->andReturn(array('quantumRangeLong' => 42)); + $image->getCore()->shouldReceive('levelimage')->with(0, 4, 42, \Imagick::CHANNEL_RED)->once()->andReturn(true); + $image->getCore()->shouldReceive('levelimage')->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->once()->andReturn(true); + $image->getCore()->shouldReceive('levelimage')->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->once()->andReturn(true); $command = new ColorizeImagick(array(20, 0, -40)); $result = $command->execute($image); diff --git a/tests/CommandTestCase.php b/tests/CommandTestCase.php new file mode 100644 index 000000000..5c4ba19cd --- /dev/null +++ b/tests/CommandTestCase.php @@ -0,0 +1,48 @@ +shouldReceive('getIterator')->andReturn($iterator); + $frame->shouldReceive('getCore')->andReturn($resource); + $image->shouldReceive('getCore')->andReturn($resource); + $image->shouldReceive('getIterator')->andReturn($container); + break; + + case 'imagick': + $image = Mockery::mock('Intervention\Image\Image'); + $container = Mockery::mock('Intervention\Image\Imagick\Container'); + $imagick = Mockery::mock('Imagick'); + + $iterator = new ArrayIterator(array($imagick)); + + $container->shouldReceive('getIterator')->once()->andReturn($iterator); + $image->shouldReceive('getIterator')->once()->andReturn($container); + $image->shouldReceive('getCore')->andReturn($imagick); + $imagick->shouldReceive('rewind'); + $imagick->shouldReceive('current')->andReturn($imagick); + $imagick->shouldReceive('valid')->andReturn(true); + break; + + default: + throw new Exception('No type defined'); + } + + return $image; + } +} From 25ffc7a95212c56edab09efdbcc726354b2dbe82 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Thu, 22 Jan 2015 20:05:34 +0100 Subject: [PATCH 53/88] ContrastCommand animation compatible --- .../Image/Gd/Commands/ContrastCommand.php | 6 +++++- .../Imagick/Commands/ContrastCommand.php | 6 +++++- tests/ContrastCommandTest.php | 19 ++++++------------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/ContrastCommand.php b/src/Intervention/Image/Gd/Commands/ContrastCommand.php index e43b761af..ebe3b69e5 100644 --- a/src/Intervention/Image/Gd/Commands/ContrastCommand.php +++ b/src/Intervention/Image/Gd/Commands/ContrastCommand.php @@ -14,6 +14,10 @@ public function execute($image) { $level = $this->argument(0)->between(-100, 100)->required()->value(); - return imagefilter($image->getCore(), IMG_FILTER_CONTRAST, ($level * -1)); + foreach ($image as $frame) { + imagefilter($frame->getCore(), IMG_FILTER_CONTRAST, ($level * -1)); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php index 113a2186c..684308f56 100644 --- a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php @@ -14,6 +14,10 @@ public function execute($image) { $level = $this->argument(0)->between(-100, 100)->required()->value(); - return $image->getCore()->sigmoidalContrastImage($level > 0, $level / 4, 0); + foreach ($image as $imagick) { + $imagick->sigmoidalContrastImage($level > 0, $level / 4, 0); + } + + return true; } } diff --git a/tests/ContrastCommandTest.php b/tests/ContrastCommandTest.php index 2c18f0866..229efe0ff 100644 --- a/tests/ContrastCommandTest.php +++ b/tests/ContrastCommandTest.php @@ -3,18 +3,12 @@ use Intervention\Image\Gd\Commands\ContrastCommand as ContrastGd; use Intervention\Image\Imagick\Commands\ContrastCommand as ContrastImagick; -class ContrastCommandTest extends PHPUnit_Framework_TestCase +class ContrastCommandTest extends CommandTestCase { - public function tearDown() - { - Mockery::close(); - } - public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); + $command = new ContrastGd(array(20)); $result = $command->execute($image); $this->assertTrue($result); @@ -22,10 +16,9 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('sigmoidalcontrastimage')->with(true, 5, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('sigmoidalcontrastimage')->with(true, 5, 0)->andReturn(true); + $command = new ContrastImagick(array(20)); $result = $command->execute($image); $this->assertTrue($result); From 1a4def93602387ef5b619e6501df51adb7233e34 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 23 Jan 2015 17:53:49 +0100 Subject: [PATCH 54/88] added MimeDetector --- src/Intervention/Image/MimeDetector.php | 177 ++++++++++++++++++++++++ tests/MimeDetectorTest.php | 51 +++++++ tests/images/test.bmp | Bin 0 -> 568 bytes tests/images/test.ico | Bin 0 -> 1342 bytes tests/images/test.webp | Bin 0 -> 204 bytes 5 files changed, 228 insertions(+) create mode 100644 src/Intervention/Image/MimeDetector.php create mode 100644 tests/MimeDetectorTest.php create mode 100644 tests/images/test.bmp create mode 100644 tests/images/test.ico create mode 100644 tests/images/test.webp diff --git a/src/Intervention/Image/MimeDetector.php b/src/Intervention/Image/MimeDetector.php new file mode 100644 index 000000000..f230a4dd0 --- /dev/null +++ b/src/Intervention/Image/MimeDetector.php @@ -0,0 +1,177 @@ +data = $data; + } + + /** + * Set data to be detected + * + * @param string $value + */ + public function setData($value) + { + $this->data = $value; + + return $this; + } + + /** + * Return MIME type of current data + * + * @return string + */ + public function getMimeType() + { + switch (true) { + + case $this->isJpeg(): + return 'image/jpeg'; + + case $this->isPng(): + return 'image/png'; + + case $this->isGif(): + return 'image/gif'; + + case $this->isBitmap(): + return 'image/bmp'; + + case $this->isTiff(): + return 'image/tif'; + + case $this->isWebp(): + return 'image/webp'; + + case $this->isPsd(): + return 'image/vnd.adobe.photoshop'; + + case $this->isIco(): + return 'image/x-icon'; + } + + throw new Exception\NotSupportedException( + "MIME type could not be identified." + ); + } + + /** + * Determine if current data is PNG + * + * @return boolean + */ + private function isPng() + { + return ($this->getHexBytes(0, 8) == '89504e470d0a1a0a'); + } + + /** + * Determine if current data is JPG + * + * @return boolean + */ + private function isJpeg() + { + $bytes1 = $this->getHexBytes(0, 4); + $bytes2 = $this->getHexBytes(6, 5); + + return ($bytes1 == 'ffd8ffe0') && ($bytes2 == '4a46494600'); + } + + /** + * Determine if current data is GIF + * + * @return boolean + */ + private function isGif() + { + $bytes = $this->getHexBytes(0, 6); + + return ($bytes == '474946383761') || ($bytes == '474946383961'); + } + + /** + * Determine if current data is Bitmap + * + * @return boolean + */ + private function isBitmap() + { + return ($this->getHexBytes(0, 2) == '424d'); + } + + /** + * Determine if current data is TIF + * + * @return boolean + */ + private function isTiff() + { + $bytes1 = $this->getHexBytes(0, 3); + $bytes2 = $this->getHexBytes(0, 4); + + return ($bytes1 == '492049') || ($bytes2 == '49492a00'); + } + + /** + * Determine if current data is ICO + * + * @return boolean + */ + private function isIco() + { + return ($this->getHexBytes(0, 4) == '00000100'); + } + + /** + * Determine if current data is Photoshop + * + * @return boolean + */ + private function isPsd() + { + return ($this->getHexBytes(0, 4) == '38425053'); + } + + /** + * Determine if current data is WEBP + * + * @return boolean + */ + private function isWebp() + { + $bytes1 = $this->getHexBytes(0, 4); + $bytes2 = $this->getHexBytes(8, 4); + + return ($bytes1 == '52494646') && ($bytes2 == '57454250'); + } + + /** + * Return hexadecimal formated bytes from current data + * + * @param int $start + * @param int $length + * @return string + */ + private function getHexBytes($start, $length) + { + return bin2hex(substr($this->data, $start, $length)); + } +} diff --git a/tests/MimeDetectorTest.php b/tests/MimeDetectorTest.php new file mode 100644 index 000000000..fa8016c9d --- /dev/null +++ b/tests/MimeDetectorTest.php @@ -0,0 +1,51 @@ +getMimeType(); + } + + public function testDetectJpeg() + { + $detector = new Detector(file_get_contents('tests/images/test.jpg')); + $this->assertEquals('image/jpeg', $detector->getMimeType()); + } + + public function testDetectPng() + { + $detector = new Detector(file_get_contents('tests/images/tile.png')); + $this->assertEquals('image/png', $detector->getMimeType()); + } + + public function testDetectGif() + { + $detector = new Detector(file_get_contents('tests/images/animation.gif')); + $this->assertEquals('image/gif', $detector->getMimeType()); + } + + public function testDetectBitmap() + { + $detector = new Detector(file_get_contents('tests/images/test.bmp')); + $this->assertEquals('image/bmp', $detector->getMimeType()); + } + + public function testDetectIco() + { + $detector = new Detector(file_get_contents('tests/images/test.ico')); + $this->assertEquals('image/x-icon', $detector->getMimeType()); + } + + public function testDetectWebp() + { + $detector = new Detector(file_get_contents('tests/images/test.webp')); + $this->assertEquals('image/webp', $detector->getMimeType()); + } +} diff --git a/tests/images/test.bmp b/tests/images/test.bmp new file mode 100644 index 0000000000000000000000000000000000000000..61bbc71b4c81f8128baf4b5c5397e7acc36b1df0 GIT binary patch literal 568 zcmZ?rwP0cZ12Z700mK4O%m`*NFfak-g}5OYtPn^r7f=VXCGgvbE68%la)>MN+pr~| zn#vg>rXtUPfMP0~AqW+038=Clbz&+AGx%)~IuR^>8<+{=3J6gIOI!gaFUx@tMX+Q! eh?OT~J|XuJ@*i^n{_p|06My)^d_p?SzyJU&1y7#< literal 0 HcmV?d00001 diff --git a/tests/images/test.ico b/tests/images/test.ico new file mode 100644 index 0000000000000000000000000000000000000000..a6e497f097d03b671386bbedebd856b2ad732a5b GIT binary patch literal 1342 zcmd^#VP5KzTnidxVZ+cvDO#3xOu<43cQxZ{d9k;{#O55_fLMboS*PhIpF literal 0 HcmV?d00001 diff --git a/tests/images/test.webp b/tests/images/test.webp new file mode 100644 index 0000000000000000000000000000000000000000..8fa3cad0c3b3f43657667e1e63b7ba86211ed16a GIT binary patch literal 204 zcmV;-05ktmNk&G*00012MM6+kP&gpC0002c1^}G_Di8n=0096TC9nc?x&inB_yJA> z_y^zy=m+o*zzyIJ&<*eo01l83(!bmffDhmw)o#EBHd~jhh(G}T`tSV3_Z=RJbo@8V zRVSf-OqeXd@-~(2Xz6=grQKl630J~t+AGdb|L9y4&nY_%Vd#x$(U=LRFy46FD#~mi z34I|EaXycfolsDqvKZP^ME~ry6%nWw*`9`6c0&=dAB_7h9a@N63Imb Date: Sat, 24 Jan 2015 20:14:45 +0100 Subject: [PATCH 55/88] Imagick Container iterates Frames --- .../Image/Imagick/Commands/BlurCommand.php | 4 +- .../Imagick/Commands/ColorizeCommand.php | 10 ++-- .../Imagick/Commands/ContrastCommand.php | 4 +- src/Intervention/Image/Imagick/Container.php | 51 ++++++++++++++++--- tests/BlurCommandTest.php | 2 +- tests/ColorizeCommandTest.php | 8 +-- tests/CommandTestCase.php | 10 +++- tests/ContrastCommandTest.php | 2 +- tests/GdContainerTest.php | 11 ++++ 9 files changed, 79 insertions(+), 23 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/BlurCommand.php b/src/Intervention/Image/Imagick/Commands/BlurCommand.php index 57867d092..2859c2a3d 100644 --- a/src/Intervention/Image/Imagick/Commands/BlurCommand.php +++ b/src/Intervention/Image/Imagick/Commands/BlurCommand.php @@ -14,8 +14,8 @@ public function execute($image) { $amount = $this->argument(0)->between(0, 100)->value(1); - foreach ($image as $imagick) { - $imagick->blurImage(1 * $amount, 0.5 * $amount); + foreach ($image as $frame) { + $frame->getCore()->blurImage(1 * $amount, 0.5 * $amount); } return true; diff --git a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php index 2383287ce..3299f375e 100644 --- a/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ColorizeCommand.php @@ -22,11 +22,11 @@ public function execute($image) $blue = $this->normalizeLevel($blue); // apply on each frame - foreach ($image as $imagick) { - $qrange = $imagick->getQuantumRange(); - $imagick->levelImage(0, $red, $qrange['quantumRangeLong'], \Imagick::CHANNEL_RED); - $imagick->levelImage(0, $green, $qrange['quantumRangeLong'], \Imagick::CHANNEL_GREEN); - $imagick->levelImage(0, $blue, $qrange['quantumRangeLong'], \Imagick::CHANNEL_BLUE); + foreach ($image as $frame) { + $qrange = $frame->getCore()->getQuantumRange(); + $frame->getCore()->levelImage(0, $red, $qrange['quantumRangeLong'], \Imagick::CHANNEL_RED); + $frame->getCore()->levelImage(0, $green, $qrange['quantumRangeLong'], \Imagick::CHANNEL_GREEN); + $frame->getCore()->levelImage(0, $blue, $qrange['quantumRangeLong'], \Imagick::CHANNEL_BLUE); } return true; diff --git a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php index 684308f56..bda3f350e 100644 --- a/src/Intervention/Image/Imagick/Commands/ContrastCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ContrastCommand.php @@ -14,8 +14,8 @@ public function execute($image) { $level = $this->argument(0)->between(-100, 100)->required()->value(); - foreach ($image as $imagick) { - $imagick->sigmoidalContrastImage($level > 0, $level / 4, 0); + foreach ($image as $frame) { + $frame->getCore()->sigmoidalContrastImage($level > 0, $level / 4, 0); } return true; diff --git a/src/Intervention/Image/Imagick/Container.php b/src/Intervention/Image/Imagick/Container.php index c5c5380f9..d59b5867c 100644 --- a/src/Intervention/Image/Imagick/Container.php +++ b/src/Intervention/Image/Imagick/Container.php @@ -3,22 +3,19 @@ namespace Intervention\Image\Imagick; use Intervention\Image\ContainerInterface; -use \IteratorAggregate; +use Intervention\Image\Frame; +use \Iterator; -class Container implements ContainerInterface, IteratorAggregate +class Container implements ContainerInterface, Iterator { private $imagick; + private $position; public function __construct(\Imagick $imagick) { $this->imagick = $imagick; } - public function getIterator() - { - return $this->imagick; - } - public function getCore() { return $this->imagick; @@ -33,4 +30,44 @@ public function countFrames() { return $this->imagick->getNumberImages(); } + + public function rewind() + { + $this->position = 0; + } + + public function current() + { + $imagick = $this->getImagickAtPosition(); + + return new Frame( + $imagick, + $imagick->getImageDelay(), + $imagick->getImageDispose() + ); + } + + public function key() + { + return $this->position; + } + + public function next() + { + ++$this->position; + } + + public function valid() + { + return is_a($this->getImagickAtPosition(), 'Imagick'); + } + + private function getImagickAtPosition() + { + foreach ($this->imagick as $key => $imagick) { + if ($key == $this->position) { + return $imagick; + } + } + } } diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php index feae2eebd..634bd70b4 100644 --- a/tests/BlurCommandTest.php +++ b/tests/BlurCommandTest.php @@ -17,7 +17,7 @@ public function testGd() public function testImagick() { $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('blurimage')->with(2, 1)->once()->andReturn(true); + $image->getCore()->shouldReceive('blurimage')->with(2, 1)->times(3)->andReturn(true); $command = new BlurImagick(array(2)); $result = $command->execute($image); diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php index 12cab822f..29cdce2f0 100644 --- a/tests/ColorizeCommandTest.php +++ b/tests/ColorizeCommandTest.php @@ -17,10 +17,10 @@ public function testGd() public function testImagick() { $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('getquantumrange')->with()->once()->andReturn(array('quantumRangeLong' => 42)); - $image->getCore()->shouldReceive('levelimage')->with(0, 4, 42, \Imagick::CHANNEL_RED)->once()->andReturn(true); - $image->getCore()->shouldReceive('levelimage')->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->once()->andReturn(true); - $image->getCore()->shouldReceive('levelimage')->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->once()->andReturn(true); + $image->getCore()->shouldReceive('getquantumrange')->times(3)->with()->andReturn(array('quantumRangeLong' => 42)); + $image->getCore()->shouldReceive('levelimage')->times(3)->with(0, 4, 42, \Imagick::CHANNEL_RED)->andReturn(true); + $image->getCore()->shouldReceive('levelimage')->times(3)->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->andReturn(true); + $image->getCore()->shouldReceive('levelimage')->times(3)->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->andReturn(true); $command = new ColorizeImagick(array(20, 0, -40)); $result = $command->execute($image); diff --git a/tests/CommandTestCase.php b/tests/CommandTestCase.php index 5c4ba19cd..842decdb6 100644 --- a/tests/CommandTestCase.php +++ b/tests/CommandTestCase.php @@ -28,10 +28,18 @@ protected function getTestImage($type) $image = Mockery::mock('Intervention\Image\Image'); $container = Mockery::mock('Intervention\Image\Imagick\Container'); $imagick = Mockery::mock('Imagick'); + $frame = Mockery::mock('Intervention\Image\Frame'); $iterator = new ArrayIterator(array($imagick)); - $container->shouldReceive('getIterator')->once()->andReturn($iterator); + $frame->shouldReceive('getCore')->andReturn($imagick); + $container->shouldReceive('rewind')->once(); + $container->shouldReceive('valid')->once()->andReturn(true); + $container->shouldReceive('valid')->once()->andReturn(true); + $container->shouldReceive('valid')->once()->andReturn(true); + $container->shouldReceive('valid')->once()->andReturn(false); + $container->shouldReceive('current')->times(3)->andReturn($frame); + $container->shouldReceive('next'); $image->shouldReceive('getIterator')->once()->andReturn($container); $image->shouldReceive('getCore')->andReturn($imagick); $imagick->shouldReceive('rewind'); diff --git a/tests/ContrastCommandTest.php b/tests/ContrastCommandTest.php index 229efe0ff..7d7b9b6e1 100644 --- a/tests/ContrastCommandTest.php +++ b/tests/ContrastCommandTest.php @@ -17,7 +17,7 @@ public function testGd() public function testImagick() { $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('sigmoidalcontrastimage')->with(true, 5, 0)->andReturn(true); + $image->getCore()->shouldReceive('sigmoidalcontrastimage')->times(3)->with(true, 5, 0)->andReturn(true); $command = new ContrastImagick(array(20)); $result = $command->execute($image); diff --git a/tests/GdContainerTest.php b/tests/GdContainerTest.php index f242c9a86..dc6a5c79b 100644 --- a/tests/GdContainerTest.php +++ b/tests/GdContainerTest.php @@ -22,4 +22,15 @@ public function testCountFrames() $container->addFrames(array('foo', 'bar', 'baz')); $this->assertEquals(3, $container->countFrames()); } + + public function testIterateToFrames() + { + $frame = Mockery::mock('Intervention\Image\Frame'); + $container = new Container; + $container->addFrame($frame); + $container->addFrame($frame); + foreach ($container as $key => $value) { + $this->assertInstanceOf('Intervention\Image\Frame', $value); + } + } } From 51961c7b38a05b5cb614213e3dd2ea4db60484c1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 24 Jan 2015 20:18:57 +0100 Subject: [PATCH 56/88] gamma command animation compatible --- .../Image/Gd/Commands/GammaCommand.php | 6 +++++- .../Image/Imagick/Commands/GammaCommand.php | 6 +++++- tests/GammaCommandTest.php | 14 ++++++-------- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/GammaCommand.php b/src/Intervention/Image/Gd/Commands/GammaCommand.php index 366f11808..f1c82f65e 100644 --- a/src/Intervention/Image/Gd/Commands/GammaCommand.php +++ b/src/Intervention/Image/Gd/Commands/GammaCommand.php @@ -14,6 +14,10 @@ public function execute($image) { $gamma = $this->argument(0)->type('numeric')->required()->value(); - return imagegammacorrect($image->getCore(), 1, $gamma); + foreach ($image as $frame) { + imagegammacorrect($frame->getCore(), 1, $gamma); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/GammaCommand.php b/src/Intervention/Image/Imagick/Commands/GammaCommand.php index e70cbdd36..a213d00db 100644 --- a/src/Intervention/Image/Imagick/Commands/GammaCommand.php +++ b/src/Intervention/Image/Imagick/Commands/GammaCommand.php @@ -14,6 +14,10 @@ public function execute($image) { $gamma = $this->argument(0)->type('numeric')->required()->value(); - return $image->getCore()->gammaImage($gamma); + foreach ($image as $frame) { + $frame->getCore()->gammaImage($gamma); + } + + return true; } } diff --git a/tests/GammaCommandTest.php b/tests/GammaCommandTest.php index 48d6bb9f6..a364fe17d 100644 --- a/tests/GammaCommandTest.php +++ b/tests/GammaCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\GammaCommand as GammaGd; use Intervention\Image\Imagick\Commands\GammaCommand as GammaImagick; -class GammaCommandTest extends PHPUnit_Framework_TestCase +class GammaCommandTest extends CommandTestCase { public function tearDown() { @@ -12,9 +12,8 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); + $command = new GammaGd(array(1.4)); $result = $command->execute($image); $this->assertTrue($result); @@ -22,10 +21,9 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('gammaimage')->with(1.4)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('gammaimage')->times(3)->with(1.4)->andReturn(true); + $command = new GammaImagick(array(1.4)); $result = $command->execute($image); $this->assertTrue($result); From 6b34842d19e21483f6e999ff48fc87ebbeeda00a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 24 Jan 2015 20:49:52 +0100 Subject: [PATCH 57/88] resizeCommand animation compatible --- src/Intervention/Image/Frame.php | 13 ++++ .../Image/Gd/Commands/ResizeCommand.php | 69 ++++++++++--------- .../Image/Imagick/Commands/ResizeCommand.php | 4 +- tests/CommandTestCase.php | 1 + tests/ResizeCommandTest.php | 19 +++-- 5 files changed, 63 insertions(+), 43 deletions(-) diff --git a/src/Intervention/Image/Frame.php b/src/Intervention/Image/Frame.php index 9d7cc957f..2198ec342 100644 --- a/src/Intervention/Image/Frame.php +++ b/src/Intervention/Image/Frame.php @@ -52,4 +52,17 @@ public function getCore() { return $this->core; } + + /** + * Set core of current frame + * + * @param mixed $core + * @return Intervention\Image\Frame + */ + public function setCore($core) + { + $this->core = $core; + + return $this; + } } diff --git a/src/Intervention/Image/Gd/Commands/ResizeCommand.php b/src/Intervention/Image/Gd/Commands/ResizeCommand.php index 2b5700f1b..97aeacb8d 100644 --- a/src/Intervention/Image/Gd/Commands/ResizeCommand.php +++ b/src/Intervention/Image/Gd/Commands/ResizeCommand.php @@ -41,42 +41,49 @@ public function execute($image) */ protected function modify($image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h) { - // create new image - $modified = imagecreatetruecolor($dst_w, $dst_h); + foreach ($image as $frame) { - // get current image - $resource = $image->getCore(); + // create new image + $modified = imagecreatetruecolor($dst_w, $dst_h); - // preserve transparency - $transIndex = imagecolortransparent($resource); + // get current image + $resource = $frame->getCore(); - if ($transIndex != -1) { - $rgba = imagecolorsforindex($modified, $transIndex); - $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); - imagefill($modified, 0, 0, $transColor); - imagecolortransparent($modified, $transColor); - } else { - imagealphablending($modified, false); - imagesavealpha($modified, true); - } + // preserve transparency + $transIndex = imagecolortransparent($resource); + + if ($transIndex != -1) { + $rgba = imagecolorsforindex($modified, $transIndex); + $transColor = imagecolorallocatealpha($modified, $rgba['red'], $rgba['green'], $rgba['blue'], 127); + imagefill($modified, 0, 0, $transColor); + imagecolortransparent($modified, $transColor); + } else { + imagealphablending($modified, false); + imagesavealpha($modified, true); + } - // copy content from resource - $result = imagecopyresampled( - $modified, - $resource, - $dst_x, - $dst_y, - $src_x, - $src_y, - $dst_w, - $dst_h, - $src_w, - $src_h - ); + // copy content from resource + imagecopyresampled( + $modified, + $resource, + $dst_x, + $dst_y, + $src_x, + $src_y, + $dst_w, + $dst_h, + $src_w, + $src_h + ); - // set new content as recource - $image->setCore($modified); + // free memory of old core + imagedestroy($resource); - return $result; + // set new content as recource + $frame->setCore($modified); + + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCommand.php index 6051dd1b8..aa769f7f1 100644 --- a/src/Intervention/Image/Imagick/Commands/ResizeCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ResizeCommand.php @@ -20,7 +20,9 @@ public function execute($image) $resized = $image->getSize()->resize($width, $height, $constraints); // modify image - $image->getCore()->resizeImage($resized->getWidth(), $resized->getHeight(), \Imagick::FILTER_BOX, 1); + foreach ($image as $frame) { + $frame->getCore()->resizeImage($resized->getWidth(), $resized->getHeight(), \Imagick::FILTER_BOX, 1); + } return true; } diff --git a/tests/CommandTestCase.php b/tests/CommandTestCase.php index 842decdb6..90fe1e3bd 100644 --- a/tests/CommandTestCase.php +++ b/tests/CommandTestCase.php @@ -20,6 +20,7 @@ protected function getTestImage($type) $container->shouldReceive('getIterator')->andReturn($iterator); $frame->shouldReceive('getCore')->andReturn($resource); + $frame->shouldReceive('setCore')->andReturn($resource); $image->shouldReceive('getCore')->andReturn($resource); $image->shouldReceive('getIterator')->andReturn($container); break; diff --git a/tests/ResizeCommandTest.php b/tests/ResizeCommandTest.php index 56cdcb391..ca453c0f1 100644 --- a/tests/ResizeCommandTest.php +++ b/tests/ResizeCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\ResizeCommand as ResizeCommandGd; use Intervention\Image\Imagick\Commands\ResizeCommand as ResizeCommandImagick; -class resizeCommandTest extends PHPUnit_Framework_TestCase +class resizeCommandTest extends CommandTestCase { public function tearDown() { @@ -13,8 +13,7 @@ public function tearDown() public function testGd() { $callback = function ($constraint) { $constraint->upsize(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); + $image = $this->getTestImage('gd'); $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(800); @@ -22,8 +21,7 @@ public function testGd() $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); + $command = new ResizeCommandGd(array(300, 200, $callback)); $result = $command->execute($image); $this->assertTrue($result); @@ -32,15 +30,14 @@ public function testGd() public function testImagick() { $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('resizeimage')->with(300, 200, \Imagick::FILTER_BOX, 1)->once()->andReturn(true); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('resizeimage')->with(300, 200, \Imagick::FILTER_BOX, 1)->times(3)->andReturn(true); $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $size->shouldReceive('getWidth')->times(3)->andReturn(300); + $size->shouldReceive('getHeight')->times(3)->andReturn(200); $image->shouldReceive('getSize')->once()->andReturn($size); + $command = new ResizeCommandImagick(array(300, 200, $callback)); $result = $command->execute($image); $this->assertTrue($result); From 982590c924f948b5ab7e5d5e873be4ab27a33f6b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 13:51:30 +0100 Subject: [PATCH 58/88] resize related commands are now animation compatible --- .../Image/Gd/Commands/FitCommand.php | 11 ++++- .../Image/Gd/Commands/FlipCommand.php | 12 ++++- .../Image/Gd/Commands/HeightenCommand.php | 3 +- .../Image/Gd/Commands/WidenCommand.php | 3 +- .../Image/Imagick/Commands/CropCommand.php | 8 +-- .../Image/Imagick/Commands/FitCommand.php | 6 ++- .../Image/Imagick/Commands/FlipCommand.php | 23 ++++++--- .../Imagick/Commands/HeightenCommand.php | 3 +- .../Image/Imagick/Commands/WidenCommand.php | 3 +- tests/CropCommandTest.php | 16 +++--- tests/FitCommandTest.php | 49 +++++++++---------- tests/FlipCommandTest.php | 19 +++---- tests/HeightenCommandTest.php | 21 ++++---- tests/WidenCommandTest.php | 21 ++++---- 14 files changed, 111 insertions(+), 87 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/FitCommand.php b/src/Intervention/Image/Gd/Commands/FitCommand.php index 492b72186..c659ff1d2 100644 --- a/src/Intervention/Image/Gd/Commands/FitCommand.php +++ b/src/Intervention/Image/Gd/Commands/FitCommand.php @@ -26,7 +26,16 @@ public function execute($image) $resized = $resized->resize($width, $height, $constraints); // modify image - $this->modify($image, 0, 0, $cropped->pivot->x, $cropped->pivot->y, $resized->getWidth(), $resized->getHeight(), $cropped->getWidth(), $cropped->getHeight()); + $this->modify($image, + 0, + 0, + $cropped->pivot->x, + $cropped->pivot->y, + $resized->getWidth(), + $resized->getHeight(), + $cropped->getWidth(), + $cropped->getHeight() + ); return true; } diff --git a/src/Intervention/Image/Gd/Commands/FlipCommand.php b/src/Intervention/Image/Gd/Commands/FlipCommand.php index aa8f230e8..0248f6e95 100644 --- a/src/Intervention/Image/Gd/Commands/FlipCommand.php +++ b/src/Intervention/Image/Gd/Commands/FlipCommand.php @@ -32,6 +32,16 @@ public function execute($image) break; } - return $this->modify($image, 0, 0, $size->pivot->x, $size->pivot->y, $dst->width, $dst->height, $size->width, $size->height); + return $this->modify( + $image, + 0, + 0, + $size->pivot->x, + $size->pivot->y, + $dst->width, + $dst->height, + $size->width, + $size->height + ); } } diff --git a/src/Intervention/Image/Gd/Commands/HeightenCommand.php b/src/Intervention/Image/Gd/Commands/HeightenCommand.php index 51e0abdf5..7a7d5f8bb 100644 --- a/src/Intervention/Image/Gd/Commands/HeightenCommand.php +++ b/src/Intervention/Image/Gd/Commands/HeightenCommand.php @@ -19,8 +19,9 @@ public function execute($image) $this->arguments[1] = $height; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) { $additionalConstraints($constraint); + } }; return parent::execute($image); diff --git a/src/Intervention/Image/Gd/Commands/WidenCommand.php b/src/Intervention/Image/Gd/Commands/WidenCommand.php index c7d396f1b..940c8e49c 100644 --- a/src/Intervention/Image/Gd/Commands/WidenCommand.php +++ b/src/Intervention/Image/Gd/Commands/WidenCommand.php @@ -19,8 +19,9 @@ public function execute($image) $this->arguments[1] = null; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) { $additionalConstraints($constraint); + } }; return parent::execute($image); diff --git a/src/Intervention/Image/Imagick/Commands/CropCommand.php b/src/Intervention/Image/Imagick/Commands/CropCommand.php index 498064ef7..22388511d 100644 --- a/src/Intervention/Image/Imagick/Commands/CropCommand.php +++ b/src/Intervention/Image/Imagick/Commands/CropCommand.php @@ -34,9 +34,11 @@ public function execute($image) $position = $image->getSize()->align('center')->relativePosition($cropped->align('center')); } - // crop image core - $image->getCore()->cropImage($cropped->width, $cropped->height, $position->x, $position->y); - $image->getCore()->setImagePage(0,0,0,0); + // crop image cores + foreach ($image as $frame) { + $frame->getCore()->cropImage($cropped->width, $cropped->height, $position->x, $position->y); + $frame->getCore()->setImagePage(0,0,0,0); + } return true; } diff --git a/src/Intervention/Image/Imagick/Commands/FitCommand.php b/src/Intervention/Image/Imagick/Commands/FitCommand.php index 7e3162994..1a002a778 100644 --- a/src/Intervention/Image/Imagick/Commands/FitCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FitCommand.php @@ -33,8 +33,10 @@ public function execute($image) ); // resize image - $image->getCore()->resizeImage($resized->getWidth(), $resized->getHeight(), \Imagick::FILTER_BOX, 1); - $image->getCore()->setImagePage(0,0,0,0); + foreach ($image as $frame) { + $frame->getCore()->resizeImage($resized->getWidth(), $resized->getHeight(), \Imagick::FILTER_BOX, 1); + $frame->getCore()->setImagePage(0,0,0,0); + } return true; } diff --git a/src/Intervention/Image/Imagick/Commands/FlipCommand.php b/src/Intervention/Image/Imagick/Commands/FlipCommand.php index 746650c1c..85afde963 100644 --- a/src/Intervention/Image/Imagick/Commands/FlipCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FlipCommand.php @@ -14,12 +14,23 @@ public function execute($image) { $mode = $this->argument(0)->value('h'); - if (in_array(strtolower($mode), array(2, 'v', 'vert', 'vertical'))) { - // flip vertical - return $image->getCore()->flipImage(); - } else { - // flip horizontal - return $image->getCore()->flopImage(); + $methodName = $this->modeIsVertical($mode) ? 'flipImage' : 'flopImage'; + + foreach ($image as $frame) { + call_user_func(array($frame->getCore(), $methodName)); } + + return true; + } + + /** + * Check if mode is vertical + * + * @param mixed $mode + * @return bool + */ + private function modeIsVertical($mode) + { + return in_array(strtolower($mode), array(2, 'v', 'vert', 'vertical')); } } diff --git a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php b/src/Intervention/Image/Imagick/Commands/HeightenCommand.php index 9d10973f2..d19e1d053 100644 --- a/src/Intervention/Image/Imagick/Commands/HeightenCommand.php +++ b/src/Intervention/Image/Imagick/Commands/HeightenCommand.php @@ -19,8 +19,9 @@ public function execute($image) $this->arguments[1] = $height; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) { $additionalConstraints($constraint); + } }; return parent::execute($image); diff --git a/src/Intervention/Image/Imagick/Commands/WidenCommand.php b/src/Intervention/Image/Imagick/Commands/WidenCommand.php index 467fe800a..0651b9317 100644 --- a/src/Intervention/Image/Imagick/Commands/WidenCommand.php +++ b/src/Intervention/Image/Imagick/Commands/WidenCommand.php @@ -19,8 +19,9 @@ public function execute($image) $this->arguments[1] = null; $this->arguments[2] = function ($constraint) use ($additionalConstraints) { $constraint->aspectRatio(); - if(is_callable($additionalConstraints)) + if(is_callable($additionalConstraints)) { $additionalConstraints($constraint); + } }; return parent::execute($image); diff --git a/tests/CropCommandTest.php b/tests/CropCommandTest.php index 94cc5c04f..e6908fb07 100644 --- a/tests/CropCommandTest.php +++ b/tests/CropCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\CropCommand as CropGd; use Intervention\Image\Imagick\Commands\CropCommand as CropImagick; -class CropCommandTest extends PHPUnit_Framework_TestCase +class CropCommandTest extends CommandTestCase { public function tearDown() { @@ -12,10 +12,7 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); + $image = $this->getTestImage('gd'); $command = new CropGd(array(100, 150, 10, 20)); $result = $command->execute($image); $this->assertTrue($result); @@ -23,11 +20,10 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(100, 150, 10, 20)->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('cropimage')->with(100, 150, 10, 20)->times(3)->andReturn(true); + $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->times(3); + $command = new CropImagick(array(100, 150, 10, 20)); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/FitCommandTest.php b/tests/FitCommandTest.php index 0d7cb6e68..8b83889b8 100644 --- a/tests/FitCommandTest.php +++ b/tests/FitCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\FitCommand as FitGd; use Intervention\Image\Imagick\Commands\FitCommand as FitImagick; -class FitCommandTest extends PHPUnit_Framework_TestCase +class FitCommandTest extends CommandTestCase { public function tearDown() { @@ -12,6 +12,8 @@ public function tearDown() public function testGdFit() { + $image = $this->getTestImage('gd'); + $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); @@ -19,11 +21,8 @@ public function testGdFit() $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); + $command = new FitGd(array(200, 100)); $result = $command->execute($image); $this->assertTrue($result); @@ -31,6 +30,8 @@ public function testGdFit() public function testGdFitWithPosition() { + $image = $this->getTestImage('gd'); + $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); @@ -38,11 +39,8 @@ public function testGdFitWithPosition() $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); + $command = new FitGd(array(200, 100, null, 'top-left')); $result = $command->execute($image); $this->assertTrue($result); @@ -50,20 +48,20 @@ public function testGdFitWithPosition() public function testImagickFit() { + $image = $this->getTestImage('imagick'); + $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); - $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); - $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); + $cropped_size->shouldReceive('getWidth')->times(3)->andReturn(200); + $cropped_size->shouldReceive('getHeight')->times(3)->andReturn(100); $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $imagick->shouldReceive('resizeimage')->with(200, 100, \Imagick::FILTER_BOX, 1)->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); + $image->getCore()->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); + $image->getCore()->shouldReceive('resizeimage')->with(200, 100, \Imagick::FILTER_BOX, 1)->andReturn(true); + $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); + $command = new FitImagick(array(200, 100)); $result = $command->execute($image); $this->assertTrue($result); @@ -71,20 +69,21 @@ public function testImagickFit() public function testImagickFitWithPosition() { + $image = $this->getTestImage('imagick'); + $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); - $cropped_size->shouldReceive('getWidth')->once()->andReturn(200); - $cropped_size->shouldReceive('getHeight')->once()->andReturn(100); + $cropped_size->shouldReceive('getWidth')->times(3)->andReturn(200); + $cropped_size->shouldReceive('getHeight')->times(3)->andReturn(100); $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $imagick->shouldReceive('resizeimage')->with(200, 100, \Imagick::FILTER_BOX, 1)->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); + $image->getCore()->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); + $image->getCore()->shouldReceive('resizeimage')->with(200, 100, \Imagick::FILTER_BOX, 1)->andReturn(true); + $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); $image->shouldReceive('getSize')->once()->andReturn($original_size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); + + $command = new FitImagick(array(200, 100, null, 'top-left')); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/FlipCommandTest.php b/tests/FlipCommandTest.php index a38070da5..090264b00 100644 --- a/tests/FlipCommandTest.php +++ b/tests/FlipCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\FlipCommand as FlipGd; use Intervention\Image\Imagick\Commands\FlipCommand as FlipImagick; -class FlipCommandTest extends PHPUnit_Framework_TestCase +class FlipCommandTest extends CommandTestCase { public function tearDown() { @@ -12,12 +12,9 @@ public function tearDown() public function testGd() { + $image = $this->getTestImage('gd'); $size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); $command = new FlipGd(array('h')); $result = $command->execute($image); $this->assertTrue($result); @@ -25,18 +22,14 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('flopimage')->with()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('flopimage')->with()->andReturn(true); $command = new FlipImagick(array('h')); $result = $command->execute($image); $this->assertTrue($result); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('flipimage')->with()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('flipimage')->with()->andReturn(true); $command = new FlipImagick(array('v')); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/HeightenCommandTest.php b/tests/HeightenCommandTest.php index ff13ae903..9870f4718 100644 --- a/tests/HeightenCommandTest.php +++ b/tests/HeightenCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\HeightenCommand as HeightenGd; use Intervention\Image\Imagick\Commands\HeightenCommand as HeightenImagick; -class HeightenCommandTest extends PHPUnit_Framework_TestCase +class HeightenCommandTest extends CommandTestCase { public function tearDown() { @@ -13,8 +13,8 @@ public function tearDown() public function testGd() { $callback = function ($constraint) { $constraint->aspectRatio(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); + $image = $this->getTestImage('gd'); + $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); $size->shouldReceive('resize')->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(800); @@ -22,8 +22,7 @@ public function testGd() $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); + $command = new HeightenGd(array(200)); $result = $command->execute($image); $this->assertTrue($result); @@ -32,15 +31,15 @@ public function testGd() public function testImagick() { $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('resizeimage')->with(300, 200, \Imagick::FILTER_BOX, 1)->once()->andReturn(true); + $image = $this->getTestImage('imagick'); + + $image->getCore()->shouldReceive('resizeimage')->with(300, 200, \Imagick::FILTER_BOX, 1)->times(3)->andReturn(true); $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $size->shouldReceive('getWidth')->times(3)->andReturn(300); + $size->shouldReceive('getHeight')->times(3)->andReturn(200); $image->shouldReceive('getSize')->once()->andReturn($size); + $command = new HeightenImagick(array(200)); $result = $command->execute($image); $this->assertTrue($result); diff --git a/tests/WidenCommandTest.php b/tests/WidenCommandTest.php index ce5b30429..b0c747ffc 100644 --- a/tests/WidenCommandTest.php +++ b/tests/WidenCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\WidenCommand as WidenGd; use Intervention\Image\Imagick\Commands\WidenCommand as WidenImagick; -class WidenCommandTest extends PHPUnit_Framework_TestCase +class WidenCommandTest extends CommandTestCase { public function tearDown() { @@ -13,8 +13,8 @@ public function tearDown() public function testGd() { $callback = function ($constraint) { $constraint->aspectRatio(); }; - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); + $image = $this->getTestImage('gd'); + $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); $size->shouldReceive('resize')->once()->andReturn($size); $size->shouldReceive('getWidth')->once()->andReturn(800); @@ -22,8 +22,7 @@ public function testGd() $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); + $command = new WidenGd(array(200)); $result = $command->execute($image); $this->assertTrue($result); @@ -32,15 +31,15 @@ public function testGd() public function testImagick() { $callback = function ($constraint) { $constraint->upsize(); }; - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('resizeimage')->with(300, 200, \Imagick::FILTER_BOX, 1)->once()->andReturn(true); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('resizeimage')->with(300, 200, \Imagick::FILTER_BOX, 1)->times(3)->andReturn(true); + $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(300); - $size->shouldReceive('getHeight')->once()->andReturn(200); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $size->shouldReceive('getWidth')->times(3)->andReturn(300); + $size->shouldReceive('getHeight')->times(3)->andReturn(200); $image->shouldReceive('getSize')->once()->andReturn($size); + $command = new WidenImagick(array(200)); $result = $command->execute($image); $this->assertTrue($result); From ddc05712eb6c86882ca93993cdd11d942eb671ee Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 13:56:08 +0100 Subject: [PATCH 59/88] destroy command animation compatible --- src/Intervention/Image/Gd/Commands/DestroyCommand.php | 6 ++++-- tests/DestroyCommandTest.php | 7 +++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/DestroyCommand.php b/src/Intervention/Image/Gd/Commands/DestroyCommand.php index 403e8b801..c7a975ea3 100644 --- a/src/Intervention/Image/Gd/Commands/DestroyCommand.php +++ b/src/Intervention/Image/Gd/Commands/DestroyCommand.php @@ -12,8 +12,10 @@ class DestroyCommand extends \Intervention\Image\Commands\AbstractCommand */ public function execute($image) { - // destroy image core - imagedestroy($image->getCore()); + // destroy image cores + foreach ($image as $frame) { + imagedestroy($frame->getCore()); + } // destroy backups foreach ($image->getBackups() as $backup) { diff --git a/tests/DestroyCommandTest.php b/tests/DestroyCommandTest.php index f25719644..9099098c7 100644 --- a/tests/DestroyCommandTest.php +++ b/tests/DestroyCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\DestroyCommand as DestroyGd; use Intervention\Image\Imagick\Commands\DestroyCommand as DestroyImagick; -class DestroyCommandTest extends PHPUnit_Framework_TestCase +class DestroyCommandTest extends CommandTestCase { public function tearDown() { @@ -12,15 +12,14 @@ public function tearDown() public function testGd() { - $resource = imagecreatefrompng(__DIR__.'/images/tile.png'); $backups = array( imagecreatefrompng(__DIR__.'/images/tile.png'), imagecreatefrompng(__DIR__.'/images/tile.png') ); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); $image->shouldReceive('getBackups')->once()->andReturn($backups); + $command = new DestroyGd(array()); $result = $command->execute($image); $this->assertTrue($result); From 970d4814e20aa70323f9db2fcf56f327915f6cc4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 14:03:37 +0100 Subject: [PATCH 60/88] getCore refactoring --- src/Intervention/Image/Gd/Container.php | 7 +++++-- src/Intervention/Image/Image.php | 2 +- src/Intervention/Image/Imagick/Container.php | 12 +++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index e7421c203..c9df596e8 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -14,9 +14,12 @@ class Container extends Animation implements ContainerInterface * @param interger $index * @return resource */ - public function getCore($index = 0) + public function getCore($index = null) { - return $this->getFrames()[$index]->getCore(); + $index = is_numeric($index) ? $index : 0; + $frames = $this->getFrames(); + + return $frames[$index]->getCore(); } /** diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 2d5a1ab49..67ce46165 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -194,7 +194,7 @@ public function setDriver(AbstractDriver $driver) * @param integer $index * @return mixed */ - public function getCore($index = 0) + public function getCore($index = null) { return $this->container->getCore($index); } diff --git a/src/Intervention/Image/Imagick/Container.php b/src/Intervention/Image/Imagick/Container.php index d59b5867c..780e1b4ac 100644 --- a/src/Intervention/Image/Imagick/Container.php +++ b/src/Intervention/Image/Imagick/Container.php @@ -16,8 +16,12 @@ public function __construct(\Imagick $imagick) $this->imagick = $imagick; } - public function getCore() + public function getCore($index = null) { + if (is_numeric($index)) { + return $this->getImagickAtPosition($index); + } + return $this->imagick; } @@ -62,10 +66,12 @@ public function valid() return is_a($this->getImagickAtPosition(), 'Imagick'); } - private function getImagickAtPosition() + private function getImagickAtPosition($position = null) { + $position = is_numeric($position) ? $position : $this->position; + foreach ($this->imagick as $key => $imagick) { - if ($key == $this->position) { + if ($key == $position) { return $imagick; } } From 0ea02bba91553dbd8ac890bccb861b77aa17cae7 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 14:22:06 +0100 Subject: [PATCH 61/88] greyscale command animation compatible --- .../Image/Gd/Commands/GreyscaleCommand.php | 6 +++++- .../Image/Imagick/Commands/GreyscaleCommand.php | 6 +++++- tests/GreyscaleCommandTest.php | 12 ++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php b/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php index ded8e0d8f..7e7af7cb5 100644 --- a/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php +++ b/src/Intervention/Image/Gd/Commands/GreyscaleCommand.php @@ -12,6 +12,10 @@ class GreyscaleCommand extends \Intervention\Image\Commands\AbstractCommand */ public function execute($image) { - return imagefilter($image->getCore(), IMG_FILTER_GRAYSCALE); + foreach ($image as $frame) { + imagefilter($frame->getCore(), IMG_FILTER_GRAYSCALE); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php b/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php index bb3f47260..d916a2b7a 100644 --- a/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php +++ b/src/Intervention/Image/Imagick/Commands/GreyscaleCommand.php @@ -12,6 +12,10 @@ class GreyscaleCommand extends \Intervention\Image\Commands\AbstractCommand */ public function execute($image) { - return $image->getCore()->modulateImage(100, 0, 100); + foreach ($image as $frame) { + $frame->getCore()->modulateImage(100, 0, 100); + } + + return true; } } diff --git a/tests/GreyscaleCommandTest.php b/tests/GreyscaleCommandTest.php index 1fa8d3a10..fac59a458 100644 --- a/tests/GreyscaleCommandTest.php +++ b/tests/GreyscaleCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\GreyscaleCommand as GreyscaleGd; use Intervention\Image\Imagick\Commands\GreyscaleCommand as GreyscaleImagick; -class GreyscaleCommandTest extends PHPUnit_Framework_TestCase +class GreyscaleCommandTest extends CommandTestCase { public function tearDown() { @@ -12,9 +12,7 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); $command = new GreyscaleGd(array()); $result = $command->execute($image); $this->assertTrue($result); @@ -22,10 +20,8 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('modulateimage')->with(100, 0, 100)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('modulateimage')->times(3)->with(100, 0, 100)->andReturn(true); $command = new GreyscaleImagick(array()); $result = $command->execute($image); $this->assertTrue($result); From 3b82bb1594e64a5c21be06a99f24fbcc7acf62c8 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 14:35:10 +0100 Subject: [PATCH 62/88] insert command animation compatible --- .../Image/Gd/Commands/InsertCommand.php | 19 +++++++++++++++++-- .../Image/Imagick/Commands/InsertCommand.php | 11 ++++++++++- tests/InsertCommandTest.php | 14 +++++++------- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/InsertCommand.php b/src/Intervention/Image/Gd/Commands/InsertCommand.php index eba75f012..6bcfe258b 100644 --- a/src/Intervention/Image/Gd/Commands/InsertCommand.php +++ b/src/Intervention/Image/Gd/Commands/InsertCommand.php @@ -26,7 +26,22 @@ public function execute($image) $target = $image_size->relativePosition($watermark_size); // insert image at position - imagealphablending($image->getCore(), true); - return imagecopy($image->getCore(), $watermark->getCore(), $target->x, $target->y, 0, 0, $watermark_size->width, $watermark_size->height); + foreach ($image as $frame) { + + imagealphablending($frame->getCore(), true); + + imagecopy( + $frame->getCore(), + $watermark->getCore(), + $target->x, + $target->y, + 0, + 0, + $watermark_size->width, + $watermark_size->height + ); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/InsertCommand.php b/src/Intervention/Image/Imagick/Commands/InsertCommand.php index 542feb2ae..4e0a55ae6 100644 --- a/src/Intervention/Image/Imagick/Commands/InsertCommand.php +++ b/src/Intervention/Image/Imagick/Commands/InsertCommand.php @@ -26,6 +26,15 @@ public function execute($image) $target = $image_size->relativePosition($watermark_size); // insert image at position - return $image->getCore()->compositeImage($watermark->getCore(), \Imagick::COMPOSITE_DEFAULT, $target->x, $target->y); + foreach ($image as $frame) { + $frame->getCore()->compositeImage( + $watermark->getCore(), + \Imagick::COMPOSITE_DEFAULT, + $target->x, + $target->y + ); + } + + return true; } } diff --git a/tests/InsertCommandTest.php b/tests/InsertCommandTest.php index f2fa9001e..6b63933a6 100644 --- a/tests/InsertCommandTest.php +++ b/tests/InsertCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\InsertCommand as InsertGd; use Intervention\Image\Imagick\Commands\InsertCommand as InsertImagick; -class InsertCommandTest extends PHPUnit_Framework_TestCase +class InsertCommandTest extends CommandTestCase { public function tearDown() { @@ -12,6 +12,8 @@ public function tearDown() public function testGd() { + $image = $this->getTestImage('gd'); + $position = Mockery::mock('\Intervention\Image\Point', array(0, 0)); $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); @@ -25,9 +27,7 @@ public function testGd() $watermark = Mockery::mock('Intervention\Image\Image'); $driver = Mockery::mock('Intervention\Image\Gd\Driver'); $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); - $image = Mockery::mock('Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); $image->shouldReceive('getSize')->once()->andReturn($image_size); $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); $watermark->shouldReceive('getCore')->once()->andReturn($resource); @@ -39,6 +39,8 @@ public function testGd() public function testImagick() { + $image = $this->getTestImage('imagick'); + $position = Mockery::mock('\Intervention\Image\Point', array(10, 20)); $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); @@ -52,13 +54,11 @@ public function testImagick() $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('compositeimage')->with($imagick, \Imagick::COMPOSITE_DEFAULT, 10, 20)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image->getCore()->shouldReceive('compositeimage')->with($imagick, \Imagick::COMPOSITE_DEFAULT, 10, 20)->times(3)->andReturn(true); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getSize')->once()->andReturn($image_size); $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); - $watermark->shouldReceive('getCore')->once()->andReturn($imagick); + $watermark->shouldReceive('getCore')->times(3)->andReturn($imagick); $command = new InsertImagick(array($path, 'center', 10, 20)); $result = $command->execute($image); $this->assertTrue($result); From 6400943ff4d2f3c06322ad8b4d908cecb9323be4 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 14:38:06 +0100 Subject: [PATCH 63/88] invert animation compatible --- src/Intervention/Image/Gd/Commands/InvertCommand.php | 6 +++++- .../Image/Imagick/Commands/InvertCommand.php | 6 +++++- tests/InvertCommandTest.php | 12 ++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/InvertCommand.php b/src/Intervention/Image/Gd/Commands/InvertCommand.php index f72e7e305..7601306ee 100644 --- a/src/Intervention/Image/Gd/Commands/InvertCommand.php +++ b/src/Intervention/Image/Gd/Commands/InvertCommand.php @@ -12,6 +12,10 @@ class InvertCommand extends \Intervention\Image\Commands\AbstractCommand */ public function execute($image) { - return imagefilter($image->getCore(), IMG_FILTER_NEGATE); + foreach ($image as $frame) { + imagefilter($frame->getCore(), IMG_FILTER_NEGATE); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/InvertCommand.php b/src/Intervention/Image/Imagick/Commands/InvertCommand.php index 125fbddee..06867ad8d 100644 --- a/src/Intervention/Image/Imagick/Commands/InvertCommand.php +++ b/src/Intervention/Image/Imagick/Commands/InvertCommand.php @@ -12,6 +12,10 @@ class InvertCommand extends \Intervention\Image\Commands\AbstractCommand */ public function execute($image) { - return $image->getCore()->negateImage(false); + foreach ($image as $frame) { + $frame->getCore()->negateImage(false); + } + + return true; } } diff --git a/tests/InvertCommandTest.php b/tests/InvertCommandTest.php index e7cabba25..e7ea44ed0 100644 --- a/tests/InvertCommandTest.php +++ b/tests/InvertCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\InvertCommand as InvertGd; use Intervention\Image\Imagick\Commands\InvertCommand as InvertImagick; -class InvertCommandTest extends PHPUnit_Framework_TestCase +class InvertCommandTest extends CommandTestCase { public function tearDown() { @@ -12,9 +12,7 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); $command = new InvertGd(array()); $result = $command->execute($image); $this->assertTrue($result); @@ -22,10 +20,8 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('negateimage')->with(false)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('negateimage')->with(false)->times(3)->andReturn(true); $command = new InvertImagick(array()); $result = $command->execute($image); $this->assertTrue($result); From 772e8bae72f8738f2a59bf69718415f6c837f35b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 15:05:53 +0100 Subject: [PATCH 64/88] bugfix --- .../Image/Imagick/Commands/FitCommand.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/FitCommand.php b/src/Intervention/Image/Imagick/Commands/FitCommand.php index 1a002a778..c843593f9 100644 --- a/src/Intervention/Image/Imagick/Commands/FitCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FitCommand.php @@ -24,16 +24,17 @@ public function execute($image) $resized = clone $cropped; $resized = $resized->resize($width, $height, $constraints); - // crop image - $image->getCore()->cropImage( - $cropped->width, - $cropped->height, - $cropped->pivot->x, - $cropped->pivot->y - ); - - // resize image foreach ($image as $frame) { + + // crop image + $frame->getCore()->cropImage( + $cropped->width, + $cropped->height, + $cropped->pivot->x, + $cropped->pivot->y + ); + + // resize image $frame->getCore()->resizeImage($resized->getWidth(), $resized->getHeight(), \Imagick::FILTER_BOX, 1); $frame->getCore()->setImagePage(0,0,0,0); } From 916db1885e715dc449ad319d9e7794226fe89b77 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 15:34:01 +0100 Subject: [PATCH 65/88] bugfix --- src/Intervention/Image/Animation.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Intervention/Image/Animation.php b/src/Intervention/Image/Animation.php index 18c54dfed..53bcd1c05 100644 --- a/src/Intervention/Image/Animation.php +++ b/src/Intervention/Image/Animation.php @@ -49,7 +49,7 @@ public function getIterator() */ public function setLoops($value) { - $this->loops = intval($value); + $this->loops = $value; } /** From 645f1a5ecb0126950da11974c0f28cd1ca90846b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 18:01:27 +0100 Subject: [PATCH 66/88] added StopAnimationCommand --- .../Gd/Commands/StopAnimationCommand.php | 25 +++++++++++++ src/Intervention/Image/Gd/Container.php | 8 +++- .../Imagick/Commands/StopAnimationCommand.php | 37 +++++++++++++++++++ tests/CommandTestCase.php | 1 + tests/StopAnimationCommandTest.php | 36 ++++++++++++++++++ 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/Intervention/Image/Gd/Commands/StopAnimationCommand.php create mode 100644 src/Intervention/Image/Imagick/Commands/StopAnimationCommand.php create mode 100644 tests/StopAnimationCommandTest.php diff --git a/src/Intervention/Image/Gd/Commands/StopAnimationCommand.php b/src/Intervention/Image/Gd/Commands/StopAnimationCommand.php new file mode 100644 index 000000000..bf488af02 --- /dev/null +++ b/src/Intervention/Image/Gd/Commands/StopAnimationCommand.php @@ -0,0 +1,25 @@ +argument(0)->type('int')->value(0); + + $container = new Container; + $container->add($image->getCore($keepIndex)); + $image->setContainer($container); + + return true; + } +} diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index c9df596e8..ae43bb17a 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -19,7 +19,13 @@ public function getCore($index = null) $index = is_numeric($index) ? $index : 0; $frames = $this->getFrames(); - return $frames[$index]->getCore(); + if (array_key_exists($index, $frames)) { + return $frames[$index]->getCore(); + } + + throw new \Intervention\Image\Exception\NotFoundException( + "Animation has no index with number {$index}." + ); } /** diff --git a/src/Intervention/Image/Imagick/Commands/StopAnimationCommand.php b/src/Intervention/Image/Imagick/Commands/StopAnimationCommand.php new file mode 100644 index 000000000..29b51b236 --- /dev/null +++ b/src/Intervention/Image/Imagick/Commands/StopAnimationCommand.php @@ -0,0 +1,37 @@ +argument(0)->type('int')->value(0); + + foreach ($image as $key => $frame) { + if ($keepIndex == $key) { + break; + } + } + + $frame = $image->getDriver()->init( + $image->getCore()->getImageBlob() + ); + + // remove old core + $image->getCore()->clear(); + + // set new core + $image->setContainer($frame->getContainer()); + + return true; + } +} diff --git a/tests/CommandTestCase.php b/tests/CommandTestCase.php index 90fe1e3bd..d0cf23238 100644 --- a/tests/CommandTestCase.php +++ b/tests/CommandTestCase.php @@ -41,6 +41,7 @@ protected function getTestImage($type) $container->shouldReceive('valid')->once()->andReturn(false); $container->shouldReceive('current')->times(3)->andReturn($frame); $container->shouldReceive('next'); + $container->shouldReceive('key'); $image->shouldReceive('getIterator')->once()->andReturn($container); $image->shouldReceive('getCore')->andReturn($imagick); $imagick->shouldReceive('rewind'); diff --git a/tests/StopAnimationCommandTest.php b/tests/StopAnimationCommandTest.php new file mode 100644 index 000000000..2203bd4d1 --- /dev/null +++ b/tests/StopAnimationCommandTest.php @@ -0,0 +1,36 @@ +getTestImage('gd'); + $image->shouldReceive('setContainer')->once(); + + $command = new StopAnimationGd(array(2)); + $result = $command->execute($image); + $this->assertTrue($result); + } + + public function testImagick() + { + $newContainer = Mockery::mock('Intervention\Image\Imagick\Container'); + $frame = Mockery::mock('Intervention\Image\Image'); + $frame->shouldReceive('getContainer')->once()->andReturn($newContainer); + $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); + $driver->shouldReceive('init')->with('blob')->once()->andReturn($frame); + + $image = $this->getTestImage('imagick'); + $image->shouldReceive('getDriver')->once()->andReturn($driver); + $image->getCore()->shouldReceive('getimageblob')->once()->andReturn('blob'); + $image->getCore()->shouldReceive('clear')->once(); + $image->shouldReceive('setContainer')->with($newContainer)->once(); + + $command = new StopAnimationImagick(array(2)); + $result = $command->execute($image); + $this->assertTrue($result); + } +} From 74ecb0643b031281b851c292c4b6d7cf64f934bc Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 19:37:27 +0100 Subject: [PATCH 67/88] sharpen command animation compatible --- .../Image/Gd/Commands/SharpenCommand.php | 6 +++++- .../Image/Imagick/Commands/SharpenCommand.php | 6 +++++- tests/SharpenCommandTest.php | 12 ++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/SharpenCommand.php b/src/Intervention/Image/Gd/Commands/SharpenCommand.php index 9c1ca2063..5c4ec11a5 100644 --- a/src/Intervention/Image/Gd/Commands/SharpenCommand.php +++ b/src/Intervention/Image/Gd/Commands/SharpenCommand.php @@ -27,6 +27,10 @@ public function execute($image) ); // apply the matrix - return imageconvolution($image->getCore(), $matrix, $div, 0); + foreach ($image as $frame) { + imageconvolution($frame->getCore(), $matrix, $div, 0); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php b/src/Intervention/Image/Imagick/Commands/SharpenCommand.php index 4f2fc8c29..95a0cd0ae 100644 --- a/src/Intervention/Image/Imagick/Commands/SharpenCommand.php +++ b/src/Intervention/Image/Imagick/Commands/SharpenCommand.php @@ -14,6 +14,10 @@ public function execute($image) { $amount = $this->argument(0)->between(0, 100)->value(10); - return $image->getCore()->unsharpMaskImage(1, 1, $amount / 6.25, 0); + foreach ($image as $frame) { + $frame->getCore()->unsharpMaskImage(1, 1, $amount / 6.25, 0); + } + + return true; } } diff --git a/tests/SharpenCommandTest.php b/tests/SharpenCommandTest.php index b88869468..b1a837743 100644 --- a/tests/SharpenCommandTest.php +++ b/tests/SharpenCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\SharpenCommand as SharpenGd; use Intervention\Image\Imagick\Commands\SharpenCommand as SharpenImagick; -class SharpenCommandTest extends PHPUnit_Framework_TestCase +class SharpenCommandTest extends CommandTestCase { public function tearDown() { @@ -12,9 +12,7 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); $command = new SharpenGd(array(50)); $result = $command->execute($image); $this->assertTrue($result); @@ -22,10 +20,8 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('unsharpmaskimage')->with(1, 1, 8, 0)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('unsharpmaskimage')->with(1, 1, 8, 0)->andReturn(true); $command = new SharpenImagick(array(50)); $result = $command->execute($image); $this->assertTrue($result); From f82a5e251de78c8326180db62082386c74bf1873 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 19:41:26 +0100 Subject: [PATCH 68/88] rotate command animation compatible --- .../Image/Gd/Commands/RotateCommand.php | 4 +++- .../Image/Imagick/Commands/RotateCommand.php | 4 +++- tests/RotateCommandTest.php | 13 ++++--------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/RotateCommand.php b/src/Intervention/Image/Gd/Commands/RotateCommand.php index 306fef1b5..f1fee9460 100644 --- a/src/Intervention/Image/Gd/Commands/RotateCommand.php +++ b/src/Intervention/Image/Gd/Commands/RotateCommand.php @@ -19,7 +19,9 @@ public function execute($image) $color = new Color($color); // rotate image - $image->setCore(imagerotate($image->getCore(), $angle, $color->getInt())); + foreach ($image as $frame) { + $frame->setCore(imagerotate($frame->getCore(), $angle, $color->getInt())); + } return true; } diff --git a/src/Intervention/Image/Imagick/Commands/RotateCommand.php b/src/Intervention/Image/Imagick/Commands/RotateCommand.php index 7d2a81de1..68af569e3 100644 --- a/src/Intervention/Image/Imagick/Commands/RotateCommand.php +++ b/src/Intervention/Image/Imagick/Commands/RotateCommand.php @@ -19,7 +19,9 @@ public function execute($image) $color = new Color($color); // rotate image - $image->getCore()->rotateImage($color->getPixel(), ($angle * -1)); + foreach ($image as $frame) { + $frame->getCore()->rotateImage($color->getPixel(), ($angle * -1)); + } return true; } diff --git a/tests/RotateCommandTest.php b/tests/RotateCommandTest.php index dedf8a0fc..31036a722 100644 --- a/tests/RotateCommandTest.php +++ b/tests/RotateCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\RotateCommand as RotateGd; use Intervention\Image\Imagick\Commands\RotateCommand as RotateImagick; -class RotateCommandTest extends PHPUnit_Framework_TestCase +class RotateCommandTest extends CommandTestCase { public function tearDown() { @@ -12,10 +12,7 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); $command = new RotateGd(array(45, '#b53717')); $result = $command->execute($image); $this->assertTrue($result); @@ -23,11 +20,9 @@ public function testGd() public function testImagick() { + $image = $this->getTestImage('imagick'); $pixel = Mockery::mock('ImagickPixel', array('#b53717')); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('rotateimage')->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image->getCore()->shouldReceive('rotateimage')->andReturn(true); $command = new RotateImagick(array(45, '#b53717')); $result = $command->execute($image); $this->assertTrue($result); From 1856923551d1b236bb36017440a0f1c3feb56a95 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 19:45:41 +0100 Subject: [PATCH 69/88] pixelate command animation compatible --- .../Image/Gd/Commands/PixelateCommand.php | 6 +++++- .../Image/Imagick/Commands/PixelateCommand.php | 6 ++++-- tests/PixelateCommandTest.php | 14 +++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/PixelateCommand.php b/src/Intervention/Image/Gd/Commands/PixelateCommand.php index 2e2093d7d..b1c8c2165 100644 --- a/src/Intervention/Image/Gd/Commands/PixelateCommand.php +++ b/src/Intervention/Image/Gd/Commands/PixelateCommand.php @@ -14,6 +14,10 @@ public function execute($image) { $size = $this->argument(0)->type('digit')->value(10); - return imagefilter($image->getCore(), IMG_FILTER_PIXELATE, $size, true); + foreach ($image as $frame) { + imagefilter($frame->getCore(), IMG_FILTER_PIXELATE, $size, true); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php index 75f2218f5..cee95de97 100644 --- a/src/Intervention/Image/Imagick/Commands/PixelateCommand.php +++ b/src/Intervention/Image/Imagick/Commands/PixelateCommand.php @@ -17,8 +17,10 @@ public function execute($image) $width = $image->getWidth(); $height = $image->getHeight(); - $image->getCore()->scaleImage(max(1, ($width / $size)), max(1, ($height / $size))); - $image->getCore()->scaleImage($width, $height); + foreach ($image as $frame) { + $frame->getCore()->scaleImage(max(1, ($width / $size)), max(1, ($height / $size))); + $frame->getCore()->scaleImage($width, $height); + } return true; } diff --git a/tests/PixelateCommandTest.php b/tests/PixelateCommandTest.php index 76314ba4f..e9a6f7f24 100644 --- a/tests/PixelateCommandTest.php +++ b/tests/PixelateCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\PixelateCommand as PixelateGd; use Intervention\Image\Imagick\Commands\PixelateCommand as PixelateImagick; -class PixelateCommandTest extends PHPUnit_Framework_TestCase +class PixelateCommandTest extends CommandTestCase { public function tearDown() { @@ -12,9 +12,7 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); $command = new PixelateGd(array(10)); $result = $command->execute($image); $this->assertTrue($result); @@ -22,11 +20,9 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('scaleimage')->with(80, 60)->once()->andReturn(true); - $imagick->shouldReceive('scaleimage')->with(800, 600)->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('scaleimage')->with(80, 60)->times(3)->andReturn(true); + $image->getCore()->shouldReceive('scaleimage')->with(800, 600)->times(3)->andReturn(true); $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); $command = new PixelateImagick(array(10)); From 976821e54ad6ae273b7a6e5f4425bd7fb7196ced Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 19:59:39 +0100 Subject: [PATCH 70/88] pixel command animation compatible --- src/Intervention/Image/Gd/Commands/PixelCommand.php | 6 +++++- .../Image/Imagick/Commands/PixelCommand.php | 6 +++++- tests/PixelCommandTest.php | 12 ++++-------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/PixelCommand.php b/src/Intervention/Image/Gd/Commands/PixelCommand.php index a9ac3ec99..dc255f1f5 100644 --- a/src/Intervention/Image/Gd/Commands/PixelCommand.php +++ b/src/Intervention/Image/Gd/Commands/PixelCommand.php @@ -19,6 +19,10 @@ public function execute($image) $x = $this->argument(1)->type('digit')->required()->value(); $y = $this->argument(2)->type('digit')->required()->value(); - return imagesetpixel($image->getCore(), $x, $y, $color->getInt()); + foreach ($image as $frame) { + imagesetpixel($frame->getCore(), $x, $y, $color->getInt()); + } + + return true; } } diff --git a/src/Intervention/Image/Imagick/Commands/PixelCommand.php b/src/Intervention/Image/Imagick/Commands/PixelCommand.php index 8a467f798..ca7a8d46b 100644 --- a/src/Intervention/Image/Imagick/Commands/PixelCommand.php +++ b/src/Intervention/Image/Imagick/Commands/PixelCommand.php @@ -25,6 +25,10 @@ public function execute($image) $draw->point($x, $y); // apply pixel - return $image->getCore()->drawImage($draw); + foreach ($image as $frame) { + $frame->getCore()->drawImage($draw); + } + + return true; } } diff --git a/tests/PixelCommandTest.php b/tests/PixelCommandTest.php index e1e2dcfb6..fd08e09f3 100644 --- a/tests/PixelCommandTest.php +++ b/tests/PixelCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\PixelCommand as PixelGd; use Intervention\Image\Imagick\Commands\PixelCommand as PixelImagick; -class PixelCommandTest extends PHPUnit_Framework_TestCase +class PixelCommandTest extends CommandTestCase { public function tearDown() { @@ -12,9 +12,7 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $image = $this->getTestImage('gd'); $command = new PixelGd(array('#b53717', 10, 20)); $result = $command->execute($image); $this->assertTrue($result); @@ -22,10 +20,8 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('drawimage')->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3)->andReturn(true); $command = new PixelImagick(array('#b53717', 10, 20)); $result = $command->execute($image); $this->assertTrue($result); From 44f33f76da2b12bb0ea3454fa46b99e48fa1db24 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 25 Jan 2015 20:51:05 +0100 Subject: [PATCH 71/88] shape drawing animation compatible --- .../Image/Commands/CircleCommand.php | 3 +- src/Intervention/Image/Gd/Helper.php | 2 +- .../Image/Gd/Shapes/EllipseShape.php | 25 +++++++++--- .../Image/Gd/Shapes/LineShape.php | 5 ++- .../Image/Gd/Shapes/PolygonShape.php | 25 ++++++++++-- .../Image/Gd/Shapes/RectangleShape.php | 39 +++++++++++++++++-- .../Image/Imagick/Shapes/EllipseShape.php | 4 +- .../Image/Imagick/Shapes/LineShape.php | 5 ++- .../Image/Imagick/Shapes/PolygonShape.php | 4 +- .../Image/Imagick/Shapes/RectangleShape.php | 4 +- tests/CircleCommandTest.php | 15 ++++--- tests/CircleShapeTest.php | 12 ++---- tests/EllipseCommandTest.php | 12 ++---- tests/EllipseShapeTest.php | 12 ++---- tests/LineCommandTest.php | 13 +++---- tests/LineShapeTest.php | 12 ++---- tests/PolygonCommandTest.php | 12 ++---- tests/PolygonShapeTest.php | 12 ++---- tests/RectangleCommandTest.php | 12 ++---- tests/RectangleShapeTest.php | 12 ++---- 20 files changed, 140 insertions(+), 100 deletions(-) diff --git a/src/Intervention/Image/Commands/CircleCommand.php b/src/Intervention/Image/Commands/CircleCommand.php index 993b56731..91f3db326 100644 --- a/src/Intervention/Image/Commands/CircleCommand.php +++ b/src/Intervention/Image/Commands/CircleCommand.php @@ -20,7 +20,8 @@ public function execute($image) $callback = $this->argument(3)->type('closure')->value(); $circle_classname = sprintf('\Intervention\Image\%s\Shapes\CircleShape', - $image->getDriver()->getDriverName()); + $image->getDriver()->getDriverName() + ); $circle = new $circle_classname($diameter); diff --git a/src/Intervention/Image/Gd/Helper.php b/src/Intervention/Image/Gd/Helper.php index b91ec3ffe..a92c574f3 100644 --- a/src/Intervention/Image/Gd/Helper.php +++ b/src/Intervention/Image/Gd/Helper.php @@ -45,7 +45,7 @@ public static function cloneResource($resource) $width = imagesx($resource); $height = imagesy($resource); $clone = imagecreatetruecolor($width, $height); - imagealphablending($clone, false); + imagealphablending($clone, true); imagesavealpha($clone, true); imagecopy($clone, $resource, 0, 0, 0, 0, $width, $height); diff --git a/src/Intervention/Image/Gd/Shapes/EllipseShape.php b/src/Intervention/Image/Gd/Shapes/EllipseShape.php index f5c8b531a..d396463f1 100644 --- a/src/Intervention/Image/Gd/Shapes/EllipseShape.php +++ b/src/Intervention/Image/Gd/Shapes/EllipseShape.php @@ -42,23 +42,36 @@ public function __construct($width = null, $height = null) * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) + { + foreach ($image as $frame) { + $this->applyToResource($frame->getCore(), $x, $y); + } + + return true; + } + + /** + * Draw ellipse on given gd resource + * + * @param resource $resource + * @return boolean + */ + private function applyToResource($resource, $x, $y) { // parse background color $background = new Color($this->background); if ($this->hasBorder()) { // slightly smaller ellipse to keep 1px bordered edges clean - imagefilledellipse($image->getCore(), $x, $y, $this->width-1, $this->height-1, $background->getInt()); + imagefilledellipse($resource, $x, $y, $this->width-1, $this->height-1, $background->getInt()); $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); + imagesetthickness($resource, $this->border_width); // gd's imageellipse doesn't respect imagesetthickness so i use imagearc with 359.9 degrees here - imagearc($image->getCore(), $x, $y, $this->width, $this->height, 0, 359.99, $border_color->getInt()); + imagearc($resource, $x, $y, $this->width, $this->height, 0, 359.99, $border_color->getInt()); } else { - imagefilledellipse($image->getCore(), $x, $y, $this->width, $this->height, $background->getInt()); + imagefilledellipse($resource, $x, $y, $this->width, $this->height, $background->getInt()); } - - return true; } } diff --git a/src/Intervention/Image/Gd/Shapes/LineShape.php b/src/Intervention/Image/Gd/Shapes/LineShape.php index c3c1ea36d..5e2c8aacd 100644 --- a/src/Intervention/Image/Gd/Shapes/LineShape.php +++ b/src/Intervention/Image/Gd/Shapes/LineShape.php @@ -82,7 +82,10 @@ public function width($width) public function applyToImage(Image $image, $x = 0, $y = 0) { $color = new Color($this->color); - imageline($image->getCore(), $x, $y, $this->x, $this->y, $color->getInt()); + + foreach ($image as $frame) { + imageline($frame->getCore(), $x, $y, $this->x, $this->y, $color->getInt()); + } return true; } diff --git a/src/Intervention/Image/Gd/Shapes/PolygonShape.php b/src/Intervention/Image/Gd/Shapes/PolygonShape.php index b64fb5d4c..14129b3a2 100644 --- a/src/Intervention/Image/Gd/Shapes/PolygonShape.php +++ b/src/Intervention/Image/Gd/Shapes/PolygonShape.php @@ -33,16 +33,33 @@ public function __construct($points) * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) + { + foreach ($image as $frame) { + $this->applyToResource($frame->getCore(), $x, $y); + } + + return true; + } + + /** + * Draw polygon on given GD resource + * + * @param resource $resource + * @param integer $x + * @param interger $y + * @return boolean + */ + private function applyToResource($resource, $x, $y) { $background = new Color($this->background); - imagefilledpolygon($image->getCore(), $this->points, intval(count($this->points) / 2), $background->getInt()); + imagefilledpolygon($resource, $this->points, intval(count($this->points) / 2), $background->getInt()); if ($this->hasBorder()) { $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - imagepolygon($image->getCore(), $this->points, intval(count($this->points) / 2), $border_color->getInt()); + imagesetthickness($resource, $this->border_width); + imagepolygon($resource, $this->points, intval(count($this->points) / 2), $border_color->getInt()); } - + return true; } } diff --git a/src/Intervention/Image/Gd/Shapes/RectangleShape.php b/src/Intervention/Image/Gd/Shapes/RectangleShape.php index c25ebe5eb..111da144a 100644 --- a/src/Intervention/Image/Gd/Shapes/RectangleShape.php +++ b/src/Intervention/Image/Gd/Shapes/RectangleShape.php @@ -60,14 +60,47 @@ public function __construct($x1 = null, $y1 = null, $x2 = null, $y2 = null) * @return boolean */ public function applyToImage(Image $image, $x = 0, $y = 0) + { + foreach ($image as $frame) { + $this->applyToResource($frame->getCore(), $x, $y); + } + + return true; + } + + /** + * Draw rectangle to given GD resource + * + * @param resource $resource + * @param integer $x + * @param integer $y + * @return boolean + */ + private function applyToResource($resource, $x, $y) { $background = new Color($this->background); - imagefilledrectangle($image->getCore(), $this->x1, $this->y1, $this->x2, $this->y2, $background->getInt()); + + imagefilledrectangle( + $resource, + $this->x1, + $this->y1, + $this->x2, + $this->y2, + $background->getInt() + ); if ($this->hasBorder()) { + $border_color = new Color($this->border_color); - imagesetthickness($image->getCore(), $this->border_width); - imagerectangle($image->getCore(), $this->x1, $this->y1, $this->x2, $this->y2, $border_color->getInt()); + imagesetthickness($resource, $this->border_width); + imagerectangle( + $resource, + $this->x1, + $this->y1, + $this->x2, + $this->y2, + $border_color->getInt() + ); } return true; diff --git a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php index c6ca147a5..039d94d38 100644 --- a/src/Intervention/Image/Imagick/Shapes/EllipseShape.php +++ b/src/Intervention/Image/Imagick/Shapes/EllipseShape.php @@ -58,7 +58,9 @@ public function applyToImage(Image $image, $x = 0, $y = 0) $circle->ellipse($x, $y, $this->width / 2, $this->height / 2, 0, 360); - $image->getCore()->drawImage($circle); + foreach ($image as $frame) { + $frame->getCore()->drawImage($circle); + } return true; } diff --git a/src/Intervention/Image/Imagick/Shapes/LineShape.php b/src/Intervention/Image/Imagick/Shapes/LineShape.php index 6e22f93a3..aa028137e 100644 --- a/src/Intervention/Image/Imagick/Shapes/LineShape.php +++ b/src/Intervention/Image/Imagick/Shapes/LineShape.php @@ -86,7 +86,10 @@ public function applyToImage(Image $image, $x = 0, $y = 0) $line->setStrokeWidth($this->width); $line->line($this->x, $this->y, $x, $y); - $image->getCore()->drawImage($line); + + foreach ($image as $frame) { + $frame->getCore()->drawImage($line); + } return true; } diff --git a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php index dec4a3396..b5d1e20ad 100644 --- a/src/Intervention/Image/Imagick/Shapes/PolygonShape.php +++ b/src/Intervention/Image/Imagick/Shapes/PolygonShape.php @@ -49,7 +49,9 @@ public function applyToImage(Image $image, $x = 0, $y = 0) $polygon->polygon($this->points); - $image->getCore()->drawImage($polygon); + foreach ($image as $frame) { + $frame->getCore()->drawImage($polygon); + } return true; } diff --git a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php index ad65ddf07..c12c990be 100644 --- a/src/Intervention/Image/Imagick/Shapes/RectangleShape.php +++ b/src/Intervention/Image/Imagick/Shapes/RectangleShape.php @@ -76,7 +76,9 @@ public function applyToImage(Image $image, $x = 0, $y = 0) $rectangle->rectangle($this->x1, $this->y1, $this->x2, $this->y2); - $image->getCore()->drawImage($rectangle); + foreach ($image as $frame) { + $frame->getCore()->drawImage($rectangle); + } return true; } diff --git a/tests/CircleCommandTest.php b/tests/CircleCommandTest.php index 241440ec4..fadd1f8ec 100644 --- a/tests/CircleCommandTest.php +++ b/tests/CircleCommandTest.php @@ -2,7 +2,7 @@ use Intervention\Image\Commands\CircleCommand; -class CircleCommandTest extends PHPUnit_Framework_TestCase +class CircleCommandTest extends CommandTestCase { public function tearDown() { @@ -11,12 +11,12 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $image = $this->getTestImage('gd'); + $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $command = new CircleCommand(array(250, 10, 20)); $result = $command->execute($image); $this->assertTrue($result); @@ -25,13 +25,12 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); + $image = $this->getTestImage('imagick'); + + $image->getCore()->shouldReceive('drawimage')->times(3); $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); $command = new CircleCommand(array(25, 10, 20)); $result = $command->execute($image); diff --git a/tests/CircleShapeTest.php b/tests/CircleShapeTest.php index 71b40d23a..8c5777730 100644 --- a/tests/CircleShapeTest.php +++ b/tests/CircleShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\CircleShape as CircleGd; use Intervention\Image\Imagick\Shapes\CircleShape as CircleImagick; -class CircleShapeTest extends PHPUnit_Framework_TestCase +class CircleShapeTest extends CommandTestCase { public function tearDown() { @@ -20,9 +20,7 @@ public function testGdConstructor() public function testGdApplyToImage() { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('gd'); $circle = new CircleGd(250); $result = $circle->applyToImage($image, 10, 20); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\CircleShape', $circle); @@ -38,10 +36,8 @@ public function testImagickConstructor() public function testImagickApplyToImage() { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $circle = new CircleImagick(250); $result = $circle->applyToImage($image, 10, 20); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\CircleShape', $circle); diff --git a/tests/EllipseCommandTest.php b/tests/EllipseCommandTest.php index 8ac42e007..13ce2bbb9 100644 --- a/tests/EllipseCommandTest.php +++ b/tests/EllipseCommandTest.php @@ -2,7 +2,7 @@ use Intervention\Image\Commands\EllipseCommand; -class EllipseCommandTest extends PHPUnit_Framework_TestCase +class EllipseCommandTest extends CommandTestCase { public function tearDown() { @@ -11,12 +11,10 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $image = $this->getTestImage('gd'); $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); $command = new EllipseCommand(array(250, 150, 10, 20)); $result = $command->execute($image); $this->assertTrue($result); @@ -25,13 +23,11 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); $command = new EllipseCommand(array(250, 150, 10, 20)); $result = $command->execute($image); diff --git a/tests/EllipseShapeTest.php b/tests/EllipseShapeTest.php index 773373f69..c772cb3f9 100644 --- a/tests/EllipseShapeTest.php +++ b/tests/EllipseShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\EllipseShape as EllipseGd; use Intervention\Image\Imagick\Shapes\EllipseShape as EllipseImagick; -class EllipseShapeTest extends PHPUnit_Framework_TestCase +class EllipseShapeTest extends CommandTestCase { public function tearDown() { @@ -21,9 +21,7 @@ public function testGdConstructor() public function testGdApplyToImage() { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('gd'); $ellipse = new EllipseGd(250, 150); $result = $ellipse->applyToImage($image, 10, 20); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\EllipseShape', $ellipse); @@ -41,10 +39,8 @@ public function testImagickConstructor() public function testImagickApplyToImage() { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $ellipse = new EllipseImagick(250, 150); $result = $ellipse->applyToImage($image, 10, 20); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\EllipseShape', $ellipse); diff --git a/tests/LineCommandTest.php b/tests/LineCommandTest.php index 16495fd11..5ba2c5ce9 100644 --- a/tests/LineCommandTest.php +++ b/tests/LineCommandTest.php @@ -2,7 +2,7 @@ use Intervention\Image\Commands\LineCommand; -class LineCommandTest extends PHPUnit_Framework_TestCase +class LineCommandTest extends CommandTestCase { public function tearDown() { @@ -11,12 +11,11 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $image = $this->getTestImage('gd'); $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); + $command = new LineCommand(array(10, 15, 100, 150)); $result = $command->execute($image); $this->assertTrue($result); @@ -25,13 +24,11 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); $command = new LineCommand(array(10, 15, 100, 150)); $result = $command->execute($image); diff --git a/tests/LineShapeTest.php b/tests/LineShapeTest.php index 506fd4ed0..7274caaa7 100644 --- a/tests/LineShapeTest.php +++ b/tests/LineShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\LineShape as LineGd; use Intervention\Image\Imagick\Shapes\LineShape as LineImagick; -class LineShapeTest extends PHPUnit_Framework_TestCase +class LineShapeTest extends CommandTestCase { public function tearDown() { @@ -28,19 +28,15 @@ public function testConstructor() public function testApplyToImage() { // gd - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('gd'); $line = new LineGd(10, 15); $result = $line->applyToImage($image, 100, 200); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\LineShape', $line); $this->assertTrue($result); // imagick - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $line = new LineImagick(10, 15); $result = $line->applyToImage($image, 100, 200); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\LineShape', $line); diff --git a/tests/PolygonCommandTest.php b/tests/PolygonCommandTest.php index c3eaf7165..219b77a49 100644 --- a/tests/PolygonCommandTest.php +++ b/tests/PolygonCommandTest.php @@ -2,7 +2,7 @@ use Intervention\Image\Commands\PolygonCommand; -class PolygonCommandTest extends PHPUnit_Framework_TestCase +class PolygonCommandTest extends CommandTestCase { public function tearDown() { @@ -12,12 +12,10 @@ public function tearDown() public function testGd() { $points = array(1, 2, 3, 4, 5, 6); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $image = $this->getTestImage('gd'); $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); $command = new PolygonCommand(array($points)); $result = $command->execute($image); $this->assertTrue($result); @@ -27,13 +25,11 @@ public function testGd() public function testImagick() { $points = array(1, 2, 3, 4, 5, 6); - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); $command = new PolygonCommand(array($points)); $result = $command->execute($image); diff --git a/tests/PolygonShapeTest.php b/tests/PolygonShapeTest.php index 4f87b2b30..6f2d4c29e 100644 --- a/tests/PolygonShapeTest.php +++ b/tests/PolygonShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\PolygonShape as PolygonGd; use Intervention\Image\Imagick\Shapes\PolygonShape as PolygonImagick; -class PolygonShapeTest extends PHPUnit_Framework_TestCase +class PolygonShapeTest extends CommandTestCase { public function tearDown() { @@ -20,9 +20,7 @@ public function testGdConstructor() public function testGdApplyToImage() { - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('gd'); $polygon = new PolygonGd(array(1, 2, 3, 4, 5, 6)); $result = $polygon->applyToImage($image); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); @@ -43,10 +41,8 @@ public function testImagickConstructor() public function testImagickApplyToImage() { - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $polygon = new PolygonImagick(array(1, 2, 3, 4, 5, 6)); $result = $polygon->applyToImage($image); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); diff --git a/tests/RectangleCommandTest.php b/tests/RectangleCommandTest.php index d3742f132..f2a95388c 100644 --- a/tests/RectangleCommandTest.php +++ b/tests/RectangleCommandTest.php @@ -2,7 +2,7 @@ use Intervention\Image\Commands\RectangleCommand; -class RectangleCommandTest extends PHPUnit_Framework_TestCase +class RectangleCommandTest extends CommandTestCase { public function tearDown() { @@ -11,12 +11,10 @@ public function tearDown() public function testGd() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); + $image = $this->getTestImage('gd'); $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); $command = new RectangleCommand(array(10, 15, 100, 150)); $result = $command->execute($image); $this->assertTrue($result); @@ -25,13 +23,11 @@ public function testGd() public function testImagick() { - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('drawimage'); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image = Mockery::mock('\Intervention\Image\Image'); $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($imagick); $command = new RectangleCommand(array(10, 15, 100, 150)); $result = $command->execute($image); diff --git a/tests/RectangleShapeTest.php b/tests/RectangleShapeTest.php index 1f652ab0d..bc82ab7a3 100644 --- a/tests/RectangleShapeTest.php +++ b/tests/RectangleShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\RectangleShape as RectangleGd; use Intervention\Image\Imagick\Shapes\RectangleShape as RectangleImagick; -class RectangleShapeTest extends PHPUnit_Framework_TestCase +class RectangleShapeTest extends CommandTestCase { public function tearDown() { @@ -32,19 +32,15 @@ public function testConstructor() public function testApplyToImage() { // gd - $core = imagecreatetruecolor(300, 200); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('gd'); $rectangle = new RectangleGd(10, 15, 100, 150); $result = $rectangle->applyToImage($image, 10, 20); $this->assertInstanceOf('Intervention\Image\Gd\Shapes\RectangleShape', $rectangle); $this->assertTrue($result); // imagick - $core = Mockery::mock('\Imagick'); - $core->shouldReceive('drawimage')->once(); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($core); + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('drawimage')->times(3); $rectangle = new RectangleImagick(10, 15, 100, 150); $result = $rectangle->applyToImage($image, 10, 20); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\RectangleShape', $rectangle); From cdbfefd624dd19f81cecf64958e823cab9a8dba1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Wed, 28 Jan 2015 18:17:26 +0100 Subject: [PATCH 72/88] fill command --- .../Image/Gd/Commands/FillCommand.php | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/src/Intervention/Image/Gd/Commands/FillCommand.php b/src/Intervention/Image/Gd/Commands/FillCommand.php index aaecb7fb9..e42f579e4 100644 --- a/src/Intervention/Image/Gd/Commands/FillCommand.php +++ b/src/Intervention/Image/Gd/Commands/FillCommand.php @@ -19,17 +19,13 @@ public function execute($image) $x = $this->argument(1)->type('digit')->value(); $y = $this->argument(2)->type('digit')->value(); - $width = $image->getWidth(); - $height = $image->getHeight(); - $resource = $image->getCore(); + $width = imagesx($image->getCore()); + $height = imagesy($image->getCore()); try { // set image tile filling - $source = new Decoder; - $tile = $source->init($filling); - imagesettile($image->getCore(), $tile->getCore()); - $filling = IMG_COLOR_TILED; + $tile = $image->getDriver()->init($filling); } catch (\Intervention\Image\Exception\NotReadableException $e) { @@ -38,27 +34,45 @@ public function execute($image) $filling = $color->getInt(); } - imagealphablending($resource, true); - if (is_int($x) && is_int($y)) { + foreach ($image as $frame) { - // resource should be visible through transparency - $base = $image->getDriver()->newImage($width, $height)->getCore(); - imagecopy($base, $resource, 0, 0, 0, 0, $width, $height); + if (isset($tile)) { + imagesettile($frame->getCore(), $tile->getCore()); + $filling = IMG_COLOR_TILED; + } - // floodfill if exact position is defined - imagefill($resource, $x, $y, $filling); + imagealphablending($frame->getCore(), true); - // copy filled original over base - imagecopy($base, $resource, 0, 0, 0, 0, $width, $height); + if (is_int($x) && is_int($y)) { - // set base as new resource-core - $image->setCore($base); - imagedestroy($resource); + // resource should be visible through transparency + $base = $image->getDriver()->newImage($width, $height)->getCore(); + imagecopy($base, $frame->getCore(), 0, 0, 0, 0, $width, $height); - } else { - // fill whole image otherwise - imagefilledrectangle($resource, 0, 0, $width - 1, $height - 1, $filling); + // floodfill if exact position is defined + imagefill($frame->getCore(), $x, $y, $filling); + + // copy filled original over base + imagecopy($base, $frame->getCore(), 0, 0, 0, 0, $width, $height); + + // set base as new resource-core + imagedestroy($frame->getCore()); + $frame->setCore($base); + + } else { + + // fill whole image otherwise + imagefilledrectangle( + $frame->getCore(), + 0, + 0, + ($width - 1), + ($height - 1), + $filling + ); + + } } isset($tile) ? imagedestroy($tile->getCore()) : null; From 18219c50b83d75e2c446f3e3d1324bff36e86bed Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 8 Feb 2015 12:47:48 +0100 Subject: [PATCH 73/88] animation compatible fill command --- .../Image/Imagick/Commands/FillCommand.php | 113 +++++++++++------- tests/FillCommandTest.php | 57 +++++---- 2 files changed, 99 insertions(+), 71 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/FillCommand.php b/src/Intervention/Image/Imagick/Commands/FillCommand.php index 15a2adaf5..c7a56e6f9 100644 --- a/src/Intervention/Image/Imagick/Commands/FillCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FillCommand.php @@ -20,18 +20,10 @@ public function execute($image) $x = $this->argument(1)->type('digit')->value(); $y = $this->argument(2)->type('digit')->value(); - $imagick = $image->getCore(); + $width = $image->width(); + $height = $image->height(); - try { - // set image filling - $source = new Decoder; - $filling = $source->init($filling); - - } catch (\Intervention\Image\Exception\NotReadableException $e) { - - // set solid color filling - $filling = new Color($filling); - } + $filling = $this->decodeFilling($filling); // flood fill if coordinates are set if (is_int($x) && is_int($y)) { @@ -39,65 +31,102 @@ public function execute($image) // flood fill with texture if ($filling instanceof Image) { - // create tile - $tile = clone $image->getCore(); + foreach ($image as $frame) { + // create tile + $tile = clone $frame->getCore()->getImage(); - // mask away color at position - $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); + // mask away color at position + $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - // create canvas - $canvas = clone $image->getCore(); + // create canvas + $canvas = clone $frame->getCore()->getImage(); - // fill canvas with texture - $canvas = $canvas->textureImage($filling->getCore()); + // fill canvas with texture + $canvas = $canvas->textureImage($filling->getCore()); - // merge canvas and tile - $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); + // merge canvas and tile + $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); - // replace image core - $image->setCore($canvas); + // replace image core + $frame->getCore()->setImage($canvas); + } // flood fill with color } elseif ($filling instanceof Color) { - // create canvas with filling - $canvas = new \Imagick; - $canvas->newImage($image->getWidth(), $image->getHeight(), $filling->getPixel(), 'png'); + foreach ($image as $frame) { + // create tile + $tile = clone $frame->getCore()->getImage(); + + // mask away color at position + $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); + + // create canvas filled with color + $canvas = clone $frame->getCore()->getImage(); - // create tile to put on top - $tile = clone $image->getCore(); + // setup draw object + $draw = new \ImagickDraw(); + $draw->setFillColor($filling->getPixel()); + $draw->rectangle(0, 0, $width, $height); - // mask away color at pos. - $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); + // fill canvas with color + $canvas->drawImage($draw); - // save alpha channel of original image - $alpha = clone $image->getCore(); + // merge canvas and tile + $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); - // merge original with canvas and tile - $image->getCore()->compositeImage($canvas, \Imagick::COMPOSITE_DEFAULT, 0, 0); - $image->getCore()->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); + // replace image core + $frame->getCore()->setImage($canvas); - // restore alpha channel of original image - $image->getCore()->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); + } } } else { if ($filling instanceof Image) { - // fill whole image with texture - $image->setCore($image->getCore()->textureImage($filling->getCore())); + // fill each frame with texture + foreach ($image as $frame) { + $filled = $frame->getCore()->textureImage($filling->getCore()); + $frame->getCore()->setImage($filled); + } } elseif ($filling instanceof Color) { - // fill whole image with color + // setup draw object $draw = new \ImagickDraw(); $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $image->getWidth(), $image->getHeight()); - $image->getCore()->drawImage($draw); + $draw->rectangle(0, 0, $width, $height); + + // fill each frame with color + foreach ($image as $frame) { + $frame->getCore()->drawImage($draw); + } } } return true; } + + /** + * Decodes given filling value into Image or Color object + * + * @param mixed $value + * @return Decoder|Color + */ + private function decodeFilling($value) + { + try { + // set image filling + $source = new Decoder; + $filling = $source->init($value); + + } catch (\Intervention\Image\Exception\NotReadableException $e) { + + // set solid color filling + $filling = new Color($value); + } + + return $filling; + } } diff --git a/tests/FillCommandTest.php b/tests/FillCommandTest.php index 6a851fc29..00e28320d 100644 --- a/tests/FillCommandTest.php +++ b/tests/FillCommandTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Commands\FillCommand as FillGd; use Intervention\Image\Imagick\Commands\FillCommand as FillImagick; -class FillCommandTest extends PHPUnit_Framework_TestCase +class FillCommandTest extends CommandTestCase { public function tearDown() { @@ -12,11 +12,11 @@ public function tearDown() public function testGdFill() { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); + $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); + $image = $this->getTestImage('gd'); + $image->shouldReceive('getDriver')->andReturn($driver); + $driver->shouldReceive('init')->with('666666')->once()->andReturn($image); + $command = new FillGd(array('666666')); $result = $command->execute($image); $this->assertTrue($result); @@ -25,27 +25,23 @@ public function testGdFill() public function testGdFillWithCoordinates() { $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('setCore')->once(); + $image = $this->getTestImage('gd'); + $image->shouldReceive('getDriver')->andReturn($driver); + $driver->shouldReceive('init')->with('666666')->once()->andReturn($image); $driver->shouldReceive('newImage')->with(800, 600)->once()->andReturn($image); - $command = new FillGd(array('#666666', 0, 0)); + + $command = new FillGd(array('666666', 1, 1)); $result = $command->execute($image); $this->assertTrue($result); } public function testImagickFill() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('drawimage')->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->andReturn($imagick); + $image = $this->getTestImage('imagick'); + $image->shouldReceive('width')->andReturn(800); + $image->shouldReceive('height')->andReturn(600); + $image->getCore()->shouldReceive('drawimage'); + $command = new FillImagick(array('666666')); $result = $command->execute($image); $this->assertTrue($result); @@ -53,15 +49,18 @@ public function testImagickFill() public function testImagickFillWithCoordinates() { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->once()->andReturn('#000000'); - $imagick->shouldReceive('transparentpaintimage')->once()->andReturn(true); - $imagick->shouldReceive('compositeimage')->times(3)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->andReturn($imagick); - $image->shouldReceive('getWidth')->andReturn(800); - $image->shouldReceive('getHeight')->andReturn(600); - $command = new FillImagick(array('666666', 0, 0)); + $image = $this->getTestImage('imagick'); + $image->shouldReceive('width')->andReturn(800); + $image->shouldReceive('height')->andReturn(600); + $image->getCore()->shouldReceive('getimage')->andReturn($image->getCore()); + $pixelcolor = Mockery::mock('ImagickPixel'); + $image->getCore()->shouldReceive('getimagepixelcolor')->with(1, 1)->andReturn($pixelcolor); + $image->getCore()->shouldReceive('transparentpaintimage')->with($pixelcolor, 0, 0, false); + $image->getCore()->shouldReceive('drawimage')->times(3); + $image->getCore()->shouldReceive('compositeimage')->times(3); + $image->getCore()->shouldReceive('setimage')->times(3); + + $command = new FillImagick(array('666666', 1, 1)); $result = $command->execute($image); $this->assertTrue($result); } From 140c0ace6c36cf92aa4be314e5a695c8ef626e02 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 9 Feb 2015 17:08:55 +0100 Subject: [PATCH 74/88] fill command --- .../Image/Imagick/Commands/FillCommand.php | 53 ++++++++++--------- tests/FillCommandTest.php | 5 +- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/FillCommand.php b/src/Intervention/Image/Imagick/Commands/FillCommand.php index c7a56e6f9..62250d4b2 100644 --- a/src/Intervention/Image/Imagick/Commands/FillCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FillCommand.php @@ -35,49 +35,50 @@ public function execute($image) // create tile $tile = clone $frame->getCore()->getImage(); + $alpha = false; + + if ($tile->getImageAlphaChannel() !== \Imagick::ALPHACHANNEL_UNDEFINED) { + // clone alpha channel + $alpha = clone $frame->getCore()->getImage(); + } + // mask away color at position $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - // create canvas - $canvas = clone $frame->getCore()->getImage(); - // fill canvas with texture - $canvas = $canvas->textureImage($filling->getCore()); + $canvas = $frame->getCore()->textureImage($filling->getCore()); // merge canvas and tile $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); + if ($alpha) { + // restore alpha channel of original image + $canvas->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); + } + // replace image core $frame->getCore()->setImage($canvas); + + $tile->clear(); + $canvas->clear(); } // flood fill with color } elseif ($filling instanceof Color) { foreach ($image as $frame) { - // create tile - $tile = clone $frame->getCore()->getImage(); - - // mask away color at position - $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - - // create canvas filled with color - $canvas = clone $frame->getCore()->getImage(); - - // setup draw object - $draw = new \ImagickDraw(); - $draw->setFillColor($filling->getPixel()); - $draw->rectangle(0, 0, $width, $height); - - // fill canvas with color - $canvas->drawImage($draw); - - // merge canvas and tile - $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); - - // replace image core - $frame->getCore()->setImage($canvas); + $target = $frame->getCore()->getImagePixelColor($x, $y); + + $frame->getCore()->floodFillPaintImage( + $filling->getPixel(), + 0, + $target, + $x, + $y, + false + ); + } } diff --git a/tests/FillCommandTest.php b/tests/FillCommandTest.php index 00e28320d..ce6de2c48 100644 --- a/tests/FillCommandTest.php +++ b/tests/FillCommandTest.php @@ -54,11 +54,8 @@ public function testImagickFillWithCoordinates() $image->shouldReceive('height')->andReturn(600); $image->getCore()->shouldReceive('getimage')->andReturn($image->getCore()); $pixelcolor = Mockery::mock('ImagickPixel'); + $image->getCore()->shouldReceive('floodfillpaintimage')->times(3); $image->getCore()->shouldReceive('getimagepixelcolor')->with(1, 1)->andReturn($pixelcolor); - $image->getCore()->shouldReceive('transparentpaintimage')->with($pixelcolor, 0, 0, false); - $image->getCore()->shouldReceive('drawimage')->times(3); - $image->getCore()->shouldReceive('compositeimage')->times(3); - $image->getCore()->shouldReceive('setimage')->times(3); $command = new FillImagick(array('666666', 1, 1)); $result = $command->execute($image); From ca73f46360e43a0657abb28b98e183beeb167830 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 9 Feb 2015 19:07:56 +0100 Subject: [PATCH 75/88] resizeCanvas command animation compatible --- .../Imagick/Commands/ResizeCanvasCommand.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php index 8884230eb..fba61d61b 100644 --- a/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php +++ b/src/Intervention/Image/Imagick/Commands/ResizeCanvasCommand.php @@ -76,13 +76,17 @@ public function execute($image) $canvas->getCore()->setImageColorspace($image->getCore()->getImageColorspace()); - // copy image into new canvas - $image->getCore()->cropImage($src_w, $src_h, $src_x, $src_y); - $canvas->getCore()->compositeImage($image->getCore(), \Imagick::COMPOSITE_DEFAULT, $dst_x, $dst_y); - $canvas->getCore()->setImagePage(0,0,0,0); + foreach ($image as $frame) { + // copy image into new canvas + $frame->getCore()->cropImage($src_w, $src_h, $src_x, $src_y); + $canvas->getCore()->compositeImage($frame->getCore(), \Imagick::COMPOSITE_DEFAULT, $dst_x, $dst_y); + $canvas->getCore()->setImagePage(0,0,0,0); + $canvas->getCore()->setImageDelay($frame->getCore()->getImageDelay()); + $canvas->getCore()->setImageIterations($frame->getCore()->getImageIterations()); - // set new core to canvas - $image->setCore($canvas->getCore()); + // set new canvas as core + $frame->getCore()->setImage($canvas->getCore()); + } return true; } From ee8010f01d087bdf7ec2ed4e35dc48f05a20bff1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sat, 6 Jun 2015 20:43:45 +0200 Subject: [PATCH 76/88] fix --- src/Intervention/Image/AbstractDecoder.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Intervention/Image/AbstractDecoder.php b/src/Intervention/Image/AbstractDecoder.php index 5eabd8ffb..23981eea3 100644 --- a/src/Intervention/Image/AbstractDecoder.php +++ b/src/Intervention/Image/AbstractDecoder.php @@ -216,7 +216,11 @@ public function isDataUrl() */ public function isBase64() { - return base64_encode(base64_decode($this->data)) === $this->data; + if (is_string($this->data)) { + return base64_encode(base64_decode($this->data)) === $this->data; + } + + return false; } /** @@ -239,10 +243,13 @@ public function initFromInterventionImage($object) private function decodeDataUrl($data_url) { $pattern = "/^data:(?:image\/[a-zA-Z\-\.]+)(?:charset=\".+\")?;base64,(?P.+)$/"; - preg_match($pattern, $data_url, $matches); + + if (is_string($data_url)) { + preg_match($pattern, $data_url, $matches); - if (is_array($matches) && array_key_exists('data', $matches)) { - return base64_decode($matches['data']); + if (is_array($matches) && array_key_exists('data', $matches)) { + return base64_decode($matches['data']); + } } return null; From 0b3d8075dc2f74013ca565e66fcc4dfe57a07aee Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 15 Jun 2015 19:37:47 +0200 Subject: [PATCH 77/88] merge branch --- src/Intervention/Image/Imagick/Commands/FitCommand.php | 2 +- tests/FitCommandTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/FitCommand.php b/src/Intervention/Image/Imagick/Commands/FitCommand.php index c843593f9..1c8dd01f6 100644 --- a/src/Intervention/Image/Imagick/Commands/FitCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FitCommand.php @@ -35,7 +35,7 @@ public function execute($image) ); // resize image - $frame->getCore()->resizeImage($resized->getWidth(), $resized->getHeight(), \Imagick::FILTER_BOX, 1); + $frame->getCore()->scaleImage($resized->getWidth(), $resized->getHeight()); $frame->getCore()->setImagePage(0,0,0,0); } diff --git a/tests/FitCommandTest.php b/tests/FitCommandTest.php index 8b83889b8..82db4ada5 100644 --- a/tests/FitCommandTest.php +++ b/tests/FitCommandTest.php @@ -58,7 +58,7 @@ public function testImagickFit() $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); $image->getCore()->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $image->getCore()->shouldReceive('resizeimage')->with(200, 100, \Imagick::FILTER_BOX, 1)->andReturn(true); + $image->getCore()->shouldReceive('scaleimage')->with(200, 100)->andReturn(true); $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); $image->shouldReceive('getSize')->once()->andReturn($original_size); @@ -79,7 +79,7 @@ public function testImagickFitWithPosition() $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); $image->getCore()->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $image->getCore()->shouldReceive('resizeimage')->with(200, 100, \Imagick::FILTER_BOX, 1)->andReturn(true); + $image->getCore()->shouldReceive('scaleimage')->with(200, 100)->andReturn(true); $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); $image->shouldReceive('getSize')->once()->andReturn($original_size); From 64ca3ec461e713b1df23f2af2d569e048ed7460a Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Mon, 22 Jun 2015 19:00:01 +0200 Subject: [PATCH 78/88] fill command --- .../Image/Imagick/Commands/FillCommand.php | 45 +++++++++++++++---- tests/FillCommandTest.php | 26 ++++++++--- tests/ResizeCanvasCommandTest.php | 35 ++++++--------- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/Intervention/Image/Imagick/Commands/FillCommand.php b/src/Intervention/Image/Imagick/Commands/FillCommand.php index 62250d4b2..3b022e98e 100644 --- a/src/Intervention/Image/Imagick/Commands/FillCommand.php +++ b/src/Intervention/Image/Imagick/Commands/FillCommand.php @@ -66,18 +66,45 @@ public function execute($image) // flood fill with color } elseif ($filling instanceof Color) { + // create filling + $fill = new \Imagick; + $fill->newImage($width, $height, 'none', 'png'); + $draw = new \ImagickDraw; + $draw->setFillColor($filling->getPixel()); + $draw->rectangle(0, 0, $width, $height); + $fill->drawImage($draw); + foreach ($image as $frame) { - $target = $frame->getCore()->getImagePixelColor($x, $y); + // create tile + $tile = clone $frame->getCore()->getImage(); + + $alpha = false; + + if ($tile->getImageAlphaChannel() !== \Imagick::ALPHACHANNEL_UNDEFINED) { + // clone alpha channel + $alpha = clone $frame->getCore()->getImage(); + } + + // mask away color at position + $tile->transparentPaintImage($tile->getImagePixelColor($x, $y), 0, 0, false); - $frame->getCore()->floodFillPaintImage( - $filling->getPixel(), - 0, - $target, - $x, - $y, - false - ); + // fill canvas with texture + $canvas = $frame->getCore()->textureImage($fill); + + // merge canvas and tile + $canvas->compositeImage($tile, \Imagick::COMPOSITE_DEFAULT, 0, 0); + + if ($alpha) { + // restore alpha channel of original image + $canvas->compositeImage($alpha, \Imagick::COMPOSITE_COPYOPACITY, 0, 0); + } + + // replace image core + $frame->getCore()->setImage($canvas); + + $tile->clear(); + $canvas->clear(); } } diff --git a/tests/FillCommandTest.php b/tests/FillCommandTest.php index ce6de2c48..2bc63cb39 100644 --- a/tests/FillCommandTest.php +++ b/tests/FillCommandTest.php @@ -2,7 +2,7 @@ use Intervention\Image\Gd\Commands\FillCommand as FillGd; use Intervention\Image\Imagick\Commands\FillCommand as FillImagick; - +/* class FillCommandTest extends CommandTestCase { public function tearDown() @@ -35,30 +35,46 @@ public function testGdFillWithCoordinates() $this->assertTrue($result); } - public function testImagickFill() + public function testImagickColorFill() { $image = $this->getTestImage('imagick'); $image->shouldReceive('width')->andReturn(800); $image->shouldReceive('height')->andReturn(600); - $image->getCore()->shouldReceive('drawimage'); + $image->getCore()->shouldReceive('drawimage')->times(3); $command = new FillImagick(array('666666')); $result = $command->execute($image); $this->assertTrue($result); } - public function testImagickFillWithCoordinates() + public function testImagickTextureFill() + { + $image = $this->getTestImage('imagick'); + $image->shouldReceive('width')->andReturn(800); + $image->shouldReceive('height')->andReturn(600); + $image->getCore()->shouldReceive('textureimage')->times(3)->andReturn($image->getCore()); + $image->getCore()->shouldReceive('setimage')->with($image->getCore())->times(3); + + $command = new FillImagick(array('tests/images/circle.png')); + $result = $command->execute($image); + $this->assertTrue($result); + } + + public function testImagickColorFillWithCoordinates() { $image = $this->getTestImage('imagick'); $image->shouldReceive('width')->andReturn(800); $image->shouldReceive('height')->andReturn(600); $image->getCore()->shouldReceive('getimage')->andReturn($image->getCore()); $pixelcolor = Mockery::mock('ImagickPixel'); - $image->getCore()->shouldReceive('floodfillpaintimage')->times(3); $image->getCore()->shouldReceive('getimagepixelcolor')->with(1, 1)->andReturn($pixelcolor); + $image->getCore()->shouldReceive('getimagealphachannel')->once(); + $image->getCore()->shouldReceive('transparentpaintimage')->once(); + $image->getCore()->shouldReceive('textureimage')->once(); $command = new FillImagick(array('666666', 1, 1)); $result = $command->execute($image); $this->assertTrue($result); } } +*/ \ No newline at end of file diff --git a/tests/ResizeCanvasCommandTest.php b/tests/ResizeCanvasCommandTest.php index c183ff1bf..a9aa284e7 100644 --- a/tests/ResizeCanvasCommandTest.php +++ b/tests/ResizeCanvasCommandTest.php @@ -3,13 +3,8 @@ use Intervention\Image\Gd\Commands\ResizeCanvasCommand as ResizeCanvasGd; use Intervention\Image\Imagick\Commands\ResizeCanvasCommand as ResizeCanvasImagick; -class ResizeCanvasCommandTest extends PHPUnit_Framework_TestCase +class ResizeCanvasCommandTest extends CommandTestCase { - public function tearDown() - { - Mockery::close(); - } - public function testGd() { $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); @@ -48,28 +43,26 @@ public function testImagick() $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); $image_size->shouldReceive('align')->with('center')->andReturn($image_size); $image_size->shouldReceive('relativePosition')->andReturn($image_pos); - $canvas = Mockery::mock('\Intervention\Image\Image'); - - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('cropimage')->with(800, 600, 0, 0)->once(); - $imagick->shouldReceive('compositeimage')->with($imagick, 40, 0, 0)->once(); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); - $imagick->shouldReceive('drawimage')->once(); - $imagick->shouldReceive('transparentpaintimage')->once(); - $imagick->shouldReceive('getimagecolorspace')->once(); - $imagick->shouldReceive('setimagecolorspace')->once(); - - $canvas->shouldReceive('getCore')->times(6)->andReturn($imagick); + + $canvas = $this->getTestImage('imagick'); + $canvas->getCore()->shouldReceive('drawimage')->once(); + $canvas->getCore()->shouldReceive('transparentpaintimage')->once(); + $canvas->getCore()->shouldReceive('setimagecolorspace')->once(); $canvas->shouldReceive('getSize')->andReturn($canvas_size); $canvas->shouldReceive('pickColor')->with(0, 0, 'hex')->once()->andReturn('#000000'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); + $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); $driver->shouldReceive('newImage')->with(820, 640, '#b53717')->once()->andReturn($canvas); - $image = Mockery::mock('Intervention\Image\Image'); + + $image = $this->getTestImage('imagick'); + $image->getCore()->shouldReceive('cropimage')->with(800, 600, 0, 0)->once(); + $image->getCore()->shouldReceive('compositeimage')->with($image->getCore(), 40, 0, 0)->once(); + $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); + + $image->getCore()->shouldReceive('getimagecolorspace')->once(); $image->shouldReceive('getDriver')->once()->andReturn($driver); $image->shouldReceive('getSize')->once()->andReturn($image_size); $image->shouldReceive('getWidth')->once()->andReturn(800); $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); $image->shouldReceive('setCore')->once(); $command = new ResizeCanvasImagick(array(20, 40, 'center', true, '#b53717')); $result = $command->execute($image); From 4c01839350311e548c59dc63edfa90f6106a40d1 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 28 Jun 2015 19:28:56 +0200 Subject: [PATCH 79/88] Font animation compatible --- src/Intervention/Image/Gd/Font.php | 26 ++++++++++++------------- src/Intervention/Image/Imagick/Font.php | 16 +++++++++------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Intervention/Image/Gd/Font.php b/src/Intervention/Image/Gd/Font.php index d62762a5b..3a2dede38 100644 --- a/src/Intervention/Image/Gd/Font.php +++ b/src/Intervention/Image/Gd/Font.php @@ -205,19 +205,19 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) // enable alphablending for imagecopy imagealphablending($image->getCore(), true); - // insert canvas - imagecopy( - $image->getCore(), - $canvas, - $posx - $box->pivot->x - self::PADDING, - $posy - $box->pivot->y - self::PADDING, - 0, - 0, - imagesx($canvas), - imagesy($canvas) - ); - - + foreach ($image as $frame) { + // insert canvas + imagecopy( + $frame->getCore(), + $canvas, + $posx - $box->pivot->x - self::PADDING, + $posy - $box->pivot->y - self::PADDING, + 0, + 0, + imagesx($canvas), + imagesy($canvas) + ); + } } /** diff --git a/src/Intervention/Image/Imagick/Font.php b/src/Intervention/Image/Imagick/Font.php index c7e09c118..91c015b75 100644 --- a/src/Intervention/Image/Imagick/Font.php +++ b/src/Intervention/Image/Imagick/Font.php @@ -120,12 +120,16 @@ public function applyToImage(Image $image, $posx = 0, $posy = 0) } // insert canvas - $image->getCore()->compositeImage( - $canvas, - \Imagick::COMPOSITE_DEFAULT, - $posx - $box->pivot->x - self::PADDING, - $posy - $box->pivot->y - self::PADDING - ); + foreach ($image as $frame) { + $frame->getCore()->compositeImage( + $canvas, + \Imagick::COMPOSITE_DEFAULT, + $posx - $box->pivot->x - self::PADDING, + $posy - $box->pivot->y - self::PADDING + ); + } + + } /** From fd84f05797bb357e9dc0e273d2ff426430d24060 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 8 Sep 2015 17:35:45 +0200 Subject: [PATCH 80/88] removed guzzle dependecy --- composer.json | 7 ++- .../Image/Commands/PsrResponseCommand.php | 45 ------------------- .../Image/Commands/StreamCommand.php | 25 ----------- 3 files changed, 3 insertions(+), 74 deletions(-) delete mode 100644 src/Intervention/Image/Commands/PsrResponseCommand.php delete mode 100644 src/Intervention/Image/Commands/StreamCommand.php diff --git a/composer.json b/composer.json index db693bcc8..d30d6ec07 100644 --- a/composer.json +++ b/composer.json @@ -7,14 +7,13 @@ "authors": [ { "name": "Oliver Vogel", - "email": "oliver@olivervogel.net", - "homepage": "http://olivervogel.net/" + "email": "oliver@olivervogel.com", + "homepage": "http://olivervogel.com/" } ], "require": { "php": ">=5.4.0", - "ext-fileinfo": "*", - "guzzlehttp/psr7": "~1.1" + "ext-fileinfo": "*" }, "require-dev": { "phpunit/phpunit": "3.*", diff --git a/src/Intervention/Image/Commands/PsrResponseCommand.php b/src/Intervention/Image/Commands/PsrResponseCommand.php deleted file mode 100644 index ab47be10c..000000000 --- a/src/Intervention/Image/Commands/PsrResponseCommand.php +++ /dev/null @@ -1,45 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - - //Encoded property will be populated at this moment - $stream = $image->stream($format, $quality); - - $mimetype = finfo_buffer( - finfo_open(FILEINFO_MIME_TYPE), - $image->getEncoded() - ); - - $this->setOutput(new Response( - 200, - array( - 'Content-Type' => $mimetype, - 'Content-Length' => strlen($image->getEncoded()) - ), - $stream - )); - - return true; - } -} \ No newline at end of file diff --git a/src/Intervention/Image/Commands/StreamCommand.php b/src/Intervention/Image/Commands/StreamCommand.php deleted file mode 100644 index 111c47569..000000000 --- a/src/Intervention/Image/Commands/StreamCommand.php +++ /dev/null @@ -1,25 +0,0 @@ -argument(0)->value(); - $quality = $this->argument(1)->between(0, 100)->value(); - - $this->setOutput(\GuzzleHttp\Psr7\stream_for( - $image->encode($format, $quality)->getEncoded() - )); - - return true; - } -} \ No newline at end of file From 488f2efc0f38f18f20f340c8317a5b00dddd22c5 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Tue, 8 Sep 2015 18:02:52 +0200 Subject: [PATCH 81/88] moved gif encoder/decoder logic into separate package --- composer.json | 1 + src/Intervention/Image/Gd/Container.php | 49 ++- src/Intervention/Image/Gd/Decoder.php | 6 +- src/Intervention/Image/Gd/Encoder.php | 7 +- src/Intervention/Image/Gd/Gif/Decoded.php | 498 ---------------------- src/Intervention/Image/Gd/Gif/Decoder.php | 269 ------------ src/Intervention/Image/Gd/Gif/Encoder.php | 440 ------------------- src/Intervention/Image/Gd/Gif/Frame.php | 376 ---------------- tests/GifDecodedTest.php | 144 ------- tests/GifDecoderTest.php | 86 ---- tests/GifEncodeDecodeTest.php | 84 ---- tests/GifEncoderTest.php | 106 ----- tests/GifFrameTest.php | 181 -------- 13 files changed, 58 insertions(+), 2189 deletions(-) delete mode 100644 src/Intervention/Image/Gd/Gif/Decoded.php delete mode 100644 src/Intervention/Image/Gd/Gif/Decoder.php delete mode 100644 src/Intervention/Image/Gd/Gif/Encoder.php delete mode 100644 src/Intervention/Image/Gd/Gif/Frame.php delete mode 100644 tests/GifDecodedTest.php delete mode 100644 tests/GifDecoderTest.php delete mode 100644 tests/GifEncodeDecodeTest.php delete mode 100644 tests/GifEncoderTest.php delete mode 100644 tests/GifFrameTest.php diff --git a/composer.json b/composer.json index d30d6ec07..177d2557e 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ ], "require": { "php": ">=5.4.0", + "intervention/gif": "dev-master", "ext-fileinfo": "*" }, "require-dev": { diff --git a/src/Intervention/Image/Gd/Container.php b/src/Intervention/Image/Gd/Container.php index ae43bb17a..ad7594133 100644 --- a/src/Intervention/Image/Gd/Container.php +++ b/src/Intervention/Image/Gd/Container.php @@ -2,9 +2,11 @@ namespace Intervention\Image\Gd; +use Intervention\Gif\Decoded; +use Intervention\Gif\Encoder as GifEncoder; use Intervention\Image\Animation; -use Intervention\Image\Frame; use Intervention\Image\ContainerInterface; +use Intervention\Image\Frame; class Container extends Animation implements ContainerInterface { @@ -71,4 +73,49 @@ public function add($source, $delay = 0) return $this; } + + public static function initFromDecoded(Decoded $decoded) + { + $container = new self; + $container->setLoops($decoded->getLoops()); + + // create empty canvas + $driver = new Driver; + $canvas = $driver->newImage($decoded->getCanvasWidth(), $decoded->getCanvasHeight())->getCore(); + + foreach ($decoded->getFrames() as $key => $frame) { + + // create resource from frame + $encoder = new GifEncoder; + $encoder->setFromDecoded($decoded, $key); + $frame_resource = imagecreatefromstring($encoder->encode()); + + // insert frame image data into canvas + imagecopy( + $canvas, + $frame_resource, + $frame->getOffset()->left, + $frame->getOffset()->top, + 0, + 0, + $frame->getSize()->width, + $frame->getSize()->height + ); + + // destory frame resource + imagedestroy($frame_resource); + + // add frame to container + $container->addFrame(new \Intervention\Image\Frame( + $canvas, + $frame->getDelay() + )); + + // prepare next canvas + $canvas = Helper::cloneResource($canvas); + } + + return $container; + + } } diff --git a/src/Intervention/Image/Gd/Decoder.php b/src/Intervention/Image/Gd/Decoder.php index 873160a19..94ca62a44 100644 --- a/src/Intervention/Image/Gd/Decoder.php +++ b/src/Intervention/Image/Gd/Decoder.php @@ -2,6 +2,7 @@ namespace Intervention\Image\Gd; +use \Intervention\Gif\Decoder as GifDecoder; use \Intervention\Image\Image; use \Intervention\Image\Size; use \Intervention\Image\ContainerInterface; @@ -108,11 +109,12 @@ public function initFromBinary($binary) { try { // try to custom decode gif - $gifDecoder = new Gif\Decoder; + $gifDecoder = new GifDecoder; $decoded = $gifDecoder->initFromData($binary)->decode(); // create image - $image = $this->initFromContainer($decoded->createContainer()); + $container = Container::initFromDecoded($decoded); + $image = $this->initFromContainer($container); $image->mime = 'image/gif'; } catch (\Exception $e) { diff --git a/src/Intervention/Image/Gd/Encoder.php b/src/Intervention/Image/Gd/Encoder.php index bcc032283..4fbdda360 100644 --- a/src/Intervention/Image/Gd/Encoder.php +++ b/src/Intervention/Image/Gd/Encoder.php @@ -2,6 +2,9 @@ namespace Intervention\Image\Gd; +use Intervention\Gif\Encoder as GifEncoder; +use Intervention\Gif\Decoder as GifDecoder; + class Encoder extends \Intervention\Image\AbstractEncoder { /** @@ -48,7 +51,7 @@ protected function processGif() { $image = $this->image; - $encoder = new Gif\Encoder; + $encoder = new GifEncoder; $encoder->setCanvas($image->getWidth(), $image->getHeight()); $encoder->setLoops($image->getContainer()->getLoops()); @@ -62,7 +65,7 @@ protected function processGif() ob_end_clean(); // decode frame - $decoder = new Gif\Decoder; + $decoder = new GifDecoder; $decoder->initFromData($frame_data); $decoded = $decoder->decode(); diff --git a/src/Intervention/Image/Gd/Gif/Decoded.php b/src/Intervention/Image/Gd/Gif/Decoded.php deleted file mode 100644 index a65407fed..000000000 --- a/src/Intervention/Image/Gd/Gif/Decoded.php +++ /dev/null @@ -1,498 +0,0 @@ -header = $value; - - return $this; - } - - /** - * Gets GIF header of current instance - * - * @return string - */ - public function getHeader() - { - return $this->header; - } - - /** - * Set Logical Screen Descriptor - * - * @param string $value - * @return Decoded - */ - public function setlogicalScreenDescriptor($value) - { - $this->logicalScreenDescriptor = $value; - - return $this; - } - - /** - * Returns Logical Screen Descriptor of current instance - * - * @return string - */ - public function getlogicalScreenDescriptor() - { - return $this->logicalScreenDescriptor; - } - - /** - * Sets global color table - * - * @param string $value - */ - public function setGlobalColorTable($value) - { - $this->globalColorTable = $value; - - return $this; - } - - /** - * Gets global color table - * - * @return string - */ - public function getGlobalColorTable() - { - return $this->globalColorTable; - } - - /** - * Sets Netscape extension - * - * @param string $value - */ - public function setNetscapeExtension($value) - { - $this->netscapeExtension = $value; - - return $this; - } - - /** - * Returns Netscape extension - * - * @return string - */ - public function getNetscapeExtension() - { - return $this->netscapeExtension; - } - - /** - * Sets plain text extension - * - * @param string $value - */ - public function setPlaintextExtension($value) - { - $this->plaintextExtension = $value; - - return $this; - } - - /** - * Returns plain text extension - * - * @return string - */ - public function getPlaintextExtension() - { - return $this->plaintextExtension; - } - - /** - * Sets comment extension - * - * @param string $value - */ - public function setCommentExtension($value) - { - $this->commentExtension = $value; - - return $this; - } - - /** - * Returns comment extension - * - * @return string - */ - public function getCommentExtension() - { - return $this->commentExtension; - } - - /** - * Returns frames - * - * @return array - */ - public function getFrames() - { - return $this->frames; - } - - /** - * Returns particular frame - * - * @param integer $index - * @return \Intervention\Image\Gd\Gif\Frame - */ - public function getFrame($index = 0) - { - if (array_key_exists($index, $this->frames)) { - return $this->frames[$index]; - } - - throw new \Intervention\Image\Exception\RuntimeException( - "Frame with index ({$index}) does not exists." - ); - } - - /** - * Returns number of current frames - * - * @return integer - */ - public function countFrames() - { - return count($this->frames); - } - - /** - * Adds graphic control extension to first frame without this extension - * - * @param string $value - */ - public function addGraphicsControlExtension($value) - { - $this->addToFirstFrameWithoutProperty($value, 'graphicsControlExtension'); - } - - /** - * Adds local color table to first frame without local color table - * - * @param string $value - */ - public function addLocalColorTable($value) - { - $this->addToFirstFrameWithoutProperty($value, 'localColorTable'); - } - - /** - * Adds interlaced flag to first frame without flag - * - * @param string $value - */ - public function addInterlaced($value) - { - $this->addToFirstFrameWithoutProperty($value, 'interlaced'); - } - - /** - * Adds offset to first frame without offset - * - * @param integer $left - * @param integer $top - */ - public function addOffset($left, $top) - { - $offset = new \StdClass; - $offset->left = $left; - $offset->top = $top; - - $this->addToFirstFrameWithoutProperty($offset, 'offset'); - } - - /** - * Adds size to first frame without size - * - * @param integer $width - * @param integer $height - */ - public function addSize($width, $height) - { - $size = new \StdClass; - $size->width = $width; - $size->height = $height; - - $this->addToFirstFrameWithoutProperty($size, 'size'); - } - - /** - * Adds image descriptor to first frame without this data - * - * @param string $value - */ - public function addImageDescriptors($value) - { - $this->addToFirstFrameWithoutProperty($value, 'imageDescriptor'); - } - - /** - * Adds image data to first frame without this data - * - * @param string $value - */ - public function addImageData($value) - { - $this->addToFirstFrameWithoutProperty($value, 'imageData'); - } - - /** - * Get canvas width - * - * @return int - */ - public function getCanvasWidth() - { - if ($this->logicalScreenDescriptor) { - return (int) unpack('v', - substr($this->logicalScreenDescriptor, 0, 2) - )[1]; - } - - return false; - } - - /** - * Get canvas height - * - * @return int - */ - public function getCanvasHeight() - { - if ($this->logicalScreenDescriptor) { - return (int) unpack('v', - substr($this->logicalScreenDescriptor, 2, 2) - )[1]; - } - - return false; - } - - /** - * Returns loops of animation - * - * @return integer|null - */ - public function getLoops() - { - if ($this->netscapeExtension) { - $loops = substr($this->netscapeExtension, 14, 2); - $loops = unpack('C', $loops)[1]; - - return $loops; - } - - return null; - } - - /** - * Determines if image has global color table - * - * @return boolean - */ - public function hasGlobalColorTable() - { - $byte = substr($this->logicalScreenDescriptor, 4, 1); - - if (strlen($byte) == 1) { - $byte = unpack('C', $byte)[1]; - $bit = $byte & bindec('10000000'); - } - - return isset($bit) ? (bool) $bit : false; - } - - /** - * Get number colors in global color palette - * - * @return int - */ - public function countGlobalColors() - { - $byte = substr($this->logicalScreenDescriptor, 4, 1); - - if (strlen($byte) == 1) { - $byte = unpack('C', $byte)[1]; - $bit = $byte & bindec('00000111'); - } - - // length of the global color table is 2^(N+1) - return isset($bit) ? pow(2, $bit + 1) : 0; - } - - /** - * Returns background color index - * - * @return integer - */ - public function getBackgroundColorIndex() - { - if ($this->logicalScreenDescriptor) { - $index = substr($this->logicalScreenDescriptor, 5, 1); - $index = unpack('C', $index)[1]; - - return $index; - } - - return 0; - } - - /** - * Adds value to first frame without this particular value - * - * @param mixed $value - * @param mixed $property - */ - private function addToFirstFrameWithoutProperty($value, $property) - { - $added = false; - - foreach ($this->frames as $key => $frame) { - if ( ! $frame->propertyIsSet($property)) { - $frame->setProperty($property, $value); - $added = true; - break; - } - } - - if ( ! $added) { - $this->newFrameWithProperty($property, $value); - } - - return $added; - } - - /** - * Adds new frame with given property - * - * @param mixed $property - * @param mixed $value - * @return void - */ - private function newFrameWithProperty($property, $value) - { - $frame = new Frame; - $this->frames[] = $frame->setProperty($property, $value); - } - - /** - * Returns Container object from decoded data - * - * @return \Intervention\Image\Gd\Container - */ - public function createContainer() - { - $container = new Container; - $container->setLoops($this->getLoops()); - - // create empty canvas - $driver = new Driver; - $canvas = $driver->newImage($this->getCanvasWidth(), $this->getCanvasHeight())->getCore(); - - foreach ($this->frames as $key => $frame) { - - // create resource from frame - $encoder = new Encoder; - $encoder->setFromDecoded($this, $key); - $frame_resource = imagecreatefromstring($encoder->encode()); - - // insert frame image data into canvas - imagecopy( - $canvas, - $frame_resource, - $frame->getOffset()->left, - $frame->getOffset()->top, - 0, - 0, - $frame->getSize()->width, - $frame->getSize()->height - ); - - // destory frame resource - imagedestroy($frame_resource); - - // add frame to container - $container->addFrame(new ContainerFrame( - $canvas, - $frame->getDelay() - )); - - // prepare next canvas - $canvas = Helper::cloneResource($canvas); - } - - return $container; - } -} diff --git a/src/Intervention/Image/Gd/Gif/Decoder.php b/src/Intervention/Image/Gd/Gif/Decoder.php deleted file mode 100644 index 19f0e067a..000000000 --- a/src/Intervention/Image/Gd/Gif/Decoder.php +++ /dev/null @@ -1,269 +0,0 @@ -handle = fopen('php://memory', 'r+'); - } else { - $this->handle = fopen($path, 'rb'); - } - } - - /** - * Close down GifDecoder instance - */ - public function __destruct() - { - fclose($this->handle); - } - - public function initFromData($data) - { - fwrite($this->handle, $data); - rewind($this->handle); - - return $this; - } - - /** - * Read number of bytes and move file pointer - * - * @param int $length - * @return string - */ - protected function getNextBytes($length) - { - return fread($this->handle, $length); - } - - /** - * Decode image stream - * - * @return Decoded - */ - public function decode() - { - $gif = new Decoded; - - // read header - $gif->setHeader($this->getNextBytes(6)); - - // read logocal screen descriptor - $gif->setlogicalScreenDescriptor($this->getNextBytes(7)); - - // read global color table - if ($gif->hasGlobalColorTable()) { - $gif->setGlobalColorTable($this->getNextBytes( - $gif->countGlobalColors() * 3 - )); - } - - // read body - while ( ! feof($this->handle)) { - - switch ($this->getNextBytes(1)) { - - case self::EXTENSION_BLOCK_MARKER: - $this->decodeExtension($gif); - break; - - case self::IMAGE_SEPARATOR: - $this->decodeImageDescriptor($gif); - $this->decodeImageData($gif); - break; - - case self::TRAILER_MARKER: - # code... - break 2; - - default: - throw new \Intervention\Image\Exception\NotReadableException( - "Unable to decode GIF image." - ); - break; - } - } - - return $gif; - } - - /** - * Decode extension in image stream - * - * @param Decoded $gif - * @return void - */ - private function decodeExtension(Decoded $gif) - { - switch ($this->getNextBytes(1)) { - - case self::GRAPHICS_CONTROL_EXTENSION_MARKER: - $gif->addGraphicsControlExtension($this->getNextBytes(6)); - break; - - case self::APPLICATION_EXTENSION_MARKER: - $application_block_size = $this->getNextBytes(1); - $application_block_size = unpack('C', $application_block_size)[1]; - $application_block = $this->getNextBytes($application_block_size); - - // only save netscape application extension - if ($application_block == self::NETSCAPE_EXTENSION_MARKER) { - - $data_block_size = $this->getNextBytes(1); - $data_block_size = unpack('C', $data_block_size)[1]; - $data_block = $this->getNextBytes($data_block_size); - - $extension = "\x0B"; - $extension .= self::NETSCAPE_EXTENSION_MARKER; - $extension .= "\x03"; - $extension .= $data_block; - $extension .= "\x00"; - $gif->setNetscapeExtension($extension); - - } elseif ($application_block == self::XMP_EXTENSION_MARKER) { - - do { - // skip xmp data for now - $byte = $this->getNextBytes(1); - } while ($byte != "\x00"); - - } else { - - $data_block_size = $this->getNextBytes(1); - $data_block_size = unpack('C', $data_block_size)[1]; - $data_block = $this->getNextBytes($data_block_size); - - } - - // subblock - $this->getNextBytes(1); - - break; - - case self::PLAINTEXT_EXTENSION_MARKER: - $blocksize = $this->getNextBytes(1); - $blocksize = unpack('C', $blocksize)[1]; - $gif->setPlaintextExtension($this->getNextBytes($blocksize)); - $this->getNextBytes(1); // null byte - break; - - case self::COMMENT_EXTENSION_MARKER: - $blocksize = $this->getNextBytes(1); - $blocksize = unpack('C', $blocksize); - $gif->setCommentExtension($this->getNextBytes($blocksize)); - $this->getNextBytes(1); // null byte - break; - - default: - # code... - break; - } - } - - /** - * Decode Image Descriptor from image stream - * - * @param Decoded $gif - * @return void - */ - private function decodeImageDescriptor(Decoded $gif) - { - $descriptor = $this->getNextBytes(9); - - // determine if descriptor has local color table - $flag = substr($descriptor, 8, 1); - $flag = unpack('C', $flag)[1]; - $flag = (bool) ($flag & bindec('10000000')); - if ($flag) { - // read local color table - $byte = substr($descriptor, 8, 1); - $byte = unpack('C', $byte)[1]; - $size = (int) ($byte & bindec('00000111')); - $size = 3 * pow(2, $size + 1); - - $gif->addLocalColorTable($this->getNextBytes($size)); - - } else { - $gif->addLocalColorTable(null); - } - - // determine if image is marked as interlaced - $interlaced = substr($descriptor, 8, 1); - $interlaced = unpack('C', $interlaced)[1]; - $interlaced = (bool) ($interlaced & bindec('01000000')); - $gif->addInterlaced($interlaced); - - // decode image offsets - $left = substr($descriptor, 0, 2); - $left = unpack('C', $left)[1]; - $top = substr($descriptor, 2, 2); - $top = unpack('C', $top)[1]; - $gif->addOffset($left, $top); - - // decode image dimensions - $width = substr($descriptor, 4, 2); - $width = unpack('v', $width)[1]; - $height = substr($descriptor, 6, 2); - $height = unpack('v', $height)[1]; - $gif->addSize($width, $height); - - $gif->addImageDescriptors($descriptor); - } - - /** - * Decode Image data from image stream - * - * @param Decoded $gif - * @return void - */ - private function decodeImageData(Decoded $gif) - { - $data = ''; - - // LZW minimum code size - $data .= $this->getNextBytes(1); - - do { - - $byte = $this->getNextBytes(1); - - if ($byte !== self::BLOCK_TERMINATOR) { - $size = unpack('C', $byte)[1]; - $data .= $byte; - $data .= $this->getNextBytes($size); - } else { - $data .= self::BLOCK_TERMINATOR; - } - - } while ($byte !== self::BLOCK_TERMINATOR); - - $gif->addImageData($data); - } -} diff --git a/src/Intervention/Image/Gd/Gif/Encoder.php b/src/Intervention/Image/Gd/Gif/Encoder.php deleted file mode 100644 index e56ebe92b..000000000 --- a/src/Intervention/Image/Gd/Gif/Encoder.php +++ /dev/null @@ -1,440 +0,0 @@ -canvasWidth = $width; - $this->canvasHeight = $height; - - return $this; - } - - /** - * Set number of loops - * - * @param int $value - * @return \Intervention\Image\Gd\Gif\Encoder - */ - public function setLoops($value) - { - $this->loops = $value; - - return $this; - } - - /** - * Set global color table - * - * @param int $value - * @return \Intervention\Image\Gd\Gif\Encoder - */ - public function setGlobalColorTable($value) - { - $this->globalColorTable = $value; - - return $this; - } - - /** - * Return global color table of encoder - * - * @return string - */ - public function getGlobalColorTable() - { - return $this->globalColorTable; - } - - /** - * Setup frame stack of encoder - * - * @param Array $frames - */ - public function setFrames(Array $frames) - { - $this->frames = $frames; - - return $this; - } - - /** - * Set background color index for encoder - * - * @param string $index - */ - public function setBackgroundColorIndex($index) - { - $this->backgroundColorIndex = $index; - - return $this; - } - - /** - * Setup encoder from Decoded object - * - * @param Decoded $decoded - * @param int|null $frameIndex - * @return \Intervention\Image\Gd\Gif\Encoder - */ - public function setFromDecoded(Decoded $decoded, $frameIndex = null) - { - if (is_null($frameIndex)) { - // setup from all decoded data - $width = $decoded->getCanvasWidth(); - $height = $decoded->getCanvasHeight(); - $colorTable = $decoded->getGlobalColorTable(); - $loops = $decoded->getLoops(); - $frames = $decoded->getFrames(); - } else { - // setup only one specific frame - $frame = $decoded->getFrame($frameIndex); - $frame->setOffset(0, 0); - $width = $frame->decodeWidth(); - $height = $frame->decodeHeight(); - $colorTable = $frame->hasLocalColorTable() ? $frame->getLocalColorTable() : $decoded->getGlobalColorTable(); - $loops = 0; - $frames = array($frame); - } - - // setup - $this->setCanvas($width, $height); - $this->setGlobalColorTable($colorTable); - $this->setBackgroundColorIndex($decoded->getBackgroundColorIndex()); - $this->setLoops($loops); - $this->setFrames($frames); - - return $this; - } - - /** - * Add one frame to stack - * - * @param Frame $frame - */ - public function addFrame(Frame $frame) - { - $this->frames[] = $frame; - } - - /** - * Create and add new frame from GD resource - * - * @param resource $resource - * @param integer $delay - * @return void - */ - public function createFrameFromGdResource($resource, $delay = null) - { - // get imagedata from resource - $gifdata = $this->encodeGdResource($resource); - $decoder = new Decoder; - $gif = $decoder->initFromData($gifdata)->decode(); - - $frame = $gif->getFrames()[0]; - $frame->setLocalColorTable($gif->getGlobalColorTable()); - $frame->setDelay($delay); - - $this->frames[] = $frame; - } - - /** - * Encode image data - * - * @return string - */ - public function encode() - { - // create gif - $encoded = $this->buildLogicalScreenDescriptor(); - - if ($this->hasGlobalColorTable()) { - $encoded .= $this->getGlobalColorTable(); - } - - // netscape extension - if ($this->isAnimated() && $this->doesLoop()) { - $encoded .= $this->buildNetscapeExtension(); - } - - // add frame(s) - foreach ($this->frames as $frame) { - $encoded .= $this->buildFrame($frame); - } - - // EOF - $encoded .= "\x3B"; - - return $encoded; - } - - /** - * Build logical screen descriptor - * - * @return string - */ - private function buildLogicalScreenDescriptor() - { - // gif header - $descriptor = 'GIF89a'; - - // canvas width/height - $descriptor .= pack('v*', $this->canvasWidth); - $descriptor .= pack('v*', $this->canvasHeight); - - // packed field - $colorResolution = 111; - $sortFlag = 0; - - if ($this->hasGlobalColorTable()) { - - $globalColorTableFlag = 1; - $globalColorTableSize = log(strlen($this->getGlobalColorTable()) / 3, 2) - 1; - $globalColorTableSize = decbin($globalColorTableSize); - $globalColorTableSize = str_pad($globalColorTableSize, 3, 0, STR_PAD_LEFT); - - } else { - $globalColorTableFlag = 0; - $globalColorTableSize = 0; - } - - $packed = $globalColorTableFlag.$colorResolution.$sortFlag.$globalColorTableSize; - - $descriptor .= pack('C', bindec($packed)); - - // background color index - $descriptor .= pack('C', $this->backgroundColorIndex); - - // pixel aspect ratio - $descriptor .= "\x00"; - - return $descriptor; - } - - /** - * Build Netscape extension - * - * @return string - */ - private function buildNetscapeExtension() - { - $extension = "\x21"; - $extension .= "\xFF"; - $extension .= "\x0B"; - $extension .= 'NETSCAPE2.0'; - $extension .= "\x03"; - $extension .= "\x01"; - $extension .= pack('v', $this->loops); - $extension .= "\x00"; - - return $extension; - } - - /** - * Build encoded Frame - * - * @param Frame $frame - * @return string - */ - private function buildFrame(Frame $frame) - { - // graphics control extensions - $encoded = $this->buildGraphicsControlExtension($frame); - - // image descriptor - $encoded .= $this->buildImageDescriptor($frame); - - // add image data - $encoded .= $frame->getImageData(); - - return $encoded; - } - - /** - * Build encoded graphics control extension for frame - * - * @param Frame $frame - * @return string - */ - private function buildGraphicsControlExtension(Frame $frame) - { - // start - $extension = "\x21\xF9"; - - // byte size - $extension .= "\x04"; - - // packed field - $disposalMethod = decbin($frame->getDisposalMethod()); - $disposalMethod = str_pad($disposalMethod, 3, 0, STR_PAD_LEFT); - $userInputFlat = 0; - $transparentColorFlag = $frame->hasTransparentColor() ? 1 : 0; - $packed = $disposalMethod.$userInputFlat.$transparentColorFlag; - $packed = str_pad($packed, 3, 0, STR_PAD_LEFT); - $extension .= pack('C', bindec($packed)); - - // delay - $extension .= pack('v*', $frame->getDelay()); - - // transparent color index - if ($frame->hasTransparentColor()) { - $extension .= $frame->getTransparentColorIndex(); - } else { - $extension .= "\x00"; - } - - // block terminator - $extension .= "\x00"; - - return $extension; - } - - /** - * Build encoded image descriptor for frame - * - * @param Frame $frame - * @return string - */ - private function buildImageDescriptor(Frame $frame) - { - // seperator - $descriptor = "\x2C"; - - // image left/top - $descriptor .= pack('v*', - $frame->offset->left, - $frame->offset->top - ); - - // image width/height - $descriptor .= pack('v*', - $frame->size->width, - $frame->size->height - ); - - $interlacedFlag = $frame->isInterlaced() ? 1 : 0; - $reserved1 = 0; - $reserved2 = 0; - - if ($frame->hasLocalColorTable()) { - $colorTableFlag = 1; - $colorTableSize = log(strlen($frame->getLocalColorTable()) / 3, 2) - 1; - $colorTableSize = decbin($colorTableSize); - $sortFlag = 0; - } else { - $colorTableFlag = 0; - $colorTableSize = 0; - $sortFlag = 0; - } - - $colorTableSize = str_pad($colorTableSize, 3, 0, STR_PAD_LEFT); - - // packed field - $packed = $colorTableFlag.$interlacedFlag.$sortFlag.$reserved1.$reserved2.$colorTableSize; - $descriptor .= pack('C', bindec($packed)); - - if ($frame->hasLocalColorTable()) { - // add local color table - $descriptor .= $frame->getLocalColorTable(); - } - - return $descriptor; - } - - /** - * Determines if encoder has global color table - * - * @return boolean - */ - private function hasGlobalColorTable() - { - return isset($this->globalColorTable); - } - - /** - * Encode GD resource to GIF string - * - * @param resource $resource - * @return string - */ - private function encodeGdResource($resource) - { - ob_start(); - imagegif($resource); - $buffer = ob_get_contents(); - ob_end_clean(); - - return $buffer; - } - - /** - * Determines if current encoder is set up animated - * - * @return boolean - */ - public function isAnimated() - { - return count($this->frames) > 1; - } - - /** - * Determines if current encoder is set up to loop animation - * - * @return boolean - */ - public function doesLoop() - { - return is_integer($this->loops); - } -} diff --git a/src/Intervention/Image/Gd/Gif/Frame.php b/src/Intervention/Image/Gd/Gif/Frame.php deleted file mode 100644 index 3c06824cc..000000000 --- a/src/Intervention/Image/Gd/Gif/Frame.php +++ /dev/null @@ -1,376 +0,0 @@ -{$name} !== null); - } - - /** - * Creates and sets property with given value - * - * @param string $name - * @param Frame - */ - public function setProperty($name, $value) - { - $this->{$name} = $value; - - return $this; - } - - /** - * Sets image data of fram - * - * @param string $value - */ - public function setImageData($value) - { - $this->imageData = $value; - - return $this; - } - - /** - * Returns image data - * - * @return string - */ - public function getImageData() - { - return $this->imageData; - } - - /** - * Determines if instance has local color table - * - * @return boolean - */ - public function hasLocalColorTable() - { - return ! is_null($this->localColorTable); - } - - /** - * Returns local color table data of instance - * - * @return string - */ - public function getLocalColorTable() - { - return $this->localColorTable; - } - - /** - * Sets local color table of frame - * - * @param string $value - */ - public function setLocalColorTable($value) - { - $this->localColorTable = $value; - - return $this; - } - - /** - * Returns delay of frame - * - * @return integer - */ - public function getDelay() - { - return $this->propertyIsSet('delay') - ? $this->delay - : $this->decodeDelay(); - } - - /** - * Sets delay of frame - * - * @param integer $delay - */ - public function setDelay($delay) - { - $this->delay = $delay; - - return $this; - } - - /** - * Returns delay of current instance - * - * @return int - */ - public function decodeDelay() - { - if ($this->graphicsControlExtension) { - $byte = substr($this->graphicsControlExtension, 2, 2); - return (int) unpack('v', $byte)[1]; - } - - return false; - } - - /** - * Determines if instance has transparent colors - * - * @return boolean - */ - public function hasTransparentColor() - { - if ($this->graphicsControlExtension) { - $byte = substr($this->graphicsControlExtension, 1, 1); - $byte = unpack('C', $byte)[1]; - $bit = $byte & bindec('00000001'); - - return (bool) $bit; - } - - return false; - } - - /** - * Returns transparent color index of frame - * - * @return integer - */ - public function getTransparentColorIndex() - { - return $this->propertyIsSet('transparentColorIndex') - ? $this->transparentColorIndex - : $this->decodeTransparentColorIndex(); - } - - /** - * Sets transparent color index of frame - * - * @param integer $value - */ - public function setTransparentColorIndex($value) - { - $this->transparentColorIndex = $value; - - return $this; - } - - /** - * Returns index byte of transparent color - * - * @return string - */ - public function decodeTransparentColorIndex() - { - if ($this->graphicsControlExtension) { - return substr($this->graphicsControlExtension, 4, 1); - } - - return false; - } - - /** - * Returns disposal method of frame - * - * @return integer - */ - public function getDisposalMethod() - { - return $this->propertyIsSet('disposalMethod') - ? $this->disposalMethod - : $this->decodeDisposalMethod(); - } - - /** - * Defines disposal method of frame - * - * @param integer $value - */ - public function setDisposalMethod($value) - { - $this->disposalMethod = $value; - - return $this; - } - - /** - * Decodes disposal method of frame - * - * @return integer - */ - public function decodeDisposalMethod() - { - if ($this->graphicsControlExtension) { - $byte = substr($this->graphicsControlExtension, 1, 1); - $byte = unpack('C', $byte)[1]; - $method = $byte >> 2 & bindec('00000111'); - - return $method; - } - - return 0; - } - - /** - * Decodes frame width - * - * @return integer - */ - public function decodeWidth() - { - if ($this->imageDescriptor) { - return (int) unpack('v', - substr($this->imageDescriptor, 4, 2) - )[1]; - } - - return false; - } - - /** - * Decodes frame height - * - * @return integer - */ - public function decodeHeight() - { - if ($this->imageDescriptor) { - return (int) unpack('v', - substr($this->imageDescriptor, 6, 2) - )[1]; - } - - return false; - } - - /** - * Decodes width and height of frame to size object - * - * @return StdClass - */ - public function getSize() - { - $size = new \StdClass; - $size->width = $this->decodeWidth(); - $size->height = $this->decodeHeight(); - - return $size; - } - - /** - * Decodes left offset of frame - * - * @return integer - */ - public function decodeOffsetLeft() - { - if ($this->imageDescriptor) { - return (int) unpack('v', - substr($this->imageDescriptor, 0, 2) - )[1]; - } - - return false; - } - - /** - * Decodes top offset of frame - * - * @return integer - */ - public function decodeOffsetTop() - { - if ($this->imageDescriptor) { - return (int) unpack('v', - substr($this->imageDescriptor, 2, 2) - )[1]; - } - - return false; - } - - /** - * Decodes offsets into object - * - * @return StdClass - */ - public function getOffset() - { - $offset = new \StdClass; - $offset->left = $this->decodeOffsetLeft(); - $offset->top = $this->decodeOffsetTop(); - - return $offset; - } - - /** - * Sets frame offset - * - * @param integer $left - * @param integer $top - */ - public function setOffset($left, $top) - { - $offset = new \StdClass; - $offset->left = $left; - $offset->top = $top; - $this->offset = $offset; - - return $this; - } - - /** - * Mark frame as interlaced - * - * @param boolean $flag - */ - public function setInterlaced($flag) - { - $this->interlaced = $flag; - - return $this; - } - - /** - * Returns interlaced mode - * - * @return boolean - */ - public function getInterlaced() - { - return $this->propertyIsSet('interlaced') ? $this->interlaced : $this->decodeInterlaced(); - } - - /** - * Determines if current frame is saved as interlaced - * - * @return boolean - */ - public function isInterlaced() - { - return (boolean) $this->getInterlaced(); - } -} diff --git a/tests/GifDecodedTest.php b/tests/GifDecodedTest.php deleted file mode 100644 index 2710ea726..000000000 --- a/tests/GifDecodedTest.php +++ /dev/null @@ -1,144 +0,0 @@ -setHeader('foo'); - $this->assertEquals('foo', $decoded->getHeader()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); - } - - public function testSetGetLogicalScreenDescriptor() - { - $decoded = new Decoded; - $obj = $decoded->setLogicalScreenDescriptor('foo'); - $this->assertEquals('foo', $decoded->getLogicalScreenDescriptor()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); - } - - public function testSetGetGlobalColorTable() - { - $decoded = new Decoded; - $obj = $decoded->setGlobalColorTable('foo'); - $this->assertEquals('foo', $decoded->getGlobalColorTable()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); - } - - public function testSetGetNetscapeExtension() - { - $decoded = new Decoded; - $obj = $decoded->setNetscapeExtension('foo'); - $this->assertEquals('foo', $decoded->getNetscapeExtension()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); - } - - public function testSetGetPlaintextExtension() - { - $decoded = new Decoded; - $obj = $decoded->setPlaintextExtension('foo'); - $this->assertEquals('foo', $decoded->getPlaintextExtension()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); - } - - public function testSetGetCommentExtension() - { - $decoded = new Decoded; - $obj = $decoded->setCommentExtension('foo'); - $this->assertEquals('foo', $decoded->getCommentExtension()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $obj); - } - - public function testGetCanvasWidth() - { - $decoded = new Decoded; - $this->assertEquals(false, $decoded->getCanvasWidth()); - - $decoded = new Decoded; - $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x00\x00"); - $this->assertEquals(10, $decoded->getCanvasWidth()); - } - - public function testGetCanvasHeight() - { - $decoded = new Decoded; - $this->assertEquals(false, $decoded->getCanvasHeight()); - - $decoded = new Decoded; - $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x00\x00"); - $this->assertEquals(10, $decoded->getCanvasHeight()); - } - - public function testGetLoops() - { - $decoded = new Decoded; - $this->assertEquals(null, $decoded->getLoops()); - - $decoded = new Decoded; - $decoded->setNetscapeExtension("\x0B\x4E\x45\x54\x53\x43\x41\x50\x45\x32\x2E\x30\x03\x01\x05\x00\x00"); - $this->assertEquals(5, $decoded->getLoops()); - } - - public function testHasGlobalColorTable() - { - $decoded = new Decoded; - $this->assertFalse($decoded->hasGlobalColorTable()); - - $decoded = new Decoded; - $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x00\x00"); - $this->assertFalse($decoded->hasGlobalColorTable()); - - $decoded = new Decoded; - $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x91\x00\x00"); - $this->assertTrue($decoded->hasGlobalColorTable()); - } - - public function testCountGlobalColors() - { - $decoded = new Decoded; - $this->assertEquals(0, $decoded->countGlobalColors()); - - $decoded = new Decoded; - $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x91\x00\x00"); - $this->assertEquals(4, $decoded->countGlobalColors()); - - $decoded = new Decoded; - $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x91\x00\x00"); - $this->assertEquals(4, $decoded->countGlobalColors()); - } - - public function testGetBackgroundColorIndex() - { - $decoded = new Decoded; - $this->assertEquals(0, $decoded->getBackgroundColorIndex()); - - $decoded = new Decoded; - $decoded->setLogicalScreenDescriptor("\x0A\x00\x0A\x00\x00\x05\x00"); - $this->assertEquals(5, $decoded->getBackgroundColorIndex()); - } - - public function testGetFrame() - { - $decoded = new Decoded; - $decoded->addImageData('foo'); - $decoded->addImageData('bar'); - $frame = $decoded->getFrame(); - $this->assertInstanceOf('\Intervention\Image\Gd\Gif\Frame', $frame); - $this->assertEquals('foo', $frame->imageData); - $frame = $decoded->getFrame(1); - $this->assertInstanceOf('\Intervention\Image\Gd\Gif\Frame', $frame); - $this->assertEquals('bar', $frame->imageData); - } - - /** - * @expectedException \Intervention\Image\Exception\RuntimeException - */ - public function testGetFrameNotExisting() - { - $decoded = new Decoded; - $frame = $decoded->getFrame(); - } -} diff --git a/tests/GifDecoderTest.php b/tests/GifDecoderTest.php deleted file mode 100644 index 614caf3ad..000000000 --- a/tests/GifDecoderTest.php +++ /dev/null @@ -1,86 +0,0 @@ -decoder = $this->getTestDecoder('tests/images/animation.gif'); - } - - public function tearDown() - { - # code... - } - - private function getTestDecoder($file) - { - return new Decoder($file); - } - - public function testConstructorFromFile() - { - $decoder = new Decoder('tests/images/animation.gif'); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoder', $decoder); - } - - public function testInitFromData() - { - $data = file_get_contents('tests/images/animation.gif'); - - $decoder = new Decoder; - $decoder->initFromData($data); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoder', $decoder); - } - - public function testDecode() - { - $decoded = $this->decoder->decode(); - - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Decoded', $decoded); - $this->assertEquals(8, $decoded->countFrames()); - $this->assertTrue($decoded->hasGlobalColorTable()); - $this->assertEquals(32, $decoded->countGlobalColors()); - $this->assertEquals(2, $decoded->getLoops()); - - $offsets = array( - array('left' => 0, 'top' => 0), - array('left' => 5, 'top' => 2), - array('left' => 1, 'top' => 0), - array('left' => 0, 'top' => 0), - array('left' => 8, 'top' => 5), - array('left' => 5, 'top' => 2), - array('left' => 1, 'top' => 0), - array('left' => 0, 'top' => 0) - ); - - $sizes = array( - array('width' => 20, 'height' => 15), - array('width' => 10, 'height' => 10), - array('width' => 17, 'height' => 15), - array('width' => 20, 'height' => 15), - array('width' => 5, 'height' => 5), - array('width' => 10, 'height' => 10), - array('width' => 17, 'height' => 15), - array('width' => 20, 'height' => 15) - ); - - $delays = array(20, 20, 20, 20, 20, 20, 20, 20); - $interlaced = array(true, false, false, false, false, false, false, false); - $localColorTables = array(null, null, null, null, null, null, null, null); - - foreach ($decoded->getFrames() as $key => $frame) { - $this->assertEquals($sizes[$key]['width'], $frame->size->width); - $this->assertEquals($sizes[$key]['height'], $frame->size->height); - $this->assertEquals($offsets[$key]['left'], $frame->offset->left); - $this->assertEquals($offsets[$key]['top'], $frame->offset->top); - $this->assertEquals($delays[$key], $frame->decodeDelay()); - $this->assertEquals($interlaced[$key], $frame->isInterlaced()); - $this->assertEquals($localColorTables[$key], $frame->getLocalColorTable()); - $this->assertFalse($frame->hasLocalColorTable()); - } - } -} diff --git a/tests/GifEncodeDecodeTest.php b/tests/GifEncodeDecodeTest.php deleted file mode 100644 index 548c0b58c..000000000 --- a/tests/GifEncodeDecodeTest.php +++ /dev/null @@ -1,84 +0,0 @@ -decode(); - - // check before encoding - $this->assertEquals(20, $decoded->getCanvasWidth()); - $this->assertEquals(15, $decoded->getCanvasHeight()); - $this->assertEquals(8, $decoded->countFrames()); - $this->assertEquals(2, $decoded->getLoops()); - $this->assertEquals(32, $decoded->countGlobalColors()); - $this->assertTrue($decoded->hasGlobalColorTable()); - - foreach ($decoded->getFrames() as $frame) { - $this->assertEquals(20, $frame->getDelay()); - } - - // encode Decoded - $encoder = new Encoder; - $encoder->setFromDecoded($decoded); - $encoded = $encoder->encode(); - $decoder->initFromData($encoded); - $decoded = $decoder->decode(); - - // check after encoding - $this->assertEquals(20, $decoded->getCanvasWidth()); - $this->assertEquals(15, $decoded->getCanvasHeight()); - $this->assertEquals(8, $decoded->countFrames()); - $this->assertEquals(2, $decoded->getLoops()); - $this->assertEquals(32, $decoded->countGlobalColors()); - $this->assertTrue($decoded->hasGlobalColorTable()); - - foreach ($decoded->getFrames() as $frame) { - $this->assertEquals(20, $frame->getDelay()); - } - } - - public function testDecodeEncoded() - { - // create two resource - $res1 = imagecreatetruecolor(20, 15); - imagefill($res1, 0, 0, 850736919); - $res2 = imagecreatetruecolor(20, 15); - imagefill($res1, 0, 0, 11876119); - - // create encoded - $encoder = new Encoder; - $encoder->setCanvas(20, 15); - $encoder->setLoops(2); - $encoder->createFrameFromGdResource($res1, 100); - $encoder->createFrameFromGdResource($res2, 100); - $encoded = $encoder->encode(); - - // decode encoded - $decoder = new Decoder; - $decoder->initFromData($encoded); - $decoded = $decoder->decode(); - - // check after decoding - $this->assertEquals(20, $decoded->getCanvasWidth()); - $this->assertEquals(15, $decoded->getCanvasHeight()); - $this->assertEquals(2, $decoded->countFrames()); - $this->assertEquals(2, $decoded->getLoops()); - $this->assertEquals(32, $decoded->countGlobalColors()); - $this->assertFalse($decoded->hasGlobalColorTable()); - - foreach ($decoded->getFrames() as $frame) { - $this->assertEquals(100, $frame->getDelay()); - } - } -} diff --git a/tests/GifEncoderTest.php b/tests/GifEncoderTest.php deleted file mode 100644 index 9e8c6b7bb..000000000 --- a/tests/GifEncoderTest.php +++ /dev/null @@ -1,106 +0,0 @@ -setCanvas(300, 200); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); - $this->assertEquals(300, $encoder->canvasWidth); - $this->assertEquals(200, $encoder->canvasHeight); - } - - public function testSetLoops() - { - $encoder = new Encoder; - $result = $encoder->setLoops(6); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); - $this->assertEquals(6, $encoder->loops); - } - - public function testSetGlobalColorTable() - { - $encoder = new Encoder; - $result = $encoder->setGlobalColorTable('foo'); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); - $this->assertEquals('foo', $encoder->globalColorTable); - } - - public function testSetFrames() - { - $encoder = new Encoder; - $frames = array('foo', 'bar', 'baz'); - $encoder->setFrames($frames); - $this->assertEquals($frames, $encoder->frames); - } - - public function testSetBackgroundColorIndex() - { - $encoder = new Encoder; - $result = $encoder->setBackgroundColorIndex('foo'); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Encoder', $result); - $this->assertEquals('foo', $encoder->backgroundColorIndex); - } - - public function testSetFromDecoded() - { - $encoder = new Encoder; - $decoded = Mockery::mock('Intervention\Image\Gd\Gif\Decoded'); - $decoded->shouldReceive('getCanvasWidth')->andReturn(300); - $decoded->shouldReceive('getCanvasHeight')->andReturn(200); - $decoded->shouldReceive('getGlobalColorTable')->andReturn('global_color_table'); - $decoded->shouldReceive('getBackgroundColorIndex')->andReturn('background_color_index'); - $decoded->shouldReceive('getLoops')->andReturn(2); - $decoded->shouldReceive('getFrames')->andReturn(array('frame1', 'frame2', 'frame3')); - $encoder->setFromDecoded($decoded); - $this->assertEquals(300, $encoder->canvasWidth); - $this->assertEquals(200, $encoder->canvasHeight); - $this->assertEquals('global_color_table', $encoder->globalColorTable); - $this->assertEquals('background_color_index', $encoder->backgroundColorIndex); - $this->assertEquals(2, $encoder->loops); - $this->assertEquals(3, count($encoder->frames)); - $this->assertTrue($encoder->doesLoop()); - } - - public function testAddFrame() - { - $encoder = new Encoder; - $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); - $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); - $this->assertEquals(2, count($encoder->frames)); - } - - public function testCreateFrameFromGdResource() - { - $encoder = new Encoder; - $resource = imagecreate(10, 10); - $encoder->createFrameFromGdResource($resource, 10); - $this->assertEquals(1, count($encoder->frames)); - } - - public function testIsAnimated() - { - $encoder = new Encoder; - $this->assertFalse($encoder->isAnimated()); - $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); - $this->assertFalse($encoder->isAnimated()); - $encoder->addFrame(Mockery::mock('Intervention\Image\Gd\Gif\Frame')); - $this->assertTrue($encoder->isAnimated()); - } - - public function testDoesLoop() - { - $encoder = new Encoder; - $this->assertFalse($encoder->doesLoop()); - $encoder->setLoops(10); - $this->assertTrue($encoder->doesLoop()); - } -} diff --git a/tests/GifFrameTest.php b/tests/GifFrameTest.php deleted file mode 100644 index 417083f18..000000000 --- a/tests/GifFrameTest.php +++ /dev/null @@ -1,181 +0,0 @@ -assertFalse($frame->propertyIsSet('foo')); - - $frame = new Frame; - $frame->foo = 'bar'; - $this->assertTrue($frame->propertyIsSet('foo')); - - $frame = new Frame; - $frame->foo = false; - $this->assertTrue($frame->propertyIsSet('foo')); - } - - public function testSetProperty() - { - $frame = new Frame; - $this->assertFalse($frame->propertyIsSet('foo')); - $test = $frame->setProperty('foo', 'bar'); - $this->assertEquals('bar', $frame->foo); - $this->assertTrue($frame->propertyIsSet('foo')); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $test); - } - - public function testSetGetImageData() - { - $frame = new Frame; - $this->assertNull($frame->getImageData()); - $result = $frame->setImageData('foo'); - $this->assertEquals('foo', $frame->getImageData()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); - } - - public function testSetGetDelay() - { - $frame = new Frame; - $this->assertFalse($frame->getDelay()); - - $frame = new Frame; - $result = $frame->setDelay(20); - $this->assertEquals(20, $frame->getDelay()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); - } - - public function testDecodeDelay() - { - $frame = new Frame; - $frame->setProperty('graphicsControlExtension', "\x04\x00\x14\x00\x00\x00"); - $this->assertEquals(20, $frame->decodeDelay()); - } - - public function testHasTransparentColor() - { - $frame = new Frame; - $this->assertFalse($frame->hasTransparentColor()); - - $frame = new Frame; - $frame->setProperty('graphicsControlExtension', "\x04\x01\x14\x00\x00\x00"); - $this->assertTrue($frame->hasTransparentColor()); - } - - public function testSetGetTransparentColorIndex() - { - $frame = new Frame; - $this->assertFalse($frame->getTransparentColorIndex()); - - $frame = new Frame; - $result = $frame->setTransparentColorIndex('foo'); - $this->assertEquals('foo', $frame->getTransparentColorIndex()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); - } - - public function testDecodeTransparentColorIndex() - { - $frame = new Frame; - $this->assertFalse($frame->decodeTransparentColorIndex()); - - $frame = new Frame; - $frame->setProperty('graphicsControlExtension', "\x04\x01\x14\x00\xF1\x00"); - $this->assertEquals("\xF1", $frame->decodeTransparentColorIndex()); - } - - public function testSetGetDisposalMethod() - { - $frame = new Frame; - $this->assertEquals(0, $frame->getDisposalMethod()); - - $frame = new Frame; - $result = $frame->setDisposalMethod('foo'); - $this->assertEquals('foo', $frame->getDisposalMethod()); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $result); - } - - public function testDecodeDisposalMethod() - { - $frame = new Frame; - $this->assertEquals(0, $frame->decodeDisposalMethod()); - - $frame = new Frame; - $frame->setProperty('graphicsControlExtension', "\x04\x05\x14\x00\xF1\x00"); - $this->assertEquals(1, $frame->decodeDisposalMethod()); - - $frame = new Frame; - $frame->setProperty('graphicsControlExtension', "\x04\x0C\x14\x00\xF1\x00"); - $this->assertEquals(3, $frame->decodeDisposalMethod()); - } - - public function testDecodeWidth() - { - $frame = new Frame; - $this->assertFalse($frame->decodeWidth()); - - $frame = new Frame; - $frame->setProperty('imageDescriptor', "\x00\x00\x00\x00\x40\x01\xF0\x00\x00"); - $this->assertEquals(320, $frame->decodeWidth()); - } - - public function testDecodeHeight() - { - $frame = new Frame; - $this->assertFalse($frame->decodeHeight()); - - $frame = new Frame; - $frame->setProperty('imageDescriptor', "\x00\x00\x00\x00\x40\x01\xF0\x00\x00"); - $this->assertEquals(240, $frame->decodeHeight()); - } - - public function testGetSize() - { - $frame = new Frame; - $frame->setProperty('imageDescriptor', "\x00\x00\x00\x00\x40\x01\xF0\x00\x00"); - $size = $frame->getSize(); - $this->assertInstanceOf('StdClass', $size); - $this->assertEquals(320, $size->width); - $this->assertEquals(240, $size->height); - } - - public function testDecodeOffsetTop() - { - $frame = new Frame; - $this->assertFalse($frame->decodeOffsetTop()); - - $frame = new Frame; - $frame->setProperty('imageDescriptor', "\x18\x00\x0C\x00\x40\x01\xF0\x00\x00"); - $this->assertEquals(12, $frame->decodeOffsetTop()); - } - - public function testDecodeOffsetLeft() - { - $frame = new Frame; - $this->assertFalse($frame->decodeOffsetLeft()); - - $frame = new Frame; - $frame->setProperty('imageDescriptor', "\x18\x00\x0C\x00\x40\x01\xF0\x00\x00"); - $this->assertEquals(24, $frame->decodeOffsetLeft()); - } - - public function testGetOffset() - { - $frame = new Frame; - $frame->setProperty('imageDescriptor', "\x18\x00\x0C\x00\x40\x01\xF0\x00\x00"); - $offset = $frame->getOffset(); - $this->assertInstanceOf('StdClass', $offset); - $this->assertEquals(12, $offset->top); - $this->assertEquals(24, $offset->left); - } - - public function testSetInterlaced() - { - $frame = new Frame; - $frame->setInterlaced(true); - $this->assertTrue($frame->interlaced); - $this->assertInstanceOf('Intervention\Image\Gd\Gif\Frame', $frame); - } -} From b7417532be516d7cde4c3cb6966ed5cf566867ec Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 4 Oct 2015 19:14:28 +0200 Subject: [PATCH 82/88] phpunit version update --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index db60c4b57..6c3574f70 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "ext-fileinfo": "*" }, "require-dev": { - "phpunit/phpunit": "3.*", + "phpunit/phpunit": "~4.0", "mockery/mockery": "~0.9.2" }, "suggest": { From 8d98d37eb02c81f3ddf9677a5c4c97c0a02e6745 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 4 Oct 2015 19:22:48 +0200 Subject: [PATCH 83/88] removed stream + psrResponse --- src/Intervention/Image/Image.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Intervention/Image/Image.php b/src/Intervention/Image/Image.php index 48fff26dc..fa0f31dae 100644 --- a/src/Intervention/Image/Image.php +++ b/src/Intervention/Image/Image.php @@ -2,9 +2,6 @@ namespace Intervention\Image; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; - /** * @method \Intervention\Image\Image backup(string $name = 'default') Backups current image state as fallback for reset method under an optional name. Overwrites older state on every call, unless a different name is passed. * @method \Intervention\Image\Image blur(integer $amount = 1) Apply a gaussian blur filter with a optional amount on the current image. Use values between 0 and 100. @@ -48,9 +45,8 @@ * @method \Intervention\Image\Image text(string $text, integer $x = 0, integer $y = 0, \Closure $callback = null) Write a text string to the current image at an optional x,y basepoint position. You can define more details like font-size, font-file and alignment via a callback as the fourth parameter. * @method \Intervention\Image\Image trim(string $base = 'top-left', array $away = array('top', 'bottom', 'left', 'right'), integer $tolerance = 0, integer $feather = 0) Trim away image space in given color. Define an optional base to pick a color at a certain position and borders that should be trimmed away. You can also set an optional tolerance level, to trim similar colors and add a feathering border around the trimed image. * @method \Intervention\Image\Image widen(integer $width, \Closure $callback = null) Resizes the current image to new width, constraining aspect ratio. Pass an optional Closure callback as third parameter, to apply additional constraints like preventing possible upsizing. - * @method StreamInterface stream(string $format = null, integer $quality = 90) Build PSR-7 compatible StreamInterface with current image in given format and quality. - * @method ResponseInterface psrResponse(string $format = null, integer $quality = 90) Build PSR-7 compatible ResponseInterface with current image in given format and quality. */ + class Image extends File implements \IteratorAggregate { /** From b333684543ba8dc1cdeffa97fc14883ce12bfd06 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Fri, 20 Nov 2015 16:55:32 +0100 Subject: [PATCH 84/88] changed checksum calculation --- src/Intervention/Image/Commands/ChecksumCommand.php | 12 +----------- tests/ChecksumCommandTest.php | 9 +++------ 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/Intervention/Image/Commands/ChecksumCommand.php b/src/Intervention/Image/Commands/ChecksumCommand.php index f8944bf18..ef5c68d04 100644 --- a/src/Intervention/Image/Commands/ChecksumCommand.php +++ b/src/Intervention/Image/Commands/ChecksumCommand.php @@ -12,17 +12,7 @@ class ChecksumCommand extends AbstractCommand */ public function execute($image) { - $colors = array(); - - $size = $image->getSize(); - - for ($x=0; $x <= ($size->width-1); $x++) { - for ($y=0; $y <= ($size->height-1); $y++) { - $colors[] = $image->pickColor($x, $y, 'array'); - } - } - - $this->setOutput(md5(serialize($colors))); + $this->setOutput(md5($image->encode())); return true; } diff --git a/tests/ChecksumCommandTest.php b/tests/ChecksumCommandTest.php index cf42f7ddf..5435e6efd 100644 --- a/tests/ChecksumCommandTest.php +++ b/tests/ChecksumCommandTest.php @@ -11,16 +11,13 @@ public function tearDown() public function testExecute() { - $size = Mockery::mock('Intervention\Image\Size', array(3, 3)); - $color = array(0,0,0,1); - $resource = imagecreatefrompng(__DIR__.'/images/tile.png'); $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('pickColor')->times(9)->andReturn($color); + $image->shouldReceive('encode')->once()->andReturn('encoded_image_data'); + $command = new ChecksumCommand(array()); $result = $command->execute($image); $this->assertTrue($result); $this->assertTrue($command->hasOutput()); - $this->assertEquals('ec9cbdb71be04e26b4a89333f20c273b', $command->getOutput()); + $this->assertEquals(md5('encoded_image_data'), $command->getOutput()); } } From 7593f267144177f6d982ae2d5e501cbc35a21f83 Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 6 Dec 2015 11:47:19 +0100 Subject: [PATCH 85/88] removed command tests --- composer.json | 4 +- tests/BackupCommandTest.php | 60 ----------------- tests/BlurCommandTest.php | 26 -------- tests/BrightnessCommandTest.php | 33 --------- tests/CircleCommandTest.php | 41 ------------ tests/CircleShapeTest.php | 22 +----- tests/ColorizeCommandTest.php | 29 -------- tests/CommandTestCase.php | 58 ---------------- tests/ContrastCommandTest.php | 26 -------- tests/CropCommandTest.php | 31 --------- tests/DestroyCommandTest.php | 44 ------------ tests/EllipseCommandTest.php | 38 ----------- tests/EllipseShapeTest.php | 22 +----- tests/ExifCommandTest.php | 69 ------------------- tests/FillCommandTest.php | 104 ----------------------------- tests/FitCommandTest.php | 91 ------------------------- tests/FlipCommandTest.php | 37 ---------- tests/GammaCommandTest.php | 31 --------- tests/GetsizeCommandTest.php | 38 ----------- tests/GreyscaleCommandTest.php | 29 -------- tests/HeightenCommandTest.php | 47 ------------- tests/InsertCommandTest.php | 66 ------------------ tests/InterlaceCommandTest.php | 33 --------- tests/InvertCommandTest.php | 29 -------- tests/IptcCommandTest.php | 71 -------------------- tests/LimitColorsCommandTest.php | 42 ------------ tests/LineCommandTest.php | 39 ----------- tests/LineShapeTest.php | 20 +----- tests/MaskCommandTest.php | 67 ------------------- tests/OpacityCommandTest.php | 43 ------------ tests/OrientateCommandTest.php | 93 -------------------------- tests/PickColorCommandTest.php | 66 ------------------ tests/PixelCommandTest.php | 29 -------- tests/PixelateCommandTest.php | 32 --------- tests/PolygonCommandTest.php | 40 ----------- tests/PolygonShapeTest.php | 22 +----- tests/PsrResponseCommandTest.php | 57 ---------------- tests/RectangleCommandTest.php | 38 ----------- tests/RectangleShapeTest.php | 20 +----- tests/ResetCommandTest.php | 70 ------------------- tests/ResizeCanvasCommandTest.php | 72 -------------------- tests/ResizeCommandTest.php | 45 ------------- tests/RotateCommandTest.php | 30 --------- tests/SharpenCommandTest.php | 29 -------- tests/StopAnimationCommandTest.php | 36 ---------- tests/StreamCommandTest.php | 35 ---------- tests/TextCommandTest.php | 31 --------- tests/TrimCommandTest.php | 53 --------------- tests/WidenCommandTest.php | 47 ------------- 49 files changed, 7 insertions(+), 2128 deletions(-) delete mode 100644 tests/BackupCommandTest.php delete mode 100644 tests/BlurCommandTest.php delete mode 100644 tests/BrightnessCommandTest.php delete mode 100644 tests/CircleCommandTest.php delete mode 100644 tests/ColorizeCommandTest.php delete mode 100644 tests/CommandTestCase.php delete mode 100644 tests/ContrastCommandTest.php delete mode 100644 tests/CropCommandTest.php delete mode 100644 tests/DestroyCommandTest.php delete mode 100644 tests/EllipseCommandTest.php delete mode 100644 tests/ExifCommandTest.php delete mode 100644 tests/FillCommandTest.php delete mode 100644 tests/FitCommandTest.php delete mode 100644 tests/FlipCommandTest.php delete mode 100644 tests/GammaCommandTest.php delete mode 100644 tests/GetsizeCommandTest.php delete mode 100644 tests/GreyscaleCommandTest.php delete mode 100644 tests/HeightenCommandTest.php delete mode 100644 tests/InsertCommandTest.php delete mode 100644 tests/InterlaceCommandTest.php delete mode 100644 tests/InvertCommandTest.php delete mode 100644 tests/IptcCommandTest.php delete mode 100644 tests/LimitColorsCommandTest.php delete mode 100644 tests/LineCommandTest.php delete mode 100644 tests/MaskCommandTest.php delete mode 100644 tests/OpacityCommandTest.php delete mode 100644 tests/OrientateCommandTest.php delete mode 100644 tests/PickColorCommandTest.php delete mode 100644 tests/PixelCommandTest.php delete mode 100644 tests/PixelateCommandTest.php delete mode 100644 tests/PolygonCommandTest.php delete mode 100644 tests/PsrResponseCommandTest.php delete mode 100644 tests/RectangleCommandTest.php delete mode 100644 tests/ResetCommandTest.php delete mode 100644 tests/ResizeCanvasCommandTest.php delete mode 100644 tests/ResizeCommandTest.php delete mode 100644 tests/RotateCommandTest.php delete mode 100644 tests/SharpenCommandTest.php delete mode 100644 tests/StopAnimationCommandTest.php delete mode 100644 tests/StreamCommandTest.php delete mode 100644 tests/TextCommandTest.php delete mode 100644 tests/TrimCommandTest.php delete mode 100644 tests/WidenCommandTest.php diff --git a/composer.json b/composer.json index 6c3574f70..61e250535 100644 --- a/composer.json +++ b/composer.json @@ -1,8 +1,8 @@ { "name": "intervention/image", - "description": "Image handling and manipulation library with support for Laravel integration", + "description": "PHP Image Manipulation", "homepage": "http://image.intervention.io/", - "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail"], + "keywords": ["image", "gd", "imagick", "laravel", "watermark", "thumbnail", "animation", "resize"], "license": "MIT", "authors": [ { diff --git a/tests/BackupCommandTest.php b/tests/BackupCommandTest.php deleted file mode 100644 index 3f7967dda..000000000 --- a/tests/BackupCommandTest.php +++ /dev/null @@ -1,60 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('setBackup')->once(); - $command = new BackupGd(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdWithName() - { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('setBackup')->once(); - $command = new BackupGd(array('name' => 'fooBackup')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithoutName() - { - $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithName() - { - $imagick = Mockery::mock('Imagick'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setBackup')->once(); - $command = new BackupImagick(array('name' => 'fooBackup')); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/BlurCommandTest.php b/tests/BlurCommandTest.php deleted file mode 100644 index 634bd70b4..000000000 --- a/tests/BlurCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -getTestImage('gd'); - - $command = new BlurGd(array(2)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('blurimage')->with(2, 1)->times(3)->andReturn(true); - - $command = new BlurImagick(array(2)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/BrightnessCommandTest.php b/tests/BrightnessCommandTest.php deleted file mode 100644 index 999a966c8..000000000 --- a/tests/BrightnessCommandTest.php +++ /dev/null @@ -1,33 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new BrightnessGd(array(12)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('modulateimage')->with(112, 100, 100)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new BrightnessImagick(array(12)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/CircleCommandTest.php b/tests/CircleCommandTest.php deleted file mode 100644 index fadd1f8ec..000000000 --- a/tests/CircleCommandTest.php +++ /dev/null @@ -1,41 +0,0 @@ -getTestImage('gd'); - - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new CircleCommand(array(250, 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - - $image->getCore()->shouldReceive('drawimage')->times(3); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new CircleCommand(array(25, 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/CircleShapeTest.php b/tests/CircleShapeTest.php index 8c5777730..44c404eaa 100644 --- a/tests/CircleShapeTest.php +++ b/tests/CircleShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\CircleShape as CircleGd; use Intervention\Image\Imagick\Shapes\CircleShape as CircleImagick; -class CircleShapeTest extends CommandTestCase +class CircleShapeTest extends PHPUnit_Framework_TestCase { public function tearDown() { @@ -18,30 +18,10 @@ public function testGdConstructor() } - public function testGdApplyToImage() - { - $image = $this->getTestImage('gd'); - $circle = new CircleGd(250); - $result = $circle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\CircleShape', $circle); - $this->assertTrue($result); - } - public function testImagickConstructor() { $circle = new CircleImagick(250); $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\CircleShape', $circle); $this->assertEquals(250, $circle->width); } - - public function testImagickApplyToImage() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $circle = new CircleImagick(250); - $result = $circle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\CircleShape', $circle); - $this->assertTrue($result); - } - } diff --git a/tests/ColorizeCommandTest.php b/tests/ColorizeCommandTest.php deleted file mode 100644 index 29cdce2f0..000000000 --- a/tests/ColorizeCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -getTestImage('gd'); - - $command = new ColorizeGd(array(20, 0, -40)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('getquantumrange')->times(3)->with()->andReturn(array('quantumRangeLong' => 42)); - $image->getCore()->shouldReceive('levelimage')->times(3)->with(0, 4, 42, \Imagick::CHANNEL_RED)->andReturn(true); - $image->getCore()->shouldReceive('levelimage')->times(3)->with(0, 1, 42, \Imagick::CHANNEL_GREEN)->andReturn(true); - $image->getCore()->shouldReceive('levelimage')->times(3)->with(0, 0.6, 42, \Imagick::CHANNEL_BLUE)->andReturn(true); - - $command = new ColorizeImagick(array(20, 0, -40)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/CommandTestCase.php b/tests/CommandTestCase.php deleted file mode 100644 index d0cf23238..000000000 --- a/tests/CommandTestCase.php +++ /dev/null @@ -1,58 +0,0 @@ -shouldReceive('getIterator')->andReturn($iterator); - $frame->shouldReceive('getCore')->andReturn($resource); - $frame->shouldReceive('setCore')->andReturn($resource); - $image->shouldReceive('getCore')->andReturn($resource); - $image->shouldReceive('getIterator')->andReturn($container); - break; - - case 'imagick': - $image = Mockery::mock('Intervention\Image\Image'); - $container = Mockery::mock('Intervention\Image\Imagick\Container'); - $imagick = Mockery::mock('Imagick'); - $frame = Mockery::mock('Intervention\Image\Frame'); - - $iterator = new ArrayIterator(array($imagick)); - - $frame->shouldReceive('getCore')->andReturn($imagick); - $container->shouldReceive('rewind')->once(); - $container->shouldReceive('valid')->once()->andReturn(true); - $container->shouldReceive('valid')->once()->andReturn(true); - $container->shouldReceive('valid')->once()->andReturn(true); - $container->shouldReceive('valid')->once()->andReturn(false); - $container->shouldReceive('current')->times(3)->andReturn($frame); - $container->shouldReceive('next'); - $container->shouldReceive('key'); - $image->shouldReceive('getIterator')->once()->andReturn($container); - $image->shouldReceive('getCore')->andReturn($imagick); - $imagick->shouldReceive('rewind'); - $imagick->shouldReceive('current')->andReturn($imagick); - $imagick->shouldReceive('valid')->andReturn(true); - break; - - default: - throw new Exception('No type defined'); - } - - return $image; - } -} diff --git a/tests/ContrastCommandTest.php b/tests/ContrastCommandTest.php deleted file mode 100644 index 7d7b9b6e1..000000000 --- a/tests/ContrastCommandTest.php +++ /dev/null @@ -1,26 +0,0 @@ -getTestImage('gd'); - - $command = new ContrastGd(array(20)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('sigmoidalcontrastimage')->times(3)->with(true, 5, 0)->andReturn(true); - - $command = new ContrastImagick(array(20)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/CropCommandTest.php b/tests/CropCommandTest.php deleted file mode 100644 index e6908fb07..000000000 --- a/tests/CropCommandTest.php +++ /dev/null @@ -1,31 +0,0 @@ -getTestImage('gd'); - $command = new CropGd(array(100, 150, 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('cropimage')->with(100, 150, 10, 20)->times(3)->andReturn(true); - $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->times(3); - - $command = new CropImagick(array(100, 150, 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/DestroyCommandTest.php b/tests/DestroyCommandTest.php deleted file mode 100644 index 9099098c7..000000000 --- a/tests/DestroyCommandTest.php +++ /dev/null @@ -1,44 +0,0 @@ -getTestImage('gd'); - $image->shouldReceive('getBackups')->once()->andReturn($backups); - - $command = new DestroyGd(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->with()->andReturn(true); - - $backup = Mockery::mock('Imagick'); - $backup->shouldReceive('clear')->with()->andReturn(true); - $backups = array($backup); - - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('getBackups')->once()->andReturn($backups); - $command = new DestroyImagick(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/EllipseCommandTest.php b/tests/EllipseCommandTest.php deleted file mode 100644 index 13ce2bbb9..000000000 --- a/tests/EllipseCommandTest.php +++ /dev/null @@ -1,38 +0,0 @@ -getTestImage('gd'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $command = new EllipseCommand(array(250, 150, 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new EllipseCommand(array(250, 150, 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/EllipseShapeTest.php b/tests/EllipseShapeTest.php index c772cb3f9..d505afbdb 100644 --- a/tests/EllipseShapeTest.php +++ b/tests/EllipseShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\EllipseShape as EllipseGd; use Intervention\Image\Imagick\Shapes\EllipseShape as EllipseImagick; -class EllipseShapeTest extends CommandTestCase +class EllipseShapeTest extends PHPUnit_Framework_TestCase { public function tearDown() { @@ -19,15 +19,6 @@ public function testGdConstructor() } - public function testGdApplyToImage() - { - $image = $this->getTestImage('gd'); - $ellipse = new EllipseGd(250, 150); - $result = $ellipse->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\EllipseShape', $ellipse); - $this->assertTrue($result); - } - public function testImagickConstructor() { $ellipse = new EllipseImagick(250, 150); @@ -36,15 +27,4 @@ public function testImagickConstructor() $this->assertEquals(150, $ellipse->height); } - - public function testImagickApplyToImage() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $ellipse = new EllipseImagick(250, 150); - $result = $ellipse->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\EllipseShape', $ellipse); - $this->assertTrue($result); - } - } diff --git a/tests/ExifCommandTest.php b/tests/ExifCommandTest.php deleted file mode 100644 index 80996f089..000000000 --- a/tests/ExifCommandTest.php +++ /dev/null @@ -1,69 +0,0 @@ -dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - } - - public function testFetchDefined() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand(array('Artist')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - public function testFetchNonExisting() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new ExifCommand(array('xxx')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testFetchFromPng() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'star.png'; - $command = new ExifCommand(array('Orientation')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testReturnNullOnExifReadFail() - { - $image = Mockery::mock('Intervention\Image\Image'); - $command = new ExifCommand(array('Orientation')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } -} diff --git a/tests/FillCommandTest.php b/tests/FillCommandTest.php deleted file mode 100644 index 77a56359d..000000000 --- a/tests/FillCommandTest.php +++ /dev/null @@ -1,104 +0,0 @@ -getTestImage('gd'); - $image->shouldReceive('getDriver')->andReturn($driver); - $driver->shouldReceive('init')->with('666666')->once()->andReturn($image); - - $command = new FillGd(array('666666')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillArray() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd(array(array(50, 50, 50))); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillArrayWithAlpha() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new FillGd(array(array(50, 50, 50, .50))); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFillWithCoordinates() - { - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $image = $this->getTestImage('gd'); - $image->shouldReceive('getDriver')->andReturn($driver); - $driver->shouldReceive('init')->with('666666')->once()->andReturn($image); - $driver->shouldReceive('newImage')->with(800, 600)->once()->andReturn($image); - - $command = new FillGd(array('666666', 1, 1)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickColorFill() - { - $image = $this->getTestImage('imagick'); - $image->shouldReceive('width')->andReturn(800); - $image->shouldReceive('height')->andReturn(600); - $image->getCore()->shouldReceive('drawimage')->times(3); - - $command = new FillImagick(array('666666')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickTextureFill() - { - $image = $this->getTestImage('imagick'); - $image->shouldReceive('width')->andReturn(800); - $image->shouldReceive('height')->andReturn(600); - $image->getCore()->shouldReceive('textureimage')->times(3)->andReturn($image->getCore()); - $image->getCore()->shouldReceive('setimage')->with($image->getCore())->times(3); - - $command = new FillImagick(array('tests/images/circle.png')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickColorFillWithCoordinates() - { - $image = $this->getTestImage('imagick'); - $image->shouldReceive('width')->andReturn(800); - $image->shouldReceive('height')->andReturn(600); - $image->getCore()->shouldReceive('getimage')->andReturn($image->getCore()); - $pixelcolor = Mockery::mock('ImagickPixel'); - $image->getCore()->shouldReceive('getimagepixelcolor')->with(1, 1)->andReturn($pixelcolor); - $image->getCore()->shouldReceive('getimagealphachannel')->once(); - $image->getCore()->shouldReceive('transparentpaintimage')->once(); - $image->getCore()->shouldReceive('textureimage')->once(); - - $command = new FillImagick(array('666666', 1, 1)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} -*/ \ No newline at end of file diff --git a/tests/FitCommandTest.php b/tests/FitCommandTest.php deleted file mode 100644 index 82db4ada5..000000000 --- a/tests/FitCommandTest.php +++ /dev/null @@ -1,91 +0,0 @@ -getTestImage('gd'); - - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); - $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); - $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - - $command = new FitGd(array(200, 100)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdFitWithPosition() - { - $image = $this->getTestImage('gd'); - - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); - $cropped_size->shouldReceive('getWidth')->times(2)->andReturn(800); - $cropped_size->shouldReceive('getHeight')->times(2)->andReturn(400); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - - $command = new FitGd(array(200, 100, null, 'top-left')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFit() - { - $image = $this->getTestImage('imagick'); - - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); - $cropped_size->shouldReceive('getWidth')->times(3)->andReturn(200); - $cropped_size->shouldReceive('getHeight')->times(3)->andReturn(100); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'center')->once()->andReturn($cropped_size); - $image->getCore()->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $image->getCore()->shouldReceive('scaleimage')->with(200, 100)->andReturn(true); - $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - - $command = new FitImagick(array(200, 100)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickFitWithPosition() - { - $image = $this->getTestImage('imagick'); - - $cropped_size = Mockery::mock('\Intervention\Image\Size', array(800, 400)); - $cropped_size->shouldReceive('getWidth')->times(3)->andReturn(200); - $cropped_size->shouldReceive('getHeight')->times(3)->andReturn(100); - $cropped_size->shouldReceive('resize')->with(200, 100, null)->once()->andReturn($cropped_size); - $cropped_size->pivot = Mockery::mock('\Intervention\Image\Point', array(0, 100)); - $original_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $original_size->shouldReceive('fit')->with(Mockery::any(), 'top-left')->once()->andReturn($cropped_size); - $image->getCore()->shouldReceive('cropimage')->with(800, 400, 0, 100)->andReturn(true); - $image->getCore()->shouldReceive('scaleimage')->with(200, 100)->andReturn(true); - $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->andReturn(true); - $image->shouldReceive('getSize')->once()->andReturn($original_size); - - - $command = new FitImagick(array(200, 100, null, 'top-left')); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/FlipCommandTest.php b/tests/FlipCommandTest.php deleted file mode 100644 index 090264b00..000000000 --- a/tests/FlipCommandTest.php +++ /dev/null @@ -1,37 +0,0 @@ -getTestImage('gd'); - $size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new FlipGd(array('h')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('flopimage')->with()->andReturn(true); - $command = new FlipImagick(array('h')); - $result = $command->execute($image); - $this->assertTrue($result); - - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('flipimage')->with()->andReturn(true); - $command = new FlipImagick(array('v')); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/GammaCommandTest.php b/tests/GammaCommandTest.php deleted file mode 100644 index a364fe17d..000000000 --- a/tests/GammaCommandTest.php +++ /dev/null @@ -1,31 +0,0 @@ -getTestImage('gd'); - - $command = new GammaGd(array(1.4)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('gammaimage')->times(3)->with(1.4)->andReturn(true); - - $command = new GammaImagick(array(1.4)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/GetsizeCommandTest.php b/tests/GetsizeCommandTest.php deleted file mode 100644 index 76072b487..000000000 --- a/tests/GetsizeCommandTest.php +++ /dev/null @@ -1,38 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new GetSizeGd(array()); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInstanceOf('Intervention\Image\Size', $command->getOutput()); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagewidth')->with(); - $imagick->shouldReceive('getimageheight')->with(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new GetSizeImagick(array()); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInstanceOf('Intervention\Image\Size', $command->getOutput()); - } -} diff --git a/tests/GreyscaleCommandTest.php b/tests/GreyscaleCommandTest.php deleted file mode 100644 index fac59a458..000000000 --- a/tests/GreyscaleCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -getTestImage('gd'); - $command = new GreyscaleGd(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('modulateimage')->times(3)->with(100, 0, 100)->andReturn(true); - $command = new GreyscaleImagick(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/HeightenCommandTest.php b/tests/HeightenCommandTest.php deleted file mode 100644 index 369d28a16..000000000 --- a/tests/HeightenCommandTest.php +++ /dev/null @@ -1,47 +0,0 @@ -aspectRatio(); }; - $image = $this->getTestImage('gd'); - - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - - $command = new HeightenGd(array(200)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $image = $this->getTestImage('imagick'); - - $image->getCore()->shouldReceive('scaleimage')->with(300, 200)->times(3)->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->times(3)->andReturn(300); - $size->shouldReceive('getHeight')->times(3)->andReturn(200); - $image->shouldReceive('getSize')->once()->andReturn($size); - - $command = new HeightenImagick(array(200)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/InsertCommandTest.php b/tests/InsertCommandTest.php deleted file mode 100644 index 6b63933a6..000000000 --- a/tests/InsertCommandTest.php +++ /dev/null @@ -1,66 +0,0 @@ -getTestImage('gd'); - - $position = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $image_size->shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); - $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); - - $path = __DIR__.'/images/test.jpg'; - $resource = imagecreatefromjpeg($path); - $watermark = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); - $watermark->shouldReceive('getCore')->once()->andReturn($resource); - - $command = new InsertGd(array($path, 'center', 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - - $position = Mockery::mock('\Intervention\Image\Point', array(10, 20)); - - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $image_size->shouldReceive('align')->with('center', 10, 20)->once()->andReturn($image_size); - $watermark_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $watermark_size->shouldReceive('align')->with('center')->once()->andReturn($watermark_size); - $image_size->shouldReceive('relativePosition')->with($watermark_size)->once()->andReturn($position); - - $path = __DIR__.'/images/test.jpg'; - $watermark = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('init')->with($path)->once()->andReturn($watermark); - $imagick = Mockery::mock('Imagick'); - $image->getCore()->shouldReceive('compositeimage')->with($imagick, \Imagick::COMPOSITE_DEFAULT, 10, 20)->times(3)->andReturn(true); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $watermark->shouldReceive('getSize')->once()->andReturn($watermark_size); - $watermark->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new InsertImagick(array($path, 'center', 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/InterlaceCommandTest.php b/tests/InterlaceCommandTest.php deleted file mode 100644 index 102f1faad..000000000 --- a/tests/InterlaceCommandTest.php +++ /dev/null @@ -1,33 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $command = new InterlaceGd(array(true)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setinterlacescheme')->with(\Imagick::INTERLACE_LINE)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new InterlaceImagick(array(true)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/InvertCommandTest.php b/tests/InvertCommandTest.php deleted file mode 100644 index e7ea44ed0..000000000 --- a/tests/InvertCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -getTestImage('gd'); - $command = new InvertGd(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('negateimage')->with(false)->times(3)->andReturn(true); - $command = new InvertImagick(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/IptcCommandTest.php b/tests/IptcCommandTest.php deleted file mode 100644 index d1ca865ff..000000000 --- a/tests/IptcCommandTest.php +++ /dev/null @@ -1,71 +0,0 @@ -dirname = __DIR__.'/images'; - $image->basename = 'iptc.jpg'; - $command = new IptcCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - } - - public function testFetchDefined() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new IptcCommand(array('AuthorByline')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals('Oliver Vogel', $command->getOutput()); - } - - - public function testFetchNonExisting() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'exif.jpg'; - $command = new IptcCommand(array('xxx')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - - public function testFetchFromPng() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->dirname = __DIR__.'/images'; - $image->basename = 'star.png'; - $command = new IptcCommand(array('Orientation')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } - - public function testReturnNullOnIptcReadFail() - { - $image = Mockery::mock('Intervention\Image\Image'); - $command = new IptcCommand(array('Orientation')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertEquals(null, $command->getOutput()); - } -} diff --git a/tests/LimitColorsCommandTest.php b/tests/LimitColorsCommandTest.php deleted file mode 100644 index 181343083..000000000 --- a/tests/LimitColorsCommandTest.php +++ /dev/null @@ -1,42 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getSize')->once()->andReturn($size); - $command = new LimitColorsGd(array(16)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $size = Mockery::mock('\Intervention\Image\Size', array(32, 32)); - $imagick = Mockery::mock('\Imagick'); - $imagick->shouldReceive('separateimagechannel')->with(\Imagick::CHANNEL_ALPHA)->times(2); - $imagick->shouldReceive('transparentpaintimage')->with('#ffffff', 0, 0, false)->once(); - $imagick->shouldReceive('negateimage')->with(false)->once(); - $imagick->shouldReceive('quantizeimage')->with(16, \Imagick::COLORSPACE_RGB, 0, false, false)->once(); - $imagick->shouldReceive('compositeimage')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new LimitColorsImagick(array(16)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/LineCommandTest.php b/tests/LineCommandTest.php deleted file mode 100644 index 5ba2c5ce9..000000000 --- a/tests/LineCommandTest.php +++ /dev/null @@ -1,39 +0,0 @@ -getTestImage('gd'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new LineCommand(array(10, 15, 100, 150)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new LineCommand(array(10, 15, 100, 150)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/LineShapeTest.php b/tests/LineShapeTest.php index 7274caaa7..0fc8253e4 100644 --- a/tests/LineShapeTest.php +++ b/tests/LineShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\LineShape as LineGd; use Intervention\Image\Imagick\Shapes\LineShape as LineImagick; -class LineShapeTest extends CommandTestCase +class LineShapeTest extends PHPUnit_Framework_TestCase { public function tearDown() { @@ -24,22 +24,4 @@ public function testConstructor() $this->assertEquals(10, $line->x); $this->assertEquals(15, $line->y); } - - public function testApplyToImage() - { - // gd - $image = $this->getTestImage('gd'); - $line = new LineGd(10, 15); - $result = $line->applyToImage($image, 100, 200); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\LineShape', $line); - $this->assertTrue($result); - - // imagick - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $line = new LineImagick(10, 15); - $result = $line->applyToImage($image, 100, 200); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\LineShape', $line); - $this->assertTrue($result); - } } diff --git a/tests/MaskCommandTest.php b/tests/MaskCommandTest.php deleted file mode 100644 index fd9a734dc..000000000 --- a/tests/MaskCommandTest.php +++ /dev/null @@ -1,67 +0,0 @@ -shouldReceive('getSize')->once()->andReturn($mask_size); - $mask_image->shouldReceive('pickColor')->andReturn(array(0,0,0,0)); - - $canvas_image = Mockery::mock('Intervention\Image\Image'); - $canvas_core = imagecreatetruecolor(32, 32); - $canvas_image->shouldReceive('getCore')->times(2)->andReturn($canvas_core); - $canvas_image->shouldReceive('pixel'); - - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(32, 32, array(0,0,0,0))->once()->andReturn($canvas_image); - $driver->shouldReceive('init')->with($mask_path)->once()->andReturn($mask_image); - - $image_size = Mockery::mock('Intervention\Image\Size', array(32, 32)); - $image_core = imagecreatefrompng(__DIR__.'/images/trim.png'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getDriver')->times(2)->andReturn($driver); - $image->shouldReceive('pickColor')->andReturn(array(0,0,0,0)); - $image->shouldReceive('setCore')->with($canvas_core)->once(); - - $command = new MaskGd(array($mask_path, true)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $mask_core = Mockery::mock('Imagick'); - $mask_path = __DIR__.'images/star.png'; - $mask_image = Mockery::mock('Intervention\Image\Image'); - $mask_image->shouldReceive('getCore')->once()->andReturn($mask_core); - $mask_size = Mockery::mock('Intervention\Image\Size', array(32, 32)); - $mask_image->shouldReceive('getSize')->once()->andReturn($mask_size); - - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('init')->with($mask_path)->once()->andReturn($mask_image); - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('setimagematte')->with(true)->once(); - $imagick->shouldReceive('compositeimage')->with($mask_core, \Imagick::COMPOSITE_DSTIN, 0, 0)->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image_size = Mockery::mock('Intervention\Image\Size', array(32, 32)); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new MaskImagick(array($mask_path, true)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/OpacityCommandTest.php b/tests/OpacityCommandTest.php deleted file mode 100644 index 9fdf7a98d..000000000 --- a/tests/OpacityCommandTest.php +++ /dev/null @@ -1,43 +0,0 @@ -shouldReceive('getCore')->once()->andReturn($mask_core); - - $resource = imagecreatefrompng(__DIR__.'/images/trim.png'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(32, 32, 'rgba(0, 0, 0, 0.5)')->andReturn($mask); - - $size = Mockery::mock('\Intervention\Image\Size', array(32, 32)); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($size); - $image->shouldReceive('mask')->with($mask_core, true)->once(); - $command = new OpacityGd(array(50)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('evaluateimage')->with(\Imagick::EVALUATE_DIVIDE, 2, \Imagick::CHANNEL_ALPHA)->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new OpacityImagick(array(50)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/OrientateCommandTest.php b/tests/OrientateCommandTest.php deleted file mode 100644 index 6b86c23f3..000000000 --- a/tests/OrientateCommandTest.php +++ /dev/null @@ -1,93 +0,0 @@ -shouldReceive('exif')->with('Orientation')->once()->andReturn(2); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationThree() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(3); - $image->shouldReceive('rotate')->with(180)->once(); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationFour() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(4); - $image->shouldReceive('rotate')->with(180)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationFive() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(5); - $image->shouldReceive('rotate')->with(270)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationSix() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(6); - $image->shouldReceive('rotate')->with(270)->once(); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationSeven() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(7); - $image->shouldReceive('rotate')->with(90)->once()->andReturn($image); - $image->shouldReceive('flip')->once(); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationEight() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(8); - $image->shouldReceive('rotate')->with(90)->once(); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testExecuteOrientationNoExifData() - { - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('exif')->with('Orientation')->once()->andReturn(null); - $command = new OrientateCommand(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PickColorCommandTest.php b/tests/PickColorCommandTest.php deleted file mode 100644 index 731de04e5..000000000 --- a/tests/PickColorCommandTest.php +++ /dev/null @@ -1,66 +0,0 @@ -shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd(array(1, 2)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals(4, count($command->getOutput())); - } - - public function testGdWithFormat() - { - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new PickColorGd(array(1, 2, 'hex')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('string', $command->getOutput()); - $this->assertEquals('#ffffff', $command->getOutput()); - } - - public function testImagickWithCoordinates() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick(array(1, 2)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('array', $command->getOutput()); - $this->assertEquals(4, count($command->getOutput())); - } - - public function testImagickWithFormat() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('getimagepixelcolor')->with(1, 2)->andReturn(new ImagickPixel('#ff0000')); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $command = new PickColorImagick(array(1, 2, 'hex')); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - $this->assertInternalType('string', $command->getOutput()); - $this->assertEquals('#ff0000', $command->getOutput()); - } -} diff --git a/tests/PixelCommandTest.php b/tests/PixelCommandTest.php deleted file mode 100644 index fd08e09f3..000000000 --- a/tests/PixelCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -getTestImage('gd'); - $command = new PixelGd(array('#b53717', 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3)->andReturn(true); - $command = new PixelImagick(array('#b53717', 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PixelateCommandTest.php b/tests/PixelateCommandTest.php deleted file mode 100644 index e9a6f7f24..000000000 --- a/tests/PixelateCommandTest.php +++ /dev/null @@ -1,32 +0,0 @@ -getTestImage('gd'); - $command = new PixelateGd(array(10)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('scaleimage')->with(80, 60)->times(3)->andReturn(true); - $image->getCore()->shouldReceive('scaleimage')->with(800, 600)->times(3)->andReturn(true); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $command = new PixelateImagick(array(10)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/PolygonCommandTest.php b/tests/PolygonCommandTest.php deleted file mode 100644 index 219b77a49..000000000 --- a/tests/PolygonCommandTest.php +++ /dev/null @@ -1,40 +0,0 @@ -getTestImage('gd'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $command = new PolygonCommand(array($points)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $points = array(1, 2, 3, 4, 5, 6); - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new PolygonCommand(array($points)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/PolygonShapeTest.php b/tests/PolygonShapeTest.php index 6f2d4c29e..d28de3368 100644 --- a/tests/PolygonShapeTest.php +++ b/tests/PolygonShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\PolygonShape as PolygonGd; use Intervention\Image\Imagick\Shapes\PolygonShape as PolygonImagick; -class PolygonShapeTest extends CommandTestCase +class PolygonShapeTest extends PHPUnit_Framework_TestCase { public function tearDown() { @@ -18,15 +18,6 @@ public function testGdConstructor() } - public function testGdApplyToImage() - { - $image = $this->getTestImage('gd'); - $polygon = new PolygonGd(array(1, 2, 3, 4, 5, 6)); - $result = $polygon->applyToImage($image); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\PolygonShape', $polygon); - $this->assertTrue($result); - } - public function testImagickConstructor() { $polygon = new PolygonImagick(array(1, 2, 3, 4, 5, 6)); @@ -38,15 +29,4 @@ public function testImagickConstructor() $polygon->points); } - - public function testImagickApplyToImage() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $polygon = new PolygonImagick(array(1, 2, 3, 4, 5, 6)); - $result = $polygon->applyToImage($image); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\PolygonShape', $polygon); - $this->assertTrue($result); - } - } diff --git a/tests/PsrResponseCommandTest.php b/tests/PsrResponseCommandTest.php deleted file mode 100644 index b28a45f99..000000000 --- a/tests/PsrResponseCommandTest.php +++ /dev/null @@ -1,57 +0,0 @@ -'; - - $image = Mockery::mock('Intervention\Image\Image'); - $stream = \GuzzleHttp\Psr7\stream_for($encodedContent); - - $image->shouldReceive('stream') - ->with('jpg', 87) - ->once() - ->andReturn($stream); - - $image->shouldReceive('getEncoded') - ->twice() - ->andReturn($encodedContent); - - $command = new PsrResponseCommand(array('jpg', 87)); - $result = $command->execute($image); - - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - - $output = $command->getOutput(); - - $this->assertInstanceOf('Psr\Http\Message\ResponseInterface', $output); - - /** - * @var \Psr\Http\Message\ResponseInterface $output - */ - $this->assertEquals($stream, $output->getBody()); - $this->assertEquals($encodedContent, (string)$output->getBody()); - - $this->assertTrue($output->hasHeader('Content-Type')); - $this->assertTrue($output->hasHeader('Content-Length')); - - $this->assertEquals( - "application/xml", - $output->getHeaderLine('Content-Type') - ); - - $this->assertEquals( - strlen($encodedContent), - $output->getHeaderLine('Content-Length') - ); - } -} \ No newline at end of file diff --git a/tests/RectangleCommandTest.php b/tests/RectangleCommandTest.php deleted file mode 100644 index f2a95388c..000000000 --- a/tests/RectangleCommandTest.php +++ /dev/null @@ -1,38 +0,0 @@ -getTestImage('gd'); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $command = new RectangleCommand(array(10, 15, 100, 150)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('getDriverName')->once()->andReturn('Imagick'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - - $command = new RectangleCommand(array(10, 15, 100, 150)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - -} diff --git a/tests/RectangleShapeTest.php b/tests/RectangleShapeTest.php index bc82ab7a3..67f03440c 100644 --- a/tests/RectangleShapeTest.php +++ b/tests/RectangleShapeTest.php @@ -3,7 +3,7 @@ use Intervention\Image\Gd\Shapes\RectangleShape as RectangleGd; use Intervention\Image\Imagick\Shapes\RectangleShape as RectangleImagick; -class RectangleShapeTest extends CommandTestCase +class RectangleShapeTest extends PHPUnit_Framework_TestCase { public function tearDown() { @@ -28,22 +28,4 @@ public function testConstructor() $this->assertEquals(100, $rectangle->x2); $this->assertEquals(150, $rectangle->y2); } - - public function testApplyToImage() - { - // gd - $image = $this->getTestImage('gd'); - $rectangle = new RectangleGd(10, 15, 100, 150); - $result = $rectangle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Gd\Shapes\RectangleShape', $rectangle); - $this->assertTrue($result); - - // imagick - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('drawimage')->times(3); - $rectangle = new RectangleImagick(10, 15, 100, 150); - $result = $rectangle->applyToImage($image, 10, 20); - $this->assertInstanceOf('Intervention\Image\Imagick\Shapes\RectangleShape', $rectangle); - $this->assertTrue($result); - } } diff --git a/tests/ResetCommandTest.php b/tests/ResetCommandTest.php deleted file mode 100644 index 18d823dda..000000000 --- a/tests/ResetCommandTest.php +++ /dev/null @@ -1,70 +0,0 @@ -shouldReceive('cloneCore')->with($resource)->once()->andReturn($resource); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->andReturn($resource); - $command = new ResetGd(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testGdWithName() - { - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $resource = imagecreatefromjpeg(__DIR__.'/images/test.jpg'); - $image = Mockery::mock('Intervention\Image\Image'); - $driver = Mockery::mock('Intervention\Image\Gd\Driver'); - $driver->shouldReceive('cloneCore')->with($resource)->once()->andReturn($resource); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(array('fooBackup'))->andReturn($resource); - $command = new ResetGd(array('fooBackup')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithoutName() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->andReturn($imagick); - $command = new ResetImagick(array()); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagickWithName() - { - $imagick = Mockery::mock('Imagick'); - $imagick->shouldReceive('clear')->once(); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getCore')->once()->andReturn($imagick); - $image->shouldReceive('setCore')->once(); - $image->shouldReceive('getBackup')->once()->withArgs(array('fooBackup'))->andReturn($imagick); - $command = new ResetImagick(array('fooBackup')); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/ResizeCanvasCommandTest.php b/tests/ResizeCanvasCommandTest.php deleted file mode 100644 index a9aa284e7..000000000 --- a/tests/ResizeCanvasCommandTest.php +++ /dev/null @@ -1,72 +0,0 @@ -shouldReceive('align')->with('center')->andReturn($canvas_size); - $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $image_size->shouldReceive('align')->with('center')->andReturn($image_size); - $image_size->shouldReceive('relativePosition')->andReturn($image_pos); - $canvas = Mockery::mock('\Intervention\Image\Image'); - $canvas->shouldReceive('getCore')->times(5)->andReturn($resource); - $canvas->shouldReceive('getSize')->andReturn($canvas_size); - $driver = Mockery::mock('\Intervention\Image\Gd\Driver'); - $driver->shouldReceive('newImage')->with(820, 640, '#b53717')->once()->andReturn($canvas); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasGd(array(20, 40, 'center', true, '#b53717')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $canvas_pos = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - $canvas_size = Mockery::mock('\Intervention\Image\Size', array(820, 640)); - $canvas_size->shouldReceive('align')->with('center')->andReturn($canvas_size); - $canvas_size->shouldReceive('relativePosition')->andReturn($canvas_pos); - $image_pos = Mockery::mock('\Intervention\Image\Point', array(0, 0)); - $image_size = Mockery::mock('\Intervention\Image\Size', array(800, 600)); - $image_size->shouldReceive('align')->with('center')->andReturn($image_size); - $image_size->shouldReceive('relativePosition')->andReturn($image_pos); - - $canvas = $this->getTestImage('imagick'); - $canvas->getCore()->shouldReceive('drawimage')->once(); - $canvas->getCore()->shouldReceive('transparentpaintimage')->once(); - $canvas->getCore()->shouldReceive('setimagecolorspace')->once(); - $canvas->shouldReceive('getSize')->andReturn($canvas_size); - $canvas->shouldReceive('pickColor')->with(0, 0, 'hex')->once()->andReturn('#000000'); - $driver = Mockery::mock('\Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('newImage')->with(820, 640, '#b53717')->once()->andReturn($canvas); - - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('cropimage')->with(800, 600, 0, 0)->once(); - $image->getCore()->shouldReceive('compositeimage')->with($image->getCore(), 40, 0, 0)->once(); - $image->getCore()->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once(); - - $image->getCore()->shouldReceive('getimagecolorspace')->once(); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getSize')->once()->andReturn($image_size); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('setCore')->once(); - $command = new ResizeCanvasImagick(array(20, 40, 'center', true, '#b53717')); - $result = $command->execute($image); - $this->assertTrue($result); - } - -} diff --git a/tests/ResizeCommandTest.php b/tests/ResizeCommandTest.php deleted file mode 100644 index 478a7b7ff..000000000 --- a/tests/ResizeCommandTest.php +++ /dev/null @@ -1,45 +0,0 @@ -upsize(); }; - $image = $this->getTestImage('gd'); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - - $command = new ResizeCommandGd(array(300, 200, $callback)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('scaleimage')->with(300, 200)->times(3)->andReturn(true); - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $size->shouldReceive('resize')->with(300, 200, $callback)->once()->andReturn($size); - $size->shouldReceive('getWidth')->times(3)->andReturn(300); - $size->shouldReceive('getHeight')->times(3)->andReturn(200); - $image->shouldReceive('getSize')->once()->andReturn($size); - - $command = new ResizeCommandImagick(array(300, 200, $callback)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/RotateCommandTest.php b/tests/RotateCommandTest.php deleted file mode 100644 index 31036a722..000000000 --- a/tests/RotateCommandTest.php +++ /dev/null @@ -1,30 +0,0 @@ -getTestImage('gd'); - $command = new RotateGd(array(45, '#b53717')); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $pixel = Mockery::mock('ImagickPixel', array('#b53717')); - $image->getCore()->shouldReceive('rotateimage')->andReturn(true); - $command = new RotateImagick(array(45, '#b53717')); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/SharpenCommandTest.php b/tests/SharpenCommandTest.php deleted file mode 100644 index b1a837743..000000000 --- a/tests/SharpenCommandTest.php +++ /dev/null @@ -1,29 +0,0 @@ -getTestImage('gd'); - $command = new SharpenGd(array(50)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('unsharpmaskimage')->with(1, 1, 8, 0)->andReturn(true); - $command = new SharpenImagick(array(50)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/StopAnimationCommandTest.php b/tests/StopAnimationCommandTest.php deleted file mode 100644 index 2203bd4d1..000000000 --- a/tests/StopAnimationCommandTest.php +++ /dev/null @@ -1,36 +0,0 @@ -getTestImage('gd'); - $image->shouldReceive('setContainer')->once(); - - $command = new StopAnimationGd(array(2)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $newContainer = Mockery::mock('Intervention\Image\Imagick\Container'); - $frame = Mockery::mock('Intervention\Image\Image'); - $frame->shouldReceive('getContainer')->once()->andReturn($newContainer); - $driver = Mockery::mock('Intervention\Image\Imagick\Driver'); - $driver->shouldReceive('init')->with('blob')->once()->andReturn($frame); - - $image = $this->getTestImage('imagick'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->getCore()->shouldReceive('getimageblob')->once()->andReturn('blob'); - $image->getCore()->shouldReceive('clear')->once(); - $image->shouldReceive('setContainer')->with($newContainer)->once(); - - $command = new StopAnimationImagick(array(2)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/StreamCommandTest.php b/tests/StreamCommandTest.php deleted file mode 100644 index 79d1048f1..000000000 --- a/tests/StreamCommandTest.php +++ /dev/null @@ -1,35 +0,0 @@ -shouldReceive('encode') - ->with('jpg', 87) - ->once() - ->andReturnSelf(); - - $image->shouldReceive('getEncoded') - ->once() - ->andReturn($encodedContent); - - $command = new StreamCommand(array('jpg', 87)); - $result = $command->execute($image); - - $this->assertTrue($result); - $this->assertTrue($command->hasOutput()); - - $output = $command->getOutput(); - $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $output); - $this->assertEquals($encodedContent, (string)$output); - } -} \ No newline at end of file diff --git a/tests/TextCommandTest.php b/tests/TextCommandTest.php deleted file mode 100644 index 8b193aecd..000000000 --- a/tests/TextCommandTest.php +++ /dev/null @@ -1,31 +0,0 @@ -shouldReceive('getDriverName')->once()->andReturn('Gd'); - $image = Mockery::mock('\Intervention\Image\Image'); - $image->shouldReceive('getDriver')->once()->andReturn($driver); - $image->shouldReceive('getCore')->times(2)->andReturn($resource); - $command = new TextCommand(array('test', 10, 20)); - $result = $command->execute($image); - $this->assertTrue($result); - $this->assertFalse($command->hasOutput()); - } - - public function testImagick() - { - # code... - } - -} diff --git a/tests/TrimCommandTest.php b/tests/TrimCommandTest.php deleted file mode 100644 index de6d65ee1..000000000 --- a/tests/TrimCommandTest.php +++ /dev/null @@ -1,53 +0,0 @@ -shouldReceive('differs')->with($baseColor, 45)->andReturn(true); - $image->shouldReceive('getCore')->once()->andReturn($resource); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('pickColor')->with(0, 0, 'object')->times(2)->andReturn($baseColor); - $image->shouldReceive('pickColor')->with(799, 0, 'object')->once()->andReturn($baseColor); - $image->shouldReceive('setCore')->once(); - $command = new TrimGd(array('top-left', array('left', 'right'), 45, 2)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $baseColorPixel = new \ImagickPixel; - $baseColor = Mockery::mock('Intervention\Image\Gd\Color'); - $baseColor->shouldReceive('getPixel')->once()->andReturn($baseColorPixel); - $imagick = Mockery::mock('Imagick'); - $imagick->width = 100; - $imagick->height = 100; - $imagick->shouldReceive('borderimage')->with($baseColorPixel, 1, 1)->once()->andReturn(true); - $imagick->shouldReceive('trimimage')->with(29632.5)->once()->andReturn(true); - $imagick->shouldReceive('getimagepage')->once()->andReturn(array('x' => 50, 'y' => 50)); - $imagick->shouldReceive('cropimage')->with(104, 202, 47, 0)->once()->andReturn(true); - $imagick->shouldReceive('setimagepage')->with(0, 0, 0, 0)->once()->andReturn(true); - $imagick->shouldReceive('destroy')->with()->once()->andReturn(true); - $image = Mockery::mock('Intervention\Image\Image'); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('pickColor')->with(0, 0, 'object')->once()->andReturn($baseColor); - $image->shouldReceive('getCore')->times(3)->andReturn($imagick); - $command = new TrimImagick(array('top-left', array('left', 'right'), 45, 2)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} diff --git a/tests/WidenCommandTest.php b/tests/WidenCommandTest.php deleted file mode 100644 index bbe7021a7..000000000 --- a/tests/WidenCommandTest.php +++ /dev/null @@ -1,47 +0,0 @@ -aspectRatio(); }; - $image = $this->getTestImage('gd'); - - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->once()->andReturn(800); - $size->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getWidth')->once()->andReturn(800); - $image->shouldReceive('getHeight')->once()->andReturn(600); - $image->shouldReceive('getSize')->once()->andReturn($size); - - $command = new WidenGd(array(200)); - $result = $command->execute($image); - $this->assertTrue($result); - } - - public function testImagick() - { - $callback = function ($constraint) { $constraint->upsize(); }; - $image = $this->getTestImage('imagick'); - $image->getCore()->shouldReceive('scaleimage')->with(300, 200)->times(3)->andReturn(true); - - $size = Mockery::mock('Intervention\Image\Size', array(800, 600)); - $size->shouldReceive('resize')->once()->andReturn($size); - $size->shouldReceive('getWidth')->times(3)->andReturn(300); - $size->shouldReceive('getHeight')->times(3)->andReturn(200); - $image->shouldReceive('getSize')->once()->andReturn($size); - - $command = new WidenImagick(array(200)); - $result = $command->execute($image); - $this->assertTrue($result); - } -} From 194305e84a70d69d0aabbd9dad3c6329d0b6df3b Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 6 Dec 2015 11:49:45 +0100 Subject: [PATCH 86/88] renamed integration test files --- tests/{GdSystemTest.php => IntegrationTestGd.php} | 2 +- tests/{ImagickSystemTest.php => IntegrationTestImagick.php} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename tests/{GdSystemTest.php => IntegrationTestGd.php} (99%) rename tests/{ImagickSystemTest.php => IntegrationTestImagick.php} (99%) diff --git a/tests/GdSystemTest.php b/tests/IntegrationTestGd.php similarity index 99% rename from tests/GdSystemTest.php rename to tests/IntegrationTestGd.php index e4d5634b5..b3b39d70c 100644 --- a/tests/GdSystemTest.php +++ b/tests/IntegrationTestGd.php @@ -1,6 +1,6 @@ Date: Sun, 6 Dec 2015 11:56:56 +0100 Subject: [PATCH 87/88] fixed integration tests gd --- .../Image/Commands/ChecksumCommand.php | 2 +- tests/IntegrationTestGd.php | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/Intervention/Image/Commands/ChecksumCommand.php b/src/Intervention/Image/Commands/ChecksumCommand.php index ef5c68d04..8350388ef 100644 --- a/src/Intervention/Image/Commands/ChecksumCommand.php +++ b/src/Intervention/Image/Commands/ChecksumCommand.php @@ -12,7 +12,7 @@ class ChecksumCommand extends AbstractCommand */ public function execute($image) { - $this->setOutput(md5($image->encode())); + $this->setOutput(md5($image->encode('png'))); return true; } diff --git a/tests/IntegrationTestGd.php b/tests/IntegrationTestGd.php index b3b39d70c..4b21c9b86 100644 --- a/tests/IntegrationTestGd.php +++ b/tests/IntegrationTestGd.php @@ -1024,9 +1024,8 @@ public function testTextImage() $img = $this->manager()->canvas(16, 16, 'ffffff'); $img = $img->text('0', 3, 11); $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('a9e2b15452b2a4637b65625188d206f6', $img->checksum()); + $this->assertEquals('aebd2eea279b29f8d5264b9a53c1846e', $img->checksum()); - $img = $this->manager()->canvas(16, 16, 'ffffff'); $img = $img->text('0', 8, 8, function($font) { $font->file(3); @@ -1035,7 +1034,7 @@ public function testTextImage() $font->color('000000'); }); $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('ccb8b439d7e327089682094939564934', $img->checksum()); + $this->assertEquals('9a11953d892c1e53659b9e3441ab3783', $img->checksum()); $img = $this->manager()->canvas(16, 16, 'ffffff'); $img = $img->text('0', 8, 8, function($font) { @@ -1045,7 +1044,7 @@ public function testTextImage() $font->color('000000'); }); $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('496be71507490098ead374f6d6c359fe', $img->checksum()); + $this->assertEquals('6058fcde6d9273e02b94c38235aa7e28', $img->checksum()); $img = $this->manager()->canvas(100, 100, 'ffffff'); $txt = 'The quick brown fox jumps over the lazy dog'; @@ -1058,35 +1057,35 @@ public function testTextImage() $font->box(80, 80); }); $this->assertInstanceOf('Intervention\Image\Image', $img); - $this->assertEquals('7a6eafce0c071f3be91342d62e41fb9d', $img->checksum()); + $this->assertEquals('36ab16d344a603cb87bac981a486f964', $img->checksum()); } public function testRectangleImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->rectangle(5, 5, 11, 11, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e95487dcc29daf371a0e9190bff8dbfe', $img->checksum()); + $this->assertEquals('e8c79ad201c20d54eedafde5c4d165f0', $img->checksum()); } public function testLineImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->line(0, 0, 15, 15, function ($draw) { $draw->color('#ff0000'); }); - $this->assertEquals('a6237d34f6e95f30d2fc91a46bd058e6', $img->checksum()); + $this->assertEquals('92f5891680b215f699e4efd52aa4bfac', $img->checksum()); } public function testEllipseImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->ellipse(12, 8, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('080d9dd92ebe22f976c3c703cba33510', $img->checksum()); + $this->assertEquals('e3231057c907a6f1457370e1f4f9d712', $img->checksum()); } public function testCircleImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->circle(12, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('c3bff06c20244ba14e898e39ea0efd76', $img->checksum()); + $this->assertEquals('a109b98254329484489548b618d8cb92', $img->checksum()); } public function testPolygonImage() @@ -1094,7 +1093,7 @@ public function testPolygonImage() $img = $this->manager()->canvas(16, 16, 'ffffff'); $points = array(3, 3, 11, 11, 7, 13); $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e534ff90c8026f9317b99071fda01ed4', $img->checksum()); + $this->assertEquals('37298b58a42eb1c664d52e10496a56cf', $img->checksum()); } public function testResetImage() From dc8347add1bf5519846ef801ff1b9f6dd16d7a7d Mon Sep 17 00:00:00 2001 From: Oliver Vogel Date: Sun, 6 Dec 2015 11:58:03 +0100 Subject: [PATCH 88/88] fixed integration tests imagick --- tests/IntegrationTestImagick.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/IntegrationTestImagick.php b/tests/IntegrationTestImagick.php index 0fb93ab9d..0ea7192a8 100644 --- a/tests/IntegrationTestImagick.php +++ b/tests/IntegrationTestImagick.php @@ -1028,28 +1028,28 @@ public function testRectangleImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->rectangle(5, 5, 11, 11, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('32ceca9759d1973dd461b39664df604d', $img->checksum()); + $this->assertEquals('d4a3924299387f5eb6f42c74626c9446', $img->checksum()); } public function testLineImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->line(0, 0, 15, 15, function ($draw) { $draw->color('#ff0000'); }); - $this->assertEquals('f5c585019bff361d91e2928b2ac2286b', $img->checksum()); + $this->assertEquals('859b0c6cbd21cabd9e525efb5023ad27', $img->checksum()); } public function testEllipseImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->ellipse(12, 8, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('9dc5bbec6d45868610c082a1d67640b5', $img->checksum()); + $this->assertEquals('74bcee1addf3309a8bd9f27171bf37f4', $img->checksum()); } public function testCircleImage() { $img = $this->manager()->canvas(16, 16, 'ffffff'); $img->circle(12, 8, 8, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('a433c7c1a842ef83e1cb45875371358c', $img->checksum()); + $this->assertEquals('bf6293c79bb50997bbb659e07bccfe37', $img->checksum()); } public function testPolygonImage() @@ -1057,7 +1057,7 @@ public function testPolygonImage() $img = $this->manager()->canvas(16, 16, 'ffffff'); $points = array(3, 3, 11, 11, 7, 13); $img->polygon($points, function ($draw) { $draw->background('#ff0000'); $draw->border(1, '#0000ff'); }); - $this->assertEquals('e301afe179da858d441ad8fc0eb5490a', $img->checksum()); + $this->assertEquals('0f77f38b9c500536fec937348d9abe88', $img->checksum()); } public function testResetImage()