imagecache_coloractions.module 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. <?php
  2. /**
  3. * @file
  4. * Additional actions for imagecache processing.
  5. *
  6. * Exposes some of the simpler PHP 'imagefilter' actions (colorshift,
  7. * brightness, negative)
  8. * - A transparency masker for merging with backgrounds.
  9. * - A pseudo - file conversion feature.
  10. *
  11. * Compatible with the 2008 revision (imagecache 2)
  12. *
  13. * @author dan http://coders.co.nz
  14. * @author sydneyshan http://enigmadigital.net.au
  15. */
  16. // During devel, caching is pointless. Flush it
  17. //imagecache_action_definitions(TRUE);
  18. if (! function_exists('imagecache_actions_calculate_relative_position') ) {
  19. module_load_include('inc', 'imagecache_actions', 'utility');
  20. }
  21. module_load_include('inc', 'imagecache_actions', 'utility-color');
  22. // @todo There doesn't seem to be a way to specify a file in hook_image_effect_info
  23. // so placing this here for the time being.
  24. module_load_include('inc', 'imagecache_coloractions', 'transparency');
  25. /**
  26. * hook_image_effect_info()
  27. *
  28. * Return the descriptions for the supported actions.
  29. */
  30. function imagecache_coloractions_image_effect_info() {
  31. $effects = array();
  32. $effects['coloractions_colorshift'] = array(
  33. 'label' => t('Color Shift'),
  34. 'help' => t('Adjust image colors.'),
  35. 'effect callback' => 'coloractions_colorshift_image',
  36. 'dimensions passthrough' => TRUE,
  37. 'form callback' => 'coloractions_colorshift_form',
  38. 'summary theme' => 'coloractions_colorshift_summary',
  39. );
  40. $effects['imagecache_coloroverlay'] = array(
  41. 'label' => t('Color Overlay'),
  42. 'help' => t('Apply a color tint to an image (retaining blacks and whites).'),
  43. 'effect callback' => 'coloractions_coloroverlay_image',
  44. 'dimensions passthrough' => TRUE,
  45. 'form callback' => 'coloractions_coloroverlay_form',
  46. 'summary theme' => 'coloractions_coloroverlay_summary',
  47. );
  48. $effects['coloractions_brightness'] = array(
  49. 'label' => t('Brightness'),
  50. 'help' => t('Adjust image brightness.'),
  51. 'effect callback' => 'coloractions_brightness_image',
  52. 'dimensions passthrough' => TRUE,
  53. 'form callback' => 'coloractions_brightness_form',
  54. 'summary theme' => 'coloractions_brightness_summary',
  55. );
  56. $effects['coloractions_inverse'] = array(
  57. 'label' => t('Negative Image'),
  58. 'help' => t('Invert colors and brightness.'),
  59. 'effect callback' => 'coloractions_inverse_image',
  60. 'dimensions passthrough' => TRUE,
  61. );
  62. // @todo Convert may need a little more work.
  63. $effects['coloractions_convert'] = array(
  64. 'label' => t('Change file format'),
  65. 'help' => t('Choose to save the image as a different filetype.'),
  66. 'effect callback' => 'coloractions_convert_image',
  67. 'dimensions passthrough' => TRUE,
  68. 'form callback' => 'coloractions_convert_form',
  69. 'summary theme' => 'coloractions_convert_summary',
  70. );
  71. $effects['imagecache_alpha'] = array(
  72. 'label' => t('Alpha Transparency'),
  73. 'help' => t('Adjust transparency.'),
  74. 'effect callback' => 'imagecache_alpha_image',
  75. 'dimensions passthrough' => TRUE,
  76. 'form callback' => 'imagecache_alpha_form',
  77. 'summary theme' => 'coloractions_alpha_summary',
  78. );
  79. return $effects;
  80. }
  81. /**
  82. * hook_theme()
  83. */
  84. function imagecache_coloractions_theme() {
  85. return array(
  86. 'coloractions_colorshift_summary' => array(
  87. 'variables' => array('data' => NULL),
  88. ),
  89. 'coloractions_coloroverlay_summary' => array(
  90. 'variables' => array('data' => NULL),
  91. ),
  92. 'coloractions_alpha_summary' => array(
  93. 'variables' => array('data' => NULL),
  94. ),
  95. 'coloractions_brightness_summary' => array(
  96. 'variables' => array('data' => NULL),
  97. ),
  98. 'coloractions_convert_summary' => array(
  99. 'variables' => array('data' => NULL),
  100. ),
  101. );
  102. }
  103. /**
  104. * Implementation of imagecache_hook_form()
  105. *
  106. * Settings for colorshift actions.
  107. *
  108. * @param $action array of settings for this action
  109. * @return a form definition
  110. */
  111. function coloractions_colorshift_form($action) {
  112. $defaults = array(
  113. 'RGB' => array(
  114. 'HEX' => '#FF0000',
  115. ),
  116. );
  117. $action = array_merge($defaults, (array) $action);
  118. $form = array('#theme' => 'imagecache_rgb_form');
  119. $form['RGB'] = imagecache_rgb_form($action['RGB']);
  120. $form['note'] = array('#value' => t("<p>
  121. Note that colorshift is a mathematical filter that doesn't always
  122. have the expected result.
  123. To shift an image precisely TO a target color,
  124. desaturate (greyscale) it before colorizing.
  125. The hue (color wheel) is the <em>direction</em> the
  126. existing colors are shifted. The tone (inner box) is the amount.
  127. Keep the tone half-way up the left site of the color box
  128. for best results.
  129. </p>"));
  130. return $form;
  131. }
  132. /**
  133. * Implementation of theme_hook() for imagecache_ui.module
  134. */
  135. function theme_coloractions_colorshift_summary($variables) {
  136. return theme_imagecacheactions_rgb($variables['data']);
  137. }
  138. /**
  139. * Implementation of hook_image()
  140. *
  141. * Process the imagecache action on the passed image
  142. *
  143. * Just converts and passes the vals to the all-purpose 'filter' action
  144. */
  145. function coloractions_colorshift_image($image, $data = array()) {
  146. // convert color from hex (as it is stored in the UI)
  147. if ($data['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($data['RGB']['HEX'])) {
  148. $data['RGB'] = array_merge($data['RGB'], $deduced);
  149. }
  150. return image_toolkit_invoke('colorshift', $image, array($data));
  151. }
  152. /**
  153. * Implementation of hook_{toolkit}_{effect}()
  154. */
  155. function image_gd_colorshift($image, $data = array()) {
  156. $RGB = $data['RGB'];
  157. if (!function_exists('imagefilter')) {
  158. module_load_include('inc', 'imagecache_actions', 'imagefilter');
  159. }
  160. return imagefilter($image->resource, 4, $RGB['red'], $RGB['green'], $RGB['blue']);
  161. }
  162. /**
  163. * Implementation of hook_{toolkit}_{effect}()
  164. */
  165. function image_imagemagick_colorshift($image, $data = array()) {
  166. $RGB = $data['RGB'];
  167. $image->ops[] = "-fill rgb" . escapeshellcmd('(') . "{$RGB['red']},{$RGB['green']},{$RGB['blue']}" . escapeshellcmd(')') . " -colorize 50" . escapeshellcmd('%');
  168. return TRUE;
  169. }
  170. /**
  171. * Implementation of imagecache_hook_form()
  172. *
  173. * Settings for coloroverlay actions.
  174. *
  175. * @param $action array of settings for this action
  176. * @return a form definition
  177. */
  178. function coloractions_coloroverlay_form($action) {
  179. $defaults = array(
  180. 'RGB' => array(
  181. 'HEX' => '#E2DB6A',
  182. ),
  183. );
  184. $action = array_merge($defaults, (array) $action);
  185. $form = array('#theme' => 'imagecache_rgb_form');
  186. $form['RGB'] = imagecache_rgb_form($action['RGB']);
  187. $form['note'] = array('#value' => t("<p>
  188. Note that color overlay is a mathematical filter that doesn't always
  189. have the expected result.
  190. To shift an image precisely TO a target color,
  191. desaturate (greyscale) it before colorizing.
  192. The hue (color wheel) is the <em>direction</em> the
  193. existing colors are shifted. The tone (inner box) is the amount.
  194. Keep the tone half-way up the left site of the color box
  195. for best results.
  196. </p>"));
  197. return $form;
  198. }
  199. /**
  200. * Implementation of theme_hook() for imagecache_ui.module
  201. */
  202. function theme_coloractions_coloroverlay_summary($variables) {
  203. return theme_imagecacheactions_rgb($variables['data']);
  204. }
  205. /**
  206. * Implementation of hook_image()
  207. *
  208. * Process the imagecache action on the passed image
  209. *
  210. * Just converts and passes the vals to the all-purpose 'filter' action
  211. */
  212. function coloractions_coloroverlay_image($image, $data = array()) {
  213. // convert color from hex (as it is stored in the UI)
  214. if ($data['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($data['RGB']['HEX'])) {
  215. $data['RGB'] = array_merge($data['RGB'], $deduced);
  216. }
  217. return image_toolkit_invoke('coloroverlay', $image, array($data));
  218. }
  219. /**
  220. * Implementation of hook_{toolkit}_{effect}()
  221. */
  222. function image_gd_coloroverlay($image, $data = array()) {
  223. $RGB = $data['RGB'];
  224. $w = $image->info['width'];
  225. $h = $image->info['height'];
  226. for($y=0;$y<$h;$y++) {
  227. for($x=0;$x<$w;$x++) {
  228. $rgb = imagecolorat($image->resource, $x, $y);
  229. $source = imagecolorsforindex($image->resource, $rgb);
  230. if($source['red'] <= 128){
  231. $final_r = (2 * $source['red'] * $RGB['red'])/256;
  232. }else{
  233. $final_r = 255 - (((255 - (2 * ($source['red'] - 128))) * (255 - $RGB['red']))/256);
  234. }
  235. if($source['green'] <= 128){
  236. $final_g = (2 * $source['green'] * $RGB['green'])/256;
  237. }else{
  238. $final_g = 255 - (((255 - (2 * ($source['green'] - 128))) * (255 - $RGB['green']))/256);
  239. }
  240. if($source['blue'] <= 128){
  241. $final_b = (2 * $source['blue'] * $RGB['blue'])/256;
  242. }else{
  243. $final_b = 255 - (((255 - (2 * ($source['blue'] - 128))) * (255 - $RGB['blue']))/256);
  244. }
  245. $final_colour = imagecolorallocatealpha($image->resource, $final_r, $final_g, $final_b, $source['alpha']);
  246. imagesetpixel($image->resource, $x, $y, $final_colour);
  247. }
  248. }
  249. return TRUE;
  250. }
  251. /**
  252. * Implementation of hook_{toolkit}_{effect}()
  253. */
  254. function image_imagemagick_coloroverlay($image, $data = array()) {
  255. $RGB = $data['RGB'];
  256. $image->ops[] = escapeshellcmd('(') . " +clone +matte -fill rgb" . escapeshellcmd('(') . "{$RGB['red']},{$RGB['green']},{$RGB['blue']}" . escapeshellcmd(')') . " -colorize 100" . escapeshellcmd('%') . " +clone +swap -compose overlay -composite " . escapeshellcmd(')') . " -compose SrcIn -composite";
  257. return TRUE;
  258. }
  259. /**
  260. * Implementation of imagecache_hook_form()
  261. *
  262. * Settings for colorshift actions.
  263. *
  264. * @param $action array of settings for this action
  265. * @return a form definition
  266. */
  267. function coloractions_brightness_form($action) {
  268. $default = array('filter_arg1' => '100');
  269. $action = array_merge($default, (array) $action);
  270. $form = array();
  271. $form['help'] = array('#value' => t("The brightness effect seldom looks good on its own, but can be useful to wash out an image before making it transparent - eg for a watermark."));
  272. $form['filter_arg1'] = array(
  273. '#type' => 'textfield',
  274. '#title' => t('Brightness'),
  275. '#description' => t('-255 - +255'),
  276. '#default_value' => $action['filter_arg1'],
  277. '#size' => 3,
  278. );
  279. return $form;
  280. }
  281. /**
  282. * Implementation of hook_image()
  283. *
  284. * Process the imagecache action on the passed image
  285. */
  286. function coloractions_brightness_image($image, $data = array()) {
  287. return image_toolkit_invoke('brightness', $image, array($data));
  288. }
  289. /**
  290. * Implementation of hook_{toolkit}_{effect}()
  291. */
  292. function image_gd_brightness($image, $data = array()) {
  293. if (!function_exists('imagefilter')) {
  294. module_load_include('inc', 'imagecache_actions', 'imagefilter'); }
  295. return imagefilter($image->resource, 2, $data['filter_arg1']);
  296. }
  297. /**
  298. * Implementation of hook_{toolkit}_{effect}()
  299. */
  300. function image_imagemagick_brightness($image, $data = array()) {
  301. $image->ops[] = "-modulate " . (int)(100 + ( $data['filter_arg1'] / 128 * 100 ));
  302. return TRUE;
  303. }
  304. /**
  305. * Implementation of theme_hook() for imagecache_ui.module
  306. */
  307. function theme_coloractions_brightness_summary($variables) {
  308. return t("Adjust") . " : " . $variables['data']['filter_arg1'];
  309. }
  310. /**
  311. * Implementation of imagecache_hook_form()
  312. *
  313. * No settings.
  314. *
  315. * @param $action array of settings for this action
  316. * @return a form definition
  317. */
  318. function coloractions_inverse_form($action) {
  319. $form = array();
  320. return $form;
  321. }
  322. /**
  323. * Implementation of hook_image()
  324. *
  325. * Process the imagecache action on the passed image
  326. */
  327. function coloractions_inverse_image($image, $data = array()) {
  328. return image_toolkit_invoke('inverse', $image, array($data));
  329. }
  330. /**
  331. * Implementation of hook_{toolkit}_{effect}()
  332. */
  333. function image_gd_inverse($image, $data = array()) {
  334. if (!function_exists('imagefilter')) {
  335. module_load_include('inc', 'imagecache_actions', 'imagefilter');
  336. }
  337. return imagefilter($image->resource, 0);
  338. }
  339. /**
  340. * Implementation of hook_{toolkit}_{effect}()
  341. */
  342. function image_imagemagick_inverse(&$image, $data = array()) {
  343. // TODO
  344. return FALSE;
  345. }
  346. /**
  347. * Implementation of imagecache_hook_form()
  348. *
  349. * @param $action array of settings for this action
  350. * @return a form definition
  351. */
  352. function coloractions_convert_form($action) {
  353. $form = array(
  354. 'help' => array(
  355. '#type' => 'markup',
  356. '#value' => t("If you've been using transparencies in the process, the result may get saved as a PNG (as the image was treated as a one in in-between processes). If this is not desired (file sizes may get too big) you should use this process to force a flatten action before saving. "),
  357. ),
  358. 'help2' => array(
  359. '#type' => 'markup',
  360. '#value' => t("For technical reasons, changing the file format within imagecache does <em>not</em> change the filename suffix. A png may be saved as a *.jpg or vice versa. This may confuse some browsers and image software, but most of them have no trouble. "),
  361. ),
  362. 'format' => array(
  363. '#title' => t("File format"),
  364. '#type' => 'select',
  365. '#default_value' => isset($action['format']) ? $action['format'] : 'image/png',
  366. '#options' => coloractions_file_formats(),
  367. ),
  368. 'quality' => array(
  369. '#type' => 'textfield',
  370. '#title' => t('Quality'),
  371. '#description' => t('Override the default image quality. Works for Imagemagick only. Ranges from 0 to 100. For jpg, higher values mean better image quality but bigger files. For png it is a combination of compression and filter'),
  372. '#size' => 10,
  373. '#maxlength' => 3,
  374. '#default_value' => isset($action['quality']) ? $action['quality'] : '75',
  375. '#field_suffix' => '%',
  376. ),
  377. );
  378. return $form;
  379. }
  380. /**
  381. * Implementation of theme_hook() for imagecache_ui.module
  382. */
  383. function theme_coloractions_convert_summary($variables) {
  384. $data = $variables['data'];
  385. $formats = coloractions_file_formats();
  386. if ($formats[$data['format']] == 'jpg') {
  387. return t('Convert to: @format, quality: @quality%', array(
  388. '@format' => $formats[$data['format']],
  389. '@quality' => $data['quality']
  390. ));
  391. }
  392. else {
  393. return t("Convert to") .": ". $formats[$data['format']];
  394. }
  395. }
  396. /**
  397. * Implementation of hook_image()
  398. *
  399. * Process the imagecache action on the passed image
  400. */
  401. function coloractions_convert_image($image, $data = array()) {
  402. $formats = coloractions_file_formats();
  403. $image->info['mime_type'] = $data['format'];
  404. $image->info['extension'] = $formats[$data['format']];
  405. image_toolkit_invoke('convert_image', $image, array($data));
  406. return TRUE;
  407. }
  408. /**
  409. * Implementation of hook_{toolkit}_{effect}()
  410. *
  411. * image_toolkit_invoke will exit with an error when no implementation is
  412. * provided for the active toolkit so provide an empty operation for the GD
  413. * tookit
  414. */
  415. function image_gd_convert_image($image, $data) {
  416. return TRUE;
  417. }
  418. /**
  419. * Implements hook_{toolkit}_{effect}().
  420. *
  421. * Converting the image format with imagemagick is done by prepending the output
  422. * format to the target file separated by a colon (:). This is done with
  423. * hook_imagemagick_arguments_alter(), see below.
  424. */
  425. function image_imagemagick_convert_image($image, $data) {
  426. $image->ops['output_format'] = $image->info['extension'];
  427. $image->ops['custom_quality_value'] = (int) $data['quality'];
  428. return TRUE;
  429. }
  430. /**
  431. * Implements hook_imagemagick_arguments_alter.
  432. *
  433. * This hook moves a change in output format from the args (action list) to the
  434. * destination format setting within the context.
  435. */
  436. function imagecache_coloractions_imagemagick_arguments_alter(&$args, &$context) {
  437. if (isset($args['output_format'])) {
  438. $context['destination_format'] = $args['output_format'];
  439. unset($args['output_format']);
  440. }
  441. if (isset($args['custom_quality_value'])) {
  442. $args['quality'] = sprintf('-quality %d', $args['custom_quality_value']);
  443. unset($args['custom_quality_value']);
  444. }
  445. }
  446. /**
  447. * Mini mime-type list
  448. */
  449. function coloractions_file_formats() {
  450. return array('image/jpeg' => 'jpg', 'image/gif' => 'gif', 'image/png' => 'png');
  451. }