前導:
最近項目中需要做一個二維碼圖片,來負責將支付寶和微信的支付合並的圖片。圖片跟大家平常看到的微信或者支付寶中生成的二維碼沒有什麼區別。但是用php的qrcode生成的二維碼呢,只是簡單的一個二維碼,還需要將二維碼嵌入到背景圖片中,下面就來看一下我最近弄的二維碼。
上一張已經生成的圖片。
這一張是不帶有logo的測試圖,圖片有點大。
準備工作:
首先我們得有一張背景圖片,有一張logo圖片。另外得有QRcode類庫,這個我就不多講了,百度搜索一下很多。
準備好了這些內容,我們就來開始寫代碼了。
生成二維碼:
首先我們要使用QRcode類庫來生成一個原始的二維碼:
先來看一下QRcode的幾個參數:
我們生成二維碼一般是使用QRcode的png()方法來生成二維碼,其他的格式我們這裏暫且不做討論。
png()方法有6個參數:
第一個:$text
生成的二維碼包含的信息。
第二個:$outputPaht
默認是否,不生成文件,這個是生成二維碼的路徑
第三個:$level
生成二維碼的容錯率,也就是有被覆蓋的區域還能識別,分別是 L(QR_ECLEVEL_L,7%),M(QR_ECLEVEL_M,15%),Q(QR_ECLEVEL_Q,25%),H(QR_ECLEVEL_H,30%);
第四個:$size
,控制生成圖片的大小,默認爲4
第五個:$margin
,控制生成二維碼的空白區域大小
第六個:$saveandprint
,保存二維碼圖片並顯示出來,$outfile必須傳遞圖片路徑。
瞭解了這些參數以後我們就可以進行一個簡單的設置:
$text = "這只是一個測試二維碼!";
$QRDir = "./base.png"; //生成的圖片路徑
$errorCorrectionLevel = 'H';//容錯率
$matrixPointSize = 10;//生成的圖片的大小
$margin = 2;
第六個參數呢,我們就不進行設置了,在實際的項目中我們不需要將圖片打印出來。這個圖片一般會設置成下載。這裏我就不多進行贅述了。
$qrCode = new QRcode();
$qrCode->png($text, $QRDir, $errorCorrectionLevel, $matrixPointSize, $margin);
通過上面的操作,我們就生成了一個二維碼,並且將這個二維碼的內容保存在了base.png圖片中。
如果是簡單的要得到一個二維碼的話,那麼通過上面的操作就達到目的。但是隻是單單一個二維碼顯得有點難看,實際項目中需要將這個二維碼進行美化,加入一些我們需要的背景和logo。接下來我們進行二維碼進一步加工,將二維碼變的美觀起來。
我的實際項目中有2種情況,一種是生成一個不帶logo的二維碼,一種是帶logo的二維碼。
我們先來說不帶logo的二維碼的生成:
由於我們生成的二維碼不一定能夠百分百的放入到我們預先設置好的背景圖片中:(當然也可以在製作背景圖片的時候測量好尺寸,正好將二維碼放入到背景圖片中),這裏需要解釋一下什麼是百分百放入到背景圖片中,就是生成的二維碼跟背景中預留的顯示二維碼的空白處能夠正好合上,由於背景有時候會是變化的,生成的二維碼的大小是固定的,這樣的話就不能滿足我們需求。
遇到上面變化的背景圖時,就需要對生成的二維碼進行處理,對生成的二維碼進行放大或者縮小的操作。
需要簡紹幾個圖像處理的函數:
imagecreatefromgif()
:創建一塊畫布,並從 GIF 文件或 URL 地址載入一副圖像
imagecreatefromjpeg()
:創建一塊畫布,並從 JPEG 文件或 URL 地址載入一副圖像
imagecreatefrompng()
:創建一塊畫布,並從 PNG 文件或 URL 地址載入一副圖像
imagecreatefromwbmp()
:創建一塊畫布,並從 WBMP 文件或 URL 地址載入一副圖像
imagecreatefromstring()
:創建一塊畫布,並從字符串中的圖像流新建一副圖像
使用上面的函數,把生成的二維碼圖片讀取出來,獲取到二維碼的高度跟寬度。
$QRImageInfo = imagecreatefromspng($QRDir);
$QR_width = imagesx($QRImageInfo);
$QR_height = imagesy($QRImageInfo);
然後再創建一副我們需要大小的圖片:
使用imagecreatetruecolor()
方法,創建圖片資源,使用imagecolorallocate()
方法,給圖片設置背景色
$width = 539;
$height = 539;
$newImage = imagecreatetruecolor($width,$height);//創建一個圖像資源
$newImage_white = imagecolorallocate($newImage, 255,255,255);//給創建好的圖像資源設置白色背景
imagefill($newImage, 0, 0, $newImage_white);//填充$newImage
接下來使用imagecopyresized()
函數將二維碼圖縮放到新建的圖片上:
imagecopyresized()
函數有10個參數:
1、要放到的新圖片資源(目標圖片資源)dst_im
2、要縮放的圖片資源(原圖片資源)src_im
3、目標圖像開始x座標 dst_x
4、目標圖像開始y座標 dst_y
5、原圖像的開始x座標 src_x
6、原圖像的開始y座標 src_y
7、目標圖像的寬度 dst_w
8、目標圖像的高度 dst_h
9、原圖像的寬度 src_w
10、原圖像的高度 src_h
根據上面的參數我們就可以將二維碼圖片放入到新建的圖像中了:
imagecopyresized($newImage, $QRImageInfo,0, 0, 0, 0, 539, 539, $QR_width, $QR_height);
這樣得到的$newImage就是一個縮放了的二維碼。
接下來就是將這個經過我們縮放了以後的二維碼放入到已經設計好的底圖上面。
imagecopyresampled這個函數和上面imagecopyresized的效果是一樣的,只不過這個函數處理出來的效果要比imagecopyresized好。參數也是一樣的。
$backgroundDir = "./background.png";//事先準備好的背景圖片
$backgroundImg = imagecreatefromspng($backgroundDir);
imagecopyresampled($backgroundImg, $newImage, 203, 330, 0, 0, 539, 539, 539, 539);
最後就是將合併好的圖片保存起來:
$resultPngPic = "./resultPngPicWithoutLogo.png";//設置最後生成圖像的路徑
imagepng($backgroundImg,$resultPngPic);
imagedestroy($newImage);//銷燬中間創建的$newImage資源
這樣就會在當前目錄下得到一張名字爲resultPngPicWithoutLogo.png的圖片,圖片的內容就是我們想要的二維碼圖片。
接下來就是生成帶Logo的二維碼:
生成帶logo的二維碼的話思路跟上面不帶logo的二維碼的方式都是一樣的,就是將我們需要的圖片進行縮放,然後跟二維碼圖片進行合併,成爲一個帶Logo的二維碼,然後呢,再把這個二維碼放入到背景圖片中去。這裏的操作完全跟上面是一樣的。我們這裏重點介紹一下如何實現圖片的圓角設置,如下圖:
在生成帶logo的二維碼的時候,我們也希望logo也是圓角的,跟微信支付寶的二維碼一樣。
首先打開事先準備好的logo圖片:
$logoDir = "./logo.png";
$resource = imagecreatefrompng($logoDir);
$image_width = imagesx($resource);
$image_height = imagesy($resource);
創建一個跟當前圖片相同大小的畫布:
$targetImg = imagecreatetruecolor($logo_width, $logo_height);
imagesavealpha($targetImg, true);//保留圖片的透明通道
$targetImgBackground = imagecolorallocatealpha($targetImg, 255, 255, 255, 127);//將目標圖片設置成透明背景
imagefill($targetImg, 0, 0, $targetImgBackground);//填充透明背景
接下來就是將logo圖片畫到目標圖片上:
這裏有倆個方法,第一種比較靈活,但是效率低:就是判斷每一個像素點是否落在了我們我們要的範圍內,落在了我們要範圍內的話,就將這個像素點畫在畫布上,重複這一過程,直到所有像素點都遍歷完。這樣我們就相當於裁剪出一個新的圖片。這種方法可以裁剪任何樣子的圖片,只要你能夠做好像素點的判斷。但是缺點就是效率太低,需要將所有的像素點遍歷一遍,而且需要對畫布修改很多次,才能得到最後的圖片,如果是專業的需要的話,犧牲點性能也能夠接受。但是我們只是需要一個簡單的圓角圖片。這樣的需求完全不需要遍歷所有的像素。
第二種方法,就是我們可以創建1個小的正方形,將這個小正形切割成一個扇形,這樣講這個切割後的扇形跟原來的圖片合成,就會形成一個圓角圖片。但是這裏有一個問題,得到的圓角圖片只是在形式上是圓角的,圓角的外面是一圈的背景,而不是透明的。
先來看第二種方法:
$r = $radius; //圓角半徑
function litter_corner($radius){
$img = imagecreatetruecolor($radius, $radius);
imagesavealpha($img, true);
$bgcolor = imagecolorallocatealpha($img, 0, 0, 0,127);
$fgcolor = imagecolorallocate($img, 0, 0, 0);
imagefill($img, 0, 0, $bgcolor);
imagefilledarc($img, $radius, $radius, $radius*2, $radius*2, 180, 270, $fgcolor, IMG_ARC_PIE);
imagecolortransparent($img, $fgcolor);
return $img;
}
// lt(左上角)
$lt_corner = litter_corner($radius);
imagecopymerge($resource, $lt_corner, 0, 0, 0, 0, $radius, $radius, 100);
// lb(左下角)
$lb_corner = imagerotate($lt_corner, 90, 0);
imagecopymerge($resource, $lb_corner, 0, $image_height - $radius, 0, 0, $radius, $radius, 100);
// rb(右上角)
$rb_corner = imagerotate($lt_corner, 180, 0);
imagecopymerge($resource, $rb_corner, $image_width - $radius, $image_height - $radius, 0, 0, $radius, $radius, 100);
// rt(右下角)
$rt_corner = imagerotate($lt_corner, 270, 0);
imagecopymerge($resource, $rt_corner, $image_width - $radius, 0, 0, 0, $radius, $radius, 100);
$image = "logoFinal.png";
imagepng($resource,$image);
這樣的就得到了一張帶背景色的圓角logo,但是這種方法沒有得到透明的圖片。後續再研究一下,看能不能得到一張透明的圖片以達到需要的效果。
第二種方法是可以得到一個透明背景的圓角logo圖片,代碼如下:(這段代碼是從網絡上找的,基本的思路就是通過計算像素的位置來確定是不是要顯示,不停的畫像素點得到的最終的圖)
function radius_img($imgpath, $radius = 15) {
$ext = pathinfo($imgpath);
$src_img = null;
switch ($ext['extension']) {
case 'jpg':
$src_img = imagecreatefromjpeg($imgpath);
break;
case 'png':
$src_img = imagecreatefrompng($imgpath);
break;
}
$wh = getimagesize($imgpath);
$w = $wh[0];
$h = $wh[1];
// $radius = $radius == 0 ? (min($w, $h) / 2) : $radius;
$img = imagecreatetruecolor($w, $h);
//這一句一定要有
imagesavealpha($img, true);
//拾取一個完全透明的顏色,最後一個參數127爲全透明
$bg = imagecolorallocatealpha($img, 255, 255, 255, 127);
imagefill($img, 0, 0, $bg);
$r = $radius; //圓 角半徑
for ($x = 0; $x < $w; $x++) {
for ($y = 0; $y < $h; $y++) {
$rgbColor = imagecolorat($src_img, $x, $y);
if (($x >= $radius && $x <= ($w - $radius)) || ($y >= $radius && $y <= ($h - $radius))) {
//不在四角的範圍內,直接畫
imagesetpixel($img, $x, $y, $rgbColor);
} else {
//在四角的範圍內選擇畫
//上左
$y_x = $r; //圓心X座標
$y_y = $r; //圓心Y座標
if (((($x - $y_x) * ($x - $y_x) + ($y - $y_y) * ($y - $y_y)) <= ($r * $r))) {
imagesetpixel($img, $x, $y, $rgbColor);
}
//上右
$y_x = $w - $r; //圓心X座標
$y_y = $r; //圓心Y座標
if (((($x - $y_x) * ($x - $y_x) + ($y - $y_y) * ($y - $y_y)) <= ($r * $r))) {
imagesetpixel($img, $x, $y, $rgbColor);
}
//下左
$y_x = $r; //圓心X座標
$y_y = $h - $r; //圓心Y座標
if (((($x - $y_x) * ($x - $y_x) + ($y - $y_y) * ($y - $y_y)) <= ($r * $r))) {
imagesetpixel($img, $x, $y, $rgbColor);
}
//下右
$y_x = $w - $r; //圓心X座標
$y_y = $h - $r; //圓心Y座標
if (((($x - $y_x) * ($x - $y_x) + ($y - $y_y) * ($y - $y_y)) <= ($r * $r))) {
imagesetpixel($img, $x, $y, $rgbColor);
}
}
}
}
return $img;
}
$final_logo = radius_img($resource);
imagepng($final_logo, "logoFinal.png");
通過調用這個函數,我們就能輕鬆的得到一個透明的圓角logo了,其實也是很簡單的,只不過相對來說要比上面的合成圖像要繁瑣點。
最後呢就是將這個已經修改好的logo圖片貼在二維碼中間。這裏面可能會涉及到圖片的大小問題,可以根據之前圖片的縮放等辦法來得到一個理想的大小。
合成代碼如下:
$QR = imagecreatefrompng("qr.png");//這裏qr.png是二維碼的圖片
$logo = imagecreatefrompng("logoFinal.png");//圓角二維碼logo
$QR_width = imagesx($QR);//二維碼圖片寬度
$QR_height = imagesy($QR);//二維碼圖片高度
$logo_width = imagesx($logo);//logo圖片寬度
$logo_height = imagesy($logo);//logo圖片高度
$logo_qr_width = $QR_width / 5;
$scale = $logo_width/$logo_qr_width;
$logo_qr_height = $logo_height/$scale;
$from_width = ($QR_width - $logo_qr_width) / 2;
//重新組合圖片並調整大小
imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height);
$QRHasLogo = "QRHasLogo.png";//最終得到的帶有logo的二維碼
imagepng($QR, $QRHasLogo);
這樣就得到一個名爲QRHasLogo.png的圖片,再將這個圖片貼到背景上面就大功告成了。
最後一步跟上面不帶logo的二維碼是相同的,就不在進行設置了。最終的結果如下: