402 lines
12 KiB
C++
402 lines
12 KiB
C++
#include "xygifframe.h"
|
||
#include "xypackimage.h"
|
||
#include "ui_xygifframe.h"
|
||
#include <QPainter>
|
||
#include <QFileDialog>
|
||
#include <QApplication>
|
||
#include <QScreen>
|
||
#include <QResizeEvent>
|
||
#include <QDateTime>
|
||
#include <QMessageBox>
|
||
#include <QDebug>
|
||
#include <QMenu>
|
||
|
||
#ifdef Q_OS_WIN32
|
||
#include <windows.h>
|
||
|
||
static QImage getScreenImage(int x, int y, int width, int height)
|
||
{
|
||
HDC hScrDC = ::GetDC(nullptr);
|
||
HDC hMemDC = nullptr;
|
||
|
||
BYTE *lpBitmapBits = nullptr;
|
||
|
||
int nWidth = width;
|
||
int nHeight = height;
|
||
|
||
hMemDC = ::CreateCompatibleDC(hScrDC);
|
||
|
||
BITMAPINFO bi;
|
||
ZeroMemory(&bi, sizeof(BITMAPINFO));
|
||
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||
bi.bmiHeader.biWidth = nWidth;
|
||
bi.bmiHeader.biHeight = nHeight;
|
||
bi.bmiHeader.biPlanes = 1;
|
||
bi.bmiHeader.biBitCount = 24;
|
||
|
||
CURSORINFO hCur ;
|
||
ZeroMemory(&hCur,sizeof(hCur));
|
||
hCur.cbSize = sizeof(CURSORINFO);
|
||
GetCursorInfo(&hCur);
|
||
|
||
ICONINFO IconInfo = {};
|
||
if(GetIconInfo(hCur.hCursor, &IconInfo))
|
||
{
|
||
hCur.ptScreenPos.x -= IconInfo.xHotspot;
|
||
hCur.ptScreenPos.y -= IconInfo.yHotspot;
|
||
if(nullptr != IconInfo.hbmMask)
|
||
DeleteObject(IconInfo.hbmMask);
|
||
if(nullptr != IconInfo.hbmColor)
|
||
DeleteObject(IconInfo.hbmColor);
|
||
}
|
||
|
||
HBITMAP bitmap = ::CreateDIBSection(hMemDC, &bi, DIB_RGB_COLORS, (LPVOID*)&lpBitmapBits, nullptr, 0);
|
||
HGDIOBJ oldbmp = ::SelectObject(hMemDC, bitmap);
|
||
|
||
::BitBlt(hMemDC, 0, 0, nWidth, nHeight, hScrDC, x, y, SRCCOPY);
|
||
DrawIconEx(hMemDC, hCur.ptScreenPos.x - x, hCur.ptScreenPos.y - y, hCur.hCursor, 0, 0, 0, nullptr, DI_NORMAL | DI_COMPAT);
|
||
|
||
BITMAPFILEHEADER bh;
|
||
ZeroMemory(&bh, sizeof(BITMAPFILEHEADER));
|
||
bh.bfType = 0x4d42; //bitmap
|
||
bh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
|
||
bh.bfSize = bh.bfOffBits + ((nWidth*nHeight)*3);
|
||
|
||
int size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 3 * nWidth * nHeight;
|
||
uchar *bmp = new uchar[size];
|
||
uint offset = 0;
|
||
memcpy(bmp, (char *)&bh, sizeof(BITMAPFILEHEADER));
|
||
offset = sizeof(BITMAPFILEHEADER);
|
||
memcpy(bmp + offset, (char *)&(bi.bmiHeader), sizeof(BITMAPINFOHEADER));
|
||
offset += sizeof(BITMAPINFOHEADER);
|
||
memcpy(bmp + offset, (char *)lpBitmapBits, 3 * nWidth * nHeight);
|
||
|
||
::SelectObject(hMemDC, oldbmp);
|
||
::DeleteObject(bitmap);
|
||
::DeleteObject(hMemDC);
|
||
::ReleaseDC(nullptr, hScrDC);
|
||
QImage image = QImage::fromData(bmp, size);
|
||
delete[] bmp;
|
||
return image;
|
||
}
|
||
#else // linux x11
|
||
#include <X11/Xlib.h>
|
||
#include <X11/extensions/Xfixes.h>
|
||
static void X11drawCursor(Display *display, QImage &image, int recording_area_x, int recording_area_y)
|
||
{
|
||
// get the cursor
|
||
XFixesCursorImage *xcim = XFixesGetCursorImage(display);
|
||
if(xcim == NULL)
|
||
return;
|
||
|
||
// calculate the position of the cursor
|
||
int x = xcim->x - xcim->xhot - recording_area_x;
|
||
int y = xcim->y - xcim->yhot - recording_area_y;
|
||
|
||
// calculate the part of the cursor that's visible
|
||
int cursor_left = std::max(0, -x), cursor_right = std::min((int) xcim->width, image.width() - x);
|
||
int cursor_top = std::max(0, -y), cursor_bottom = std::min((int) xcim->height, image.height() - y);
|
||
|
||
unsigned int pixel_bytes, r_offset, g_offset, b_offset; // ARGB
|
||
pixel_bytes = 4;
|
||
r_offset = 2; g_offset = 1; b_offset = 0;
|
||
|
||
// draw the cursor
|
||
// XFixesCursorImage uses 'long' instead of 'int' to store the cursor images, which is a bit weird since
|
||
// 'long' is 64-bit on 64-bit systems and only 32 bits are actually used. The image uses premultiplied alpha.
|
||
for(int j = cursor_top; j < cursor_bottom; ++j) {
|
||
unsigned long *cursor_row = xcim->pixels + xcim->width * j;
|
||
uint8_t *image_row = (uint8_t*) image.bits() + image.bytesPerLine() * (y + j);
|
||
for(int i = cursor_left; i < cursor_right; ++i) {
|
||
unsigned long cursor_pixel = cursor_row[i];
|
||
uint8_t *image_pixel = image_row + pixel_bytes * (x + i);
|
||
int cursor_a = (uint8_t) (cursor_pixel >> 24);
|
||
int cursor_r = (uint8_t) (cursor_pixel >> 16);
|
||
int cursor_g = (uint8_t) (cursor_pixel >> 8);
|
||
int cursor_b = (uint8_t) (cursor_pixel >> 0);
|
||
if(cursor_a == 255) {
|
||
image_pixel[r_offset] = cursor_r;
|
||
image_pixel[g_offset] = cursor_g;
|
||
image_pixel[b_offset] = cursor_b;
|
||
} else {
|
||
image_pixel[r_offset] = (image_pixel[r_offset] * (255 - cursor_a) + 127) / 255 + cursor_r;
|
||
image_pixel[g_offset] = (image_pixel[g_offset] * (255 - cursor_a) + 127) / 255 + cursor_g;
|
||
image_pixel[b_offset] = (image_pixel[b_offset] * (255 - cursor_a) + 127) / 255 + cursor_b;
|
||
}
|
||
}
|
||
}
|
||
// free the cursor
|
||
XFree(xcim);
|
||
}
|
||
#endif
|
||
|
||
XYGifFrame::XYGifFrame(QWidget *parent)
|
||
: XYMovableWidget(parent),
|
||
mStartResize(false),
|
||
ui(new Ui::XYGifFrame)
|
||
{
|
||
ui->setupUi(this);
|
||
mGifCreator = new XYGifCreator(this);
|
||
mTimer.setSingleShot(false);
|
||
setWindowFlags( Qt::WindowStaysOnTopHint);
|
||
|
||
ui->width->installEventFilter(this);
|
||
ui->height->installEventFilter(this);
|
||
connect(ui->width, SIGNAL(editingFinished()), this, SLOT(doResize()));
|
||
connect(ui->height, SIGNAL(editingFinished()), this, SLOT(doResize()));
|
||
connect(ui->gif, SIGNAL(clicked()), this, SLOT(active()));
|
||
connect(ui->img, SIGNAL(clicked()), this, SLOT(packImages()));
|
||
connect(&mTimer, SIGNAL(timeout()), this, SLOT(frame()));
|
||
connect(ui->save, SIGNAL(clicked()), this, SLOT(setGifFile()));
|
||
connect(mGifCreator, &XYGifCreator::finished, this, [this](){
|
||
this->ui->tips->setText(QStringLiteral("保存Gif完成!"));
|
||
});
|
||
|
||
ui->content->adjustSize();
|
||
setMouseTracking(true);
|
||
setMinimumSize(162, 150);
|
||
ui->gif->setFocus();
|
||
m_tray = new QSystemTrayIcon(this);//实例化
|
||
QPixmap m_logo(":/gif.ico");
|
||
m_tray->setIcon(QIcon(m_logo));//设置图标
|
||
m_tray->show();
|
||
connect(m_tray,&QSystemTrayIcon::activated,this,&XYGifFrame::TrayIconAction);
|
||
m_menu = new QMenu(this);
|
||
m_resetAction = new QAction(this);
|
||
m_resetAction->setText("show");
|
||
m_quitAction = new QAction(this);
|
||
m_resetAction->setIcon(QIcon(m_logo));
|
||
m_quitAction->setText("quit");
|
||
m_quitAction->setIcon(QIcon(m_logo));
|
||
connect(m_quitAction,&QAction::triggered,qApp,&QApplication::quit);
|
||
connect(m_resetAction,&QAction::triggered,this,&XYGifFrame::restory);
|
||
// connect(m_closeDialog,&closeDialog::traySiganal,this,&XYGifFrame::Tray);
|
||
m_tray->setContextMenu(m_menu);//设置托盘菜单
|
||
m_menu->addAction(m_resetAction);
|
||
m_menu->addAction(m_quitAction);
|
||
|
||
}
|
||
|
||
XYGifFrame::~XYGifFrame()
|
||
{
|
||
delete ui;
|
||
}
|
||
|
||
void XYGifFrame::doResize()
|
||
{
|
||
QRect rect(pos(), QSize(ui->width->value(), ui->height->value()));
|
||
rect.adjust(-3, -3, 3, ui->content->height() + 5);
|
||
|
||
resize(rect.size());
|
||
}
|
||
|
||
void XYGifFrame::packImages()
|
||
{
|
||
XYPackImage img(this);
|
||
img.exec();
|
||
}
|
||
|
||
void XYGifFrame::setGifFile()
|
||
{
|
||
mGifFile = QFileDialog::getSaveFileName(this, "", QString("xy-%1.gif").arg(QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss")));
|
||
|
||
ui->save->setToolTip(mGifFile);
|
||
}
|
||
|
||
void XYGifFrame::active()
|
||
{
|
||
if (!mTimer.isActive())
|
||
{
|
||
if (mGifCreator->isRunning())
|
||
{
|
||
QMessageBox::warning(this, QStringLiteral("提醒"), QStringLiteral("正在保存Gif,请稍等!"));
|
||
return;
|
||
}
|
||
start();
|
||
}
|
||
else
|
||
{
|
||
stop();
|
||
}
|
||
}
|
||
|
||
void XYGifFrame::start()
|
||
{
|
||
if (!mGifFile.isEmpty())
|
||
{
|
||
mGifCreator->startThread();
|
||
|
||
mGifCreator->begin(mGifFile.toUtf8().data(), ui->width->value(), ui->height->value(), 1);
|
||
|
||
mPixs = 0;
|
||
ui->gif->setText(QStringLiteral("停止录制"));
|
||
|
||
frame();
|
||
mTimer.start(static_cast<int>(1000.0 / ui->interval->value()));
|
||
ui->width->setDisabled(true);
|
||
ui->height->setDisabled(true);
|
||
ui->interval->setDisabled(true);
|
||
}
|
||
else
|
||
{
|
||
QMessageBox::warning(this, QStringLiteral("提醒"), QStringLiteral("请先设置保存gif的位置!"));
|
||
}
|
||
}
|
||
|
||
void XYGifFrame::stop()
|
||
{
|
||
mTimer.stop();
|
||
ui->gif->setText(QStringLiteral("开始录制"));
|
||
mGifCreator->end();
|
||
|
||
ui->width->setEnabled(true);
|
||
ui->height->setEnabled(true);
|
||
ui->interval->setEnabled(true);
|
||
this->ui->tips->setText(QStringLiteral("请等待保存Gif..."));
|
||
}
|
||
|
||
void XYGifFrame::frame()
|
||
{
|
||
QImage img = getCurScreenImage();
|
||
if (!img.isNull())
|
||
{
|
||
mGifCreator->frame(img, ui->interval->value());
|
||
mPixs++;
|
||
|
||
ui->tips->setText(QStringLiteral("已保存 %1 张图片").arg(mPixs));
|
||
}
|
||
}
|
||
|
||
bool XYGifFrame::eventFilter(QObject *watched, QEvent *event)
|
||
{
|
||
if (watched == ui->width
|
||
|| watched == ui->height)
|
||
{
|
||
if (event->type() == QEvent::Wheel)
|
||
{
|
||
doResize();
|
||
}
|
||
}
|
||
return XYMovableWidget::eventFilter(watched, event);
|
||
}
|
||
|
||
void XYGifFrame::paintEvent(QPaintEvent *)
|
||
{
|
||
QPainter painter(this);
|
||
painter.fillRect(rect(), Qt::gray);
|
||
}
|
||
|
||
void XYGifFrame::resizeEvent(QResizeEvent *)
|
||
{
|
||
QRect rect = this->rect();
|
||
rect.adjust(3, 3, -3, -(ui->content->height() + 5));
|
||
mRecordRect = rect;
|
||
|
||
ui->width->setValue(mRecordRect.width());
|
||
ui->height->setValue(mRecordRect.height());
|
||
|
||
QRegion region(this->rect());
|
||
setMask(region.xored(mRecordRect));
|
||
qDebug()<<region.xored(mRecordRect);
|
||
ui->content->move(width() - ui->content->width() - 3, height() - ui->content->height() - 3);
|
||
}
|
||
|
||
void XYGifFrame::mousePressEvent(QMouseEvent *event)
|
||
{
|
||
QRect rect(QPoint(width() - 3, height() - 3), QSize(3, 3));
|
||
if (rect.contains(event->pos()) && !mTimer.isActive())
|
||
{
|
||
mStartResize = true;
|
||
mStartGeometry = QRect(event->globalPos(), size());
|
||
}
|
||
else
|
||
{
|
||
XYMovableWidget::mousePressEvent(event);
|
||
}
|
||
}
|
||
|
||
void XYGifFrame::mouseReleaseEvent(QMouseEvent *event)
|
||
{
|
||
mStartResize = false;
|
||
setCursor(Qt::ArrowCursor);
|
||
XYMovableWidget::mouseReleaseEvent(event);
|
||
}
|
||
|
||
void XYGifFrame::mouseMoveEvent(QMouseEvent *event)
|
||
{
|
||
QRect rect(QPoint(width() - 3, height() - 3), QSize(3, 3));
|
||
|
||
if (mStartResize)
|
||
{
|
||
QPoint ch = event->globalPos() - mStartGeometry.topLeft();
|
||
resize(mStartGeometry.size() + QSize(ch.x(), ch.y()));
|
||
}
|
||
else if (rect.contains(event->pos()) && !mTimer.isActive())
|
||
{
|
||
setCursor(Qt::SizeFDiagCursor);
|
||
}
|
||
else
|
||
{
|
||
setCursor(Qt::ArrowCursor);
|
||
XYMovableWidget::mouseMoveEvent(event);
|
||
}
|
||
}
|
||
|
||
void XYGifFrame::wheelEvent(QWheelEvent *event)
|
||
{
|
||
if (event->delta() < 0)
|
||
{
|
||
ui->content->move(ui->content->x() + 6, ui->content->y());
|
||
}
|
||
else
|
||
{
|
||
ui->content->move(ui->content->x() - 6, ui->content->y());
|
||
}
|
||
|
||
XYMovableWidget::wheelEvent(event);
|
||
}
|
||
|
||
QImage XYGifFrame::getCurScreenImage()
|
||
{
|
||
QImage img;
|
||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||
auto screen = qApp->screenAt(pos());
|
||
#else
|
||
QScreen *screen = nullptr;
|
||
foreach (screen, qApp->screens())
|
||
{
|
||
if (screen->geometry().contains(pos()))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
if (screen != nullptr)
|
||
{
|
||
#ifdef Q_OS_WIN32
|
||
img = getScreenImage(x() + mRecordRect.x(),
|
||
y() + mRecordRect.y(),
|
||
mRecordRect.width(),
|
||
mRecordRect.height());
|
||
#else
|
||
img = screen->grabWindow(0,
|
||
x() + mRecordRect.x(),
|
||
y() + mRecordRect.y(),
|
||
mRecordRect.width(),
|
||
mRecordRect.height()).toImage();
|
||
|
||
// 需要系统是x11后端
|
||
auto display = XOpenDisplay(NULL);
|
||
X11drawCursor(display, img, x() + mRecordRect.x(), y() + mRecordRect.y());
|
||
XCloseDisplay(display);
|
||
#endif
|
||
}
|
||
|
||
return img;
|
||
}
|
||
|
||
void XYGifFrame::on_quit_clicked()
|
||
{
|
||
this->hide();
|
||
}
|