Posts Tagged: превью

Создание превьюшек в PHP

Создать экскиз-превью по изображению в PHP – задача тривиальная, необходимо только воспользоваться встроенными функциями imagecopyresampled() и imagejpeg().

Однако есть две вещи, которые...

Однако есть две вещи, которые хочется улучшить в этом процессе:

  1. Сделать изображение квадратным, обрезав лишнее слева и справа или сверху и снизу в равных количествах в случае, если изображение вытянуто в ширину, либо в высоту, соответственно.
  2. Зашарпить изображение.

Очень просто всё сделать становится с помощью этого класса:

/**
 * Пример использования:
 * <code>
 * $cover = new Cover("/images/cover.jpg");
 * $img = $cover->getPreparedImage();
 * imagejpeg($img, "/images/cover_preview.jpg", 96);
 * </code>
 * Либо сразу:
 * <code>
 * $cover = new Cover("/images/cover.jpg");
 * $cover->prepareAndWrite("/images/cover_preview.jpg");
 * </code>
 * 
 * http://kremer.pro/
 */
class Cover {

    /**
     * Сторона конечного изображения в пикселях по умолчанию.
     */
    const PREVIEW_SIZE = 128;

    /**
     * Максимальный размер исходного изображения в пикселях
     * (Ограничение на работу с изображением).
     */
    const MAX_ORIG_SIZE = 2000;

    const DEFAULT_JPEG_QUALITY = 92;

    private $size;

    /**
     * @param string $path путь к изображению
     * @param string $previewSize размер конечного изображения
     */
    public function __construct($path, $previewSize = self::PREVIEW_SIZE) {
        $this->img = imagecreatefromjpeg($path);
        if (!$this->img)
            throw new Exception("Unable to create from jpeg");
        $this->size = $previewSize;
    }

    public function __destruct() {
        imagedestroy($this->img);
    }

    /**
     * Шарпит изображение.
     */
    private function sharpen() {
        $filter = array ( // стандартный вид шарп-матрицы
            array( 0,  -1,   0),

            array(-1,   9,  -1),

            array( 0,  -1,   0),
        );
        $divisor = array_sum(array_map('array_sum', $filter));

        if (!imageconvolution($this->img, $filter, $divisor, 0))
            throw new Exception("Unable to sharpen");
    }

    /**
     * Создаёт незашарпленное квадратное превью.
     */
    private function cropAndResize() {
        $img_preview = imagecreatetruecolor($this->size, $this->size);

        $width_orig = imagesx($this->img);
        $height_orig = imagesy($this->img);

        if ($width_orig <= self::PREVIEW_SIZE && $height_orig <= self::PREVIEW_SIZE) {
            // нет смысла растягивать, лучше сразу зашарпить маленькое
            return;
        }
        if ($width_orig > self::MAX_ORIG_SIZE || $height_orig > self::MAX_ORIG_SIZE)
            throw new Exception("Image too lagre to process it");

        // генерируем квадратное изображение, обрезая либо полосы слева и справа,
        // либо серху и снизу:
        if ($width_orig > $height_orig) {
            $a = round(($width_orig - $height_orig)/2);//x координата, откуда режем
            if (!imagecopyresampled($img_preview, $this->img,
                0,              0,              $a,             0,
                $this->size,    $this->size,    $height_orig,    $height_orig)) {
                throw new Exception("Unable to resample");
            }
        } else {
            $a = round(($height_orig - $width_orig)/2);//y координата, откуда режем
            if (!imagecopyresampled ($img_preview, $this->img,
                0,              0,              0,              $a,
                $this->size,    $this->size,    $width_orig,    $width_orig)) {
                throw new Exception("Unable to resample");
            }
        }

        $this->img = $img_preview; // теперь в объекте уже превьюшка
    }

    /**
     * @return resource $img - сжатое и зашарпленное изображение.
     * Выбрасывает исключения в случаях, если обработать изображение не удалось.
     */
    public function getPreparedImage() {
        $this->cropAndResize();
        $this->sharpen();
        return $this->img;
    }

    /**
     * Сразу делает превью и записывает его в файл.
     * Выбрасывает исключения в случаях, если обработать изображение не удалось.
     * После записи освобождает resource с изображением.
     */
    public function prepareAndWrite($path, $q = self::DEFAULT_JPEG_QUALITY) {
        imagejpeg($this->getPreparedImage(), $path, $q);
        imagedestroy($this->img);
    }

    public function clear() {
        imagedestroy($this->img);
    }

    private $img; // resource изображение, с которым работаем
}