Skip to content
523 changes: 475 additions & 48 deletions src/main_window.cpp

Large diffs are not rendered by default.

53 changes: 49 additions & 4 deletions src/main_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "show_buttons.h"
#include "widgets/shapeswidget.h"
#include "widgets/toptips.h"
#include "widgets/fineadjusttips.h"
#include <functional>

QT_BEGIN_NAMESPACE
Expand Down Expand Up @@ -535,6 +536,11 @@ public slots:
void onAiAssistantSelected(int func);
void prepareScreenshot();
void hideScreenshotTips();

/**
* @brief 刷新像素级微调快捷键提示面板(模式、位置、遮挡与置灰状态)
*/
void updateFineAdjustTips();
void captureScreenshotImage();
void finishScreenshot();
bool saveAction(const QPixmap &pix);
Expand Down Expand Up @@ -819,6 +825,7 @@ public slots:
void keyReleaseEvent(QKeyEvent *event) override;

int getAction(QEvent *event);
int getAction(const QPoint &pos);


/**
Expand All @@ -828,10 +835,12 @@ public slots:
*/
QPixmap paintImage();
void paintEvent(QPaintEvent *event) override;
void resizeBottom(QMouseEvent *event);
void resizeLeft(QMouseEvent *event);
void resizeRight(QMouseEvent *event);
void resizeTop(QMouseEvent *event);
void showEvent(QShowEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void resizeBottom(const QPoint &pos);
void resizeLeft(const QPoint &pos);
void resizeRight(const QPoint &pos);
void resizeTop(const QPoint &pos);
/**
* @brief 更新光标样式
* @param event
Expand All @@ -849,6 +858,30 @@ public slots:
void hideAllWidget();
void adjustLayout(QVBoxLayout *layout, int layoutWidth, int layoutHeight);
void initShapeWidget(QString type);
/**
* @brief 标注模式下是否命中选区边框拖拽点,命中则开始/继续调整截图区域大小
* @param event 原始鼠标事件
* @param mainPos 主窗口坐标系下的光标位置
* @return 是否已消费该事件
*/
bool handleEditModeSelectionResize(QMouseEvent *event, const QPoint &mainPos);
/**
* @brief 应用标注模式下的选区拖拽调整(复用框选阶段的 resize 逻辑)
*/
void applyEditModeSelectionResize(const QPoint &mainPos);
/**
* @brief 标注模式下用 Ctrl/Ctrl+Shift + 方向键调整截图区域大小
* @return 是否已处理该按键
*/
bool handleEditModeSelectionResizeKey(QKeyEvent *keyEvent);
/**
* @brief 同步 ShapesWidget 到当前选区,并可保持已画标注的屏幕绝对位置
*/
void syncShapesWidgetGeometry(bool keepAnnotationsInPlace);
/**
* @brief 选区边框拖拽动作对应的鼠标样式
*/
int resizeCursorShape(int action) const;
int getRecordInputType(bool selectedMic, bool selectedSystemAudio);
/**
* @brief initBackground 初始化截图背景,启动截图时调用
Expand Down Expand Up @@ -1037,6 +1070,14 @@ public slots:
* @brief 是否画骨架点
*/
bool drawDragPoint = false;
/**
* @brief 标注模式下是否正在拖拽选区边框调整截图区域大小
*/
bool m_editModeResizing = false;
/**
* @brief 标注模式下是否已把光标切换为边框缩放样式
*/
bool m_editModeResizeCursorActive = false;
/**
* @brief 是否第一次拖动鼠标
*/
Expand Down Expand Up @@ -1205,6 +1246,10 @@ public slots:
* @brief 左上角图片大小提示
*/
TopTips *m_sizeTips = nullptr;
/**
* @brief 左下角像素级微调快捷键提示
*/
FineAdjustTips *m_fineAdjustTips = nullptr;
/**
* @brief 滚动截图时图片的大小
*/
Expand Down
2 changes: 2 additions & 0 deletions src/src.pro
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ HEADERS += main_window.h \
widgets/zoomIndicator.h \
widgets/textedit.h \
widgets/toptips.h \
widgets/fineadjusttips.h \
widgets/toolbar.h \
widgets/shapeswidget.h \
widgets/toolbutton.h \
Expand Down Expand Up @@ -290,6 +291,7 @@ SOURCES += main.cpp \
utils_interface.cpp \
wayland-treeland-capture-unstable-v1-protocol.c \
widgets/toptips.cpp \
widgets/fineadjusttips.cpp \
widgets/shapeswidget.cpp \
widgets/textedit.cpp \
widgets/zoomIndicator.cpp \
Expand Down
82 changes: 75 additions & 7 deletions src/utils/borderprocessinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
#include <DFontSizeManager>
#include <dstyle.h>

#include <QBitmap>
#include <QBrush>
#include <QPen>
#include <QRegion>

QT_BEGIN_NAMESPACE
// 参考DTK blurEffect 的实现
Expand Down Expand Up @@ -570,6 +573,25 @@ QPixmap ShadowBorderProcess::getPixmapAddBorder(const QPixmap &pix)
const QColor borderColor(0, 0, 0, 50);
const QColor shadowColor(0, 0, 0, 70);

// 判断截图是否包含透明区域(圆角截图会切掉四个圆角),
// 有透明区域时投影与描边都要跟随 alpha 轮廓,否则会透出直角阴影/边框。
QImage shapeImage;
bool shaped = false;
if (pix.hasAlphaChannel()) {
shapeImage = pix.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
const int w = shapeImage.width();
const int h = shapeImage.height();
for (int y = 0; y < h && !shaped; ++y) {
const QRgb *line = reinterpret_cast<const QRgb *>(shapeImage.constScanLine(y));
for (int x = 0; x < w; ++x) {
if (qAlpha(line[x]) != 255) {
shaped = true;
break;
}
}
}
}

// 获取当前缩放比例
qreal radius = shadowRadius * pix.devicePixelRatioF();
QRect generateRect = pix.rect().adjusted(0, 0, radius * 2, radius * 2);
Expand All @@ -580,7 +602,7 @@ QPixmap ShadowBorderProcess::getPixmapAddBorder(const QPixmap &pix)
qCDebug(dsrApp) << "Adjusting draw area: xOffset and yOffset are less than radius.";
imageRect.moveTo(radius - xOffset, radius - yOffset);
} else {
qCDebug(dsrApp) << "Adjusting draw area: xOffset or yOffset is greater than or equal to radius.";
qCDebug(dsrApp) << "Adjusting draw area: xOffset or yOffset are greater than or equal to radius.";
if (xOffset > radius) {
qCDebug(dsrApp) << "Adjusting draw area: xOffset > radius.";
imageRect.setX(0);
Expand All @@ -604,7 +626,7 @@ QPixmap ShadowBorderProcess::getPixmapAddBorder(const QPixmap &pix)
painter.save();
qCDebug(dsrApp) << "Drawing shadow.";
// 绘制投影 DTK提供的接口调整了绘制的透明度,不便直接使用,参考 QPixmapDropShadowFilter 实现
drawShadow(&painter, generateRect, shadowColor, radius);
drawShadow(&painter, generateRect, shadowColor, radius, shapeImage, shaped);
painter.restore();

qCDebug(dsrApp) << "Drawing image.";
Expand All @@ -613,8 +635,13 @@ QPixmap ShadowBorderProcess::getPixmapAddBorder(const QPixmap &pix)

qCDebug(dsrApp) << "Drawing border.";
// 绘制边框 设置 0, 不同缩放率下均绘制1px
painter.setPen(QPen(borderColor));
painter.drawRect(imageRect.adjusted(0, 0, -1, -1));
if (shaped) {
// 圆角截图:描边跟随 alpha 轮廓,避免直角边框压在圆角上
drawShapeBorder(&painter, imageRect, borderColor, shapeImage);
} else {
painter.setPen(QPen(borderColor));
painter.drawRect(imageRect.adjusted(0, 0, -1, -1));
}

painter.end();
return newPixmap;
Expand All @@ -623,7 +650,8 @@ QPixmap ShadowBorderProcess::getPixmapAddBorder(const QPixmap &pix)
/**
@brief 根据传入的矩形 \a rect 绘制居中阴影,以模糊强度 \a radius 为边距(像素计算范围)居中,阴影基础颜色以 \a color 为准
*/
void ShadowBorderProcess::drawShadow(QPainter *p, const QRect &rect, const QColor &color, int radius)
void ShadowBorderProcess::drawShadow(QPainter *p, const QRect &rect, const QColor &color, int radius,
const QImage &source, bool shaped)
{
qCDebug(dsrApp) << "Drawing shadow with rect:" << rect << ", color:" << color << ", radius:" << radius;
// 绘制区域必须大于像素模糊半径
Expand All @@ -637,10 +665,23 @@ void ShadowBorderProcess::drawShadow(QPainter *p, const QRect &rect, const QColo
tmp.fill(0);
QPainter tmpPainter(&tmp);
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
tmpPainter.setBrush(color);
// 调整蒙版图片,居中
QRect tmpRect = rect.adjusted(radius, radius, -radius, -radius);
tmpPainter.drawRect(tmpRect);
if (shaped && !source.isNull()) {
// 用截图自身的 alpha 轮廓作为阴影蒙版:圆角截图的投影同样保持圆角。
QImage silhouette(source.size(), QImage::Format_ARGB32_Premultiplied);
silhouette.fill(Qt::transparent);
{
QPainter silhouettePainter(&silhouette);
silhouettePainter.drawImage(0, 0, source);
silhouettePainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
silhouettePainter.fillRect(silhouette.rect(), color);
}
tmpPainter.drawImage(tmpRect.topLeft(), silhouette);
} else {
tmpPainter.setBrush(color);
tmpPainter.drawRect(tmpRect);
}
tmpPainter.end();

// 在区域中绘制模糊背景
Expand All @@ -649,3 +690,30 @@ void ShadowBorderProcess::drawShadow(QPainter *p, const QRect &rect, const QColo
qt_blurImage(p, tmp, radius, false, true);
p->restore();
}

void ShadowBorderProcess::drawShapeBorder(QPainter *p, const QRect &imageRect, const QColor &color,
const QImage &source)
{
if (source.isNull()) {
return;
}
qCDebug(dsrApp) << "Drawing shape border for rounded pixmap.";
// 1px 圆角描边:对 alpha 轮廓做 1px 膨胀后减去原始轮廓,
// 得到跟随圆角的细边,避免直角边框压在圆角截图上。
const QRegion base(QBitmap::fromImage(source.createAlphaMask(Qt::ThresholdDither)));
QRegion dilated;
for (int dx = -1; dx <= 1; ++dx) {
for (int dy = -1; dy <= 1; ++dy) {
dilated = dilated.united(base.translated(dx, dy));
}
}
const QRegion ring = dilated.subtracted(base).translated(imageRect.topLeft());
if (ring.isEmpty()) {
return;
}

p->save();
p->setClipRegion(ring);
p->fillRect(imageRect.adjusted(-1, -1, 1, 1), color);
p->restore();
}
7 changes: 6 additions & 1 deletion src/utils/borderprocessinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define BORDERPROCESSINTERFACE_H

#include <QObject>
#include <QImage>
#include <QPixmap>
#include <QtSvg>
class BorderProcessInterface : public QObject
Expand Down Expand Up @@ -117,7 +118,11 @@ class ShadowBorderProcess : public BorderProcessInterface
virtual QPixmap getPixmapAddBorder(const QPixmap &pix) override;

private:
void drawShadow(QPainter *p, const QRect &rect, const QColor &color, int radius);
// 依据截图 alpha 轮廓绘制投影/边框,使圆角截图的投影和描边同样保持圆角
void drawShadow(QPainter *p, const QRect &rect, const QColor &color, int radius,
const QImage &source, bool shaped);
void drawShapeBorder(QPainter *p, const QRect &imageRect, const QColor &color,
const QImage &source);
};

#endif // BORDERPROCESSINTERFACE_H
Expand Down
Loading
Loading