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

C# 圖片水印實作:文字與圖片水印、縮圖、透明度控制完整教學

     編輯:關於C語言

為圖片添加水印是保護版權、品牌推廣的常見需求。本文基於 System.Drawing,實作圖片水印、文字水印 (支援半透明/抗鋸齒)、等比縮圖等核心功能,並封裝為可重用的 ImageHelper 靜態類。

核心輔助方法

1. 等比縮放計算


public static Size CalculateResizeSize(Size original, int maxWidth, int maxHeight)
{
    if (original.Width <= maxWidth && original.Height <= maxHeight) return original;
    
    double ratio = Math.Min((double)maxWidth / original.Width, (double)maxHeight / original.Height);
    return new Size((int)(original.Width * ratio), (int)(original.Height * ratio));
}

2. 產生高品質縮圖


public static Image GenerateThumbnail(Image source, int maxWidth, int maxHeight)
{
    Size newSize = CalculateResizeSize(source.Size, maxWidth, maxHeight);
    var thumb = new Bitmap(newSize.Width, newSize.Height);
    
    using (var g = Graphics.FromImage(thumb))
    {
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.DrawImage(source, new Rectangle(Point.Empty, newSize));
    }
    return thumb;
}

水印核心功能

1. 圖片水印 (支援透明 PNG、位置自訂)


public static void AddImageWatermark(string imagePath, string watermarkPath, 
    WatermarkPosition position = WatermarkPosition.BottomRight, int margin = 10, float opacity = 1.0f)
{
    using (var src = Image.FromFile(imagePath))
    using (var wm = Image.FromFile(watermarkPath))
    using (var g = Graphics.FromImage(src))
    {
        // 計算位置
        Point loc = CalculatePosition(src.Size, wm.Size, position, margin);
        
        // 透明度處理
        var attrs = new ImageAttributes();
        var cm = new ColorMatrix { Matrix33 = opacity }; // Alpha 縮放
        attrs.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
        
        g.DrawImage(wm, new Rectangle(loc, wm.Size), 0, 0, wm.Width, wm.Height, 
            GraphicsUnit.Pixel, attrs);
    }
    
    // 覆蓋儲存 (建議先備份)
    File.Copy(imagePath, imagePath + ".bak", true);
    using (var final = Image.FromFile(imagePath)) // 重新載入避免 GDI+ 鎖定
    {
        // 這裡需將修改後的 src 存回,實務上建議存到新檔再替換
    }
}

2. 文字水印 (抗鋸齒、半透明、陰影/描邊可擴充)


public static void AddTextWatermark(string imagePath, string text, 
    Font font, Color color, WatermarkPosition position = WatermarkPosition.BottomRight, 
    int margin = 10, float opacity = 0.6f)
{
    using (var src = Image.FromFile(imagePath))
    using (var g = Graphics.FromImage(src))
    {
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
        
        // 量測文字大小
        SizeF textSize = g.MeasureString(text, font);
        PointF loc = CalculatePositionF(src.Size, textSize, position, margin);
        
        // 半透明筆刷
        int alpha = (int)(255 * opacity);
        using (var brush = new SolidBrush(Color.FromArgb(alpha, color)))
        {
            // 可選:加陰影增強可讀性
            // g.DrawString(text, font, Brushes.Black, loc.X + 1, loc.Y + 1);
            g.DrawString(text, font, brush, loc);
        }
    }
    // 儲存邏輯同上
}

位置計算輔助


public enum WatermarkPosition { TopLeft, TopRight, BottomLeft, BottomRight, Center }

private static Point CalculatePosition(Size container, Size watermark, WatermarkPosition pos, int margin)
{
    int x = pos switch
    {
        WatermarkPosition.TopLeft or WatermarkPosition.BottomLeft => margin,
        WatermarkPosition.Center => (container.Width - watermark.Width) / 2,
        _ => container.Width - watermark.Width - margin
    };
    int y = pos switch
    {
        WatermarkPosition.TopLeft or WatermarkPosition.TopRight => margin,
        WatermarkPosition.Center => (container.Height - watermark.Height) / 2,
        _ => container.Height - watermark.Height - margin
    };
    return new Point(Math.Max(0, x), Math.Max(0, y));
}

private static PointF CalculatePositionF(Size container, SizeF watermark, WatermarkPosition pos, int margin)
    => new PointF(CalculatePosition(container, watermark.ToSize(), pos, margin));

完整 ImageHelper 類別結構


public static class ImageHelper
{
    // 水印 Logo 相對路徑 (Web 專案常用)
    public static string LogoPath => "/images/watermark.png";

    // 公開 API
    public static void AddWatermark(string imagePath, string watermarkPath, ...);
    public static void AddTextWatermark(string imagePath, string text, ...);
    public static Image Resize(Image source, int maxW, int maxH);
    public static void Save(Image img, string path, ImageFormat fmt = null);
    
    // Stream 版本 (Web 上傳直接處理)
    public static void AddWatermark(Stream input, Stream watermark, Stream output, ...);
}

ASP.NET Core / WebForms 使用範例


// 上傳即時加水印儲存
public void ProcessUpload(HttpPostedFileBase file)
{
    string savePath = Server.MapPath($"~/uploads/{Guid.NewGuid()}.jpg");
    using (var ms = new MemoryStream())
    {
        file.InputStream.CopyTo(ms);
        ms.Position = 0;
        
        using (var src = Image.FromStream(ms))
        using (var wm = Image.FromFile(Server.MapPath(ImageHelper.LogoPath)))
        {
            ImageHelper.AddImageWatermark(src, wm, WatermarkPosition.BottomRight);
            src.Save(savePath, ImageFormat.Jpeg);
        }
    }
}

關鍵技術細節與避坑指南

主題說明
GDI+ 檔案鎖定Image.FromFile 會鎖定檔案直到 Dispose。修改後存回同路徑會報錯。
解決:存到暫存檔 → 刪原檔 → 改名,或先 File.Copy 到記憶體處理。
透明度實現使用 ColorMatrix.Matrix33 縮放 Alpha 通道,配合 ImageAttributes 套用至 DrawImage。
文字清晰度設定 SmoothingMode.AntiAlias + TextRenderingHint.ClearTypeGridFit。
大圖記憶體處理超大圖 (如 4K+ 照片) 易 OOM。建議分塊處理或使用 ImageSharp 等現代庫。
跨平台System.Drawing.Common 僅支援 Windows/Linux (需 libgdiplus)。Linux 容器建議改用 SixLabors.ImageSharp (純 managed)。
EXIF 方向手機照片含 Orientation 標籤,直接畫會旋轉錯誤。需先讀取 EXIF 旋轉修正。

總結

掌握 Graphics.DrawImage (配合 ImageAttributes) 與 DrawString 即可實作專業級水印功能。封裝為 ImageHelper 統一管理參數與流程,專案中即可一鍵呼叫。現代雲原生環境建議遷移至 ImageSharp 獲得更好的跨平台穩定性與記憶體安全。

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