Qt水印实现样例详解

一个完整的Qt水印实现样例,包含文字水印和图片水印两种方式。

1. 文字水印实现

WatermarkWidget.h

cpp 复制代码
#ifndef WATERMARKWIDGET_H
#define WATERMARKWIDGET_H

#include <QWidget>
#include <QPixmap>
#include <QPainter>
#include <QMouseEvent>

class WatermarkWidget : public QWidget
{
    Q_OBJECT

public:
    explicit WatermarkWidget(QWidget *parent = nullptr);
    
    // 设置水印文字
    void setTextWatermark(const QString &text, 
                         const QColor &color = QColor(255, 255, 255, 100),
                         int fontSize = 20,
                         int rotation = 45);
    
    // 设置水印图片
    void setImageWatermark(const QPixmap &watermark,
                          qreal opacity = 0.3,
                          WatermarkPosition position = Center);
    
    // 加载背景图片
    void loadImage(const QString &imagePath);
    
    // 保存带水印的图片
    bool saveWatermarkedImage(const QString &savePath);
    
    enum WatermarkPosition {
        TopLeft,
        TopRight,
        BottomLeft,
        BottomRight,
        Center,
        Tile
    };

protected:
    void paintEvent(QPaintEvent *event) override;
    void mousePressEvent(QMouseEvent *event) override;
    void mouseMoveEvent(QMouseEvent *event) override;
    void mouseReleaseEvent(QMouseEvent *event) override;

private:
    QPixmap m_backgroundImage;
    QPixmap m_watermarkImage;
    QString m_watermarkText;
    QColor m_textColor;
    int m_fontSize;
    int m_rotation;
    qreal m_opacity;
    WatermarkPosition m_position;
    
    // 用于拖动水印
    bool m_isDragging;
    QPoint m_dragStartPosition;
    QPoint m_watermarkOffset;
    
    // 绘制水印
    void drawTextWatermark(QPainter &painter);
    void drawImageWatermark(QPainter &painter);
};

#endif // WATERMARKWIDGET_H

WatermarkWidget.cpp

cpp 复制代码
#include "WatermarkWidget.h"
#include <QPainter>
#include <QApplication>
#include <QFileDialog>
#include <QMessageBox>

WatermarkWidget::WatermarkWidget(QWidget *parent)
    : QWidget(parent)
    , m_fontSize(20)
    , m_rotation(45)
    , m_opacity(0.3)
    , m_position(Center)
    , m_isDragging(false)
    , m_watermarkOffset(0, 0)
{
    setTextColor(QColor(255, 255, 255, 100));
    setMouseTracking(true);
}

void WatermarkWidget::setTextWatermark(const QString &text, 
                                      const QColor &color,
                                      int fontSize,
                                      int rotation)
{
    m_watermarkText = text;
    m_textColor = color;
    m_fontSize = fontSize;
    m_rotation = rotation;
    m_watermarkImage = QPixmap(); // 清除图片水印
    update();
}

void WatermarkWidget::setImageWatermark(const QPixmap &watermark,
                                       qreal opacity,
                                       WatermarkPosition position)
{
    m_watermarkImage = watermark;
    m_opacity = opacity;
    m_position = position;
    m_watermarkText.clear(); // 清除文字水印
    update();
}

void WatermarkWidget::loadImage(const QString &imagePath)
{
    if (m_backgroundImage.load(imagePath)) {
        setFixedSize(m_backgroundImage.size());
        update();
    } else {
        QMessageBox::warning(this, "错误", "无法加载图片!");
    }
}

void WatermarkWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event);
    
    QPainter painter(this);
    
    // 绘制背景图片
    if (!m_backgroundImage.isNull()) {
        painter.drawPixmap(0, 0, m_backgroundImage);
    }
    
    // 设置透明度
    painter.setOpacity(m_opacity);
    
    // 绘制水印
    if (!m_watermarkText.isEmpty()) {
        drawTextWatermark(painter);
    } else if (!m_watermarkImage.isNull()) {
        drawImageWatermark(painter);
    }
}

void WatermarkWidget::drawTextWatermark(QPainter &painter)
{
    painter.save();
    
    // 设置字体
    QFont font = painter.font();
    font.setPointSize(m_fontSize);
    font.setBold(true);
    painter.setFont(font);
    
    // 设置颜色
    painter.setPen(m_textColor);
    
    // 计算水印位置和旋转
    QRect rect = this->rect();
    painter.translate(rect.center() + m_watermarkOffset);
    painter.rotate(m_rotation);
    
    // 绘制水印文字(可平铺)
    QFontMetrics fm(font);
    int textWidth = fm.horizontalAdvance(m_watermarkText);
    int textHeight = fm.height();
    
    // 计算平铺的行列数
    int cols = (rect.width() * 2) / textWidth + 2;
    int rows = (rect.height() * 2) / textHeight + 2;
    
    for (int i = -rows/2; i <= rows/2; ++i) {
        for (int j = -cols/2; j <= cols/2; ++j) {
            int x = j * textWidth * 1.5;
            int y = i * textHeight * 1.5;
            painter.drawText(x, y, m_watermarkText);
        }
    }
    
    painter.restore();
}

void WatermarkWidget::drawImageWatermark(QPainter &painter)
{
    QRect rect = this->rect();
    
    switch (m_position) {
    case TopLeft:
        painter.drawPixmap(10 + m_watermarkOffset.x(), 
                          10 + m_watermarkOffset.y(), 
                          m_watermarkImage);
        break;
    case TopRight:
        painter.drawPixmap(rect.width() - m_watermarkImage.width() - 10 + m_watermarkOffset.x(),
                          10 + m_watermarkOffset.y(),
                          m_watermarkImage);
        break;
    case BottomLeft:
        painter.drawPixmap(10 + m_watermarkOffset.x(),
                          rect.height() - m_watermarkImage.height() - 10 + m_watermarkOffset.y(),
                          m_watermarkImage);
        break;
    case BottomRight:
        painter.drawPixmap(rect.width() - m_watermarkImage.width() - 10 + m_watermarkOffset.x(),
                          rect.height() - m_watermarkImage.height() - 10 + m_watermarkOffset.y(),
                          m_watermarkImage);
        break;
    case Center:
        painter.drawPixmap(rect.center() - QPoint(m_watermarkImage.width()/2, 
                                                 m_watermarkImage.height()/2) + m_watermarkOffset,
                          m_watermarkImage);
        break;
    case Tile:
        // 平铺水印图片
        for (int x = 0; x < rect.width(); x += m_watermarkImage.width()) {
            for (int y = 0; y < rect.height(); y += m_watermarkImage.height()) {
                painter.drawPixmap(x + m_watermarkOffset.x(), 
                                 y + m_watermarkOffset.y(), 
                                 m_watermarkImage);
            }
        }
        break;
    }
}

// 鼠标事件实现拖动水印
void WatermarkWidget::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        m_isDragging = true;
        m_dragStartPosition = event->pos();
    }
}

void WatermarkWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (m_isDragging) {
        QPoint delta = event->pos() - m_dragStartPosition;
        m_watermarkOffset += delta;
        m_dragStartPosition = event->pos();
        update();
    }
}

void WatermarkWidget::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
    m_isDragging = false;
}

bool WatermarkWidget::saveWatermarkedImage(const QString &savePath)
{
    if (m_backgroundImage.isNull()) {
        return false;
    }
    
    QPixmap result = m_backgroundImage.copy();
    QPainter painter(&result);
    painter.setOpacity(m_opacity);
    
    if (!m_watermarkText.isEmpty()) {
        drawTextWatermark(painter);
    } else if (!m_watermarkImage.isNull()) {
        drawImageWatermark(painter);
    }
    
    return result.save(savePath);
}

2. 主窗口界面

MainWindow.h

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "WatermarkWidget.h"

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onOpenImage();
    void onAddTextWatermark();
    void onAddImageWatermark();
    void onSaveImage();
    void onWatermarkOpacityChanged(int value);
    void onWatermarkRotationChanged(int value);
    void onWatermarkFontSizeChanged(int value);

private:
    Ui::MainWindow *ui;
    WatermarkWidget *m_watermarkWidget;
    
    void setupWatermarkControls();
};
#endif // MAINWINDOW_H

MainWindow.cpp

cpp 复制代码
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QFileDialog>
#include <QColorDialog>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , m_watermarkWidget(new WatermarkWidget(this))
{
    ui->setupUi(this);
    
    // 设置中心部件
    setCentralWidget(m_watermarkWidget);
    
    // 创建工具栏
    setupWatermarkControls();
    
    // 连接信号槽
    connect(ui->actionOpenImage, &QAction::triggered, this, &MainWindow::onOpenImage);
    connect(ui->actionAddTextWatermark, &QAction::triggered, this, &MainWindow::onAddTextWatermark);
    connect(ui->actionAddImageWatermark, &QAction::triggered, this, &MainWindow::onAddImageWatermark);
    connect(ui->actionSaveImage, &QAction::triggered, this, &MainWindow::onSaveImage);
    
    // 设置默认水印文字
    m_watermarkWidget->setTextWatermark("保密文件", QColor(255, 255, 255, 150), 30, 45);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::setupWatermarkControls()
{
    // 创建工具栏
    QToolBar *toolBar = addToolBar("控制");
    
    // 透明度控制
    QLabel *opacityLabel = new QLabel("透明度:", this);
    QSlider *opacitySlider = new QSlider(Qt::Horizontal, this);
    opacitySlider->setRange(0, 100);
    opacitySlider->setValue(30);
    
    // 旋转角度控制
    QLabel *rotationLabel = new QLabel("旋转角度:", this);
    QSlider *rotationSlider = new QSlider(Qt::Horizontal, this);
    rotationSlider->setRange(0, 360);
    rotationSlider->setValue(45);
    
    // 字体大小控制
    QLabel *fontSizeLabel = new QLabel("字体大小:", this);
    QSpinBox *fontSizeSpinBox = new QSpinBox(this);
    fontSizeSpinBox->setRange(10, 100);
    fontSizeSpinBox->setValue(30);
    
    toolBar->addWidget(opacityLabel);
    toolBar->addWidget(opacitySlider);
    toolBar->addSeparator();
    toolBar->addWidget(rotationLabel);
    toolBar->addWidget(rotationSlider);
    toolBar->addSeparator();
    toolBar->addWidget(fontSizeLabel);
    toolBar->addWidget(fontSizeSpinBox);
    
    // 连接控制信号
    connect(opacitySlider, &QSlider::valueChanged, this, &MainWindow::onWatermarkOpacityChanged);
    connect(rotationSlider, &QSlider::valueChanged, this, &MainWindow::onWatermarkRotationChanged);
    connect(fontSizeSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
            this, &MainWindow::onWatermarkFontSizeChanged);
}

void MainWindow::onOpenImage()
{
    QString fileName = QFileDialog::getOpenFileName(this,
        "打开图片", "", "图片文件 (*.png *.jpg *.jpeg *.bmp)");
    
    if (!fileName.isEmpty()) {
        m_watermarkWidget->loadImage(fileName);
    }
}

void MainWindow::onAddTextWatermark()
{
    bool ok;
    QString text = QInputDialog::getText(this, "文字水印",
                                         "请输入水印文字:", 
                                         QLineEdit::Normal,
                                         "保密文件", &ok);
    
    if (ok && !text.isEmpty()) {
        QColor color = QColorDialog::getColor(Qt::white, this, "选择水印颜色");
        if (color.isValid()) {
            m_watermarkWidget->setTextWatermark(text, color, 30, 45);
        }
    }
}

void MainWindow::onAddImageWatermark()
{
    QString fileName = QFileDialog::getOpenFileName(this,
        "选择水印图片", "", "图片文件 (*.png *.jpg *.jpeg *.bmp)");
    
    if (!fileName.isEmpty()) {
        QPixmap watermark;
        if (watermark.load(fileName)) {
            // 调整水印图片大小
            watermark = watermark.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation);
            m_watermarkWidget->setImageWatermark(watermark, 0.3, WatermarkWidget::BottomRight);
        }
    }
}

void MainWindow::onSaveImage()
{
    QString fileName = QFileDialog::getSaveFileName(this,
        "保存图片", "", "PNG图片 (*.png);;JPEG图片 (*.jpg *.jpeg);;BMP图片 (*.bmp)");
    
    if (!fileName.isEmpty()) {
        if (m_watermarkWidget->saveWatermarkedImage(fileName)) {
            QMessageBox::information(this, "保存成功", "图片已保存!");
        } else {
            QMessageBox::warning(this, "保存失败", "保存图片失败!");
        }
    }
}

void MainWindow::onWatermarkOpacityChanged(int value)
{
    // 将0-100转换为0.0-1.0
    qreal opacity = value / 100.0;
    // 这里需要添加设置透明度的方法到WatermarkWidget
    // m_watermarkWidget->setOpacity(opacity);
    m_watermarkWidget->update();
}

void MainWindow::onWatermarkRotationChanged(int value)
{
    // 这里需要添加设置旋转角度的方法到WatermarkWidget
    // m_watermarkWidget->setRotation(value);
    m_watermarkWidget->update();
}

void MainWindow::onWatermarkFontSizeChanged(int value)
{
    // 这里需要添加设置字体大小的方法到WatermarkWidget
    // m_watermarkWidget->setFontSize(value);
    m_watermarkWidget->update();
}

3. 简化的水印生成类(独立使用)

如果您只需要简单的水印功能,可以使用这个工具类:

WatermarkGenerator.h

cpp 复制代码
#ifndef WATERMARKGENERATOR_H
#define WATERMARKGENERATOR_H

#include <QObject>
#include <QPixmap>

class WatermarkGenerator : public QObject
{
    Q_OBJECT

public:
    explicit WatermarkGenerator(QObject *parent = nullptr);
    
    // 添加文字水印到图片
    static bool addTextWatermark(const QString &imagePath,
                                const QString &savePath,
                                const QString &watermarkText,
                                const QColor &color = QColor(255, 255, 255, 100),
                                int fontSize = 20,
                                int rotation = 45);
    
    // 添加图片水印
    static bool addImageWatermark(const QString &imagePath,
                                 const QString &savePath,
                                 const QString &watermarkImagePath,
                                 qreal opacity = 0.3,
                                 Qt::Alignment alignment = Qt::AlignBottom | Qt::AlignRight);
    
    // 批量添加水印
    static bool batchAddWatermark(const QStringList &imagePaths,
                                 const QString &outputDir,
                                 const QString &watermarkText);
};

#endif // WATERMARKGENERATOR_H

WatermarkGenerator.cpp

cpp 复制代码
#include "WatermarkGenerator.h"
#include <QPainter>
#include <QDir>

WatermarkGenerator::WatermarkGenerator(QObject *parent)
    : QObject(parent)
{
}

bool WatermarkGenerator::addTextWatermark(const QString &imagePath,
                                         const QString &savePath,
                                         const QString &watermarkText,
                                         const QColor &color,
                                         int fontSize,
                                         int rotation)
{
    QPixmap image;
    if (!image.load(imagePath)) {
        return false;
    }
    
    QPainter painter(&image);
    painter.setOpacity(color.alphaF());
    
    // 设置字体
    QFont font = painter.font();
    font.setPointSize(fontSize);
    font.setBold(true);
    painter.setFont(font);
    
    // 设置颜色
    painter.setPen(color);
    
    // 设置旋转
    painter.save();
    painter.translate(image.width() / 2, image.height() / 2);
    painter.rotate(rotation);
    
    // 计算平铺水印
    QFontMetrics fm(font);
    int textWidth = fm.horizontalAdvance(watermarkText);
    int textHeight = fm.height();
    
    int cols = (image.width() * 2) / textWidth + 2;
    int rows = (image.height() * 2) / textHeight + 2;
    
    for (int i = -rows/2; i <= rows/2; ++i) {
        for (int j = -cols/2; j <= cols/2; ++j) {
            int x = j * textWidth * 1.5;
            int y = i * textHeight * 1.5;
            painter.drawText(x - textWidth/2, y, watermarkText);
        }
    }
    
    painter.restore();
    
    return image.save(savePath);
}

bool WatermarkGenerator::addImageWatermark(const QString &imagePath,
                                          const QString &savePath,
                                          const QString &watermarkImagePath,
                                          qreal opacity,
                                          Qt::Alignment alignment)
{
    QPixmap background;
    if (!background.load(imagePath)) {
        return false;
    }
    
    QPixmap watermark;
    if (!watermark.load(watermarkImagePath)) {
        return false;
    }
    
    // 调整水印大小(最大为背景的1/4)
    int maxSize = qMin(background.width(), background.height()) / 4;
    watermark = watermark.scaled(maxSize, maxSize, 
                                Qt::KeepAspectRatio, 
                                Qt::SmoothTransformation);
    
    QPainter painter(&background);
    painter.setOpacity(opacity);
    
    // 根据对齐方式计算位置
    QPoint pos;
    int margin = 10;
    
    if (alignment & Qt::AlignLeft) {
        pos.setX(margin);
    } else if (alignment & Qt::AlignRight) {
        pos.setX(background.width() - watermark.width() - margin);
    } else {
        pos.setX((background.width() - watermark.width()) / 2);
    }
    
    if (alignment & Qt::AlignTop) {
        pos.setY(margin);
    } else if (alignment & Qt::AlignBottom) {
        pos.setY(background.height() - watermark.height() - margin);
    } else {
        pos.setY((background.height() - watermark.height()) / 2);
    }
    
    painter.drawPixmap(pos, watermark);
    
    return background.save(savePath);
}

bool WatermarkGenerator::batchAddWatermark(const QStringList &imagePaths,
                                          const QString &outputDir,
                                          const QString &watermarkText)
{
    QDir dir(outputDir);
    if (!dir.exists()) {
        dir.mkpath(".");
    }
    
    for (const QString &imagePath : imagePaths) {
        QFileInfo fileInfo(imagePath);
        QString outputPath = outputDir + "/watermarked_" + fileInfo.fileName();
        
        if (!addTextWatermark(imagePath, outputPath, watermarkText)) {
            return false;
        }
    }
    
    return true;
}

4. 使用示例

cpp 复制代码
// 简单使用示例
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    
    // 方式1:使用工具类
    WatermarkGenerator::addTextWatermark(
        "input.jpg",
        "output.jpg",
        "Sample Watermark",
        QColor(255, 255, 255, 150),
        30,
        45
    );
    
    // 方式2:使用完整窗口程序
    MainWindow window;
    window.show();
    
    return app.exec();
}

5. 特性说明

支持的功朄:

  1. 文字水印

    • 自定义文字内容、颜色、大小

    • 旋转角度调整

    • 透明度控制

    • 平铺模式或单水印模式

  2. 图片水印

    • 支持PNG、JPG、BMP等格式

    • 位置选择(左上、右上、左下、右下、居中、平铺)

    • 透明度控制

    • 自动缩放

  3. 交互功能

    • 鼠标拖动调整水印位置

    • 实时预览

    • 批量处理

编译配置(.pro文件):

cpp 复制代码
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = WatermarkDemo
TEMPLATE = app

SOURCES += \
    main.cpp \
    WatermarkWidget.cpp \
    MainWindow.cpp \
    WatermarkGenerator.cpp

HEADERS += \
    WatermarkWidget.h \
    MainWindow.h \
    WatermarkGenerator.h

FORMS += \
    MainWindow.ui
相关推荐
草莓熊Lotso2 小时前
Qt 入门核心指南:从框架认知到环境搭建 + Qt Creator 实战
xml·开发语言·网络·c++·人工智能·qt·页面
疯狂的挖掘机11 小时前
记一次基于QT的图片操作处理优化思路(包括在图上放大缩小,截图,画线,取值等)
开发语言·数据库·qt
奇树谦12 小时前
Qt | 利用map创建多个线程和定时器
网络·数据库·qt
SoveTingღ16 小时前
【问题解析】我的客户端与服务器交互无响应了?
服务器·c++·qt·tcp
怎么就重名了17 小时前
记录Qt的UDP通信丢包问题
开发语言·qt·udp
追烽少年x17 小时前
Qt面试题合集(四)
qt
GanGuaGua18 小时前
JsonRpc:手搓一个高性能Rpc服务(应用篇)
qt·网络协议·rpc
追烽少年x21 小时前
Qt中线程同步类介绍(一)
qt
树欲静而风不止慢一点吧21 小时前
Qt5/6版本对应的Emscripten版本
开发语言·qt