【问题标题】:Rounded transparent _smooth_ corners using imagecopyresampled() PHP GD使用 imagecopyresampled() PHP GD 圆角透明 _smooth_ 角
【发布时间】:2011-08-11 15:10:19
【问题描述】:

我需要一个在提供的图像上制作圆形透明角的脚本。我找到了一个,它工作得很好,除了一件事:应用的角落看起来不光滑。 imageantialias()PHP is running on Debian 以来引发致命错误,并且无法重新编译它。

我发现让这些角落看起来平滑的技巧是使用imagecopyresampled() 调整图像大小,如下所示:

  1. 准备图像;
  2. imagecopyresample 到 10 倍大小;
  3. 用特殊颜色绘制角;
  4. 使该颜色透明;
  5. 将图片缩小到原来的大小

但是问题来了:结果图像的角(在第 5 步之后)是smooth, but not transparent。在步骤 4 之后发送以输出图像时(即在减小其大小之前)–everything's as it should be.

下面是负责圆角的代码部分:

// $dest = 图片资源 $q=10; // 让一切都变大 10 倍 $新宽度=$宽度*$q; $新高度=$高度*$q; $半径=$半径*$q; $magnified=imagecreatetruecolor($new_width, $new_height); imagecopyresampled($magnified, $dest, 0,0, 0,0, $new_width,$new_height, ($new_width/$q),($new_height/$q)); // 选择唯一的颜色 $找到=假; 而($找到==假){ $r = 兰德(0, 255); $g = 兰德(0, 255); $b = 兰德(0, 255); if(imagecolorexact($magnified, $r, $g, $b) != (-1)) { $找到=真; } } $colorcode = imagecolorallocate($magnified, $r, $g, $b); // 画角 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); // 使独特的颜色透明 imagecolortransparent($magnified, $colorcode); // 将放大后的图片缩小到原来的大小 // 期望角落保持透明 imagecopyresampled($dest, $magnified, 0,0, 0,0, ($new_width/$q),($new_height/$q), $new_width,$new_height); // 但他们不是 // 发送 $magnified 到输出以进行测试 $dest=$放大; // 将 $dest 输出为 image/png

如您所见,当放大的图像被图像复制重新采样到其原始大小时,就会出现问题。透明的角落被$colorcode 颜色填充。我一直在玩imagesavealpha()imagealphablending() 作为advised,但没有结果。

请帮助我完成这项工作。

附:这可能很有用:当将 large PNG 上传到 imgur.com 时,它有 converted to JPG 并且您可以看到所有角落都充满了恢复后的 $colorcode。

附:希望我不会因为过度使用“放大”这个词而被禁止:)

【问题讨论】:

  • 如果您设置大 W/H,此行会导致致命错误(不要忘记您将原始 W/H 乘以 10)...$magnified=imagecreatetruecolor($new_width, $new_height);。您可以达到内存限制(或您在 php.ini 中设置的限制)。
  • @Wh1T3h4Ck5 我有一个我可能需要的预定义图像类型数组,其中包含有关宽度、高度和角的信息。在使用图像大小的技巧之前,我一直收到imageantialias() 致命错误,所以我确定这是 Debian 的问题。

标签: php png transparency gd rounded-corners


【解决方案1】:

经过几个小时的测试并将我的头撞到墙上,我想我找到了解决方案。问题是关于使用imagecolorallocate() 分配透明颜色。我第一眼没看懂。这是完全错误的做法。不过,imagecolorallocatealpha() 帮了我很多忙。

此外,在工作层上保存 Alpha 通道之前,必须关闭 Alpha 混合。但是,必须在创建空白真彩色图像后立即完成,例如

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

此代码是resize-down后在透明区域获得平滑角的关键。

毕竟这个函数是我写的

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

我已经用几张图像对其进行了测试,它返回的图像对于每种背景颜色都有平滑的边角。

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

输出

原图

在浏览器中(相同的 png 文件“test.png”)

它最终返回完全透明的 Alpha 通道,因此您可以在所需的每个背景上使用该图像。

我差点忘了贴功能代码:)

函数 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。

【讨论】:

  • @Wh1T3h4Ck5 感谢您的回答。 1)。我不得不承认修复背景颜色不是一种选择......我的意思是图像可以用于填充渐变背景的 div,所以我仍然需要透明角。 3)。是的,我将$magnified 缩小到$dest 并导出后者。我试过imagecolortransparent($dest, $colorcode);,但关键是我需要在之前 删除$colorcode,以保持角落清晰。 4)。我将它用于 test 目的 - 只是为了导出 $magnified 并查看它的样子。
  • 5)。是的,我在导出阶段有header();,带有内容类型和缓存控制设置。对于圆形图像,我发送 image/png 标题。我可以使用 $q=5; 而不是 10 来节省内存,这很好,但我需要这个脚本首先按预期工作。 6)。您的示例中的角是透明的还是具有固定背景的?我看到那家伙的手上有黑色的“噪音”,你缩小后是否删除了$colorcode
  • @Wh1T3h4Ck5 是的,那是因为imagecolortransparent($dest, $colorcode); removes $colorcode from the already scaled down image,并保留了它的半透明部分。当图像仍然很大并且圆形边框清晰时,我需要使$colorcode 透明。
  • @The - 终于成功了 :) 干杯伙伴。老实说,这对我来说是真正的挑战。
  • @Wh1T3h4Ck5 自 2006 年初以来,我一直是一名网络开发人员,这是我第一次在互联网上得到如此专注、有用和完整的答案。非常感谢!现在可以了。
【解决方案2】:

来自@Wh1T3h4Ck5 的改进代码。 这个功能可以拍摄图像并为它制作圆角,而不会浪费内存。它还必须在大图像上更快地工作。 例如 1024*1024 图像在原始代码中需要 400MB 临时图像。现在只有 230 KB。 (如果您使用半径 10 像素)。

函数获取 GD 图像对象,以 px 为单位的半径并返回 GD 图像对象。目前它与原始 GD 图像对象相同。

功能假设您的图像尺寸从半径较大。确切地说,它需要大于(或等于)任何一侧的($radius + 2)*2

还将此图像的功能设置为 imagealphablendingtrue。如果你需要保存到 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;
}

【讨论】:

  • 尚未测试,因为我无法再访问该代码(毕竟已经 7 年了),但对这项工作表示支持。谢谢!
  • ATB - Seven Years¯_(ツ)_/¯
  • 这非常有效。谢谢!
【解决方案3】:
...

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

...

试试这个...

【讨论】:

    猜你喜欢
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-20
    • 1970-01-01
    • 1970-01-01
    • 2010-12-26
    • 2020-11-29
    相关资源
    最近更新 更多