capture_gif/xygifframe.cpp

402 lines
12 KiB
C++
Raw Normal View History

2021-09-13 06:08:34 +00:00
#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();
}