Форум dkLab и Denwer
Здесь общаются Web-разработчики.
Генеральный спонсор:
Хостинг «Джино»

Функция для создания уменьшенной копии изображения (Юрий Насретдинов)
Author Message
Юрий Насретдинов
Модератор



Joined: 13 Mar 2003
Posts: 8638
Карма: 221
   поощрить/наказать

Location: 007 495

PostPosted: Sat Apr 01, 2006 6:38 pm (написано за 35 минут 46 секунд)
   Post subject: Функция для создания уменьшенной копии изображения
Reply with quote

Функция send_thumbnail создаёт уменьшенную копию изображения $fullpath, и впихивает её в рамки $width x $height (по умолчанию 80х60). Если изображение меньше, чем указанные размеры, оно не растягивается. Также можно явно указать, нужно или нет использовать resampling (качественный способ уменьшения), используя параметр $resample (TRUE - использовать, FALSE - нет).

После создания, изображение тутже посылается пользователю. Кеширование данная функция не поддерживает! Из-за огромного числа различных способов это организовать, я лишь далее приведу пример, как можно реализовать кеширование изображений на основе этой функции (без правки исходного текста функции, т.к. он ещё будет дополняться).

Функция возвращает TRUE в случае успеха и FALSE в случае ошибки. Получить текст ошибки можно, вызвав функцию d_error() без параметров.

P.S. Также можно определить константу USE_RESAMPLE - если она стоит в TRUE, то используется качественный алгоритм уменьшения, если FALSE - быстрый (сделано для обратной совместимости).

P.P.S. Данная функция будет работать с GD и GD2, не будет «вылетать» при нехватке памяти (лишь возвращает false) и использует быстрый алгоритм ресеймплинга (то есть сначала создаётся промежуточное изображение, которое было ресайзнуто быстро, а потом качественно ресайзится до конечного значения - прирост скорости может быть очень ощутимым).
Code (php): скопировать код в буфер обмена
/**
 * @desc This function sends to the browser thumbnail of image $fullpath, and, if you want, you can point $width and $height for this thumbnail. default is 80x60. If $resample is set to TRUE, the quality resize algorithm will be used.
 *
 * @author Nasretdinov Yuriy <n a s r e t d i n o v [all-known-symbol] g m a i l . c o m>
 * @version 0.9.2
 * @param string $fullpath
 * @param int $width
 * @param int $height
 * @param bool $resample
 * @return bool
 */

function send_thumbnail($fullpath,$width=80,$height=60,$resample=-1)
{       
        @$w = getimagesize($fullpath);
        if (!$w) return d_error('Could not get image size');
        if ( (@$lim = return_bytes(ini_get('memory_limit'))) > 0)
        {
                $sz = ( $w[0]*$w[1] + $width*$height ) * 5 + (function_exists('memory_get_usage') ? memory_get_usage() : 0); // approximate size of image in memory (yes, 5 bytes per pixel!!)
                if ($sz >= $lim) return d_error('Memory limit exceeded');
        }
       
        if ($w[0] <= $width && $w[1] <= $height)
        {
                header('Content-type: '.$w['mime']);
                readfile($fullpath);
               
                return true;
        }
       
        if (!function_exists('imagecreate') || !function_exists('imagecopyresized')) return d_error('GD not found: either imagecreate() or imagecopyresized() do not exist.');

        $types = array('','gif','jpeg','png');
        @$ext = $types[$w[2]];
       
        if (function_exists($func = 'imagecreatefrom'.$ext)) $src = $func($fullpath);
        else return d_error('Unsupported image type (the function called is '.$func.'). Maybe, invalid extension?');
       
        header('Content-type: image/jpeg');
       
        //proportions
        $new_width = round(($height/$w[1])*$w[0]);
        $new_height = round(($width/$w[0])*$w[1]);
        if ($new_width>$width) $new_width = $width;
        if ($new_height>$height) $new_height = $height;
       
        if (!function_exists($cfunc = 'imagecreatetruecolor')) $cfunc='imagecreate';
        $thumb = $cfunc($new_width,$new_height);
       
        $func = (($resample===-1 && defined('USE_RESAMPLE') && USE_RESAMPLE) || $resample===true) && function_exists('imagecopyresampled') ? 'imagecopyresampled' : 'imagecopyresized';
       
        // optimisations for big images
        // (idea taken from http://xpoint.ru/know-how/PHP/TeoreticheskieStat'i/QuickThumbs?comments)
        $c = 2;
        if ($func != 'imagecopyresized' && ($w[0] > $c*$new_width || $w[1] > $c*$new_height))
        {
                /* TODO: add check for memory available for doing this operation */
                $thumb_c = $cfunc($c*$new_width,$c*$new_height);
                imagecopyresized($thumb_c,$src,0,0,0,0,$c*$new_width,$c*$new_height,$w[0],$w[1]);
                imagedestroy($src);
                $src = $thumb_c;
                list($w[0],$w[1]) = array($c*$new_width,$c*$new_height);
        }
       
        $func($thumb,$src,0,0,0,0,$new_width,$new_height,$w[0],$w[1]);

        imagedestroy($src);
        imagejpeg($thumb);
        imagedestroy($thumb);
       
        return true;
}
/**
 * The function from PHP manual that returns size in bytes of PHP.INI sizes: e.g. 4K , 5M , 10G
 *
 * @param string $val
 * @return int
 */

function return_bytes($val) {
    $val = trim($val);
    $last = strtolower($val{strlen($val)-1});
    switch($last) {
        // The 'G' modifier is available since PHP 5.1.0
        case 'g':
            $val *= 1024;
        case 'm':
            $val *= 1024;
        case 'k':
            $val *= 1024;
    }

    return $val;
}

// the function to log errors.
//
// If $error_text is set to 'all', the function returns an array of all error messages.
// If $error_text is not set, the function returns the last error message.
// Else it logs the error message and returns false.
//
// example of usage:
//
// if(false) return d_error('You have set "false" in "if" condition');

function d_error($error_text = 0)
{
        static $errors = array();
       
        if($error_text === 'all') return $errors;
        if($error_text === 0) return (sizeof($errors)>0 ? $errors[sizeof($errors)-1] : '');
        $errors[]=$error_text;
        return false;
}
Примеры использования:

1. Создание качественной превьюшки (thumbnail, preview, тумбнейла) изображения «на лету» - без кеширования
Code (php): скопировать код в буфер обмена
<?
include('func.php'); //пусть эти функции лежат в файле func.php

send_thumbnail('image.jpeg',200,150,true);
?>
2. То же самое, но с использованием «быстрого» алгоритма уменьшения
Code (php): скопировать код в буфер обмена
<?
include('func.php');

send_thumbnail('image.jpeg',200,150);
?>
3. Создание эскизов страниц с кешированием (например для галереи):
Code (php): скопировать код в буфер обмена
<?
$image = 'image.jpeg'; // в переменной $image будет храниться имя файла, который в данный момент обрабатывается

define('CACHE_DIR','cache'); // директория для хранения кеша (она должна уже существовать)
include('func.php');

$name = CACHE_DIR.'/'.$image.'.jpg'; // имя превьюшки (последнее ".jpg" - не ошибка!)

// если кешированный эскиз уже существует, отдать его и завершить работу

if (file_exists($name))
{
        header('Content-type: image/jpeg');
        readfile($name);
        exit();
}

// иначе - создаём новый эскиз

ob_start();
$res = send_thumbnail($image,200,150); // $res - результат создания эскиза (TRUE или FALSE)
$content = ob_get_contents();          // записываем в $content содержимое эскиза

if ($res && (@$fp = fopen($name,'wb+'))) // если всё хорошо, создаём файлик с кешем и пишем туда содержимое
{
        fputs($fp,$content);
        fclose($fp);
        ob_end_flush(); // выводим эскиз
}else
{
        @ob_end_clean(); // очищаем буфер и рапортуем об ошибке
        header('Content-type: text/html'); // функция для создания preview отправляет заголовок Content-type: image/jpeg, а буферизация вывода заголовки пропускает
        die('Could not create preview! Reason: '.d_error());
}
?>
Внимание! Данный пример является достаточно примитивным описанием того, как можно реализовать кеширование. Вы должны сами его дописать, если хотите сделать более-менее нормальную галерею, сайт с фотографиями или что-либо ещё, где требуется кеширование уменьшенных копий изображения.

Пример работы - см. аттачмент


example.jpg
 Description:
Показано 4 варианта вместе с куском кода, ответственным за вывод.
 Filesize:  111 KB
 Viewed:  2564 Time(s)

example.jpg
Щелкните, чтобы посмотреть полноразмерное изображение.





Last edited by Юрий Насретдинов on Sat Mar 17, 2007 12:30 am; edited 11 times in total
Back to top
View user's profile Send private message Send e-mail Visit poster's website ICQ Number
Антон Макаренко
Участник форума



Joined: 05 Feb 2004
Posts: 374
Карма: 30
   поощрить/наказать

Location: Киев

PostPosted: Tue Mar 25, 2008 1:03 am (спустя 1 год 11 месяцев 23 дня 6 часов 25 минут; написано за 2 минуты 41 секунду)
   Post subject:
Reply with quote

Готовое решение с альтернативной функциональностью.

Ужимает изображение из файла в файловой системе. Возвращает содержимое, или же записывает в указанный файл и возвращает NULL.
Новые размеры и mime-тип изображения будут записаны в переменную, переданную по ссылке в аргументе.
Поддерживается jpeg, gif, png.
Сохраняет прозрачность/полупрозрачность для gif и png. Экономно сохраняет png, если он индексирован (от 1 до 256 цветов).
Gif-анимацию урезает до первого кадра.
(!) Бросается эксепшенами при любых ошибках, с подробным объяснением причины.

Пример для получения содержимого:
Code (php): скопировать код в буфер обмена
$image_info = array();
$raw_data = constrain_image('test.jpg', 800, 600, $image_info);
// $raw_data содержит новое изображение в бинарном виде
// $image_info содержит новые ширину и высоту, а также тип изображения
list($new_w, $new_h, $mime_type) = $image_info;
Пример для создания файла-миниатюры:
Code (php): скопировать код в буфер обмена
$image_info = array();
constrain_image('test.png', 150, 150, $image_info, 'thumbnails/test.png');
// ничего не возвращается
// далее аналогично предыдущему примеру
 
Если исходное изображение и так умещается в указанные рамки, функция отработает в соответствии в документацией, разве что вместо GD-функций будет задействован обычный file_get_contents() или copy().
Quote:
The getimagesize() function does not require the GD image library.
Значение $overwrite имеет смысл, только если указан файл, куда записывать.

Значение $quality не имеет смысла для формата GIF и должно быть от 1 до 100 для форматов JPEG и PNG.
Поскольку в PNG используется шкала качества от 0 до 9 (0 - наилучшее, 9 - наихудшее), то функция сама корректно пересчитывает в этой шкале...
Code (php): скопировать код в буфер обмена
<?php
/**
 * Constrain an image proportionally and write it to destination file, if specified.
 *
 * Array($new_w, $new_h, $mime_type) of new constrained image will be written to $image_info
 *
 * When $dest_file is NULL, the raw contents will be returned.
 * Otherwise, contents will be written to specified file and NULL will be returned.
 *
 * Quality for image/png (0 to 9) will be rounded (and inverted) automatically from given values 1..100
 *
 * Alpha and non-alpha transparency for gif/png is preserved, if applicable.
 *
 * Gif animation is not supported (outputs gif87a)
 *
 * @param string $src_file - source file, must exist in filesystem
 * @param int $max_w - max width to constrain
 * @param int $max_h - max height to constrain
 * @param array &$image_info - new constrained width, height and mimetype will be put here
 * @param string|null $dst_file - destination file. Must NOT be equal to source
 * @param int $quality - from 1 to 100
 * @param bool $overwrite
 * @exception on any processing errors
 * @return mixed
 * @version 2.3 production/stable
 * @author Anton Makarenko
 * @link http://anton.makarenko.name/
 */

function constrain_image($src_file, $max_w, $max_h, &$image_info, $dst_file = null, $quality = 75, $overwrite = true)
{
    // check params
    $max_w = @(int)$max_w;
    $max_h = @(int)$max_h;
    if ((empty($src_file)) || ((null !== $dst_file) && empty($dst_file))
        || ($max_w <= 0) || ($max_h <= 0)
        || ($quality < 1) || ($quality > 100)
    )
        throw new Exception('Wrong incoming params specified.');

    // setup funcs for supported types
    $mime_types=array(
         'image/jpeg'  => array('imageCreateFromJpeg', 'imageJpeg')
        ,'image/gif'   => array('imageCreateFromGif''imageGif')
        ,'image/png'   => array('imageCreateFromPng''imagePng')
    );

    // check if file names are appropriate
    $src_file = realpath($src_file);
    $dst_real_file = realpath($dst_file);
    if (empty($src_file) || !file_exists($src_file))
        throw new Exception("Source file '{$src_file}' does not exist.");
    if (null !== $dst_file)
        if ((!$overwrite) && (!empty($dst_real_file)) && file_exists($dst_real_file))
            throw new Exception("Overwriting option is disabled, but target file '{$dst_real_file}' exists.");
    if ($src_file === $dst_real_file)
        throw new Exception('Source path equals to destination path.');

    // try to obtain source image size and type
    @list($src_w, $src_h, $src_type) = array_values(getimagesize($src_file));
    $src_type = image_type_to_mime_type($src_type);
    if (empty($src_w) || empty($src_h) || empty($src_type))
        throw new Exception('Failed to obtain source image properties.');

    // check if constraining required
    if (!(($src_w > $max_w) || ($src_h > $max_h)))
    {
        $image_info = array($src_w, $src_h, $src_type);

        // return raw contents
        if (null === $dst_file)
        {
            $raw_data = file_get_contents($src_file);
            if (empty($raw_data))
                throw new Exception('Constraining is not required, but failed to get source raw data.');
            return $raw_data;
        }

        // just copy the file
        if (!copy($src_file, $dst_file))
            throw new Exception('Constraining is not required, but failed to copy source file to destination file.');
        return null;
    }

    // calculate new dimensions
    $dst_w = $max_w;
    $dst_h = $max_h;
    if (($src_w - $max_w) > ($src_h - $max_h))
        $dst_h = (int)(($max_w / $src_w) * $src_h);
    else
        $dst_w = (int)(($max_h / $src_h) * $src_w);
    $image_info = array($dst_w, $dst_h, $src_type);

    // check if source type supported
    @list($create_callback, $write_callback) = $mime_types[$src_type];
    if (empty($mime_types[$src_type])
        || (!function_exists($create_callback))
        || (!function_exists($write_callback))
    )
        throw new Exception("Source image type '{$src_type}' is not supported.");

    // create source image resource and determine its colors number
    $src_img = call_user_func($create_callback, $src_file);
    if (empty($src_img))
        throw new Exception("Failed to create source image with {$create_callback}().");
    $src_colors = imagecolorstotal($src_img);

    // create destination image (indexed, if possible)
    if ($src_colors > 0 && $src_colors <= 256)
        $dst_img = imagecreate($dst_w, $dst_h);
    else
        $dst_img = imagecreatetruecolor($dst_w, $dst_h);
    if (empty($dst_img))
        throw new Exception("Failed to create blank destination image.");

    // preserve non-alpha transparency, if it is defined
    $transparent_index = imagecolortransparent($src_img);
    if ($transparent_index >= 0)
    {
        $t_c = imagecolorsforindex($src_img, $transparent_index);
        $transparent_index = imagecolorallocate($dst_img, $t_c['red'], $t_c['green'], $t_c['blue']);
        if (false === $transparent_index)
            throw new Exception('Failed to allocate transparency index for image.');
        if (!imagefill($dst_img, 0, 0, $transparent_index))
            throw new Exception('Failed to fill image with transparency.');
        imagecolortransparent($dst_img, $transparent_index);
    }

    // or preserve alpha transparency for png
    elseif ('image/png' === $src_type)
    {
        if (!imagealphablending($dst_img, false))
            throw new Exception('Failed to set alpha blending for PNG image.');
        $transparency = imagecolorallocatealpha($dst_img, 0, 0, 0, 127);
        if (false === $transparency)
            throw new Exception('Failed to allocate alpha transparency for PNG image.');
        if (!imagefill($dst_img, 0, 0, $transparency))
            throw new Exception('Failed to fill PNG image with alpha transparency.');
        if (!imagesavealpha($dst_img, true))
            throw new Exception('Failed to save alpha transparency into PNG image.');
    }

    // resample the image with new sizes
    if (!imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_w, $dst_h, $src_w, $src_h))
        throw new Exception('Failed to resample image.');

    // recalculate quality value for png image
    if ('image/png' === $src_type)
    {
        $quality = round(($quality / 100) * 10);
        if ($quality < 1)
            $quality = 1;
        elseif ($quality > 10)
            $quality = 10;
        $quality = 10 - $quality;
    }

    // write into destination file or into output buffer
    if (null === $dst_file)
        ob_start();
    if (!call_user_func($write_callback, $dst_img, $dst_file, $quality))
    {
        // do not forget to cleanup buffer ;-)
        if (null === $dst_file)
            ob_end_clean();
        throw new Exception('Failed to write destination image.');
    }
    if (null === $dst_file)
        return ob_get_clean();

    return null;
}


Last edited by Антон Макаренко on Wed Mar 26, 2008 2:07 am; edited 1 time in total
Back to top
View user's profile Send private message Send e-mail ICQ Number
Maus
Модератор



Joined: 29 Jun 2003
Posts: 8046
Карма: 264
   поощрить/наказать

Location: пос. Омсукчан Магаданской области

PostPosted: Thu Mar 27, 2008 10:49 pm (спустя 2 дня 21 час 45 минут)
   Post subject:
Reply with quote


М

Ветка выделена в отдельную тему «Функция для создания уменьшенной копии изображения - обсуждение 2»,
расположенную в форуме Разное :: PHP (27 Марта 2008, 22:49).
Back to top
View user's profile Send private message
Юрий Насретдинов
Модератор



Joined: 13 Mar 2003
Posts: 8638
Карма: 221
   поощрить/наказать

Location: 007 495

PostPosted: Fri Jul 17, 2009 2:59 pm (спустя 1 год 3 месяца 20 дней 16 часов 9 минут)
   Post subject:
Reply with quote


М

Ветка выделена в отдельную тему «сколько можно...»,
расположенную в форуме Лень сходить в Поиск или почитать документацию (17 Июля 2009, 15:59).
Back to top
View user's profile Send private message Send e-mail Visit poster's website ICQ Number
ExTRANE
Заглянувший



Joined: 13 Dec 2010
Posts: 2
Карма: 0
   поощрить/наказать


PostPosted: Mon Dec 13, 2010 2:07 am (спустя 1 год 4 месяца 26 дней 11 часов 8 минут; написано за 1 минуту 8 секунд)
   Post subject:
Reply with quote

У меня вопрос
Из-за плохо настроенного сервера у меня невозможно создать уменьшенную копию картинки!
Какой модуль на сервере отвечает за обработку фото ????
Back to top
View user's profile Send private message
bæv
Модератор «Дзена»



Joined: 27 Aug 2003
Posts: 6995
Карма: ∞
   поощрить/наказать


PostPosted: Mon Dec 13, 2010 7:39 am (спустя 5 часов 31 минуту; написано за 12 секунд)
   Post subject:
Reply with quote

ExTRANE:
Юрий Насретдинов wrote:
Данная функция будет работать с GD и GD2
Back to top
View user's profile Send private message Visit poster's website
ExTRANE
Заглянувший



Joined: 13 Dec 2010
Posts: 2
Карма: 0
   поощрить/наказать


PostPosted: Mon Dec 13, 2010 3:39 pm (спустя 8 часов 21 секунду; написано за 1 минуту)
   Post subject:
Reply with quote

Эти модули установлены и вроде бы в работоспособном состоянии ...
Чтоб может быть еще?
Сами скрипты движка сайта - исключено ...
Back to top
View user's profile Send private message
Maus
Модератор



Joined: 29 Jun 2003
Posts: 8046
Карма: 264
   поощрить/наказать

Location: пос. Омсукчан Магаданской области

PostPosted: Mon Dec 13, 2010 5:57 pm (спустя 2 часа 17 минут; написано за 24 секунды)
   Post subject:
Reply with quote

ExTRANE
http://phpfaq.ru/debug
Back to top
View user's profile Send private message
Юрий Насретдинов
Модератор



Joined: 13 Mar 2003
Posts: 8638
Карма: 221
   поощрить/наказать

Location: 007 495

PostPosted: Sat Dec 18, 2010 11:42 pm (спустя 5 дней 5 часов 45 минут; написано с мобильной версии)
   Post subject:
Reply with quote

Функция d_error() должна вернуть текст ошибки. Если GD есть, то, скорее всего, дело в нехватке памяти.
Back to top
View user's profile Send private message Send e-mail Visit poster's website ICQ Number
Display posts from previous:   
Post new topic   Reply to topic All times are GMT + 3 Hours
Page 1 of 1   
You cannot post new topics in this forum. You cannot reply to topics in this forum. You cannot edit your posts in this forum. You cannot delete your posts in this forum. You cannot vote in polls in this forum. You cannot attach files in this forum. You can download files in this forum.
  XML