猫窝私语 — Makumo's Blog

玛酷猫的温馨小窝,记录生活点点滴滴。

@玛酷猫3年前

09/17
12:27
PHP

如何用PHP裁出一个圆

先说下需求:前段时间神秘花园很火,于是乎客户想要制作神秘花园的网页游戏,涂色盘是圆形的,网页将涂色盘加载进当前的canvas中,当网友涂色完毕提交后,将canvas内容转化为图片数据传送至后台保存。这时接收到的数据包括整个canvas内容,整体是个矩形,除了网友涂色部分,还有整个背景、颜色盘、一些按钮之类的,这就需要把网友涂色的那个圆形部分裁剪下来。

我的思路分成两部分,首先裁剪成正方形,毕竟直接在矩形上面裁出一个圆相对来说麻烦一些。项目使用的thinkphp框架(v3.1),裁正方形还是比较快捷的,直接调用自带的函数即可

import('ORG.Util.Image.ThinkImage');
$image = new ThinkImage(THINKIMAGE_GD, $file);
$image->crop(516,516,45,119)->save($file);//crop四个参数分别为长、宽、x偏移、y偏移

下一步就是要裁圆了,网上搜索了下居然没有搜索到多少有用的代码,感觉这个对大家都不是什么难处,看来自己这个半路出家的基础还是不太好呀。幸好在一个很老的帖子(08年的帖子)的回帖里面看到Sunyanzi@phpchina的一个思路,原帖地址传送门,代码如下:

<?php
//-----------------------------------------------------------
// * Sunyanzi @ phpchina
//-----------------------------------------------------------

class image_cutter {
	private $original_image;
	private $cutted_image;
	private $diameter;
	private $radius;

	public function __construct( $image_path ) {
		/* load the original image ... */
		$image = imagecreatefromjpeg( $image_path );
		/* get image size ... */
		$x = imagesx( $image );
		$y = imagesy( $image );		
		/* diameter of the circle is always the smaller side ... */
		$this->diameter = $x > $y ? $y : $x; 
		/* radius is half a diameter ... am i must explain this ...? */
		$this->radius = $this->diameter / 2;		
		/* save the original image ... */
		$this->original_image = $image;		
		/* create new canvas to save our work ... */
		$this->create_blank_image();		
		/* PAINTING TIME ... */
		$this->read_the_original_image_and_write();		
		/* i'm positively bursting to see what we have done ... */
		return;
	}

	private function __destruct() {
		/* hey my dear brower ... it's not a html page comes ... */
		header("Content-type: image/png");
		/* show our work ... */
		imagepng( $this->cutted_image );
		/* we have to cleaned up the mass before left ... */
		imagedestroy( $this->original_image );
		imagedestroy( $this->cutted_image );		
		/* so ... how do you think about this ...? */
		return;			
	}

	private function create_blank_image() {			
		/* create a true color square whose side length equal to diameter of the circle ... */
		$image = imagecreatetruecolor( $this->diameter,$this->diameter );
		/* we also need a transparent background ... */
		imagesavealpha($image, true);
		/* create a transparent color ... */
		$color = imagecolorallocatealpha($image, 0, 0, 0, 127);
		/* ... then fill the image with it ... */
		imagefill($image, 0, 0, $color);		
		/* nothing to do then ... just save the new image ... */
		$this->cutted_image = $image;		
		/* go back and see what should we do next ..? */
		return;			
	}

	private function read_the_original_image_and_write() {
		/* actually we need a smooth circle ... */
		for ( $x = 0; $x <= $this->radius; $x += 0.01 ) {
			/* standard form for the equation of a circle ... don't tell me you never knew that ... */
			$y = sqrt( $this->diameter * $x - pow( $x , 2 ) ) + $this->radius;
			/* i think i should call this successive scans ... */
			for ( $i = $x; $i < $this->diameter - $x; $i++ ) {
				/* half of the circle ... */
				imagesetpixel (
					$this->cutted_image , $i, $y, 
					imagecolorat( $this->original_image, $i, $y )
				);

				/* the other half of course ... */
				imagesetpixel ( 
					$this->cutted_image , $i, $this->diameter - $y, 
					imagecolorat( $this->original_image, $i, $this->diameter - $y ) 
				);
			}				
		}
			
		/* avoid the white line when the diameter is an even number ... */
		if ( ! is_float( $this->radius ) )
			for ( $i = 0; $i < $this->diameter; $i++ )
				imagesetpixel ( 
								$this->cutted_image , $i, $this->radius - 1,
								imagecolorat( $this->original_image, $i, $this->radius - 1 )
				);						
		/* woo ... not as difficult as you think ... that's all ... */
		return;
	}
}

new image_cutter( HERE_COMES_THE_ORIGINAL_IMAGE_PATH );
?>

读了下代码,大概了解了下原理,以x轴为参考,0.01像素步长,通过圆的公式计算出对应y点的位置,然后将循环将X轴这条线上落在y点外(圆外)的点用透明色填充。实际放到项目中测试,发现个问题,由于项目中圆的半径比较大,516像素,按照0.01步长来计算,在x刚起步时,计算后的y值之间的间隔会大于1个像素,表现在页面上就是在圆的中间出现两条白线,将步长减少到0.004的时候白线消失,但是由于步长变小,整个循环数目变大,耗时严重,一次将近30秒的时间。

既然Sunyanzi提供了一个思路,那我就把它简化下,毕竟图片最小点是像素,那我就以1像素为步长,y轴也不计算了,直接也以1像素为步长,通过圆的方程式算出半径,比较半径的大小,大于我需要的圆的半径的点,把他用透明色填充掉就OK了。思路有了就开始动手。仅修改上面代码中裁圆的那个函数read_the_original_image_and_write()

private function read_the_original_image_and_write(){
        for ( $x = 0; $x <= $this->radius; $x++ ) {
            for ( $y = 0; $y <= $this->radius; $y++ ) {
                if(sqrt( pow( $x- $this->radius , 2 ) + pow( $y- $this->radius , 2 ))<$this->radius){
                    imagesetpixel (
                        $this->cutted_image , $x, $y,
                        imagecolorat( $this->original_image, $x, $y )
                    );
                    imagesetpixel (
                        $this->cutted_image , $this->diameter - $x, $y,
                        imagecolorat( $this->original_image, $this->diameter - $x, $y )
                    );
                    imagesetpixel (
                        $this->cutted_image , $x, $this->diameter - $y,
                        imagecolorat( $this->original_image, $x, $this->diameter - $y )
                    );
                    imagesetpixel (
                        $this->cutted_image ,$this->diameter -  $x, $this->diameter - $y,
                        imagecolorat( $this->original_image, $this->diameter - $x, $this->diameter - $y )
                    );
                }
            }
        }
    }

实际效果速度那是没说的,毕竟简化了,基本上秒出,效果相对之前的方法来说差了点,一周的锯齿感强了点,不过由于图片比较大,反倒不是很明显,有需要的话可以将步长调到0.5,效果会好一点,速度不是太影响,图片小的话可以放得更低一点。上面例子是直接输出图片,实际应用为保存成文件,这个就比较简单了,就不在这里贴代码了。

如何用PHP裁出一个圆

@玛酷猫3年前

08/17
11:45
PHP

用phpstorm+git+dropbox开发项目

一直以来都是一个人在开发项目,最多也就是配备一个设计、一个前端,整体代码部分也还都是一个人在写,也就很少会使用到版本控制。phpstorm的历史功能用起来也就够了,很方便的查询之前的修改记录。直到周边朋友和同事或多或少的都出现了一些意外导致代码丢失或者损坏,比如硬盘损坏呀,误删除、误格式化之类的,为了这种苦逼的事情不落在自己头上(事实上自己也苦逼了一次,格式化错U盘了,幸好里面都是些暂存资料和音乐,都有备份,损失不大)一直想找个方便的备份方法,又要能及时同步信息。

一开始是用U盘/SD卡(电脑上常年插着一张SD卡)作为备份方法,总觉得U盘也不方便,而且出过一次意外后,就想着换用云盘来处理。国内云盘虽然空间一个比一个大,但是总感觉不靠谱的样子,对国内的企业的诚信缺乏信心,虽然自己写的代码也值不了几个钱。还有就是不方便,用的多的百度云盘,最然提供的自动同步的功能,但是如果换个电脑或者重装一下百度云,还都需要重新每个文件同步一下,对于一个项目若干碎片文件,很费时间,而且貌似没有对比机制,直接全部上传覆盖。选来选去还是使用以前一直用的dropbox,虽然问题也比较多,需要翻墙(对程序猿来说架梯子都是基本技能),空间小(初始2G,好友邀请满才25G,相对于国内动不动就上T小很多,不过存代码基本够用),网络不稳定(这个用梯子克服),但是易用度还是不错的,尤其是能有对比机制来同步,再加上dropbox的共享功能,完全可以做到多人协同使用。

说到协同就不得不说git,要弄就一次到位,所以也参考网上的文章把git也用了起来。dropbox直接同步git仓库,快1个月的使用,感觉也非常好。尤其是在使用git后,每次提交都会写清楚开发/修改的地方,开发进程清楚明了。不过目前还是没有经历过多人git开发的项目,想想项目各种分支各种合并,也是蛮有趣的。

PS:配置方法就不多说了,网上很多,比如这篇: 用Dropbox作为Git服务器——详细图解,phpstorm在设置里面的版本控制 填上git的路径即可。

用phpstorm+git+dropbox开发项目