New module 'ImageAPI'

This commit is contained in:
Manuel Cillero 2017-07-26 13:28:58 +02:00
parent 003589f73e
commit 6be6a844b3
12 changed files with 1929 additions and 0 deletions

View file

@ -0,0 +1,274 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
verbatim copies of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to
share and change it. By contrast, the GNU General Public License is
intended to guarantee your freedom to share and change free software--to
make sure the software is free for all its users. This General Public License
applies to most of the Free Software Foundation's software and to any other
program whose authors commit to using it. (Some other Free Software
Foundation software is covered by the GNU Library General Public License
instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the
freedom to distribute copies of free software (and charge for this service if
you wish), that you receive source code or can get it if you want it, that you
can change the software or use pieces of it in new free programs; and that
you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to
deny you these rights or to ask you to surrender the rights. These restrictions
translate to certain responsibilities for you if you distribute copies of the
software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for
a fee, you must give the recipients all the rights that you have. You must make
sure that they, too, receive or can get the source code. And you must show
them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2)
offer you this license which gives you legal permission to copy, distribute
and/or modify the software.
Also, for each author's protection and ours, we want to make certain that
everyone understands that there is no warranty for this free software. If the
software is modified by someone else and passed on, we want its recipients
to know that what they have is not the original, so that any problems
introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We
wish to avoid the danger that redistributors of a free program will individually
obtain patent licenses, in effect making the program proprietary. To prevent
this, we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification
follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
MODIFICATION
0. This License applies to any program or other work which contains a notice
placed by the copyright holder saying it may be distributed under the terms
of this General Public License. The "Program", below, refers to any such
program or work, and a "work based on the Program" means either the
Program or any derivative work under copyright law: that is to say, a work
containing the Program or a portion of it, either verbatim or with
modifications and/or translated into another language. (Hereinafter, translation
is included without limitation in the term "modification".) Each licensee is
addressed as "you".
Activities other than copying, distribution and modification are not covered
by this License; they are outside its scope. The act of running the Program is
not restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made
by running the Program). Whether that is true depends on what the Program
does.
1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this License
and to the absence of any warranty; and give any other recipients of the
Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you
may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it,
thus forming a work based on the Program, and copy and distribute such
modifications or work under the terms of Section 1 above, provided that you
also meet all of these conditions:
a) You must cause the modified files to carry prominent notices stating that
you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or in
part contains or is derived from the Program or any part thereof, to be
licensed as a whole at no charge to all third parties under the terms of this
License.
c) If the modified program normally reads commands interactively when run,
you must cause it, when started running for such interactive use in the most
ordinary way, to print or display an announcement including an appropriate
copyright notice and a notice that there is no warranty (or else, saying that
you provide a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this License.
(Exception: if the Program itself is interactive but does not normally print such
an announcement, your work based on the Program is not required to print
an announcement.)
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be
reasonably considered independent and separate works in themselves, then
this License, and its terms, do not apply to those sections when you distribute
them as separate works. But when you distribute the same sections as part
of a whole which is a work based on the Program, the distribution of the
whole must be on the terms of this License, whose permissions for other
licensees extend to the entire whole, and thus to each and every part
regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to
work written entirely by you; rather, the intent is to exercise the right to
control the distribution of derivative or collective works based on the
Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of a
storage or distribution medium does not bring the other work under the scope
of this License.
3. You may copy and distribute the Program (or a work based on it, under
Section 2) in object code or executable form under the terms of Sections 1
and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source
code, which must be distributed under the terms of Sections 1 and 2 above
on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give
any third party, for a charge no more than your cost of physically performing
source distribution, a complete machine-readable copy of the corresponding
source code, to be distributed under the terms of Sections 1 and 2 above on
a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute
corresponding source code. (This alternative is allowed only for
noncommercial distribution and only if you received the program in object
code or executable form with such an offer, in accord with Subsection b
above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source code
means all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation and
installation of the executable. However, as a special exception, the source
code distributed need not include anything that is normally distributed (in
either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
If distribution of executable or object code is made by offering access to
copy from a designated place, then offering equivalent access to copy the
source code from the same place counts as distribution of the source code,
even though third parties are not compelled to copy the source along with the
object code.
4. You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy,
modify, sublicense or distribute the Program is void, and will automatically
terminate your rights under this License. However, parties who have received
copies, or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the
Program or its derivative works. These actions are prohibited by law if you
do not accept this License. Therefore, by modifying or distributing the
Program (or any work based on the Program), you indicate your acceptance
of this License to do so, and all its terms and conditions for copying,
distributing or modifying the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these terms and
conditions. You may not impose any further restrictions on the recipients'
exercise of the rights granted herein. You are not responsible for enforcing
compliance by third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), conditions
are imposed on you (whether by court order, agreement or otherwise) that
contradict the conditions of this License, they do not excuse you from the
conditions of this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any other pertinent
obligations, then as a consequence you may not distribute the Program at all.
For example, if a patent license would not permit royalty-free redistribution
of the Program by all those who receive copies directly or indirectly through
you, then the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply and
the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or
other property right claims or to contest validity of any such claims; this
section has the sole purpose of protecting the integrity of the free software
distribution system, which is implemented by public license practices. Many
people have made generous contributions to the wide range of software
distributed through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing to
distribute software through any other system and a licensee cannot impose
that choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain
countries either by patents or by copyrighted interfaces, the original copyright
holder who places the Program under this License may add an explicit
geographical distribution limitation excluding those countries, so that
distribution is permitted only in or among countries not thus excluded. In such
case, this License incorporates the limitation as if written in the body of this
License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will be
similar in spirit to the present version, but may differ in detail to address new
problems or concerns.
Each version is given a distinguishing version number. If the Program specifies
a version number of this License which applies to it and "any later version",
you have the option of following the terms and conditions either of that
version or of any later version published by the Free Software Foundation. If
the Program does not specify a version number of this License, you may
choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs
whose distribution conditions are different, write to the author to ask for
permission. For software which is copyrighted by the Free Software
Foundation, write to the Free Software Foundation; we sometimes make
exceptions for this. Our decision will be guided by the two goals of
preserving the free status of all derivatives of our free software and of
promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE,
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT
PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR
AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR
ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
OR DATA BEING RENDERED INACCURATE OR LOSSES
SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN
IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF
THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

View file

@ -0,0 +1,38 @@
ImageAPI
A non writing image manipulation API for Drupal. This API is meant to be used in place of the API provided
by image.inc. You probably do not need to install this module unless another module are you using requires
it. It provides no new features to your drupal site. It only provides an API other modules can leverage.
Changes From image.inc API:
- Images are objects.
- Images are not written on each image operation and must be explicitly
closed when processing is complete.
- Multiple Image ToolKits can be used simultaneously. However, only the image
toolkit and image was opened with can be used to process it. This is hidden
in the imageapi layer.
API Quick Reference:
imageapi_image_scale_and_crop($image, $width, $height)
imageapi_image_scale($image, $width, $height, $upscale = FALSE)
imageapi_image_resize($image, $width, $height)
imageapi_image_rotate($image, $degrees, $bgcolor = 0x000000)
imageapi_image_crop($image, $x, $y, $width, $height)
imageapi_image_desaturate($image)
imageapi_image_open($file, $toolkit = FALSE)
imageapi_image_close($image, $destination)
$image is an image object returned from imageapi_image_open();
Expanding ImageAPI:
If you wish to expand on ImageAPI add a new wrapper function to
imageapi.module. Do any common preprocessing for all underlying layers in the
wrapper function, then invoke the driver. Pay heed to the function naming in
ImageAPI and ImageAPI GD. If the toolkit changes the size of an image it must
update the $image->info['width'] and $image->info['height'] variables. All
ToolKit functions should return TRUE on success and FALSE on failure.
For more detailed documentation read imageapi.module.
-dopry

View file

@ -0,0 +1,12 @@
name = ImageAPI
description = ImageAPI supporting multiple toolkits.
package = ImageCache
core = 6.x
php = 5.1
; Information added by drupal.org packaging script on 2011-05-16
version = "6.x-1.10"
core = "6.x"
project = "imageapi"
datestamp = "1305563215"

View file

@ -0,0 +1,41 @@
<?php
/**
* Implementation of hook_requirements().
*/
function imageapi_requirements($phase) {
$requirements = array();
// Ensure translations don't break at install time.
$t = get_t();
// Check this at runtime rather than install time because the order of
// installation doesn't take dependencies into account. ImageAPI may have
// been installed by not loaded and if we report a requirement error
// because we can't find its function or no toolkit is enabled modules that
// depend on us will still be enabled but will have a missing dependency.
// Seems like a better idea to let everything get enabled and then inform
// them of the problem.
if ($phase == 'runtime') {
if (count(imageapi_get_available_toolkits()) == 0) {
$requirements['imageapi_toolkits'] = array(
'title' => $t('ImageAPI Toolkit'),
'value' => $t('No ImageAPI toolkits available'),
'severity' => REQUIREMENT_ERROR,
'description' => $t('ImageAPI requires a Toolkit such as ImageAPI GD or ImageAPI ImageMagick to function. Go to !modules and enable one of them.', array('!modules' => l('admin/build/modules', 'admin/build/modules'))),
);
}
}
return $requirements;
}
function imageapi_install() {
}
/**
* Implementation of hook_uninstall().
*
*/
function imageapi_uninstall() {
variable_del('imageapi_image_toolkit');
}

View file

@ -0,0 +1,463 @@
<?php
/**
* @file
*
* An ImageAPI supporting additional image plugins as modules.
* Images are treated as objects, and images are not written per
* manipulation as Drupal's core image handling works.
*
*
* imageapi image api workflow...
* $image = imageapi_image_open($path) to get an image object for $path...
* image_X($image, $arg1, $arg2) to manipulate image object...
* imageapi_image_close($image) to overwrite original image.
*
*/
/**
* Implementation of hook_perm().
*/
function imageapi_perm() {
return array('administer imageapi');
}
/**
* Implementation of hook_menu().
*/
function imageapi_menu() {
$items = array();
$items['admin/settings/imageapi'] = array(
'title' => 'ImageAPI',
'description' => 'Configure ImageAPI.',
'page callback' => 'drupal_get_form',
'page arguments' => array('imageapi_settings'),
'access arguments' => array('administer imageapi'),
);
$toolkits = imageapi_get_available_toolkits();
if ($toolkits) {
$items['admin/settings/imageapi/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -1,
);
$items['admin/settings/imageapi/config'] = array(
'title' => 'Configure',
'type' => MENU_LOCAL_TASK,
'page callback' => 'drupal_get_form',
'page arguments' => array(imageapi_default_toolkit() .'_settings_form'),
'access arguments' => array('administer imageapi'),
);
foreach ($toolkits as $module => $info) {
if (function_exists($module .'_settings_form')) {
$items['admin/settings/imageapi/config/'. $module] = array(
'title' => '@name',
'title arguments' => array('@name' => $info['name']),
'page callback' => 'drupal_get_form',
'page arguments' => array($module .'_settings_form'),
'access arguments' => array('administer imageapi'),
'type' => $module == imageapi_default_toolkit() ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
);
}
else {
drupal_set_message(t('ImageAPI toolkit missing settings form'), 'error');
}
}
}
return $items;
}
function imageapi_settings() {
$form = array();
$toolkits = imageapi_get_available_toolkits();
switch (count($toolkits)) {
case 0:
$form['imageapi_toolkits']['#value'] = t('There are no image toolkit modules enabled. Toolkit modules can be enabled from the <a href="!admin-build-modules">module configuration page</a>.', array('!admin-build-modules' => url('admin/build/modules')));
return $form;
case 1:
$toolkit = key($toolkits);
// The variable needs to be manually set. Otherwise, if a user has two
// toolkits and disables the selected one they won't be able to select the
// remaing toolkit.
variable_set('imageapi_image_toolkit', $toolkit);
$form['imageapi_image_toolkit']['#value'] = t('The %toolkit module is the only enabled image toolkit. Drupal will use it for resizing, cropping and other image manipulations.', array('%toolkit' => $toolkits[$toolkit]['name']));
return $form;
default:
$options = array();
foreach ($toolkits as $module => $info) {
$options[$module] = $info['name'];
}
$form['imageapi_image_toolkit'] = array(
'#type' => 'radios',
'#title' => t('Select a default image processing toolkit'),
'#default_value' => imageapi_default_toolkit(),
'#options' => $options,
'#description' => t('This setting lets you choose which toolkit Drupal uses resizing, cropping and other image manipulations.'),
);
}
return system_settings_form($form);
}
/**
* Return a list of available toolkits.
*
* @return
* An array of the enabled image toolkit modules. The module name is the key
* and the value is a sub-array with the module's 'name' and 'description'.
*/
function imageapi_get_available_toolkits() {
static $toolkits;
if (!isset($toolkits)) {
$toolkits = array();
foreach (module_implements('imageapi_toolkit', TRUE) as $module) {
$info = drupal_parse_info_file(drupal_get_path('module', $module) .'/'. $module .'.info');
$toolkits[$module] = array('name' => $info['name'], 'description' => $info['description']);
}
}
return $toolkits;
}
/**
* Retrieve the name of the currently used toolkit.
*
* @return
* String containing the name of the toolkit, or FALSE if none is available.
*/
function imageapi_default_toolkit() {
$toolkit = variable_get('imageapi_image_toolkit', 'imageapi_gd');
// Verify that the image toolkit is available.
if (isset($toolkit) && module_exists($toolkit)) {
return $toolkit;
}
// If it's not fall back to first available toolist.
foreach (imageapi_get_available_toolkits() as $toolkit => $info) {
return $toolkit;
}
return FALSE;
}
/**
* Invokes the given method using the currently selected toolkit.
*
* @param $method
* A string containing the method to invoke.
* @param $image
* An image object returned by imageapi_image_open().
* @param $params
* An optional array of parameters to pass to the toolkit method.
* @return
* Mixed values (typically Boolean indicating successful operation).
*/
function imageapi_toolkit_invoke($method, &$image, array $params = array()) {
$function = $image->toolkit . '_image_' . $method;
if (function_exists($function)) {
array_unshift($params, $image);
$params[0] = &$image;
return call_user_func_array($function, $params);
}
watchdog('imageapi', 'The selected image handling toolkit %toolkit can not correctly process %function.', array('%toolkit' => $image->toolkit, '%function' => $function), WATCHDOG_ERROR);
return FALSE;
}
/**
* Scales an image to the exact width and height given.
*
* This function achieves the target aspect ratio by cropping the original image
* equally on both sides, or equally on the top and bottom. This function is
* useful to create uniform sized avatars from larger images.
*
* The resulting image always has the exact target dimensions.
*
* @param $image
* An image object returned by imageapi_image_open().
* @param $width
* The target width, in pixels.
* @param $height
* The target height, in pixels.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_image_scale_and_crop(&$image, $width, $height) {
$scale = max($width / $image->info['width'], $height / $image->info['height']);
$x = ($image->info['width'] * $scale - $width) / 2;
$y = ($image->info['height'] * $scale - $height) / 2;
if (imageapi_image_resize($image, $image->info['width'] * $scale, $image->info['height'] * $scale)) {
return imageapi_image_crop($image, $x, $y, $width, $height);
}
return FALSE;
}
/**
* Scales an image to the given width and height while maintaining aspect
* ratio.
*
* The resulting image can be smaller for one or both target dimensions.
*
* @param $image
* An image object returned by imageapi_image_open().
* @param $width
* The target width, in pixels. This value is omitted then the scaling will
* based only on the height value.
* @param $height
* The target height, in pixels. This value is omitted then the scaling will
* based only on the width value.
* @param $upscale
* Boolean indicating that files smaller than the dimensions will be scalled
* up. This generally results in a low quality image.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_image_scale(&$image, $width = NULL, $height = NULL, $upscale = FALSE) {
$aspect = $image->info['height'] / $image->info['width'];
if ($upscale) {
// Set width/height according to aspect ratio if either is empty.
$width = !empty($width) ? $width : $height / $aspect;
$height = !empty($height) ? $height : $width / $aspect;
}
else {
// Set impossibly large values if the width and height aren't set.
$width = !empty($width) ? $width : 9999999;
$height = !empty($height) ? $height : 9999999;
// Don't scale up.
if (round($width) >= $image->info['width'] && round($height) >= $image->info['height']) {
return TRUE;
}
}
if ($aspect < $height / $width) {
$height = $width * $aspect;
}
else {
$width = $height / $aspect;
}
return imageapi_image_resize($image, $width, $height);
}
/**
* Resize an image to the given dimensions (ignoring aspect ratio).
*
* @param $image
* An image object returned by imageapi_image_open().
* @param $width
* The target width, in pixels.
* @param $height
* The target height, in pixels.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_image_resize(&$image, $width, $height) {
$width = (int) round($width);
$height = (int) round($height);
return imageapi_toolkit_invoke('resize', $image, array($width, $height));
}
/**
* Rotate an image by the given number of degrees.
*
* @param $image
* An image object returned by imageapi_image_open().
* @param $degrees
* The number of (clockwise) degrees to rotate the image.
* @param $background
* An hexadecimal integer specifying the background color to use for the
* uncovered area of the image after the rotation. E.g. 0x000000 for black,
* 0xff00ff for magenta, and 0xffffff for white. For images that support
* transparency, this will default to transparent. Otherwise it will
* be white.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_image_rotate(&$image, $degrees, $background = NULL) {
return imageapi_toolkit_invoke('rotate', $image, array($degrees, $background));
}
/**
* Sharpen an image given some sharpening parameters.
*
* NOTE: These parameters only have an effect when Imagemagick is used.
* GD will used a fixed convolution matrix as described in imageapi_gd.module
*
* @param $image
* An imageapi image object returned by imageapi_image_open().
* @param $radius
* The radius of the gaussian, in pixels, not counting the center pixel. (default 0.5)
* @param $sigma
* The standard deviation of the gaussian, in pixels. (default 0.5)
* @param $amount
* The percentage of the difference between the original and the blur image that is
* added back into the original. (default 100)
* @param $threshold
* The threshold, as a fraction of max RGB levels, needed to apply the difference
* amount. (default 0.05)
* @return
* True or FALSE, based on success.
*/
function imageapi_image_sharpen(&$image, $radius, $sigma, $amount, $threshold) {
return imageapi_toolkit_invoke('sharpen', $image, array($radius, $sigma, $amount, $threshold));
}
/**
* Crop an image to the rectangle specified by the given rectangle.
*
* @param $image
* An image object returned by imageapi_image_open().
* @param $x
* The top left coordinate, in pixels, of the crop area (x axis value).
* @param $y
* The top left coordinate, in pixels, of the crop area (y axis value).
* @param $width
* The target width, in pixels.
* @param $height
* The target height, in pixels.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_image_crop(&$image, $x, $y, $width, $height) {
$aspect = $image->info['height'] / $image->info['width'];
if (empty($height)) $height = $width / $aspect;
if (empty($width)) $width = $height * $aspect;
$width = (int) round($width);
$height = (int) round($height);
return imageapi_toolkit_invoke('crop', $image, array($x, $y, $width, $height));
}
/**
* Convert an image to grayscale.
*
* @param $image
* An image object returned by imageapi_image_open().
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_image_desaturate(&$image) {
return imageapi_toolkit_invoke('desaturate', $image);
}
/**
* Open an image file and return an image object.
*
* Any changes to the file are not saved until imageapi_image_close() is called.
*
* @param $file
* Path to an image file.
* @param $toolkit
* An optional, image toolkit name to override the default.
* @return
* An image object or FALSE if there was a problem loading the file. The
* image object has the following properties:
* - 'source' - The original file path.
* - 'info' - The array of information returned by image_get_info()
* - 'toolkit' - The name of the image toolkit requested when the image was
* loaded.
* Image tookits may add additional properties. The caller is advised not to
* monkey about with them.
*/
function imageapi_image_open($file, $toolkit = FALSE) {
if (!$toolkit) {
$toolkit = imageapi_default_toolkit();
}
if ($toolkit) {
$image = new stdClass();
$image->source = $file;
$image->info = image_get_info($file);
$image->toolkit = $toolkit;
if (imageapi_toolkit_invoke('open', $image)) {
return $image;
}
}
return FALSE;
}
/**
* Close the image and save the changes to a file.
*
* @param $image
* An image object returned by imageapi_image_open(). The object's 'info'
* property will be updated if the file is saved successfully.
* @param $destination
* Destination path where the image should be saved. If it is empty the
* original image file will be overwritten.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_image_close($image, $destination = NULL) {
if (empty($destination)) {
$destination = $image->source;
}
if ($return = imageapi_toolkit_invoke('close', $image, array($destination))) {
// Clear the cached file size and refresh the image information.
clearstatcache();
$image->info = image_get_info($destination);
if (@chmod($destination, 0664)) {
return $return;
}
watchdog('imageapi', 'Could not set permissions on destination file: %file', array('%file' => $destination));
}
return FALSE;
}
/**
* Convert a hex string to its RGBA (Red, Green, Blue, Alpha) integer
* components.
*
* @param $hex
* A string specifing an RGB color in the formats:
* '#ABC','ABC','#ABCD','ABCD','#AABBCC','AABBCC','#AABBCCDD','AABBCCDD'
* @return
* An array with four elements for red, green, blue, and alpha.
*/
function imageapi_hex2rgba($hex) {
$hex = ltrim($hex, '#');
if (preg_match('/^[0-9a-f]{3}$/i', $hex)) {
// 'FA3' is the same as 'FFAA33' so r=FF, g=AA, b=33
$r = str_repeat($hex{0}, 2);
$g = str_repeat($hex{1}, 2);
$b = str_repeat($hex{2}, 2);
$a = '0';
}
elseif (preg_match('/^[0-9a-f]{6}$/i', $hex)) {
// #FFAA33 or r=FF, g=AA, b=33
list($r, $g, $b) = str_split($hex, 2);
$a = '0';
}
elseif (preg_match('/^[0-9a-f]{8}$/i', $hex)) {
// #FFAA33 or r=FF, g=AA, b=33
list($r, $g, $b, $a) = str_split($hex, 2);
}
elseif (preg_match('/^[0-9a-f]{4}$/i', $hex)) {
// 'FA37' is the same as 'FFAA3377' so r=FF, g=AA, b=33, a=77
$r = str_repeat($hex{0}, 2);
$g = str_repeat($hex{1}, 2);
$b = str_repeat($hex{2}, 2);
$a = str_repeat($hex{3}, 2);
}
else {
//error: invalide hex string, TODO: set form error..
return FALSE;
}
$r = hexdec($r);
$g = hexdec($g);
$b = hexdec($b);
$a = hexdec($a);
return array($r, $g, $b, $a);
}

View file

@ -0,0 +1,11 @@
name = ImageAPI GD2
description = Uses PHP's built-in GD2 image processing support.
package = ImageCache
core = 6.x
; Information added by drupal.org packaging script on 2011-05-16
version = "6.x-1.10"
core = "6.x"
project = "imageapi"
datestamp = "1305563215"

View file

@ -0,0 +1,84 @@
<?php
function imageapi_gd_requirements($phase) {
$requirements = array();
$t = get_t();
$gd = function_exists('imagegd2');
if (!$gd) {
$requirements['imageapi_gd'] = array(
'title' => $t('GD library'),
'value' => $t('Not installed'),
'severity' => REQUIREMENT_ERROR,
'description' => $t('The GD library for PHP is missing or outdated. Please check the <a href="@url">PHP image documentation</a> for information on how to correct this.', array('@url' => 'http://www.php.net/manual/en/image.setup.php')),
);
return $requirements;
}
// If color module hasn't already do so, report the version.
if (!module_exists('color')) {
$info = gd_info();
$requirements['imageapi_gd'] = array(
'title' => $t('GD library'),
'value' => $info['GD Version'],
'severity' => REQUIREMENT_OK,
);
}
// Check image format support
foreach (array('gif', 'jpeg', 'png') as $format) {
if (function_exists('imagecreatefrom'. $format)) continue;
$requirements['imageapi_gd_'. $format] = array(
'title' => $t('GD !format Support', array('!format' => drupal_ucfirst($format))),
'value' => $t('Not installed'),
'severity' => REQUIREMENT_ERROR,
'description' => $t('PHP GD was not compiled with %format support.', array('%format' => $format)),
);
}
// check non required stuff aka not installation blockers.
if ($phase == 'runtime') {
if (!function_exists('imagerotate')) {
$requirements['imageapi_gd_imagerotate'] = array(
'title' => $t('GD Image Rotation'),
'value' => $t('Low Quality / Poor Performance'),
'severity' => REQUIREMENT_WARNING,
'description' => $t('The installed version of PHP GD does not support image rotations. It was probably compiled using the official GD libraries from <a href="http://www.libgd.org">http://www.libgd.org</a> instead of the GD library bundled with PHP. You should recompile PHP --with-gd using the bundled GD library. See <a href="http://www.php.net/manual/en/image.setup.php">PHP Manual</a> for more information. A slower implementation of imagerotate() written in PHP will used in the interim.'),
);
}
if (!function_exists('imagefilter')) {
$requirements['imageapi_gd_imagefilter'] = array(
'title' => $t('GD Image Filtering'),
'value' => $t('Low Quality / Poor Performance'),
'severity' => REQUIREMENT_WARNING,
'description' => $t('The installed version of PHP GD does not support image filtering(desaturate, blur, negate, etc). It was probably compiled using the official GD libraries from <a href="http://www.libgd.org">http://www.libgd.org</a> instead of the GD library bundled with PHP. You should recompile PHP --with-gd using the bundled GD library. See <a href="http://www.php.net/manual/en/image.setup.php">PHP Manual</a> for more information. A slower implementation of imagefilter() written in PHP will be used in the interim.'),
);
}
// Test the 'memory_limit' PHP configuration directive
$memory_limit = ini_get('memory_limit');
// If $memory_limit contains a value of -1, the PHP runtime
// doesn't impose a limit on memory used by PHP scripts
if ($memory_limit && $memory_limit != -1 && parse_size($memory_limit) < parse_size('96M')) {
$requirements['imagecache_memory_limit'] = array(
'title' => $t('ImageAPI GD Memory Limit'),
'value' => $memory_limit,
'severity' => REQUIREMENT_WARNING,
'description' => $t('It is highly recommended that you set you PHP memory_limit to 96M to use ImageAPI GD. A 1600x1200 images consumes ~45M of memory when decompressed and there are instances where ImageAPI GD is operating on two decompressed images at once.'),
);
}
}
return $requirements;
}
/**
* Implementation of hook_uninstall().
*/
function imageapi_gd_uninstall() {
variable_del('imageapi_jpeg_quality');
}

View file

@ -0,0 +1,430 @@
<?php // $Id$
/**
* @file
* GD2 toolkit functions
*/
/**
* Implementation of hook_imageapi_toolkit().
*
* this hook only gets called to see is a module implements the imageapi hooks...
*/
function imageapi_gd_imageapi_toolkit() {
}
/**
* Settings form for the toolkit.
*/
function imageapi_gd_settings_form() {
$form['imageapi_jpeg_quality'] = array(
'#type' => 'textfield',
'#title' => t('JPEG quality'),
'#description' => t('Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality, but bigger files.'),
'#size' => 10,
'#maxlength' => 3,
'#default_value' => variable_get('imageapi_jpeg_quality', 75),
'#field_suffix' => '%',
);
$form['imageapi_crop_background'] = array(
'#type' => 'textfield',
'#title' => t('Crop background'),
'#description' => t('Hex string specifying the background color to use when cropping images. If not provided, will use the default. Examples: "ABC", "ABCD", "AABBCC", "AABBCCDD".'),
'#size' => 10,
'#maxlength' => 8,
'#default_value' => variable_get('imageapi_crop_background', ''),
'#field_prefix' => '#',
);
$form['imageapi_interlaced'] = array(
'#title' => t('Enable Interlacing'),
'#type' => 'checkbox',
'#default_value' => variable_get('imageapi_interlaced', 0),
'#description' => t('Interlacing is a method of encoding a bitmap image such that a person who has partially received it sees a degraded copy of the entire image. When communicating over a slow communications link, this is often preferable to seeing a perfectly clear copy of one part of the image, as it helps the viewer decide more quickly whether to abort or continue the transmission.')
);
return system_settings_form($form);
}
/**
* Open an image file.
*
* @param $image
* An image object. The $image->resource value will populated by this call.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_gd_image_open($image) {
$extension = str_replace('jpg', 'jpeg', $image->info['extension']);
$function = 'imagecreatefrom'. $extension;
return (function_exists($function) && $image->resource = $function($image->source));
}
/**
* Save an image file to a destination.
*
* @param $image
* An image object.
* @param $destination
* A string file path where the image should be saved.
* @param $extension
* A string containing one of the following extensions: gif, jpg, jpeg, png.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_gd_image_close($image, $destination) {
$extension = str_replace('jpg', 'jpeg', $image->info['extension']);
$function = 'image'. $extension;
if (!function_exists($function)) {
return FALSE;
}
if (variable_get('imageapi_interlaced', 0)) {
imageinterlace($image->resource, 1);
}
if ($extension == 'jpeg') {
return $function($image->resource, $destination, variable_get('imageapi_jpeg_quality', 75));
}
else {
// Always save PNG images with full transparency.
if ($extension == 'png') {
imagealphablending($image->resource, FALSE);
imagesavealpha($image->resource, TRUE);
}
return $function($image->resource, $destination);
}
}
/**
* Crop an image using the GD toolkit.
*
* @param $image
* An image object. The $image->resource, $image->info['width'], and
* $image->info['height'] values will be modified by this call.
* @param $x
* The starting x offset at which to start the crop, in pixels.
* @param $y
* The starting y offset at which to start the crop, in pixels.
* @param $width
* The width of the cropped area, in pixels.
* @param $height
* The height of the cropped area, in pixels.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_gd_image_crop(&$image, $x, $y, $width, $height) {
// Create an image with the new width and height.
$res = imageapi_gd_create_tmp($image, $width, $height);
// Fill the background color if desired.
$background = variable_get('imageapi_crop_background', '');
if (!empty($background)) {
$background = imageapi_hex2rgba($background);
$background = imagecolorallocatealpha($res, $background[0], $background[1], $background[2], $background[3]);
imagefill($res, 0, 0, $background);
}
// Copy the source image to our new destination image. We use
// $image->info['width] instead of $width because we are copying
// using the source image's width and height, not the destination
// width and height.
if (!imagecopyresampled($res, $image->resource, -$x, -$y, 0, 0, $image->info['width'], $image->info['height'], $image->info['width'], $image->info['height'])) {
return FALSE;
}
// Destroy the original image and return the modified image.
imagedestroy($image->resource);
$image->resource = $res;
$image->info['width'] = $width;
$image->info['height'] = $height;
return TRUE;
}
/**
* Scale an image to the specified size using GD.
*
* @param $image
* An image object. The $image->resource, $image->info['width'], and
* $image->info['height'] values will be modified by this call.
* @param $width
* The new width of the resized image, in pixels.
* @param $height
* The new height of the resized image, in pixels.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_gd_image_resize(&$image, $width, $height) {
$res = imageapi_gd_create_tmp($image, $width, $height);
if (!imagecopyresampled($res, $image->resource, 0, 0, 0, 0, $width, $height, $image->info['width'], $image->info['height'])) {
return FALSE;
}
imagedestroy($image->resource);
// Update image object.
$image->resource = $res;
$image->info['width'] = $width;
$image->info['height'] = $height;
return TRUE;
}
/**
* Rotate an image the given number of degrees.
*
* @param $image
* An image object. The $image->resource, $image->info['width'], and
* $image->info['height'] values will be modified by this call.
* @param $degrees
* The number of (clockwise) degrees to rotate the image.
* @param $background
* An hexadecimal integer specifying the background color to use for the
* uncovered area of the image after the rotation. E.g. 0x000000 for black,
* 0xff00ff for magenta, and 0xffffff for white. For images that support
* transparency, this will default to transparent. Otherwise it will
* be white.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_gd_image_rotate(&$image, $degrees, $background) {
// PHP installations using non-bundled GD do not have imagerotate.
if (!function_exists('imagerotate')) {
require_once drupal_get_path('module', 'imageapi_gd') .'/imagerotate.inc';
}
$width = $image->info['width'];
$height = $image->info['height'];
// Convert the hexadecimal background value to a color index value.
if (isset($background)) {
$rgb = array();
for ($i = 16; $i >= 0; $i -= 8) {
$rgb[] = (($background >> $i) & 0xFF);
}
$background = imagecolorallocatealpha($image->resource, $rgb[0], $rgb[1], $rgb[2], 0);
}
// Set the background color as transparent if $background is NULL.
else {
// Get the current transparent color.
$background = imagecolortransparent($image->resource);
// If no transparent colors, use white.
if ($background == 0) {
$background = imagecolorallocatealpha($image->resource, 255, 255, 255, 0);
}
}
// Images are assigned a new color pallete when rotating, removing any
// transparency flags. For GIF images, keep a record of the transparent color.
if ($image->info['extension'] == 'gif') {
$transparent_index = imagecolortransparent($image->resource);
if ($transparent_index != 0) {
$transparent_gif_color = imagecolorsforindex($image->resource, $transparent_index);
}
}
$image->resource = imagerotate($image->resource, 360 - $degrees, $background);
// GIFs need to reassign the transparent color after performing the rotate.
if (isset($transparent_gif_color)) {
$background = imagecolorexactalpha($image->resource, $transparent_gif_color['red'], $transparent_gif_color['green'], $transparent_gif_color['blue'], $transparent_gif_color['alpha']);
imagecolortransparent($image->resource, $background);
}
$image->info['width'] = imagesx($image->resource);
$image->info['height'] = imagesy($image->resource);
return TRUE;
}
function imageapi_gd_image_sharpen(&$image, $radius, $sigma, $amount, $threshold) {
$threshold = round($threshold * 255);
$image->resource = imageapi_gd_unsharp_mask($image->resource, $radius, $sigma, $amount, $threshold);
return TRUE;
}
/**
* Convert an image resource to grayscale.
*
* Note that transparent GIFs loose transparency when desaturated.
*
* @param $image
* An image object. The $image->resource value will be modified by this call.
* @return
* TRUE or FALSE, based on success.
*/
function imageapi_gd_image_desaturate(&$image) {
// PHP installations using non-bundled GD do not have imagefilter.
if (!function_exists('imagefilter')) {
require_once drupal_get_path('module', 'imageapi_gd') .'/imagefilter.inc';
}
return imagefilter($image->resource, IMG_FILTER_GRAYSCALE);
}
/**
* Create a truecolor image preserving transparency from a provided image.
*
* @param $image
* An image object.
* @param $width
* The new width of the new image, in pixels.
* @param $height
* The new height of the new image, in pixels.
* @return
* A GD image handle.
*/
function imageapi_gd_create_tmp($image, $width, $height) {
$res = imagecreatetruecolor($width, $height);
if ($image->info['extension'] == 'gif') {
// Grab transparent color index from image resource.
$transparent = imagecolortransparent($image->resource);
if ($transparent >= 0 && $transparent < imagecolorstotal($image->resource)) {
// The original must have a transparent color, allocate to the new image.
$transparent_color = imagecolorsforindex($image->resource, $transparent);
$transparent = imagecolorallocate($res, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
// Flood with our new transparent color.
imagefill($res, 0, 0, $transparent);
imagecolortransparent($res, $transparent);
}
}
elseif ($image->info['extension'] == 'png') {
imagealphablending($res, FALSE);
$transparency = imagecolorallocatealpha($res, 0, 0, 0, 127);
imagefill($res, 0, 0, $transparency);
imagealphablending($res, TRUE);
imagesavealpha($res, TRUE);
}
else {
imagefill($res, 0, 0, imagecolorallocate($res, 255, 255, 255));
}
return $res;
}
/**
* $sigma is currently unused for _gd_sharp_mask due to 3x3 convolution matrix limit.
* we should explore properly implementing sigma.
*/
function imageapi_gd_unsharp_mask($img, $radius, $sigma, $amount, $threshold) {
//////////////////////////////////////////////////////////////
////
//// Unsharp Mask for PHP - version 2.1.1
////
//// Unsharp mask algorithm by Torstein H<>nsi 2003-07.
//// thoensi_at_netcom_dot_no.
//// Please leave this notice.
////
//////////////////////////////////////////////////////////////
// http://vikjavev.no/computing/ump.php
// $img is an image that is already created within php using
// imgcreatetruecolor. No url! $img must be a truecolor image.
// Attempt to calibrate the parameters to Photoshop:
if ($amount > 500) $amount = 500;
$amount = $amount * 0.016;
if ($radius > 50) $radius = 50;
$radius = $radius * 2;
if ($threshold > 255) $threshold = 255;
$radius = abs(round($radius)); // Only integers make sense.
if ($radius == 0) {
return $img; imagedestroy($img);
break;
}
$w = imagesx($img); $h = imagesy($img);
$img_canvas = imagecreatetruecolor($w, $h);
$img_blur = imagecreatetruecolor($w, $h);
// Gaussian blur matrix:
//
// 1 2 1
// 2 4 2
// 1 2 1
//
//////////////////////////////////////////////////
$matrix = array(
array( 1, 2, 1 ),
array( 2, 4, 2 ),
array( 1, 2, 1 )
);
imagecopy($img_blur, $img, 0, 0, 0, 0, $w, $h);
imageconvolution($img_blur, $matrix, 16, 0);
if ($threshold > 0) {
// Calculate the difference between the blurred pixels and the original
// and set the pixels
for ($x = 0; $x < $w-1; $x++) { // each row
for ($y = 0; $y < $h; $y++) { // each pixel
$rgb_orig = imagecolorat($img, $x, $y);
$r_orig = (($rgb_orig >> 16) & 0xFF);
$g_orig = (($rgb_orig >> 8) & 0xFF);
$b_orig = ($rgb_orig & 0xFF);
$rgb_blur = imagecolorat($img_blur, $x, $y);
$r_blur = (($rgb_blur >> 16) & 0xFF);
$g_blur = (($rgb_blur >> 8) & 0xFF);
$b_blur = ($rgb_blur & 0xFF);
// When the masked pixels differ less from the original
// than the threshold specifies, they are set to their original value.
$r_new = (abs($r_orig - $r_blur) >= $threshold)
? max(0, min(255, ($amount * ($r_orig - $r_blur)) + $r_orig))
: $r_orig;
$g_new = (abs($g_orig - $g_blur) >= $threshold)
? max(0, min(255, ($amount * ($g_orig - $g_blur)) + $g_orig))
: $g_orig;
$b_new = (abs($b_orig - $b_blur) >= $threshold)
? max(0, min(255, ($amount * ($b_orig - $b_blur)) + $b_orig))
: $b_orig;
if (($r_orig != $r_new) || ($g_orig != $g_new) || ($b_orig != $b_new)) {
$pix_col = imagecolorallocate($img, $r_new, $g_new, $b_new);
imagesetpixel($img, $x, $y, $pix_col);
}
}
}
}
else{
for ($x = 0; $x < $w; $x++) { // each row
for ($y = 0; $y < $h; $y++) { // each pixel
$rgb_orig = imagecolorat($img, $x, $y);
$r_orig = (($rgb_orig >> 16) & 0xFF);
$g_orig = (($rgb_orig >> 8) & 0xFF);
$b_orig = ($rgb_orig & 0xFF);
$rgb_blur = imagecolorat($img_blur, $x, $y);
$r_blur = (($rgb_blur >> 16) & 0xFF);
$g_blur = (($rgb_blur >> 8) & 0xFF);
$b_blur = ($rgb_blur & 0xFF);
$r_new = ($amount * ($r_orig - $r_blur)) + $r_orig;
if ($r_new>255) $r_new=255;
elseif ($r_new<0) $r_new=0;
$g_new = ($amount * ($g_orig - $g_blur)) + $g_orig;
if ($g_new>255) $g_new=255;
elseif ($g_new<0) $g_new=0;
$b_new = ($amount * ($b_orig - $b_blur)) + $b_orig;
if ($b_new>255) $b_new=255;
elseif ($b_new<0) $b_new=0;
$rgb_new = ($r_new << 16) + ($g_new <<8) + $b_new;
imagesetpixel($img, $x, $y, $rgb_new);
}
}
}
imagedestroy($img_canvas);
imagedestroy($img_blur);
return $img;
}

View file

@ -0,0 +1,12 @@
name = ImageAPI ImageMagick
description = Command Line ImageMagick support.
package = ImageCache
core = 6.x
; Information added by drupal.org packaging script on 2011-05-16
version = "6.x-1.10"
core = "6.x"
project = "imageapi"
datestamp = "1305563215"

View file

@ -0,0 +1,242 @@
<?php // $Id$
/**
* @file
* ImageMagick toolkit functions
*/
/**
* Implementation of hook_imageapi_toolkit().
*
* this hook only gets called to see is a module implements the imageapi hooks...
*/
function imageapi_imagemagick_imageapi_toolkit() {
}
/**
* Settings form for the toolkit.
*/
function imageapi_imagemagick_settings_form() {
$form['imageapi_imagemagick_quality'] = array(
'#type' => 'textfield',
'#title' => t('Compression Quality'),
'#description' => t('Ranges from 0 to 100. Higher values mean better image quality, but bigger files.'),
'#size' => 10,
'#maxlength' => 3,
'#default_value' => variable_get('imageapi_imagemagick_quality', 75),
'#field_suffix' => '%',
'#element_validate' => array('imageapi_imagemagick_quality_element_validate'),
);
$form['imageapi_imagemagick_binary'] = array(
'#type' => 'fieldset',
'#title' => t('ImageMagick Binary'),
'#collapsible' => FALSE,
'#description' => t('ImageMagick is a standalone program used to manipulate images. To use it, it must be installed on your server and you need to know where it is located. If you are unsure of the exact path consult your ISP or server administrator.'),
);
$form['imageapi_imagemagick_binary']['version'] = array('#weight' => -1);
$form['imageapi_imagemagick_binary']['#after_build'] = array('_imageapi_imagemagick_build_version');
$form['imageapi_imagemagick_binary']['imageapi_imagemagick_convert'] = array(
'#type' => 'textfield',
'#title' => t('Path to the "convert" binary'),
'#default_value' => variable_get('imageapi_imagemagick_convert', '/usr/bin/convert'),
'#required' => TRUE,
'#description' => t('Specify the complete path to the ImageMagic <kbd>convert</kbd> binary. For example: <kbd>/usr/bin/convert</kbd> or <kbd>C:\Program Files\ImageMagick-6.3.4-Q16\convert.exe</kbd>'),
'#element_validate' => array('imageapi_imagemagick_validate_path'),
);
$form['imageapi_imagemagick_binary']['imageapi_imagemagick_debugging'] = array(
'#type' => 'checkbox',
'#title' => t('Display debugging information'),
'#default_value' => variable_get('imageapi_imagemagick_debugging', 0),
'#description' => t('Checking this option will display the ImageMagick commands and output to users with the <em>administer site configuration</em> permission.'),
'#weight' => 2,
);
return system_settings_form($form);
}
function _imageapi_imagemagick_build_version($form_element, $form_state) {
// make sure path is set and valid before running after build.
if ($path_errors = _imageapi_imagemagick_check_path($form_state['values']['imageapi_imagemagick_convert'])) {
// check here is primarily for pre-existing bad settings...
// the #validate should prevent users from adding bad paths.
$form_element['imageapi_imagemagick_binary'] = array(
'#value' => '<p class="error">'. implode('<br />', $path_errors) .'</p>',
);
}
else {
_imageapi_imagemagick_convert_exec('-version', $output, $errors);
$form_element['imageapi_imagemagick_binary']['version'] = array(
'#title' => t('Version information'),
'#value' => '<pre>'. check_plain(trim($output)) .'</pre>',
'#description' => t('The ImageMagick <kbd>convert</kbd> binary was located and return this version information.'),
);
}
$form_element['imageapi_imagemagick_binary']['version']['#type'] = 'item';
$form_element['imageapi_imagemagick_binary']['version']['#weight'] = -1;
return $form_element;
}
function imageapi_imagemagick_validate_path($element) {
if ($errors = _imageapi_imagemagick_check_path($element['#value'])) {
form_set_error($element['#parents'][0], implode('<br />', $errors));
//form_set_value($element['#parents'][0], variable_get('imageapi_imagemagick_convert', '/usr/bin/convert'));
return FALSE;
}
return TRUE;
}
function imageapi_imagemagick_quality_element_validate($element) {
if ($element['#value'] < 0 || $element['#value'] > 100) {
form_set_error($element['#name'], t('Compression Quality must be a value between 0 and 100.'));
}
}
function imageapi_imagemagick_image_open($image) {
$image->ops = array();
return $image;
}
function imageapi_imagemagick_image_close($image, $dst) {
return _imageapi_imagemagick_convert($image->source, $dst, $image->ops);
}
function imageapi_imagemagick_image_crop(&$image, $x, $y, $width, $height) {
$image->ops[] = '-crop '. (int) $width .'x'. (int) $height .'+'. (int) $x .'+'. (int) $y .'!';
$image->info['width'] = $width;
$image->info['height'] = $height;
return TRUE;
}
function imageapi_imagemagick_image_resize(&$image, $width, $height) {
$image->ops[] = '-resize '. (int) $width .'x'. (int) $height .'!';
$image->info['width'] = $width;
$image->info['height'] = $height;
return TRUE;
}
function imageapi_imagemagick_image_rotate(&$image, $degrees, $bgcolor) {
if (is_int($bgcolor)) {
$bgcolor = '#'. str_pad(dechex($bgcolor), 6, 0);
}
else {
$bgcolor = str_replace('0x', '#', $bgcolor);
}
$image->ops[] = '-background '. escapeshellarg($bgcolor) .' -rotate '. (float) $degrees;
return TRUE;
}
function imageapi_imagemagick_image_sharpen(&$image, $radius, $sigma, $amount, $threshold) {
$unsharp_arg = $radius .'x'. $sigma .'+'. $amount/100 .'+'. $threshold;
$image->ops[] = '-unsharp '. $unsharp_arg;
return TRUE;
}
function imageapi_imagemagick_image_desaturate(&$image) {
$image->ops[] = '-colorspace GRAY';
return TRUE;
}
/**
* Calls the convert executable with the specified filter.
*/
function _imageapi_imagemagick_convert($source, $dest, $args) {
$args['quality'] = '-quality '. escapeshellarg(variable_get('imageapi_imagemagick_quality', 75));
// To make use of ImageMagick 6's parenthetical command grouping we need to make
// the $source image the first parameter and $dest the last.
// See http://www.imagemagick.org/Usage/basics/#cmdline for more info.
$command = escapeshellarg($source) .' '. implode(' ', $args) .' '. escapeshellarg($dest);
if (0 != _imageapi_imagemagick_convert_exec($command, $output, $errors)) {
return FALSE;
}
return file_exists($dest);
}
function _imageapi_imagemagick_check_path($path) {
$errors = array();
if (!is_file($path)) {
$errors[] = t('The specified ImageMagick path %file does not exist.', array('%file' => $path));
}
if (!$errors && !is_executable($path)) {
$errors[] = t('The specified ImageMagick path %file is not executable.', array('%file' => $path));
}
if ($errors && $open_basedir = ini_get('open_basedir')) {
$errors[] = t('PHP\'s <a href="!open-basedir">open_basedir</a> security restriction is set to %open-basedir, which may be interfering with attempts to locate ImageMagick.', array('%file' => $path, '%open-basedir' => $open_basedir, '!info-link' => url('http://php.net/features.safe-mode#ini.open-basedir')));
}
return $errors;
}
function _imageapi_imagemagick_convert_exec($command_args, &$output, &$errors) {
$convert_path = variable_get('imageapi_imagemagick_convert', '/usr/bin/convert');
if ($errors = _imageapi_imagemagick_check_path($convert_path)) {
watchdog('imageapi imagemagick', '!errors', array('!errors' => implode('<br />', $errors)), WATCHDOG_ERROR);
return FALSE;
}
// Specify Drupal's root as the working a working directory so that relative
// paths are interpreted correctly.
$drupal_path = getcwd();
if (strstr($_SERVER['SERVER_SOFTWARE'], 'Win32') || strstr($_SERVER['SERVER_SOFTWARE'], 'IIS')) {
// Use Window's start command to avoid the "black window" from showing up:
// http://us3.php.net/manual/en/function.exec.php#56599
// Use /D to run the command from PHP's current working directory so the
// file paths don't have to be absolute.
$convert_path = 'start "window title" /D'. escapeshellarg($drupal_path) .' /B '. escapeshellarg($convert_path);
}
$descriptors = array(
0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w') // stderr
);
if ($h = proc_open($convert_path .' '. $command_args, $descriptors, $pipes, $drupal_path)) {
$output = '';
while (!feof($pipes[1])) {
$output .= fgets($pipes[1]);
}
$errors = '';
while (!feof($pipes[2])) {
$errors .= fgets($pipes[2]);
}
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
$return_code = proc_close($h);
// Display debugging information to authorized users.
if (variable_get('imageapi_imagemagick_debugging', FALSE) && user_access('administer site configuration')) {
drupal_set_message(t('ImageMagick command: @command', array('@command' => $convert_path .' '. $command_args)));
drupal_set_message(t('ImageMagick output: @output', array('@output' => $output)));
}
if ($return_code == 1 && $command_args == '-version') {
// Some versions of ImageMagick return status code 1 when outputting the
// version info. This should not be caught as an error.
}
else if ($return_code != 0) {
// If ImageMagick returned a non-zero code, trigger a PHP error that will
// be caught by Drupal's error handler, logged to the watchdog and
// eventually displayed to the user if configured to do so.
// If $errors is empty, only report the error code.
if (empty($errors)) {
trigger_error(t('ImageMagick reported error code !code.', array('!code' => $return_code)), E_USER_ERROR);
}
// Otherwise report the error code, and the error message.
else {
trigger_error(t("ImageMagick reported error code !code.\nMessage:\n!error", array('!code' => $return_code, '!error' => $errors)), E_USER_ERROR);
}
}
return $return_code;
}
return FALSE;
}

View file

@ -0,0 +1,209 @@
<?php
//Made by Chao Xu(Mgccl) 3/1/07
//www.webdevlogs.com
//V 1.0
// Drupal code style and some restructuring by dopry. http://www.darrelopry.com
define('IMG_FILTER_NEGATE', 0);
define('IMG_FILTER_GRAYSCALE', 1);
define('IMG_FILTER_BRIGHTNESS', 2);
define('IMG_FILTER_CONTRAST', 3);
define('IMG_FILTER_COLORIZE', 4);
define('IMG_FILTER_EDGEDETECT', 5);
define('IMG_FILTER_EMBOSS', 6);
define('IMG_FILTER_GAUSSIAN_BLUR', 7);
define('IMG_FILTER_SELECTIVE_BLUR', 8);
define('IMG_FILTER_MEAN_REMOVAL', 9);
define('IMG_FILTER_SMOOTH', 10);
define('IMAGEAPI_IMAGEFILTER_PHP', 1);
/**
* walk each pixel in an image applying $callback to it.
*/
function _imageapi_gd_pixel_color_walk(&$im, $callback, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL, $arg4 = NULL) {
$max_y = imagesy($im);
$max_x = imagesx($im);
for ($y=0; $y < $max_y; ++$y) {
for ($x=0; $x < $max_x; ++$x) {
$rgb = imagecolorat($im, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = ($rgb & 0xFF);
$a = $rgb >> 24;
$callback($r, $g, $b, $a, $arg1, $arg2, $arg3, $arg4);
// sanitize rgb values.
$r = ($r > 255)? 255 : (($r < 0)? 0:$r);
$g = ($g > 255)? 255 : (($g < 0)? 0:$g);
$b = ($b > 255)? 255 : (($b < 0)? 0:$b);
$a = ($a > 127)? 127 : (($a < 0)? 0:$a);
if (!$color = imagecolorallocatealpha($im, $r, $g, $b, $a)) {
$color = imagecolorclosestalpha($im, $r, $g, $b, $a);
}
imagesetpixel($im, $x, $y, $color);
}
}
}
function _imageapi_gd_pixel_negate(&$r, &$g, &$b, &$a) {
$r = 255 - $r;
$g = 255 - $g;
$b = 255 - $b;
}
function _imageapi_gd_pixel_grayscale(&$r, &$g, &$b, &$a) {
$r = round($r * 0.299 + $g * 0.587 + $b * 0.114);
$g = $r;
$b = $r;
}
function _imageapi_gd_pixel_brightness(&$r, &$g, &$b, &$a, $arg1) {
$r += $arg1;
$g += $arg1;
$b += $arg1;
}
function _imageapi_gd_pixel_contrast(&$r, &$g, &$b, &$a, $contrast) {
// normalize color value between -0.5 - 0.5
// multiply by contrast value to accentuate positive/negative value.
// denormalize to 0-255 range.
$r = ((($r/255 - 0.5) * $contrast) + 0.5) * 255;
$g = ((($g/255 - 0.5) * $contrast) + 0.5) * 255;
$b = ((($b/255 - 0.5) * $contrast) + 0.5) * 255;
}
function _imageapi_gd_pixel_colorize(&$r, &$g, &$b, &$a, $arg1, $arg2, $arg3, $arg4) {
$r += $arg1;
$g += $arg2;
$b += $arg3;
}
function imagefilter(&$im, $var, $arg1 = NULL, $arg2 = NULL, $arg3 = NULL, $arg4 = NULL) {
switch ($var) {
case IMG_FILTER_NEGATE:
_imageapi_gd_pixel_color_walk($im, '_imageapi_gd_pixel_negate');
return TRUE;
case IMG_FILTER_GRAYSCALE:
_imageapi_gd_pixel_color_walk($im, '_imageapi_gd_pixel_grayscale');
return TRUE;
case IMG_FILTER_BRIGHTNESS:
_imageapi_gd_pixel_color_walk($im, '_imageapi_gd_pixel_brightness', $arg1);
return TRUE;
case IMG_FILTER_CONTRAST:
// normalize between 0-1, square to keep positive.
$contrast = pow((100-$arg1)/100, 2);
_imageapi_gd_pixel_color_walk($im, '_imageapi_gd_pixel_contrast', $contrast);
return TRUE;
case IMG_FILTER_COLORIZE:
$arg1 = (is_null($arg1)) ? 0 : $arg1;
$arg2 = (is_null($arg2)) ? 0 : $arg2;
$arg3 = (is_null($arg3)) ? 0 : $arg3;
$arg4 = (is_null($arg4)) ? 0 : $arg4;
_imageapi_gd_pixel_color_walk($im, '_imageapi_gd_pixel_colorize', $arg1, $arg2, $arg3, $arg4);
return TRUE;
case IMG_FILTER_EDGEDETECT:
return imageconvolution($im, array(array(-1, 0, -1), array(0, 4, 0), array(-1, 0, -1)), 1, 127);
case IMG_FILTER_EMBOSS:
return imageconvolution($im, array(array(1.5, 0, 0), array(0, 0, 0), array(0, 0, -1.5)), 1, 127);
case IMG_FILTER_GAUSSIAN_BLUR:
return imageconvolution($im, array(array(1, 2, 1), array(2, 4, 2), array(1, 2, 1)), 16, 0);
case IMG_FILTER_SELECTIVE_BLUR:
for ($y = 0; $y<$max_y; ++$y) {
for ($x = 0; $x<$max_x; ++$x) {
$flt_r_sum = $flt_g_sum = $flt_b_sum = 0;
$cpxl = imagecolorat($im, $x, $y);
for ($j=0; $j<3; ++$j) {
for ($i=0; $i<3; ++$i) {
if (($j == 1) && ($i == 1)) {
$flt_r[1][1] = $flt_g[1][1] = $flt_b[1][1] = 0.5;
}
else {
$pxl = imagecolorat($im, $x-(3>>1)+$i, $y-(3>>1)+$j);
$new_a = $pxl >> 24;
//$r = (($pxl >> 16) & 0xFF);
//$g = (($pxl >> 8) & 0xFF);
//$b = ($pxl & 0xFF);
$new_r = abs((($cpxl >> 16) & 0xFF) - (($pxl >> 16) & 0xFF));
if ($new_r != 0) {
$flt_r[$j][$i] = 1/$new_r;
}
else {
$flt_r[$j][$i] = 1;
}
$new_g = abs((($cpxl >> 8) & 0xFF) - (($pxl >> 8) & 0xFF));
if ($new_g != 0) {
$flt_g[$j][$i] = 1/$new_g;
}
else {
$flt_g[$j][$i] = 1;
}
$new_b = abs(($cpxl & 0xFF) - ($pxl & 0xFF));
if ($new_b != 0) {
$flt_b[$j][$i] = 1/$new_b;
}
else {
$flt_b[$j][$i] = 1;
}
}
$flt_r_sum += $flt_r[$j][$i];
$flt_g_sum += $flt_g[$j][$i];
$flt_b_sum += $flt_b[$j][$i];
}
}
for ($j=0; $j<3; ++$j) {
for ($i=0; $i<3; ++$i) {
if ($flt_r_sum != 0) $flt_r[$j][$i] /= $flt_r_sum;
if ($flt_g_sum != 0) $flt_g[$j][$i] /= $flt_g_sum;
if ($flt_b_sum != 0) $flt_b[$j][$i] /= $flt_b_sum;
$new_r = $new_g = $new_b = 0;
for ($j=0; $j<3; ++$j) {
for ($i=0; $i<3; ++$i) {
$pxl = imagecolorat($im, $x-(3>>1)+$i, $y-(3>>1)+$j);
$new_r += (($pxl >> 16) & 0xFF) * $flt_r[$j][$i];
$new_g += (($pxl >> 8) & 0xFF) * $flt_g[$j][$i];
$new_b += ($pxl & 0xFF) * $flt_b[$j][$i];
}
}
$new_r = ($new_r > 255)? 255 : (($new_r < 0)? 0:$new_r);
$new_g = ($new_g > 255)? 255 : (($new_g < 0)? 0:$new_g);
$new_b = ($new_b > 255)? 255 : (($new_b < 0)? 0:$new_b);
$new_pxl = imagecolorallocatealpha($im, (int)$new_r, (int)$new_g, (int)$new_b, $new_a);
if ($new_pxl == FALSE) {
$new_pxl = imagecolorclosestalpha($im, (int)$new_r, (int)$new_g, (int)$new_b, $new_a);
}
imagesetpixel($im, $x, $y, $new_pxl);
}
}
}
}
return TRUE;
case IMG_FILTER_MEAN_REMOVAL:
return imageconvolution($im, array(array(-1, -1, -1), array(-1, 9, -1), array(-1, -1, -1)), 1, 0);
case IMG_FILTER_SMOOTH:
return imageconvolution($im, array(array(1, 1, 1), array(1, $arg1, 1), array(1, 1, 1)), $arg1 + 8, 0);
}
}

View file

@ -0,0 +1,113 @@
<?php
/**
* @file
* provides the imagerotate function for php-gd extensions compiled with the
* upstream libgd instead of the libgd bundled with php.
*/
// Define as included.
define('IMAGEAPI_IMAGEROTATE_PHP', 1);
function imagerotate(&$im, $angle, $bgcolor) {
if ($angle === 0) {
return $im;
}
// imagerotate() in php's libgd rotates the image counterclockwise,
// this implementation rotates clockwise. The angle needs to be
// inverted to give the same behaviour between these implementations.
$angle = 360 - $angle;
$width = imagesx($im);
$height = imagesy($im);
// background color.
list($r, $g, $b, $a) = imageapi_hex2rgba($bgcolor);
switch ($angle) {
case 270:
case 90:
// flip dimensions.
$rot_width = $height;
$rot_height = $width;
break;
case 180:
// maintain dims.
$rot_width = $width;
$rot_height = $height;
break;
default:
// well it won't be easy but we'll actually rotate this
// puppy ourselves.. gonna require a little trig.
// @todo: convert to a polar equation and use 1/2 length of hypoteneus.
$center_x = floor($width/2);
$center_y = floor($height/2);
// convert to radians and precompute ...
$cosangle = cos(deg2rad($angle+180));
$sinangle = sin(deg2rad($angle+180));
// caluculate new width and height.
$corners=array(array(0, 0), array($width, 0), array($width, $height), array(0, $height));
$max_x = $min_x = $max_y = $min_y = 0;
foreach ($corners as $key => $value) {
$value[0] -= $center_x; //Translate coords to center for rotation
$value[1] -= $center_y;
$x = $value[0] * $cosangle + $value[1] * $sinangle;
$y = $value[1] * $cosangle - $value[0] * $sinangle;
$max_x = max($x, $max_x);
$min_x = min($x, $min_x);
$max_y = max($y, $max_y);
$min_y = min($y, $min_y);
}
$rot_width = (int)$max_x - $min_x;
$rot_height = (int)$max_y - $min_y;
$rot_center_x = floor($rot_width/2);
$rot_center_y = floor($rot_height/2);
}
$rotate = imagecreatetruecolor($rot_width, $rot_height);
$bg = imagecolorallocatealpha($rotate, $r, $g, $b, $a);
imagefilledrectangle($rotate, 0, 0, $rot_width, $rot_height, $bg);
imagealphablending($rotate, FALSE);
imagesavealpha($rotate, TRUE);
switch ($angle) {
case 90:
$rot_width--;
for ($y = 0; $y < $height; ++$y)
for ($x = 0; $x < $width; ++$x)
imagesetpixel($rotate, $rot_width - $y, $x, imagecolorat($im, $x, $y));
break;
case 270:
$rot_height--;
for ($y = 0; $y < $height; ++$y)
for ($x = 0; $x < $width; ++$x)
imagesetpixel($rotate, $y, $rot_height - $x, imagecolorat($im, $x, $y));
break;
case 180:
$rot_width--;
$rot_height--;
for ($y = 0; $y < $height; ++$y)
for ($x = 0; $x < $width; ++$x)
imagesetpixel($rotate, $rot_width - $x, $rot_height - $y, imagecolorat($im, $x, $y));
break;
default:
for ($y = 0; $y < $rot_height; ++$y) {
for ($x = 0; $x < $rot_width; ++$x) {
$mod_y = $rot_center_y-$y;
$mod_x = $rot_center_x-$x;
$old_x = $mod_x * $cosangle + $mod_y * $sinangle + $center_x;
$old_y = $mod_y * $cosangle - $mod_x * $sinangle + $center_y;
if ($old_x >= 0 && $old_x < $width && $old_y >= 0 && $old_y < $height) {
$color = imagecolorat($im, $old_x, $old_y);
imagesetpixel($rotate, $x, $y, $color);
}
}
}
}
return $rotate;
}