в PHP

Toolkit для ресайза и кропа изображений на PHP

Поделиться
Плюсануть
Запинить

Тулкит для ресайза и кропа изображенийДостаточно давно мы написали статью, в которой представили наш класc для ресайза и кропа изображений средствами php. В нём было множество недочётов и ошибок. И вот, наконец, мы выпускаем новую версию нашего продукта, существенно доработав его и исправив. Теперь это уже не одиночный класс, а целый Toolkit.

Фактически весь код был переписан, добавлен некоторый функционал, а также, что самое важное, была реализована «геометрия», позволяющая удобно описывать такие сущности, как размер, точка, прямоугольник. Разумеется, в этот раз мы не станем описывать работу самого кода, ибо это займёт не один десяток тысяч символов статьи и не менее часа вашего времени. Остановимся мы лишь на использовании сего инструментария. Но сперва перечислим более подробно весь список изменений, которым подвергся наш продукт.

Итак, вот он:

  • Существенно переписан весь код;
  • Добавлена «геометрия»;
  • Реализован шаблон проектирования – фабрика (factory), что серьёзно сократило дублирование в коде;
  • Добавлен класс AcColor, позволяющий удобно работать с цветом;
  • Реализованы исключения (exceptions);
  • Исправлено определение типа изображения;
  • Добавлена функция, определяющая, существует ли исходный файл. Работает также и с удалёнными изображениями.
  • Добавлена статическая функция isFileImage, определяющая, является ли файл изображением;
  • Добавлена проверка поддержки библиотекой GD возможности открытия определённых типов файлов;
  • Центральный кроп полностью переписан, с учётом различных принимаемых аргументов (пиксели, проценты, пропорции);
  • Добавлена возможность установки цвета фона на прозрачные области изображений (по-умолчанию — белый);
  • Добавлена возможность размещения логотипа на исходном изображении;
  • Добавлена статическая функция setTransparency, включающая, либо отключающая поддержку прозрачности изображений (по-умолчанию поддержка прозрачности включена);
  • Добавлена статическая функция setRewrite, устанавливающая права на перезапись файлов при конфликте имён сохраняемых изображений (по-умолчанию перезапись отключена);
  • Добавлена статическая функция setQuality, устанавливающая качество сохранения JPG-изображений (по умолчанию — 85);
  • Функция save теперь сохраняет изображение в исходном формате;
  • Реализованы функции saveAsJPG, saveAsPNG и saveAsGIF, позволяющие сохранять изображения в соответствующих форматах;

Начнём, пожалуй, с «геометрии», ибо она в наибольшей мере изменила код класса. Мы реализовали три вспомогательных класса: Point, Rectangle и Size, которые, соответственно, описывают точку, прямоугольник и размер. При помощи этих классов удалось избавиться от столь частого дублирования в коде. В подробности работы и функционала «геометрии» мы вдаваться не станем, так как это займёт много времени. Да и необходимости это знать нет, ибо пользователь класса может обойтись без их использования (а оперировать ими будет лишь сам класс AcImage). Впрочем, возможность их использования мы опишем ниже, рассматривая функционал.

Немаловажным новшеством является появление исключений (exceptions). Теперь мы можем заключить код в блоки try{…} и catch(){…} и обработать различные непредвиденные ситуации, собственноручно определив механизм исключительной ситуации. Например, если открываемый файл не обнаружен, мы хотим вывести некое окошко, оповещающее об этом. Сделать это можно будет так:

try
{
   //создаём экземпляр класса
}
catch (FileNotFoundException $ex)
{
   //выводим необходимое оформление окошка
   $ex->getMessage(); //Выведет 'File not found'
}

Также был реализован вспомогательный класс AcColor, необходимый для удобной работы с цветом. Его мы также не станем описывать, так как дёргается он в основном не нами, а классом AcImage.

Использование

В использовании нашего тулкита осталось мало чего похожего на первую версию. Первые же изменения коснулись создания экземпляра класса. Теперь у нас нет необходимости самим создавать его, это сделает фабрика…

Создание экземпляра класса

В старой версии класса код зачастую ветвился и дублировался. В основном, из-за того, что функционал при работе с различными типами изображения был как похож, так немного и отличался друг от друга. Поэтому, нами было решено вынести код, отвечающий за работу с конкретным типом изображений (jpg, png, gif) в отдельные классы-потомки, наследующие родительский класс AcImage. Это классы AcImageJPG, AcImagePNG, AcImageGIF. Создание же экземпляра класса будет производиться следующим образом:

$img = AcImage::createImage('image.jpg');

Функция же createImage сама решит, экземпляр какого из трёх классов создать, после чего вернёт его нам. Так мы реализовали фабрику.

uml фабрики

При создании экземпляра будет осуществлена проверка на существование файла, причём, сейчас она корректно работает и с удалёнными файлами, то есть, находящимися на другом сервере.

$img = AcImage::createImage('http://site.com/image.jpg');

Реализована и корректная проверка на то, является ли файл изображением. В противном случае будет брошено исключение.

Про наследование в php можно прочитать по ссылке.

Бросаемые методом исключения:

  • GDnotInstalledException– библиотека GD не подключена;
  • InvalidFileException – файл не является изображением;
  • FileNotFoundException – файл не найден.

Ресайз изображений

В плане использования метода ресайза, класс также изменился. Теперь есть два варианта его использования: использование двух параметров (высоты и ширины области, в которую необходимо пропорционально вписать наше изображение) типа int или одного – экземпляра класса Size.

Вот оба примера использования:

$img = AcImage::createImage('image.jpg');
//первый вариант
$img->resize(640, 480);
//второй вариант
$size = new Size(640, 480); //можно провести некоторые манипуляции с этим экземпляром
$img->resize($size);

Результат выполнения будет одним и тем же – изображение пропорционально впишется в рамки (640×480). Примерно так:

Иллюстрация обычного ресайза

$img->resize(640, 480)

В прошлой версии класса была возможность масштабировать изображение лишь по одной из сторон, передав лишь ширину или высоту в функцию. Теперь для этого реализованы две отдельные функции:

$img->resizeByWidth(640); //ужать по ширине до 640px

Сжатие по ширине

$img->resizeByWidth(640)

$img->resizeByHeight(480); //ужать по высоте до 480px

Сжатие по высоте

$img->resizeByHeight(480)

Бросаемые методом исключения:

  • IllegalArgumentException – переданы некорректные аргументы.

Кроп

Как и ресайз, кроп можно использовать двумя способами – используя координаты вырезаемой области или же используя экземпляр класса Rectangle (прямоугольник). В первом случае мы передаём в метод crop координаты верхнего левого угла, а также ширину и высоту области, которую желаем вырезать из исходного изображения. Делается это следующим образом:

$img = AcImage::createImage('image.jpg');
$img->crop(100, 200, 640, 480);

Как и с ресайзом, можно поступить следующим образом:

$rect = new Rectangle(100, 200, 640, 480);
$img->crop($rect);

Кроп

$img->crop(100, 200, 640, 480)

В этой версии тулкита, появилась возможность передавать отрицательные координаты, например, такие:

(-100, 30, 500, 600).

Или такие:

(600, 300, -500, -400)

В обоих случаях будет вырезана корректная область, без пустот.

Кроп с отрицательными параметрами

$img->crop(600, 300, -500, -400)

Бросаемые методом исключения:

  • IllegalArgumentException – переданы некорректные аргументы.

Квадратный кроп

Квадратный кроп позволяет вырезать из исходного изображения исключительно квадратную область. Однако, в данном случае эта область не может выходить за пределы картинки, так как в этом случае есть вероятность, что в результате получится не квадратное изображение.

Использовать этот кроп можно тремя способами, как передавая параметры верхнего левого угла и ширину вырезаемого квадрата, так и передавая экземпляры классов «геометрии». Делается это следующими способами:

$img = AcImage::createImage('image.jpg');
$img->cropSquere(100, 200, 400);

Первые два аргумента – координаты верхнего левого угла вырезаемого квадрата (они должны быть исключительно положительными). Третий аргумент – собственно, сама сторона вырезаемого квадрата (она же может быть отрицательной, лишь бы полученная область не выходила за пределы исходного изображения).

Во втором случае используется экземпляр класса Point (точка, она же – верхний левый угол) и ширина, значения int:

$img = AcImage::createImage('image.jpg');
$point = new Point(100, 200);
$img->cropSquere($point, 400);

Третий же способ предусматривает лишь один аргумент – прямоугольник (экземпляр класса Rectangle). Но есть один небольшой нюанс – он обязательно должен быть квадратом, иначе будет брошено исключение.

$img = AcImage::createImage('image.jpg');
$rect = new Rectangle(100, 200, 400, 400);
$img->cropSquere($rect);

Выглядеть это будет так:

Квадратный кроп

$img->cropSquere(100, 200, 400)

Кроп с отрицательной стороной квадрата хорошо иллюстрирует следующая картинка:

Квадратный кроп с отрицательной стороной квадрата

$img->cropSquere(500, 400, -300)

Если же вырезаемая область выходит за пределы исходного изображения, будет брошено исключение:

Квадратный кроп, выходящий за пределы изображения

$img->cropSquere(500, 400, 600)

Бросаемые методом исключения:

  • IllegalArgumentException – переданы некорректные аргументы.

Центральный кроп

Центральный кроп позволяет вырезать из исходного изображения некоторую область. Но, в отличие от обыкновенного кропа, описанного ранее, вырезается область из центра картинки. Данная возможность может быть необходима, например, в случае, когда необходимо сделать красивые превью одинакового размера. В этом случае вы вырезаете максимально большую область из центра в пропорциях, например, 4×3.

Способов использования центрального кропа также несколько. Можно вырезать центральную область, строго указав её размеры в пикселях, можно указать ширину и высоту в процентах от оригинального изображения, а можно просто указать соотношение ширины и высоты вырезаемой области.

Остановимся на каждом моменте отдельно.

Самый простой способ использования этого вида кропа – фиксированные размеры вырезаемой области, в пикселях. Делается это одним из следующих способов:

$img = AcImage::createImage('image.jpg');
$img->cropCenter(640, 480); //первый способ
$img->cropCenter('640px', '480px'); //второй способ

Работают оба варианта абсолютно одинаково – из центра исходной картинки вырежется область, размером 640×480. Выглядит это так:

Центральный кроп с фиксированными параметрами

$img->cropCenter(‘640px’, ‘480px’)

Если по какой-либо стороне будет указано значение, превышающее исходный размер картинки, будет вырезана максимально возможная область.

Центральный кроп с параметрами, выходящими за пределы изображения

$img->cropCenter(‘640px’, ‘300px’)

Кроме указания области в пикселях, можно использовать и проценты. Например, так:

$img = AcImage::createImage('image.jpg');
$img->cropCenter('50%', '25%');

В этом случае по ширине изображения из центра будет вырезана половина, а по высоте, четверть.

Центральный кроп с процентами

$img->cropCenter(‘50%’, ‘25%’)

Если значения будут больше 100%, то, как и в прошлом примере, ничего страшного не произойдёт – будет вырезана максимально возможная область.

Возможно, одним из наиболее часто используемых применений центрального кропа, станет обрезка по пропорциям. Чтобы понять, зачем это нужно, представим, что в альбоме нам необходимо вывести в виде сетки превью всех изображения. Но если их отобразить просто в уменьшенном виде, получится нечто следующее:

Сетка превью в фотоальбоме

Некрасиво, не правда ли? В таком случае было бы удобно, чтобы отношение сторон всех превью были, скажем, 4×3. То есть, для создания таких миниатюр, нам необходимо вырезать из исходного изображения максимально возможную область с отношением сторон 4×3. Визуально это выглядит так:

Центральный кроп с пропорциями

$img->cropCenter(‘4pr’, ‘3pr’)

Используется следующим образом:

$img = AcImage::createImage('image.jpg');
$img->cropCenter('4pr', '3pr');

В итоге наша сетка превью в альбоме будет выглядеть куда более привлекательно (разумеется, для этого также необходимо пропорционально ресайзить все картинки):

Сетка превью в фотоальбоме после кропа

Кроме трёх основных направлений центрального кропа (по пикселям, по процентам и пропорциям) можно использовать и смешанные параметры. Например, следующим образом:

$img = AcImage::createImage('image.jpg');
$img->cropCenter('500px', '50%'); //первый вариант
$img->cropCenter('50%', '500px'); //второй вариант

В первом варианте вырезанная область будет иметь ширину 500px, а высота будет составлять половину от высоты исходного изображения. Во втором варианте – наоборот.

Смешанный центральный кроп

$img->cropCenter(‘500px’, ‘50%’)

Следует заметить, что смешивать можно только проценты и пиксели. Ни в коем случае с пропорциями сочетать ничего другого нельзя. Думаю, можете догадаться и сами, почему.

Весьма полезно будет взять на вооружение следующую практику. Предположим, что мы хотим вырезать область, которая по высоте занимает всё изображение, а по ширине – какой-то фиксированный размер. В этом случае также удобно использовать смешанные аргументы, где высота будет равна 100%.

$img = AcImage::createImage('image.jpg');
$img->cropCenter('500px', '100%');

Обрезка по ширине

$img->cropCenter(‘500px’, ‘100%’)

Бросаемые методом исключения:

  • IllegalArgumentException – переданы некорректные аргументы.

Умное создание миниатюр

Чуть ранее, описывая принцип работы центрального кропа, мы рассказали, как создать красивые и одинаковые превью для фотоальбома. Но в том случае все миниатюры физически обрезались, да и не всегда могли получиться содержательными: представьте, что мы увидим на картинке, размером 1000×200, из которой вырезали часть 200×200). Примерно, следующее:

Неудачное использование центрального кропа

Намного лучше было бы, чтобы большая часть изображения оказалась на миниатюре. Именно для этого мы реализовали так называемое умное создание миниатюр. В этом случае изображения физически обрезаться не будут, но будут ужиматься в особые рамки. Некоторая работа тут ляжет на верстальщика, ибо ему придётся спрятать ненужные области, центрировав на фоне дива картинку. Об этом расскажем ниже.

Итак, сперва использование:

$img = AcImage::createImage('image.jpg');
$img->thumbnail(200, 150);

В данном случае изображение так впишется в рамки, что будет занимать всю указанную область, не оставляя пустых мест. Однако, изображение наверняка не будет полностью помещаться в них, а слегка будет выходить за пределы. Верстальщику необходимо их спрятать за пределы дива. В данном случае гарантируется полная сохранность изображения – ничего от него не будет отрезано.

Умное создание миниатюр

$img->thumbnail (200, 150)

Но что же делать с необычными размерами исходных изображений? Если ими заполнять всю необходимую область, отведённую для миниатюры, то лишь малая часть информации на изображении будет видна. Тут приходится идти на компромисс – не до конца заполнять всю область, оставляя пустые пространства, однако, сохраняя большую часть изображения в поле видимости. Следующая иллюстрация прекрасно отражает этот пример:

Примеры плохого и хорошего создания миниатюр

Для определения этого самого предела, когда необходимо жертвовать заполненностью области превью, используется специальный коэффициент, равный двум. То есть, если часть исходного изображения более чем на половину (1/2) оказывается вне области миниатюры, то картинка ужимается так, чтобы большая её часть всё же оказалась внутри. При этом, разумеется, появляются пустые области. Этот коэффициент можно поменять, передав в функцию thumbnail третий (необязательный) параметр. Стоит заметить, что практической надобности делать это в большинстве случаев нет, ибо коэффициент 2 наиболее оптимален. Если всё же вам по какой-то причине захотелось его изменить, делается это следующим образом:

$img = AcImage::createImage('image.jpg');
$img->thumbnail(200, 150, 3); //коэффициент превышения равен трём

Чтобы показать, насколько лучше использовать именно эту функцию, сравним её с обычным ресайзом на примере создания таблицы превью в фотоальбоме.

Превью в фотоальбоме

Второй вариант:

Превью в фотоальбоме с применением умного ресайза

Второй вариант намного привлекательнее. Единственный нюанс, который стоит учесть – это то, что на самом деле изображения получились несколько больше необходимых размеров, поэтому их необходимо выводить следующим образом:

<div style="background: url(images/img.jpg) 
center  #999 no-repeat;  
width: 200px; height: 150px"></div>

Этот код помещает на фон div’а миниатюру, пряча не влезающую часть. В случае, если высота или ширина миниатюры оказалась меньше высоты или ширины div’а, пустые поля будут серого цвета (#999). Стоит заметить, что размеры div’а должны совпадать с размерами, которые были переданы методу thumbnail.

Бросаемые методом исключения:

  • IllegalArgumentException – переданы некорректные аргументы.

Размещение логотипа

Лого

В этой версии продукта мы также реализовали и отрисовку логотипа на исходном изображении. Он может быть размещён в одном из четырёх углов картинки, причём, по умолчанию, его размер (ширина либо высота) не сможет превышать 0.1 (10%) от размера (высоты либо ширины) исходного изображения. Отступы же логотипа от края составят 0.02 (2%). Эти значения, разумеется, можно изменить, но об этом позже. Сперва расскажем, как же поместить логотип на изображение.
Опять же, способ не один. Вот первый вариант:

$img = AcImage::createImage('image.jpg');
$img->drawLogo('logo.png', AcImage::BOTTOM_RIGHT);

В этом случае мы передали в качестве параметра ссылку на изображение-логотип, а также угол, в котором следует его разместить. Впрочем, второй параметр можно было и не передавать – по умолчанию угол размещения лого – нижний правый.

Если же изображение логотипа следует разместить в другом углу, стоит передавать одно из следующих значений: TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT.

Второй способ размещения лого состоит в том, что в метод drawLogo необходимо передавать не ссылку на файл с логотипом, а экземпляр класса изображения-лого.

$img = AcImage::createImage('image.jpg');
$logo = AcImage::createImage('logo.png');
$img->drawLogo($logo, AcImage::BOTTOM_RIGHT);

В результате, наш логотип должен разместиться в одном из углов исходного изображения, примерно, вот так:

Варианты размещения лого

Естественно, заботиться о размерах логотипа вам не следует – его размер будет пропорционально уменьшен.
Напоследок лишь стоит упомянуть о некоторых сеттерах. Так, вы можете изменить относительный размер лого. Сейчас по любой из сторон он не сможет быть больше 10% от стороны исходного изображения.

//теперь по высоте и ширине лого не будет больше 15% от исходного изображения
AcImage::setMaxProportionLogo(0.15);

Разумеется, значение, передаваемое в функцию setMaxProportionLogo должно быть больше нуля и не более единицы.

Также можно изменить отступ логотипа от края изображения. По умолчанию он равен двум процентам.

//изменили отступ от края на 5%
AcImage::setPaddingProportionLogo(0.05);

Следует, пожалуй, заметить, что на наш взгляд эти значения наиболее оптимальны и стоит подумать, прежде чем их менять.

Бросаемые методом исключения:

  • IllegalArgumentException – переданы некорректные аргументы.
  • GDnotInstalledException– библиотека GD не подключена;
  • InvalidFileException – файл не является изображением;
  • FileNotFoundException – файл не найден.

Сохранение изображения

Принцип сохранения изображений также претерпел значительные изменения. Теперь для этого существуют несколько функций: save, saveAsJPG, saveAsPNG и saveAsGIF. Первая сохраняет изображение в исходном формате, остальные — думаю, понятно.

То есть, если исходным являлось PNG-изображение, то следующим образом мы его и сохраним как PNG:

$img = AcImage::createImage('image.png');
//производим какие-либо действия с изображением
$img->save('img/image.png');

Функция save, как и три другие функции в качестве аргумента принимает адрес, по которому мы желаем сохранить картинку. Функции saveAsJPG, saveAsPNG и saveAsGIF работают аналогично, но сохраняют изображения в соответствующих форматах.

$img = AcImage::createImage('image.png');
//производим какие-либо действия с изображением
$img-> saveAsJPG('img/image.jpg');

Цепочки вызовов

Очень хорошей возможностью, как и в прошлой версии класса, являются цепочки вызовов, позволяющие удобно выполнить с изображением сразу несколько различных действий. Делается это очень просто. Например, так:

$img = AcImage:: createImage('image.jpg');
$img
   ->resize(640, 480)
   ->crop(100, 100, 400, 300)
   ->drawLogo('logo,jpg', AcImage::BOTTOM_RIGHT)
   ->save('img/image.jpg');

Таким образом, сначала изображение будет вписано в рамки 640×480, затем из него будет вырезана область (100, 100, 400, 300), на которой, впоследствии, будет нарисован логотип из файла logo.jpg. В конечном итоге, изображение будет сохранено по адресу img/image.jpg. Возможно это благодаря тому, что все функции возвращают экземпляр текущего класса — $this.

Некоторые полезные статические функции

Кроме вышеописанного функционала мы реализовали множество геттеров и сеттеров. Описывать их всех мы не станем, а остановимся лишь на основных.

isFileExists

C помощью этой статической функции мы можем ещё до создания экземпляра класса проверить, существует ли файл. В случае если файл существует, вернётся true, и false – в противном случае. Использовать это мы можем, например, так:

if(AcImage::isFileExists('image.jpg'))
   $image = AcImage::createImage('image.jpg');
else echo "Файл не существует";

isFileImage

Это также статическая функция. Она проверяет, является ли файл изображением. Например, следующим образом:

if(AcImage::isFileImage('image.jpg'))
   $image = AcImage::createImage('image.jpg');
else echo "Файл не является изображением";

setRewrite

Эта статическая функция позволяет установить флаг, который будет либо запрещать, либо разрешать сохранять изображение, если файл с таким именем уже существует.

AcImage::setRewrite(true); //разрешить перезапись при конфликте имён
AcImage::setRewrite(false); //запретить перезапись при конфликте имён

По умолчанию, перезапись при конфликте имён запрещена.

Если перезапись отключена, и скрипт пытается сохранить изображения по адресу уже существующего файла, будет брошено исключение FileAlreadyExistsException.

setQuality

Здесь всё просто. Эта статическая функция устанавливает качество сохраняемых изображений. По умолчанию установлено значение, равное 85. Диапазон возможных значений – (0, 100].

AcImage::setQuality(100);

КачествоsetTransparency

Функция указывает, стоит ли сохранять в исходных изображениях прозрачность. Возможные принимаемые значения – true (сохранять прозрачность) и false (не сохранять прозрачность).

Если значение установлено в true, то изображения с прозрачностью такими и останутся, если, разумеется, не будут сохранены в jpg. В противном случае, области с прозрачностью будут «залиты» определённым цветом (об этом расскажем далее).

Примеры использования:

AcImage::setTransparency(true); //поддержка прозрачности включена
AcImage::setTransparency(false); //поддержка прозрачности отключена

Прозрачность

setBackgroundColor

А теперь представьте, что мы загрузили некое PNG-изображение с прозрачными областями. Так что же с ним будет после сохранения в JPG? Прозрачные области будут заполнены некоторым цветом. По умолчанию – белый цвет. Однако его можно изменить. Делается это при помощи функции setBackgroundColor. Установить цвет можно двумя способами — передав RGB-составляющие или же передав экземпляр класса AcColor. Вот примеры использования обоих вариантов:

//первый вариант
AcImage::setBackgroundColor(100, 100, 100);
//второй вариант
$color = new AcColor(100, 100, 100);
AcImage::setBackgroundColor($color);

Разумеется, все RGB-составляющие должны лежать в пределах от 0 до 255. В случае передачи некорректных аргументов будет брошено исключение IllegalArgumentException.

Мы описали не все сеттеры. Если же вам они будут интересны, вы сможете их найти в документации к тулкиту.

Также к статье мы прикрепляем сам класс и несколько демок с примерами использования класса. Если вами будут обнаружены какие-либо недочёты или ошибки в работе нашего продукта, отписывайтесь в комментариях. Также мы будем рады любым предложениям и пожеланиям. Проект и дальше будет нами поддерживаться и развиваться.

Обновлено 12 марта 2013

Были исправлены ошибки, связанные с определение поддержки библиотеки GD. Были изменены методы AcImage::isSupportedGD и AcImage::getGDinfo. Исправлена ошибка в методе AcImageJPG::isSupport(), который некорректно работал для php версии ниже 5.3. Добавлен метод AcImage::getShortPHPVersion().

Поделиться
Плюсануть
Запинить
  • Максим

    Спасибо за работу парни!

  • Ого, целый мануал по работе выложен. Подробно, доступно даже для тех, кто на танке. Большое спасибо.

  • Где ссылка скачать?

  • Как где? Уже 56 человек скачали тулкит. Картинка «скачать» — ссылка.
    http://true-coder.ru/image-toolkit/download

  • Доброй ночи, мой сайт http://www.devhelper.ru схожей тематики с Вашим блогом, давайте обменяемся ссылками или еще как-нибудь посотрудничаем.

  • В итоге получаются очень приятные изображения ))

  • Дмитрий

    Огромное спасибо! Замечательный Toolkit!!!

  • ну поистине!!!Спасибо…работа на ура!!!

  • Shinji

    Зачет! Продолжайте в том же духе)

  • Толян

    А как вывести изображение из объекта сразу в браузер, не сохраняя дополнительно отредактированный вариант?

    • Право, не вспомню, почему, но мы не стали реализовывать эти функции но…
      Делается это очень просто. В статье мы не описывали все сеттеры и геттеры. Но в документации они есть. В данном случае нам необходим вот этот геттер. Он вернёт идентификатор изображения, который используйте в качестве аргумента функции imagejpeg. Разумеется, не забывай о заголовках (по ссылке в первом примере описано).

      • Саня

        Можете подробно описать? И желательно с примером. А то маюсь, а понять не могу. Заранее спасибо!

  • Круто! Отличный класс, может быть вы его на github выложите ?

  • Андрей

    Добрый день!

    Файлы больших размеров, например 4500×6000 px, не обрабатываются почему-то…

    В чем может быть проблема? Ошибок не пишет при E_ALL.

  • Андрей

    P.S. фотка при этом легкая может быть, даже 100кб. ограничение именно на масштаб фото, а не на вес.

    • Даже не могу что-то сказать. Сперва я тоже подумал про вес фото. В таком случае решалось бы это изменением некоторых настроек php. Но с другой стороны, проблема с настройками может быть и в вашем случае. Фото очень большое и содержит огромное количество пикселей. Возможно, в классе, при создании ресурса изображения, попросту не хватает памяти для фото. Покопайтесь в настройках, может что-то и получится.

      Класс же должен работать с изображениями любых размеров.

  • zickfreed

    здравствуйте. я вот чёто не могу понять как сохранять миниатюры в определённых размерах к примеру 250х200. но когда выставляю такие параметры, фотки сохраняются не в таком размере. а сохраняются по меньшей стороне. на выходе 333х200 — почему так? заранее благодарен

    • Потому что сжатие происходит пропорционально. Если бы фото ужималось строго в 250×200, то изображение деформировалось бы, что есть убого. Вам нужны либо умное создание миниатюр, либо центральный кроп.

      Умное создание миниатют:
      $img->thumbnail(250, 200);
      Картинка получится больше, чем нужно и её придётся частично прятать при выводе. Об этом написано в соответствующей главе статьи.

      Центральный кроп:
      Сперва обрезаем картинку в необходимых пропорциях (в вашем случае это 5 к 4), а затем ресайзим под конкретный размер (250×200):
      $img->cropCenter(‘5pr’, ‘4pr’)->resize(250, 200);
      Но в таком случае часть картинки обрежется.

  • Отличный урок !!!

  • Алексей

    Уважаемый Загорцев Андрей! Работает все отлично. Но вставив код на страничку php в joomla вообще перестает грузится сайт. Из-за этих строк:
    »
    $image = AcImage::createImage($filePath);

    $image
    ->thumbnail($width, $height)
    ->save($savePath);

    »
    Что мне делать??Заранее спасибо

    • Алексей, может быть в Joomla уже существует переменная $image и вы ее переопределяете. Попробуйте выбрать другое название для этой переменной.

  • Любовь

    Добрый день!
    у меня почему-то вылетает вот такая ошибка: Fatal error: Class ‘Size’ not found in F:\webhome\php\juliacaprice.it-studio.ru\ResizeCropImage\AcImage.php on line 132. Подскажите, пожалуйста, что это значит?

    • Любовь, здравствуйте.
      Ошибка гласит, что класс Size недоступен. Проверьте, находится ли соответствующий файл с таким же классом внутри каталога geometry.

      • Любовь

        Андрей, проверила, в папке geometry лежит файл Size.php. В нем прописан соответствующий класс class Size {…}. Но оказалось, что в файле AcImage.php не прописан код подключения файла Size.php. Я его подключила, теперь все работает)

  • Александр

    Делаю миниатюру
    ->thumbnail(1024, 768)

    На выходе 1023×1535. Т.е. высота в 2 раза больше заданной, это лечится?

    • Внимательно прочитайте, что делает данная функция. Она не ужимает картинку в рамки. Эта функция сжимает фото по меньшей стороне, и оно будет выходить за заданные рамки. Однако, будет полностью заполнять предложенную область. При выводе на страницу её надо будет средствами html и css прятать. Это в статье подробно описано.

      Если же вам нужно изображение строго заданного размера, то можно сделать это, например, так:

      1
      2
      3
      
      $image->thumbnail(1024, 768)
      ->crop(0, 0, 1024, 768)
      ->saveAsJPG($path);

      В данном случае сперва делается миниатюра (которую можно было бы просто спрятать на фон div’а), а затем обрезается под заданные рамки. Но в таком случае часть изображения безвозвратно теряется. Подумайте, может вам годится первый способ.

  • Максим

    public static function isSupport()
    {
    $gdInfo = parent::getGDinfo();
    return (bool)$gdInfo['JPEG Support'];
    }

    Данный метод возвращает false, если версия php ниже 5.3. Так как в GD с версии 5.3 JPG Support переименован в JPEG Support. Стоит наверное проверять версию php и передавать нужное значение. На заметку)))

    • Максим, огромное спасибо за замечание! Я проверил — описная вами ошибка действительно была. Я уже исправил эту недоработку и заменил архив, доступный для скачивания.

  • Георгий

    Добрый день!
    У меня такая проблема. При ресайзе картинки очень сильно падает цветопередача.
    Пишу такой код
    require (self::getConfig('path.lib').DS.'AcImage'.DS.'AcImage.php');
    $img = AcImage::createImage($file);
    $img::setQuality(100);
    $img->saveAsJPG($save);

    На вход подаю вот это фото
    На выходе получаю вот такую картинку.
    По идеи качество картинки не должно было упасть. Но если посмотреть например на красный цвет — то качество на выходе существенно ниже.

    Причем это только для фото в формате jpg. В какую сторону копать?

  • Марат

    Все классно, вот только один вопрос: есть возможность не сохранят картинку а просто с генерировать размер и показывать? Че то я не нашел.

    • Марат, я так понял, вы говорите о выводе на страницу, без сохранения на сервере?
      Мы подумаем, действительно ли эта возможность нужна и, весьма возможно, добавим ей в новой версии.

      • Павел Солдатов

        Надо, очень надо.

    • Павел Солдатов

      Класс посторое на старых добрых PHP фун.

      Используй
      header(«Content-type: image/png»);
      $img->save(NULL);

      Просто NULL , без всяких кавычек

      • Павел Солдатов

        и еще если при передаче параметров по GET, почему попутано с типами, приходится жестко указывать (int)

        $filePath = $_GET[‘pf’];
        $wid_ = $_GET[‘w’];
        $hei_ = $_GET[‘h’];
        require_once ‘AcImage/AcImage.php’;
        AcImage::setQuality(100);
        $img = AcImage::createImage($filePath);
        $img->thumbnail((int)$wid_, (int)$hei_, 5);
        $img->cropCenter((int)$wid_, (int)$hei_);
        header(«Content-type: image/png»);
        $img->save(NULL);

  • Новичок в этом деле, как создать форму, чтобы сохранять и в бд засововать, после обработки тулкитом

  • Gansik

    От души огромное спасибо. То что искал. Завтра буду тестить:)

  • kortes

    Жаль нет варианта с наложением логотипа по центру фото..

    • Мы решили, что логотип по центру едва ли кому-нибудь захочется накладывать и не добавляли такую возможность. Это можно было бы назвать водяными знаками. Пожалуй, подумаем над такой возможностью и, если решим, что это необходимо, обновим версию тулкита.

      • А вот я тоже хотел использовать логотип по центру, но да ладно) Будем пользоваться тем что есть. Спасибо за инструмент! Красавцы)

      • Guest

        Надо, очень надо

  • Спасибо за столь качественную статью и toolkit.
    Попробовал, потестировал — на локальной машине работает отлично, а вот когда перенёс на сервер — не загружаются картинки с удалённых сайтов. Возможно на хостинге что-то не настроено, не могли бы вы подсказать в какую сторону «копать» в поисках решения? Использую для теста demo1.php из архива и вставляю ссылку на вот эту картинку: http://true-coder.ru/wp-content/uploads/2012/11/27-opacity.jpg и ни чего не происходит, белый лист..

    • Разобрался, allow_url_fopen=on помогло.
      Ещё раз спасибо за отличный инструмент!

  • GoGo

    Спасибо, автор, либа превосходная!)) Именно то, что я искал.
    Библиотека реально очень хороша, тут все:
    Во-первых, картинки, которые получаются, полностью валидны, на них не ругаются редакторы и другие либы.
    Во-вторых, отличный ооп-синтаксис, браво!) Сделал все, что надо за 1 строку, так и надо писать программы)

  • Действительно, библиотека классная! Правда иногда для работы не хватает одной функции: создание картинки с фиксированным размером. Например, нам нужно получить картинку 200х200. Исходный файл имеет размеры 150х200. Исходную картинку разместить по центру, а не хватающее по бокам место залить фоновым цветом, скажем белым. Конечно, каждый может дописать сам эту функцию, но мне кажется она была бы полезна в библиотеке. Но в любом случае авторы молодцы!

  • Юра

    Нужно добавить возможность перезаписывать существующий файл при сохранении.

    • Читайте внимательно. Такая возможность существует.

      /*
       * После этой строки существующие файлы 
       * с изображениями будут перезаписаны
       * при конфликте имён.
       */
      AcImage::setRewrite(true);
      • Юра

        Если есть, то это хорошо. Но я бы сделал так, к примеру:

        1
        
        AcImage::save($fileName, $rewrite = true);
  • Макс

    А как все это дело можно реализовать как вконтакте? я имею ввиду загрузил фото, и сам уже на js предварительно обрезаешь, кропаешь все делаешь сам руками, жмешь сохранить-и все изменения сохраняются?

    • Infinite

      Сделал с помощью Jcrop

  • Макс

    Да именно так, но это просто js который кропает, а сохраняет и обрабатывает/проверяет то php, или я не прав? Как все это дело подключить то) можно рабочий пример?

    • Infinite

      Ставишь Jcrop, он возвращает координаты и размеры кропа, потом например аяксом отправляешь эти данные в php скрипт где подключен этот toolkit и делаешь $img = AcImage::createImage($file_path); $img->crop($x, $y, $w, $h);
      $img->save($save_path); вот и все

  • Макс

    Рабочий вариант в студию!) Ктонибудь уже сделал?

  • Макс

    Ну пожааалуйста ((

  • Здравствуйте!
    Сайт расположен на WordPress.
    Все было нормально, но сейчас при публикации выдает ошибку:

    Fatal error: Call to undefined function imgresize() in /sata1/home/users/you4/www/www.mysalon.com.ua/wp-content/themes/geoplaces/library/functions/custom_functions.php on line 1442

    Вот строки:
    1442 $new_img_path = image_resize( $file_path, $width, $height, $crop );
    1443 $new_img_size = getimagesize( $new_img_path );
    1444 $new_img = str_replace( basename( $image_src[0] ), basename( $new_img_path ), $image_src[0] );

    Я не специалист — скажите, где и какая функция у меня не заявлена?

  • Жесть

    И если уж на то пошло, не нужно использовать скрипт, который описывается в этой статье, если использовать JCrop. Достаточно лишь скрипта, который от JCrop примет и сохранит картинку с заданными координатами от него.

    • Infinite

      Может он логотип хочет запилить на картинку, зачем ему что-то писать если уже написан скрипт с нормальным функционалом?

    • Насколько я знаю, JCrop просто предоставляет удобный интерфейс для выбора области обрезки, а сама обрезка должна происходить на сервере по координатам, присланным сим скриптом. Наш тулкит для непосредственной обрезки/ресайза/кропа/лого и предназначен. Вам всё-равно придётся его использовать, ну или аналоги. JCrop — не аналог.

      P. S. В планах была идея написать что-то вроде своего JCrop, только с функционалом, подогнанным под наш тулкит.

      • Александр

        Очень хорошая идея!

  • Жесть

    А если требуется просто, загружаем изображение на сервер, далее как вконтакте например, изменяем как надо, и жмем кнопочку сохранить. Ктонибудь уже так делал?

  • Shy

    Прекрасный инструмент! Который, к тому же, несложно «перепелить» подсвой вкус.
    Есть лишь один вопрос, подскажите, может я просто что-то пропустил:

    1
    2
    3
    
    $img = AcImage::createImage('image.png');
    //производим какие-либо действия с изображением
    $img-&gt;save('img/image.png');

    Сохранит картинку как image.png, но если мы не знаем какого типа у нас файл? Может быть стоит как-то сделать так, чтобы расширение файла подставлялось автоматически? Для себя я сделал это так:

    1
    2
    3
    
    $this-&gt;putBackground(self::$backgroundColor);
    if(!imagejpeg(self::getResource(), $path.'.jpeg', self::getQuality()))
    throw new FileNotSaveException($path);

    Но выглядит не очень

  • Доброго времени суток! Планирую использовать этот класс на своих, в том числе и служебных, сайтах. Сказать, что я поражен — ничего не сказать, все отлично, великолепно, замечательно.

    Работа проделана колоссальная, готов оплатить, или разместить баннер, или иную информацию на своих сайтах, где будет использоваться данный класс.

  • Султан

    $img = AcImage::createImage(‘image.jpg’);
    $img->resize(640, 480);

    В варианте если исходное изображение image.jpg имеет размер на пример 300 на 150 (лежит внутри диапазона 640 на 480) ничего не происходит. Это ошибка или так задумано?

    • Но в таком случае изображение и не должно ресайзиться, ведь его размеры меньше требуемых рамок. Если под фразой «ничего не происходит» вы имеете ввиду это, то так и должно быть. Если же вы имеете ввиду то, что изображение не… «разжимается» до заданных размеров, то это вполне логично. Было бы большой глупостью реализовать «разжатие» изображений.

      • Султан

        Ну почему же сразу «глупостью»? Вполне нормальное желание все разношерстные фото (не только разных размеров но и разных соотношений сторон) вогнать в один размер (конечно при этом контролируя минимально разрешенные размеры высоты и ширины исходного изображения).

        • Вы ведь понимаете, что растянутое изображение сильно потеряет в качестве? Сие как увеличить битрейд аудиозаписи при конвертации и наслаждаться невесть откуда появившимся FLAC-качеством (извините за тавтологию).

  • Стас

    Доброго времени суток!

    Если вам не трудно подскажите как прописать папку для сохранения изображения, я не очень понял где она прописана?

    Заранее благодарю за ответ!

    • $img->save('img/image.jpg');

      Изображение сохранится в папку img в файл image.jpg. Замечу лишь, что в данном случае папка должна находиться в том же каталоге, что и скрипт, оперирующий загрузкой. Если требуется другая папка, скажем, на уровень выше, то читаем про адресацию.

  • Андрей

    Внимание, данный скрипт имеет какую-то уязвимость, через нее мне залили на сайт в папку «uploads» файлы.

    • Это ужасно! Особенно учитывая тот факт, что тулкит и признан сохранять в определённые папки файлы изображений.

      Опишите по-человечески, что и как заливается. В чём, собственно, проблема?

  • Дмитрий

    Всем привет, автору огромное спасибо!
    Нужна помощь, очень.
    Нужно приведенный ниже код вывести в цикле, чтобы сканировалась директория, фотки ресайзелись и корпились и в нее же сохранялись под именами mini_, а потом выводились.
    Заранее благодарен!

    resizeByWidth(220)
    ->cropCenter($width, $height)
    ->save($savePath);

    ?>

    Кроп
    <img src="» />

  • Дмитрий

    Задам вопрос яснее:
    Как сделать так, чтобы скриптом прочитывалась какая-то определенная папка, затем все находящиеся в этой папке изображения ресайзились и обрезались по правилу и затем уже в ту же папку копировались уже новые файлы с именами с префиксом mini_, а затем выводились все эти картинки на страничке по порядку?..

  • Привет. Для жаждущих реализовал возможность размещать логотип по центру. Репозиторий на гитхабе

    • Совсем забыл код для этого $image->drawLogo($logo, AcImage::CENTER);

  • Александр

    Здравствуйте. В вашем старом классе была возможность с помощью $img->cropSquare() без указания значений вырезать из изображения максимально возможный квадрат. Порылся в новом классе, но подобной возможности не нашел. Может подскажете где и что можно подкрутить чтобы реализовать такую функцию в новом классе?

  • Мегареспект за немалый труд. Пошел внедрять

  • Сергей

    Здравствуйте. Есть проблема при загрузке фотографий. Похоже что фото которые были сняты на телефон под другим углом при обработке снова сохраняются с ним. Пробовали ваш и другой класс результат один. Не сталкивались с такой проблемой?

  • Иван

    Почему вот так работает:

    1
    2
    3
    
    $size = new Size(280, 190);
    $image-&gt;resize($size);
    $image-&gt;save($uploaddir_preview.$img_original);

    А вот так нет:

    1
    2
    3
    4
    5
    
    $preview_w="280";
    $preview_h="190";
    $size = new Size($preview_w, $preview_h);
    $image-&gt;resize($size);
    $image-&gt;save($uploaddir_preview.$img_original);
    • Попробуйте убрать кавычки.

    • Потому что надо передавать цифры, а не строки 🙂

      1
      2
      
      $preview_w=280;
      $preview_h=190;
      • Иван

        Да, я лошара, спасибо, но я уже разобрался))

  • Vitaly Usatov

    Что-то не работает ваш скрипт.

    См. скрин http://joxi.ru/vDr8npxBTvnd26

  • Игорь Брежнев

    Если исходное изображение меньше, чем указано в параметрах thumbnail, то изображение еще сильнее ужимается!

    Если применять resize — такого глюка нет.
    Еще бы решить проблему с прозрачными GIF

  • Vladimir

    Скажите, а качество создаваемых миниатюр по части DPI (точек на дюйм) можно как-то регулировать?

  • Алекс Земляной

    Большое спасибо за проделанный вами труд. С небольшими доработками интегрировал на bitrix все отлично работает. Пока это единственный что я нашел класс адекватно работающий с вертикальными фото.
    P.S.
    Ненавижу вертикальные видео/фото.

  • Дмитрий

    В константах добавить:

    const TOP_CENTER = 4;
    const BOTTOM_CENTER = 5;

    Изменить:

    private static $correctCorners = array (0, 1, 2, 3, 4, 5);

    В private function getLogoPosition($corner, $width, $height)

    Добавить

    if ($corner == self::BOTTOM_CENTER || $corner == self::TOP_CENTER)
    $x = $this->getWidth()/2 — $paddingX — $width/2;
    else
    $x = $paddingX;

    И в методе $img->drawLogo появится возможность размещать лого по центру