Закругленные прозрачные _smooth_ углы с использованием imagecopyresampled () PHP GD

Мне нужен скрипт, который делает закругленные прозрачные углы на изображении. Я нашел один, и он работает хорошо, за исключением одного: нанесенные углы не выглядят гладкими. imageantialias() выдает фатальную ошибку, поскольку PHP работает на Debian, и его повторная компиляция невозможна.

Уловка, которую я нашел, чтобы эти углы выглядели гладкими, заключается в изменении размера изображения с помощью _2 _ следующим образом:

  1. подготовить изображение;
  2. imagecopyresample до 10-кратного размера;
  3. углы прорисовываем особым цветом;
  4. сделайте этот цвет прозрачным;
  5. уменьшить изображение до исходного размера

Но здесь возникает проблема: углы результирующего изображения (после шага 5) гладкие, но не прозрачные < / а>. При отправке на вывод изображения после шага 4 (т.е. перед уменьшением его размера) - все в порядке как и должно быть.

Вот часть кода, отвечающая за скругление углов:

    //    $dest = image resource


        $q=10;
        // making everything 10x bigger
        $new_width=$width*$q;
        $new_height=$height*$q;
        $radius=$radius*$q;

        $magnified=imagecreatetruecolor($new_width, $new_height);
        imagecopyresampled($magnified, $dest, 0,0, 0,0, $new_width,$new_height, ($new_width/$q),($new_height/$q));

        // picking the unique colour
        $found = false;
        while($found == false) {
            $r = rand(0, 255);
            $g = rand(0, 255);
            $b = rand(0, 255);
            if(imagecolorexact($magnified, $r, $g, $b) != (-1)) {
                $found = true;
            }
        }
        $colorcode = imagecolorallocate($magnified, $r, $g, $b);

            // drawing corners
            imagearc($magnified, $radius-1, $radius-1, $radius*2, $radius*2, 180, 270, $colorcode);
            imagefilltoborder($magnified, 0, 0, $colorcode, $colorcode);
            imagearc($magnified, $new_width-$radius, $radius-1, $radius*2, $radius*2, 270, 0, $colorcode);
            imagefilltoborder($magnified, $new_width-1, 0, $colorcode, $colorcode);
            imagearc($magnified, $radius-1, $new_height-$radius, $radius*2, $radius*2, 90, 180, $colorcode);
            imagefilltoborder($magnified, 0, $new_height-1, $colorcode, $colorcode);
            imagearc($magnified, $new_width-$radius, $new_height-$radius, $radius*2, $radius*2, 0, 90, $colorcode);
            imagefilltoborder($magnified, $new_width-1, $new_height-1, $colorcode, $colorcode);

        // making the unique colour transparent
        imagecolortransparent($magnified, $colorcode);

        // scaling down the enlarged image to it's original size
        // expecting corners to remain transparent
        imagecopyresampled($dest, $magnified, 0,0, 0,0, ($new_width/$q),($new_height/$q), $new_width,$new_height);
        // but they're not
        // sending $magnified to output for testing purposes
        $dest=$magnified;

    //    outputting $dest as image/png

Итак, как видите, проблема возникает, когда увеличенное изображение копируется в исходный размер. Прозрачные углы заполняются цветом $colorcode. Я играл с imagesavealpha() и _ 5_ как рекомендуется, но безрезультатно.

Пожалуйста, помогите мне сделать эту работу.

P.S. Это может быть полезно: при загрузке большого PNG на imgur.com он был преобразован в JPG, и, как вы можете видеть, все углы были заполнены этим очень восстановленным $ цветовой код.

P.S. Надеюсь, меня не забанят за чрезмерное употребление слова «расширение» :)


person aexl    schedule 23.04.2011    source источник
comment
Эта строка вызывает фатальную ошибку, если вы устанавливаете большое W / H (не забывайте, что вы умножаете исходное W / H на 10) ... $magnified=imagecreatetruecolor($new_width, $new_height);. Вы можете выйти за пределы памяти (или ограничения, которые вы установили в php.ini).   -  person Wh1T3h4Ck5    schedule 24.04.2011
comment
@ Wh1T3h4Ck5 У меня есть предопределенный массив типов изображений, которые могут мне понадобиться, которые содержат информацию о ширине, высоте и углах. Я постоянно получал imageantialias() фатальную ошибку перед трюками с размером образа, так что я уверен, что это проблема Debian.   -  person aexl    schedule 24.04.2011


Ответы (3)


После пары часов тестирования и удара головой о стену, я думаю, что нашел решение. Проблема заключалась в выделении прозрачного цвета с помощью imagecolorallocate(). Не понял с первого взгляда. Это был совершенно неправильный подход. Однако imagecolorallocatealpha() мне очень помог.

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

  $im = imagecreatetruecolor($w, $h);
  $alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127);
  imagealphablending($im, false);
  imagesavealpha($im, true);

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

Ведь я написал эту функцию

  function imageCreateCorners($sourceImageFile, $radius) {
  # function body
  }

Я протестировал его с несколькими изображениями, и он вернул изображение с гладкими углами для каждого цвета bg.

  imagepng(imageCreateCorners('jan_vesely_and_james_gist.jpg', 9), 'test.png');

Вывод

Исходное изображение

введите описание изображения здесь

В БРАУЗЕРЕ (тот же файл png test.png)

введите описание изображения здесь

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

Чуть не забыл опубликовать код функции :)

функция imageCreateCorners ($ sourceImageFile, $ radius)

  function imageCreateCorners($sourceImageFile, $radius) {
    # test source image
    if (file_exists($sourceImageFile)) {
      $res = is_array($info = getimagesize($sourceImageFile));
      } 
    else $res = false;

    # open image
    if ($res) {
      $w = $info[0];
      $h = $info[1];
      switch ($info['mime']) {
        case 'image/jpeg': $src = imagecreatefromjpeg($sourceImageFile);
          break;
        case 'image/gif': $src = imagecreatefromgif($sourceImageFile);
          break;
        case 'image/png': $src = imagecreatefrompng($sourceImageFile);
          break;
        default: 
          $res = false;
        }
      }

    # create corners
    if ($res) {

      $q = 10; # change this if you want
      $radius *= $q;

      # find unique color
      do {
        $r = rand(0, 255);
        $g = rand(0, 255);
        $b = rand(0, 255);
        }
      while (imagecolorexact($src, $r, $g, $b) < 0);

      $nw = $w*$q;
      $nh = $h*$q;

      $img = imagecreatetruecolor($nw, $nh);
      $alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127);
      imagealphablending($img, false);
      imagesavealpha($img, true);
      imagefilledrectangle($img, 0, 0, $nw, $nh, $alphacolor);

      imagefill($img, 0, 0, $alphacolor);
      imagecopyresampled($img, $src, 0, 0, 0, 0, $nw, $nh, $w, $h);

      imagearc($img, $radius-1, $radius-1, $radius*2, $radius*2, 180, 270, $alphacolor);
      imagefilltoborder($img, 0, 0, $alphacolor, $alphacolor);
      imagearc($img, $nw-$radius, $radius-1, $radius*2, $radius*2, 270, 0, $alphacolor);
      imagefilltoborder($img, $nw-1, 0, $alphacolor, $alphacolor);
      imagearc($img, $radius-1, $nh-$radius, $radius*2, $radius*2, 90, 180, $alphacolor);
      imagefilltoborder($img, 0, $nh-1, $alphacolor, $alphacolor);
      imagearc($img, $nw-$radius, $nh-$radius, $radius*2, $radius*2, 0, 90, $alphacolor);
      imagefilltoborder($img, $nw-1, $nh-1, $alphacolor, $alphacolor);
      imagealphablending($img, true);
      imagecolortransparent($img, $alphacolor);

      # resize image down
      $dest = imagecreatetruecolor($w, $h);
      imagealphablending($dest, false);
      imagesavealpha($dest, true);
      imagefilledrectangle($dest, 0, 0, $w, $h, $alphacolor);
      imagecopyresampled($dest, $img, 0, 0, 0, 0, $w, $h, $nw, $nh);

      # output image
      $res = $dest;
      imagedestroy($src);
      imagedestroy($img);
      }

    return $res;
    }

Функция возвращает объект GD или false.


Функция работает с твердыми изображениями JPEG, GIF и PNG. Кроме того, он отлично работает с прозрачными PNG и GIF.

person Wh1T3h4Ck5    schedule 23.04.2011
comment
@ Wh1T3h4Ck5 спасибо за ответ. 1). Я должен признать, что исправление цвета фона - это не вариант ... Я имею в виду, что изображения могут использоваться поверх div, заполненных градиентным фоном, поэтому мне все еще нужны прозрачные углы. 3). Да, я уменьшаю $magnified до $dest и экспортирую последнее. Я пробовал imagecolortransparent($dest, $colorcode);, но дело в том, что мне нужно удалить $colorcode перед уменьшением масштаба изображения, чтобы углы оставались чистыми. 4). Я использовал это для тестовых целей - просто чтобы экспортировать $magnified и посмотреть, как это выглядит. - person aexl; 24.04.2011
comment
5). Да, у меня header(); на этапе экспорта с настройками управления типом содержимого и кешем. Для закругленных изображений я отправляю заголовки image / png. Я могу использовать $q=5; вместо 10 для экономии памяти, это нормально, но мне нужно, чтобы этот скрипт работал должным образом. 6). Углы в вашем примере прозрачные или с фиксированным фоном? Я вижу черный шум на руке парня, вы убрали $colorcode после уменьшения? - person aexl; 24.04.2011
comment
@ Wh1T3h4Ck5 да, это потому, что imagecolortransparent($dest, $colorcode); удаляет $colorcode из уже уменьшенного изображения и сохраняет полупрозрачность его части. Мне нужно сделать $colorcode прозрачным, когда изображение все еще большое, а закругленные границы резкие. - person aexl; 24.04.2011
comment
@The - Наконец-то работает :) Ура, дружище. Если честно, для меня это было настоящим испытанием. - person Wh1T3h4Ck5; 24.04.2011
comment
@ Wh1T3h4Ck5 Я занимаюсь веб-разработкой с начала 2006 года, и это первый раз, когда я получил такой посвященный, полезный и полный ответ в Интернете. Большое тебе спасибо! Теперь это работает. - person aexl; 24.04.2011
comment
Очень дорогое решение для памяти. Нужно скопировать 4 угла на одно изображение (конечно, с некоторым зазором). Затем сделайте увеличенную версию этого изображения, разместите здесь скругленные углы, уменьшите и скопируйте углы обратно. Это использует меньше памяти. Значительно меньше. Например, если у вас есть изображение 1024 * 1024 и вы хотите округлить его до 10 пикселей. Исходный код создает временное изображение размером 400 МБ. Уменьшенная версия использует только ~ 230 КБ. Требуется больше кода, но оно того стоит. - person Enyby; 03.10.2018

...

/* rounded corner */
$radius = 20;

// find ghost color
do
{
  $r = rand(0, 255);
  $g = rand(0, 255);
  $b = rand(0, 255);
} while (imagecolorexact($img_resource, $r, $g, $b) < 0);
$ghost_color = imagecolorallocate($img_resource, $r, $g, $b);

imagearc($img_resource, $radius-1, $radius-1, $radius*2, $radius*2, 180, 270, $ghost_color);
imagefilltoborder($img_resource, 0, 0, $ghost_color, $ghost_color);
imagearc($img_resource, $img_width-$radius, $radius-1, $radius*2, $radius*2, 270, 0, $ghost_color);
imagefilltoborder($img_resource, $img_width-1, 0, $ghost_color, $ghost_color);
imagearc($img_resource, $radius-1, $img_height-$radius, $radius*2, $radius*2, 90, 180, $ghost_color);
imagefilltoborder($img_resource, 0, $img_height-1, $ghost_color, $ghost_color);
imagearc($img_resource, $img_width-$radius, $img_height-$radius, $radius*2, $radius*2, 0, 90, $ghost_color);
imagefilltoborder($img_resource, $img_width-1, $img_height-1, $ghost_color, $ghost_color);

imagecolortransparent($img_resource, $ghost_color);

...

Попробуй это ...

person jiappo    schedule 17.10.2013

Улучшенный код от @ Wh1T3h4Ck5. Эта функция делает снимок и делает для него скругленные углы, не тратя лишнюю память. Также он должен работать быстрее с огромными изображениями. Например, для изображения 1024 * 1024 требуется временное изображение размером 400 МБ в исходном коде. Теперь всего 230 КБ. (если вы используете радиус 10 пикселей).

Функция принимает объект изображения GD, радиус в пикселях и возвращает объект изображения GD. В настоящее время он такой же, как исходный объект изображения GD.

Функция предполагает, что размер вашего изображения больше радиуса. Точно оно должно быть больше (или равно) от ($radius + 2)*2 с любой стороны.

Также функция устанавливает imagealphablending в true для этого изображения. Если вам нужно сохранить в png, не забудьте установить imagesavealpha на true.

function roundCorners($source, $radius) {
    $ws = imagesx($source);
    $hs = imagesy($source);

    $corner = $radius + 2;
    $s = $corner*2;

    $src = imagecreatetruecolor($s, $s);
    imagecopy($src, $source, 0, 0, 0, 0, $corner, $corner);
    imagecopy($src, $source, $corner, 0, $ws - $corner, 0, $corner, $corner);
    imagecopy($src, $source, $corner, $corner, $ws - $corner, $hs - $corner, $corner, $corner);
    imagecopy($src, $source, 0, $corner, 0, $hs - $corner, $corner, $corner);

    $q = 8; # change this if you want
    $radius *= $q;

    # find unique color
    do {
        $r = rand(0, 255);
        $g = rand(0, 255);
        $b = rand(0, 255);
    } while (imagecolorexact($src, $r, $g, $b) < 0);

    $ns = $s * $q;

    $img = imagecreatetruecolor($ns, $ns);
    $alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127);
    imagealphablending($img, false);
    imagefilledrectangle($img, 0, 0, $ns, $ns, $alphacolor);

    imagefill($img, 0, 0, $alphacolor);
    imagecopyresampled($img, $src, 0, 0, 0, 0, $ns, $ns, $s, $s);
    imagedestroy($src);

    imagearc($img, $radius - 1, $radius - 1, $radius * 2, $radius * 2, 180, 270, $alphacolor);
    imagefilltoborder($img, 0, 0, $alphacolor, $alphacolor);
    imagearc($img, $ns - $radius, $radius - 1, $radius * 2, $radius * 2, 270, 0, $alphacolor);
    imagefilltoborder($img, $ns - 1, 0, $alphacolor, $alphacolor);
    imagearc($img, $radius - 1, $ns - $radius, $radius * 2, $radius * 2, 90, 180, $alphacolor);
    imagefilltoborder($img, 0, $ns - 1, $alphacolor, $alphacolor);
    imagearc($img, $ns - $radius, $ns - $radius, $radius * 2, $radius * 2, 0, 90, $alphacolor);
    imagefilltoborder($img, $ns - 1, $ns - 1, $alphacolor, $alphacolor);
    imagealphablending($img, true);
    imagecolortransparent($img, $alphacolor);

    # resize image down
    $dest = imagecreatetruecolor($s, $s);
    imagealphablending($dest, false);
    imagefilledrectangle($dest, 0, 0, $s, $s, $alphacolor);
    imagecopyresampled($dest, $img, 0, 0, 0, 0, $s, $s, $ns, $ns);
    imagedestroy($img);

    # output image
    imagealphablending($source, false);
    imagecopy($source, $dest, 0, 0, 0, 0, $corner, $corner);
    imagecopy($source, $dest, $ws - $corner, 0, $corner, 0, $corner, $corner);
    imagecopy($source, $dest, $ws - $corner, $hs - $corner, $corner, $corner, $corner, $corner);
    imagecopy($source, $dest, 0, $hs - $corner, 0, $corner, $corner, $corner);
    imagealphablending($source, true);
    imagedestroy($dest);

    return $source;
}
person Enyby    schedule 03.10.2018
comment
Не тестировал, поскольку у меня больше нет доступа к этому коду (в конце концов, прошло 7 лет), но голосую за усилия. Спасибо! - person aexl; 04.10.2018
comment
ATB - семь лет ¯_ (ツ) _ / ¯ - person aexl; 04.10.2018