API New and renamed image functions

Renamed image functions with more expressive names. Added CropWidth & CropHeight functions. Added no-upsampling capabilities. Cleaned up Image docs. Closes #4211
This commit is contained in:
Jonathon Menz 2015-06-12 16:42:51 -07:00
parent 24a268a12b
commit 838926085c
15 changed files with 603 additions and 267 deletions

View File

@ -512,7 +512,7 @@ The staff section templates aren't too difficult to create, thanks to the utilit
<% loop $Children %>
<article>
<h2><a href="$Link" title="Read more on &quot;{$Title}&quot;">$Title</a></h2>
$Photo.SetWidth(150)
$Photo.ScaleWidth(150)
<p>$Content.FirstParagraph</p>
<a href="$Link" title="Read more on &quot;{$Title}&quot;">Read more &gt;&gt;</a>
</article>
@ -521,7 +521,7 @@ The staff section templates aren't too difficult to create, thanks to the utilit
</div>
This template is very similar to the *ArticleHolder* template. The *SetWidth* method of the `[api:Image]` class
This template is very similar to the *ArticleHolder* template. The *ScaleWidth* method of the `[api:Image]` class
will resize the image before sending it to the browser. The resized image is cached, so the server doesn't have to
resize the image every time the page is viewed.
@ -537,13 +537,13 @@ The *StaffPage* template is also very straight forward.
<article>
<h1>$Title</h1>
<div class="content">
$Photo.SetWidth(433)
$Photo.ScaleWidth(433)
$Content</div>
</article>
$Form
</div>
Here we use the *SetWidth* method to get a different sized image from the same source image. You should now have
Here we use the *ScaleWidth* method to get a different sized image from the same source image. You should now have
a complete staff section.
![](../_images/tutorial2_einstein.jpg)

View File

@ -0,0 +1,40 @@
summary: Learn how to work with File and Image records
# File Management
## Files, Images and Folders as database records
All files, images and folders in the 'assets' directory are stored in the database. Each record has the following database fields:
| Field name | Description |
| ---------- | ----------- |
| `ClassName` | The class name of the file (e.g. File, Image or Folder). |
| `Name` | The 'basename' of the file, or the folder name. For example 'my-image.jpg', or 'images' for a folder. |
| `Title` | The optional, human-readable title of the file for display only (doesn't apply to folders). |
| `Filename` | The path to the file/folder, relative to the webroot. For example 'assets/images/my-image.jpg', or 'assets/images/' for a folder. |
| `Content` | Typically unused, but handy for a textual representation of files. For example for fulltext indexing of PDF documents. |
| `ShowInSearch` | Whether the file should be shown in search results, defaults to '1'. See ["Tutorial 4 - Site Search"](/tutorials/site_search) for enabling search. |
| `ParentID` | The ID of the parent Folder that this File/Folder is in. A ParentID of '0' indicates that the File/Folder is in the 'assets' directory. |
| `OwnerID` | The ID of the Member that 'owns' the File/Folder (not related to filesystem permissions). |
## Management through the "Files" section of the CMS
If you have the CMS module installed, you can manage files, folders and images in the "Files" section of the CMS. Inside this section, you will see a list of files and folders like below:
![](../../_images/assets.png)
You can click on any file to edit it, or click on any folder to open it. To delete a file or a folder, simply click the red 'X' symbol next to it. If you click to open a folder, you can go back up one level by clicking the 'up' arrow above the folder name (highlighted below):
![](../../_images/assets_up.png)
Once you click to edit a file, you will see a form similar to the one below, in which you can edit the file's title, filename, owner, or even change which folder the file is located in:
![](../../_images/assets_editform.png)
You may also notice the 'Sync files' button (highlighted below). This button allows CMS users to 'synchronise' the database (remember, all files/folders are stored as database records) with the filesystem. This is particularly useful if someone has uploaded or removed files/folders via FTP, for example.
![](../../_images/assets_sync.png)
## Upload
Files can be managed through a `FileField` or an `UploadField`. The `[api:FileField]` class provides a simple HTML input with a type of "file", whereas an `[api:UploadField]` provides a much more feature-rich field (including AJAX-based uploads, previews, relationship management and file data management). See [`Reference - UploadField`](/developer_guides/forms/field_types/uploadfield) for more information about how to use the `UploadField` class.

View File

@ -1,135 +0,0 @@
# Image
## Introduction
Represents an image object through the `[api:Image]` class, inheriting all base functionality from the `[api:File]` class with extra functionality including resizing.
## Usage
### Managing images through form fields
Images can be uploaded like any other file, through `[api:FileField]`.
More advanced usage is possible through `[api:UploadField]`,
which provides thumbnails, a detail view of the image properties,
and management of relationships to other DataObject instances.
Allows upload of images through limiting file extensions with `setAllowedExtensions()`.
### Inserting images into the WYSIWYG editor
See [Topics: Rich Text Editing](/topics/rich-text-editing).
### Resizing Images in PHP
The following are methods defined on the GD class which you can call on Image Objects. Note to get the following to work
you need to have GD2 support in your PHP installation and because these generate files you must have write access to
your tmp folder.
:::php
// manipulation functions
$image->resize(width,height); // Basic resize, just skews the image
$image->resizeRatio(width,height) // Resizes an image with max width and height
$image->paddedResize(width,height) // Adds padding after resizing to width or height.
$image->croppedImage(width,height) // Crops the image from the centre, to given values.
$image->resizeByHeight(height) // Maximum height the image resizes to, keeps proportion
$image->resizeByWidth(width) // Maximum width the image resizes to, keeps proportion
$image->greyscale(r,g,b) // alters image channels ===
// values
$image->getHeight() // Returns the height of the image.
$image->getWidth() // Returns the width of the image
$image->getOrienation() // Returns a class constant: ORIENTATION_SQUARE or ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE
You can also create your own functions by extending the image class, for example
:::php
class MyImage extends Image {
public function generateRotateClockwise(GD $gd) {
return $gd->rotate(90);
}
public function generateRotateCounterClockwise(GD $gd) {
return $gd->rotate(270);
}
public function clearResampledImages() {
$files = glob(Director::baseFolder().'/'.$this->Parent()->Filename."_resampled/*-$this->Name");
foreach($files as $file) {unlink($file);}
}
public function Landscape() {
return $this->getWidth() > $this->getHeight();
}
public function Portrait() {
return $this->getWidth() < $this->getHeight();
}
public function generatePaddedImageByWidth(GD $gd,$width=600,$color="fff"){
return $gd->paddedResize($width, round($gd->getHeight()/($gd->getWidth()/$width),0),$color);
}
public function Exif(){
//http://www.v-nessa.net/2010/08/02/using-php-to-extract-image-exif-data
$image = $this->AbsoluteURL;
$d=new ArrayList();
$exif = exif_read_data($image, 0, true);
foreach ($exif as $key => $section) {
$a=new ArrayList();
foreach ($section as $name => $val)
$a->push(new ArrayData(array("Title"=>$name,"Content"=>$val)));
$d->push(new ArrayData(array("Title"=>strtolower($key),"Content"=>$a)));
}
return $d;
}
}
### Resizing in Templates
You can call certain resize functions directly from the template, to use the inbuilt GD functions as the template parser
supports these, for example SetWidth() or SetHeight().
For output of an image tag with the image automatically resized to 80px width, you can use:
:::php
$Image.SetWidth(80) // returns a image 80px wide, ratio kept the same
$Image.SetHeight(80) // returns a image 80px tall, ratio kept the same
$Image.SetSize(80,80) // returns a 80x80px padded image
$Image.SetRatioSize(80,80) // Returns an image scaled proportional, with its greatest diameter scaled to 80px
$Image.CroppedImage(80,80) // Returns an 80x80 image cropped from the center.
$Image.PaddedImage(80, 80, FFFFFF) // Returns an 80x80 image. Unused space is padded white. No crop. No stretching
$Image.Width // returns width of image
$Image.Height // returns height of image
$Image.Orientation // returns Orientation
$Image.Filename // returns filename
$Image.URL // returns filename
### Form Upload
For usage on a website form, see `[api:FileField]`.
If you want to upload images within the CMS, see `[api:UploadField]`.
### Image Quality
To adjust the quality of the generated images when they are resized add the following to your mysite/config/config.yml file:
:::yml
GDBackend:
default_quality: 90
The default value is 75.
### Clearing Thumbnail Cache
Images are (like all other Files) synchronized with the SilverStripe database.
This syncing happens whenever you load the "Files & Images" interface,
and whenever you upload or modify an Image through SilverStripe.
If you encounter problems with images not appearing, or have mysteriously disappeared, you can try manually flushing the
image cache.
http://localhost/dev/tasks/FlushGeneratedImagesTask
## API Documentation
`[api:Image]`

View File

@ -0,0 +1,163 @@
summary: Learn how to crop and resize images in templates and PHP code
# Image
Represents an image object through the `[api:Image]` class, inheriting all base functionality from the `[api:File]` class with extra functionality including resizing.
## Usage
### Managing images through form fields
Images can be uploaded like any other file, through `[api:FileField]`.
More advanced usage is possible through `[api:UploadField]`,
which provides thumbnails, a detail view of the image properties,
and management of relationships to other DataObject instances.
Allows upload of images through limiting file extensions with `setAllowedExtensions()`.
### Inserting images into the WYSIWYG editor
See [Topics: Rich Text Editing](/topics/rich-text-editing).
### Manipulating images in Templates
You can manipulate images directly from templates to create images that are
resized and cropped to suit your needs. This doesn't affect the original
image or clutter the CMS with any additional files, and any images you create
in this way are cached for later use. In most cases the pixel aspect ratios of
images are preserved (meaning images are not stretched).
![](../../_images/image-methods.jpg)
Here are some examples, assuming the `$Image` object has dimensions of 200x100px:
:::ss
// Scaling functions
$Image.ScaleWidth(150) // Returns a 150x75px image
$Image.ScaleMaxWidth(100) // Returns a 100x50px image (like ScaleWidth but prevents up-sampling)
$Image.ScaleHeight(150) // Returns a 300x150px image (up-sampled. Try to avoid doing this)
$Image.ScaleMaxHeight(150) // Returns a 200x100px image (like ScaleHeight but prevents up-sampling)
$Image.Fit(300,300) // Returns an image that fits within a 300x300px boundary, resulting in a 300x150px image (up-sampled)
$Image.FitMax(300,300) // Returns a 200x100px image (like Fit but prevents up-sampling)
// Cropping functions
$Image.Fill(150,150) // Returns a 150x150px image resized and cropped to fill specified dimensions (up-sampled)
$Image.FillMax(150,150) // Returns a 100x100px image (like Fill but prevents up-sampling)
$Image.CropWidth(150) // Returns a 150x100px image (trims excess pixels off the x axis from the center)
$Image.CropHeight(50) // Returns a 200x50px image (trims excess pixels off the y axis from the center)
// Padding functions (add space around an image)
$Image.Pad(100,100) // Returns a 100x100px padded image, with white bars added at the top and bottom
$Image.Pad(100, 100, CCCCCC) // Same as above but with a grey background
// Metadata
$Image.Width // Returns width of image
$Image.Height // Returns height of image
$Image.Orientation // Returns Orientation
$Image.Title // Returns the friendly file name
$Image.Name // Returns the actual file name
$Image.FileName // Returns the actual file name including directory path from web root
$Image.Link // Returns relative URL path to image
$Image.AbsoluteLink // Returns absolute URL path to image
Image methods are chainable. Example:
:::ss
<body style="background-image:url($Image.ScaleWidth(800).CropHeight(800).Link)">
### Manipulating images in PHP
The image manipulation functions can be used in your code with the same names, example: `$image->Fill(150,150)`.
Some of the MetaData functions need to be prefixed with 'get', example `getHeight()`, `getOrientation()` etc.
Please refer to the `[api:Image]` API documentation for specific functions.
### Creating custom image functions
You can also create your own functions by extending the image class, for example
:::php
class MyImage extends DataExtension {
public function Landscape() {
return $this->owner->getWidth() > $this->owner->getHeight();
}
public function Portrait() {
return $this->owner->getWidth() < $this->owner->getHeight();
}
public function PerfectSquare() {
return $this->owner->getFormattedImage('PerfectSquare');
}
public function generatePerfectSquare(Image_Backend $backend) {
return $backend->croppedResize(100,100);
}
public function Exif(){
//http://www.v-nessa.net/2010/08/02/using-php-to-extract-image-exif-data
$image = $this->owner->AbsoluteLink();
$d=new ArrayList();
$exif = exif_read_data($image, 0, true);
foreach ($exif as $key => $section) {
$a=new ArrayList();
foreach ($section as $name => $val)
$a->push(new ArrayData(array("Title"=>$name,"Content"=>$val)));
$d->push(new ArrayData(array("Title"=>strtolower($key),"Content"=>$a)));
}
return $d;
}
}
:::yml
Image:
extensions:
- MyImage
### Form Upload
For usage on a website form, see `[api:FileField]`.
If you want to upload images within the CMS, see `[api:UploadField]`.
### Image Quality
To adjust the quality of the generated images when they are resized add the
following to your mysite/config/config.yml file:
:::yml
GDBackend:
default_quality: 90
# or
ImagickBackend:
default_quality: 90
The default value is 75.
By default SilverStripe image functions will not resample an image if no
cropping or resizing is taking place. You can tell SilverStripe to always to
always produce resampled output by adding this to your
mysite/config/config.yml file:
:::yml
Image:
force_resample: true
If you are intending to resample images with SilverStripe it is good practice
to upload high quality (minimal compression) images as these will produce
better results when resampled. Very high resolution images may cause GD to
crash so a good size for website images is around 2000px on the longest edge.
### Clearing Thumbnail Cache
Images are (like all other Files) synchronized with the SilverStripe database.
This syncing happens whenever you load the "Files & Images" interface,
and whenever you upload or modify an Image through SilverStripe.
If you encounter problems with images not appearing, or have mysteriously
disappeared, you can try manually flushing the image cache.
http://localhost/dev/tasks/FlushGeneratedImagesTask
## API Documentation
`[api:Image]`

View File

@ -1,40 +1,11 @@
summary: Learn how to deal with File and Image records
title: Files
summary: Upload, manage and manipulate files and images.
introduction: Upload, manage and manipulate files and images.
# Files, Images and Folders
[CHILDREN]
## Files, Images and Folders as database records
## API Documentation
All files, images and folders in the 'assets' directory are stored in the database. Each record has the following database fields:
| Field name | Description |
| ---------- | ----------- |
| `ClassName` | The class name of the file (e.g. File, Image or Folder). |
| `Name` | The 'basename' of the file, or the folder name. For example 'my-image.jpg', or 'images' for a folder. |
| `Title` | The optional, human-readable title of the file for display only (doesn't apply to folders). |
| `Filename` | The path to the file/folder, relative to the webroot. For example 'assets/images/my-image.jpg', or 'assets/images/' for a folder. |
| `Content` | Typically unused, but handy for a textual representation of files. For example for fulltext indexing of PDF documents. |
| `ShowInSearch` | Whether the file should be shown in search results, defaults to '1'. See ["Tutorial 4 - Site Search"](/tutorials/site_search) for enabling search. |
| `ParentID` | The ID of the parent Folder that this File/Folder is in. A ParentID of '0' indicates that the File/Folder is in the 'assets' directory. |
| `OwnerID` | The ID of the Member that 'owns' the File/Folder (not related to filesystem permissions). |
## Management through the "Files" section of the CMS
If you have the CMS module installed, you can manage files, folders and images in the "Files" section of the CMS. Inside this section, you will see a list of files and folders like below:
![](../../_images/assets.png)
You can click on any file to edit it, or click on any folder to open it. To delete a file or a folder, simply click the red 'X' symbol next to it. If you click to open a folder, you can go back up one level by clicking the 'up' arrow above the folder name (highlighted below):
![](../../_images/assets_up.png)
Once you click to edit a file, you will see a form similar to the one below, in which you can edit the file's title, filename, owner, or even change which folder the file is located in:
![](../../_images/assets_editform.png)
You may also notice the 'Sync files' button (highlighted below). This button allows CMS users to 'synchronise' the database (remember, all files/folders are stored as database records) with the filesystem. This is particularly useful if someone has uploaded or removed files/folders via FTP, for example.
![](../../_images/assets_sync.png)
## Upload
Files can be managed through a `FileField` or an `UploadField`. The `[api:FileField]` class provides a simple HTML input with a type of "file", whereas an `[api:UploadField]` provides a much more feature-rich field (including AJAX-based uploads, previews, relationship management and file data management). See [`Reference - UploadField`](/developer_guides/forms/field_types/uploadfield) for more information about how to use the `UploadField` class.
* [api:File]
* [api:Image]
* [api:Folder]

View File

@ -20,6 +20,9 @@
* Implementation of new "Archive" concept for page removal, which supercedes "delete". Where deletion removed
pages only from draft, archiving removes from both draft and live simultaneously.
* Support for multiple HtmlEditorConfigs on the same page.
* Most of the `Image` manipulation methods have been renamed
* New `Image` methods `CropWidth` and `CropHeight` added
* 'Max' versions of `Image` methods introduced to prevent up-sampling
#### Deprecated classes/methods removed
@ -393,6 +396,15 @@ In order to remove the new "archive" action and restore the old "delete" action
- CMSBatchAction_Delete
### Update Image method names in PHP code and templates
* `SetRatioSize` -> `Fit`
* `CroppedImage` -> `Fill`
* `PaddedImage` -> `Pad`
* `SetSize` -> `Pad`
* `SetWidth` -> `ScaleWidth`
* `SetHeight` -> `ScaleHeight`
### Other
* Helper function `DB::placeholders` can be used to generate a comma separated list of placeholders

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -364,7 +364,7 @@ class File extends DataObject {
// Preview
if($this instanceof Image) {
$formattedImage = $this->getFormattedImage(
'SetWidth',
'ScaleWidth',
Config::inst()->get('Image', 'asset_preview_width')
);
$thumbnail = $formattedImage ? $formattedImage->URL : '';

View File

@ -673,7 +673,7 @@ class HtmlEditorField_Toolbar extends RequestHandler {
*/
protected function getFieldsForImage($url, $file) {
if($file->File instanceof Image) {
$formattedImage = $file->File->generateFormattedImage('SetWidth',
$formattedImage = $file->File->generateFormattedImage('ScaleWidth',
Config::inst()->get('Image', 'asset_preview_width'));
$thumbnailURL = Convert::raw2att($formattedImage ? $formattedImage->URL : $url);
} else {

View File

@ -873,8 +873,8 @@ class UploadField extends FileField {
return $file->getThumbnail($width, $height)->getURL();
} elseif ($file->hasMethod('getThumbnailURL')) {
return $file->getThumbnailURL($width, $height);
} elseif ($file->hasMethod('SetRatioSize')) {
return $file->SetRatioSize($width, $height)->getURL();
} elseif ($file->hasMethod('Fit')) {
return $file->Fit($width, $height)->getURL();
} else {
return $file->Icon();
}

View File

@ -216,30 +216,271 @@ class Image extends File implements Flushable {
}
/**
* Resize the image by preserving aspect ratio, keeping the image inside the
* $width and $height
* Scale image proportionally to fit within the specified bounds
*
* @param integer $width The width to size within
* @param integer $height The height to size within
* @return Image
*/
public function SetRatioSize($width, $height) {
public function Fit($width, $height) {
// Prevent divide by zero on missing/blank file
if(!$this->getWidth() || !$this->getHeight()) return null;
// Check if image is already sized to the correct dimension
$widthRatio = $width / $this->getWidth();
$heightRatio = $height / $this->getHeight();
if( $widthRatio < $heightRatio ) {
// Target is higher aspect ratio than image, so check width
if($this->isWidth($width) && !Config::inst()->get('Image', 'force_resample')) return $this;
} else {
// Target is wider aspect ratio than image, so check height
// Target is wider or same aspect ratio as image, so check height
if($this->isHeight($height) && !Config::inst()->get('Image', 'force_resample')) return $this;
}
// Item must be regenerated
return $this->getFormattedImage('SetRatioSize', $width, $height);
return $this->getFormattedImage('Fit', $width, $height);
}
/**
* Scale image proportionally to fit within the specified bounds
*
* @param Image_Backend $backend
* @param integer $width The width to size within
* @param integer $height The height to size within
* @return Image_Backend
*/
public function generateFit(Image_Backend $backend, $width, $height) {
return $backend->resizeRatio($width, $height);
}
/**
* Proportionally scale down this image if it is wider or taller than the specified dimensions.
* Similar to Fit but without up-sampling. Use in templates with $FitMax.
*
* @uses Image::Fit()
* @param integer $width The maximum width of the output image
* @param integer $height The maximum height of the output image
* @return Image
*/
public function FitMax($width, $height) {
// Temporary $force_resample support for 3.x, to be removed in 4.0
if (Config::inst()->get('Image', 'force_resample') && $this->getWidth() <= $width && $this->getHeight() <= $height) return $this->Fit($this->getWidth(),$this->getHeight());
return $this->getWidth() > $width || $this->getHeight() > $height
? $this->Fit($width,$height)
: $this;
}
/**
* Resize and crop image to fill specified dimensions.
* Use in templates with $Fill
*
* @param integer $width Width to crop to
* @param integer $height Height to crop to
* @return Image
*/
public function Fill($width, $height) {
return $this->isSize($width, $height) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('Fill', $width, $height);
}
/**
* Resize and crop image to fill specified dimensions.
* Use in templates with $Fill
*
* @param Image_Backend $backend
* @param integer $width Width to crop to
* @param integer $height Height to crop to
* @return Image_Backend
*/
public function generateFill(Image_Backend $backend, $width, $height) {
return $backend->croppedResize($width, $height);
}
/**
* Crop this image to the aspect ratio defined by the specified width and height,
* then scale down the image to those dimensions if it exceeds them.
* Similar to Fill but without up-sampling. Use in templates with $FillMax.
*
* @uses Image::Fill()
* @param integer $width The relative (used to determine aspect ratio) and maximum width of the output image
* @param integer $height The relative (used to determine aspect ratio) and maximum height of the output image
* @return Image
*/
public function FillMax($width, $height) {
// Prevent divide by zero on missing/blank file
if(!$this->getWidth() || !$this->getHeight()) return null;
// Temporary $force_resample support for 3.x, to be removed in 4.0
if (Config::inst()->get('Image', 'force_resample') && $this->isSize($width, $height)) return $this->Fill($width, $height);
// Is the image already the correct size?
if ($this->isSize($width, $height)) return $this;
// If not, make sure the image isn't upsampled
$imageRatio = $this->getWidth() / $this->getHeight();
$cropRatio = $width / $height;
// If cropping on the x axis compare heights
if ($cropRatio < $imageRatio && $this->getHeight() < $height) return $this->Fill($this->getHeight()*$cropRatio, $this->getHeight());
// Otherwise we're cropping on the y axis (or not cropping at all) so compare widths
if ($this->getWidth() < $width) return $this->Fill($this->getWidth(), $this->getWidth()/$cropRatio);
return $this->Fill($width, $height);
}
/**
* Fit image to specified dimensions and fill leftover space with a solid colour (default white). Use in templates with $Pad.
*
* @param integer $width The width to size to
* @param integer $height The height to size to
* @return Image
*/
public function Pad($width, $height, $backgroundColor='FFFFFF') {
return $this->isSize($width, $height) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('Pad', $width, $height, $backgroundColor);
}
/**
* Fit image to specified dimensions and fill leftover space with a solid colour (default white). Use in templates with $Pad.
*
* @param Image_Backend $backend
* @param integer $width The width to size to
* @param integer $height The height to size to
* @return Image_Backend
*/
public function generatePad(Image_Backend $backend, $width, $height, $backgroundColor='FFFFFF') {
return $backend->paddedResize($width, $height, $backgroundColor);
}
/**
* Scale image proportionally by width. Use in templates with $ScaleWidth.
*
* @param integer $width The width to set
* @return Image
*/
public function ScaleWidth($width) {
return $this->isWidth($width) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('ScaleWidth', $width);
}
/**
* Scale image proportionally by width. Use in templates with $ScaleWidth.
*
* @param Image_Backend $backend
* @param int $width The width to set
* @return Image_Backend
*/
public function generateScaleWidth(Image_Backend $backend, $width) {
return $backend->resizeByWidth($width);
}
/**
* Proportionally scale down this image if it is wider than the specified width.
* Similar to ScaleWidth but without up-sampling. Use in templates with $ScaleMaxWidth.
*
* @uses Image::ScaleWidth()
* @param integer $width The maximum width of the output image
* @return Image
*/
public function ScaleMaxWidth($width) {
// Temporary $force_resample support for 3.x, to be removed in 4.0
if (Config::inst()->get('Image', 'force_resample') && $this->getWidth() <= $width) return $this->ScaleWidth($this->getWidth());
return $this->getWidth() > $width
? $this->ScaleWidth($width)
: $this;
}
/**
* Scale image proportionally by height. Use in templates with $ScaleHeight.
*
* @param integer $height The height to set
* @return Image
*/
public function ScaleHeight($height) {
return $this->isHeight($height) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('ScaleHeight', $height);
}
/**
* Scale image proportionally by height. Use in templates with $ScaleHeight.
*
* @param Image_Backend $backend
* @param integer $height The height to set
* @return Image_Backend
*/
public function generateScaleHeight(Image_Backend $backend, $height){
return $backend->resizeByHeight($height);
}
/**
* Proportionally scale down this image if it is taller than the specified height.
* Similar to ScaleHeight but without up-sampling. Use in templates with $ScaleMaxHeight.
*
* @uses Image::ScaleHeight()
* @param integer $height The maximum height of the output image
* @return Image
*/
public function ScaleMaxHeight($height) {
// Temporary $force_resample support for 3.x, to be removed in 4.0
if (Config::inst()->get('Image', 'force_resample') && $this->getHeight() <= $height) return $this->ScaleHeight($this->getHeight());
return $this->getHeight() > $height
? $this->ScaleHeight($height)
: $this;
}
/**
* Crop image on X axis if it exceeds specified width. Retain height.
* Use in templates with $CropWidth. Example: $Image.ScaleHeight(100).$CropWidth(100)
*
* @uses Image::Fill()
* @param integer $width The maximum width of the output image
* @return Image
*/
public function CropWidth($width) {
// Temporary $force_resample support for 3.x, to be removed in 4.0
if (Config::inst()->get('Image', 'force_resample') && $this->getWidth() <= $width) return $this->Fill($this->getWidth(), $this->getHeight());
return $this->getWidth() > $width
? $this->Fill($width, $this->getHeight())
: $this;
}
/**
* Crop image on Y axis if it exceeds specified height. Retain width.
* Use in templates with $CropHeight. Example: $Image.ScaleWidth(100).CropHeight(100)
*
* @uses Image::Fill()
* @param integer $height The maximum height of the output image
* @return Image
*/
public function CropHeight($height) {
// Temporary $force_resample support for 3.x, to be removed in 4.0
if (Config::inst()->get('Image', 'force_resample') && $this->getHeight() <= $height) return $this->Fill($this->getWidth(), $this->getHeight());
return $this->getHeight() > $height
? $this->Fill($this->getWidth(), $height)
: $this;
}
/**
* Resize the image by preserving aspect ratio, keeping the image inside the
* $width and $height
*
* @param integer $width The width to size within
* @param integer $height The height to size within
* @return Image
* @deprecated 4.0 Use Fit instead
*/
public function SetRatioSize($width, $height) {
Deprecation::notice('4.0', 'Use Fit instead');
return $this->Fit($width, $height);
}
/**
@ -250,8 +491,10 @@ class Image extends File implements Flushable {
* @param integer $width The width to size within
* @param integer $height The height to size within
* @return Image_Backend
* @deprecated 4.0 Use generateFit instead
*/
public function generateSetRatioSize(Image_Backend $backend, $width, $height) {
Deprecation::notice('4.0', 'Use generateFit instead');
return $backend->resizeRatio($width, $height);
}
@ -260,11 +503,11 @@ class Image extends File implements Flushable {
*
* @param integer $width The width to set
* @return Image
* @deprecated 4.0 Use ScaleWidth instead
*/
public function SetWidth($width) {
return $this->isWidth($width) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('SetWidth', $width);
Deprecation::notice('4.0', 'Use ScaleWidth instead');
return $this->ScaleWidth($width);
}
/**
@ -273,8 +516,10 @@ class Image extends File implements Flushable {
* @param Image_Backend $backend
* @param int $width The width to set
* @return Image_Backend
* @deprecated 4.0 Use generateScaleWidth instead
*/
public function generateSetWidth(Image_Backend $backend, $width) {
Deprecation::notice('4.0', 'Use generateScaleWidth instead');
return $backend->resizeByWidth($width);
}
@ -283,11 +528,11 @@ class Image extends File implements Flushable {
*
* @param integer $height The height to set
* @return Image
* @deprecated 4.0 Use ScaleHeight instead
*/
public function SetHeight($height) {
return $this->isHeight($height) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('SetHeight', $height);
Deprecation::notice('4.0', 'Use ScaleHeight instead');
return $this->ScaleHeight($height);
}
/**
@ -296,8 +541,10 @@ class Image extends File implements Flushable {
* @param Image_Backend $backend
* @param integer $height The height to set
* @return Image_Backend
* @deprecated 4.0 Use generateScaleHeight instead
*/
public function generateSetHeight(Image_Backend $backend, $height){
Deprecation::notice('4.0', 'Use generateScaleHeight instead');
return $backend->resizeByHeight($height);
}
@ -308,11 +555,11 @@ class Image extends File implements Flushable {
* @param integer $width The width to size to
* @param integer $height The height to size to
* @return Image
* @deprecated 4.0 Use Pad instead
*/
public function SetSize($width, $height) {
return $this->isSize($width, $height) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('SetSize', $width, $height);
Deprecation::notice('4.0', 'Use Pad instead');
return $this->Pad($width, $height);
}
/**
@ -322,8 +569,10 @@ class Image extends File implements Flushable {
* @param integer $width The width to size to
* @param integer $height The height to size to
* @return Image_Backend
* @deprecated 4.0 Use generatePad instead
*/
public function generateSetSize(Image_Backend $backend, $width, $height) {
Deprecation::notice('4.0', 'Use generatePad instead');
return $backend->paddedResize($width, $height);
}
@ -370,11 +619,11 @@ class Image extends File implements Flushable {
* @param integer $width The width to size to
* @param integer $height The height to size to
* @return Image
* @deprecated 4.0 Use Pad instead
*/
public function PaddedImage($width, $height, $backgroundColor='FFFFFF') {
return $this->isSize($width, $height) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('PaddedImage', $width, $height, $backgroundColor);
Deprecation::notice('4.0', 'Use Pad instead');
return $this->Pad($width, $height, $backgroundColor);
}
/**
@ -384,8 +633,10 @@ class Image extends File implements Flushable {
* @param integer $width The width to size to
* @param integer $height The height to size to
* @return Image_Backend
* @deprecated 4.0 Use generatePad instead
*/
public function generatePaddedImage(Image_Backend $backend, $width, $height, $backgroundColor='FFFFFF') {
Deprecation::notice('4.0', 'Use generatePad instead');
return $backend->paddedResize($width, $height, $backgroundColor);
}
@ -547,11 +798,11 @@ class Image extends File implements Flushable {
* @param integer $width Width to crop to
* @param integer $height Height to crop to
* @return Image
* @deprecated 4.0 Use Fill instead
*/
public function CroppedImage($width, $height) {
return $this->isSize($width, $height) && !Config::inst()->get('Image', 'force_resample')
? $this
: $this->getFormattedImage('CroppedImage', $width, $height);
Deprecation::notice('4.0', 'Use Fill instead');
return $this->Fill($width, $height);
}
/**
@ -562,8 +813,10 @@ class Image extends File implements Flushable {
* @param integer $width Width to crop to
* @param integer $height Height to crop to
* @return Image_Backend
* @deprecated 4.0 Use generateFill instead
*/
public function generateCroppedImage(Image_Backend $backend, $width, $height) {
Deprecation::notice('4.0', 'Use generateFill instead');
return $backend->croppedResize($width, $height);
}

View File

@ -3,7 +3,7 @@
<div class="ss-uploadfield-item-preview">
<% if $Width %>
<span>$Preview.SetRatioSize(30, 40)</span>
<span>$Preview.Fit(30, 40)</span>
<% else %>
<span class="no-preview"></span>
<% end_if %>

View File

@ -161,7 +161,7 @@ class GDTest extends SapphireTest {
$fullPath = realpath(dirname(__FILE__) . '/gdtest/test_jpg.jpg');
try {
$gdFailure = new GDBackend_Failure($fullPath, array('SetWidth', 123));
$gdFailure = new GDBackend_Failure($fullPath, array('ScaleWidth', 123));
$this->fail('GDBackend_Failure should throw an exception when setting image resource');
} catch (GDBackend_Failure_Exception $e) {
$cache = SS_Cache::factory('GDBackend_Manipulations');
@ -169,8 +169,8 @@ class GDTest extends SapphireTest {
$data = unserialize($cache->load($key));
$this->assertArrayHasKey('SetWidth|123', $data);
$this->assertTrue($data['SetWidth|123']);
$this->assertArrayHasKey('ScaleWidth|123', $data);
$this->assertTrue($data['ScaleWidth|123']);
}
}
@ -183,12 +183,12 @@ class GDTest extends SapphireTest {
$fullPath = realpath(dirname(__FILE__) . '/gdtest/test_jpg.jpg');
try {
$gdFailure = new GDBackend_Failure($fullPath, array('SetWidth-failed', 123));
$gdFailure = new GDBackend_Failure($fullPath, array('ScaleWidth-failed', 123));
$this->fail('GDBackend_Failure should throw an exception when setting image resource');
} catch (GDBackend_Failure_Exception $e) {
$gd = new GDBackend($fullPath, array('SetWidth', 123));
$this->assertTrue($gd->failedResample($fullPath, 'SetWidth-failed|123'));
$this->assertFalse($gd->failedResample($fullPath, 'SetWidth-not-failed|123'));
$gd = new GDBackend($fullPath, array('ScaleWidth', 123));
$this->assertTrue($gd->failedResample($fullPath, 'ScaleWidth-failed|123'));
$this->assertFalse($gd->failedResample($fullPath, 'ScaleWidth-not-failed|123'));
}
}

View File

@ -34,13 +34,13 @@ class GDImageTest extends ImageTest {
try {
// Simluate a failed manipulation
$gdFailure = new GDBackend_Failure($fullPath, array('SetWidth', 123));
$gdFailure = new GDBackend_Failure($fullPath, array('ScaleWidth', 123));
$this->fail('GDBackend_Failure should throw an exception when setting image resource');
} catch (GDBackend_Failure_Exception $e) {
// Check that the cache has stored the manipulation failure
$data = unserialize($cache->load($key));
$this->assertArrayHasKey('SetWidth|123', $data);
$this->assertTrue($data['SetWidth|123']);
$this->assertArrayHasKey('ScaleWidth|123', $data);
$this->assertTrue($data['ScaleWidth|123']);
// Delete the image object
$image->delete();

View File

@ -100,7 +100,7 @@ class ImageTest extends SapphireTest {
public function testMultipleGenerateManipulationCalls() {
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
$imageFirst = $image->SetWidth(200);
$imageFirst = $image->ScaleWidth(200);
$this->assertNotNull($imageFirst);
$expected = 200;
$actual = $imageFirst->getWidth();
@ -124,27 +124,27 @@ class ImageTest extends SapphireTest {
$this->assertTrue($image->isSize(300, 300));
// Set width to 300 pixels
$imageSetWidth = $image->SetWidth(300);
$this->assertEquals($imageSetWidth->getWidth(), 300);
$this->assertEquals($image->Filename, $imageSetWidth->Filename);
$imageScaleWidth = $image->ScaleWidth(300);
$this->assertEquals($imageScaleWidth->getWidth(), 300);
$this->assertEquals($image->Filename, $imageScaleWidth->Filename);
// Set height to 300 pixels
$imageSetHeight = $image->SetHeight(300);
$this->assertEquals($imageSetHeight->getHeight(), 300);
$this->assertEquals($image->Filename, $imageSetHeight->Filename);
$imageScaleHeight = $image->ScaleHeight(300);
$this->assertEquals($imageScaleHeight->getHeight(), 300);
$this->assertEquals($image->Filename, $imageScaleHeight->Filename);
// Crop image to 300 x 300
$imageCropped = $image->CroppedImage(300, 300);
$imageCropped = $image->Fill(300, 300);
$this->assertTrue($imageCropped->isSize(300, 300));
$this->assertEquals($image->Filename, $imageCropped->Filename);
// Resize (padded) to 300 x 300
$imageSized = $image->SetSize(300, 300);
$imageSized = $image->Pad(300, 300);
$this->assertTrue($imageSized->isSize(300, 300));
$this->assertEquals($image->Filename, $imageSized->Filename);
// Padded image 300 x 300 (same as above)
$imagePadded = $image->PaddedImage(300, 300);
$imagePadded = $image->Pad(300, 300);
$this->assertTrue($imagePadded->isSize(300, 300));
$this->assertEquals($image->Filename, $imagePadded->Filename);
@ -153,16 +153,16 @@ class ImageTest extends SapphireTest {
$this->assertTrue($imageStretched->isSize(300, 300));
$this->assertEquals($image->Filename, $imageStretched->Filename);
// SetRatioSize (various options)
$imageSetRatioSize = $image->SetRatioSize(300, 600);
$this->assertTrue($imageSetRatioSize->isSize(300, 300));
$this->assertEquals($image->Filename, $imageSetRatioSize->Filename);
$imageSetRatioSize = $image->SetRatioSize(600, 300);
$this->assertTrue($imageSetRatioSize->isSize(300, 300));
$this->assertEquals($image->Filename, $imageSetRatioSize->Filename);
$imageSetRatioSize = $image->SetRatioSize(300, 300);
$this->assertTrue($imageSetRatioSize->isSize(300, 300));
$this->assertEquals($image->Filename, $imageSetRatioSize->Filename);
// Fit (various options)
$imageFit = $image->Fit(300, 600);
$this->assertTrue($imageFit->isSize(300, 300));
$this->assertEquals($image->Filename, $imageFit->Filename);
$imageFit = $image->Fit(600, 300);
$this->assertTrue($imageFit->isSize(300, 300));
$this->assertEquals($image->Filename, $imageFit->Filename);
$imageFit = $image->Fit(300, 300);
$this->assertTrue($imageFit->isSize(300, 300));
$this->assertEquals($image->Filename, $imageFit->Filename);
}
/**
@ -178,27 +178,27 @@ class ImageTest extends SapphireTest {
Config::inst()->update('Image', 'force_resample', true);
// Set width to 300 pixels
$imageSetWidth = $image->SetWidth(300);
$this->assertEquals($imageSetWidth->getWidth(), 300);
$this->assertNotEquals($image->Filename, $imageSetWidth->Filename);
$imageScaleWidth = $image->ScaleWidth(300);
$this->assertEquals($imageScaleWidth->getWidth(), 300);
$this->assertNotEquals($image->Filename, $imageScaleWidth->Filename);
// Set height to 300 pixels
$imageSetHeight = $image->SetHeight(300);
$this->assertEquals($imageSetHeight->getHeight(), 300);
$this->assertNotEquals($image->Filename, $imageSetHeight->Filename);
$imageScaleHeight = $image->ScaleHeight(300);
$this->assertEquals($imageScaleHeight->getHeight(), 300);
$this->assertNotEquals($image->Filename, $imageScaleHeight->Filename);
// Crop image to 300 x 300
$imageCropped = $image->CroppedImage(300, 300);
$imageCropped = $image->Fill(300, 300);
$this->assertTrue($imageCropped->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageCropped->Filename);
// Resize (padded) to 300 x 300
$imageSized = $image->SetSize(300, 300);
$imageSized = $image->Pad(300, 300);
$this->assertTrue($imageSized->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageSized->Filename);
// Padded image 300 x 300 (same as above)
$imagePadded = $image->PaddedImage(300, 300);
$imagePadded = $image->Pad(300, 300);
$this->assertTrue($imagePadded->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imagePadded->Filename);
@ -207,16 +207,16 @@ class ImageTest extends SapphireTest {
$this->assertTrue($imageStretched->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageStretched->Filename);
// SetRatioSize (various options)
$imageSetRatioSize = $image->SetRatioSize(300, 600);
$this->assertTrue($imageSetRatioSize->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageSetRatioSize->Filename);
$imageSetRatioSize = $image->SetRatioSize(600, 300);
$this->assertTrue($imageSetRatioSize->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageSetRatioSize->Filename);
$imageSetRatioSize = $image->SetRatioSize(300, 300);
$this->assertTrue($imageSetRatioSize->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageSetRatioSize->Filename);
// Fit (various options)
$imageFit = $image->Fit(300, 600);
$this->assertTrue($imageFit->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageFit->Filename);
$imageFit = $image->Fit(600, 300);
$this->assertTrue($imageFit->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageFit->Filename);
$imageFit = $image->Fit(300, 300);
$this->assertTrue($imageFit->isSize(300, 300));
$this->assertNotEquals($image->Filename, $imageFit->Filename);
Config::inst()->update('Image', 'force_resample', $origForceResample);
}
@ -225,20 +225,52 @@ class ImageTest extends SapphireTest {
$this->assertTrue($image->isSize(300, 300));
// Test normal resize
$resized = $image->SetSize(150, 100);
$resized = $image->Pad(150, 100);
$this->assertTrue($resized->isSize(150, 100));
// Test cropped resize
$cropped = $image->CroppedImage(100, 200);
$cropped = $image->Fill(100, 200);
$this->assertTrue($cropped->isSize(100, 200));
// Test padded resize
$padded = $image->PaddedImage(200, 100);
$padded = $image->Pad(200, 100);
$this->assertTrue($padded->isSize(200, 100));
// Test SetRatioSize
$ratio = $image->SetRatioSize(80, 160);
// Test Fit
$ratio = $image->Fit(80, 160);
$this->assertTrue($ratio->isSize(80, 80));
// Test FitMax
$fitMaxDn = $image->FitMax(200, 100);
$this->assertTrue($fitMaxDn->isSize(100, 100));
$fitMaxUp = $image->FitMax(500, 400);
$this->assertTrue($fitMaxUp->isSize(300, 300));
//Test ScaleMax
$scaleMaxWDn = $image->ScaleMaxWidth(200);
$this->assertTrue($scaleMaxWDn->isSize(200, 200));
$scaleMaxWUp = $image->ScaleMaxWidth(400);
$this->assertTrue($scaleMaxWUp->isSize(300, 300));
$scaleMaxHDn = $image->ScaleMaxHeight(200);
$this->assertTrue($scaleMaxHDn->isSize(200, 200));
$scaleMaxHUp = $image->ScaleMaxHeight(400);
$this->assertTrue($scaleMaxHUp->isSize(300, 300));
// Test FillMax
$cropMaxDn = $image->FillMax(200, 100);
$this->assertTrue($cropMaxDn->isSize(200, 100));
$cropMaxUp = $image->FillMax(400, 200);
$this->assertTrue($cropMaxUp->isSize(300, 150));
// Test Clip
$clipWDn = $image->CropWidth(200);
$this->assertTrue($clipWDn->isSize(200, 300));
$clipWUp = $image->CropWidth(400);
$this->assertTrue($clipWUp->isSize(300, 300));
$clipHDn = $image->CropHeight(200);
$this->assertTrue($clipHDn->isSize(300, 200));
$clipHUp = $image->CropHeight(400);
$this->assertTrue($clipHUp->isSize(300, 300));
}
/**
@ -247,15 +279,15 @@ class ImageTest extends SapphireTest {
public function testGenerateImageWithInvalidParameters() {
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
$image->setHeight('String');
$image->PaddedImage(600,600,'XXXXXX');
$image->Pad(600,600,'XXXXXX');
}
public function testCacheFilename() {
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
$imageFirst = $image->SetSize(200,200);
$imageFirst = $image->Pad(200,200,'CCCCCC');
$imageFilename = $imageFirst->getFullPath();
// Encoding of the arguments is duplicated from cacheFilename
$neededPart = 'SetSize' . base64_encode(json_encode(array(200,200)));
$neededPart = 'Pad' . base64_encode(json_encode(array(200,200,'CCCCCC')));
$this->assertContains($neededPart, $imageFilename, 'Filename for cached image is correctly generated');
}
@ -263,7 +295,7 @@ class ImageTest extends SapphireTest {
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
$folder = new SS_FileFinder();
$imageFirst = $image->SetSize(200,200);
$imageFirst = $image->Pad(200,200);
$this->assertNotNull($imageFirst);
$expected = 200;
$actual = $imageFirst->getWidth();
@ -276,7 +308,7 @@ class ImageTest extends SapphireTest {
$actual = $imageSecond->getHeight();
$this->assertEquals($expected, $actual);
$imageThird = $imageSecond->PaddedImage(600,600,'0F0F0F');
$imageThird = $imageSecond->Pad(600,600,'0F0F0F');
// Encoding of the arguments is duplicated from cacheFilename
$argumentString = base64_encode(json_encode(array(600,600,'0F0F0F')));
$this->assertNotNull($imageThird);
@ -300,7 +332,7 @@ class ImageTest extends SapphireTest {
public function testRegenerateImages() {
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
$image_generated = $image->SetWidth(200);
$image_generated = $image->ScaleWidth(200);
$p = $image_generated->getFullPath();
$this->assertTrue(file_exists($p), 'Resized image exists after creation call');
$this->assertEquals(1, $image->regenerateFormattedImages(), 'Cached images were regenerated correct');
@ -315,7 +347,7 @@ class ImageTest extends SapphireTest {
*/
public function testRegenerateImagesWithRenaming() {
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
$image_generated = $image->SetWidth(200);
$image_generated = $image->ScaleWidth(200);
$p = $image_generated->getFullPath();
$this->assertTrue(file_exists($p), 'Resized image exists after creation call');
@ -337,7 +369,7 @@ class ImageTest extends SapphireTest {
public function testGeneratedImageDeletion() {
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
$image_generated = $image->SetWidth(200);
$image_generated = $image->ScaleWidth(200);
$p = $image_generated->getFullPath();
$this->assertTrue(file_exists($p), 'Resized image exists after creation call');
$numDeleted = $image->deleteFormattedImages();
@ -351,11 +383,11 @@ class ImageTest extends SapphireTest {
public function testMultipleGenerateManipulationCallsImageDeletion() {
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
$firstImage = $image->SetWidth(200);
$firstImage = $image->ScaleWidth(200);
$firstImagePath = $firstImage->getFullPath();
$this->assertTrue(file_exists($firstImagePath));
$secondImage = $firstImage->SetHeight(100);
$secondImage = $firstImage->ScaleHeight(100);
$secondImagePath = $secondImage->getFullPath();
$this->assertTrue(file_exists($secondImagePath));
@ -369,11 +401,11 @@ class ImageTest extends SapphireTest {
*/
public function testPathPropertiesCachedImage() {
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
$firstImage = $image->SetWidth(200);
$firstImage = $image->ScaleWidth(200);
$firstImagePath = $firstImage->getRelativePath();
$this->assertEquals($firstImagePath, $firstImage->Filename);
$secondImage = $firstImage->SetHeight(100);
$secondImage = $firstImage->ScaleHeight(100);
$secondImagePath = $secondImage->getRelativePath();
$this->assertEquals($secondImagePath, $secondImage->Filename);
}