transparency.inc 8.7 KB

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