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

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

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

  • подготовить изображение;
  • imagecopyresample до размера 10x;
  • рисовать углы со специальным цветом;
  • сделать этот цвет прозрачным;
  • уменьшить изображение до его первоначального размера

Но здесь возникает проблема: углы изображения результата (после шага 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 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

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

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

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

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

Ответ 1

После нескольких часов тестирования и удара головой о стену, я думаю, что нашел решение. Проблема заключалась в распределении прозрачного цвета с помощью 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');

Выход

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

enter image description here

В BROWSER (тот же файл png 'test.png')

enter image description here

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

Я почти забыл написать код функции:)

function 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.

Ответ 2

...

/* 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);

...

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