canvasactions.inc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. <?php
  2. /**
  3. * @file Helper functions for the text2canvas action for imagecache
  4. *
  5. * @author Dan Morrison http://coders.co.nz
  6. *
  7. * Individually configurable rounded corners logic contributed by canaryMason
  8. * 2009 03 http://drupal.org/node/402112
  9. *
  10. * Better algorithm for trimming rounded corners from donquixote
  11. * 2009 09 http://drupal.org/node/564036
  12. *
  13. */
  14. if (!function_exists('image_overlay')) {
  15. module_load_include('inc', 'imagecache_actions', 'image_overlay');
  16. }
  17. if (!function_exists('imagecache_actions_pos_form')) {
  18. module_load_include('inc', 'imagecache_actions', 'utility-form');
  19. }
  20. if (!function_exists('imagecache_actions_keyword_filter')) {
  21. module_load_include('inc', 'imagecache_actions', 'utility');
  22. }
  23. ////////////////////////////////////////////////
  24. // IMAGEMASK
  25. /**
  26. * Implements the form callback for the image mask effect.
  27. *
  28. * @param array $data
  29. * array of settings for this action.
  30. * @return array
  31. * A form definition.
  32. */
  33. function canvasactions_imagemask_form($data) {
  34. // @todo: add offset/positioning/scaling support - currently the mask is applied to the supplied image without resizing and positioned at (0,0)
  35. $form = array();
  36. $form['effect_help_text'] = array(
  37. '#type' => 'item',
  38. '#title' => t('Image mask'),
  39. '#description' => t('This effect will add (or replace) a transparency channel to your image. The mask file should be a grayscale image where black is fully transparent and white is fully opaque. The referenced mask will be applied to the top left of the image.'),
  40. );
  41. $form['path'] = array(
  42. '#type' => 'textfield',
  43. '#title' => t('Mask file name'),
  44. '#default_value' => isset($data['path']) ? $data['path'] : '',
  45. '#description' => imagecache_actions_file_field_description(),
  46. '#element_validate' => array('imagecache_actions_validate_file'),
  47. );
  48. return $form;
  49. }
  50. /**
  51. * Implements the summary theme function for the image mask effect.
  52. */
  53. function theme_canvasactions_imagemask_summary($variables) {
  54. $data = $variables['data'];
  55. return 'file: ' . $data['path'];
  56. }
  57. /**
  58. * Implement the effect callback for the image mask effect.
  59. *
  60. * @param object $image
  61. * @param array $data
  62. *
  63. * @return bool
  64. */
  65. function canvasactions_imagemask_image(&$image, $data = array()) {
  66. $mask = imagecache_actions_image_load($data['path'], $image->toolkit);
  67. if ($mask) {
  68. // @todo: (sydneyshan) Consider best way to add offset support - I assume we
  69. // would position the mask somewhere (top/left/offset px etc) and choose if
  70. // the surrounding area is white or black (opaque or transparent) using an
  71. // extra form element (radio). Assess existing positioning code first to
  72. // reduce duplication of code. Pass the results to the following function as
  73. // array($mask, $data). Perhaps add a 'scale mask to fit image'/'scale image
  74. // to fit mask'/'no scale' radio group?
  75. return image_toolkit_invoke('imagemask', $image, array($mask));
  76. }
  77. return FALSE;
  78. }
  79. /**
  80. * Implements the image mask effect using the GD toolkit.
  81. *
  82. * $image object
  83. * Image object containing the GD image resource to operate on.
  84. * $mask object
  85. * An image object containing the image to use as mask.
  86. */
  87. function image_gd_imagemask($image, $mask) {
  88. $newPicture = imagecreatetruecolor($image->info['width'], $image->info['height']);
  89. imagesavealpha($newPicture, TRUE);
  90. imagealphablending($newPicture, TRUE);
  91. $transparent = imagecolorallocatealpha($newPicture, 0, 0, 0, 127);
  92. imagefill($newPicture, 0, 0, $transparent);
  93. // Perform pixel-based alpha map application
  94. for ($x = 0; $x < $image->info['width']; $x++) {
  95. for ($y = 0; $y < $image->info['height']; $y++) {
  96. // Deal with images with mismatched sizes
  97. if ($x >= $mask->info['width'] || $y >= $mask->info['height']) {
  98. imagesetpixel($newPicture, $x, $y, $transparent);
  99. }
  100. else {
  101. $alpha = imagecolorsforindex($mask->resource, imagecolorat($mask->resource, $x, $y));
  102. $alpha = 127 - floor($alpha['red'] / 2);
  103. $color = imagecolorsforindex($image->resource, imagecolorat($image->resource, $x, $y));
  104. imagesetpixel($newPicture, $x, $y, imagecolorallocatealpha($newPicture, $color['red'], $color['green'], $color['blue'], $alpha));
  105. }
  106. }
  107. }
  108. // Copy back to original picture
  109. imagedestroy($image->resource);
  110. $image->resource = $newPicture;
  111. return TRUE;
  112. }
  113. /**
  114. * Implements the image mask effect using the imagemagick toolkit.
  115. *
  116. * $image object
  117. * Image object containing a.o. the image to operate on.
  118. * $mask object
  119. * An image object containing the image to use as mask.
  120. */
  121. function image_imagemagick_imagemask($image, $mask) {
  122. $image->ops[] = escapeshellarg($mask->source) . ' -alpha Off -compose CopyOpacity -composite';
  123. return TRUE;
  124. }
  125. ////////////////////////////////////////////////
  126. /**
  127. * Implementation of imagecache_hook_form()
  128. *
  129. * Settings for preparing a canvas.
  130. *
  131. * @param $data array of settings for this action
  132. * @return a form definition
  133. */
  134. function canvasactions_definecanvas_form($data) {
  135. module_load_include('inc', 'imagecache_actions', 'utility-color');
  136. $defaults = array(
  137. 'RGB' => array(
  138. 'HEX' => '#333333',
  139. ),
  140. 'under' => TRUE,
  141. 'exact' => array(
  142. 'width' => '',
  143. 'height' => '',
  144. 'xpos' => 'center',
  145. 'ypos' => 'center',
  146. ),
  147. 'relative' => array(
  148. 'leftdiff' => '',
  149. 'rightdiff' => '',
  150. 'topdiff' => '',
  151. 'bottomdiff' => '',
  152. ),
  153. );
  154. $data = array_merge($defaults, (array) $data);
  155. $form = array(
  156. 'RGB' => imagecache_rgb_form($data['RGB']),
  157. 'help' => array(
  158. '#type' => 'markup',
  159. '#value' => t('Enter no color value for transparent. This will have the effect of adding clear margins around the image.'),
  160. '#prefix' => '<p>',
  161. '#suffix' => '</p>',
  162. ),
  163. 'under' => array(
  164. '#type' => 'checkbox',
  165. '#title' => t('Resize canvas <em>under</em> image (possibly cropping)'),
  166. '#default_value' => $data['under'],
  167. '#description' => t('If <em>not</em> set, this will create a solid flat layer, probably totally obscuring the source image'),
  168. ),
  169. );
  170. $form['info'] = array('#value' => t('Enter values in ONLY ONE of the below options. Either exact or relative. Most values are optional - you can adjust only one dimension as needed. If no useful values are set, the current base image size will be used.'));
  171. $form['exact'] = array(
  172. '#type' => 'fieldset',
  173. '#collapsible' => TRUE,
  174. '#title' => 'Exact size',
  175. 'help' => array(
  176. '#type' => 'markup',
  177. '#value' => t('Set the canvas to a precise size, possibly cropping the image. Use to start with a known size.'),
  178. '#prefix' => '<p>',
  179. '#suffix' => '</p>',
  180. ),
  181. 'width' => array(
  182. '#type' => 'textfield',
  183. '#title' => t('Width'),
  184. '#default_value' => $data['exact']['width'],
  185. '#description' => t('Enter a value in pixels or percent'),
  186. '#size' => 5,
  187. ),
  188. 'height' => array(
  189. '#type' => 'textfield',
  190. '#title' => t('Height'),
  191. '#default_value' => $data['exact']['height'],
  192. '#description' => t('Enter a value in pixels or percent'),
  193. '#size' => 5,
  194. ),
  195. );
  196. $form['exact'] = array_merge($form['exact'], imagecache_actions_pos_form($data['exact']));
  197. if (!$data['exact']['width'] && !$data['exact']['height']) {
  198. $form['exact']['#collapsed'] = TRUE;
  199. }
  200. $form['relative'] = array(
  201. '#type' => 'fieldset',
  202. '#collapsible' => TRUE,
  203. '#title' => t('Relative size'),
  204. 'help' => array(
  205. '#type' => 'markup',
  206. '#value' => '<p>' . 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.') . '</p>',
  207. ),
  208. 'leftdiff' => array(
  209. '#type' => 'textfield',
  210. '#title' => t('left difference'),
  211. '#default_value' => $data['relative']['leftdiff'],
  212. '#size' => 6,
  213. '#description' => t('Enter an offset in pixels.'),
  214. ),
  215. 'rightdiff' => array(
  216. '#type' => 'textfield',
  217. '#title' => t('right difference'),
  218. '#default_value' => $data['relative']['rightdiff'],
  219. '#size' => 6,
  220. '#description' => t('Enter an offset in pixels.'),
  221. ),
  222. 'topdiff' => array(
  223. '#type' => 'textfield',
  224. '#title' => t('top difference'),
  225. '#default_value' => $data['relative']['topdiff'],
  226. '#size' => 6,
  227. '#description' => t('Enter an offset in pixels.'),
  228. ),
  229. 'bottomdiff' => array(
  230. '#type' => 'textfield',
  231. '#title' => t('bottom difference'),
  232. '#default_value' => $data['relative']['bottomdiff'],
  233. '#size' => 6,
  234. '#description' => t('Enter an offset in pixels.'),
  235. ),
  236. );
  237. if (!$data['relative']['leftdiff'] && !$data['relative']['rightdiff'] && !$data['relative']['topdiff'] && !$data['relative']['bottomdiff']) {
  238. $form['relative']['#collapsed'] = TRUE;
  239. }
  240. $form['#submit'][] = 'canvasactions_definecanvas_form_submit';
  241. return $form;
  242. }
  243. /**
  244. * Implementation of theme_hook() for imagecache_ui.module
  245. */
  246. function theme_canvasactions_definecanvas_summary($variables) {
  247. $data = $variables['data'];
  248. if ($data['exact']['width'] || $data['exact']['height']) {
  249. $w = !empty($data['exact']['width']) ? $data['exact']['width'] : '100%';
  250. $h = !empty($data['exact']['height']) ? $data['exact']['height'] : '100%';
  251. $x = !empty($data['exact']['xpos']) ? $data['exact']['xpos'] : '0';
  252. $y = !empty($data['exact']['ypos']) ? $data['exact']['ypos'] : '0';
  253. $output = "{$w}x{$h} ($x, $y)";
  254. }
  255. else {
  256. $output = ' left:' . $data['relative']['leftdiff'];
  257. $output .= ' right:' . $data['relative']['rightdiff'];
  258. $output .= ' top:' . $data['relative']['topdiff'];
  259. $output .= ' bottom:' . $data['relative']['bottomdiff'];
  260. }
  261. $output .= theme('imagecacheactions_rgb', array('RGB' => $data['RGB']));
  262. $output .= ($data['under']) ? t(" <b>under</b> image ") : t(" <b>over</b> image ");
  263. return $output;
  264. }
  265. /**
  266. * Implementation of hook_image()
  267. *
  268. * Creates a solid background canvas
  269. *
  270. * Process the imagecache action on the passed image
  271. *
  272. * @param $image
  273. * array defining an image file, including :
  274. *
  275. * $image- >source as the filename,
  276. *
  277. * $image->info array
  278. *
  279. * $image->resource handle on the image object
  280. */
  281. function canvasactions_definecanvas_effect($image, $data) {
  282. // May be given either exact or relative dimensions.
  283. if ($data['exact']['width'] || $data['exact']['height']) {
  284. // Allows only one dimension to be used if the other is unset.
  285. if (!$data['exact']['width']) {
  286. $data['exact']['width'] = $image->info['width'];
  287. }
  288. if (!$data['exact']['height']) {
  289. $data['exact']['height'] = $image->info['height'];
  290. }
  291. $targetsize['width'] = imagecache_actions_percent_filter($data['exact']['width'], $image->info['width']);
  292. $targetsize['height'] = imagecache_actions_percent_filter($data['exact']['height'], $image->info['height']);
  293. $targetsize['left'] = image_filter_keyword($data['exact']['xpos'], $targetsize['width'], $image->info['width']);
  294. $targetsize['top'] = image_filter_keyword($data['exact']['ypos'], $targetsize['height'], $image->info['height']);
  295. }
  296. else {
  297. // calculate relative size
  298. $targetsize['width'] = $image->info['width'] + $data['relative']['leftdiff'] + $data['relative']['rightdiff'];
  299. $targetsize['height'] = $image->info['height'] + $data['relative']['topdiff'] + $data['relative']['bottomdiff'];
  300. $targetsize['left'] = $data['relative']['leftdiff'];
  301. $targetsize['top'] = $data['relative']['topdiff'];
  302. }
  303. // convert from hex (as it is stored in the UI)
  304. if ($data['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($data['RGB']['HEX'])) {
  305. $data['RGB'] = array_merge($data['RGB'], $deduced);
  306. }
  307. // All the maths is done, now defer to the api toolkits;
  308. $data['targetsize'] = $targetsize;
  309. $success = image_toolkit_invoke('definecanvas', $image, array($data));
  310. if ($success) {
  311. $image->info['width'] = $targetsize['width'];
  312. $image->info['height'] = $targetsize['height'];
  313. }
  314. return $success;
  315. }
  316. function canvasactions_definecanvas_dimensions(array &$dimensions, array $data) {
  317. // May be given either exact or relative dimensions.
  318. if ($data['exact']['width'] || $data['exact']['height']) {
  319. // Allows only one dimension to be used if the other is unset.
  320. if (!$data['exact']['width']) {
  321. $data['exact']['width'] = $dimensions['width'];
  322. }
  323. if (!$data['exact']['height']) {
  324. $data['exact']['height'] = $dimensions['height'];
  325. }
  326. $dimensions['width'] = imagecache_actions_percent_filter($data['exact']['width'], $dimensions['width']);
  327. $dimensions['height'] = imagecache_actions_percent_filter($data['exact']['height'], $dimensions['height']);
  328. }
  329. else {
  330. // calculate relative size
  331. $dimensions['width'] = $dimensions['width'] + $data['relative']['leftdiff'] + $data['relative']['rightdiff'];
  332. $dimensions['height'] = $dimensions['height'] + $data['relative']['topdiff'] + $data['relative']['bottomdiff'];
  333. }
  334. }
  335. /**
  336. * Draw a color (or transparency) behind an image
  337. *
  338. * $targetsize is an array expected to contain a width,height and a left,top
  339. * offset.
  340. */
  341. function image_gd_definecanvas($image, $data = array()) {
  342. $targetsize = $data['targetsize'];
  343. $RGB = $data['RGB'];
  344. $newcanvas = imagecreatetruecolor($targetsize['width'], $targetsize['height']);
  345. imagesavealpha($newcanvas, TRUE);
  346. imagealphablending($newcanvas, FALSE);
  347. imagesavealpha($image->resource, TRUE);
  348. if ($RGB['HEX']) {
  349. // Set color, allow it to define transparency, or assume opaque.
  350. $background = imagecolorallocatealpha($newcanvas, $RGB['red'], $RGB['green'], $RGB['blue'], $RGB['alpha']);
  351. }
  352. else {
  353. // No color, attempt transparency, assume white
  354. $background = imagecolorallocatealpha($newcanvas, 255, 255, 255, 127);
  355. }
  356. imagefilledrectangle($newcanvas, 0, 0, $targetsize['width'], $targetsize['height'], $background);
  357. # imagealphablending($newcanvas, TRUE);
  358. if ($data['under']) {
  359. $canvas_object = (object) array(
  360. 'resource' => $newcanvas,
  361. 'info' => array(
  362. 'width' => $targetsize['width'],
  363. 'height' => $targetsize['height'],
  364. 'mime_type' => $image->info['mime_type'],
  365. 'extension' => $image->info['extension'],
  366. ),
  367. 'toolkit' => $image->toolkit,
  368. );
  369. image_overlay($image, $canvas_object, $targetsize['left'], $targetsize['top'], 100, TRUE);
  370. }
  371. else {
  372. $image->resource = $newcanvas;
  373. }
  374. return TRUE;
  375. }
  376. /**
  377. * Draw a color (or transparency) behind an image
  378. * $targetsize is an array expected to contain a width,height and a left,top
  379. * offset.
  380. *
  381. * See http://www.imagemagick.org/script/command-line-options.php#extent
  382. * @todo: reset gravity?
  383. */
  384. function image_imagemagick_definecanvas($image, $data = array()) {
  385. $backgroundcolor = $data['RGB']['HEX'] != '' ? '#' . $data['RGB']['HEX'] : 'None';
  386. $image->ops[] = '-background ' . escapeshellarg($backgroundcolor);
  387. $compose_operator = $data['under'] ? 'src-over' : 'dst-over';
  388. $image->ops[] = "-compose $compose_operator";
  389. $targetsize = $data['targetsize'];
  390. $geometry = sprintf('%dx%d', $targetsize['width'], $targetsize['height']);
  391. if ($targetsize['left'] || $targetsize['top']) {
  392. $geometry .= sprintf('%+d%+d', -$targetsize['left'], -$targetsize['top']);
  393. }
  394. $image->ops[] = "-extent $geometry";
  395. return TRUE;
  396. }
  397. ////////////////////////////////////////////////
  398. /**
  399. * Place a given image under the current canvas
  400. *
  401. * Implementation of imagecache_hook_form()
  402. *
  403. * @param $data array of settings for this action
  404. * @return a form definition
  405. */
  406. function canvasactions_canvas2file_form($data) {
  407. // if (image_get_toolkit() != 'gd') {
  408. // drupal_set_message('Overlays are not currently supported by using imagemagick. This effect requires GD image toolkit only.', 'warning');
  409. // }
  410. $defaults = array(
  411. 'xpos' => '0',
  412. 'ypos' => '0',
  413. 'alpha' => '100',
  414. 'path' => '',
  415. 'dimensions' => 'original',
  416. );
  417. $data = array_merge($defaults, (array) $data);
  418. $form = imagecache_actions_pos_form($data);
  419. $form['alpha'] = array(
  420. '#type' => 'textfield',
  421. '#title' => t('opacity'),
  422. '#default_value' => $data['alpha'],
  423. '#size' => 6,
  424. '#description' => t('Opacity: 0-100. Be aware that values other than 100% may be slow to process.'),
  425. );
  426. $form['path'] = array(
  427. '#type' => 'textfield',
  428. '#title' => t('file name'),
  429. '#default_value' => $data['path'],
  430. '#description' => imagecache_actions_file_field_description(),
  431. '#element_validate' => array('imagecache_actions_validate_file'),
  432. );
  433. $form['dimensions'] = array(
  434. '#type' => 'radios',
  435. '#title' => t('final dimensions'),
  436. '#default_value' => $data['dimensions'],
  437. '#options' => array(
  438. 'original' => 'original (dimensions are retained)',
  439. 'background' => 'background (image will be forced to match the size of the background)',
  440. 'minimum' => 'minimum (image may be cropped)',
  441. 'maximum' => 'maximum (image may end up with gaps)',
  442. ),
  443. '#description' => t('What to do when the background image is a different size from the source image. Backgrounds are not tiled, but may be arbitrarily large.'),
  444. );
  445. return $form;
  446. }
  447. /**
  448. * Implementation of theme_hook() for imagecache_ui.module
  449. */
  450. function theme_canvasactions_canvas2file_summary($variables) {
  451. $data = $variables['data'];
  452. $file = $data['path'];
  453. return "xpos:{$data['xpos']} , ypos:{$data['ypos']} alpha:{$data['alpha']}%. file: $file, dimensions:{$data['dimensions']}";
  454. }
  455. /**
  456. * Place the source image on the current background
  457. *
  458. * Implementation of hook_image()
  459. *
  460. * Note - this is currently incompatable with imagemagick, due to the way it
  461. * addresses $image->resource directly - a gd only thing.
  462. *
  463. * @param $image
  464. * @param $data
  465. */
  466. function canvasactions_canvas2file_image(&$image, $data = array()) {
  467. $underlay = imagecache_actions_image_load($data['path'], $image->toolkit);
  468. if ($underlay) {
  469. // To handle odd sizes, we will resize/crop the background image to the
  470. // desired dimensions before starting the merge. The built-in
  471. // imagecopymerge, and the watermark library both do not allow overlays to
  472. // be bigger than the target.
  473. // Adjust size
  474. $crop_rules = array(
  475. 'xoffset' => 0,
  476. 'yoffset' => 0,
  477. );
  478. if (empty($data['dimensions'])) {
  479. $data['dimensions'] = 'original';
  480. }
  481. switch ($data['dimensions']) {
  482. case 'original':
  483. // If the underlay is smaller than the target size,
  484. // then when preparing the underlay by cropping it,
  485. // the offsets may need to be negative
  486. // which will produce a 'cropped' image larger than the original.
  487. // In this case, we need to calculate the position of the bg image
  488. // in relation to the space it will occupy under the top layer
  489. #$crop_rules['xoffset'] = $underlay->info['width'] - $image->info['width'] ;
  490. $crop_rules['width'] = $image->info['width'];
  491. $crop_rules['height'] = $image->info['height'];
  492. break;
  493. case 'background':
  494. $crop_rules['width'] = $underlay->info['width'];
  495. $crop_rules['height'] = $underlay->info['height'];
  496. break;
  497. case 'minimum':
  498. $crop_rules['width'] = min($underlay->info['width'], $image->info['width']);
  499. $crop_rules['height'] = min($underlay->info['height'], $image->info['height']);
  500. break;
  501. case 'maximum':
  502. $crop_rules['width'] = max($underlay->info['width'], $image->info['width']);
  503. $crop_rules['height'] = max($underlay->info['height'], $image->info['height']);
  504. break;
  505. }
  506. // imageapi crop assumes upsize is legal.
  507. // Crop both before processing to avoid unwanted processing.
  508. image_crop_effect($underlay, $crop_rules);
  509. # BUG - this doesn't position either
  510. // Actually this fails because imagecache_crop fills it with solid color when 'cropping' to a larger size.
  511. #imagecache_crop_image($image, $crop_rules);
  512. #dpm(get_defined_vars());
  513. // This func modifies the underlay image by ref, placing the current canvas on it
  514. if (image_overlay($image, $underlay, $data['xpos'], $data['ypos'], $data['alpha'], TRUE)) {
  515. #$image->resource = $underlay->resource;
  516. $image = $underlay;
  517. return TRUE;
  518. }
  519. }
  520. return FALSE;
  521. }
  522. /**
  523. * Image dimensions callback; canvas2file (underlay/background).
  524. *
  525. * @param array $dimensions
  526. * Dimensions to be modified - an associative array containing the items
  527. * 'width' and 'height' (in pixels).
  528. * @param $data
  529. * An associative array containing the effect data.
  530. */
  531. function canvasactions_canvas2file_dimensions(array &$dimensions, array $data) {
  532. if ($data['dimensions'] !== 'original') {
  533. $underlay = imagecache_actions_image_load($data['path']);
  534. if ($underlay) {
  535. // If the new dimensions are taken from the background, we don't need to
  536. // know the original dimensions, we can just set the new dimensions to the
  537. // dimensions of the background. Otherwise, we need to know the old
  538. // dimensions. If unknown we have to leave them unknown.
  539. switch ($data['dimensions']) {
  540. case 'background':
  541. $dimensions['width'] = $underlay->info['width'];
  542. $dimensions['height'] = $underlay->info['height'];
  543. break;
  544. case 'minimum':
  545. $dimensions['width'] = isset($dimensions['width']) ? min($underlay->info['width'], $dimensions['width']) : NULL;
  546. $dimensions['height'] = isset($dimensions['height']) ? min($underlay->info['height'], $dimensions['height']) : NULL;
  547. break;
  548. case 'maximum':
  549. $dimensions['width'] = isset($dimensions['width']) ? max($underlay->info['width'], $dimensions['width']) : NULL;
  550. $dimensions['height'] = isset($dimensions['height']) ? max($underlay->info['height'], $dimensions['height']) : NULL;
  551. break;
  552. }
  553. }
  554. }
  555. }
  556. /**
  557. * Place a given image on top of the current canvas
  558. *
  559. * Implementation of imagecache_hook_form()
  560. *
  561. * @param $data array of settings for this action
  562. * @return a form definition
  563. */
  564. function canvasactions_file2canvas_form($data) {
  565. $defaults = array(
  566. 'xpos' => '',
  567. 'ypos' => '',
  568. 'alpha' => '100',
  569. 'path' => '',
  570. );
  571. $data = array_merge($defaults, (array) $data);
  572. $form = array(
  573. 'help' => array(
  574. '#type' => 'markup',
  575. '#value' => t('Note that using a transparent overlay that is larger than the source image may result in unwanted results - a solid background.'),
  576. ),
  577. );
  578. $form += imagecache_actions_pos_form($data);
  579. $form['alpha'] = array(
  580. '#type' => 'textfield',
  581. '#title' => t('opacity'),
  582. '#default_value' => $data['alpha'],
  583. '#size' => 6,
  584. '#description' => t('Opacity: 0-100. <b>Warning:</b> 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.'),
  585. );
  586. $form['path'] = array(
  587. '#type' => 'textfield',
  588. '#title' => t('file name'),
  589. '#default_value' => $data['path'],
  590. '#description' => imagecache_actions_file_field_description(),
  591. '#element_validate' => array('imagecache_actions_validate_file'),
  592. );
  593. return $form;
  594. }
  595. /**
  596. * Implementation of theme_hook() for imagecache_ui.module
  597. */
  598. function theme_canvasactions_file2canvas_summary($variables) {
  599. $data = $variables['data'];
  600. return '<strong>' . $data['path'] . '</strong> x:' . $data['xpos'] . ', y:' . $data['ypos'] . ' alpha:' . (@$data['alpha'] ? $data['alpha'] : 100) . '%';
  601. }
  602. /**
  603. * Place the source image on the current background
  604. *
  605. * Implementation of hook_image()
  606. *
  607. *
  608. * @param $image
  609. * @param $data
  610. */
  611. function canvasactions_file2canvas_image($image, $data = array()) {
  612. $overlay = imagecache_actions_image_load($data['path']);
  613. if ($overlay) {
  614. if (!isset($data['alpha'])) {
  615. $data['alpha'] = 100;
  616. }
  617. return image_overlay($image, $overlay, $data['xpos'], $data['ypos'], $data['alpha']);
  618. }
  619. return FALSE;
  620. }
  621. ///////////////////////////////////////////////////////////////////
  622. /**
  623. * Place the source image on top of the current canvas
  624. *
  625. * Implementation of imagecache_hook_form()
  626. *
  627. *
  628. *
  629. * @param $data array of settings for this action
  630. * @return a form definition
  631. */
  632. function canvasactions_source2canvas_form($data) {
  633. $defaults = array(
  634. 'xpos' => '',
  635. 'ypos' => '',
  636. 'alpha' => '100',
  637. 'path' => '',
  638. );
  639. $data = array_merge($defaults, (array) $data);
  640. $form = imagecache_actions_pos_form($data);
  641. $form['alpha'] = array(
  642. '#type' => 'textfield',
  643. '#title' => t('opacity'),
  644. '#default_value' => $data['alpha'],
  645. '#size' => 6,
  646. '#description' => t('Opacity: 0-100.'),
  647. );
  648. return $form;
  649. }
  650. /**
  651. * Implementation of theme_hook() for imagecache_ui.module
  652. */
  653. function theme_canvasactions_source2canvas_summary($variables) {
  654. $data = $variables['data'];
  655. return 'xpos:' . $data['xpos'] . ', ypos:' . $data['ypos'] . ' alpha:' . $data['alpha'] . '%';
  656. }
  657. /**
  658. * Place the source image on the current background
  659. *
  660. * Implementation of hook_image()
  661. *
  662. *
  663. * @param $image
  664. * @param $data
  665. */
  666. function canvasactions_source2canvas_image($image, $data = array()) {
  667. $overlay = image_load($image->source, $image->toolkit);
  668. return image_overlay($image, $overlay, $data['xpos'], $data['ypos'], $data['alpha']);
  669. }
  670. /**
  671. * Implements the image effect form callback for the aspect switcher effect.
  672. *
  673. * @param array $data
  674. * Array of settings for this action
  675. * @return array
  676. * The form definition
  677. */
  678. function canvasactions_aspect_form($data) {
  679. $defaults = array(
  680. 'ratio_adjustment' => 1,
  681. 'portrait' => NULL,
  682. 'landscape' => NULL,
  683. );
  684. $data = array_merge($defaults, (array) $data);
  685. $form = array(
  686. 'help' => array(
  687. '#type' => 'markup',
  688. '#value' => t('You must create the two presets to use <em>before</em> enabling this process.'),
  689. )
  690. );
  691. $styles = image_style_options(TRUE);
  692. // @todo: remove the current style to prevent (immediate) recursion?
  693. $form['portrait'] = array(
  694. '#type' => 'select',
  695. '#title' => t('Style to use if the image is portrait (vertical)'),
  696. '#default_value' => $data['portrait'],
  697. '#options' => $styles,
  698. '#description' => t('If you choose none, no extra processing will be done.'),
  699. );
  700. $form['landscape'] = array(
  701. '#type' => 'select',
  702. '#title' => t('Style to use if the image is landscape (horizontal)'),
  703. '#default_value' => $data['landscape'],
  704. '#options' => $styles,
  705. '#description' => t('If you choose none, no extra processing will be done.'),
  706. );
  707. $form['ratio_adjustment'] = array(
  708. '#type' => 'textfield',
  709. '#title' => t('Ratio Adjustment (advanced)'),
  710. '#size' => 3,
  711. '#default_value' => $data['ratio_adjustment'],
  712. '#description' => t("
  713. This allows you to bend the rules for how different the proportions need to be to trigger the switch.
  714. <br/>If the (width/height)*n is greater than 1, use 'landscape', otherwise use 'portrait'.
  715. <br/>When n = 1 (the default) it will switch between portrait and landscape modes.
  716. <br/>If n > 1, images that are slightly wide will still be treated as portraits.
  717. If n < 1 then blunt portraits will be treated as landscape.
  718. "),
  719. );
  720. return $form;
  721. }
  722. /**
  723. * Implements the summary theme callback for the aspect switcher effect.
  724. *
  725. * @param array $variables
  726. *
  727. * @return string
  728. */
  729. function theme_canvasactions_aspect_summary($variables) {
  730. $data = $variables['data'];
  731. $ratio_adjustment = '';
  732. if ($data['ratio_adjustment'] != 1) {
  733. $ratio_adjustment = " (switch at 1:{$data['ratio_adjustment']})";
  734. }
  735. return 'Portrait size: <strong>' . $data['portrait'] . '</strong>. Landscape size: <strong>' . $data['landscape'] . '</strong>' . $ratio_adjustment;
  736. }
  737. /**
  738. * Implements the image effect callback for the aspect switcher effect.
  739. *
  740. * @param object $image
  741. * @param array $data
  742. *
  743. * @return bool
  744. */
  745. function canvasactions_aspect_image($image, $data = array()) {
  746. $ratio_adjustment = isset($data['ratio_adjustment']) ? floatval( $data['ratio_adjustment']) : 1;
  747. $aspect = $image->info['width'] / $image->info['height'];
  748. // width / height * adjustment. If > 1, it's wide.
  749. $style_name = (($aspect * $ratio_adjustment) > 1) ? $data['landscape'] : $data['portrait'];
  750. if (empty($style_name)) {
  751. // Do nothing. just return what we've got.
  752. return TRUE;
  753. }
  754. $style = image_style_load($style_name);
  755. if (empty($style)) {
  756. // Required preset has gone missing?
  757. 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);
  758. return FALSE;
  759. }
  760. // Run the preset actions ourself.
  761. foreach ($style['effects'] as $sub_effect) {
  762. image_effect_apply($image, $sub_effect);
  763. }
  764. return TRUE;
  765. }
  766. /**
  767. * Implements the dimension callback for the aspect switcher effect.
  768. *
  769. * @param array $dimensions
  770. * Dimensions to be modified - an associative array containing the items
  771. * 'width' and 'height' (in pixels).
  772. * @param array $data
  773. * An associative array containing the effect data.
  774. */
  775. function canvasactions_aspect_dimensions(array &$dimensions, array $data) {
  776. if (empty($dimensions['width']) || empty($dimensions['height'])) {
  777. return;
  778. }
  779. $ratio_adjustment = isset($data['ratio_adjustment']) ? floatval( $data['ratio_adjustment']) : 1;
  780. $aspect = $dimensions['width'] / $dimensions['height'];
  781. $style_name = (($aspect * $ratio_adjustment) > 1) ? $data['landscape'] : $data['portrait'];
  782. image_style_transform_dimensions($style_name, $dimensions);
  783. }