Search code examples
phpphp-gd

Image as isosceles trapezoid PHP GD


i have this function that transforms image to trapezoid using PHP GD:

 function perspective($i,$gradient=0.9,$rightdown=true,$background=0xFFFFFF) {
    $mult=3;
    $w=imagesx($i);
    $h=imagesy($i);
    $image=imagecreatetruecolor($w*$mult,$h*$mult);
    imagecopyresized($image,$i,0,0,0,0,$w*$mult,$h*$mult,$w,$h);
    imagedestroy($i);
    $w*=$mult;
    $h*=$mult;
    $im=imagecreatetruecolor($w,$h);
    $background=imagecolorallocate($im,($background>>16)&0xFF,($background>>8)&0xFF,$background&0xFF);
    imagefill($im,0,0,$background);
    imageantialias($im,true);
    $nh=$h-($h*$gradient);
    for ($x=0; $x<$w; $x++) {
        $ni=(($rightdown) ? $x : $w-$x);
        $p=intval($h-(($ni/$w)*$nh));
        if (($p%2)<>0)
        $p-=1;
        $nx=intval(($p-$h)/2);
        imagecopyresampled($im,$image,$x,0,$x,$nx,1,$p,1,$h-1);
        imageline($im,$x,$h-1,$x,$h+$nx,$background);
        imageline($im,$x,0,$x,-$nx-1,$background);
    }
    imagedestroy($image);
    imagefilter($im,IMG_FILTER_SMOOTH,10);
    $i=imagecreatetruecolor($w/$mult,$h/$mult);
    imageantialias($i,true);
    imagecopyresampled($i,$im,0,0,0,0,$w,$h,$w*$mult,$h*$mult);
    imagedestroy($im);
    return $i;
 }

But i cant modify it to produce isosceles trapezoid, i think there needs just one small modification, but i cant figure it outh (i tried lot of things).

Can someone help?


Solution

  • Right, basically that code should generate the right values, but due to a bug has a lot of cludges in place to get a trapezium shape. The bug is that the copy of each line has the destination-y and the source-y values transposed. The source-y should always be 0, the destination-y should change.

    There were also a few other small numerical bugs and double rounding at unnecessary points throwing off the results.

    Also, the variable naming was atrocious, so I have rewritten it so that the entire function is clear.

    Try the following:

    function makeTrapeziumImage($image, $gradient, $rightToLeft = false, $background = 0xFFFFFF, $supersampleScale = 3) {
      $originalWidth = imagesx($image);
      $originalHeight = imagesy($image);
    
      $supersampledWidth = $originalWidth * $supersampleScale;
      $supersampledHeight = $originalHeight * $supersampleScale;
    
      $supersampledImage = imagecreatetruecolor($supersampledWidth, $supersampledHeight);
    
      imagecopyresized($supersampledImage, $image,
                       0, 0, 0, 0,
                       $supersampledWidth, $supersampledHeight, $originalWidth, $originalHeight);
    
      $workingImage = imagecreatetruecolor($supersampledWidth, $supersampledHeight);
    
      $backgroundColour = imagecolorallocate($workingImage, ($background >> 16) & 0xFF, ($background >> 8) & 0xFF, $background & 0xFF);
      imagefill($workingImage, 0, 0, $backgroundColour);
    
      imageantialias($workingImage,true);
    
      $endHeight = $supersampledHeight - ($supersampledHeight * $gradient);
    
      for ($x = 0; $x < $supersampledWidth; $x++) {
        $cX = ($rightToLeft ? $supersampledWidth - $x : $x);
    
        $dstHeight = $supersampledHeight - ((($cX + 1) / $supersampledWidth) * $endHeight);
    
        $dstY = intval(($supersampledHeight - $dstHeight) / 2) - 1; // -1 required as zero-indexed
        $dstY = ($dstY < 0 ? 0 : $dstY); // Rounding can make $dstY = -1
    
        $dstHeight = intval($dstHeight); // Round the height after calculating $dstY
    
        imagecopyresampled($workingImage, $supersampledImage,
                           $cX, $dstY, $cX, 0,
                           1, $dstHeight, 1, $supersampledHeight);
      }
    
      imagedestroy($supersampledImage);
      imagefilter($workingImage, IMG_FILTER_SMOOTH, 10);
    
      $resizedImage = imagecreatetruecolor($originalWidth, $originalHeight);
      imageantialias($resizedImage, true);
    
      imagecopyresampled($resizedImage, $workingImage,
                         0, 0, 0, 0,
                         $originalWidth, $originalHeight, $supersampledWidth, $supersampledHeight);
    
      imagedestroy($workingImage);
    
      return $resizedImage;
    }
    

    The essential mechanism of the inner loop, as before, is to take each column of pixels, along the x-axis and resize them over a gradient. It naturally creates an isosceles trapezium. In order to create a non-isosceles trapezoid, another gradient would have to be specified. Alternatively, a set of start and end y-values could be specified and the gradients calculated from them.

    Whilst this example works along the x-axis, in either direction as before, it could just as easily work along the y-axis (or the image could be rotated 90 degrees, processed, then rotated back).