一个完整的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. 特性说明
支持的功朄:
-
文字水印:
-
自定义文字内容、颜色、大小
-
旋转角度调整
-
透明度控制
-
平铺模式或单水印模式
-
-
图片水印:
-
支持PNG、JPG、BMP等格式
-
位置选择(左上、右上、左下、右下、居中、平铺)
-
透明度控制
-
自动缩放
-
-
交互功能:
-
鼠标拖动调整水印位置
-
实时预览
-
批量处理
-
编译配置(.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