首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP GD 庫實現圖片灰度半透明疊加效果:原理、代碼與性能優化

     編輯:PHP 相關知識

PHP GD 庫實現圖片灰度半透明疊加效果:原理、代碼與性能優化

在圖片版權保護、狀態標識(如「已售罄」「審核中」)、緩存縮略圖預覽等場景,常需在原圖上疊加一層灰色半透明遮罩。本文基於 GD 庫,詳解「創建同尺寸遮罩 → 填充半透明灰色 → imagecopy() 疊加 → 輸出」的完整流程,並提供生產級封裝函數、記憶體優化建議及 ImageMagick 對比方案。

核心實現代碼

<?php
/**
 * 給圖片添加灰色半透明遮罩
 * @param string $srcPath  源圖路徑 (支援 JPEG/PNG/GIF)
 * @param string $dstPath  輸出路徑 (為空則直接輸出到瀏覽器)
 * @param int    $opacity  透明度 0-127 (0 不透明, 127 完全透明), 默認 75 ≈ 40% 可見度
 * @param array  $color    RGB 顏色, 默認灰色 [0,0,0]
 * @return bool 成功返回 true
 */
function applyGrayOverlay(string $srcPath, string $dstPath = '', int $opacity = 75, array $color = [0,0,0]): bool {
    // 1. 根據副檔名/MIME 創建源圖資源
    $info = @getimagesize($srcPath);
    if (!$info) return false;
    $mime = $info['mime'];
    $createFrom = match ($mime) {
        'image/jpeg' => 'imagecreatefromjpeg',
        'image/png'  => 'imagecreatefrompng',
        'image/gif'  => 'imagecreatefromgif',
        'image/webp' => 'imagecreatefromwebp',
        default      => null,
    };
    if (!$createFrom) return false;
    $srcImg = $createFrom($srcPath);
    if (!$srcImg) return false;

    // 2. 獲取尺寸
    $w = imagesx($srcImg);
    $h = imagesy($srcImg);

    // 3. 創建真彩色遮罩畫布
    $overlay = imagecreatetruecolor($w, $h);
    if (!$overlay) { imagedestroy($srcImg); return false; }

    // 4. 分配帶 Alpha 的顏色 (0-127)
    $alpha = max(0, min(127, $opacity));
    $colorId = imagecolorallocatealpha($overlay, $color[0], $color[1], $color[2], $alpha);
    if ($colorId === false) { imagedestroy($srcImg); imagedestroy($overlay); return false; }

    // 5. 填充整個遮罩
    imagefill($overlay, 0, 0, $colorId);

    // 6. 關鍵:啟用 Alpha 混合模式,否則會蓋住源圖
    imagealphablending($srcImg, true);
    imagesavealpha($srcImg, true); // 保存完整 Alpha 通道 (輸出 PNG 時必須)

    // 7. 將遮罩疊加到源圖 (src -> dst)
    // imagecopy(dst, src, dst_x, dst_y, src_x, src_y, w, h)
    imagecopy($srcImg, $overlay, 0, 0, 0, 0, $w, $h);

    // 8. 輸出
    $success = false;
    if ($dstPath) {
        $ext = strtolower(pathinfo($dstPath, PATHINFO_EXTENSION));
        $success = match ($ext) {
            'png'  => imagepng($srcImg, $dstPath, 9), // 壓縮級別 9
            'jpg', 'jpeg' => imagejpeg($srcImg, $dstPath, 85),
            'webp' => imagewebp($srcImg, $dstPath, 85),
            default => false,
        };
    } else {
        // 直接瀏覽器輸出
        header('Content-Type: ' . $mime);
        $success = match ($mime) {
            'image/jpeg' => imagejpeg($srcImg, null, 85),
            'image/png'  => imagepng($srcImg, null, 9),
            'image/gif'  => imagegif($srcImg),
            'image/webp' => imagewebp($srcImg, null, 85),
            default      => false,
        };
    }

    // 9. 釋放資源 (極重要,防記憶體洩漏)
    imagedestroy($srcImg);
    imagedestroy($overlay);

    return $success;
}

// === 使用範例 ===
// 保存文件
applyGrayOverlay('uploads/photo.jpg', 'uploads/photo_masked.png', 80); // 更透
// 直接輸出 (配合 )
if (isset($_GET['img'])) {
    applyGrayOverlay('uploads/' . basename($_GET['img']));
}

關鍵技術點詳解

  1. imagecolorallocatealpha() Alpha 範圍 0-127:0 完全不透明,127 完全透明。75 ≈ 41% 不透明度,視覺上為「灰色半透明」。
  2. imagealphablending($dst, true) 必須開啟:否則 imagecopy 會直接複製像素覆蓋,而非混合。源圖 ($srcImg) 作為目標畫布需開啟混合。
  3. imagesavealpha($srcImg, true) 輸出 PNG 時必須:保證合成後的 Alpha 通道寫入文件,否則透明區域變黑。
  4. 輸出格式選擇:疊加透明層後必須輸出支援 Alpha 的格式(PNG/WebP)。若強制輸出 JPEG,透明區域會變為黑色/白色背景。

進階:單側/區域遮罩、漸變遮罩、文字水印組合

1. 僅底部 30% 高度添加遮罩(如視頻封面標題區)

$h = imagesy($srcImg);
$maskH = (int)($h * 0.3);
$overlay = imagecreatetruecolor($w, $maskH);
// ... 填充顏色 ...
imagecopy($srcImg, $overlay, 0, $h - $maskH, 0, 0, $w, $maskH);

2. 線性漸變遮罩(透明 → 灰)

$overlay = imagecreatetruecolor($w, $h);
for ($y = 0; $y < $h; $y++) {
    $alpha = (int)(127 * ($y / $h)); // 頂部透明 底部不透明
    $colorId = imagecolorallocatealpha($overlay, 0, 0, 0, $alpha);
    imageline($overlay, 0, $y, $w, $y, $colorId);
}
imagecopy($srcImg, $overlay, 0, 0, 0, 0, $w, $h);

3. 組合文字水印

// 在遮罩上寫文字
$font = __DIR__ . '/fonts/SourceHanSansCN-Regular.otf';
$text = '僅供預覽';
$bbox = imagettfbbox(24, 0, $font, $text);
$textW = $bbox[2] - $bbox[0];
$textH = $bbox[1] - $bbox[5];
$x = ($w - $textW) / 2;
$y = ($h + $textH) / 2;
$white = imagecolorallocatealpha($overlay, 255, 255, 255, 30); // 白色半透明文字
imagettftext($overlay, 24, 0, $x, $y, $white, $font, $text);
// 然後 imagecopy 疊加

性能與記憶體優化指南

優化點說明效果
及時 imagedestroy()每個 imagecreate* 必須配對銷毀,尤其批量處理時避免記憶體洩漏,單張 4000x3000 JPEG 約佔 48MB 記憶體
限制最大尺寸處理前檢查 imagesx() * imagesy() > 16_000_000 則縮放或拒絕防止惡意大圖耗盡 memory_limit
使用 imagecopyresampled() 先縮略再處理若只需縮略圖預覽,先縮小至 800px 再疊加速度提升 10-20 倍,記憶體降低 90%
批量處理用 CLI + pcntl_fork / Symfony Process避免 Web 請求超時,並行利用多核吞吐量提升 N 倍
生產環境考慮 ImageMagick (imagick 擴展)$im->compositeImage($mask, Imagick::COMPOSITE_OVER, 0, 0);速度更快、格式支援更全、色彩管理專業

GD vs ImageMagick 快速對比


// ImageMagick 版本 (需安裝 php-imagick)
$im = new Imagick('photo.jpg');
$mask = new Imagick();
$mask->newPseudoImage($im->getImageWidth(), $im->getImageHeight(), 'canvas:gray');
$mask->setImageOpacity(0.4); // 0-1
$im->compositeImage($mask, Imagick::COMPOSITE_OVER, 0, 0);
$im->writeImage('photo_masked.png');
// 優勢:單行疊加、自動色彩空間、支援 CMYK/HEIC/AVIF、多線程

常見問題排查

  • 輸出圖片全黑/全白: 未調用 imagesavealpha($dst, true) 或輸出 JPEG 導致 Alpha 丟失。
  • 遮罩顏色不對: 確認 imagecolorallocatealpha 的 RGB 順序(紅、綠、藍),且源圖為真彩色 (imagecreatetruecolor),調色板模式不支援 Alpha 混合。
  • 記憶體耗盡: memory_limit 過小,或未銷毀資源。大圖處理建議 ini_set('memory_limit', '512M')。
  • 透明 PNG 疊加後邊緣鋸齒: 開啟 imageantialias($overlay, true) 或使用 ImageMagick。

總結

GD 庫實現灰度半透明遮罩的核心是 「真彩色畫布 + Alpha 顏色分配 + 開啟混合模式 + imagecopy 疊加」。封裝為 applyGrayOverlay() 可滿足大多數業務需求。對於高併發、大尺寸、多格式、專業色彩管理場景,強烈建議遷移至 ImageMagick (imagick 擴展),代碼更簡潔、性能更優、功能更強。無論選哪種方案,imagedestroy() 釋放資源、限制輸入尺寸、錯誤處理都是生產環境的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved