08 GD图像处理

GD是一种图像处理扩展(外部提供的API),允许PHP在脚本中使用对应的函数实现画图功能
目前默认已经被集成到PHP中扩展中,只需要开启扩展,即可使用

GD图像基本处理

画图的基本流程:画图的本质是在内存中开辟一块很大的内存区用于图片制作

  • 准备画布
  • 开始作画
  • 保存内容
  • 销毁画布

1. 创建画布

返回结果是资源类型

1.1. imagecreate(宽, 高):创建一个空白画布,背景色是白色的
1.2. imagecreatetruecolor(宽,高):创建一个真彩画布,背景色是黑色的,就是一个无色图片,需要自己填充
$img = imagecreatetruecolor(100, 200);
var_dump($img);
1.3. imagecreatefromjpeg(图片文件路径):打开一个jpeg格式的图片资源
$img = imagecreatefromjpeg('img/1.jpg');
var_dump($img);
1.4. imagecreatefromgif(图片文件路径):打开一个gif格式的图片资源
1.5. imagecreatefrompng(图片文件路径):打开一个png格式的图片资源

2. 操作画布

注意:所有的画布操作都是需要指定画布资源的,都是第一个参数

2.1 imagecolorallocate(画布资源,0,0,0):分配颜色

根据RGB三原色组合给指定的画布分配一组颜色(每组色号都是从0到255),会返回一个颜色句柄;
在真彩图片资源中,所有分配的颜色都不会自动给图片资源上色,是用来后续操作图片资源的时候,指定着色的:但是如果当前使用的 imaggsreate,创建的图片资源,那么第一个分配的颜色,会自动被着色为图片背景色。

一般凡是需要在图片上添加内容的,都是需要给画布资源分配颜色的

<?php
// 1. 创建一个宽100,高100的无色画布资源
$ch = imagecreatetruecolor(100, 100);

// 2. 分配颜色
$color = imagecolorallocate($ch, 94, 197, 138);
var_dump($color);  // 返回 int(6210954)
2.2 imagefill(画布资源,起始x坐标,起始y坐标,$color颜色句柄):填充区域

指定位置填充指定颜色:x坐标从左向右增大,y坐标从上往下增大

// 3. 指定位置填充指定颜色
imagefill($ch, 100, 200, $color);
2.3 imageline(画布资源,起始点x,起始点y,终点x,终点y,$color颜色):画直线
// 4.1 制作直线
// 创建直线的颜色
$line_color = imagecolorallocate($ch, 39, 206, 89);
imageline($ch, 200, 320, 300, 320, $line_color);
2.4 imagerectangle(画布资源,,左上角x,左上角y,右下角x,右下角y,$color颜色):画矩形
// 4.2 画矩形
// 创建矩形的颜色
$rec_color = imagecolorallocate($ch, 38, 44, 197);
imagerectangle($ch, 20, 20, 120, 140, $rec_color);
2.5 imagearc((画布资源,圆心x, 圆心y, 宽, 高, 起始角度x, 结束角度y, $arc_color):画圆弧
// 4.3. 画圆弧
// 创建圆弧的颜色
// 角度是0-360
$arc_color = imagecolorallocate($ch, 174, 34, 175);
imagearc($ch, 100, 200, 90, 90, 120, 360, $arc_color);
// 一个圆圈
imagearc($ch, 100, 300, 90, 90, 0, 360, $arc_color);
2.6 在画布上写字:imagestring()和imagettftext()

imagestring(图片资源,文字大小,起始x,起始y,颜色):只能是英文,文字大小0-5
imagettftext(图片资源,文字大小,旋转角度,起始x,起始y,颜色,字体,内容):可以是任意字符
字体:需要加载来自windows的字体,可以从 C:\Windows\Fonts 复制字体

// 4.4. 在画布上写字
// 文字大小1-5
$txt_color = imagecolorallocate($ch, 255, 255, 255);
// 输出应为字母
imagestring($ch, 5, 200, 80, 'Hello China', $txt_color);
// 任意字符  字体:C:\Windows\Fonts
imagettftext($ch, 40, 0, 130, 140, $txt_color, 'F:/mylpj/phpdemo/gd/simsun.ttc', '你好,中国');

3. 输出画布

将设置好的图片资源输出到画布,有2种方式:

  • 输出为图片文件:以图片文件形式保存到本地文件夹
  • 输出为网页图片:将图片展出给HTML

如果只提供了图片资源,没有指定图片文件的保存位置,系统默认认为是输出给浏览器;如果指定了具体的图片文件保存位置,那么系统认为是保存到本地

// 一般是保存为png,因为是自己创建的画布;如果是打开的图片资源,那么一般会保存为相应的图片格式
imagepng($ch, 'my.png'); // 保存为图片文件

// 输出到浏览器html:必须设置响应头是图片
header('Content-type:image/png');
imagepng($ch);

4. 销毁画布

从内存中将画布资源释放掉

// 释放资源
imagedestroy($ch);

5. 一个简单的应用

<?php
// 1. 创建一个宽400,高400的无色画布资源
$ch = imagecreatetruecolor(400, 400);

// 2. 分配一个颜色
$color = imagecolorallocate($ch, 187, 94, 94);

// 3. 给画布指定位置填充指定颜色
imagefill($ch, 0, 0, $color);


// 4.1 制作直线
// 创建直线的颜色
$line_color = imagecolorallocate($ch, 39, 206, 89);
imageline($ch, 200, 320, 300, 320, $line_color);

// 4.2 画矩形
// 创建矩形的颜色
$rec_color = imagecolorallocate($ch, 38, 44, 197);
imagerectangle($ch, 20, 20, 120, 140, $rec_color);


// 4.3. 画圆弧
// 创建圆弧的颜色
// 角度是0-360
$arc_color = imagecolorallocate($ch, 174, 34, 175);
imagearc($ch, 100, 200, 90, 90, 120, 360, $arc_color);
// 一个圆圈
imagearc($ch, 100, 300, 90, 90, 0, 360, $arc_color);


// 4.4. 在画布上写字
// 文字大小1-5
$txt_color = imagecolorallocate($ch, 255, 255, 255);
// 输出应为字母
imagestring($ch, 5, 200, 80, 'Hello China', $txt_color);
// 任意字符  字体:C:\Windows\Fonts
imagettftext($ch, 40, 0, 130, 140, $txt_color, 'F:/mylpj/phpdemo/gd/simsun.ttc', '你好,中国');


// 5. 保存输出画布
// 如果只提供了图片资源,没有指定图片文件的保存位置,系统默认认为是输出给浏览器;
// 如果指定了具体的图片文件保存位置,那么系统认为是保存到本地
// imagejpeg() // 保存成jpg格式图片
// imagegif() // 保存成gif格式图片
// imagepng() // 保存成png格式图片

// 一般是保存为png,因为是自己创建的画布;如果是打开的图片资源,那么一般会保存为相应的图片格式
imagepng($ch, 'my.png'); // 保存为图片文件

// 输出到浏览器html:必须设置响应头是图片
header('Content-type:image/png');
imagepng($ch);


// 释放资源
imagedestroy($ch);

image

6. 获取图片基本信息

imagesx和imagesy函数获取图片资源的宽度和高度,其参数必须是图片资源

getimagesize获取图片的所有信息,参数可以直接是图片路径

// GD函数:获取图片基本信息
$image = 'my.png';

// 打开图片资源
$img = imagecreatefrompng($image);

// imagesx和imagesy的参数必须是图片资源

// 获取图片资源的宽度
$width = imagesx($img);
// 获取图片资源的宽度
$height = imagesy($img);

// 获取图片全部信息
$mm = getimagesize($image);
echo '<pre>';
print_r($mm);

返回一个具有四个单元的数组。
索引0包含图像宽度的像素值
索引1包含图像高度的像素值
索引2是图像类型的标记:
1= GIF,2=JPG,3 = PNG, 4 = SWF, 5 = PSD ,6 = BMP,7 = TIFF(intel byte order), 8 = TiFF(motorola byte order),9 = Jpc , 10 IP2,11 =JPX,12 = JB2,13= SWC,14 = IFF,15= WBMP,16 = XBM

索引3 是文本字符串,内容为“height="yyy" width="xxX"”,可直接用于 IMG 标记。

验证码

图片验证码:计算机将拿到的验证码存放图片中,展示给用户,用户根据验证码再提交到服务器,服务器再与之前的验证码进行比对。

生成随机验证码

captcah.php
image

<?php
// 随机验证码
// 1. 创建一个100 100的画布
$ch = imagecreatetruecolor(200, 100);

// 2. 构建画布的颜色
$ch_color = imagecolorallocate($ch, 255, 255, 255);

// 3. 填充画布颜色
imagefill($ch, 0, 0, $ch_color);


// 4. 生成文字
for ($m = 1; $m < 5; $m++) {
  // 随机字符
  $text = generateRandomString(1);
  // 随机X位置
  $x = 30 + 40 * ($m - 1);
  // 随机旋转角度
  $roate = random_int(-30, 30);
  // 随机字体颜色
  $randColor = random_int(0, 180);  // 颜色深一点
  $font_color = imagecolorallocate($ch,  $randColor,   $randColor,   $randColor);

  imagettftext($ch, 30,  $roate, $x, 64, $font_color, 'F:/mylpj/phpdemo/gd/simsun.ttc', $text);
}


// 5. 验证码背景或干扰噪点:实际上就是随机化一些点或线
// 5.1 干扰点
for ($m = 1; $m < 40; $m++) {
  $randColor = random_int(180, 250);  // 颜色淡一点
  $font_color = imagecolorallocate($ch,  $randColor,   $randColor,   $randColor);
  imagestring($ch, random_int(1, 5), random_int(0, 200), random_int(0, 100), '*', $font_color);
}

// 5.2 干扰线
for ($m = 1; $m < 6; $m++) {
  $randColor = random_int(180, 250);  // 颜色淡一点
  $font_color = imagecolorallocate($ch,  $randColor,   $randColor,   $randColor);
  // 画直线
  imageline($ch, random_int(0, 200), random_int(0, 100), random_int(0, 200), random_int(0, 100), $font_color);
}


// 展示画布资源
header('Content-type:image/png');
imagepng($ch);

// 销毁资源
imagedestroy($ch);




// 产生随机字符串(数字和字母)
function generateRandomString($length = 10)
{
  $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
  $randomString = '';
  $charactersLength = strlen($characters);
  for ($i = 0; $i < $length; $i++) {
    $randomIndex = random_int(0, $charactersLength - 1);
    $randomString .= $characters[$randomIndex];
  }
  return $randomString;
}

缩略图

缩略图:通过技术处理,将原图变成一个尺寸较小的图
缩略图的原理:将原图打开,然后放到另外一个较小的图片资源中,最后保存即可
流程:

  • 打开一个原图资源
  • 构建一个较小的缩略图图片资源
  • 图片采集复制:GD函数库imagecopyresampled
  • 保存缩略图
  • 销毁所有资源

复制采样图片imagecopyresampled:
imagecopyresampled(缩略图资源,原始资源,缩略图存放开始x,缩略图存放开始y,采样原始图x,采样原始图y,缩略图宽,缩略图高,原始图宽,原始图高)
dst_image目标图像资源 src_image原始图像资源
如果原始资源的宽度和高度与目标资源的宽度、高度不一致,则会进行相应的收缩或拉伸

1. 固定宽高

<?php

/**
 * 缩略图
 * 1. 打开一个原图资源
 * 2. 构建一个较小的缩略图图片资源
 * 3. 图片采集复制:GD函数库
 * 4. 保存缩略图
 * 5. 销毁所有资源
 */

//  制作固定尺寸的缩略图:不管原图比例,会存在失真的情况
//  1. 打开一个原图资源
$img = 'img/2.jpg';
$src_img = imagecreatefromjpeg($img);

// 2. 构建一个较小的缩略图图片资源
$dst_img = imagecreatetruecolor(100, 100);

// 3. 图片采集复制,采样拷贝部分图像并调整大小,返回结果是布尔值
// imagecopyresampled(缩略图资源,原始资源,缩略图存放开始x,缩略图存放开始y,采样原始图x,采样原始图y,缩略图宽,缩略图高,原始图宽,原始图高)
// dst_image目标图像资源  src_image原始图像资源
// 如果原始资源的宽度和高度与目标资源的宽度、高度不一致,则会进行相应的收缩或拉伸
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, 100, 100, imagesx($src_img), imagesy($src_img));

// 4. 保存缩略图
header('Content-type:image/png');
imagepng($dst_img); // 输出到浏览器
imagepng($dst_img, 'img/thumb-2.png'); // 保存为图片

// 5. 销毁资源
imagedestroy($dst_img);
imagedestroy($src_img);

2. 等比例宽高缩略图

等比例宽高计算的原理

  • 计算缩略图的宽高比
  • 计算原始图的宽高比
  • 将原始图和缩略图的宽高比进行比较:
    1. 如果 原始图比 < 缩略图比,说明原始图很高,那么缩略图的高是完整的,宽度不够(补白)
    1. 如果 原始图比 > 缩略图比,说明原始图很宽,那么缩略图的宽是完整的,高度不够(补白)
<?php
//  制作 等比例 缩放的缩量图:图片不会变形,但是缩略图有些部分需要额外的白色填充(补白)
//  1. 打开一个原图资源
$img = 'img/1.jpg';
$src_img = imagecreatefromjpeg($img);

// 2. 指定画布的宽高
$thum_width_max = 300;
$thum_height_max = 100;

// 3. 计算缩略图的宽和高
// 等比例宽高计算的原理
// * 计算缩略图的宽高比
// * 计算原始图的宽高比
// * 将原始图和缩略图的宽高比进行比较:
// * 1. 如果 原始图比 < 缩略图比,说明原始图很高,那么缩略图的高是完整的,宽度不够(补白)
// * 2. 如果 原始图比 > 缩略图比,说明原始图很宽,那么缩略图的宽是完整的,高度不够(补白)

// 原始图的宽高比
$src_width = imagesx($src_img);
$src_height = imagesy($src_img);
$src_b = $src_width / $src_height;

// 缩略图宽高比
$thum_width = $thum_height = 0;
$thum_b  = $thum_width_max / $thum_height_max;

if ($src_b  > $thum_b) {
  // 1. 如果 原始图比 < 缩略图比,说明原始图很高,那么缩略图的高是完整的,宽度不够(补白)
  // echo '1';
  $thum_width  = $thum_width_max;
  $thum_height = $thum_width / $src_b;
} else {
  // echo '2';
  // 2. 如果 原始图比 > 缩略图比,说明原始图很宽,那么缩略图的宽是完整的,高度不够(补白)
  $thum_height = $thum_height_max;
  $thum_width =  $thum_height *  $src_b;
}
// echo  '缩略图宽高比:' . $thum_b . '</br>';
// echo  '原始图宽高比:' . $src_b . '</br>';
// echo  '缩量图的宽和高:' . $thum_width . '和' . $thum_height . '</br>';

// 4. 构建一个较小的缩略图图片资源
$dst_img = imagecreatetruecolor($thum_width, $thum_height);
// // 3. 补白 填充白色
// $dst_color = imagecolorallocate($dst_img, 255, 255, 255);
// imagefill($dst_img, 0, 0, $dst_color);

// 5. 图片采集复制
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0,  $thum_width, $thum_height,  $src_width, $src_height);


// 6. 保存缩略图
header('Content-type:image/jpeg');
imagepng($dst_img); // 输出到浏览器
imagepng($dst_img, 'img/11-thum.jpg'); // 保存为图片

// 7. 销毁资源
imagedestroy($dst_img);
imagedestroy($src_img);

水印图

水印图:一般是在某个图片上增加透明的印记
原理:将一张带有明显标记的图片放到需要处理的原始图上
流程:

  • 获取原始图资源(需要处理的图片资源)
  • 获取水印图资源(水印标记)
  • 合并图片(将水印图放到原始图资源上)
  • 保存输出
  • 销毁资源

合并图片:
imagecopymerge函数用于将一个图像(源图像--水印标记)复制并合并到另一个图像(目标图像)上
imagecopymerge(目标图象资源,源图象资源,目标点的 x 坐标,目标点的 y 坐标,源点的 x 坐标,源点的 x 坐标,源图象的宽度,源图象的高度,pct透明度)

<?php
// 水印图:将一张带有明显标记的图片放到需要处理的原始图上
/**
 * 流程
 * 1. 获取原始图资源(需要处理的图片资源)
 * 2. 获取水印图资源(水印标记)
 * 3. 合并图片(将水印图放到原始图资源上)
 * 4. 保存输出
 * 5. 销毁资源
 */


/**
 * 制作水印图
 * $dst_image:原图 filename路径
 * $wat_image:水印标记 filename路径
 * $path:制作后的水印图存储路径 filename路径
 * &$error:记录错误信息的变量
 * $position:$position=1,水印标记位置1左上角 依次类推,9是右下角
 * $pct:透明度 默认10,值越小越模糊
 */
$error = '';
function generateWatermark($src_image, $wat_image, $path, &$error, $position = 1, $pct = 20)
{
  // 验证原图和水印标记是否存在
  if (!is_file($src_image)) {
    $error = '原图不存在';
    return false;
  }
  if (!is_file($wat_image)) {
    $error = '水印标记不存在';
    return false;
  }

  // 判定保存路径是否存在
  if (!is_dir($path)) {
    $error = '保存位置不正确';
    return false;
  }

  $src_info = getimagesize($src_image);
  $wat_info = getimagesize($wat_image);
  $src_type = $src_info['mime']; // 图片的mime类型
  $wat_type = $wat_info['mime'];

  $allow = array(
    'image/jpeg' => 'jpeg',
    'image/png' => 'png',
    'image/jpg' => 'jpg',
  );

  // src_info检查数组里是否有指定的键名或索引
  if (!array_key_exists($src_type, $allow)) {
    $error = '原图片类型有误';
    return false;
  }
  if (!array_key_exists($wat_type, $allow)) {
    $error = '水印标记图片类型有误';
    return false;
  }

  // 组合 图片名称
  $src_open = 'imagecreatefrom' . $allow[$src_type];
  $wat_open = 'imagecreatefrom' . $allow[$wat_type];
  $src_save = 'image' . $allow[$src_type]; // 保存输出的图片数据类型

  // 打开图片资源
  $src_img = $src_open($src_image);
  $wat_img = $wat_open($wat_image);

  // 合并图片(将水印图放到原始图资源上):固定位置
  // 函数用于将一个图像(源图像--水印标记)复制并合并到另一个图像(目标图像)上
  // imagecopymerge(目标图象资源,源图象资源,目标点的 x 坐标,目标点的 y 坐标,源点的 x 坐标,源点的 x 坐标,源图象的宽度,源图象的高度,pct透明度)
  // 两个图像将根据 pct 合并,范围是 0 到 100 透明度
  // 计算水印的位置
  $start_x = $start_y = 0;
  switch ($position) {
    case 1:
      // 左上角
      $start_x = $start_y = 0;
      break;
    case 2:
      // 上中
      $start_x = floor(($src_info[0] - $wat_info[0]) / 2);
      $start_y = 0;
      break;
    case 3:
      // 右上角
      $start_x = floor($src_info[0] - $wat_info[0]);
      $start_y = 0;
      break;
    case 4:
      // 中左
      $start_x = 0;
      $start_y = floor(($src_info[1] - $wat_info[1]) / 2);
      break;
    case 5:
      // 正中间
      $start_x = floor(($src_info[0] - $wat_info[0]) / 2);
      $start_y = floor(($src_info[1] - $wat_info[1]) / 2);
      break;
    case 6:
      // 中右
      $start_x = floor(($src_info[0] - $wat_info[0]));
      $start_y = floor(($src_info[1] - $wat_info[1]) / 2);
      break;
    case 7:
      // 下左
      $start_x = 0;
      $start_y = floor($src_info[1] - $wat_info[1]);;
      break;
    case 8:
      // 下中
      $start_x = floor(($src_info[0] - $wat_info[0]) / 2);
      $start_y = floor($src_info[1] - $wat_info[1]);;
      break;
    case 9:
      // 下右
      $start_x = floor($src_info[0] - $wat_info[0]);
      $start_y = floor($src_info[1] - $wat_info[1]);
      break;
  }
  imagecopymerge($src_img, $wat_img, $start_x, $start_y, 0, 0, imagesx($wat_img), imagesy($wat_img), $pct);

  // 目标图片保存路径
  $filePath = $path . date('Y-m-d-H-i-s') . '-watermark.' . pathinfo($src_image)['extension'];
  // 4. 保存输出
  header("Content-type:{ $src_type }");
  $src_save($src_img);  // 输出到浏览器
  $src_save($src_img,   $filePath); // 保存为图片

  // 7. 销毁资源
  imagedestroy($src_img);
  imagedestroy($wat_img);
  return true;
}

$dst_image = 'img/1.jpg';
$wat_image  = 'img/icon4.png';
if (generateWatermark($dst_image, $wat_image, 'img/', $error, 5, 40)) {
  echo '制作成功';
} else {
  echo $error;
}

posted @ 2024-11-24 18:00  songxia777  阅读(2)  评论(0编辑  收藏  举报