image_overlay.inc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <?php
  2. /**
  3. * @file extension to imageapi, provide an overlay action for blending two
  4. * layers, preserving transparency.
  5. */
  6. // Not sure where this library will live between upgrade versions.
  7. // Be careful to not conflict with another copy of myself.
  8. if (! function_exists('image_overlay')) {
  9. /**
  10. * Place one image over another
  11. *
  12. * @param object $image
  13. * An image object.
  14. * @param object $overlay
  15. * An image object.
  16. * @param $x
  17. * Position of the overlay
  18. * @param $y
  19. * Position of the overlay
  20. * @param $alpha
  21. * Transparency of the overlay from 0-100. 0 is totally transparent. 100
  22. * (default) is totally opaque.
  23. * @param $reverse
  24. * BOOL flag to indicate the 'overlay' actually goes under the image. As
  25. * the imageapi callbacks modify the $image object by reference, this is needed
  26. * to replace the old image resource with the new one.
  27. * @return bool success
  28. *
  29. * @ingroup imageapi
  30. */
  31. function image_overlay($image, &$layer, $x, $y, $alpha = 100, $reverse = FALSE) {
  32. if ($reverse) {
  33. $x = imagecache_actions_keyword_filter($x, $layer->info['width'], $image->info['width']);
  34. $y = imagecache_actions_keyword_filter($y, $layer->info['height'], $image->info['height']);
  35. }
  36. else {
  37. $x = imagecache_actions_keyword_filter($x, $image->info['width'], $layer->info['width']);
  38. $y = imagecache_actions_keyword_filter($y, $image->info['height'], $layer->info['height']);
  39. }
  40. return image_toolkit_invoke('overlay', $image, array($layer, $x, $y, $alpha, $reverse));
  41. }
  42. /**
  43. * Place one image over another
  44. * This modifies the passed image by reference
  45. *
  46. * This func is nominated for inclusion in imageapi package. Until then, we do
  47. * it ourselves.
  48. *
  49. * NOTE that the PHP libraries are not great at merging images SO we include a
  50. * library that does it pixel-by-pixel which is INCREDIBLY inefficient. If this
  51. * can be improved, in a way that supports all transparency, please let us know!
  52. *
  53. * A watermark is layer onto image, return the image. An underlay is image onto
  54. * layer, return the layer. Almost identical, but seeing as we work with
  55. * resource handles, the handle needs to be swapped before returning.
  56. *
  57. * @ingroup imageapi
  58. * @param $image
  59. * Base imageapi object.
  60. * @param $overlay
  61. * May be a filename or an imageAPI object
  62. * @param $x
  63. * Position of the overlay
  64. * @param $y
  65. * Position of the overlay
  66. * @param $alpha
  67. * Transparency of the overlay from 0-100. 0 is totally transparent. 100
  68. * (default) is totally opaque.
  69. * @param $reverse
  70. * BOOL flag to indicate the 'overlay' actually goes under the image. As
  71. * the imageapi callbacks modify the $image object by reference, this is needed
  72. * to replace the old image resource with the new one.
  73. * @return bool success
  74. */
  75. function image_gd_overlay($image, $layer, $x, $y, $alpha = 100, $reverse = FALSE) {
  76. if (empty($layer->resource)) {
  77. trigger_error("Invalid input to " . __FUNCTION__ . " 'layer' is not a valid resource");
  78. #dpm($layer);
  79. return FALSE;
  80. }
  81. // If the given alpha is 100%, we can use imagecopy - which actually works,
  82. // Is more efficient, and seems to retain the overlays partial transparancy
  83. // Still does not work great for indexed gifs though?
  84. if ($reverse) {
  85. $upper = &$image;
  86. $lower = &$layer;
  87. }
  88. else {
  89. $upper = &$layer;
  90. $lower = &$image;
  91. }
  92. if ($alpha == 100 && ($upper->info['mime_type'] != 'image/gif')) {
  93. imagealphablending($lower->resource, TRUE);
  94. imagesavealpha($lower->resource, TRUE);
  95. imagealphablending($upper->resource, TRUE);
  96. imagesavealpha($upper->resource, TRUE);
  97. imagecopy($lower->resource, $upper->resource, $x, $y, 0, 0, $upper->info['width'], $upper->info['height']);
  98. imagedestroy($upper->resource);
  99. $image->resource = $lower->resource;
  100. $image->info = $lower->info;
  101. }
  102. else {
  103. // Else imagecopymerge fails and we have to use the slow library.
  104. module_load_include('inc', 'imagecache_actions', 'watermark');
  105. $watermark = new watermark();
  106. $result_img = $watermark->create_watermark($lower->resource, $upper->resource, $x, $y, $alpha);
  107. // Watermark creates a new image resource, so clean up both old images.
  108. imagedestroy($lower->resource);
  109. imagedestroy($upper->resource);
  110. $image->resource = $result_img;
  111. $image->info = $lower->info;
  112. }
  113. return TRUE;
  114. }
  115. /**
  116. * Improvements on this are welcomed!
  117. *
  118. * Please be aware of the limitations of imagemagick libraries out there - the
  119. * versions distributed on hosted servers (if any) are often several years
  120. * behind. Using the latest imagemagick release features will make this function
  121. * unusable in real deployments.
  122. *
  123. * @param $image
  124. * Base imageapi object.
  125. * @param $layer
  126. * May be a filename or an imageAPI object, gets placed on top
  127. * If using reverse, this is going to be the result we carry on working with.
  128. */
  129. function image_imagemagick_overlay($image, $layer, $x = 0, $y = 0, $alpha = 100, $reverse = FALSE) {
  130. // In imagemagick terms:
  131. // - $image is the destination (the image being constructed)
  132. // - $layer is the source (the source of the current operation)
  133. // Add the layer image to the imagemagick command line.
  134. $image->ops[] = escapeshellarg($layer->source) . ' ';
  135. // Set its offset. Offset arguments require a sign in front.
  136. if ($x >= 0) {
  137. $x = "+$x";
  138. }
  139. if ($y >= 0) {
  140. $y = "+$y";
  141. }
  142. $image->ops[] = " -geometry $x$y";
  143. // And compose it with the destination.
  144. if ($alpha == 100) {
  145. // Lay one image over the other. The transparency channel of the upper
  146. // image and/or its dimensions (being smaller than the lower image) will
  147. // determine what remains visible of the lower image).
  148. //
  149. // Note: In explicitly setting a -compose operator we reset/overwrite any
  150. // previously set one (former versions could produce erroneous results
  151. // in combination with other effects before this one).
  152. if ($reverse) {
  153. $compose_operator = 'dst-over';
  154. }
  155. else {
  156. $compose_operator = 'src-over';
  157. }
  158. $image->ops[] = "-compose $compose_operator -composite ";
  159. }
  160. else {
  161. // Alpha is not 100, so this image effect turns into a blend operation.
  162. // The alpha determines what percentage of the upper image pixel will be
  163. // taken. From the lower image pixel, 100 - alpha percent will be taken.
  164. //
  165. // Note 1: I'm not sure if and how transparency of one or both images is
  166. // used in or after the blend operation.
  167. // Note 2: As of IM v6.5.3-4 (around june 2009) we can use:
  168. // -compose blend -define compose:args=30[,70]
  169. $image->ops[] = "-compose blend -define compose:args=$alpha -composite ";
  170. }
  171. return TRUE;
  172. }
  173. }