Author: Tobias Schlitt Date: 2007-02-26 10:08:15 +0100 (Mon, 26 Feb 2007) New Revision: 4682
Log: - Implemented issue #9565: Add a thumbnail filter. Added: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testCropThumbnailHorizontal trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testCropThumbnailVertical trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testFillThumbnailHorizontal trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testFillThumbnailVertical trunk/ImageConversion/tests/data/compare/ezcImageConversionHandlerGdTest_testSaveOldfileNoconvert trunk/ImageConversion/tests/data/compare/ezcImageConversionHandlerTest_testSaveOldfileNoconvert Modified: trunk/ImageConversion/src/handlers/gd.php trunk/ImageConversion/tests/filtersgd_test.php Modified: trunk/ImageConversion/src/handlers/gd.php =================================================================== --- trunk/ImageConversion/src/handlers/gd.php 2007-02-25 07:30:38 UTC (rev 4681) +++ trunk/ImageConversion/src/handlers/gd.php 2007-02-26 09:08:15 UTC (rev 4682) @@ -21,7 +21,7 @@ * @package ImageConversion * @access private */ -class ezcImageGdHandler extends ezcImageGdBaseHandler implements ezcImageGeometryFilters, ezcImageColorspaceFilters, ezcImageWatermarkFilters +class ezcImageGdHandler extends ezcImageGdBaseHandler implements ezcImageGeometryFilters, ezcImageColorspaceFilters, ezcImageWatermarkFilters, ezcImageThumbnailFilters { /** * Scale filter. @@ -369,39 +369,8 @@ $height = $sourceHeight - $y; } - if ( imageistruecolor( $oldResource ) ) - { - $newResource = imagecreatetruecolor( $width, $height ); - } - else - { - $newResource = imagecreate( $width, $height ); - } - - // Save transparency, if image has it - $bgColor = imagecolorallocatealpha( $newResource, 255, 255, 255, 127 ); - imagealphablending( $newResource, true ); - imagesavealpha( $newResource, true ); - imagefill( $newResource, 1, 1, $bgColor ); - - $res = imagecopyresampled( - $newResource, // destination resource - $oldResource, // source resource - 0, // destination x coord - 0, // destination y coord - $x, // source x coord - $y, // source y coord - $width, // destination width - $height, // destination height - $width, // source witdh - $height // source height - ); - if ( $res === false ) - { - throw new ezcImageFilterFailedException( 'crop', 'Resampling of image failed.' ); - } - imagedestroy( $oldResource ); - $this->setActiveResource( $newResource ); + $this->performCrop( $x, $y, $width, $height ); + } /** @@ -585,7 +554,112 @@ // Restore original image reference $this->setActiveReference( $originalRef ); } + + /** + * Creates a thumbnail, and crops parts of the given image to fit the range best. + * This filter creates a thumbnail of the given image. The image is scaled + * down, keeping the original ratio and keeping the image larger as the + * given range, if necessary. Overhead for the target range is cropped from + * both sides equally. + * + * @param int $width Width of the thumbnail. + * @param int $height Height of the thumbnail. + */ + public function croppedThumbnail( $width, $height ) + { + if ( !is_int( $width ) || $width < 1 ) + { + throw new ezcBaseValueException( 'width', $width, 'int > 0' ); + } + if ( !is_int( $height ) || $height < 1 ) + { + throw new ezcBaseValueException( 'height', $height, 'int > 0' ); + } + $resource = $this->getActiveResource(); + $data[0] = imagesx( $resource ); + $data[1] = imagesy( $resource ); + + $scaleRatio = max( $width / $data[0], $height / $data[1] ); + $scaleWidth = round( $data[0] * $scaleRatio ); + $scaleHeight = round( $data[1] * $scaleRatio ); + + $cropOffsetX = ( $scaleWidth > $width ) ? round( ( $scaleWidth - $width ) / 2 ) : 0; + $cropOffsetY = ( $scaleHeight > $height ) ? round( ( $scaleHeight - $height ) / 2 ) : 0; + $this->performScale( array( "x" => $scaleWidth, "y" => $scaleHeight ) ); + $this->performCrop( $cropOffsetX, $cropOffsetY, $width, $height ); + } + + /** + * Creates a thumbnail, and fills up the image to fit the given range. + * This filter creates a thumbnail of the given image. The image is scaled + * down, keeping the original ratio and scaling the image smaller as the + * given range, if necessary. Overhead for the target range is filled with the given + * color on both sides equally. + * + * The color is defined by the following array format (integer values 0-255): + * + * <code> + * array( + * 0 => <red value>, + * 1 => <green value>, + * 2 => <blue value>, + * ); + * </code> + * + * @param int $width Width of the thumbnail. + * @param int $height Height of the thumbnail. + * @param array $color Fill color. + * + * @return void + */ + public function filledThumbnail( $width, $height, $color = array() ) + { + $i = 0; + foreach ( $color as $id => $colorVal ) + { + if ( $i++ > 2 ) + { + break; + } + if ( !is_int( $colorVal ) || $colorVal < 0 || $colorVal > 255 ) + { + throw new ezcBaseValueException( "color[$id]", $color[$id], 'int > 0 and < 256' ); + } + } + + // Sanity checks for $width and $height performed by scale() method. + $this->scale( $width, $height, ezcImageGeometryFilters::SCALE_BOTH ); + + $oldResource = $this->getActiveResource(); + + $realWidth = imagesx( $oldResource ); + $realHeight = imagesy( $oldResource ); + $xOffset = ( $width > $realWidth ) ? round( ( $width - $realWidth ) / 2 ) : 0; + $yOffset = ( $height > $realHeight ) ? round( ( $height - $realHeight ) / 2 ) : 0; + + $newResource = imagecreatetruecolor( $width, $height ); + $bgColor = $this->getColor( $newResource, $color[0], $color[1], $color[2] ); + if ( imagefill( $newResource, 0, 0, $bgColor ) === false ) + { + throw new ezcImageFilterFailedException( "filledThumbnail", "Color fill failed." ); + } + + imagecopy( + $newResource, + $oldResource, + $xOffset, + $yOffset, + 0, + 0, + $realWidth, + $realHeight + ); + + $this->setActiveResource( $newResource ); + imagedestroy( $oldResource ); + } + // private /** @@ -764,6 +838,7 @@ { return $res; } + // @TODO Fix filter name to be generic throw new ezcImageFilterFailedException( 'colorspace', "Color allocation failed for color r: '{$r}', g: '{$g}', b: '{$b}'." ); } @@ -777,6 +852,8 @@ * If no valid resource for the active reference could be found. * @throws ezcImageFilterFailedException. * If the operation performed by the the filter failed. + * + * @todo Change signature, should be ( $width, $height ) */ protected function performScale( $dimensions ) { @@ -812,5 +889,52 @@ imagedestroy( $oldResource ); $this->setActiveResource( $newResource ); } + + /** + * General method to perform a crop operation. + * + * @param int $x + * @param int $y + * @param int $width + * @param int $height + * @return void + */ + private function performCrop( $x, $y, $width, $height ) + { + $oldResource = $this->getActiveResource(); + if ( imageistruecolor( $oldResource ) ) + { + $newResource = imagecreatetruecolor( $width, $height ); + } + else + { + $newResource = imagecreate( $width, $height ); + } + + // Save transparency, if image has it + $bgColor = imagecolorallocatealpha( $newResource, 255, 255, 255, 127 ); + imagealphablending( $newResource, true ); + imagesavealpha( $newResource, true ); + imagefill( $newResource, 1, 1, $bgColor ); + + $res = imagecopyresampled( + $newResource, // destination resource + $oldResource, // source resource + 0, // destination x coord + 0, // destination y coord + $x, // source x coord + $y, // source y coord + $width, // destination width + $height, // destination height + $width, // source witdh + $height // source height + ); + if ( $res === false ) + { + throw new ezcImageFilterFailedException( 'crop', 'Resampling of image failed.' ); + } + imagedestroy( $oldResource ); + $this->setActiveResource( $newResource ); + } } ?> Added: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testCropThumbnailHorizontal =================================================================== (Binary files differ) Property changes on: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testCropThumbnailHorizontal ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testCropThumbnailVertical =================================================================== (Binary files differ) Property changes on: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testCropThumbnailVertical ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testFillThumbnailHorizontal =================================================================== (Binary files differ) Property changes on: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testFillThumbnailHorizontal ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testFillThumbnailVertical =================================================================== (Binary files differ) Property changes on: trunk/ImageConversion/tests/data/compare/ezcImageConversionFiltersGdTest_testFillThumbnailVertical ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/ImageConversion/tests/data/compare/ezcImageConversionHandlerGdTest_testSaveOldfileNoconvert =================================================================== (Binary files differ) Property changes on: trunk/ImageConversion/tests/data/compare/ezcImageConversionHandlerGdTest_testSaveOldfileNoconvert ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/ImageConversion/tests/data/compare/ezcImageConversionHandlerTest_testSaveOldfileNoconvert =================================================================== (Binary files differ) Property changes on: trunk/ImageConversion/tests/data/compare/ezcImageConversionHandlerTest_testSaveOldfileNoconvert ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Modified: trunk/ImageConversion/tests/filtersgd_test.php =================================================================== --- trunk/ImageConversion/tests/filtersgd_test.php 2007-02-25 07:30:38 UTC (rev 4681) +++ trunk/ImageConversion/tests/filtersgd_test.php 2007-02-26 09:08:15 UTC (rev 4682) @@ -606,5 +606,131 @@ 20 ); } + + public function testCropThumbnailVertical() + { + $this->handler->croppedThumbnail( 50, 50 ); + $this->handler->save( $this->imageReference, $this->getTempPath() ); + $this->assertImageSimilar( + $this->getReferencePath(), + $this->getTempPath(), + "Image not rendered as expected.", + ezcImageConversionTestCase::DEFAULT_SIMILARITY_GAP + ); + } + + public function testCropThumbnailHorizontal() + { + $this->handler->croppedThumbnail( 100, 50 ); + $this->handler->save( $this->imageReference, $this->getTempPath() ); + $this->assertImageSimilar( + $this->getReferencePath(), + $this->getTempPath(), + "Image not rendered as expected.", + ezcImageConversionTestCase::DEFAULT_SIMILARITY_GAP + ); + } + + public function testCroppedThumbnailFailures() + { + try + { + $this->handler->croppedThumbnail( -10, 50 ); + $this->fail( "CroppedThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->croppedThumbnail( "foo", 50 ); + $this->fail( "CroppedThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->croppedThumbnail( 50, -10 ); + $this->fail( "CroppedThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->croppedThumbnail( 50, false ); + $this->fail( "CroppedThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + } + + public function testFillThumbnailVertical() + { + $this->handler->filledThumbnail( 50, 50, array( 255, 0, 0 ) ); + $this->handler->save( $this->imageReference, $this->getTempPath() ); + $this->assertImageSimilar( + $this->getReferencePath(), + $this->getTempPath(), + "Image not rendered as expected.", + ezcImageConversionTestCase::DEFAULT_SIMILARITY_GAP + ); + } + + public function testFillThumbnailHorizontal() + { + $this->handler->filledThumbnail( 100, 50, array( 255, 0, 0 ) ); + $this->handler->save( $this->imageReference, $this->getTempPath() ); + $this->assertImageSimilar( + $this->getReferencePath(), + $this->getTempPath(), + "Image not rendered as expected.", + ezcImageConversionTestCase::DEFAULT_SIMILARITY_GAP + ); + } + + public function testFilledThumbnailFailures() + { + try + { + $this->handler->filledThumbnail( -10, 50, array( 255, 0, 0 ) ); + $this->fail( "FilledThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->filledThumbnail( "foo", 50, array( 255, 0, 0 ) ); + $this->fail( "FilledThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->filledThumbnail( 50, -10, array( 255, 0, 0 ) ); + $this->fail( "FilledThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->filledThumbnail( 50, false, array( 255, 0, 0 ) ); + $this->fail( "FilledThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->filledThumbnail( 50, 50, array( 255, false, 0 ) ); + $this->fail( "FilledThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + try + { + $this->handler->filledThumbnail( 50, 50, array( "bar", 0, 0 ) ); + $this->fail( "FilledThumbnail filter accepted incorrect value." ); + } + catch ( ezcBaseValueException $e ) + {} + } } ?> -- svn-components mailing list svn-components@lists.ez.no http://lists.ez.no/mailman/listinfo/svn-components