diff options
-rw-r--r-- | admin/derivatives.php | 97 | ||||
-rw-r--r-- | admin/include/image.class.php | 104 | ||||
-rw-r--r-- | admin/themes/default/template/derivatives.tpl | 61 | ||||
-rw-r--r-- | i.php | 43 | ||||
-rw-r--r-- | include/derivative_params.inc.php | 5 | ||||
-rw-r--r-- | include/derivative_std_params.inc.php | 43 | ||||
-rw-r--r-- | themes/default/watermarks/Owned_Stamp.png | bin | 0 -> 42282 bytes | |||
-rw-r--r-- | themes/default/watermarks/Sample.png | bin | 0 -> 21395 bytes |
8 files changed, 340 insertions, 13 deletions
diff --git a/admin/derivatives.php b/admin/derivatives.php index fbf1ccffb..fde3e0db3 100644 --- a/admin/derivatives.php +++ b/admin/derivatives.php @@ -28,6 +28,7 @@ $errors = array(); if ( isset($_POST['d']) ) { $pderivatives = $_POST['d']; + $pwatermark = $_POST['w']; // step 1 - sanitize HTML input foreach($pderivatives as $type => &$pderivative) @@ -86,10 +87,55 @@ if ( isset($_POST['d']) ) $prev_w = intval($pderivative['w']); $prev_h = intval($pderivative['h']); } + + $v = intval($pderivative['sharpen']); + if ($v<0 || $v>100) + { + $errors[$type]['sharpen'] = '[0..100]'; + } + $v = intval($pderivative['quality']); + if ($v<=0 || $v>100) + { + $errors[$type]['quality'] = '(0..100]'; + } + } + $v = intval($pwatermark['xpos']); + if ($v<0 || $v>100) + { + $errors['watermark']['xpos'] = '[0..100]'; } + $v = intval($pwatermark['ypos']); + if ($v<0 || $v>100) + { + $errors['watermark']['ypos'] = '[0..100]'; + } + $v = intval($pwatermark['opacity']); + if ($v<=0 || $v>100) + { + $errors['watermark']['opacity'] = '(0..100]'; + } + + // step 3 - save data if (count($errors)==0) { + $watermark = new WatermarkParams(); + $watermark->file = $pwatermark['file']; + $watermark->xpos = intval($pwatermark['xpos']); + $watermark->ypos = intval($pwatermark['ypos']); + $watermark->xrepeat = intval($pwatermark['xrepeat']); + $watermark->opacity = intval($pwatermark['opacity']); + $watermark->min_size = array(intval($pwatermark['minw']),intval($pwatermark['minh'])); + + $old_watermark = ImageStdParams::get_watermark(); + $watermark_changed = + $watermark->file != $old_watermark->file + || $watermark->xpos != $old_watermark->xpos + || $watermark->ypos != $old_watermark->ypos + || $watermark->xrepeat != $old_watermark->xrepeat + || $watermark->opacity != $old_watermark->opacity; + ImageStdParams::set_watermark($watermark); + $enabled = ImageStdParams::get_defined_type_map(); $disabled = @unserialize( @$conf['disabled_derivatives'] ); if ($disabled===false) @@ -106,11 +152,15 @@ if ( isset($_POST['d']) ) { $new_params = new DerivativeParams( new SizingParams( - array($pderivative['w'],$pderivative['h']), + array(intval($pderivative['w']), intval($pderivative['h'])), round($pderivative['crop'] / 100, 2), - array($pderivative['minw'],$pderivative['minh']) + array(intval($pderivative['minw']), intval($pderivative['minh'])) ) ); + $new_params->sharpen = intval($pderivative['sharpen']); + $new_params->quality = intval($pderivative['quality']); + ImageStdParams::apply_global($new_params); + if (isset($enabled[$type])) { $old_params = $enabled[$type]; @@ -126,6 +176,22 @@ if ( isset($_POST['d']) ) { $same = false; } + + if ( $same && + ( $new_params->sharpen != $old_params->sharpen + || $new_params->quality > $old_params->quality) + ) + { + $same = false; + } + + if ($same && + ( $new_params->use_watermark != $old_params->use_watermark + || $new_params->use_watermark && $watermark_changed ) + ) + { + $same = false; + } if (!$same) { @@ -182,6 +248,7 @@ if ( isset($_POST['d']) ) else { $template->assign('derivatives', $pderivatives); + $template->assign('watermark', $pwatermark); $template->assign('ferrors', $errors); } } @@ -224,11 +291,37 @@ if (count($errors)==0) { $tpl_var['minw'] = $tpl_var['minh'] = ""; } + $tpl_var['sharpen'] = $params->sharpen; + $tpl_var['quality'] = $params->quality; } $tpl_vars[$type]=$tpl_var; } $template->assign('derivatives', $tpl_vars); + + $wm = ImageStdParams::get_watermark(); + $template->assign('watermark', array( + 'file' => $wm->file, + 'minw' => $wm->min_size[0], + 'minh' => $wm->min_size[1], + 'xpos' => $wm->xpos, + 'ypos' => $wm->ypos, + 'xrepeat' => $wm->xrepeat, + 'opacity' => $wm->opacity, + )); +} + +$watermark_files = array(); +foreach (glob(PHPWG_ROOT_PATH.'themes/default/watermarks/*.png') as $file) +{ + $watermark_files[] = substr($file, strlen(PHPWG_ROOT_PATH)); +} +$watermark_filemap = array( '' => '---' ); +foreach( $watermark_files as $file) +{ + $display = basename($file); + $watermark_filemap[$file] = $display; } +$template->assign('watermark_files', $watermark_filemap); $template->set_filename('derivatives', 'derivatives.tpl'); $template->assign_var_from_handle('ADMIN_CONTENT', 'derivatives'); diff --git a/admin/include/image.class.php b/admin/include/image.class.php index 53a27b924..03cf419a4 100644 --- a/admin/include/image.class.php +++ b/admin/include/image.class.php @@ -41,6 +41,10 @@ interface imageInterface function rotate($rotation); function resize($width, $height); + + function sharpen($amount); + + function compose($overlay, $x, $y, $opacity); function write($destination_filepath); } @@ -258,6 +262,31 @@ class pwg_image return $rotation; } + /** Returns a normalized convolution kernel for sharpening*/ + static function get_sharpen_matrix($amount) + { + // Amount should be in the range of 18-10 + $amount = round(abs(-18 + ($amount * 0.08)), 2); + + $matrix = array + ( + array(-1, -1, -1), + array(-1, $amount, -1), + array(-1, -1, -1), + ); + + $norm = array_sum(array_map('array_sum', $matrix)); + + for ($i=0; $i<3; $i++) + { + $line = & $matrix[$i]; + for ($j=0; $j<3; $j++) + $line[$j] /= $norm; + } + + return $matrix; + } + private function get_resize_result($destination_filepath, $width, $height, $time=null) { return array( @@ -397,6 +426,18 @@ class image_imagick implements imageInterface return $this->image->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 0.9); } + function sharpen($amount) + { + $m = pwg_image::get_sharpen_matrix($amount); + return $this->image->convolveImage($m); + } + + function compose($overlay, $x, $y, $opacity) + { + // todo + return false; + } + function write($destination_filepath) { return $this->image->writeImage($destination_filepath); @@ -415,16 +456,17 @@ class image_ext_imagick implements imageInterface var $height = ''; var $commands = array(); - function __construct($source_filepath, $imagickdir='') + function __construct($source_filepath) { + global $conf; $this->source_filepath = $source_filepath; - $this->imagickdir = $imagickdir; + $this->imagickdir = $conf['ext_imagick_dir']; - $command = $imagickdir.'identify -format "%wx%h" "'.realpath($source_filepath).'"'; + $command = $this->imagickdir.'identify -format "%wx%h" "'.realpath($source_filepath).'"'; @exec($command, $returnarray); if(!is_array($returnarray) or empty($returnarray[0]) or !preg_match('/^(\d+)x(\d+)$/', $returnarray[0], $match)) { - die("[External ImageMagick] Corrupt image"); + die("[External ImageMagick] Corrupt image\n" . var_export($returnarray, true)); } $this->width = $match[1]; @@ -479,6 +521,31 @@ class image_ext_imagick implements imageInterface return true; } + function sharpen($amount) + { + $m = pwg_image::get_sharpen_matrix($amount); + + $param ='convolve "'.count($m).':'; + foreach ($m as $line) + { + $param .= ' '; + $param .= implode(',', $line); + } + $param .= '"'; + $this->add_command('morphology', $param); + return true; + } + + function compose($overlay, $x, $y, $opacity) + { + $param = 'compose dissolve -define compose:args='.$opacity; + $param .= ' '.escapeshellarg(realpath($overlay->image->source_filepath)); + $param .= ' -gravity NorthWest -geometry +'.$x.'+'.$y; + $param .= ' -composite'; + $this->add_command($param); + return true; + } + function write($destination_filepath) { $exec = $this->imagickdir.'convert'; @@ -496,6 +563,8 @@ class image_ext_imagick implements imageInterface $dest = pathinfo($destination_filepath); $exec .= ' "'.realpath($dest['dirname']).'/'.$dest['basename'].'"'; @exec($exec, $returnarray); + + //echo($exec); return is_array($returnarray); } } @@ -611,6 +680,33 @@ class image_gd implements imageInterface return $result; } + function sharpen($amount) + { + $m = pwg_image::get_sharpen_matrix($amount); + return imageconvolution($this->image, $m, 1, 0); + } + + function compose($overlay, $x, $y, $opacity) + { + $ioverlay = $overlay->image->image; + /* A replacement for php's imagecopymerge() function that supports the alpha channel + See php bug #23815: http://bugs.php.net/bug.php?id=23815 */ + + $ow = imagesx($ioverlay); + $oh = imagesy($ioverlay); + + // Create a new blank image the site of our source image + $cut = imagecreatetruecolor($ow, $oh); + + // Copy the blank image into the destination image where the source goes + imagecopy($cut, $this->image, 0, 0, $x, $y, $ow, $oh); + + // Place the source image in the destination image + imagecopy($cut, $ioverlay, 0, 0, 0, 0, $ow, $oh); + imagecopymerge($this->image, $cut, $x, $y, 0, 0, $ow, $oh, $opacity); + return true; + } + function write($destination_filepath) { $extension = strtolower(get_extension($destination_filepath)); diff --git a/admin/themes/default/template/derivatives.tpl b/admin/themes/default/template/derivatives.tpl index e0796024b..59f6344a7 100644 --- a/admin/themes/default/template/derivatives.tpl +++ b/admin/themes/default/template/derivatives.tpl @@ -23,6 +23,42 @@ {/literal}{/html_head} <form method="post" id="derviativesForm"> +<fieldset> +<legend>{'Watermark'|@translate}</legend> + + +<select name="w[file]" id="wSelect"> + {html_options options=$watermark_files selected=$watermark.file} +</select> + +<p><img id="wImg"></img></p> + +<label>{'Min Width'|@translate} + <input type="text" name="w[minw]" value="{$watermark.minw}"{if isset($ferrors.watermark.minw)}class="dError"{/if}> +</label> + +<label>{'Min Height'|@translate} + <input type="text" name="w[minh]" value="{$watermark.minh}"{if isset($ferrors.watermark.minh)}class="dError"{/if}> +</label> + +<label>{'X Position'|@translate} + <input type="text" name="w[xpos]" value="{$watermark.xpos}"{if isset($ferrors.watermark.xpos)}class="dError"{/if}> +%</label> + +<label>{'Y Position'|@translate} + <input type="text" name="w[ypos]" value="{$watermark.ypos}"{if isset($ferrors.watermark.ypos)}class="dError"{/if}> +%</label> + +<label>{'X Repeat'|@translate} + <input type="text" name="w[xrepeat]" value="{$watermark.xrepeat}"{if isset($ferrors.watermark.xrepeat)}class="dError"{/if}> +</label> + +<label>{'Opacity'|@translate} + <input type="text" name="w[opacity]" value="{$watermark.opacity}"{if isset($ferrors.watermark.opacity)}class="dError"{/if}> +</label> + +</fieldset> + <table class="table2"> <thead> <tr> @@ -33,6 +69,8 @@ <td>{'Crop'|@translate} (%)</td> <td>{'Min Width'|@translate}</td> <td>{'Min Height'|@translate}</td> + <td>{'Sharpen'|@translate} (%)</td> + <td>{'Quality'|@translate} (%)</td> </tr> </thead> {foreach from=$derivatives item=d key=type} @@ -65,7 +103,14 @@ <input type="text" name="d[{$type}][minh]" value="{$d.minh}"{if isset($ferrors.$type.minh)}class="dError"{/if}> {if isset($ferrors.$type.minh)}<span class="dErrorDesc" title="{$ferrors.$type.minh}">!</span>{/if} {/if}</td> - + <td> + <input type="text" name="d[{$type}][sharpen]" value="{$d.sharpen}"{if isset($ferrors.$type.sharpen)}class="dError"{/if}> + {if isset($ferrors.$type.sharpen)}<span class="dErrorDesc" title="{$ferrors.$type.sharpen}">!</span>{/if} + </td> + <td> + <input type="text" name="d[{$type}][quality]" value="{$d.quality}"{if isset($ferrors.$type.quality)}class="dError"{/if}> + {if isset($ferrors.$type.quality)}<span class="dErrorDesc" title="{$ferrors.$type.quality}">!</span>{/if} + </td> </tr> {/foreach} </table> @@ -76,4 +121,18 @@ jQuery(".dError").bind("focus", function () { jQuery(this).removeClass("dError"); } ); + +function onWatermarkChange() +{ + var val = jQuery("#wSelect").val(); + if (val.length) { + jQuery("#wImg").attr('src', {/literal}'{$ROOT_URL}'{literal}+val).show(); + } + else { + jQuery("#wImg").hide(); + } +} + +onWatermarkChange(); +jQuery("#wSelect").bind("change", onWatermarkChange ); {/literal}{/footer_script}
\ No newline at end of file @@ -269,6 +269,10 @@ if (!$need_generate) include_once(PHPWG_ROOT_PATH . 'admin/include/image.class.php'); + +ignore_user_abort(true); +set_time_limit(0); + $image = new pwg_image($page['src_path']); if (!mkgetdir(dirname($page['derivative_path']))) @@ -281,17 +285,49 @@ $changes = 0; // todo rotate // Crop & scale -$params->sizing->compute( array($image->get_width(),$image->get_height()), $page['coi'], $crop_rect, $scale_width ); +$o_size = $d_size = array($image->get_width(),$image->get_height()); +$params->sizing->compute($o_size , $page['coi'], $crop_rect, $scaled_size ); if ($crop_rect) { $changes++; $image->crop( $crop_rect->width(), $crop_rect->height(), $crop_rect->l, $crop_rect->t); } -if ($scale_width) +if ($scaled_size) { $changes++; - $image->resize( $scale_width[0], $scale_width[1] ); + $image->resize( $scaled_size[0], $scaled_size[1] ); + $d_size = $scaled_size; +} + +if ($params->sharpen) +{ + $changes += $image->sharpen( $params->sharpen ); +} + +if ($params->use_watermark) +{ + $wm = ImageStdParams::get_watermark(); + $wm_image = new pwg_image(PHPWG_ROOT_PATH.$wm->file); + $wm_size = array($wm_image->get_width(),$wm_image->get_height()); + if ($d_size[0]<$wm_size[0] or $d_size[1]<$wm_size[1]) + { + $wm_scaling_params = SizingParams::classic($d_size[0], $d_size[1]); + $wm_scaling_params->compute($wm_size, null, $tmp, $wm_scaled_size); + $wm_size = $wm_scaled_size; + $wm_image->resize( $wm_scaled_size[0], $wm_scaled_size[1] ); + } + $x = round( ($wm->xpos/100)*($d_size[0]-$wm_size[0]) ); + $y = round( ($wm->ypos/100)*($d_size[1]-$wm_size[1]) ); + if ($image->compose($wm_image, $x, $y, $wm->opacity)) + { + $changes++; + if ($wm->xrepeat) + { + // todo + } + } + $wm_image->destroy(); } // no change required - redirect to source @@ -301,6 +337,7 @@ if (!$changes) ierror( $page['src_url'], 301); } +$image->set_compression_quality( $params->quality ); $image->write( $page['derivative_path'] ); $image->destroy(); diff --git a/include/derivative_params.inc.php b/include/derivative_params.inc.php index 42bdfb7d5..be28b6589 100644 --- a/include/derivative_params.inc.php +++ b/include/derivative_params.inc.php @@ -282,7 +282,10 @@ final class DerivativeParams { public $type = IMG_CUSTOM; public $last_mod_time = 0; // used for non-custom images to regenerate the cached files + public $use_watermark = false; public $sizing; + public $sharpen = 0; + public $quality = 85; function __construct($sizing) { @@ -291,7 +294,7 @@ final class DerivativeParams public function __sleep() { - return array('last_mod_time', 'sizing'); + return array('last_mod_time', 'sizing', 'sharpen', 'quality'); } function add_url_tokens(&$tokens) diff --git a/include/derivative_std_params.inc.php b/include/derivative_std_params.inc.php index 41056cadf..9377b4c67 100644 --- a/include/derivative_std_params.inc.php +++ b/include/derivative_std_params.inc.php @@ -28,12 +28,24 @@ define('IMG_XLARGE', 'xlarge'); define('IMG_XXLARGE', 'xxlarge'); define('IMG_CUSTOM', 'custom'); +final class WatermarkParams +{ + public $file = ''; + public $min_size = array(500,500); + public $xpos = 50; + public $ypos = 50; + public $xrepeat = 0; + public $opacity = 100; +} + + final class ImageStdParams { private static $all_types = array(IMG_SQUARE,IMG_THUMB,IMG_SMALL,IMG_MEDIUM,IMG_LARGE,IMG_XLARGE,IMG_XXLARGE); private static $all_type_map = array(); private static $type_map = array(); private static $undefined_type_map = array(); + private static $watermark; static function get_all_types() { @@ -60,6 +72,11 @@ final class ImageStdParams return self::$all_type_map[$type]; } + static function get_watermark() + { + return self::$watermark; + } + static function load_from_db() { global $conf; @@ -67,6 +84,8 @@ final class ImageStdParams if (false!==$arr) { self::$type_map = $arr['d']; + self::$watermark = @$arr['w']; + if (!self::$watermark) self::$watermark = new WatermarkParams(); } else { @@ -82,6 +101,8 @@ final class ImageStdParams if (false!==$arr) { self::$type_map = $arr['d']; + self::$watermark = @$arr['w']; + if (!self::$watermark) self::$watermark = new WatermarkParams(); } else { @@ -90,21 +111,28 @@ final class ImageStdParams self::build_maps(); } + static function set_watermark($watermark) + { + self::$watermark = $watermark; + } + static function set_and_save($map) { global $conf; self::$type_map = $map; $ser = serialize( array( - 'd' => self::$type_map + 'd' => self::$type_map, + 'w' => self::$watermark, ) ); conf_update_param('derivatives', addslashes($ser) ); file_put_contents(PHPWG_ROOT_PATH.$conf['data_location'].'derivatives.dat', $ser); self::build_maps(); } - static function make_default() + private static function make_default() { + self::$watermark = new WatermarkParams(); self::$type_map[IMG_SQUARE] = new DerivativeParams( SizingParams::square(100,100) ); self::$type_map[IMG_THUMB] = new DerivativeParams( SizingParams::classic(144,144) ); self::$type_map[IMG_SMALL] = new DerivativeParams( SizingParams::classic(240,240) ); @@ -114,11 +142,22 @@ final class ImageStdParams self::$type_map[IMG_XXLARGE] = new DerivativeParams( SizingParams::classic(1200,900) ); } + public static function apply_global($params) + { + if (!empty(self::$watermark->file) && + (self::$watermark->min_size[0]<=$params->sizing->ideal_size[0] + && self::$watermark->min_size[1]<=$params->sizing->ideal_size[1] ) ) + { + $params->use_watermark = true; + } + } + private static function build_maps() { foreach (self::$type_map as $type=>$params) { $params->type = $type; + self::apply_global($params); } self::$all_type_map = self::$type_map; diff --git a/themes/default/watermarks/Owned_Stamp.png b/themes/default/watermarks/Owned_Stamp.png Binary files differnew file mode 100644 index 000000000..1e893440a --- /dev/null +++ b/themes/default/watermarks/Owned_Stamp.png diff --git a/themes/default/watermarks/Sample.png b/themes/default/watermarks/Sample.png Binary files differnew file mode 100644 index 000000000..d976199ed --- /dev/null +++ b/themes/default/watermarks/Sample.png |