image_overlay.inc 6.6 KB

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