diff --git a/sites/all/modules/imagecache_actions/.gitignore b/sites/all/modules/imagecache_actions/.gitignore deleted file mode 100644 index 1d45c0a..0000000 --- a/sites/all/modules/imagecache_actions/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.patch diff --git a/sites/all/modules/imagecache_actions/CHANGELOG.txt b/sites/all/modules/imagecache_actions/CHANGELOG.txt index 74ad35e..f1866f0 100644 --- a/sites/all/modules/imagecache_actions/CHANGELOG.txt +++ b/sites/all/modules/imagecache_actions/CHANGELOG.txt @@ -3,6 +3,140 @@ Imagecache Actions Imagecache Actions 7.x-1.x-dev ------------------------------ + +Imagecache Actions 7.x-1.9 +-------------------------- +- [#2760121] by lebster, fietserwin: Add a "Perspective" effect. +- [#2947014]: PHP 7.2 warning that each() has been deprecated. + +Imagecache Actions 7.x-1.8 +-------------------------- +- [#2917097]: PHP 7.1 Warning "A non-numeric value encountered". +- [#2905130] by bserem, fietserwin: Remove animation from moving images. +- [#2888678] by das-peter: ImageMagick: Add support for invert effect. +- [#1099300]: Notice: A non well formed numeric value encountered in + image_gd_definecanvas() (line 381 of canvasactions.inc). +- [#2719661] by SKAUGHT: Division by zero. +- [#2717789]: Prevent exif_read_data() warnings. +- Warning: Theme hook coloractions_invert_summary not found. +- [#2696225] by mnlund, fietserwin: Add support for multiply color blending. +- by fietserwin: image_styles_admin: sort image styles by label. +- [#2039379]: Add some better examples for using custom actions. +- Made more contextual information (image style and image effect) available in + the custom action and text image effects. +- [#2690337] by Ajithlal, fietserwin: Would like a feature for converting case + in text effect. + +Imagecache Actions 7.x-1.7 +-------------------------- +- [#2671526]: Fatal error on image style flush when image styles are defined in + Features. + +Imagecache Actions 7.x-1.6 +-------------------------- +- [#2664020]: IE fails to display images whose extension and Content-Type header + do not match the actual image type. +- [#1591484]: Underlay not working (on IM). +- [#2359523]: Follow up to improve help text. +- [#2615986] by hargobind: Effect summary in image_styles_admin.module. +- Notice: Undefined index: element in theme_coloractions_alpha_summary() (line + 98 of coloractions\transparency.inc). +- Removed superfluous theme entry 'imagecache_subroutine_summary' from + autorotate sub module. +- [#2080877] by heathdutton: Interlace / Progressive effect. +- [#760438] by fietserwin: Resize Overlay / Watermark for ImageMagick. +- [#760438] by raintonr, Ace Cooper, ttkaminski, thijsvdanker, fietserwin: + Resize Overlay / Watermark (GD only). +- [#768980] by idebr, fietserwin: Blur effect. +- [#2636314]: 'Import style' can fail on newline characters. +- [#2573225]: Resize by percentage not calculating other field. +- [#2447419]: Correct erroneous call to module_load_include in + imagecache_canvasactions.module. +- [#2416805]: Remove reference to non-existent imagecache_coloractions.install. +- [#2416805]: remove deprecated jquery attr() and replace with FAPI #states + (rounded corners UI form). +- [#2580805]: Coding standards corrections. + + +Imagecache Actions 7.x-1.5 2014-11-29 +------------------------------------- +- [#2366163]: autorotate sometimes doesn't work with ImageMagick. +- Text effect: small clean-up of hook_exit(). +- [#2379359]: image_imagemagick_overlay() passes a stream wrapper to convert. +- [#2359523]: Overlay: allow to define offsets from all sides. +- [#1717436]: Add effects: color level adjustment and desaturate while retaining + alpha. +- [#2278233]: Call to undefined function imagecache_actions_get_style_label(). +- [#2183721]: Duplicate/Export links just edit, they don't duplicate or export. + possible solution only. +- [#2176727]: New actions: scale / resize by percent. +- [#2190759]: hook_image_style_flush(): Prefer silent failure over clear failure. +- [#2166715]: Typo causes extra query every page load. +- [#2139091]: # sign before HEX in settings form breaks Define canvas action. +- [#2085967]: Remove .gitignore from version control. +- Text overlay: treat empty texts as normal situation. +- [#2003446]: Clean up or remove .install files, adapt .info files. +- [1854270-48]: Minor optimization for Posterize. +- Refactor: eliminate test warnings and errors. +- Refactor: standardize function and parameter naming, function order, doxygen + documentation and comments, remove spelling errors in doc and comments. + + +Imagecache Actions 7.x-1.4 2013-08-24 +------------------------------------- +- [#1778214]: Remove project from image_effects_text_test.info +- [#1854270]: Posterize action for file size/bandwidth saving on PNGs. +- [#2060173]: Support image labels (introduced by D7.23). +- Cleaned up the output of image style export. Id's and help texts are not used + on import, so don't have to be exported. +- Repaired farbtastic colorpicker. It seems the library invocation changed + sometime in history. Rebuilt the color picker form to use #attached libraries + and simpler inline script instead. REMOVED the theme function that is now + redundant as #attached is a better way than drupal_add_js. +- [#1152736]: Support tokens in text actions for images. +- [#2025631]: Imagecache Testsuite refers to non-existent permissions. +- Drupal.org went from http to https: changed all links in our documentation and + code. +- Added ImageEffects as other image effect providing module. + + +Imagecache Actions 7.x-1.3 2013-06-04 +------------------------------------- +- Removed some errors and warnings from the test suite code (but not all). +- [#2010560]: Fix spelling for "Update 7001". + + +Imagecache Actions 7.x-1.2 2013-06-01 +------------------------------------- +- [#1830130]: Allow file fields as referring entities in effects with context. +- [#1446160]: Integrate with media module: Title and Alt text. +- [#1591198]: Have dimensions calculations not ignore NULL's. +- Refactored canvas effects: added parameter types in documentation and + signatures. Standardized function documentation. Comments according to Drupal + coding standards. +- [#1999140]: Canvas effects using imagemagick might fail due to earlier + -gravity settings. +- Refactored the image_effects_text_test module to no longer depend on features. +- [#1998354]: Non-ASCII characters fail to display correctly on many systems + (when using Imagemagick). +- Further code enhancements (messages, comments) to autorotate. +- [#1930728]: Autorotate does not reset EXIF orientation tag. +- [#1990620] Many config form docs missing from the D7 upgrade. Converted all + legacy instances of #type=markup into #markup='content..' +- Removed deprecated functions imagecache_actions_entities_from_references() and + imagecache_actions_fields_from_filepath() (see [#1844298]). +- [#1983168]: Custom text via PHP causing EntityMalformedException ... +- Replaced lhandw.ttf font with some real free fonts that may also be + distributed for free. Changed all text effect tests accordingly. +- Better text effect defaults (translated sample text, text visible by default, + sample font file). +- [#1986304]: Strict warning: Only variables should be passed by reference ... +- [#1981490]: Wrong filename imagecache_Actions.install. +- [#1858760]: Unable to find the file 'lhandw.ttf'. Please check the path. + + +Imagecache Actions 7.x-1.1 2012-12-04 +------------------------------------- - [#1591198]: Image dimensions callbacks should handle unknown (NULL) dimensions as valid input. - [#464092]: Aspect Switcher -- Need to Flush Presets. Side-effect: now allows @@ -38,7 +172,7 @@ Incompatibilities: file. This may cause current styles to not being able to find specified files anymore. - Custom actions: custom snippets are now executed using the PHP filter module, - meaning that the image syle editor must have the 'Use PHP for settings' + meaning that the image style editor must have the 'Use PHP for settings' permission to be able to edit the custom action snippet. - Custom actions: information and variables that are available in your custom snippet have changed. See the README.txt of the custom actions module. @@ -54,6 +188,6 @@ coverage". Current and past maintainers for Imagecache Actions --------------------------------------------------- -- dman (http://drupal.org/user/33240) -- sidneyshan (http://drupal.org/user/652426) -- fietserwin (http://drupal.org/user/750928) +- dman (https://drupal.org/user/33240) +- sidneyshan (https://drupal.org/user/652426) +- fietserwin (https://drupal.org/user/750928) diff --git a/sites/all/modules/imagecache_actions/README.txt b/sites/all/modules/imagecache_actions/README.txt index 8a26e60..5cc9074 100644 --- a/sites/all/modules/imagecache_actions/README.txt +++ b/sites/all/modules/imagecache_actions/README.txt @@ -1,15 +1,30 @@ README for the Imagecache Actions Drupal module ----------------------------------------------- -Project page: http://drupal.org/project/imagecache_actions +Project page: https://drupal.org/project/imagecache_actions Current and past maintainers for Imagecache Actions: -- dman (http://drupal.org/user/33240) -- sidneyshan (http://drupal.org/user/652426) -- fietserwin (http://drupal.org/user/750928) +- dman (https://drupal.org/user/33240) +- sidneyshan (https://drupal.org/user/652426) +- fietserwin (https://drupal.org/user/750928) + + +Release notes for 7.x-1.x-dev +----------------------------- +- Clear all caches after updating. + + +Release notes for 7.x-1.4 +------------------------- +- This release supports image labels as introduced by Drupal 7.23. +- See CHANGELOG.txt for a full overview of changes. + Release notes for 7.x-1.1 ------------------------- +- If you use the module:// notation anywhere in an image effect, you must now + install the System Stream Wrapper module + (https://drupal.org/project/system_stream_wrapper). - Clear the cache after updating. @@ -57,27 +72,28 @@ The additional effects that Imagecache Actions provides include: - Text overlay: add e.g. a copyright notice to your image. - Color-shifting: colorize images. - Brighten/Darken. -- Alpha blending: use a grayscale image to define the transparency layer of an +- Alpha blending: use a gray scale image to define the transparency layer of an image. -- Canvas manipulation: resize the canvas and add a backgroundcolor or image. -- File Format switcher: if you need tranparency in JPGs, make them PNG. If your +- Canvas manipulation: resize the canvas and add a background color or image. +- File Format switcher: if you need transparency in JPGs, make them PNG. If your PNG thumbnails are 30K each, save them as JPGs. - Rounded corners. -- TODO: complete list, check short descrptions +- TODO: complete list, check short descriptions -These effects are grouped in submodules. Just enable the ones you want to use. -TODO: list submodules and their sets of effects. +These effects are grouped in sub-modules. Just enable the ones you want to use. +TODO: list sub-modules and their sets of effects. Imagecache Actions supports both the GD toolkit from Drupal core and the Imagemagick toolkit. However, please note that Imagemagick support is not yet complete. Please file an issue if you encounter problems in using Imagemagick. + What is imagecache_action not? ------------------------------ Imagecache Actions does not provide a new UI or new menu items. It hooks into the already existing image styles system (from Drupal core). See -http://drupal.org/documentation/modules/image for more information about working -with images. +https://drupal.org/documentation/modules/image for more information about +working with images. A note about the name of this module @@ -90,7 +106,7 @@ porting to D7, that name has not been changed (yet). Which toolkit to use? --------------------- -Personally, I (fieterwin) prefer the imagemagick toolkit: +Personally, I (fietserwin) prefer the imagemagick toolkit: - It is better in anti-aliasing. Try to rotate an image using both toolkits and you will see what I mean. - It does not execute in the PHP memory space, so is not restricted by the @@ -102,27 +118,36 @@ On the other hand: the GD toolkit is always available (in the correct version), whereas imagemagick is not always present on shared hosting or may be present in an antique version that might give problems. -Please also note that effects may give different results depending on the +Please note that effects may give different results depending on the toolkit used. +Please also note that a 3rd image toolkit exists: + Imagick (https://www.drupal.org/project/imagick) +This toolkit uses the Imagick extension and thus does not call the ImageMagick +binaries directly. However our module does not implement the toolkit specific +parts for the effects we provide for the Imagick toolkit, but the Imagick +toolkit comes with its own set of effects that partly covers our effects. So, +depending on the effects you require it may be a replacement for both our module +and the Imagemagick module or the core GD toolkit. Hard Dependencies ----------------- -- Drupal 7.x - Image module from Drupal core At least 1 of the available image toolkits: -- GD toolkit from Drupal core -- Imagemagick toolkit: http://drupal.org/project/imagemagick +- GD toolkit from Drupal core. +- Imagemagick toolkit: https://drupal.org/project/imagemagick. + Soft Dependencies ----------------- -- System stream wrapper (http://drupal.org/project/system_stream_wrapper) -- Remote stream wrapper (http://drupal.org/project/remote_stream_wrapper) +- System stream wrapper (https://drupal.org/project/system_stream_wrapper) +- Remote stream wrapper (https://drupal.org/project/remote_stream_wrapper) These modules provide additional stream wrappers. Especially the system stream wrapper is very handy as it provides, among others, a module:// and theme:// wrapper. + Installing ---------- As usual. @@ -175,7 +200,6 @@ be defined using either: - A relative (to the current directory, probably Drupal root) or absolute path. - Support ------- Via the issue queue of this project at Drupal.org. @@ -194,3 +218,8 @@ well). - Brightness values outside the -250 .. 250 range are accepted. - Check color fields that allow a transparency component or allow to be empty to specify fully transparent. + +Known problems: Imagemagick +--------------------------- +- Define canvas using offsets may bot work on older versions. We have an error + report for version 6.5.4.7 (2009-07) (https://drupal.org/node/888644). diff --git a/sites/all/modules/imagecache_actions/ROADMAP.txt b/sites/all/modules/imagecache_actions/ROADMAP.txt index eca5a64..544cff8 100644 --- a/sites/all/modules/imagecache_actions/ROADMAP.txt +++ b/sites/all/modules/imagecache_actions/ROADMAP.txt @@ -1,90 +1,112 @@ -Imagecache Actions roadmap --------------------------- - -This is a non-contractual roadmap for the D7 branch of the imagecache actions -module. Actual release dates and completed features will largely depend on -available time. So support is always welcome. Furthermore, critical bugs may -make us release more often, but that should only change the release number in -which certain features are planned, not the timeline. - - -Imagecache Actions 7.x-1.0 --------------------------- -Targeted release date: may 2012 - -- (DONE) Clean up D7 issue queue -- (DONE) Commit posted patches from the issue queue -- (DONE) Solve easy to solve bug reports -- Do some basic testing -- (DONE) Reintroduce the text action, currently living in a sandbox project at - http://drupal.org/node/1090312 -- (DONE) Update README.txt -- (DONE) Introduce CHANGELOG.txt -- (DONE) Add this ROADMAP.txt - - -Imagecache Actions 7.x-1.1 --------------------------- -Targeted release date: end 2012 - -Mainly a bug fix release -- (DONE) Solve remaining outstanding bug reports -- (ALMOST DONE) Keep D7 issue queue clean - - -Imagecache Actions 7.x-1.2 --------------------------- -Targeted release date: spring 2013 - -- Check help and documentation. a.o: hook_help, effect descriptions. -- Continue to keep the D7 issue queue clean. -- Improve Imagemagick support and/or document what effects are not working for - which toolkit (version). -- Check that all effects implement all of the effects API (especially the - dimensions callback). -- Add testing. The idea is to create a set of image styles that cover all - effects and can be used to visually check for regressions. - - Automated testing would be nice, but I am not sure that we can guarantee that - jpeg or png files are binary identical on each run across different computers. - But if the number of false positives is small, it would certainly be a useful - addition. - - -Imagecache Actions 7.x-1.3 --------------------------- -Targeted release date: ... - -- Look at outstanding feature requests -- Continue to keep the D7 issue queue clean -- Keep improving Imagemagick support, a.o: - - Try to get insight into what version of imagemagick is required by what - effect. - - Document which effects might produce different results. -- Refactor code - - Extract common form fields - - Extract common error handling - - (STARTED) Clean up comments, todo's, etc. - - (STARTED) Doxygen code documentation according to latest standards - - Increase the amount of lazy loaded code - - -Imagecache Actions 7.x-2.x --------------------------- -Targeted release date: ... - -- Can we refactor effects into auto loading class based plugins? What does - CTools offer in these? How do D8 core effects do this? -- If so, will it make implementing new effects much simpler? Because that is - what we should strive for. -- Should we rename the module to image_effects? -- If we manage to provide an easy to use plugin system, we should strive to - merge with other modules that provide effects: - - FiltersIE module (http://drupal.org/project/filtersie) - - Imagecache Effects: http://drupal.org/project/imagecache_effects (D6 only, - but are their effects more advanced then our counterparts or do they add new - effects?) - - Image watermark: http://drupal.org/project/watermark (D5 only, but is their - watermark effect more advanced?) - - ImageCache Scale-9 Actions: http://drupal.org/project/imagecache_scale9actions - - etc. +Imagecache Actions roadmap +-------------------------- + +This is a non-contractual roadmap for the D7 branch of the imagecache actions +module. Actual release dates and completed features will largely depend on +available time. So support is always welcome. Furthermore, critical bugs may +make us release more often, but that should only change the release number in +which certain features are planned, not the timeline. + + +Imagecache Actions 7.x-1.0 +-------------------------- +Targeted release date: may 2012 + +- (DONE) Clean up D7 issue queue +- (DONE) Commit posted patches from the issue queue +- (DONE) Solve easy to solve bug reports +- Do some basic testing +- (DONE) Reintroduce the text action, currently living in a sandbox project at + https://drupal.org/node/1090312 +- (DONE) Update README.txt +- (DONE) Introduce CHANGELOG.txt +- (DONE) Add this ROADMAP.txt + + +Imagecache Actions 7.x-1.1 +-------------------------- +Targeted release date: end 2012 + +Mainly a bug fix release +- (DONE) Solve remaining outstanding bug reports +- (ALMOST DONE) Keep D7 issue queue clean + + +Imagecache Actions 7.x-1.2 +-------------------------- +Targeted release date: spring 2013 + +- Check help and documentation. a.o: hook_help, effect descriptions. +- (DONE) Continue to keep the D7 issue queue clean. +- Improve Imagemagick support and/or document what effects are not working for + which toolkit (version). +- (ALMOST DONE) Check that all effects implement all of the effects API (especially the + dimensions callback). +- Add testing. The idea is to create a set of image styles that cover all + effects and can be used to visually check for regressions. + + Automated testing would be nice, but I am not sure that we can guarantee that + jpeg or png files are binary identical on each run across different computers. + But if the number of false positives is small, it would certainly be a useful + addition. + + +Imagecache Actions 7.x-1.3 +-------------------------- +Targeted release date: ... + +- Continue to keep the D7 issue queue clean. +- Check help and documentation. a.o: hook_help, effect descriptions. +- Improve Imagemagick support and/or document what effects are not working for + which toolkit (version). +- Add testing. The idea is to create a set of image styles that cover all + effects and can be used to visually check for regressions. + + Automated testing would be nice, but I am not sure that we can guarantee that + jpeg or png files are binary identical on each run across different computers. + But if the number of false positives is small, it would certainly be a useful + addition. +- Refactor code: + - Extract common form fields. + - Extract common error handling. + - (STARTED) Clean up comments, todo's, etc. + - (STARTED) Doxygen code documentation according to latest standards + - Increase the amount of lazy loaded code. + + +Imagecache Actions 7.x-1.5 +-------------------------- +Targeted release date: ... + +- Look at outstanding feature requests +- Continue to keep the D7 issue queue clean. +- Keep improving Imagemagick support, a.o: + - Try to get insight into what version of imagemagick is required by what + effect. + - Document which effects might produce different results. +- Continue refactoring code: + - Extract common form fields. + - Extract common error handling. + - (STARTED) Clean up comments, todo's, etc. + - (STARTED) Doxygen code documentation according to latest standards. + - Increase the amount of lazy loaded code. + + +Imagecache Actions 8.x-2.x +-------------------------- +Targeted release date: ... + +- Convert image effects into plugins (as [#1821854] does for core). +- This will make adding new effects much simpler, so we can easier add them. +- Should we rename the module to image_effects? +- We should strive to merge with other modules that provide image effects: + - FiltersIE module (https://drupal.org/project/filtersie) + - Imagecache Effects: https://drupal.org/project/imagecache_effects (D6 only, + but are their effects more advanced then our counterparts or do they add new + effects?) + - Image watermark: https://drupal.org/project/watermark (D5 only, but is their + watermark effect more advanced?) + - ImageCache Scale-9 Actions: https://drupal.org/project/imagecache_scale9actions + - ImageEffects: https://drupal.org/project/imageeffects (pixelate and flip). + - Imagick: https://www.drupal.org/project/imagick + - etc. diff --git a/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.info b/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.info index 65de451..5b5a913 100644 --- a/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.info +++ b/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.info @@ -1,16 +1,13 @@ name = Imagecache Autorotate -description = Autorotate image based on EXIF Orientation. +description = Provides an image effect to autorotate an image based on EXIF data. package = Media core = 7.x dependencies[] = image -files[] = imagecache_autorotate.install -files[] = imagecache_autorotate.module - -; Information added by drupal.org packaging script on 2012-12-04 -version = "7.x-1.1" +; Information added by Drupal.org packaging script on 2018-03-20 +version = "7.x-1.9" core = "7.x" project = "imagecache_actions" -datestamp = "1354653754" +datestamp = "1521550387" diff --git a/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.install b/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.install index 1f8c8f0..4e55856 100644 --- a/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.install +++ b/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.install @@ -1,13 +1,25 @@ 'EXIF extension', + 'value'=> $phase === 'runtime' ? $t('Disabled') : '', + 'description'=> $t('The autorotate image effect requires the exif extension to be enabled.'), + 'severity' => REQUIREMENT_ERROR, + ); } + else { + $result['imagecache_autorotate_exif_extension'] = array( + 'title'=> 'EXIF extension', + 'value'=> $t('Enabled'), + 'severity' => REQUIREMENT_OK, + ); + + } + return $result; } diff --git a/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.module b/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.module index 106faad..2889ca6 100644 --- a/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.module +++ b/sites/all/modules/imagecache_actions/autorotate/imagecache_autorotate.module @@ -1,61 +1,71 @@ - - - 6 - - - - - */ - function imagecache_autorotate_image_effect_info() { $effects = array(); $effects['imagecache_autorotate'] = array( 'label' => t('Autorotate'), - 'help' => t('Add autorotate image based on EXIF Orientation.'), - 'effect callback' => 'imagecache_autorotate_image', + 'help' => t('Autorotate image based on EXIF orientation and reset that tag.'), + 'effect callback' => 'imagecache_autorotate_effect', 'dimensions callback' => 'imagecache_autorotate_dimensions', + 'form callback' => 'imagecache_autorotate_form', + 'summary theme' => 'imagecache_autorotate_summary', ); return $effects; } /** - * @todo: This form is no longer needed nor defined in the hook above. If this - * information still needs to be displayed it should probably be moved to help. + * Implements hook_theme(). + * + * Registers theme functions for the effect summaries. + */ +function imagecache_autorotate_theme() { + return array( + 'imagecache_autorotate_summary' => array( + 'variables' => array('data' => NULL), + ), + ); +} + +/** + * Builds the auto-rotate form. + * + * This effect has no options, only some help text, so the form is displayed + * anyway. */ function imagecache_autorotate_form() { $form = array(); $form['help'] = array( - '#type' => 'markup', - '#value' => "

- There are no user-configurable options for this process. -

- Certain cameras can embed orientation information into image + '#markup' => "

There are no user-configurable options for this process.

+

Certain cameras can embed orientation information into image files when they save them. This information is embedded in an EXIF tag and can be used to rotate images to their correct position for display. -

Not all cameras or images contain this information. - This process is only useful for those that do. -

- The expected/supported values are -
Tag: 0x0112 Orientation + This process is only useful for images that contain this information, + whereas for other images it is harmless. +

+

Although most modern browsers do support the orientation tag, the + information may get lost or become incorrect by other operations. + So, to support all browsers and prevent rotation errors, it is better to + start each image style with this effect. +

+

The expected/supported values are:
+ Tag: 0x0112 Orientation

-

- -EXIF Reference -

+

Wikipedia: Exchangeable image file format

", ); return $form; } /** - * Autorotate image based on EXIF Orientation tag. + * Implements theme_hook() for the autorotate effect summary. * - * See code at - * http://sylvana.net/jpegcrop/exif_orientation.html + * param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. * - * and reference at - * http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html - * - * @todo: - * Add horizontal and vertical flips etc. - * Need to create sample set for tests. + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable */ -function imagecache_autorotate_image($image, $data) { - // Test to see if EXIF is supported for image type (e.g. not PNG). - if ($image->info['mime_type'] == 'image/jpeg') { - if (!function_exists('exif_read_data')) { - watchdog('image', 'The image %file could not be auto-rotated because the exif_read_data() function is not available in this PHP installation. You probably will have to enable the exif extension.', array('%file' => $image->source)); - return FALSE; - } - $exif = exif_read_data(drupal_realpath($image->source)); - if (isset($exif['Orientation'])) { - switch ($exif['Orientation']) { - case 3: - $degrees = 180; - break; - case 6: - $degrees = 90; - break; - case 8: - $degrees = 270; - break; - default: - $degrees = 0; - } - if ($degrees != 0) { - $org_width = $image->info['width']; - $org_height = $image->info['height']; - image_rotate($image, $degrees); - if (($degrees === 90 || $degrees === 270) && $image->info['width'] === $org_width) { - // The toolkit failed to alter the dimensions (imagemagick currently - // fails to do so). So we do it ourselves. - $image->info['width'] = $org_height; - $image->info['height'] = $org_width; - } - } - } +function theme_imagecache_autorotate_summary(/*array $variables*/) { + return 'image based on its EXIF data.'; +} + +/** + * Autorotate image based on EXIF Orientation tag. + */ +function imagecache_autorotate_effect(stdClass $image /*, $data*/) { + // Test to see if EXIF is supported by the current image type. + if (in_array($image->info['mime_type'], array('image/jpeg', 'image/tiff'))) { + // Hand over to toolkit. + return image_toolkit_invoke('imagecache_autorotate', $image); } - else if ($image->source === 'modules/image/sample.png' && !function_exists('exif_read_data')) { - // Issue a warning if we are in the admin screen and the exif extension is - // not enabled. - drupal_set_message(t('The exif_read_data() function is not available in this PHP installation. You probably will have to enable the exif extension.'), 'warning'); + else if ($image->source === 'modules/image/sample.png' && user_access('administer image styles')) { + if (!extension_loaded('exif')) { + // Issue a warning if we are in the admin screen and the exif extension is + // not enabled. + drupal_set_message(t('The autorotate image effect requires the exif extension to be enabled.'), 'warning'); + if ($image->toolkit === 'imagemagick') { + drupal_set_message(t('Though imagemagick will work without the exif extension, subsequent effects may fail as the image dimensions cannot be updated.'), 'warning'); + } + } } return TRUE; } /** - * Image dimensions callback; Auto-rotate. + * GD toolkit specific implementation of this image effect. + * + * @param stdClass $image + * + * @return bool + * true on success, false otherwise. + */ +function image_gd_imagecache_autorotate(stdClass $image) { + if (!function_exists('exif_read_data')) { + watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function is not available in this PHP installation. You probably have to enable the exif extension.'))); + return FALSE; + } + + // Read and check result. + $exif = @exif_read_data(drupal_realpath($image->source)); + if ($exif === FALSE && $image->extension === 'jpg') { + watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function returned FALSE.'))); + return FALSE; + } + + if (isset($exif['Orientation'])) { + // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html: + // 1 = Horizontal (normal) + // 2 = Mirror horizontal + // 3 = Rotate 180 + // 4 = Mirror vertical + // 5 = Mirror horizontal and rotate 270 CW + // 6 = Rotate 90 CW + // 7 = Mirror horizontal and rotate 90 CW + // 8 = Rotate 270 CW + // @todo: Add horizontal and vertical flips etc. + // imagecopy seems to be able to mirror, see conmments on + // http://php.net/manual/en/function.imagecopy.php + // @todo: Create sample set for tests. + switch ($exif['Orientation']) { + case 3: + $degrees = 180; + break; + case 6: + $degrees = 90; + break; + case 8: + $degrees = 270; + break; + default: + $degrees = 0; + } + if ($degrees != 0) { + return image_rotate($image, $degrees); + } + } + return TRUE; +} + +/** + * Imagemagick toolkit specific implementation of this image effect. + * + * @param stdClass $image + * An image object. + * + * @return bool + * true on success, false otherwise. + * + * @see http://www.imagemagick.org/script/command-line-options.php#auto-orient + */ +function image_imagemagick_imagecache_autorotate(stdClass $image) { + // Use the exif extension, if enabled, to figure out the new dimensions. + // Moreover (see [#2366163]): to prevent a bug in IM to incorrectly rotate the + // image when it should not, we only pass the auto-orient argument when the + // exif extension could detect the 'Orientation' tag. + if (function_exists('exif_read_data')) { + $exif = @exif_read_data(drupal_realpath($image->source)); + if (isset($exif['Orientation'])) { + switch ($exif['Orientation']) { + case 1: + // Normal orientation: no need to rotate or to change the dimensions. + break; + case 5: + case 6: + case 7: + case 8: + // 90 or 270 degrees rotation (+ optional mirror): swap dimensions. + $image->ops[] = '-auto-orient'; + $tmp = $image->info['width']; + $image->info['width'] = $image->info['height']; + $image->info['height'] = $tmp; + break; + default: + // All other orientations: pass the arguments, but the dimensions + // remain the same. + $image->ops[] = '-auto-orient'; + break; + } + } + elseif ($exif === FALSE && $image->extension === 'jpg') { + watchdog('imagecache_actions', 'Image %file could not be auto-rotated: !message', array('%file' => $image->source, '!message' => t('The exif_read_data() function returned FALSE.'))); + } + } + else { + // We do add the auto-orient argument to IM. IM will determine itself + // whether to rotate or not. + $image->ops[] = '-auto-orient'; + // However we cannot keep track of the dimensions anymore. + if ($image->info['width'] !== $image->info['height']) { + $image->info['width'] = $image->info['height'] = NULL;; + } + } + return TRUE; +} + +/** + * Image dimensions callback for this image effect. * * @param array $dimensions * An array with the dimensions (in pixels) to be modified. - * @param array $data - * An array of parameters for the autorotate effect (empty for this effect). -*/ -function imagecache_autorotate_dimensions(array &$dimensions, array $data) { + * param array $data + * An associative array containing the effect data. + */ +function imagecache_autorotate_dimensions(array &$dimensions/*, array $data*/) { // We can only know the resulting dimensions if both dimensions are equal. // Otherwise we need to inspect the image itself, which is not passed in here. - // (this callback was introduced to enhance performance by not accessing the + // (this callback was introduced to enhance performance by NOT accessing the // image file when rendering the width and height attributes of the html img // tag). if ($dimensions['width'] !== $dimensions['height']) { diff --git a/sites/all/modules/imagecache_actions/autorotate/portrait-painting.jpg b/sites/all/modules/imagecache_actions/autorotate/portrait-painting.jpg new file mode 100644 index 0000000..c6e4fdb Binary files /dev/null and b/sites/all/modules/imagecache_actions/autorotate/portrait-painting.jpg differ diff --git a/sites/all/modules/imagecache_actions/canvasactions/canvasactions.inc b/sites/all/modules/imagecache_actions/canvasactions/canvasactions.inc index 4c2d04c..b5ecf4c 100644 --- a/sites/all/modules/imagecache_actions/canvasactions/canvasactions.inc +++ b/sites/all/modules/imagecache_actions/canvasactions/canvasactions.inc @@ -1,14 +1,14 @@ toolkit); if ($mask) { // @todo: (sydneyshan) Consider best way to add offset support - I assume we @@ -83,21 +90,24 @@ function canvasactions_imagemask_image(&$image, $data = array()) { } /** - * Implements the image mask effect using the GD toolkit. + * GD toolkit specific implementation of the image mask effect. * - * $image object + * @param stdClass $image * Image object containing the GD image resource to operate on. - * $mask object + * @param stdClass $mask * An image object containing the image to use as mask. + * + * @return bool + * true on success, false otherwise. */ -function image_gd_imagemask($image, $mask) { +function image_gd_imagemask(stdClass $image, stdClass $mask) { $newPicture = imagecreatetruecolor($image->info['width'], $image->info['height']); imagesavealpha($newPicture, TRUE); imagealphablending($newPicture, TRUE); $transparent = imagecolorallocatealpha($newPicture, 0, 0, 0, 127); imagefill($newPicture, 0, 0, $transparent); - // Perform pixel-based alpha map application + // Perform pixel-based alpha map application. for ($x = 0; $x < $image->info['width']; $x++) { for ($y = 0; $y < $image->info['height']; $y++) { // Deal with images with mismatched sizes @@ -113,7 +123,7 @@ function image_gd_imagemask($image, $mask) { } } - // Copy back to original picture + // Copy back to original picture. imagedestroy($image->resource); $image->resource = $newPicture; @@ -122,30 +132,32 @@ function image_gd_imagemask($image, $mask) { /** - * Implements the image mask effect using the imagemagick toolkit. + * Imagemagick toolkit specific implementation of the image mask effect. * - * $image object - * Image object containing a.o. the image to operate on. - * $mask object + * @param stdClass $image + * Image object containing the image resource to operate on. + * @param stdClass $mask * An image object containing the image to use as mask. + * + * @return bool + * true on success, false otherwise. */ -function image_imagemagick_imagemask($image, $mask) { - $image->ops[] = escapeshellarg($mask->source) . ' -alpha Off -compose CopyOpacity -composite'; +function image_imagemagick_imagemask(stdClass $image, stdClass $mask) { + $image->ops[] = escapeshellarg($mask->source); + $image->ops[] = '-alpha Off -compose CopyOpacity -composite'; return TRUE; } -//////////////////////////////////////////////// - - /** - * Implementation of imagecache_hook_form() + * Image effect form callback for the define canvas effect. * - * Settings for preparing a canvas. + * @param array $data + * The current configuration for this image effect. * - * @param $data array of settings for this action - * @return a form definition + * @return array + * The form definition for this effect. */ -function canvasactions_definecanvas_form($data) { +function canvasactions_definecanvas_form(array $data) { module_load_include('inc', 'imagecache_actions', 'utility-color'); $defaults = array( 'RGB' => array( @@ -165,13 +177,12 @@ function canvasactions_definecanvas_form($data) { 'bottomdiff' => '', ), ); - $data = array_merge($defaults, (array) $data); + $data += $defaults; $form = array( 'RGB' => imagecache_rgb_form($data['RGB']), 'help' => array( - '#type' => 'markup', - '#value' => t('Enter no color value for transparent. This will have the effect of adding clear margins around the image.'), + '#markup' => t('Enter no color value for transparent. This will have the effect of adding clear margins around the image.'), '#prefix' => '

', '#suffix' => '

', ), @@ -188,8 +199,7 @@ function canvasactions_definecanvas_form($data) { '#collapsible' => TRUE, '#title' => 'Exact size', 'help' => array( - '#type' => 'markup', - '#value' => t('Set the canvas to a precise size, possibly cropping the image. Use to start with a known size.'), + '#markup' => t('Set the canvas to a precise size, possibly cropping the image. Use to start with a known size.'), '#prefix' => '

', '#suffix' => '

', ), @@ -218,8 +228,9 @@ function canvasactions_definecanvas_form($data) { '#collapsible' => TRUE, '#title' => t('Relative size'), 'help' => array( - '#type' => 'markup', - '#value' => '

' . t('Set the canvas to a relative size, based on the current image dimensions. Use to add simple borders or expand by a fixed amount. Negative values may crop the image.') . '

', + '#markup' => t('Set the canvas to a relative size, based on the current image dimensions. Use to add simple borders or expand by a fixed amount. Negative values may crop the image.'), + '#prefix' => '

', + '#suffix' => '

', ), 'leftdiff' => array( '#type' => 'textfield', @@ -258,10 +269,19 @@ function canvasactions_definecanvas_form($data) { return $form; } +/** @noinspection PhpDocMissingThrowsInspection */ /** - * Implementation of theme_hook() for imagecache_ui.module + * Implements theme_hook() for the define canvas effect summary. + * + * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. + * + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable */ -function theme_canvasactions_definecanvas_summary($variables) { +function theme_canvasactions_definecanvas_summary(array $variables) { $data = $variables['data']; if ($data['exact']['width'] || $data['exact']['height']) { $w = !empty($data['exact']['width']) ? $data['exact']['width'] : '100%'; @@ -276,28 +296,22 @@ function theme_canvasactions_definecanvas_summary($variables) { $output .= ' top:' . $data['relative']['topdiff']; $output .= ' bottom:' . $data['relative']['bottomdiff']; } + /** @noinspection PhpUnhandledExceptionInspection */ $output .= theme('imagecacheactions_rgb', array('RGB' => $data['RGB'])); $output .= ($data['under']) ? t(" under image ") : t(" over image "); return $output; } /** - * Implementation of hook_image() + * Image effect callback for the define canvas effect. * - * Creates a solid background canvas + * @param stdClass $image + * @param array $data * - * Process the imagecache action on the passed image - * - * @param $image - * array defining an image file, including : - * - * $image- >source as the filename, - * - * $image->info array - * - * $image->resource handle on the image object + * @return boolean + * true on success, false otherwise. */ -function canvasactions_definecanvas_effect($image, $data) { +function canvasactions_definecanvas_effect(stdClass $image, array $data) { // May be given either exact or relative dimensions. if ($data['exact']['width'] || $data['exact']['height']) { // Allows only one dimension to be used if the other is unset. @@ -308,37 +322,126 @@ function canvasactions_definecanvas_effect($image, $data) { $data['exact']['height'] = $image->info['height']; } - $targetsize['width'] = imagecache_actions_percent_filter($data['exact']['width'], $image->info['width']); - $targetsize['height'] = imagecache_actions_percent_filter($data['exact']['height'], $image->info['height']); + $target_size['width'] = imagecache_actions_percent_filter($data['exact']['width'], $image->info['width']); + $target_size['height'] = imagecache_actions_percent_filter($data['exact']['height'], $image->info['height']); - $targetsize['left'] = image_filter_keyword($data['exact']['xpos'], $targetsize['width'], $image->info['width']); - $targetsize['top'] = image_filter_keyword($data['exact']['ypos'], $targetsize['height'], $image->info['height']); + $target_size['left'] = image_filter_keyword($data['exact']['xpos'], $target_size['width'], $image->info['width']); + $target_size['top'] = image_filter_keyword($data['exact']['ypos'], $target_size['height'], $image->info['height']); } else { - // calculate relative size - $targetsize['width'] = $image->info['width'] + $data['relative']['leftdiff'] + $data['relative']['rightdiff']; - $targetsize['height'] = $image->info['height'] + $data['relative']['topdiff'] + $data['relative']['bottomdiff']; - $targetsize['left'] = $data['relative']['leftdiff']; - $targetsize['top'] = $data['relative']['topdiff']; + // Calculate relative size. + $target_size['width'] = $image->info['width'] + ((int) $data['relative']['leftdiff']) + ((int) $data['relative']['rightdiff']); + $target_size['height'] = $image->info['height'] + ((int) $data['relative']['topdiff']) + ((int) $data['relative']['bottomdiff']); + $target_size['left'] = (int) $data['relative']['leftdiff']; + $target_size['top'] = (int) $data['relative']['topdiff']; } - // convert from hex (as it is stored in the UI) + // Convert from hex (as it is stored in the UI). if ($data['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($data['RGB']['HEX'])) { $data['RGB'] = array_merge($data['RGB'], $deduced); } - // All the maths is done, now defer to the api toolkits; - $data['targetsize'] = $targetsize; + // All the math is done, now defer to the toolkit in use. + $data['targetsize'] = $target_size; $success = image_toolkit_invoke('definecanvas', $image, array($data)); if ($success) { - $image->info['width'] = $targetsize['width']; - $image->info['height'] = $targetsize['height']; + $image->info['width'] = $target_size['width']; + $image->info['height'] = $target_size['height']; } return $success; } +/** + * GD toolkit specific implementation of the define canvas effect. + * + * @param stdClass $image + * @param array $data + * The parameters for this effect. $data['targetsize'] is an array expected to + * contain a width, height and a left, top. + * + * @return bool + * true on success, false otherwise. + */ +function image_gd_definecanvas(stdClass $image, array $data) { + $target_size = $data['targetsize']; + $RGB = $data['RGB']; + + $newcanvas = imagecreatetruecolor($target_size['width'], $target_size['height']); + imagesavealpha($newcanvas, TRUE); + imagealphablending($newcanvas, FALSE); + imagesavealpha($image->resource, TRUE); + if ($RGB['HEX']) { + // Set color, allow it to define transparency, or assume opaque. + $background = imagecolorallocatealpha($newcanvas, $RGB['red'], $RGB['green'], $RGB['blue'], $RGB['alpha']); + } + else { + // No color, attempt transparency, assume white. + $background = imagecolorallocatealpha($newcanvas, 255, 255, 255, 127); + } + imagefilledrectangle($newcanvas, 0, 0, $target_size['width'], $target_size['height'], $background); + + if ($data['under']) { + $canvas_object = new stdClass(); + $canvas_object->resource = $newcanvas; + $canvas_object->info = array( + 'width' => $target_size['width'], + 'height' => $target_size['height'], + 'mime_type' => $image->info['mime_type'], + 'extension' => $image->info['extension'], + ); + $canvas_object->toolkit = $image->toolkit; + image_overlay($image, $canvas_object, $target_size['left'], $target_size['top'], 100, TRUE); + } + else { + $image->resource = $newcanvas; + } + return TRUE; +} + +/** + * Imagemagick toolkit specific implementation of the define canvas effect. + * + * @param stdClass $image + * @param array $data + * The parameters for this effect. $data['targetsize'] is an array expected to + * contain a width, height and a left, top. + * + * @return bool + * true on success, false otherwise. + * + * @see http://www.imagemagick.org/script/command-line-options.php#extent + */ +function image_imagemagick_definecanvas(stdClass $image, $data) { + // Reset any gravity settings from earlier effects. + $image->ops[] = '-gravity None'; + + $backgroundcolor = $data['RGB']['HEX'] != '' ? '#' . ltrim($data['RGB']['HEX'], '#') : 'None'; + $image->ops[] = '-background ' . escapeshellarg($backgroundcolor); + + $compose_operator = $data['under'] ? 'src-over' : 'dst-over'; + $image->ops[] = "-compose $compose_operator"; + + $target_size = $data['targetsize']; + $geometry = sprintf('%dx%d', $target_size['width'], $target_size['height']); + if ($target_size['left'] || $target_size['top']) { + $geometry .= sprintf('%+d%+d', -$target_size['left'], -$target_size['top']); + } + $image->ops[] = '-extent ' . escapeshellarg($geometry); + + return TRUE; +} + +/** + * Image dimensions callback for the define canvas effect. + * + * @param array $dimensions + * Dimensions to be modified - an associative array containing the items + * 'width' and 'height' (in pixels). + * @param array $data + * An associative array containing the effect data. + */ function canvasactions_definecanvas_dimensions(array &$dimensions, array $data) { // May be given either exact or relative dimensions. if ($data['exact']['width'] || $data['exact']['height']) { @@ -354,96 +457,27 @@ function canvasactions_definecanvas_dimensions(array &$dimensions, array $data) $dimensions['height'] = imagecache_actions_percent_filter($data['exact']['height'], $dimensions['height']); } else { - // calculate relative size - $dimensions['width'] = $dimensions['width'] + $data['relative']['leftdiff'] + $data['relative']['rightdiff']; - $dimensions['height'] = $dimensions['height'] + $data['relative']['topdiff'] + $data['relative']['bottomdiff']; + // Calculate relative sizes (only possible if we have the current size). + if ($dimensions['width'] !== NULL) { + $dimensions['width'] = $dimensions['width'] + (int) $data['relative']['leftdiff'] + (int) $data['relative']['rightdiff']; + } + if ($dimensions['height'] !== NULL) { + $dimensions['height'] = $dimensions['height'] + (int) $data['relative']['topdiff'] + (int) $data['relative']['bottomdiff']; + } } } -/** - * Draw a color (or transparency) behind an image - * - * $targetsize is an array expected to contain a width,height and a left,top - * offset. - */ -function image_gd_definecanvas($image, $data = array()) { - $targetsize = $data['targetsize']; - $RGB = $data['RGB']; - - $newcanvas = imagecreatetruecolor($targetsize['width'], $targetsize['height']); - imagesavealpha($newcanvas, TRUE); - imagealphablending($newcanvas, FALSE); - imagesavealpha($image->resource, TRUE); - if ($RGB['HEX']) { - // Set color, allow it to define transparency, or assume opaque. - $background = imagecolorallocatealpha($newcanvas, $RGB['red'], $RGB['green'], $RGB['blue'], $RGB['alpha']); - } - else { - // No color, attempt transparency, assume white - $background = imagecolorallocatealpha($newcanvas, 255, 255, 255, 127); - } - imagefilledrectangle($newcanvas, 0, 0, $targetsize['width'], $targetsize['height'], $background); - # imagealphablending($newcanvas, TRUE); - - if ($data['under']) { - $canvas_object = (object) array( - 'resource' => $newcanvas, - 'info' => array( - 'width' => $targetsize['width'], - 'height' => $targetsize['height'], - 'mime_type' => $image->info['mime_type'], - 'extension' => $image->info['extension'], - ), - 'toolkit' => $image->toolkit, - ); - image_overlay($image, $canvas_object, $targetsize['left'], $targetsize['top'], 100, TRUE); - } - else { - $image->resource = $newcanvas; - } - return TRUE; -} /** - * Draw a color (or transparency) behind an image - * $targetsize is an array expected to contain a width,height and a left,top - * offset. + * Image effect form callback for the underlay (background) effect. * - * See http://www.imagemagick.org/script/command-line-options.php#extent - * @todo: reset gravity? + * @param array $data + * The current configuration for this image effect. + * + * @return array + * The form definition for this effect. */ -function image_imagemagick_definecanvas($image, $data = array()) { - $backgroundcolor = $data['RGB']['HEX'] != '' ? '#' . $data['RGB']['HEX'] : 'None'; - $image->ops[] = '-background ' . escapeshellarg($backgroundcolor); - - $compose_operator = $data['under'] ? 'src-over' : 'dst-over'; - $image->ops[] = "-compose $compose_operator"; - - $targetsize = $data['targetsize']; - $geometry = sprintf('%dx%d', $targetsize['width'], $targetsize['height']); - if ($targetsize['left'] || $targetsize['top']) { - $geometry .= sprintf('%+d%+d', -$targetsize['left'], -$targetsize['top']); - } - $image->ops[] = "-extent $geometry"; - - return TRUE; -} - -//////////////////////////////////////////////// - -/** - * Place a given image under the current canvas - * - * Implementation of imagecache_hook_form() - * - * @param $data array of settings for this action - * @return a form definition - */ -function canvasactions_canvas2file_form($data) { -// if (image_get_toolkit() != 'gd') { -// drupal_set_message('Overlays are not currently supported by using imagemagick. This effect requires GD image toolkit only.', 'warning'); -// } - +function canvasactions_canvas2file_form(array $data) { $defaults = array( 'xpos' => '0', 'ypos' => '0', @@ -484,7 +518,15 @@ function canvasactions_canvas2file_form($data) { } /** - * Implementation of theme_hook() for imagecache_ui.module + * Implements theme_hook() for the underlay (background) effect summary. + * + * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. + * + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable */ function theme_canvasactions_canvas2file_summary($variables) { $data = $variables['data']; @@ -493,17 +535,15 @@ function theme_canvasactions_canvas2file_summary($variables) { } /** - * Place the source image on the current background + * Image effect callback for the underlay (background) effect. * - * Implementation of hook_image() + * @param stdClass $image + * @param array $data * - * Note - this is currently incompatable with imagemagick, due to the way it - * addresses $image->resource directly - a gd only thing. - * - * @param $image - * @param $data + * @return boolean + * true on success, false otherwise. */ -function canvasactions_canvas2file_image(&$image, $data = array()) { +function canvasactions_canvas2file_effect(stdClass $image, array $data) { $underlay = imagecache_actions_image_load($data['path'], $image->toolkit); if ($underlay) { // To handle odd sizes, we will resize/crop the background image to the @@ -511,7 +551,7 @@ function canvasactions_canvas2file_image(&$image, $data = array()) { // imagecopymerge, and the watermark library both do not allow overlays to // be bigger than the target. - // Adjust size + // Adjust size. $crop_rules = array( 'xoffset' => 0, 'yoffset' => 0, @@ -527,7 +567,7 @@ function canvasactions_canvas2file_image(&$image, $data = array()) { // which will produce a 'cropped' image larger than the original. // In this case, we need to calculate the position of the bg image // in relation to the space it will occupy under the top layer - #$crop_rules['xoffset'] = $underlay->info['width'] - $image->info['width'] ; + //$crop_rules['xoffset'] = $underlay->info['width'] - $image->info['width'] ; $crop_rules['width'] = $image->info['width']; $crop_rules['height'] = $image->info['height']; @@ -549,14 +589,14 @@ function canvasactions_canvas2file_image(&$image, $data = array()) { // Crop both before processing to avoid unwanted processing. image_crop_effect($underlay, $crop_rules); - # BUG - this doesn't position either + // @todo: BUG - this doesn't position either // Actually this fails because imagecache_crop fills it with solid color when 'cropping' to a larger size. - #imagecache_crop_image($image, $crop_rules); - #dpm(get_defined_vars()); - // This func modifies the underlay image by ref, placing the current canvas on it + //imagecache_crop_image($image, $crop_rules); + //dpm(get_defined_vars()); + // This func modifies the underlay image by ref, placing the current canvas on it. if (image_overlay($image, $underlay, $data['xpos'], $data['ypos'], $data['alpha'], TRUE)) { - #$image->resource = $underlay->resource; - $image = $underlay; + //$image->resource = $underlay->resource; + //$image = $underlay; //@todo: this is a no-op. return TRUE; } } @@ -564,12 +604,12 @@ function canvasactions_canvas2file_image(&$image, $data = array()) { } /** - * Image dimensions callback; canvas2file (underlay/background). + * Image dimensions callback for the underlay (background) effect. * * @param array $dimensions * Dimensions to be modified - an associative array containing the items * 'width' and 'height' (in pixels). - * @param $data + * @param array $data * An associative array containing the effect data. */ function canvasactions_canvas2file_dimensions(array &$dimensions, array $data) { @@ -586,40 +626,50 @@ function canvasactions_canvas2file_dimensions(array &$dimensions, array $data) { $dimensions['height'] = $underlay->info['height']; break; case 'minimum': - $dimensions['width'] = isset($dimensions['width']) ? min($underlay->info['width'], $dimensions['width']) : NULL; - $dimensions['height'] = isset($dimensions['height']) ? min($underlay->info['height'], $dimensions['height']) : NULL; + if ($dimensions['width'] !== NULL) { + $dimensions['width'] = min($underlay->info['width'], $dimensions['width']); + } + if ($dimensions['height'] !== NULL) { + $dimensions['height'] = min($underlay->info['height'], $dimensions['height']); + } break; case 'maximum': - $dimensions['width'] = isset($dimensions['width']) ? max($underlay->info['width'], $dimensions['width']) : NULL; - $dimensions['height'] = isset($dimensions['height']) ? max($underlay->info['height'], $dimensions['height']) : NULL; - break; + if ($dimensions['width'] !== NULL) { + $dimensions['width'] = max($underlay->info['width'], $dimensions['width']); + } + if ($dimensions['height'] !== NULL) { + $dimensions['height'] = max($underlay->info['height'], $dimensions['height']); + } + break; } } } } + /** - * Place a given image on top of the current canvas + * Image effect form callback for the overlay (watermark) effect. * - * Implementation of imagecache_hook_form() + * @param array $data + * The current configuration for this image effect. * - * @param $data array of settings for this action - * @return a form definition + * @return array + * The form definition for this effect. */ -function canvasactions_file2canvas_form($data) { +function canvasactions_file2canvas_form(array $data) { $defaults = array( 'xpos' => '', 'ypos' => '', 'alpha' => '100', + 'scale' => '', 'path' => '', ); $data = array_merge($defaults, (array) $data); $form = array( 'help' => array( - '#type' => 'markup', - '#value' => t('Note that using a transparent overlay that is larger than the source image may result in unwanted results - a solid background.'), + '#markup' => t('Note that using a non transparent overlay that is larger than the source image may result in unwanted results - a solid background.'), ), ); $form += imagecache_actions_pos_form($data); @@ -627,8 +677,19 @@ function canvasactions_file2canvas_form($data) { '#type' => 'textfield', '#title' => t('opacity'), '#default_value' => $data['alpha'], + '#field_suffix' => t('%'), '#size' => 6, '#description' => t('Opacity: 0-100. Warning: Due to a limitation in the GD toolkit, using an opacity other than 100% requires the system to use an algorithm that\'s much slower than the built-in functions. If you want partial transparency, you are better to use an already-transparent png as the overlay source image.'), + '#element_validate' => array('imagecache_actions_validate_number_non_negative'), + ); + $form['scale'] = array( + '#type' => 'textfield', + '#title' => t('scale'), + '#default_value' => $data['scale'], + '#field_suffix' => t('%'), + '#size' => 6, + '#description' => t('Scales the overlay with respect to the source, thus not its own dimensions. Leave empty to use the original size of overlay image.'), + '#element_validate' => array('imagecache_actions_validate_number_positive'), ); $form['path'] = array( '#type' => 'textfield', @@ -641,25 +702,41 @@ function canvasactions_file2canvas_form($data) { } /** - * Implementation of theme_hook() for imagecache_ui.module + * Implements theme_hook() for the overlay (watermark) effect summary. + * + * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. + * + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable */ -function theme_canvasactions_file2canvas_summary($variables) { +function theme_canvasactions_file2canvas_summary(array $variables) { $data = $variables['data']; - return '' . $data['path'] . ' x:' . $data['xpos'] . ', y:' . $data['ypos'] . ' alpha:' . (@$data['alpha'] ? $data['alpha'] : 100) . '%'; + return '' . $data['path'] . ', x:' . $data['xpos'] . ', y:' . $data['ypos'] . ', alpha:' . (!empty($data['alpha']) ? $data['alpha'] : 100) . '%' . ', scale:' . (!empty($data['scale']) ? $data['scale'].'%' : '-'); } /** - * Place the source image on the current background + * Image effect callback for the overlay (watermark) image effect. * - * Implementation of hook_image() + * @param stdClass $image + * @param array $data * - * - * @param $image - * @param $data + * @return boolean + * true on success, false otherwise. */ -function canvasactions_file2canvas_image($image, $data = array()) { +function canvasactions_file2canvas_effect(stdClass $image, array $data) { $overlay = imagecache_actions_image_load($data['path']); if ($overlay) { + if (!empty($data['scale']) && $data['scale'] > 0) { + // Scale the overlay with respect to the dimensions of the source being + // overlaid. To maintain the aspect ratio, only the width of the overlay + // is scaled like that, the height of the overlay follows the aspect + // ratio (that is why we use image_scale instead of image_resize). + $overlay_w = $image->info['width'] * $data['scale'] / 100; + image_scale($overlay, $overlay_w, NULL, TRUE); + } if (!isset($data['alpha'])) { $data['alpha'] = 100; } @@ -668,17 +745,15 @@ function canvasactions_file2canvas_image($image, $data = array()) { return FALSE; } -/////////////////////////////////////////////////////////////////// /** - * Place the source image on top of the current canvas + * Image effect form callback for the overlay: source image to canvas effect. * - * Implementation of imagecache_hook_form() + * @param array $data + * The current configuration for this image effect. * - * - * - * @param $data array of settings for this action - * @return a form definition + * @return array + * The form definition for this effect. */ function canvasactions_source2canvas_form($data) { $defaults = array( @@ -700,53 +775,64 @@ function canvasactions_source2canvas_form($data) { return $form; } - /** - * Implementation of theme_hook() for imagecache_ui.module + * Implements theme_hook() for the overlay: source img to canvas effect summary. + * + * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. + * + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable */ -function theme_canvasactions_source2canvas_summary($variables) { +function theme_canvasactions_source2canvas_summary(array $variables) { $data = $variables['data']; return 'xpos:' . $data['xpos'] . ', ypos:' . $data['ypos'] . ' alpha:' . $data['alpha'] . '%'; } /** - * Place the source image on the current background + * Image effect callback for the overlay: source image to canvas effect. * - * Implementation of hook_image() + * @param stdClass $image + * @param array $data * - * - * @param $image - * @param $data + * @return boolean + * true on success, false otherwise. */ -function canvasactions_source2canvas_image($image, $data = array()) { +function canvasactions_source2canvas_effect(stdClass $image, array $data) { $overlay = image_load($image->source, $image->toolkit); return image_overlay($image, $overlay, $data['xpos'], $data['ypos'], $data['alpha']); } + /** - * Implements the image effect form callback for the aspect switcher effect. + * Image effect form callback for the aspect switcher effect. * * @param array $data - * Array of settings for this action + * The current configuration for this image effect. + * * @return array - * The form definition + * The form definition for this effect. */ -function canvasactions_aspect_form($data) { +function canvasactions_aspect_form(array $data) { $defaults = array( 'ratio_adjustment' => 1, - 'portrait' => NULL, - 'landscape' => NULL, + 'portrait' => '', + 'landscape' => '', ); $data = array_merge($defaults, (array) $data); $form = array( 'help' => array( - '#type' => 'markup', - '#value' => t('You must create the two presets to use before enabling this process.'), + '#markup' => t('You must create the two presets to use before enabling this process.'), ) ); - $styles = image_style_options(TRUE); + // The PASS_THROUGH parameter is new as of D7.23, and is added here to prevent + // image_style_options() from double-encoding the human-readable image style + // name, since the form API will already sanitize options in a select list. + $styles = image_style_options(TRUE, PASS_THROUGH); // @todo: remove the current style to prevent (immediate) recursion? $form['portrait'] = array( @@ -782,30 +868,39 @@ If n < 1 then blunt portraits will be treated as landscape. /** - * Implements the summary theme callback for the aspect switcher effect. + * Implements theme_hook() for the aspect switcher effect summary. * * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. * * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable */ -function theme_canvasactions_aspect_summary($variables) { +function theme_canvasactions_aspect_summary(array $variables) { $data = $variables['data']; - $ratio_adjustment = ''; + + $label = imagecache_actions_get_style_label($data['portrait']); + $output = t('Portrait size: %label', array('%label' => $label)); + $label = imagecache_actions_get_style_label($data['landscape']); + $output .= ', ' . t('Landscape size: %label', array('%label' => $label)); if ($data['ratio_adjustment'] != 1) { - $ratio_adjustment = " (switch at 1:{$data['ratio_adjustment']})"; + $output .= ', ' . t("(switch at 1:@ratio_adjustment)", array('@ratio_adjustment' => $data['ratio_adjustment'])); } - return 'Portrait size: ' . $data['portrait'] . '. Landscape size: ' . $data['landscape'] . '' . $ratio_adjustment; + return trim($output); } /** - * Implements the image effect callback for the aspect switcher effect. + * Image effect callback for the aspect switcher effect. * - * @param object $image + * @param stdClass $image * @param array $data * - * @return bool + * @return boolean + * true on success, false otherwise. */ -function canvasactions_aspect_image($image, $data = array()) { +function canvasactions_aspect_effect(stdClass $image, array $data) { $ratio_adjustment = isset($data['ratio_adjustment']) ? floatval( $data['ratio_adjustment']) : 1; $aspect = $image->info['width'] / $image->info['height']; @@ -821,7 +916,7 @@ function canvasactions_aspect_image($image, $data = array()) { if (empty($style)) { // Required preset has gone missing? - watchdog('imagecache_canvasactions', "When running 'aspect' action, I was unable to load sub-action %style_name. Either it's been deleted or the DB needs an update", array('%style_name' => $style_name), WATCHDOG_ERROR); + watchdog('imagecache_actions', "When running 'aspect' action, I was unable to load sub-action %style_name. Either it's been deleted or the DB needs an update", array('%style_name' => $style_name), WATCHDOG_ERROR); return FALSE; } @@ -833,7 +928,7 @@ function canvasactions_aspect_image($image, $data = array()) { } /** - * Implements the dimension callback for the aspect switcher effect. + * Image dimensions callback for the aspect switcher effect. * * @param array $dimensions * Dimensions to be modified - an associative array containing the items @@ -843,10 +938,756 @@ function canvasactions_aspect_image($image, $data = array()) { */ function canvasactions_aspect_dimensions(array &$dimensions, array $data) { if (empty($dimensions['width']) || empty($dimensions['height'])) { - return; + // We cannot know which preset would be executed and thus cannot know the + // resulting dimensions, unless both styles return the same dimensions: + $landscape_dimensions = $portrait_dimensions = $dimensions; + image_style_transform_dimensions($data['landscape'], $landscape_dimensions); + image_style_transform_dimensions($data['portrait'], $portrait_dimensions); + if ($landscape_dimensions == $portrait_dimensions) { + $dimensions = $landscape_dimensions; + } + else { + $dimensions['width'] = $dimensions['height'] = NULL; + } + } + else { + $ratio_adjustment = isset($data['ratio_adjustment']) ? floatval( $data['ratio_adjustment']) : 1; + $aspect = $dimensions['width'] / $dimensions['height']; + $style_name = (($aspect * $ratio_adjustment) > 1) ? $data['landscape'] : $data['portrait']; + image_style_transform_dimensions($style_name, $dimensions); } - $ratio_adjustment = isset($data['ratio_adjustment']) ? floatval( $data['ratio_adjustment']) : 1; - $aspect = $dimensions['width'] / $dimensions['height']; - $style_name = (($aspect * $ratio_adjustment) > 1) ? $data['landscape'] : $data['portrait']; - image_style_transform_dimensions($style_name, $dimensions); +} + +/** + * Image effect form callback for the resize percent effect. + * + * @param array $data + * The current configuration for this image effect. + * + * @return array + * The form definition for this effect. + */ +function canvasactions_resizepercent_form(array $data) { + $defaults = array( + 'width' => '', + 'height' => '', + ); + $data = array_merge($defaults, (array) $data); + + $form['#element_validate'] = array('image_effect_scale_validate'); + + $form['width'] = array( + '#type' => 'textfield', + '#title' => t('Width'), + '#default_value' => !empty($data['width']) ? (float) $data['width'] : '', + '#field_suffix' => ' ' . t('percent'), + '#required' => FALSE, + '#size' => 10, + '#element_validate' => array('canvasactions_resizepercent_validate'), + '#allow_negative' => FALSE, + ); + $form['height'] = array( + '#type' => 'textfield', + '#title' => t('Height'), + '#default_value' => !empty($data['height']) ? (float) $data['height'] : '', + '#field_suffix' => ' ' . t('percent'), + '#required' => FALSE, + '#size' => 10, + '#element_validate' => array('canvasactions_resizepercent_validate'), + '#allow_negative' => FALSE, + ); + + return $form; +} + +/** + * Element validate handler to ensure that a positive number is specified. + * + * @param array $element + * The form element to validate. + * @param array $form_state + * The form state. + */ +function canvasactions_resizepercent_validate($element, &$form_state) { + element_validate_number($element, $form_state); + if (!form_get_error($element)) { + if ($element['#value'] != '' && (float) $element['#value'] <= 0) { + form_error($element, t('!name must be a positive number.', array('!name' => $element['#title']))); + } + } +} +/** + * Calculate percent based on input, fallback if only one value is provided. + * + * @param array $data + * + * @return float[]|FALSE + */ +function _canvasactions_resizepercent_calculate_percent(array $data) { + // Fallback if only one value is provided. + if (empty($data['height'])) { + if (empty($data['width'])) { + return FALSE; + } + $data['height'] = $data['width']; + } + else if (empty($data['width'])) { + $data['width'] = $data['height']; + } + + // Get percentage values in decimal values. + $data['width'] = (float) $data['width'] / 100; + $data['height'] = (float) $data['height'] / 100; + + return $data; +} + +/** + * Implements theme_hook() for the resize percent effect summary. + * + * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. + * + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable + */ +function theme_canvasactions_resizepercent_summary(array $variables) { + $data = _canvasactions_resizepercent_calculate_percent($variables['data']); + if (!$data) { + return t('Invalid effect data'); + } + if ($data['width'] != $data['height']) { + return t('@width%x@height%', array('@width' => 100 * $data['width'], '@height' => 100 * $data['height'])); + } + else { + return t('scale to @percent%', array('@percent' => (float) 100 * $data['height'])); + } +} + +/** + * Image effect callback for the resize percent effect. + * + * @param stdClass $image + * @param array $data + * + * @return boolean + * true on success, false otherwise. + */ +function canvasactions_resizepercent_effect(stdClass $image, array $data) { + $data = _canvasactions_resizepercent_calculate_percent($data); + + $data['width'] = (int) round($image->info['width'] * $data['width']); + $data['height'] = (int) round($image->info['height'] * $data['height']); + + return image_resize_effect($image, $data); +} + +/** + * Image dimensions callback for the resize percent effect. + * + * @param array $dimensions + * Dimensions to be modified - an associative array containing the items + * 'width' and 'height' (in pixels). + * @param array $data + * An associative array containing the effect data. + */ +function canvasactions_resizepercent_dimensions(array &$dimensions, array $data) { + $data = _canvasactions_resizepercent_calculate_percent($data); + + $dimensions['width'] = (int) round($dimensions['width'] * $data['width']); + $dimensions['height'] = (int) round($dimensions['height'] * $data['height']); +} + +/** + * Image effect form callback for the blur effect. + * + * @param array $data + * The current configuration for this image effect. + * + * @return array + * The form definition for this effect. + */ +function canvasactions_blur_form(array $data) { + $form['intensity'] = array( + '#type' => 'textfield', + '#title' => t('Blur intensity'), + '#description' => t('A higher intensity results in more blur. The larger the image, the larger value you need to get a really blurred image, think 50 to 100 for 600x400 images.'), + '#size' => 5, + '#default_value' => isset($data['intensity']) ? (int) $data['intensity'] : 2, + '#element_validate' => array('element_validate_integer_positive'), + ); + return $form; +} + +/** + * Implements theme_hook() for the underlay (background) effect summary. + * + * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. + * + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable + */ +function theme_canvasactions_blur_summary($variables) { + return t('Intensity: @intensity', array('@intensity' => $variables['data']['intensity'])); +} + +/** + * Image effect callback for the resize percent effect. + * + * @param stdClass $image + * @param array $data + * + * @return boolean + * true on success, false otherwise. + */ +function canvasactions_blur_effect(stdClass $image, array $data) { + return image_toolkit_invoke('blur', $image, $data); +} + +/** + * GD toolkit specific implementation of the blur effect. + * + * @param stdClass $image + * @param int $intensity + * The number of times to apply the blur filter. + * + * @return boolean + * True on success, false otherwise. + */ +function image_gd_blur(stdClass $image, $intensity) { + $intensity = (int) $intensity; + $result = TRUE; + $i = 0; + while ($result && $i++ < $intensity) { + $result = imagefilter($image->resource, IMG_FILTER_GAUSSIAN_BLUR); + } + return $result; +} + +/** + * Imagemagick toolkit specific implementation of the blur effect. + * + * See http://www.imagemagick.org/Usage/blur/. + * + * @param stdClass $image + * @param int $intensity + * The "intensity" of the blur effect. + * + * @return boolean + * True on success, false otherwise. + */ +function image_imagemagick_blur(stdClass $image, $intensity) { + // To get similar results asd with the GD factor, we use a formula to alter + // the intensity into the sigma value that is passed to IM. + $sigma = 4.0 + 0.8 * $intensity; + $image->ops[] = '-blur ' . escapeshellarg(sprintf('0x%f', $sigma)); + return TRUE; +} + +/** + * Builds the interlace form. + * + * This effect has no options, only some help text, so the form is displayed + * anyway. + */ +function canvasactions_interlace_form() { + $form = array(); + $form['help'] = array( + '#markup' => '

There are no user-configurable options for this process.

+

This effect will save the derivative image in an interlace / progressive way + which might improve perceived performance, especially for large images. + File size and loading speed will not change, but the user will pretty quickly + see a "degraded" copy of the entire image instead of a clear copy of the upper + part of the image.

+

Wikipedia: Interlacing (bitmaps)

', + ); + return $form; +} + +/** + * Image effect callback for the Interlace / Progressive effect. + * + * @param stdClass $image + * @param array $data + * + * @return bool + * true on success, false otherwise. + */ +function canvasactions_interlace_effect(stdClass $image, array $data) { + return image_toolkit_invoke('interlace', $image, array($data)); +} + +/** + * GD toolkit specific implementation of the image interlace effect. + * + * @param stdClass $image + * Image object containing the GD image resource to operate on. + * param array $data + * The current configuration for this image effect. + * + * @return bool + * true on success, false otherwise. + */ +function image_gd_interlace($image/*, array $data*/) { + imageinterlace($image->resource, 1); + return TRUE; +} + +/** + * Imagemagick toolkit specific implementation of the image interlace effect. + * + * @param stdClass $image + * Image object containing the image resource to operate on. + * param array $data + * The current configuration for this image effect. + * + * @return bool + * true on success, false otherwise. + */ +function image_imagemagick_interlace($image/*, array $data*/) { + $image->ops[] = '-interlace Plane'; + return TRUE; +} + +/** + * Image effect form callback for the Perspective effect. + * + * @param array $data + * The current configuration for this image effect. + * + * @return array + * The form definition for this effect. + */ +function canvasactions_perspective_form(array $data) { + $defaults = array( + 'vanish' => 'right', + 'symmetry' => 'symmetrical', + 'distortion' => 14, + 'opposite_distortion' => 10, + ); + $data = array_merge($defaults, $data); + + $form['vanish'] = array( + '#type' => 'radios', + '#title' => t('Vanishing point'), + '#options' => array( + 'top' => t('Top'), + 'left' => t('Left'), + 'right' => t('Right'), + 'bottom' => t('Bottom'), + ), + '#theme' => 'canvasactions_perspective_anchor', + '#default_value' => $data['vanish'], + '#description' => t('The position of the vanishing point relative to the source image.'), + ); + + $form['symmetry'] = array( + '#type' => 'radios', + '#title' => t('Symmetry of image perspective'), + '#description' => t('If symmetrical, the perspective effect will be built symmetrically. If asymmetrical, you can set different distortion values for both sides. Mathematically speaking: symmetrical distortion results in an isosceles trapezoid, whereas asymmetrical distortion results in just an acute trapezoid.'), + '#default_value' => $data['symmetry'], + '#options' => array( + 'symmetrical' => t('Symmetrical perspective'), + 'asymmetrical' => t('Asymmetrical perspective'), + ), + ); + + $form['distortion'] = array( + '#type' => 'textfield', + '#title' => t('Distortion'), + '#field_suffix' => '%', + '#size' => 5, + '#default_value' => $data['distortion'], + '#element_validate' => array('canvasactions_perspective_distortion_validate'), + '#description' => t('How much the corner(s) (on the vanishing point side of the image) should move to the horizon (i.e. the line containing the vanishing point). With 0% you will have no perspective effect at all and the vanishing point will be infinitely far away. With a sum of 100%, the 2 corner(s) and the vanishing point will be the same, resulting in a triangle instead of a trapezoid. For a pleasing effect, you should choose (a) number(s) between 0 and 35%, especially with ImageMagick as that toolkit also adds some stretching within the image.'), + ); + + $form['opposite_distortion'] = array( + '#type' => 'textfield', + '#title' => t('Distortion for opposite side'), + '#states' => array( + 'visible' => array( + ':input[name="data[symmetry]"]' => array('value' => 'asymmetrical'), + ), + ), + '#field_suffix' => '%', + '#size' => 5, + '#default_value' => $data['opposite_distortion'], + '#element_validate' => array('canvasactions_perspective_distortion_validate'), + '#description' => t('How much the 2nd corner on the vanishing point side of the image should move to the horizon line containing the vanishing point.'), + ); + + $form['additional_help'] = array( + '#markup' => '

Some notes:

+ ', + ); + + return $form; +} + +/** + * Form element validation handler for distortion. + * + * @param array $element + * The form element to validate. + * @param array $form_state + * The form state. + */ +function canvasactions_perspective_distortion_validate($element, &$form_state) { + $symmetrical = $form_state['values']['data']['symmetry'] === 'symmetrical'; + $element_name = $element['#name']; + // Do not check opposite distortion if it is hidden in the UI. + if (!$symmetrical || $element_name === 'data[distortion]') { + $value = $element['#value']; + // Check value itself: a number between 0 and 100 (50 if symmetrical): + $max_value = $symmetrical ? 50 : 100; + if (!is_numeric($value) || $value < 0 || $value >= $max_value) { + if ($symmetrical) { + form_error($element, t('!name must be a value between 0 and 50.', array('!name' => $element['#title']))); + } + else { + form_error($element, t('!name must be a value between 0 and 100.',array('!name' => $element['#title']))); + } + } + // Sum of both distortion values should also be smaller then 100. + if (!$symmetrical) { + $other_value_name = $element_name === 'data[distortion]' ? 'opposite_distortion' : 'distortion'; + $other_value = $form_state['values']['data'][$other_value_name]; + if (is_numeric($other_value) && $value + $other_value >= 100) { + form_error($element, t('The sum of %name and %name2 must be lower then 100.', + array( + '%name' => $element['#title'], + '%name2' => $other_value_name === 'distortion' ? t('Distortion') : t('Distortion for opposite side'), + )) + ); + } + } + } +} + +/** + * Implements theme_hook() for the define perspective effect summary. + * + * @param array $variables + * An associative array containing: + * - data: The current configuration for this image effect. + * + * @return string + * The HTML for the summary of this image effect. + * @ingroup themeable + */ +function theme_canvasactions_perspective_summary(array $variables) { + $data = $variables['data']; + $output = array(); + + $output[] = t('%symmetry. Vanishing point: %vanish.', + array( + '%symmetry' => $data['symmetry'], + '%vanish' => $data['vanish'], + )); + + if ($data['symmetry'] == 'asymmetrical') { + switch ($data['vanish']) { + case 'top': + case 'bottom': + $output[] = t('Left distortion: %distortion, right distortion: %opposite_distortion.', + array( + '%distortion' => $data['distortion'] . '%', + '%opposite_distortion' => $data['opposite_distortion'] . '%', + )); + break; + + case 'right': + case 'left': + $output[] = t('Top distortion: %distortion, bottom distortion: %opposite_distortion.', + array( + '%distortion' => $data['distortion'] . '%', + '%opposite_distortion' => $data['opposite_distortion'] . '%', + )); + break; + } + } + else { + $output[] = t('Distortion: %distortion.', array('%distortion' => $data['distortion'] . '%')); + } + + return implode(' ', $output); +} + +/** + * Image effect callback for the Perspective effect. + * + * @param stdClass $image + * Image object containing the image to operate on. + * @param array $data + * The current configuration for this image effect, contains the keys: + * distortion, vanish, symmetry and opposite_distortion options. + * + * @return bool + * True on success, false otherwise. + */ +function canvasactions_perspective_effect(stdClass $image, array $data) { + if (!image_toolkit_invoke('perspective', $image, array($data))) { + watchdog('imagecache_canvasactions', 'Image perspective transform failed using the %toolkit toolkit on %path (%mimetype, %dimensions)', array( + '%toolkit' => $image->toolkit, + '%path' => $image->source, + '%mimetype' => $image->info['mime_type'], + '%dimensions' => $image->info['height'] . 'x' . $image->info['height'], + ), WATCHDOG_ERROR); + return FALSE; + } + + return TRUE; +} + +/** + * GD toolkit specific implementation of the image Perspective effect. + * + * @param stdClass $image + * Image object containing the image resource to operate on. + * @param array $data + * The current configuration for this image effect, contains the keys: + * distortion, vanish, symmetry and opposite_distortion options. + * + * @return bool + * True on success, false otherwise. + */ +function image_gd_perspective(stdClass $image, $data) { + $width = $image->info['width']; + $height = $image->info['height']; + $distortion = $data['distortion']; + $opposite_distortion = $data['symmetry'] === 'symmetrical' ? $distortion : $data['opposite_distortion']; + + // To reduce distortion, we work with a temporary hires version of the image. + // @todo: during processing we have 2 resources this size: this might crash on memory limits: warn and/or prevent. + $multiplier = 3; + $hires_width = $width * $multiplier; + $hires_height = $height * $multiplier; + $hires_source_image = imagecreatetruecolor($hires_width, $hires_height); + $transparent_white = imagecolorallocatealpha($hires_source_image, 255, 255, 255, 127); + imagealphablending($hires_source_image, FALSE); + imagefilledrectangle($hires_source_image, 0, 0, $hires_width, $hires_height, $transparent_white); + imagesavealpha($hires_source_image, TRUE); + imagecopyresized($hires_source_image, $image->resource, 0, 0, 0, 0, $hires_width, $hires_height, $width, $height); + imagedestroy($image->resource); + + // Creating a hires target canvas to apply the perspective effect on. + $hires_target_image = imagecreatetruecolor($hires_width, $hires_height); + $transparent_white = imagecolorallocatealpha($hires_target_image, 255, 255, 255, 127); + // We don't want to blend: the transparent background we set is only for the + // parts that do not get covered. + imagealphablending($hires_target_image, FALSE); + imagefilledrectangle($hires_target_image, 0, 0, $hires_width, $hires_height, $transparent_white); + + // Building perspective effect with help four point distortion methods. + // On each step found new distortion point by right triangle formula. + switch ($data['vanish']) { + case 'top': + $left = round($hires_width * $distortion / 100); + $right = round($hires_width - ($hires_width * (100 - $opposite_distortion) / 100)); + + $tg_beta_left = $left / $hires_height; + $tg_beta_right = $right / $hires_height; + + for ($y = 0; $y < $hires_height; $y++) { + $new_left = ($hires_height - $y) * $tg_beta_left; + $new_right = ($hires_height - $y) * $tg_beta_right; + $new_width = $hires_width - $new_left - $new_right; + imagecopyresampled($hires_target_image, $hires_source_image, + $new_left, $y, + 0, $y, + $new_width, 1, + $hires_width, 1); + } + break; + + case 'bottom': + $left = round($hires_width * $distortion / 100); + $right = round($hires_width - ($hires_width * (100 - $opposite_distortion) / 100)); + + $tg_beta_left = $left / $hires_height; + $tg_beta_right = $right / $hires_height; + + for ($y = $hires_height; $y > 0; $y--) { + $new_left = $y * $tg_beta_left; + $new_right = $y * $tg_beta_right; + $new_width = $hires_width - $new_left - $new_right; + imagecopyresampled($hires_target_image, $hires_source_image, + $new_left, $y, + 0, $y, + $new_width, 1, + $hires_width, 1); + } + break; + + case 'right': + $top = round($hires_height * $distortion / 100); + $bottom = round($hires_height - ($hires_height * (100 - $opposite_distortion) / 100)); + + $tg_beta_top = $top / $hires_width; + $tg_beta_bottom = $bottom / $hires_width; + + for ($x = $hires_width; $x > 0; $x--) { + $new_top = $x * $tg_beta_top; + $new_bottom = $x * $tg_beta_bottom; + $new_height = $hires_height - $new_top - $new_bottom; + imagecopyresampled($hires_target_image, $hires_source_image, + $x, $new_top, + $x, 0, + 1, $new_height, + 1, $hires_height); + } + break; + + case 'left': + $top = round($hires_height * $distortion / 100); + $bottom = round($hires_height - ($hires_height * (100 - $opposite_distortion) / 100)); + + $tg_beta_top = $top / $hires_width; + $tg_beta_bottom = $bottom / $hires_width; + + for ($x = 0; $x < $hires_width; $x++) { + $new_top = ($hires_width - $x) * $tg_beta_top; + $new_bottom = ($hires_width - $x) * $tg_beta_bottom; + $new_height = $hires_height - $new_top - $new_bottom; + imagecopyresampled($hires_target_image, $hires_source_image, + $x, $new_top, + $x, 0, + 1, $new_height, + 1, $hires_height); + } + break; + } + imagedestroy($hires_source_image); + imagealphablending($hires_target_image, FALSE); + imagesavealpha($hires_target_image, TRUE); + + // Return image with perspective effect to original size. + $target_image = imagecreatetruecolor($width, $height); + imagealphablending($target_image, FALSE); + imagecopyresampled($target_image, $hires_target_image, 0, 0, 0, 0, $width, $height, $hires_width, $hires_height); + imagedestroy($hires_target_image); + imagesavealpha($target_image, TRUE); + + $image->resource = $target_image; + + return TRUE; +} + +/** + * Imagemagick toolkit specific implementation of the image Perspective effect. + * + * @param stdClass $image + * Image object containing the image to operate on. + * @param array $data + * The current configuration for this image effect, contains the keys + * distortion, vanish, symmetry and opposite_distortion options. + * + * @return bool + * True on success, false otherwise. + */ +function image_imagemagick_perspective(stdClass $image, $data) { + $width = $image->info['width']; + $height = $image->info['height']; + $distortion = $data['distortion']; + $opposite_distortion = $data['symmetry'] === 'symmetrical' ? $distortion : $data['opposite_distortion']; + + switch ($data['vanish']) { + case 'top': + $left = round($width * $distortion / 100); + $right = round($width * (100 - $opposite_distortion) / 100); + $perspective_arg = "0,0,$left,0 0,$height,0,$height $width,0,$right,0 $width,$height,$width,$height"; + break; + + case 'right': + default: // Prevents warning about possibly undefined variable. + $top = round($height * $distortion / 100); + $bottom = round($height * (100 - $opposite_distortion) / 100); + $perspective_arg = "0,0,0,0 0,$height,0,$height $width,0,$width,$top $width,$height,$width,$bottom"; + break; + + case 'bottom': + $left = round($width * $distortion / 100); + $right = round($width * (100 - $opposite_distortion) / 100); + $perspective_arg = "0,0,0,0 0,$height,$left,$height $width,0,$width,0 $width,$height,$right,$height"; + break; + + case 'left': + $top = round($height * $distortion / 100); + $bottom = round($height * (100 - $opposite_distortion) / 100); + $perspective_arg = "0,0,0,$top 0,$height,0,$bottom $width,0,$width,0 $width,$height,$width,$height"; + break; + } + + $transparent_white = escapeshellarg('#ffffffff'); + $perspective = escapeshellarg($perspective_arg); + $image->ops[] = "-background $transparent_white -virtual-pixel background -distort Perspective $perspective"; + + return TRUE; +} + +/** @noinspection PhpDocMissingThrowsInspection */ +/** + * Implements theme_hook(). + * + * @param array $variables + * An associative array containing: + * - element: A render element containing radio buttons. + * + * @return string + * The HTML for a 3x3 grid of checkboxes for image anchors. + * @ingroup themeable + */ +function theme_canvasactions_perspective_anchor($variables) { + $element = $variables['element']; + + $rows = $row = $option = array(); + + $blank = array('#markup' => ''); + $blank = drupal_render($blank); + + /** @noinspection PhpUnhandledExceptionInspection */ + $image = array( + '#markup' => theme('image', array( + 'path' => drupal_get_path('module', 'image') . '/sample.png', + 'attributes' => array('width' => "40", 'height' => "40"), + )), + ); + $image = drupal_render($image); + + foreach (element_children($element) as $key) { + $element[$key]['#attributes']['title'] = $element[$key]['#title']; + unset($element[$key]['#title']); + $option[] = drupal_render($element[$key]); + } + + $row[] = $blank; + $row[] = $option[0]; + $row[] = $blank; + $rows[] = $row; + $row = array(); + + $row[] = $option[1]; + $row[] = $image; + $row[] = $option[2]; + $rows[] = $row; + $row = array(); + + $row[] = $blank; + $row[] = $option[3]; + $row[] = $blank; + $rows[] = $row; + + /** @noinspection PhpUnhandledExceptionInspection */ + return theme('table', array('header' => array(), 'rows' => $rows, 'attributes' => array('class' => array('image-anchor')))); } diff --git a/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.info b/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.info index f5b0f06..8fa7110 100644 --- a/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.info +++ b/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.info @@ -1,31 +1,14 @@ name = Imagecache Canvas Actions -description = Actions for manipulating image canvases layers, including watermark and background effect. Also an aspect switcher (portrait/landscape) +description = Provides image effects for manipulating image canvases: define canvas, image mask, watermark, underlay background image, rounded corners, composite source to image and resize by percent effect. Also provides an aspect switcher (portrait/landscape). package = Media core = 7.x dependencies[] = imagecache_actions dependencies[] = image -files[] = canvasactions.inc -files[] = imagecache_canvasactions.install -files[] = imagecache_canvasactions.module -files[] = rounded_corners.inc -files[] = tests/cheap_dropshadow.imagecache_preset.inc -files[] = tests/keyword_positioning.imagecache_preset.inc -files[] = tests/positioned_underlay.imagecache_preset.inc -files[] = tests/rotate_alpha.imagecache_preset.inc -files[] = tests/rotate_alpha_gif.imagecache_preset.inc -files[] = tests/rotate_scale.imagecache_preset.inc -files[] = tests/rotate_scale_alpha.imagecache_preset.inc -files[] = tests/rounded.imagecache_preset.inc -files[] = tests/rounded_bl.imagecache_preset.inc -files[] = tests/rounded_flattened.imagecache_preset.inc -files[] = tests/watermark_100.imagecache_preset.inc -files[] = tests/watermark_50.imagecache_preset.inc - -; Information added by drupal.org packaging script on 2012-12-04 -version = "7.x-1.1" +; Information added by Drupal.org packaging script on 2018-03-20 +version = "7.x-1.9" core = "7.x" project = "imagecache_actions" -datestamp = "1354653754" +datestamp = "1521550387" diff --git a/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.install b/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.install deleted file mode 100644 index d646578..0000000 --- a/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.install +++ /dev/null @@ -1,27 +0,0 @@ - l(t('settings'), 'admin/config/media/image-styles')))); -} - -/** - * @todo Please document this function. - * @see http://drupal.org/node/1354 - */ -function imagecache_canvasactions_uninstall() { - if (function_exists('imagecache_action_definitions') ) { - imagecache_action_definitions(TRUE); - } - cache_clear_all('imagecache_actions', 'cache'); -} diff --git a/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.module b/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.module index ad282a8..fc2f591 100644 --- a/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.module +++ b/sites/all/modules/imagecache_actions/canvasactions/imagecache_canvasactions.module @@ -5,7 +5,7 @@ * including "Watermark" * * Based on first draft of the code by Dimm (imagecache.module 5--1) - * http://drupal.org/node/184816 + * https://drupal.org/node/184816 * * Rewritten and ported to Imagecache actions API (imagecache.module 5--2) by * dman http://coders.co.nz/ @@ -39,21 +39,20 @@ * */ -// During devel, caching is pointless. Flush it -// imagecache_action_definitions(TRUE); - if (! function_exists('imagecache_actions_calculate_relative_position') ) { - module_load_include('inc', 'imagecache_canvasactions', 'utility'); + module_load_include('inc', 'imagecache_actions', 'utility'); } // @todo There doesn't seem to be a way to specify a file in hook_image_effect_info // so placing this here for the time being. module_load_include('inc', 'imagecache_canvasactions', 'canvasactions'); module_load_include('inc', 'imagecache_canvasactions', 'rounded_corners'); -// imageapi extensions -module_load_include('inc', 'imagcache_actions', 'image_overlay.inc'); - +/** + * Implements hook_image_effect_info(). + * + * Defines information about the supported effects. + */ function imagecache_canvasactions_image_effect_info() { $effects = array(); @@ -69,8 +68,8 @@ function imagecache_canvasactions_image_effect_info() { $effects['canvasactions_imagemask'] = array( 'label' => t('Image mask'), 'help' => t(' Choose the file image you wish to use as a mask, and apply it to the canvas.'), - 'effect callback' => 'canvasactions_imagemask_image', 'dimensions passthrough' => TRUE, + 'effect callback' => 'canvasactions_imagemask_effect', 'form callback' => 'canvasactions_imagemask_form', 'summary theme' => 'canvasactions_imagemask_summary', ); @@ -78,8 +77,8 @@ function imagecache_canvasactions_image_effect_info() { $effects['canvasactions_file2canvas'] = array( 'label' => t('Overlay (watermark)'), 'help' => t('Choose the file image you wish to use as an overlay, and position it in a layer on top of the canvas.'), - 'effect callback' => 'canvasactions_file2canvas_image', 'dimensions passthrough' => TRUE, + 'effect callback' => 'canvasactions_file2canvas_effect', 'form callback' => 'canvasactions_file2canvas_form', 'summary theme' => 'canvasactions_file2canvas_summary', ); @@ -87,7 +86,7 @@ function imagecache_canvasactions_image_effect_info() { $effects['canvasactions_canvas2file'] = array( 'label' => t('Underlay (background)'), 'help' => t('Choose the file image you wish to use as an background, and position the processed image on it.'), - 'effect callback' => 'canvasactions_canvas2file_image', + 'effect callback' => 'canvasactions_canvas2file_effect', 'dimensions callback' => 'canvasactions_canvas2file_dimensions', 'form callback' => 'canvasactions_canvas2file_form', 'summary theme' => 'canvasactions_canvas2file_summary', @@ -96,8 +95,8 @@ function imagecache_canvasactions_image_effect_info() { $effects['canvasactions_source2canvas'] = array( 'label' => t('Overlay: source image to canvas'), 'help' => t('Places the source image onto the canvas for compositing.'), - 'effect callback' => 'canvasactions_source2canvas_image', 'dimensions passthrough' => TRUE, + 'effect callback' => 'canvasactions_source2canvas_effect', 'form callback' => 'canvasactions_source2canvas_form', 'summary theme' => 'canvasactions_source2canvas_summary', ); @@ -105,8 +104,8 @@ function imagecache_canvasactions_image_effect_info() { $effects['canvasactions_roundedcorners'] = array( 'label' => t('Rounded Corners'), 'help' => t('This is true cropping, not overlays, so the result can be transparent.'), - 'effect callback' => 'canvasactions_roundedcorners_image', 'dimensions passthrough' => TRUE, + 'effect callback' => 'canvasactions_roundedcorners_effect', 'form callback' => 'canvasactions_roundedcorners_form', 'summary theme' => 'canvasactions_roundedcorners_summary', ); @@ -114,55 +113,105 @@ function imagecache_canvasactions_image_effect_info() { $effects['canvasactions_aspect'] = array( 'label' => t('Aspect switcher'), 'help' => t('Use different effects depending on whether the image is landscape of portrait shaped. This re-uses other preset definitions, and just chooses between them based on the rule.'), - 'effect callback' => 'canvasactions_aspect_image', + 'effect callback' => 'canvasactions_aspect_effect', 'dimensions callback' => 'canvasactions_aspect_dimensions', 'form callback' => 'canvasactions_aspect_form', 'summary theme' => 'canvasactions_aspect_summary', ); + $effects['canvasactions_resizepercent'] = array( + 'label' => t('Resize (percent)'), + 'help' => t('Resize the image based on percent. If only a single dimension is specified, the other dimension will be calculated.'), + 'effect callback' => 'canvasactions_resizepercent_effect', + 'dimensions callback' => 'canvasactions_resizepercent_dimensions', + 'form callback' => 'canvasactions_resizepercent_form', + 'summary theme' => 'canvasactions_resizepercent_summary', + ); + + $effects['canvasactions_blur'] = array( + 'label' => t('Blur'), + 'help' => t('Make the image become unclear.'), + 'effect callback' => 'canvasactions_blur_effect', + 'dimensions passthrough' => TRUE, + 'form callback' => 'canvasactions_blur_form', + 'summary theme' => 'canvasactions_blur_summary', + ); + + $effects['canvasactions_interlace'] = array( + 'label' => t('Interlace / Progressive'), + 'help' => t('Create interlaced PNG/GIF or progressive JPG.'), + 'effect callback' => 'canvasactions_interlace_effect', + 'dimensions passthrough' => TRUE, + 'form callback' => 'canvasactions_interlace_form', + ); + + $effects['canvasactions_perspective'] = array( + 'label' => t('Perspective transform'), + 'help' => t('Adds a perspective transformation to the image.'), + 'effect callback' => 'canvasactions_perspective_effect', + 'dimensions passthrough' => TRUE, + 'form callback' => 'canvasactions_perspective_form', + 'summary theme' => 'canvasactions_perspective_summary', + ); + return $effects; } - /** - * Need to register the theme functions we expect to use + * Implements hook_theme(). + * + * Registers theme functions for the effect summaries. */ function imagecache_canvasactions_theme() { - $util_dir = drupal_get_path('module', 'imagecache_actions'); return array( 'canvasactions_definecanvas_summary' => array( - 'file' => 'canvasactions.inc', 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', ), 'canvasactions_imagemask_summary' => array( - 'file' => 'canvasactions.inc', 'arguments' => array('element' => NULL), + 'file' => 'canvasactions.inc', ), 'canvasactions_file2canvas_summary' => array( - 'file' => 'canvasactions.inc', 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', ), 'canvasactions_source2canvas_summary' => array( - 'file' => 'canvasactions.inc', 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', ), 'canvasactions_canvas2file_summary' => array( - 'file' => 'canvasactions.inc', 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', ), 'canvasactions_roundedcorners_summary' => array( - 'file' => 'rounded_corners.inc', 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', ), 'canvasactions_aspect_summary' => array( - 'file' => 'canvasactions.inc', 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', + ), + 'canvasactions_resizepercent_summary' => array( + 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', + ), + 'canvasactions_blur_summary' => array( + 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', + ), + 'canvasactions_perspective_summary' => array( + 'variables' => array('data' => NULL), + 'file' => 'canvasactions.inc', + ), + 'canvasactions_perspective_anchor' => array( + 'render element' => 'element', ), ); } /** - * Implements hook_image_style_flush. + * Implements hook_image_style_flush(). * * This hook checks if the image style that is being flushed is used in an * aspect switcher effect. If so, the style that contains the aspect switcher @@ -171,7 +220,7 @@ function imagecache_canvasactions_theme() { * @param array $flushed_style * The image style that is being flushed. */ -function imagecache_canvasactions_image_style_flush($flushed_style) { +function imagecache_canvasactions_image_style_flush(/*array*/ $flushed_style) { $styles = image_styles(); foreach ($styles as $style) { if ($style['name'] !== $flushed_style['name']) { diff --git a/sites/all/modules/imagecache_actions/canvasactions/rounded_corners.inc b/sites/all/modules/imagecache_actions/canvasactions/rounded_corners.inc index 8cde4cb..770fd6e 100644 --- a/sites/all/modules/imagecache_actions/canvasactions/rounded_corners.inc +++ b/sites/all/modules/imagecache_actions/canvasactions/rounded_corners.inc @@ -60,6 +60,11 @@ function canvasactions_roundedcorners_form($action) { $form['independent_corners_set']['radii'] = array( '#type' => 'item', '#id' => 'independent-corners-set', + '#states' => array( + 'visible' => array( + ':input[name="data[independent_corners_set][independent_corners]"]' => array('checked' => TRUE), + ), + ), ); foreach ($corners as $attribute => $label) { $form['independent_corners_set']['radii'][$attribute] = array( @@ -79,8 +84,7 @@ function canvasactions_roundedcorners_form($action) { ); */ $form['notes'] = array( - '#type' => 'markup', - '#value' => t(' + '#markup' => t(' Note: the rounded corners effect uses true alpha transparency masking. This means that this effect will fail to be saved on jpegs unless you either