From: garvinhicking Date: Fri, 16 Jun 2006 18:21:45 +0000 (+0000) Subject: Crop images via JavaScript. HIGHLY experimental. X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=034bb84502b1485e4a1f3682e9b764f4793c020f;p=s9y.git Crop images via JavaScript. HIGHLY experimental. --- diff --git a/bundled-libs/dragdrop.js b/bundled-libs/dragdrop.js index ffc4e73..567b240 100644 --- a/bundled-libs/dragdrop.js +++ b/bundled-libs/dragdrop.js @@ -673,4 +673,121 @@ function pluginMovergetSortEvent() { order = document.getElementById("eventorder"); order.value = DragDrop.serData('g1', null); return order.value; -} \ No newline at end of file +} + +/************************************************** + * dom-drag.js + * 09.25.2001 + * www.youngpup.net + ************************************************** + * 10.28.2001 - fixed minor bug where events + * sometimes fired off the handle, not the root. + **************************************************/ + +var DOMDrag = { + + obj : null, + + init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper) { + o.onmousedown = DOMDrag.start; + + o.hmode = bSwapHorzRef ? false : true ; + o.vmode = bSwapVertRef ? false : true ; + + o.root = oRoot && oRoot != null ? oRoot : o ; + + if (o.hmode && isNaN(parseInt(o.root.style.left ))) o.root.style.left = "0px"; + if (o.vmode && isNaN(parseInt(o.root.style.top ))) o.root.style.top = "0px"; + if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right = "0px"; + if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px"; + + o.minX = typeof minX != 'undefined' ? minX : null; + o.minY = typeof minY != 'undefined' ? minY : null; + o.max = typeof maxX != 'undefined' ? maxX : null; + o.maxY = typeof maxY != 'undefined' ? maxY : null; + + o.xMapper = fXMapper ? fXMapper : null; + o.yMapper = fYMapper ? fYMapper : null; + + o.root.onDragStart = new Function(); + o.root.onDragEnd = new Function(); + o.root.onDrag = new Function(); + }, + + start : function(e) { + var o = DOMDrag.obj = this; + e = DOMDrag.fixE(e); + var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom); + var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right ); + o.root.onDragStart(x, y); + + o.lastMouseX = e.clientX; + o.lastMouseY = e.clientY; + + if (o.hmode) { + if (o.minX != null) o.minMouseX = e.clientX - x + o.minX; + if (o.maxX != null) o.maxMouseX = o.minMouseX + o.maxX - o.minX; + } else { + if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x; + if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x; + } + + if (o.vmode) { + if (o.minY != null) o.minMouseY = e.clientY - y + o.minY; + if (o.maxY != null) o.maxMouseY = o.minMouseY + o.maxY - o.minY; + } else { + if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y; + if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y; + } + + document.onmousemove = DOMDrag.drag; + document.onmouseup = DOMDrag.end; + + return false; + }, + + drag : function(e) { + e = DOMDrag.fixE(e); + var o = DOMDrag.obj; + + var ey = e.clientY; + var ex = e.clientX; + var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom); + var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right ); + var nx, ny; + + if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX); + if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX); + if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY); + if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY); + + nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1)); + ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1)); + + if (o.xMapper) nx = o.xMapper(y) + else if (o.yMapper) ny = o.yMapper(x) + + DOMDrag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px"; + DOMDrag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px"; + DOMDrag.obj.lastMouseX = ex; + DOMDrag.obj.lastMouseY = ey; + + DOMDrag.obj.root.onDrag(nx, ny); + return false; + }, + + end : function() { + document.onmousemove = null; + document.onmouseup = null; + DOMDrag.obj.root.onDragEnd( parseInt(DOMDrag.obj.root.style[DOMDrag.obj.hmode ? "left" : "right"]), + parseInt(DOMDrag.obj.root.style[DOMDrag.obj.vmode ? "top" : "bottom"])); + DOMDrag.obj = null; + }, + + fixE : function(e) { + if (typeof e == 'undefined') e = window.event; + if (typeof e.layerX == 'undefined') e.layerX = e.offsetX; + if (typeof e.layerY == 'undefined') e.layerY = e.offsetY; + return e; + } +}; \ No newline at end of file diff --git a/bundled-libs/imgedit.js b/bundled-libs/imgedit.js new file mode 100644 index 0000000..8007fcb --- /dev/null +++ b/bundled-libs/imgedit.js @@ -0,0 +1,302 @@ +/************************************************** + * imgedit.js + * 2003-10-17 + * www.sonnd.com / www.supergarv.de + * + * COPYRIGHT (C) BY sonnd / Garvin Hicking + * Published as GPL. Copyright notice has to stay in effect. + **************************************************/ + +// Gets a position of an element on a certain axis +function imgedit_position(element, axis) { + if (axis == 'x') { + return (element.x) ? element.x : imgedit_subposition(element, 'Left'); + } else { + return (element.y) ? element.y : imgedit_subposition(element, 'Top'); + } +} + +// Calculate possible referenced subpositions to really get the absolute position. +function imgedit_subposition(element, axis) { + currentPos = 0; + while (element != null) { + currentPos += element['offset' + axis]; + element = element.offsetParent; + } + + return currentPos; +} + +// Places the cropping area to a certain X/Y coordinate. Then clips the overlay picture correspondingly +function imgedit_placeArea(new_x, new_y) { + o_area.style.left = new_x + 'px'; + o_area.style.top = new_y + 'px'; + o_overlay.style.clip = "rect(" + (new_y+area_border) + " " + (new_x+inner_area_x) + " " + (new_y+inner_area_y) + " " + (new_x+area_border) + ")"; +} + +// Set correct restraints of the cropping area inside which it can move +function imgedit_setMax(new_width, new_height) { + o_area.maxX = imgedit_getMax('x', new_width, area_orientation); + o_area.maxY = imgedit_getMax('y', new_height, area_orientation); +} + +// Toggle the current area orientation to the opposite one +function imgedit_areaOrientation() { + if (area_orientation == 'h') { + imgedit_toggleAreaOrientation('v'); + } else { + imgedit_toggleAreaOrientation('h'); + } + + return false; +} + +// Toggle the current area orientation +function imgedit_toggleAreaOrientation(new_orientation) { + if (new_orientation == area_orientation) { + return; + } + + // Display the corresponding cropping area and hide the other one. + if (new_orientation == 'h') { + area_orientation = 'h'; + o_area = o_harea; + area_x = harea_x; + area_y = harea_y; + inner_area_x = inner_harea_x; + inner_area_y = inner_harea_y; + + o_varea.style.visibility = 'hidden'; + o_area.style.left = o_varea.style.left; + o_area.style.top = o_varea.style.top; + o_area.style.visibility = 'visible'; + } else { + area_orientation = 'v'; + o_area = o_varea; + area_x = varea_x; + area_y = varea_y; + inner_area_x = inner_varea_x; + inner_area_y = inner_varea_y; + + o_harea.style.visibility = 'hidden'; + o_area.style.left = o_harea.style.left; + o_area.style.top = o_harea.style.top; + o_area.style.visibility = 'visible'; + } + + // Set the new clipping inside the cropping area + imgedit_setMax(o_backdrop.width, o_backdrop.height); + o_overlay.style.clip = "rect(" + (parseInt(o_area.style.top)+area_border) + " " + (parseInt(o_area.style.left)+inner_area_x) + " " + (parseInt(o_area.style.top)+inner_area_y) + " " + (parseInt(o_area.style.left)+area_border) + ")"; +} + +// Zoom the image. Takes a given stepping (can be negative) +function imgedit_zoom(step) { + pos = parseInt(o_zoombutton.style.top); + if (pos+step > slider_top && pos+step < slider_bottom) { + imgedit_zoomTo(position(o_zoombutton, 'y') + step); + o_zoombutton.style.top = pos + step + 'px'; + } + + return false; +} + +// Automatically resize the window to fit cropping area +function imgedit_autoSize(flip) { + + // First find the largest side + if (real_x > real_y) { + // The image is a horizontal one. Resize height to fit. + fitmode = 'height'; + } else { + // The image is a vertical one. Resize width to fit. + fitmode = 'width'; + } + + // Check if the size should be flipped. If it is 'true' the image will completely fit inside the cropping area. + // If it is 'false', it will only fit one side inside the cropping area + if (flip == 'true') { + if (fitmode == 'width') { + fitmode = 'height'; + } else { + fitmode = 'width'; + } + } + + // Get new width/height of the image + if (fitmode == 'width') { + new_width = inner_area_x - area_border; + ratio = new_width / real_x; + new_height = real_y * ratio; + } else { + new_height = inner_area_y - area_border; + ratio = new_height / real_y; + new_width = real_x * ratio; + } + + // Place cropping area to (0|0), because the image has been resized? + imgedit_scaleIMG(new_width, new_height); + imgedit_placeArea(-area_border, -area_border); + + // Place the slider to corresponding ratio. + o_zoombutton.style.top = slider_bottom - parseInt(ratio/2 * (slider_middle/3)) + 'px'; + + // Adjust some settings inside the HTML form. + document.getElementById('scaletext').style.visibility = 'visible'; + document.getElementById('autoguess_clicked').value = 'true'; + new_ratio = ratio; + + return false; +} + +// Get the maximum width/height for a certain axis the cropping area is allowed to move to +function imgedit_getMax(axis, pixels, area_orientation) { + + // Which is the size we should get? + if (area_orientation == 'h') { + maxarea_x = harea_x; + maxarea_y = harea_y + } else if (area_orientation == 'v') { + maxarea_x = varea_x; + maxarea_y = varea_y + } else { + maxarea_x = area_x; + maxarea_y = area_y + } + + if (axis == 'x') { + value = pixels - maxarea_x + area_border; + } else { + value = pixels - maxarea_y + area_border; + } + + if (value < -area_border) { + value = -area_border; + } + + return value; +} + +// Scales the background image to a certain size +function imgedit_scaleIMG(new_width, new_height) { + o_backdrop.width = new_width; + o_backdrop.height = new_height; + + o_overlay.width = new_width; + o_overlay.height = new_height; + + imgedit_setMax(new_width, new_height); + + return true; +} + +// Zooms the image to a certain stepping +function imgedit_zoomTo(y) { + current = slider_bottom - y; + + temp_height = current - slider_middle; + temp_ratio = temp_height / (slider_middle*3); + + if (current > slider_middle) { + // make smaller than 100% + new_ratio = 1 + (temp_ratio+temp_ratio); + } else { + // make larger than 100% + new_ratio = 1 + (temp_ratio+temp_ratio); + } + + new_width = parseInt(real_x * new_ratio); + new_height = parseInt(real_y * new_ratio); + + imgedit_scaleIMG(new_width, new_height); + + return true; +} + +// OnSubmit catch. Parses current JS values into the form +function imgedit_getCoordinates() { + document.getElementById('zoombox_x').value = parseInt(o_area.style.left); + document.getElementById('zoombox_y').value = parseInt(o_area.style.top); + document.getElementById('zoombox_factor').value = new_ratio; + document.getElementById('area_orientation').value = area_orientation; + + return true; +} + +// Initializes everything +function imgedit_init(zoombox_width, init_area_border, pad_left, pad_top, init_area_orientation) { + // Store objects + o_backdrop = document.getElementById("backdrop"); + o_overlay = document.getElementById("overlay"); + o_harea = document.getElementById("harea"); + o_varea = document.getElementById("varea"); + o_zoombutton = document.getElementById("zoombutton"); + + // Object sizes + full_x = parseInt(o_backdrop.width); + full_y = parseInt(o_backdrop.height); + + real_x = document.getElementById('real_img_width').value; + real_y = document.getElementById('real_img_height').value; + + area_border = init_area_border; + + harea_x = parseInt(o_harea.width); + harea_y = parseInt(o_harea.height); + + varea_x = parseInt(o_varea.width); + varea_y = parseInt(o_varea.height); + + inner_harea_x = harea_x - area_border; + inner_harea_y = harea_y - area_border; + + inner_varea_x = varea_x - area_border; + inner_varea_y = varea_y - area_border; + + new_ratio = document.getElementById('zoombox_factor').value; + + slider_width = 10; + slider_top = 0; + slider_bottom = 95; + slider_space = slider_bottom - slider_top; + slider_middle = slider_space / 2; + zoombox_left = -(o_zoombutton.width/2) + (slider_width/2); + + // Make objects dragabble + DOMDrag.init(o_harea, null, -area_border, imgedit_getMax('x', full_x, 'h'), -area_border, imgedit_getMax('y', full_y, 'h')); + DOMDrag.init(o_varea, null, -area_border, imgedit_getMax('x', full_x, 'v'), -area_border, imgedit_getMax('y', full_y, 'v')); + DOMDrag.init(o_zoombutton, null, zoombox_left, zoombox_left, slider_top, slider_bottom); + + o_harea.onDrag = function (x, y) { + o_overlay.style.clip = "rect(" + (y+area_border) + " " + (x+inner_harea_x) + " " + (y+inner_harea_y) + " " + (x+area_border) + ")"; + } + + o_varea.onDrag = function (x, y) { + o_overlay.style.clip = "rect(" + (y+area_border) + " " + (x+inner_varea_x) + " " + (y+inner_varea_y) + " " + (x+area_border) + ")"; + } + + o_zoombutton.onDrag = function (x, y) { + imgedit_zoomTo(y); + } + + o_zoombutton.style.left = zoombox_left + 'px'; + + zf = document.getElementById('zoombox_factor').value; + if (zf != 1) { + o_zoombutton.style.top = slider_bottom - parseInt(zf/2 * (slider_middle/3)) + 'px'; + } else { + o_zoombutton.style.top = slider_top + slider_middle + 'px'; + } + + o_zoombutton.style.visibility = 'visible'; + + o_harea.style.cursor = 'move'; + o_harea.style.left = pad_left + 'px'; + o_harea.style.top = pad_top + 'px'; + + o_varea.style.cursor = 'move'; + o_varea.style.left = pad_left + 'px'; + o_varea.style.top = pad_top + 'px'; + + area_orientation = ''; + imgedit_toggleAreaOrientation(init_area_orientation); +} \ No newline at end of file diff --git a/docs/NEWS b/docs/NEWS index 59236c4..f038da4 100644 --- a/docs/NEWS +++ b/docs/NEWS @@ -3,6 +3,19 @@ Version 1.1-alpha7() ------------------------------------------------------------------------ + * Support to crop images from within the media database. Pick a + picture in the MDB, go to the property section of that image + and click on the "EDIT" link. (garvinhicking) + + TODO: + - Operate also on PNG, TIFF etc. (currently only JPEG!) + - Support image magick (currently ony gdlib!) + - Currently backup files are scattered around, fix this. + - Interface cleanup + - Add options to only affect the images thumbnail instead + of always saving the whole picture. + - Internationalization! + * Move the DB charset option to serendipity_config_local.inc.php to issue propper DB connections instantly. (garvinhicking) diff --git a/include/admin/images.inc.php b/include/admin/images.inc.php index dae773f..3e8fa9b 100644 --- a/include/admin/images.inc.php +++ b/include/admin/images.inc.php @@ -11,6 +11,40 @@ if (!serendipity_checkPermission('adminImages')) { } switch ($serendipity['GET']['adminAction']) { + case 'imgedit': + echo '
' . PREFERENCE_USE_JS_WARNING . '
'; + + if (!isset($serendipity['eyecandy']) || serendipity_db_bool($serendipity['eyecandy'])) { + } else { + return true; + } + + include(S9Y_INCLUDE_PATH . "include/functions_images_crop.inc.php"); + $media['is_imgedit'] = true; + $media['css_imgedit'] = serendipity_getTemplateFile('admin/imgedit.css'); + + if (isset($serendipity['GET']['fid'])) { + $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); + if (!is_array($file) || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { + return; + } + + $fullfile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $file['path'] . $file['name'] . '.' . $file['extension']; + $httpfile = $serendipity['serendipityHTTPPath'] . $serendipity['uploadHTTPPath'] . $file['path'] . $file['name'] . '.' . $file['extension']; + + $img = new imgedit($fullfile, $httpfile); + + // Set the filenames used for the cropping areas. Width/Height are automagically detected. Orientation is either horizontal or vertical. + $img->setArea('imgedit_area.gif', 'h'); + $img->setArea('imgedit_varea.gif', 'v'); + + // Let the IMGEditor do its magic. It will parse its results straightly into a template variable array. + $img->main(); + $serendipity['smarty']->assign('imgedit', $img->imgedit_smarty); + serendipity_smarty_fetch('IMGEDIT', $img->output_template); + } + break; + case 'sync': if (!serendipity_checkPermission('adminImagesSync')) { break; diff --git a/include/functions_images.inc.php b/include/functions_images.inc.php index 747be3b..1d0b29c 100644 --- a/include/functions_images.inc.php +++ b/include/functions_images.inc.php @@ -604,6 +604,7 @@ function serendipity_makeThumbnail($file, $directory = '', $size = false, $thumb $infile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $directory . $file; + echo 'From: ' . $infile . '
'; if ($is_temporary) { $temppath = dirname($thumbname); if (!is_dir($temppath)) { @@ -613,6 +614,7 @@ function serendipity_makeThumbnail($file, $directory = '', $size = false, $thumb } else { $outfile = $serendipity['serendipityPath'] . $serendipity['uploadPath'] . $directory . $f . '.' . $thumbname . '.' . $suf; } + echo 'To: ' . $outfile . '
'; $fdim = @serendipity_getimagesize($infile, '', $suf); if (isset($fdim['noimage'])) { diff --git a/serendipity_admin.php b/serendipity_admin.php index 9389fff..77b7a63 100644 --- a/serendipity_admin.php +++ b/serendipity_admin.php @@ -43,6 +43,7 @@ if (serendipity_is_iframe()) { + + + {/if} + @@ -99,6 +145,11 @@ {/if} {$media.external} + + {if $media.is_imgedit} + {$IMGEDIT} + {/if} + {elseif $media.case == 'default'} diff --git a/templates/default/admin/media_imgedit.tpl b/templates/default/admin/media_imgedit.tpl new file mode 100644 index 0000000..dee9c26 --- /dev/null +++ b/templates/default/admin/media_imgedit.tpl @@ -0,0 +1,57 @@ +
+
+
+ +
+ OR +
+
+ + + + + + + + + +
+
+ +
+ + + + + +
+
+ + +
+ + +
+ +
+ Backdrop + Overlay + [Crop area] + [Crop area] +
+
+
+ + + \ No newline at end of file diff --git a/templates/default/admin/media_imgedit_done.tpl b/templates/default/admin/media_imgedit_done.tpl new file mode 100644 index 0000000..b8291d4 --- /dev/null +++ b/templates/default/admin/media_imgedit_done.tpl @@ -0,0 +1,37 @@ +
+ Output Information + + Your image was {$imgedit.real_img_width} x {$imgedit.real_img_height} pixels (Orientation: {$imgedit.area_orientation}).
+ Depending on your zoom of {$imgedit.zoombox_factor}x, it was {$imgedit.zoom_img_width} x {$imgedit.zoom_img_height} pixels.
+ It got scaled to {$imgedit.img_width} x {$imgedit.img_height} pixels.
+ Then a rectangle starting from ({$imgedit.slice_from_x}|{$imgedit.slice_from_y}) to ({$imgedit.slice_to_x}|{$imgedit.slice_to_y}) has been sliced from it.
+
+ + {if $imgedit.image_cut} + The image has been correctly cropped and only the part inside of the rectangle is going to be shown. + {/if} + + {if $imgedit.image_no_cut} + Because the source dimensions were smaller than the destination dimensions, the image does not fill up the complete space. + {/if} + + {if $imgedit.image_error} + However, there were errors processing your image. + {/if} +
+ +
+ Image Result + + Cropped Image:
+ +
+ Cropped image +
+
+ +
+ Play it again, Sam + + That was great! So, please once more, with feeling!. +
\ No newline at end of file diff --git a/templates/default/admin/media_items.tpl b/templates/default/admin/media_items.tpl index 6afe8c2..e09bc28 100644 --- a/templates/default/admin/media_items.tpl +++ b/templates/default/admin/media_items.tpl @@ -88,6 +88,11 @@ {/foreach} + {if $file.is_image} +
+
{$CONST.EDIT} +
+ {/if}

{$CONST.MEDIA_KEYWORDS}