#php #gd #crop #jcrop
#php #gd #обрезка #jcrop
Вопрос:
Я пытаюсь создать уменьшенное изображение для большего изображения. Я использую Jcrop для получения координат обрезки, но я не могу превратить это в правильно обрезанное уменьшенное изображение. Я правильно настроил Jcrop, и он отправляет координаты x-y вместе с размером поля, но я не могу понять, как обрезать из этого.
У меня есть путь к изображению на моем сервере, а также 4 координаты, а также ширина и высота квадратного прямоугольника, который они создают (я зафиксировал соотношение сторон 1: 1, поскольку мне нужны квадратные эскизы). Затем я отправляю это через Ajax в свой скрипт обрезки PHP, но я не могу заставить его обрезать на основе того, что установлено.
Это то, что у меня есть до сих пор:
public function Crop($file, $crop) {
$height = $width = 180;
$ratio = $width / $height;
$pos = strrpos($file, '.');
$name = substr($file, 0, $pos);
$ext = strtolower(substr($file, $pos));
if( ! in_array($ext, array('.gif', '.jpg', '.jpeg', '.png'))) {
return 'INVALID_EXT';
}
// When loading the image we check to see if the first character in file is a slash, and if so remove it as the last character of root is a slash.
$src = ROOT . (in_array(substr($file, 0, 1), array('/', '\')) ? substr($file, 1) : $file);
$srcRes = imagecreatefromstring(file_get_contents($src));
if( ! $srcRes) {
return 'INVALID_FILE';
}
$srcWidth = imagesx($srcRes);
$srcHeight = imagesy($srcRes);
$srcRatio = $srcWidth / $srcHeight;
$dstRes = imagecreatetruecolor($crop['w'], $crop['h']);
if($ext == '.gif') {
$dstBg = imagecolorallocate($dstRes, 0, 0, 0);
imagecolortransparent($dstRes, $dstBg);
} elseif($ext == '.png') {
$dstBg = imagecolorallocate($dstRes, 0, 0, 0);
imagecolortransparent($dstRes, $dstBg);
imagealphablending($dstRes, FALSE);
imagesavealpha($dstRes, TRUE);
}
$srcX = 0;
$srcY = 0;
if($srcRatio > $ratio) {
$tmpWidth = $srcHeight * $ratio;
$tmpHeight = $srcHeight;
$srcX = ($srcWidth - $tmpWidth) / 2;
$srcY = 0;
} else {
$tmpWidth = $srcWidth;
$tmpHeight = $srcWidth / $ratio;
$srcX = 0;
$srcY = ($srcHeight - $tmpHeight) / 2;
}
imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], $crop['w'], $crop['h'], $tmpWidth, $tmpHeight);
$dst = ROOT . (in_array(substr($name, 0, 1), array('/', '\')) ? substr($name, 1) : $name) . '-thumb' . $ext;
if($ext == '.gif') {
$try = imagegif($dstRes, $dst);
} elseif($ext == '.jpg' || $ext == '.jpeg') {
$try = imagejpeg($dstRes, $dst, 80);
} elseif($ext == '.png') {
$try = imagepng($newThumbImageResource, $dst);
}
if( ! $try) {
return 'CREATE_ERR';
}
return 'SUCCESS';
}
Я пробовал изменять всевозможные вещи, imagecopyresampled
но не могу заставить его обрезать в соответствии с тем, что отправляет Jcrop. Если я использую $srcWidth
, и $srcHeight
он сохраняет соотношение сторон исходного изображения и сжимает его в квадрат 180 пикселей. Но, похоже, он не изменяет его размер или не обрезает его из нужного места.
Может кто-нибудь, пожалуйста, помочь мне определить, какие цифры я должен использовать где? Я весь день бился об это головой.
Редактировать
Вот остальная часть кода. Сначала JavaScript, который запускает JCrop. Он запускается после загрузки файла Ajax для создания миниатюры этого изображения:
Это загрузка функции Ajax, которая в конце вызывает обрезку.
$(function () {
'use strict';
// Change this to the location of your server-side upload handler:
var url = '/eshop/library/ajax/ajax.file-upload.php';
var uploadDir = 'prodimages/';
$('.listing-image').fileupload({
url: url,
dataType: 'json',
autoUpload: true,
acceptFileTypes: /(.|/)(gif|jpe?g|png)$/i,
maxFileSize: 1000000, // 1 MB
// Enable image resizing, except for Android and Opera,
// which actually support image resizing, but fail to
// send Blob objects via XHR requests:
disableImageResize: /Android(?!.*Chrome)|Opera/
.test(window.navigator.userAgent),
previewMaxWidth: 120,
previewMaxHeight: 120,
previewCrop: true,
paramName: 'files[]',
formData: {uploadDir: uploadDir}
})/*.on('fileuploadprocessalways', function (e, data) {
var index = data.index;
var file = data.files[index];
$(this).html(file.preview);
})*/.on('fileuploadprogressall', function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('.listing-progress', this).css(
'width',
progress '%'
);
}).on('fileuploaddone', function (e, data) {
var file = data.result.files[0];
var html = '<div class="listing-preview">
<img src="' file.thumbnailUrl '" data-name="' file.name '">
<div class="listing-preview-delete">Delete</div>
</div>';
$(this).html(html).data('delete-url', file.deleteUrl).css('padding', 0);
Crop('/' uploadDir file.name, $(this).prop('id'));
});
});
This sends the crop details through to the PHP script above via Ajax.
$(document).on('click', '.crop-btn', function() {
var data = {
file: $(this).data('src'),
crop: jcrop.tellSelect()
}
ShowLoadingById('crop-loading');
AjaxHandler('/eshop/library/ajax/ajax.product-crop-thumb.php', data, 'POST', true);
});
The crop window is a lightbox, this function vertically centres it and resizes the image if it’s vertically bigger than the available space.
function CentreCrop() {
var m = ($(window).height() - ($('.crop > div').height() 60)) / 2;
$('.crop > div').css('margin-top', m);
if($('#crop-img').height() > $('.crop > div').height() - 30) {
$('#crop-img-container').height($('.crop > div').height() - 30);
}
}
This is the initial function that stores the file to be cropped and calls the worker if it’s not running.
var toBeCropped = [];
var working = false;
function Crop(file, id) {
toBeCropped.push({path: file, id: id});
if( ! working) {
working = true;
CropWorker();
}
}
This is the function I run when the crop has finished to destroy jcrop and clear the crop lightbox ready for the next image to be cropped.
function CropSuccess() {
$('.crop').fadeOut(250, function() {
jcrop.destroy();
$(this).html('');
CropWorker();
});
}
Это рабочий, который фактически создает содержимое в лайтбоксе и инициирует jcrop.
function CropWorker() {
if(toBeCropped.length > 0) {
file = toBeCropped.shift();
html = '<div>
<div id="crop-img-container" class="row-fluid">
<img id="crop-img" src="' file.path '">
</div>
<div class="row-fluid">
<div class="span3 offset9">
<button class="span12 btn crop-btn" data-id="' file.id '" data-src="' file.path '">Create Thumb</button>
</div>
</div>
<div class="row-fluid loading-screen" id="crop-loading">
<div>
<h4>Cropping...</h4>
<img src="/img/loading.gif">
</div>
</div>
</div>';
$('.crop').html(html);
$('.crop').fadeIn(250);
$('#crop-img').load(function() {
CentreCrop();
$('#crop-img').Jcrop({
aspectRatio: 1/1,
bgColor: 'black',
bgOpacity: 0.4,
boxWidth: $('#crop-img').width(), // Only just recently added boxWidth and height to see if that would fix it, no difference with or without.
boxHeight: $('#crop-img').height(),
//maxSize: [300,300],
minSize: [180,180]
}, function() {
jcrop = this;
jcrop.setSelect([0,0,180,180]);
});
});
} else {
working = false;
}
}
Обновить
Похоже, частью проблемы было изменение размера изображения. Я менял размер изображения, чтобы оно соответствовало экрану, я думал, что JCrop позаботится об этом для меня, но, похоже, вы должны сообщить JCrop исходные размеры изображения. Я добавил параметр true size при инициализации JCrop, и, похоже, мы почти на месте.
Для изображений меньшего размера, размером менее 1000 пикселей, обрезка, похоже, работает отлично. Но для больших (1000 пикселей ) он создает черные изображения. Этого не происходит, когда я использую его с демонстрационным скриптом JCrop, но единственное различие между ними заключается в том, что один выводит файл на экран, а другой сохраняет его. Я не вижу никакой другой разницы или не понимаю, почему это может произойти.
ОБНОВЛЕНИЕ 2 Похоже, это влияет только в том случае, если я запускаю код через Ajax. Если я запускаю точно такую же функцию, просто размещая на странице и запуская ее вверху, эскиз создается идеально каждый раз, независимо от размера исходного изображения или размера рамки, которую я рисую.
Ответ №1:
В вашем коде $crop['w']
и $crop['h']
для источника;
$tmpWidth
и $tmpHeight
предназначены для назначения;
поэтому вы должны переключать их в соответствии с imagecopyresampled
функцией (http://php.net//manual/fr/function.imagecopyresampled.php ).
imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], $tmpWidth, $tmpHeight, $crop['w'], $crop['h']);
редактировать 2
Ваше целевое изображение должно быть установлено не с $crop
данными, а с нужным размером :
$dstRes = imagecreatetruecolor(180, 180);
И я не уверен, какое значение у вас есть, $tmpWidth
и $tmpHeight
но они оба должны быть 180, так как это нужный вам размер :
imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], 180, 180, $crop['w'], $crop['h']);
Редактировать 3
Хорошо, последняя попытка, у меня есть рабочий код для jcrop, но я не использую его внутри функции, он все еще работает так же. Я проверил построчно ваш код, и вот что у меня есть. Я удалил все, что касается ratio, Jcrop управляет этим, нет необходимости делать это с помощью PHP. Правильное dest-изображение, правильное imagecopyresampled
, выглядит хорошо для меня :/
public function Crop($file, $crop) {
$height = $width = 180;
// your file type checking
$pos = strrpos($file, '.');
$name = substr($file, 0, $pos);
$ext = strtolower(substr($file, $pos));
if( ! in_array($ext, array('.gif', '.jpg', '.jpeg', '.png'))) {
return 'INVALID_EXT';
}
// source image
$src = ROOT . (in_array(substr($file, 0, 1), array('/', '\')) ? substr($file, 1) : $file);
$srcRes = imagecreatefromstring(file_get_contents($src));
if( ! $srcRes) {
return 'INVALID_FILE';
}
// destination image
$dstRes = imagecreatetruecolor($width, $height);
// file type transparence
if($ext == '.gif') {
$dstBg = imagecolorallocate($dstRes, 0, 0, 0);
imagecolortransparent($dstRes, $dstBg);
} elseif($ext == '.png') {
$dstBg = imagecolorallocate($dstRes, 0, 0, 0);
imagecolortransparent($dstRes, $dstBg);
imagealphablending($dstRes, FALSE);
imagesavealpha($dstRes, TRUE);
}
// bool imagecopyresampled ( resource $dst_image , resource $src_image , int $dst_x , int $dst_y , int $src_x , int $src_y , int $dst_w , int $dst_h , int $src_w , int $src_h )
imagecopyresampled($dstRes, $srcRes, 0, 0, $crop['x'], $crop['y'], $width, $height, $crop['w'], $crop['h']);
$dst = ROOT . (in_array(substr($name, 0, 1), array('/', '\')) ? substr($name, 1) : $name) . '-thumb' . $ext;
if($ext == '.gif') {
$try = imagegif($dstRes, $dst);
} elseif($ext == '.jpg' || $ext == '.jpeg') {
$try = imagejpeg($dstRes, $dst, 80);
} elseif($ext == '.png') {
$try = imagepng($newThumbImageResource, $dst);
}
if( ! $try) {
return 'CREATE_ERR';
}
return 'SUCCESS';
}
Комментарии:
1. Это не работает. Это не изменяет размер изображения, поэтому обрезка сильно увеличивается. Если я выбираю обрезку 300 x 300, она должна обрезать ее, а затем изменить ее размер до 180 x 180, но это не так.
2. Это тоже не работает. Верхний левый угол не совпадает с тем местом, где я размещаю его на кадрировщике, и он также не обрезается при правильном увеличении.
3. Я скопировал ваш код и вставил его, чтобы убедиться, что он правильный, и все равно получил то же самое. Нет ли какой-либо команды, в которой я мог бы ввести 4 координаты (верхний левый, верхний правый, нижний левый, нижний правый) и получить их, а затем изменить размер до 180?
4. Также мне интересно, связано ли это с масштабированием изображения? Jcrop должен быть в состоянии справиться с этим, но мне интересно, правильно ли я это делаю?
5. Ну, разве я не чувствую себя глупо. Я фильтровал целые числа, но он отправляет значения с плавающей запятой. Я не знаю, почему это работало для изображений меньшего размера, они должны отправлять целые числа, а не числа с плавающей запятой, или фильтр округлил их или что-то в этом роде. Но теперь он отсортирован. Это работает. Спасибо за всю вашу помощь.
Ответ №2:
Попробуйте использовать эту функцию во время изменения размера изображения
Просто вызовите функцию следующим образом:
resize_image($source_image, $destination_filename, 200, 200);
Эта функция также включает в себя функции обрезки. Вы можете включить или отключить параметр обрезки, передав в него значение true или false ($crop = true / false).
function resize_image($source_image, $destination_filename, $width, $height, $quality = 100, $crop = true)
{
if( ! $image_data = getimagesize( $source_image ) )
{
return false;
}
switch( $image_data['mime'] )
{
case 'image/gif':
$get_func = 'imagecreatefromgif';
$suffix = ".gif";
break;
case 'image/jpeg';
$get_func = 'imagecreatefromjpeg';
$suffix = ".jpg";
break;
case 'image/png':
$get_func = 'imagecreatefrompng';
$suffix = ".png";
break;
}
$img_original = call_user_func( $get_func, $source_image );
$old_width = $image_data[0];
$old_height = $image_data[1];
$new_width = $width;
$new_height = $height;
$src_x = 0;
$src_y = 0;
$current_ratio = round( $old_width / $old_height, 2 );
$desired_ratio_after = round( $width / $height, 2 );
$desired_ratio_before = round( $height / $width, 2 );
if( $old_width < $width || $old_height < $height )
{
/**
* The desired image size is bigger than the original image.
* Best not to do anything at all really.
*/
return false;
}
/**
* If the crop option is left on, it will take an image and best fit it
* so it will always come out the exact specified size.
*/
if( $crop )
{
/**
* create empty image of the specified size
*/
$new_image = imagecreatetruecolor( $width, $height );
/**
* Landscape Image
*/
if( $current_ratio > $desired_ratio_after )
{
$new_width = $old_width * $height / $old_height;
}
/**
* Nearly square ratio image.
*/
if( $current_ratio > $desired_ratio_before amp;amp; $current_ratio < $desired_ratio_after )
{
if( $old_width > $old_height )
{
$new_height = ma(angry) $width, $height );
$new_width = $old_width * $new_height / $old_height;
}
else
{
$new_height = $old_height * $width / $old_width;
}
}
/**
* Portrait sized image
*/
if( $current_ratio < $desired_ratio_before )
{
$new_height = $old_height * $width / $old_width;
}
/**
* Find out the ratio of the original photo to it's new, thumbnail-based size
* for both the width and the height. It's used to find out where to crop.
*/
$width_ratio = $old_width / $new_width;
$height_ratio = $old_height / $new_height;
/**
* Calculate where to crop based on the center of the image
*/
$src_x = floor( ( ( $new_width - $width ) / 2 ) * $width_ratio );
$src_y = round( ( ( $new_height - $height ) / 2 ) * $height_ratio );
}
/**
* Don't crop the image, just resize it proportionally
*/
else
{
if( $old_width > $old_height )
{
$ratio = ma(angry) $old_width, $old_height ) / ma(angry) $width, $height );
}else{
$ratio = ma(angry) $old_width, $old_height ) / min( $width, $height );
}
$new_width = $old_width / $ratio;
$new_height = $old_height / $ratio;
$new_image = imagecreatetruecolor( $new_width, $new_height );
}
/**
* Where all the real magic happens
*/
imagecopyresampled( $new_image, $img_original, 0, 0, $src_x, $src_y, $new_width, $new_height, $old_width, $old_height );
/**
* Save it as a JPG File with our $destination_filename param.
*/
imagejpeg( $new_image, $destination_filename, $quality );
/**
* Destroy the evidence!
*/
imagedestroy( $new_image );
imagedestroy( $img_original );
/**
* Return true because it worked and we're happy. Let the dancing commence!
*/
return true;
}
Комментарии:
1. Как мне указать, откуда обрезать с помощью этого скрипта? Похоже, он не принимает никаких параметров x, y.
2. Для обрезки вы должны установить $destination_filename в качестве абсолютного пути, и он автоматически обрезает изображение из центра основного изображения.
3. Я не хочу, чтобы он обрезался из центра основного изображения, я хочу указать координаты места для обрезки.