Skip to content

Instantly share code, notes, and snippets.

@staylor
Created December 9, 2012 00:24
Show Gist options
  • Save staylor/4242708 to your computer and use it in GitHub Desktop.
Save staylor/4242708 to your computer and use it in GitHub Desktop.
I am using a script called "test.php" in the root of WordPress with a folder for images called "test-images/*". The original image is called "scott.jpg" - change that value with your image to play along
<?php
error_reporting(-1);
require( 'wp-load.php' );
require_once( ABSPATH . WPINC . '/class-wp-image-editor.php' );
require_once( ABSPATH . WPINC . '/class-wp-image-editor-imagick.php' );
/**
* Tosses Imagick methods up the chain to WP_Image_Editor_Imagick::image.
* WP_Image_Editor_Imagick::image is protected, so you can't act on
* it directly in an instance / there is no getter. Decorator pattern was an
* odd choice here IMO. Probably because its functionality is purposely limited
* to the immediate needs of core. Eh.
*
* Activates the Builder pattern on Imagick methods.
*
* Output the edited image:
* get_cropped()->adaptiveBlurImage(5, 3)->stream();
* Save the edited image:
* get_cropped()->adaptiveBlurImage(5, 3)->write( 'blur', false );
* Save the edited image and then display it in an <img> tag:
* get_cropped()->adaptiveBlurImage(5, 3)->write( 'blur' );
*/
class WP_Imagick_Access extends WP_Image_Editor_Imagick {
function __call( $name, $arguments ) {
call_user_func_array( array( $this->image, $name ), $arguments );
return $this;
}
static function __callStatic( $name, $arguments ) {
call_user_func_array( array( $this->image, $name ), $arguments );
return $this;
}
/**
* This works better when using Imagick method to overload
*
* @param string $mime_type
*/
function stream( $mime_type = 'image/jpeg' ) {
header( "Content-Type: $mime_type" );
echo $this->image;
}
/**
* Save an image and output an <img> tag
*
* @param string $name
* @param boolean $display
* @return \WP_Imagick_Access
*/
function write( $name, $display = true ) {
//if ( ! file_exists( "test-images/$name.jpg" ) )
$this->save( "test-images/$name.jpg" );
if ( $display )
echo '<img src="', home_url( "test-images/$name.jpg" ), '" />';
return $this;
}
}
function add_wp_imagick_access( $editors ) {
array_unshift( $editors, 'WP_Imagick_Access' );
return $editors;
}
add_filter( 'wp_image_editors', 'add_wp_imagick_access' );
/**
* Change size to create new image
*
* @return WP_Image_Editor_Imagick
*/
function get_cropped() {
$size = 250;
if ( ! file_exists( 'test-images/cropped-' . $size . '.jpg' ) ) {
$image = wp_get_image_editor( 'scott.jpg' );
$image->resize( $size, $size, true );
$image->write( 'test-images/cropped-' . $size . '.jpg', false );
}
return wp_get_image_editor( 'test-images/cropped-' . $size . '.jpg' );
}
/**
* Imagick CHANNEL constants
*/
$channel = array();
foreach ( array( 'DEFAULT', 'RED', 'GRAY', 'CYAN', 'GREEN', 'MAGENTA', 'BLUE', 'YELLOW', 'ALPHA', 'OPACITY', 'MATTE', 'BLACK', 'INDEX', 'ALL', 'UNDEFINED' ) as $chan ) {
$channel["imagick::CHANNEL_{$chan}"] = constant( "imagick::CHANNEL_{$chan}" );
}
/**
* Imagick NOISE constants
*/
$noise = array();
foreach ( array( 'UNIFORM', 'GAUSSIAN', 'MULTIPLICATIVEGAUSSIAN', 'IMPULSE', 'LAPLACIAN', 'POISSON', 'RANDOM' ) as $nois ) {
$noise["imagick::NOISE_{$nois}"] = constant( "imagick::NOISE_{$nois}" );
}
/**
* Min / Max floats for generic parameters - still tweaking ideal values for these
*/
$radius = array( 0.5, 50 );
$sigma = array( 0.5, 20 );
$angle = array( 0.0, 360.0 );
$opacity = array( 0.0, 1.0 );
$percentage = array( 0.0, 100.0 );
/**
* Since we operate by convention:
* slug => array( method, array( param1 => array( min, max ), ... ) )
*
* TODO: some of the 0.0 values are wrong, still learning what each method does
*
*/
$map = array(
'adaptive-blur' => array( 'adaptiveBlurImage', array( 'radius' => $radius, 'sigma' => $sigma, 'channel' => $channel ) ),
'adaptive-threshold' => array( 'adaptiveThresholdImage', array( 'width' => 0.0, 'height' => 0.0, 'offset' => 0.0 ) ),
'adaptive-sharpen' => array( 'adaptiveSharpenImage', array( 'radius' => $radius, 'sigma' => $sigma, 'channel' => $channel ) ),
'noise' => array( 'addNoiseImage', array( 'noise_type' => $noise, 'channel' => $channel ) ),
'blur' => array( 'blurImage', array( 'radius' => $radius, 'sigma' => $sigma, 'channel' => $channel ) ),
'charcoal' => array( 'charcoalImage', array( 'radius' => $radius, 'sigma' => $sigma ) ),
'edge' => array( 'edgeImage', array( 'radius' => $radius ) ),
'emboss' => array( 'embossImage', array( 'radius' => $radius, 'sigma' => $sigma ) ),
'median' => array( 'medianFilterImage', array( 'radius' => $radius ) ),
'modulate' => array( 'modulateImage', array( 'brightness' => $percentage , 'saturation' => $percentage, 'hue' => $percentage ) ),
'motion' => array( 'motionBlurImage', array( 'radius' => $radius, 'sigma' => $sigma, 'angle' => $angle, 'channel' => $channel ) ),
'normalize' => array( 'normalizeImage', array( 'channel' => $channel ) ),
'oilpaint' => array( 'oilPaintImage', array( 'radius' => $radius ) ),
'radial-blur' => array( 'radialBlurImage', array( 'angle' => $angle, 'channel' => $channel ) ),
'random-threshold' => array( 'randomThresholdImage', array( 'low' => 0.0, 'high' => 0.0, 'channel' => $channel ) ),
'reduce-noise' => array( 'reduceNoiseImage', array( 'radius' => $radius ) ),
'resample' => array( 'resampleImage', array( 'x_resolution' => 0.0, 'y_resolution' => 0.0, 'filter' => 0.0 , 'blur' => 0.0 ) ),
'sepia' => array( 'sepiaToneImage', array( 'threshold' => $percentage ) ),
'shade' => array( 'shadeImage', array( 'gray' => $percentage, 'azimuth' => $percentage, 'elevation' => $percentage ) ),
'sharpen' => array( 'sharpenImage', array( 'opacity' => $opacity, 'sigma' => $sigma, 'x' => 0.0, 'y' => 0.0 ) ),
'sketch' => array( 'sketchImage', array( 'radius' => $radius, 'sigma' => $sigma, 'angle' => $angle ) ),
'solarize' => array( 'solarizeImage', array( 'threshold' => 0.0 ) ),
'spread' => array( 'spreadImage', array( 'radius' => $radius ) ),
'swirl' => array( 'swirlImage', array( 'degrees' => $angle ) ),
'tint' => array( 'tintImage', array( 'tint' => $percentage, 'opacity' => $opacity ) ),
'unsharp' => array( 'unsharpMaskImage', array( 'radius' => $radius, 'sigma' => $sigma, 'amount' => 0.0, 'threshold' => 0.0, 'channel' => $channel ) ),
'vignette' => array( 'vignetteImage', array( 'black' => $percentage, 'white' => $percentage, 'x' => 0.0, 'y' => 0.0 ) ),
'wave' => array( 'waveImage', array( 'amplitude' => $percentage, 'length' => 0.0 ) ),
);
/**
* Handle a request to resample the cropped image
*
*/
if ( isset( $_GET['slug'] ) ) {
list( $method, $params ) = $map[$_GET['slug']];
$args = array();
foreach ( $params as $arg => $default ) {
if ( isset( $_GET[$arg] ) ) {
$passed = $_GET[$arg];
if ( is_array( $default ) && empty( $passed ) )
$passed = reset( $default );
if ( is_int( $passed ) || in_array( $arg, array( 'channel', 'noise_type' ) ) )
$cast = intval( $passed );
else
$cast = floatval( $passed );
$args[$arg] = $cast;
} else {
if ( is_array( $default ) )
$args[$arg] = reset( $default );
else
$args[$arg] = $default;
}
}
$obj = get_cropped();
/**
* Depending on how cray you get, you might run into a memory alloc fatal
* if out of sync params are passed (working on it!)
*/
try {
call_user_func_array( array( $obj, $method ), $args );
} catch (Exception $e) {
$obj = get_cropped();
}
$obj->stream();
exit();
}
/** end resampling */
/**
* UI for ImageMagick methods
*
*/
wp_register_style( 'jquery-ui', '/test-images/jquery-ui-1.9.2.custom.min.css' );
wp_print_styles( array( 'jquery-ui' ) );
wp_print_scripts( array( 'jquery-ui-slider' ) );
/**
* Inline Styles
*
*/
?>
<style>
* {margin: 0; padding: 0}
body {font-family: helvetica; padding: 20px; font-size: 12px}
h3 {margin: 10px 0; font-family: monospace; font-weight: normal}
#images {overflow: hidden}
.image {float: left; margin: 0 10px 10px 0; width: 250px; height: 470px; margin-bottom: 20px}
.image-frame {display: block; width: 250px; height: 250px; overflow: hidden; background: #eee}
img {display: block}
.slider, select {display: block; margin: 10px; width: 85%}
.reset-me {color: red; cursor: pointer; text-decoration: underline}
.ui-slider .ui-slider-handle {widht: 0.9em; height: 0.9em}
.ui-slider-horizontal {height: 0.6em}
</style>
<script type="text/javascript">
/**
* UI event handlers
*
*/
(function ($) {
var locked = false;
window.resample = function (event) {
if (locked)
return;
var elem = $(event.target), wrap, query, sliders = $('.slider');
sliders.slider({disabled: true});
wrap = elem.closest('.image');
query = {slug: wrap.prop('id')};
wrap.find('.slider').map(function (index, slider) {
slider = $(slider);
slider.prev('label').find('span').text(slider.slider('value'));
query[slider.data('name')] = slider.slider('value');
});
wrap.find('select').map(function (index, widg) {
widg = $(widg);
query[widg.prop('name')] = widg.val();
});
/**
* Sigma can't be greater than radius, also blows up when 0
*/
if (query.sigma && query.radius && query.sigma > query.radius) {
query.sigma = query.radius;
wrap.find('.slider[data-name="sigma"]').slider('value', query.radius);
}
/**
* Replace the image with the URL for the altered one
*/
wrap.find('img').replaceWith($('<img/>').attr({src: '/test.php?' + $.param(query)}));
sliders.slider({disabled: false});
};
/**
* Reload cropped image and reset sliders
*/
window.reset_me = function (e) {
locked = true;
var wrap = $(e.target).closest('.image');
wrap.find('.slider').slider('value', 0);
wrap.find('select').val('');
wrap.find('img').replaceWith($('<img/>').attr({src: '/test-images/cropped-250.jpg'}));
locked = false;
};
}(jQuery));
</script>
<div id="images">
<?php
foreach ( $map as $slug => $data ):
list( $method, $params ) = $data;
?>
<div class="image" id="<?php echo $slug ?>">
<div class="image-frame">
<img src="http://wordpress-core/test-images/cropped-250.jpg"/>
</div>
<h3>Imagick::<?php echo $method ?></h3>
<?php
/**
* Values are not dynamic
*/
foreach ( $params as $key => $default ):
if ( in_array( $key, array( 'channel', 'noise_type' ) ) ): ?>
<label><?php echo ucwords( str_replace( '_', ' ', $key ) ) ?></label>
<select name="<?php echo $key ?>">
<?php
foreach ( $default as $const => $value ): ?>
<option value="<?php echo $value ?>"><?php echo $const ?></option>
<?php
endforeach;
?>
</select>
<?php
/**
* Output minimum for jQuery UI slider
*/
else:
?>
<label><?php echo ucwords( $key ) ?> <span></span></label>
<div class="slider <?php echo $key ?>" data-name="<?php echo $key ?>"></div>
<?php
endif;
endforeach ?>
<a class="reset-me">Reset</a>
<script type="text/javascript">
jQuery(function ($) {
<?php
/**
* For now, output JS inline for each item
* Convert max / min to padded int - will be coverted to float later
*
*/
foreach ( $params as $key => $default ):
if ( in_array( $key, array( 'channel', 'noise_type' ) ) )
continue;
if ( is_array( $default ) ):
$start = reset( $default );
$end = end( $default );
?>
var elem = $('#<?php echo $slug, ' .', $key ?>'), params = {
animate: true,
min: <?php echo $start ?>,
max: <?php echo $end ?>,
stop : resample,
step : 1
};
elem.slider(params);
<?php
else:
/**
* These are the 0.0 values, these may sorta work for some params
* but need appropriate default ranges later
*/
?>
var elem = $('#<?php echo $slug, ' .', $key ?>'), params = {
animate: true,
min : <?php echo $default ?>,
max : 800,
stop : resample,
step : 1
};
elem.slider(params);
<?php
endif;
endforeach ?>
});
</script>
</div>
<?php endforeach ?>
</div>
<script type="text/javascript">
/**
* Generical handlers
*/
jQuery(function ($) {
$('select').change(resample);
$('.reset-me').click(reset_me);
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment