基本ロジック
1.画像セットの合計の幅と高さを計算し、最大の幅と高さを抽出します
。2 。最大の幅と高さが合計の幅または高さの1/4を超える場合、それらは水平または垂直にのみマージされます。次に、手順13に進み
ます。3。サイズと位置のあるすべての写真を取り出し、並べ替えます(幅+高さは大きいものから小さいものへ)
4。最初のスペースは全幅の1/4で、追加を開始します水平方向の
画像5.画像セットがまだ存在するかどうかを確認します画像がない場合は、手順13に
進みます。6。空き領域に入れることができる画像を取り出し、残りの空き領域を計算して、この位置を記録します画像を追加して、適切な画像がなくなるまで次の空き領域に追加します
。7に配置できます。マップがある場合は、最後に残った領域のデータが下の行に設定された残りの領域に追加されます。新しい画像はマージされません
。手順9にスキップします。8。下の行に設定された残りのスペースは、高さの順に小さいものから大きいものへと並べ替えられます。9 。
小さいものを順番に取り出します。下の行の高さの残りのスペースはスキップします。手順5に
進みます。10。最小の高さの下の行の残りのスペースと、最後にマージされていない新しい画像の残りのスペースを取り出してマージを試みます。隣接している場合、マージは成功し、手順511にスキップし
ます。結合できない残りのスペースは一時コレクションに追加されます。一時コレクション内のデータの数が下部の空のスペースセットの数を超えると、マージサイクルはこれらの残りのスペースをマージしてから、下部に配置します。空のスペースセット
12、手順5にスキップします
。13。マージ後に最大の幅と高さを取り出します。
環境要件
1、php7.2 +
2、GD拡張
ロジック処理コードのマージ
/**
* 合并碎图
*/
class ImageMerge {
/**
* @var array 支持处理图片
*/
const IMAGE_CREATE_FUNC = [
IMAGETYPE_GIF => 'imagecreatefromgif',
IMAGETYPE_JPEG => 'imagecreatefromjpeg',
IMAGETYPE_PNG => 'imagecreatefrompng',
IMAGETYPE_BMP => 'imagecreatefrombmp',
IMAGETYPE_WBMP => 'imagecreatefromwbmp',
IMAGETYPE_XBM => 'imagecreatefromxbm',
];
/**
* @var int 宽度索引
*/
const KEY_WIDTH = 0;
/**
* @var int 高度索引
*/
const KEY_HEIGHT = 1;
/**
* @var int 图片类型索引
*/
const KEY_TYPE = 2;
/**
* @var int 图片文件名索引
*/
const KEY_FILENAME = 3;
/**
* @var int 图片X轴坐标索引
*/
const KEY_X = 4;
/**
* @var int 图片Y轴坐标索引
*/
const KEY_Y = 5;
/**
* @var array 要处理的图像集
*/
protected $images = [];
/**
* @var array 背景色值
*/
protected $bgColor = ['red' => 0, 'green' => 0, 'blue' => 0, 'alpha' => 127];
/**
* 初始化处理
* @param array $bgColor
*/
public function __construct(array $bgColor = null) {
if ($bgColor) {
$this->bgColor = array_merge($this->bgColor, $bgColor);
}
}
/**
* 添加合并图片所在目录
* @param string $dir
* @param bool $recursion
*/
public function addDir(string $dir, bool $recursion = true) {
if (file_exists($dir) && is_dir($dir) && $handle = opendir($dir)) {
while (false !== $file = readdir($handle)) {
if ($file == '.' || $file == '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $file;
if (is_dir($file)) {
$recursion && $this->addDir($path, $recursion);
} else {
$this->add($path);
}
}
}
}
/**
* 添加要合并的图片集
* @param string $files
*/
public function addFile(string ...$files) {
foreach ($files as $file) {
$this->add($file);
}
}
/**
* 保存合并图片
* @param string $savepath
* @param int $space
* @param int $quality
* @return bool
*/
public function save(string $savepath = null, int $space = 2, int $quality = 3) {
if ($image = $this->make($space)) {
if ($savepath) {
$dir = dirname($savepath);
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
}
}
return imagepng($image, $savepath, $quality);
}
return false;
}
/**
* 生成图片
* @param int $space
* @return resource|null
*/
public function make(int $space = 2) {
if (count($this->images)) {
list($array, $width, $height) = $this->equidistribution($space);
$image = $this->create($width, $height);
foreach ($array as $img) {
imagecopy($image, call_user_func(static::IMAGE_CREATE_FUNC[$img[static::KEY_TYPE]], $img[static::KEY_FILENAME]), $img[static::KEY_X], $img[static::KEY_Y], 0, 0, $img[static::KEY_WIDTH], $img[static::KEY_HEIGHT]);
}
return $image;
}
}
/**
* 分布处理
* @param int $space
* @return array
*/
protected function equidistribution(int $space) {
$maxwidth = $totalwidth = $maxheight = $totalheight = 0;
foreach ($this->images as $item) {
$totalwidth += $item[static::KEY_WIDTH];
$totalheight += $item[static::KEY_HEIGHT];
$maxwidth = max($maxwidth, $item[static::KEY_WIDTH]);
$maxheight = max($maxheight, $item[static::KEY_HEIGHT]);
}
$theory = ceil($totalwidth / 4);
if ($theory < $maxwidth) { //直接横放
list($array, $width) = $this->oneway($space, static::KEY_Y, static::KEY_X, static::KEY_WIDTH, static::KEY_HEIGHT);
$height = $maxheight;
} elseif ($totalheight / 4 < $maxheight) { //直接坚放
list($array, $height) = $this->oneway($space, static::KEY_X, static::KEY_Y, static::KEY_HEIGHT, static::KEY_WIDTH);
$width = $maxwidth;
} else { //铺开
return $this->spread($this->images, $space, $theory + $space);
}
return [$array, $width, $height];
}
/**
* 均匀铺开处理
* @param array $images
* @param int $space
* @param int $maxWidth
* @return array
*/
protected function spread(array $images, int $space, int $maxWidth) {
$lists = $heights = $array = [];
$surplusWidth = $maxWidth;
$x = $y = 0;
usort($images, function($prev, $next) {
return $prev[static::KEY_WIDTH] + $prev[static::KEY_HEIGHT] < $next[static::KEY_WIDTH] + $next[static::KEY_HEIGHT] ? 1 : -1;
});
while ($count = count($images)) {
$surplusWidth -= $space;
foreach ($images as $key => $img) {
if ($img[static::KEY_WIDTH] <= $surplusWidth) {
$width = $img[static::KEY_WIDTH] + $space;
$img[static::KEY_X] = $x;
$img[static::KEY_Y] = $y;
$array[] = $img;
$heights[] = [static::KEY_WIDTH => $width, static::KEY_X => $x, static::KEY_Y => $y + $img[static::KEY_HEIGHT] + $space];
$surplusWidth -= $width;
$x += $width;
unset($images[$key]);
if ($surplusWidth <= 0) {
break;
}
}
}
$surplusWidth += $space;
if ($count > count($images)) {//上面找到合适的需要新位置
if ($surplusWidth > 0) {//追加多余部分
$heights[] = [static::KEY_WIDTH => $surplusWidth, static::KEY_X => $x, static::KEY_Y => $y];
}
$heights = $this->sortHeights(array_merge($lists, $heights));
$lists = [];
} else {// 上面没合适的需要位置
$prev = [static::KEY_WIDTH => $surplusWidth, static::KEY_X => $x, static::KEY_Y => $y];
if (count($heights)) {
$merge = $this->mergeAdjoin([$prev, $heights[0]]);
if (count($merge) == 1) { //合并确认是否为相邻
$heights[0] = end($merge);
goto INIT_SET;
}
}
$lists[] = $prev;
if (count($heights) <= count($lists)) {
$heights = $this->mergeAdjoin(array_merge($lists, $heights));
$lists = [];
}
}
INIT_SET: [static::KEY_X => $x, static::KEY_Y => $y, static::KEY_WIDTH => $surplusWidth] = array_shift($heights);
}
[$width, $height] = $this->getMaxSize($array);
return [$array, $width, $height];
}
/**
* 获取最大空间
* @param array $array
* @return array
*/
protected function getMaxSize(array $array) {
$maxWidth = $maxHeight = 0;
foreach ($array as $item) {
$width = $item[static::KEY_WIDTH] + $item[static::KEY_X];
$height = $item[static::KEY_Y] + $item[static::KEY_HEIGHT];
if ($width > $maxWidth) {
$maxWidth = $width;
}
if ($height > $maxHeight) {
$maxHeight = $height;
}
}
return [$maxWidth, $maxHeight];
}
/**
* 位置高度排序
* @param array $heights
* @return array
*/
protected function sortHeights(array $heights) {
usort($heights, function($prev, $next) {
return $prev[static::KEY_Y] > $next[static::KEY_Y] ? 1 : -1;
});
return $heights;
}
/**
* 合并相邻的位置
* @param array $heights
* @return array
*/
protected function mergeAdjoin(array $heights) {
$array = [];
while (count($heights)) {
$current = array_shift($heights);
[static::KEY_X => $x, static::KEY_Y => $y, static::KEY_WIDTH => $surplusWidth] = $current;
foreach ($heights as $key => $item) {
if ($item[static::KEY_X] + $item[static::KEY_WIDTH] == $x) {//相邻右边
$x = $item[static::KEY_X];
} elseif ($item[static::KEY_X] - $surplusWidth != $x) {//不相邻右边
continue;
}
$array[] = [static::KEY_X => $x, static::KEY_Y => $item[static::KEY_Y], static::KEY_WIDTH => $surplusWidth + $item[static::KEY_WIDTH]];
unset($heights[$key]);
continue 2;
}
$array[] = $current;
}
return $this->sortHeights($array);
}
/**
* 单向放
* @param int $space
* @param int $fixed
* @param int $move
* @param int $size
* @return array
*/
protected function oneway(int $space, int $fixed, int $move, int $size) {
$array = [];
$pos = 0;
foreach ($this->images as $img) {
$img[$move] = $pos;
$img[$fixed] = 0;
$pos += $space + $img[$size];
$array[] = $img;
}
return [$array, $pos - $space];
}
/**
* 创建底图
* @param int $width
* @param int $height
* @return resource
*/
protected function create(int $width, int $height) {
$image = imagecreatetruecolor($width, $height);
imagesavealpha($image, true);
$color = imagecolorallocatealpha($image, $this->bgColor['red'], $this->bgColor['green'], $this->bgColor['blue'], $this->bgColor['alpha']);
imagefill($image, 0, 0, $color);
return $image;
}
/**
* 添加图片文件
* @param string $filename
*/
protected function add(string $filename) {
if (file_exists($filename) && strpos(mime_content_type($filename), 'image/') === 0 && (false !== $array = getimagesize($filename)) && isset(static::IMAGE_CREATE_FUNC[$array[2]])) {
$this->images[] = [
static::KEY_WIDTH => $array[0],
static::KEY_HEIGHT => $array[1],
static::KEY_TYPE => $array[2],
static::KEY_FILENAME => $filename,
];
}
}
}
テスト検証コード
//合并
$dir = '要合并的图片集目录'
$imgMerge = new ImageMerge();
$imgMerge->addDir($dir);
$imgMerge->save($dir . '/merge-image.png', 5);