transparency.inc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. /**
  3. * @file Helper functions for the alpha action for imagecache
  4. *
  5. * @author dan http://coders.co.nz
  6. */
  7. /**
  8. * Implementation of imagecache_hook_form()
  9. *
  10. * Settings for alpha actions.
  11. *
  12. * @param $action array of settings for this action
  13. * @return a form definition
  14. */
  15. function imagecache_alpha_form($action) {
  16. $defaults = array(
  17. 'flatten' => FALSE,
  18. 'RGB' => array('HEX' => '#000000'),
  19. 'opacity' => 0.5,
  20. );
  21. $action = array_merge($defaults, (array) $action);
  22. $form = array();
  23. $form['help'] = array(
  24. '#markup' => t("
  25. <p>You can <em>either</em> set the alpha values of the image to a fixed
  26. amount by defining opacity, <em>or</em> choose a color and let the
  27. darkness of the image pixels define an opacity.
  28. These are different effects. Don't do both
  29. or you will just get a plain block of color of a certain opacity.
  30. ")
  31. );
  32. $form['opacity'] = array(
  33. '#type' => 'textfield',
  34. '#title' => t('Opacity'),
  35. '#default_value' => $action['opacity'],
  36. '#size' => 3,
  37. '#description' => t("
  38. A decimal between 0 and 1.
  39. You can define the amount of transparency to apply, eg 0.8 (80%) opacity
  40. will make the image slightly transparent.
  41. Use this with <em>no</em> fill color defined for normal results.
  42. If you follow up by flattening the image onto white or grey,
  43. this will have the effect of partial desaturation.
  44. "),
  45. );
  46. $form['description'] = array('#value' => t(
  47. "<p>Alpha toning is an advanced method of greyscaling or colorizing.
  48. It works using transparency, not colour matching.
  49. The results of this filter are excellent for using as watermarks,
  50. and for 'sepia' type imprints on coloured or textured backgrounds.
  51. It converts dark areas of the image to opaque, light to transparent.</p>
  52. <p>Note that if you are working with JPEGs, this alpha effect will not last into the final image
  53. <em>unless</em> you either <strong>flatten</strong> this image against a background color
  54. or image in a later process or <strong>convert</strong> it to a PNG before saving
  55. using available imagecache actions.</p>"
  56. ));
  57. $form['RGB'] = imagecache_rgb_form($action['RGB']);
  58. $form['RGB']['#type'] = 'fieldset';
  59. $form['RGB']['#title'] = t('Fill Color');
  60. $form['RGB']['HEX']['#description'] = t("
  61. Although this image will end up as an alpha transparency mask,
  62. it still has to have some colour to be visible.
  63. Black is safe. Dark Sepia #704214 is good too.
  64. Set it to nothing to not perform any color shift.
  65. ");
  66. $form['flatten'] = array(
  67. '#type' => 'checkbox',
  68. '#title' => t('Flatten Transparency'),
  69. '#default_value' => $action['flatten'],
  70. '#return_value' => TRUE,
  71. '#description' => t("The opposite of adding alpha transparency, 'flatten' will place the given colour solidly behind the image. Use this if you can't trust IE, or you really do want the image filled in with a solid colour."),
  72. );
  73. return $form;
  74. }
  75. /**
  76. * Implementation of theme_hook() for imagecache_ui.module
  77. */
  78. function theme_imagecache_alpha($variables) {
  79. $element = $variables['element'];
  80. return ($element['#value']['flatten'] ? t("Flatten") : t("Transparent"))
  81. . ($element['#value']['opacity'] ? " : " . ($element['#value']['opacity'] * 100) . '%' : '')
  82. . " : " . theme_imagecacheactions_rgb($element['#value']['RGB']);
  83. }
  84. /**
  85. * Given an image, manipulate the transparancy behaviour.
  86. *
  87. * implementation of hook_image()
  88. *
  89. * Either convert light parts of an image to see-through, or place a solid
  90. * colour behind areas that would otherwise be see-though
  91. *
  92. * An imagecache_action_hook() . Handle a pipelined image transformation.
  93. *
  94. * To save a partially transparent image, the image resource must be switched to PNG.
  95. * REMEMBER TO SWITCH IT BACK if needed
  96. *
  97. * @param $image handle on the image definition, including a gd image resource
  98. * to act upon
  99. * @param $data settings for this process.
  100. * @return bool success
  101. */
  102. function imagecache_alpha_image($image, $data = array()) {
  103. if (! $data['flatten']) {
  104. // given an image, convert dark areas to opaque,
  105. // light to transparent,
  106. return png_color2alpha($image, $data['RGB']['HEX'], $data['opacity']);
  107. }
  108. else {
  109. // Do the opposite, flatten the transparency ONTO the given colour
  110. $info = $image->info;
  111. if (!$info) {
  112. watchdog("imagecache", "Problem converting image to fill behind. Source image returned no info");
  113. #dsm($source);
  114. return; // error
  115. }
  116. $base_image = imagecreatetruecolor($info['width'], $info['height']);
  117. imagesavealpha($base_image, TRUE);
  118. imagealphablending($base_image, FALSE);
  119. // Start with a solid colour
  120. $background_rgb = imagecache_actions_hex2rgba($data['RGB']['HEX']);
  121. // Setting the background colour here solid is what flattens the image
  122. $background_color = @imagecolorallocatealpha($base_image, $background_rgb['red'], $background_rgb['green'], $background_rgb['blue'], 0);
  123. // But what I really want to do is set it
  124. // coloured rgb AND 100% transparent, in the hope that
  125. // a failure to render transparency would instead render
  126. // THAT colour.
  127. $background_color = @imagecolorallocatealpha($base_image, $background_rgb['red'], $background_rgb['green'], $background_rgb['blue'], 0);
  128. // But that still doesn't work.
  129. // Yet somehow I've seen transparent images that fallback to white, not silver.
  130. imagefill( $base_image, 0, 0, $background_color );
  131. // And set the overlay behaviour back again
  132. imagealphablending($base_image, TRUE);
  133. // Place the current image over it
  134. $foreground = $image->resource;
  135. $success = imagecopy($base_image, $image->resource, 0, 0, 0, 0, $info['width'], $info['height']);
  136. $image->resource = $base_image;
  137. return TRUE;
  138. }
  139. }
  140. /**
  141. * This achives a tonal effect by converting the images combined tone and
  142. * existing transparency into one shade value. This is then used as the ALPHA
  143. * transparency for that pixel, while the whole thing is coloured the same
  144. * shade. Images 'greytoned' in this manner should sit smoothly on any
  145. * background.
  146. *
  147. * With no color set, use the existing hue.
  148. *
  149. * To save a partially transparent image, the image resource must be switched to PNG.
  150. * ... or maybe not. Just flatten it yourself, or switch the format yourself.
  151. * This hack would produce side effects otherwise.
  152. *
  153. * This algorithm runs maths per-pixel, and therefore is incredibly much more
  154. * inefficient than any native routine. Will kill the server on large images.
  155. *
  156. * @param $opacity between 0 transparent and 1 solid.
  157. */
  158. function png_color2alpha($image, $color, $opacity = NULL) {
  159. $info = $image->info;
  160. if (!$info) {
  161. return FALSE;
  162. }
  163. $im1 = $image->resource;
  164. imagesavealpha($im1, TRUE);
  165. imagealphablending($im1, FALSE);
  166. if ($color) {
  167. $background = imagecache_actions_hex2rgba($color);
  168. }
  169. $width = imagesx($im1);
  170. $height = imagesy($im1);
  171. if (($width * $height) > (1200 * 1200)) {
  172. watchdog('imagecache_actions', __FUNCTION__ . " on {$image->source}. Image is TOO BIG to run the per-pixel algorithm. Aborting.");
  173. return TRUE;
  174. }
  175. for ($i = 0; $i < $height; $i++) {
  176. //this loop traverses each row in the image
  177. for ($j = 0; $j < $width; $j++) {
  178. //this loop traverses each pixel of each row
  179. // Get the color & alpha info of the current pixel.
  180. $retrieved_color = imagecolorat($im1, $j, $i); // an index
  181. $rgba_array = imagecolorsforindex($im1, $retrieved_color);
  182. $alpha = 127;
  183. // Calculate the total shade value of this pixel.
  184. // If the rule sets a color, then the darkness of the existing
  185. // pixel will define the desired alpha value.
  186. if ($color) {
  187. $lightness = ( $rgba_array['red'] + $rgba_array['green'] + $rgba_array['blue'] ) / 3;
  188. // Need to flip the numbers around before doing maths.
  189. #$opacity = 1-($rgba_array['alpha']/127);
  190. #$darkness = 1-($lightness/256); // 0 is white, 1 is black
  191. #$visibility = $darkness * $opacity;
  192. #$alpha = (1-$visibility) * 127;
  193. $alpha = (1 - ((1 -($lightness / 256)) * (1 -($rgba_array['alpha'] / 127)))) * 127;
  194. }
  195. // If color is NOT set, then the existing color is passed though, only
  196. // made somewhat transparent.
  197. if (!$color) {
  198. $background = $rgba_array;
  199. }
  200. if ($opacity) {
  201. // It's a user-defined alpha value
  202. $alpha = $alpha * $opacity;
  203. }
  204. // Paint the pixel.
  205. $color_to_paint = imagecolorallocatealpha($image->resource, $background['red'], $background['green'], $background['blue'], $alpha);
  206. imagesetpixel($image->resource, $j, $i, $color_to_paint);
  207. }
  208. }
  209. return TRUE;
  210. }