Проблема 1)
Когда у QGraphicsView нет масштабирования, окно перемещается и изображение рисуется, оно мерцает, я не уверен, можно ли это назвать мерцанием, ореолы, ??, я не могу найти причину такого дрожания изображения:

Я попробовал несколько вещей, чтобы сделать захват как можно быстрее:
- Установите QTimer TimerType в Qt::PresizedTimer
- Установите интервал таймера на 0
- Использовал BitBlt в окне захвата Qt
- Кэшировал биты QImage в DIBSection (ensureCaptureSurface)
- Задайте для QGraphicsViewviewportUpdateMode значение SmartViewportUpdate
- Задайте для QGraphicsView RenderHint(QPainter::SmoothPixmapTransform, false);
Когда GrapchisView увеличен, я пытаюсь заставить мышь или окно двигаться медленнее, потому что пиксели большие, и при быстром движении мышь прыгает по пикселям, и вы не можете точно переместить ее над определенным пикселем:

Чтобы добиться этого, я возвращаю false в mouseProcHookCallback функция, которая заставляет перехватчик мыши в CALLBACK MouseProc возвращать 1; которые мешают продолжению ввода сообщения, затем я попытался манипулировать позицией курсора с помощью SendInput, как видно на рисунке выше, это также вызывает мерцание, возможно, мерцание из проблемы 1 плюс моя попытка изменить положение курсора.
Я также попробовал использовать WINAPI ClipCursor , это уменьшило мерцание, но движение курсора по-прежнему выглядит странно и не выглядит естественно.
Даже при тестировании В режиме выпуска и без регистрации обе проблемы сохраняются.
Как я могу остановить эти проблемы с мерцанием?
(Примечание: я пытался добавить сюда обе картинки, но ничего не вышло, может ли это исправить мод?)
Main.cpp
#include "ColorPicker.h"
static bool createColorPicker(WPARAM wParam, LPARAM lParam)
{
if (wParam == WM_KEYDOWN && ((KBDLLHOOKSTRUCT*)lParam)->vkCode == VK_F4)
new ColorPicker();
return true;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
qApp->setQuitOnLastWindowClosed(false);
setInputHooks();
setKeyboardHookCallback(new QObject(), createColorPicker);
new ColorPicker();
return app.exec();
}
ColorPicker.h
#pragma once
#include
inline HHOOK g_keyboardHook = NULL;
inline HHOOK g_mouseHook = NULL;
inline QMap g_keyboardHookCallbacks;
inline QMap g_mouseHookCallbacks;
inline LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode != HC_ACTION)
return CallNextHookEx(g_keyboardHook, nCode, wParam, lParam);
for (const auto& callback : g_keyboardHookCallbacks.values())
{
if (!callback(wParam, lParam))
return 1; // Block the event if any callback returns false
}
return CallNextHookEx(g_keyboardHook, nCode, wParam, lParam);
}
inline LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode != HC_ACTION)
return CallNextHookEx(g_mouseHook, nCode, wParam, lParam);
MSLLHOOKSTRUCT* mouseStruct = (MSLLHOOKSTRUCT*)lParam;
for (const auto& callback : g_mouseHookCallbacks.values())
{
if (!callback(wParam, lParam))
return 1; // Block the event if any callback returns false
}
return CallNextHookEx(g_mouseHook, nCode, wParam, lParam);
}
template
static void setKeyboardHookCallback(T* obj, bool(T::*callback)(WPARAM, LPARAM))
{
QObject::connect(obj, &QObject::destroyed, [obj]
{
g_keyboardHookCallbacks.remove(obj);
});
g_keyboardHookCallbacks[obj] = ([obj, callback](WPARAM wParam, LPARAM lParam) { return (obj->*callback)(wParam, lParam); });
}
template
static void setMouseHookCallback(T* obj, bool(T::*callback)(WPARAM, LPARAM))
{
QObject::connect(obj, &QObject::destroyed, [obj]
{
g_mouseHookCallbacks.remove(obj);
});
g_mouseHookCallbacks[obj] = ([obj, callback](WPARAM wParam, LPARAM lParam) { return (obj->*callback)(wParam, lParam); });
}
inline void setKeyboardHookCallback(QObject* obj, std::function callback)
{
QObject::connect(obj, &QObject::destroyed, [obj]
{
g_keyboardHookCallbacks.remove(obj);
});
g_keyboardHookCallbacks[obj] = callback;
}
inline void setInputHooks()
{
QSettings settings("HKEY_CURRENT_USER\\Control Panel\\Desktop", QSettings::NativeFormat);
settings.setValue("LowLevelHooksTimeout", 100);
settings.sync();
g_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0);
g_mouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, 0);
if (g_keyboardHook == NULL || g_mouseHook == NULL)
MessageBox(NULL, L"Failed to set input hook", L"Error", MB_ICONERROR);
}
class GridView : public QGraphicsView
{
public:
QGraphicsScene* m_scene = nullptr;
QPixmap m_pixmap; // Pixmap set on the pixmapItem
QGraphicsPixmapItem* m_pixmapItem;
QGraphicsItemGroup* m_gridGroup;
bool m_enabled = false;
bool m_gridEnabled = false;
QRect m_sceneRect;
QPoint m_lastMousePos;
qreal m_baseScale = 1.0;
qreal m_currentScale = 1.0;
GridView(QWidget* p = nullptr);
void setPixmap(const QPixmap& pixmap);
void setGraphicsViewEnabled();
void setGridEnabled();
void resetView();
void centerScene();
void updateGrid();
void wheelEvent(QWheelEvent* event) override;
};
class ColorPicker : public QWidget
{
Q_OBJECT
public:
const int GRID_WIDTH = 256;
const int GRID_HEIGHT = 256;
QTimer* m_captureScreenTimer = nullptr;
GridView* m_gridView = nullptr;
QPointF m_cursorAccum;
QPoint m_lastCursorPos;
QSize m_captureSize;
qreal m_captureDpr = 0.0;
HDC m_captureDC = nullptr;
HBITMAP m_captureBitmap = nullptr;
HGDIOBJ m_captureOldBitmap = nullptr;
QImage m_captureImage;
ColorPicker();
~ColorPicker();
void destroyCaptureSurface();
bool ensureCaptureSurface();
void lockCursor();
void unlockCursor();
void updateOverlayPosition(const QPoint& pt);
void captureScreenTimerTimeout();
bool keyboardProcHookCallback(WPARAM wParam, LPARAM lParam);
bool mouseProcHookCallback(WPARAM wParam, LPARAM lParam);
};
ColorPicker.cpp
#include "ColorPicker.h"
#include
#include
#include
static std::ofstream& getLog()
{
static std::ofstream log("D:\\colorpicker_debug.log", std::ios::trunc);
return log;
}
void logCaptureState(const QPoint& cursorPos, const QPoint& windowPos, const QSize& captureSize, qreal dpr)
{
static QPoint lastCursorPos(std::numeric_limits::min(), std::numeric_limits::min());
static QPoint lastWindowPos(std::numeric_limits::min(), std::numeric_limits::min());
static QSize lastCaptureSize;
static qreal lastDpr = 0.0;
if (cursorPos == lastCursorPos && windowPos == lastWindowPos && captureSize == lastCaptureSize && dpr == lastDpr)
return;
lastCursorPos = cursorPos;
lastWindowPos = windowPos;
lastCaptureSize = captureSize;
lastDpr = dpr;
auto& log = getLog();
log 0 || topLeft.y() > 0)
{
QTransform t;
t.translate(
topLeft.x() > 0 ? -topLeft.x() / m_currentScale : 0,
topLeft.y() > 0 ? -topLeft.y() / m_currentScale : 0
);
setTransform(t, true);
}
else
{
// If not, then check if perhaps the bottom right corner is.
QPointF bottomRight = mapFromScene(width(), height());
if (bottomRight.x() < width() || bottomRight.y() < height())
{
QTransform t;
t.translate(
bottomRight.x() < width() ? - (bottomRight.x() - width()) / m_currentScale : 0,
bottomRight.y() < height() ? -(bottomRight.y() - height()) / m_currentScale : 0
);
setTransform(t, true);
}
}
QPolygonF point = mapToScene(viewport()->rect());
if (!point.isEmpty())
m_sceneRect = QRect(point[0].toPoint(), point[2].toPoint());
}
void GridView::updateGrid()
{
if (!m_gridEnabled)
return;
// Clear existing grid
while (!m_gridGroup->childItems().isEmpty()) {
delete m_gridGroup->childItems().first();
}
// Get visible area in scene coordinates
QRectF visibleRect = mapToScene(viewport()->rect()).boundingRect();
QRectF imageRect = m_pixmapItem->boundingRect();
QRectF gridRect = visibleRect.intersected(imageRect);
// Calculate grid spacing in scene coordinates
qreal gridSpacing = 1.0; // One pixel in image coordinates
QPen gridPen(QColor(200, 200, 200, 255), 0); // Width 0 for single pixel line
// Calculate grid line positions
qreal startX = std::ceil(gridRect.left() / gridSpacing) * gridSpacing;
qreal startY = std::ceil(gridRect.top() / gridSpacing) * gridSpacing;
// Draw vertical lines
for (qreal x = startX; x setPen(gridPen);
m_gridGroup->addToGroup(line);
}
// Draw horizontal lines
for (qreal y = startY; y setPen(gridPen);
m_gridGroup->addToGroup(line);
}
}
void GridView::wheelEvent(QWheelEvent* event)
{
if (m_pixmapItem->pixmap().isNull() || !m_enabled)
return QGraphicsView::wheelEvent(event);
// Store cursor position relative to scene
QPointF mousePos = event->position(),
mousePosCurrent = mapToScene(mousePos.x(), mousePos.y());
// Calculate zoom factor
double factor = pow(1.5, event->angleDelta().y() / 240.0);
double newScale = m_currentScale * factor;
if (newScale < 1.) { // Do not allow zooming out (in absolute)
factor /= newScale;
newScale = 1.;
}
else if (newScale > 50.) { // Do not allow zoom > 50x
factor = factor * 50. / newScale;
newScale = 50.;
}
// Zoom centered on the mouse cursor
QTransform t;
t.translate(mousePosCurrent.x(), mousePosCurrent.y());
t.scale(factor, factor);
t.translate(-mousePosCurrent.x(), -mousePosCurrent.y());
setTransform(t, true);
m_currentScale = newScale;
centerScene();
// Update grid based on zoom level
if (m_gridEnabled)
{
m_gridGroup->setVisible(m_currentScale > 3.0);
if (m_currentScale > 3.0)
updateGrid();
}
event->accept();
}
ColorPicker::ColorPicker() : QWidget()
{
setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
setKeyboardHookCallback(this, &ColorPicker::keyboardProcHookCallback);
setMouseHookCallback(this, &ColorPicker::mouseProcHookCallback);
QWidget* widget = new QWidget(this);
widget->setFixedSize(GRID_WIDTH + 4, GRID_HEIGHT + 5);
widget->setStyleSheet("background-color: red;");
widget->move(0, 0);
m_gridView = new GridView(this);
m_gridView->setGraphicsViewEnabled();
m_gridView->setGridEnabled();
m_gridView->setFixedSize(GRID_WIDTH, GRID_HEIGHT);
m_gridView->move(2, 3);
m_captureScreenTimer = new QTimer(this);
m_captureScreenTimer->setTimerType(Qt::PreciseTimer);
m_captureScreenTimer->setInterval(0);
m_captureScreenTimer->start();
connect(m_captureScreenTimer, &QTimer::timeout, this, &ColorPicker::captureScreenTimerTimeout);
m_lastCursorPos = QCursor::pos();
HWND hwnd = (HWND)winId();
LONG exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_LAYERED | WS_EX_TRANSPARENT);
SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);
SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);
ensureCaptureSurface();
show();
}
ColorPicker::~ColorPicker()
{
if (m_captureScreenTimer != nullptr)
m_captureScreenTimer->stop();
destroyCaptureSurface();
unlockCursor();
}
void ColorPicker::destroyCaptureSurface()
{
m_captureImage = QImage();
if (m_captureDC != nullptr && m_captureOldBitmap != nullptr)
{
SelectObject(m_captureDC, m_captureOldBitmap);
m_captureOldBitmap = nullptr;
}
if (m_captureBitmap != nullptr)
{
DeleteObject(m_captureBitmap);
m_captureBitmap = nullptr;
}
if (m_captureDC != nullptr)
{
DeleteDC(m_captureDC);
m_captureDC = nullptr;
}
m_captureSize = QSize();
m_captureDpr = 0.0;
}
bool ColorPicker::ensureCaptureSurface()
{
qreal dpr = m_gridView->devicePixelRatioF();
QSize captureSize(qRound(256 * dpr), qRound(256 * dpr));
if (captureSize.isEmpty())
return false;
if (m_captureBitmap != nullptr && captureSize == m_captureSize && dpr == m_captureDpr)
return true;
destroyCaptureSurface();
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = captureSize.width();
bmi.bmiHeader.biHeight = -captureSize.height();
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
void* dibBits = nullptr;
m_captureBitmap = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &dibBits, nullptr, 0);
if (m_captureBitmap == nullptr || dibBits == nullptr)
{
destroyCaptureSurface();
return false;
}
m_captureDC = CreateCompatibleDC(nullptr);
if (m_captureDC == nullptr)
{
destroyCaptureSurface();
return false;
}
m_captureOldBitmap = SelectObject(m_captureDC, m_captureBitmap);
m_captureImage = QImage(static_cast(dibBits), captureSize.width(), captureSize.height(), captureSize.width() * 4, QImage::Format_ARGB32);
m_captureImage.setDevicePixelRatio(dpr);
m_captureSize = captureSize;
m_captureDpr = dpr;
return !m_captureImage.isNull();
}
void ColorPicker::lockCursor()
{
POINT pt;
GetCursorPos(&pt);
RECT rc = { pt.x, pt.y, pt.x + 1, pt.y + 1 };
ClipCursor(&rc);
}
void ColorPicker::unlockCursor()
{
ClipCursor(NULL);
}
void ColorPicker::updateOverlayPosition(const QPoint& cursorPos)
{
QPoint newPos = QPoint(cursorPos.x() - (size().width() / 2) + 9,
cursorPos.y() - (size().height() / 2));
if (pos() != newPos)
move(newPos);
}
bool ColorPicker::keyboardProcHookCallback(WPARAM wParam, LPARAM lParam)
{
KBDLLHOOKSTRUCT* kbStruct = (KBDLLHOOKSTRUCT*)lParam;
if (wParam == WM_KEYDOWN && kbStruct->vkCode == VK_ESCAPE)
{
close();
deleteLater();
return false;
}
return true;
}
bool ColorPicker::mouseProcHookCallback(WPARAM wParam, LPARAM lParam)
{
MSLLHOOKSTRUCT* mouseStruct = (MSLLHOOKSTRUCT*)lParam;
short delta = HIWORD(mouseStruct->mouseData);
QPoint pt(mouseStruct->pt.x, mouseStruct->pt.y);
if (wParam == WM_MOUSEMOVE)
{
auto& log = getLog();
qreal scale = m_gridView->m_currentScale;
if (scale > 1.0)
{
// Accept our own injected moves
if (mouseStruct->flags & LLMHF_INJECTED)
{
log
Подробнее здесь: https://stackoverflow.com/questions/799 ... aphicsview
Мобильная версия