• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • imagefilledpolygon()

    (PHP 4, PHP 5, PHP 7)

    画一多边形并填充

    说明

    imagefilledpolygon(resource $image,array $points,int $num_points,int $color): bool

    imagefilledpolygon()$image图像中画一个填充了的多边形。

    $points参数是一个按顺序包含有多边形各顶点的xy坐标的数组。

    $num_points参数是顶点的总数,必须大于 3。

    imagefilledpolygon()例子

    <?php
    // 建立多边形各顶点坐标的数组
    $values = array(
                40,  50,  // Point 1 (x, y)
                20,  240, // Point 2 (x, y)
                60,  60,  // Point 3 (x, y)
                240, 20,  // Point 4 (x, y)
                50,  40,  // Point 5 (x, y)
                10,  10   // Point 6 (x, y)
                );
    // 创建图像
    $image = imagecreatetruecolor(250, 250);
    // 设定颜色
    $bg   = imagecolorallocate($image, 200, 200, 200);
    $blue = imagecolorallocate($image, 0, 0, 255);
    // 画一个多边形
    imagefilledpolygon($image, $values, 6, $blue);
    // 输出图像
    header('Content-type: image/png');
    imagepng($image);
    imagedestroy($image);
    ?>
    
    <?php function _makeFiveSidedStar( $x, $y, $radius, $shape='polygon', $spiky=NULL ) {
      // $x, $y co-ords of origin (in pixels), $radius (in pixels), $shape - 'polygon' or 'star', $spikiness - ratio between 0 and 1
      $point = array() ;                                                          
      $angle = 360 / 5 ;                                                          
      $point[0]['x'] = $x ;                                                         
      $point[0]['y'] = $y - $radius ;                                                    
      $point[2]['x'] = $x + ( $radius * cos( deg2rad( 90 - $angle ) ) ) ; 
      $point[2]['y'] = $y - ( $radius * sin( deg2rad( 90 - $angle ) ) ) ;
      $point[4]['x'] = $x + ( $radius * sin( deg2rad( 180 - ( $angle*2 ) ) ) ) ;
      $point[4]['y'] = $y + ( $radius * cos( deg2rad( 180 - ( $angle*2 ) ) ) ) ;
      $point[6]['x'] = $x - ( $radius * sin( deg2rad( 180 - ( $angle*2 ) ) ) ) ;                              
      $point[6]['y'] = $y + ( $radius * cos( deg2rad( 180 - ( $angle*2 ) ) ) ) ;
      $point[8]['x'] = $x - ( $radius * cos( deg2rad( 90 - $angle ) ) ) ;                                  
      $point[8]['y'] = $y - ( $radius * sin( deg2rad( 90 - $angle ) ) ) ;
      if( $shape == 'star' ) {
        if( $spiky == NULL ) $spiky = 0.5 ; // default to 0.5
        $indent = $radius * $spiky ;
        $point[1]['x'] = $x + ( $indent * cos( deg2rad( 90 - $angle/2 ) ) ) ;                               
        $point[1]['y'] = $y - ( $indent * sin( deg2rad( 90 - $angle/2 ) ) ) ;                           
        $point[3]['x'] = $x + ( $indent * sin( deg2rad( 180 - $angle ) ) ) ;                               
        $point[3]['y'] = $y - ( $indent * cos( deg2rad( 180 - $angle ) ) ) ;
        $point[5]['x'] = $x ;                                                       
        $point[5]['y'] = $y + ( $indent * sin( deg2rad( 180 - $angle ) ) ) ;
        $point[7]['x'] = $x - ( $indent * sin( deg2rad( 180 - $angle ) ) ) ;                               
        $point[7]['y'] = $y - ( $indent * cos( deg2rad( 180 - $angle ) ) ) ;                               
        $point[9]['x'] = $x - ( $indent * cos( deg2rad( 90 - $angle/2 ) ) ) ;                               
        $point[9]['y'] = $y - ( $indent * sin( deg2rad( 90 - $angle/2 ) ) ) ;
      }
      ksort( $point ) ;
      $coords = array() ; // new array                                                         
      foreach( $point as $pKey=>$pVal ) {                                                  
        if( is_array( $pVal ) ) {                                                     
          foreach( $pVal as $pSubKey=>$pSubVal ) {                                           
            if( !empty( $pSubVal ) ) array_push( $coords, $pSubVal ) ;                                
          }                                                               
        }                                                                 
      }
      return $coords ;
    }
    $values = _makeFiveSidedStar( 100, 100, 50, 'star' ) ;
    // Put values into imagepolygon function. You need to define the $image and $color, and flush it out to an image type.?>
    
    In spite of what it says about requiring more than 3 vertices, it is possible to draw a triangle with this function!
    How to draw a simple 6-sided star img where x,y is center of the star and s is the size:
    function drawStar($img, $x, $y, $s, $color) {
      $x=$x-$s/2;
      $y=$y-$s/4;
      $points=array($x,$y, $x+$s/2,$y+$s, $x+$s,$y);
      imagefilledpolygon($img, $points, 3, $color);
      $points=array($x,2/3*$s+$y, $x+$s/2,$y-$s/3, $x+$s,2/3*$s+$y);
      imagefilledpolygon($img, $points, 3, $color);
    }
    My version of drawStar (with examples)
    <?php
    header ("Content-type: image/png");
    /* drawStar or regular polygon
      $x, $y -> Position in the image
      $radius -> Radius of the star
      $spikes -> Number of spikes (min 2)
      $ratio -> Ratio between outer and inner points
      $dir  -> Rotation 270° for having an up spike( with ratio<1)
    */
    function drawStar($x, $y, $radius, $spikes=5, $ratio=0.5, $dir=270) {
      $coordinates = array();
      $angle = 360 / $spikes ;
      for($i=0; $i<$spikes; $i++){
        $coordinates[] = $x + (    $radius * cos(deg2rad($dir+$angle*$i)));
        $coordinates[] = $y + (    $radius * sin(deg2rad($dir+$angle*$i)));
        $coordinates[] = $x + ($ratio*$radius * cos(deg2rad($dir+$angle*$i + $angle/2)));
        $coordinates[] = $y + ($ratio*$radius * sin(deg2rad($dir+$angle*$i + $angle/2)));
      }
      return $coordinates ;
    }
    // 14*20+24*2 = 328 Examples
    $im = imagecreate(800,600);
       imagecolorallocate($im,  0,  0,  0);
    $w = imagecolorallocate($im, 255, 255, 255);
    $r = imagecolorallocate($im, 255,  0,  0);
    for ($spikes=2; $spikes<16; $spikes++) { //[2-15] 
      for ($ratio=1; $ratio<21; $ratio++) { //[0.1-2.0]
        $values = drawStar(40*$ratio-20, $spikes*40-60, 10, $spikes, $ratio/10);
        imagefilledpolygon($im, $values, count($values)/2, ($ratio % 5 == 0) ? $r : $w);
      }
    }
    for ($dir=0; $dir<24; $dir++) {
      $values = drawStar(30*$dir+20, 580, 10, 2, 1.5, $dir*15);
      imagefilledpolygon($im, $values, count($values)/2, $w);
      $values = drawStar(30*$dir+20, 580, 10, 2, 0.2, $dir*15);
      imagefilledpolygon($im, $values, count($values)/2, $r);
    }
    imagepng($im);
    imagedestroy($im);
    ?>
    
    Discovered while working on printing geographical boundaries to an image: if you provide floating point vertices, then the decimal value is automatically truncated. This can cause images drawn with floating point vertices to shift slightly towards the top-left corner. My personal resolution is to round all of the vertices to their nearest whole values, which eliminates this shift.
    Actually the minimum it allows is 3. It says "Total number of vertices, which must be bigger than 3." but it allows 3...
    There is a simple function to draw a filled point with a chosen radius and color.
    <?php
    function drawPoint($img, $radius, $origo_x, $origo_y, $pointColor)
    {
     for ($i=0;$i<=360;$i++)
     {
      $pont[] = $origo_x + ($radius * sin(deg2rad($i)));
      $pont[] = $origo_y - ($radius * cos(deg2rad($i)));
     }
     reset($pont);
     ImageFilledPolygon ($img, $pont, (sizeof($pont)/2), $pointColor);
    }
    ?>
    
    I discovered that the GD imagefilledpolygon function is incorrect for some drawing with transparent color (for example red 50% : RGBA = 255, 0, 0, 64).
    I tried to draw a complex form with lots of points really near (1 pixel of distance) and a transparent red.
    The problem was : some border pixels were not drawn by the imagefilledpolygon but were drawn with imagepolygon !?!?
    So I wrote my own imagefilledpolygon function which work very well in all case I tested.
    <?php
    // $points should be an array of coordinates like that :
    $points = array(
      array(0, 0),
      array(100, 50),
      array(90, 100),
      array(50, 50),
      array(70, 30),
      array(10, 10),
    );
    ?>
    <?php
    function myimagefilledpolygon(& $img, $points, $color) {
      $scanline = 99999;
      // compute edges
      $all_edges = array();
      $n = count($points);
      for($i=0; $i<$n; $i++) {
        $p1 = $points[$i];
        if ($i == $n-1) { $p2 = $points[0]; } else { $p2 = $points[$i+1]; }
        $x1 = $p1[0]; $y1 = $p1[1];
        $x2 = $p2[0]; $y2 = $p2[1];
        if ($y1 != $y2) {
          $invslope = ($x2 - $x1)/($y2 - $y1);
          if ($y1 < $y2 ) {
            $ymin = $y1;
            $xval = $x1;
            $ymax = $y2;
          } else {
            $ymin = $y2;
            $xval = $x2;
            $ymax = $y1;
          }
          $all_edges[] = array($ymin, $ymax, $xval, $invslope);
          if ($ymin < $scanline) { $scanline = $ymin; }
        } else {
          if ($y1 < $scanline) { $scanline = $y1; }
          if ($y2 < $scanline) { $scanline = $y2; }
        }
      }
      // draw
      $active = array();
      do {
        // add edges to active array
        $tmp = array();
        $n = count($all_edges);
        for($i=0; $i<$n; $i++) {
          if ($all_edges[$i][0] == $scanline) {
            $active[] = $all_edges[$i];
          } else {
            $tmp[] = $all_edges[$i];
          }
        }
        $all_edges = $tmp;
        // remove previous edges from active array
        $tmp = array();
        $n = count($active);
        for($i=0; $i<$n; $i++) {
          if ($active[$i][1] > $scanline) {
            $tmp[] = $active[$i];
          }
        }
        $active = $tmp;
        // sort active tab
        $n = count($active);
        for($i=0; $i<$n-1; $i++) {
          $min = $i;
          for($k=$i+1; $k<$n; $k++) {
            if ($active[$k][2] < $active[$min][2]) { $min = $k; }
          }
          if ($i != $min) {
            $tmp = $active[$i];
            $active[$i] = $active[$min];
            $active[$min] = $tmp;
          }
        }
        // draw
        $n = count($active);
        for($i=0; $i<$n; $i+=2) {
          if ($i+1 < $n) {
            if ($tmp[$i][2] == $active[$i+1][2]) {
              imagesetpixel($img, round($active[$i][2]), $scanline, $color);
            } else {
              imageline($img, round($active[$i][2]), $scanline, round($active[$i+1][2]), $scanline, $color);
            }
          }
        }
        // increment x values
        $n = count($active);
        for($i=0; $i<$n; $i++) { $active[$i][2] += $active[$i][3]; }
        $scanline++;
      } while (count($all_edges) + count($active) > 0);
    }
    ?>
    
    Just thought that 'tatlar at yahoo dot com's function has some redundant code in it, so I tried to "improve" it. Now you can choose a variable number of spikes.
    <?php
    error_reporting(E_ALL);
    function drawStar($x, $y, $radius, $spikes=5) {
      // $x, $y -> Position in the image
      // $radius -> Radius of the star
      // $spikes -> Number of spikes
      $coordinates = array();
      $angel = 360 / $spikes ;
      // Get the coordinates of the outer shape of the star
      $outer_shape = array();
      for($i=0; $i<$spikes; $i++){
        $outer_shape[$i]['x'] = $x + ($radius * cos(deg2rad(270 - $angel*$i)));
        $outer_shape[$i]['y'] = $y + ($radius * sin(deg2rad(270 - $angel*$i)));
      }
      // Get the coordinates of the inner shape of the star
      $inner_shape = array();
      for($i=0; $i<$spikes; $i++){
        $inner_shape[$i]['x'] = $x + (0.5*$radius * cos(deg2rad(270-180 - $angel*$i)));
        $inner_shape[$i]['y'] = $y + (0.5*$radius * sin(deg2rad(270-180 - $angel*$i)));
      }
      // Bring the coordinates in the right order
      foreach($inner_shape as $key => $value){
        if($key == (floor($spikes/2)+1))
           break;
        $inner_shape[] = $value;
        unset($inner_shape[$key]);
      }
      // Reset the keys
      $i=0;
      foreach($inner_shape as $value){
        $inner_shape[$i] = $value;
        $i++;
      }
      // "Merge" outer and inner shape
      foreach($outer_shape as $key => $value){
         $coordinates[] = $outer_shape[$key]['x'];
         $coordinates[] = $outer_shape[$key]['y'];
         $coordinates[] = $inner_shape[$key]['x'];
         $coordinates[] = $inner_shape[$key]['y'];
      }
      // Return the coordinates
      return $coordinates ;
    }
    // Example
    $spikes = 5;
    $values = drawStar(250, 250, 200, $spikes);
    $im = imagecreate(500,500);
    imagecolorallocate($im,0,0,0);
    $w = imagecolorallocate($im, 255, 255, 255);
    imagefilledpolygon($im, $values, $spikes*2, $w);
    imageGIF($im);
    imagedestroy($im);
    ?>