mirror of
https://github.com/yuanyuanxiang/SimpleRemoter.git
synced 2026-02-19 04:43:09 +08:00
214 lines
7.2 KiB
C++
214 lines
7.2 KiB
C++
// ScrollDetector.h: Scroll detection for screen capture optimization
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef SCROLL_DETECTOR_H
|
|
#define SCROLL_DETECTOR_H
|
|
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
#include "../common/commands.h"
|
|
|
|
// Scroll detection parameters
|
|
#define MIN_SCROLL_LINES 16 // Minimum scroll lines to detect (increased to reduce noise)
|
|
#define MAX_SCROLL_RATIO 4 // Maximum scroll = height / MAX_SCROLL_RATIO
|
|
#define MATCH_THRESHOLD 85 // Percentage of rows that must match (increased for stability)
|
|
|
|
// Horizontal scroll direction constants (for future use)
|
|
#define SCROLL_DIR_LEFT 2
|
|
#define SCROLL_DIR_RIGHT 3
|
|
|
|
// CRC32 lookup table for row hashing
|
|
static uint32_t crc32_table[256] = { 0 };
|
|
static bool crc32_table_initialized = false;
|
|
|
|
inline void InitCRC32Table()
|
|
{
|
|
if (crc32_table_initialized) return;
|
|
|
|
for (uint32_t i = 0; i < 256; i++) {
|
|
uint32_t crc = i;
|
|
for (int j = 0; j < 8; j++) {
|
|
crc = (crc >> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
|
|
}
|
|
crc32_table[i] = crc;
|
|
}
|
|
crc32_table_initialized = true;
|
|
}
|
|
|
|
inline uint32_t ComputeCRC32(const uint8_t* data, size_t length)
|
|
{
|
|
uint32_t crc = 0xFFFFFFFF;
|
|
for (size_t i = 0; i < length; i++) {
|
|
crc = (crc >> 8) ^ crc32_table[(crc ^ data[i]) & 0xFF];
|
|
}
|
|
return crc ^ 0xFFFFFFFF;
|
|
}
|
|
|
|
class CScrollDetector
|
|
{
|
|
private:
|
|
uint32_t* m_prevRowHashes; // Hash for each row of previous frame
|
|
uint32_t* m_currRowHashes; // Hash for each row of current frame
|
|
int m_width; // Frame width in pixels
|
|
int m_height; // Frame height in pixels
|
|
int m_bytesPerPixel; // Bytes per pixel (typically 4 for BGRA)
|
|
int m_stride; // Bytes per row
|
|
int m_lastScrollAmount; // Last detected scroll amount
|
|
|
|
public:
|
|
CScrollDetector(int width, int height, int bpp = 4)
|
|
: m_width(width), m_height(height), m_bytesPerPixel(bpp)
|
|
{
|
|
InitCRC32Table();
|
|
m_stride = width * bpp;
|
|
m_prevRowHashes = new uint32_t[height];
|
|
m_currRowHashes = new uint32_t[height];
|
|
m_lastScrollAmount = 0;
|
|
memset(m_prevRowHashes, 0, height * sizeof(uint32_t));
|
|
memset(m_currRowHashes, 0, height * sizeof(uint32_t));
|
|
}
|
|
|
|
~CScrollDetector()
|
|
{
|
|
if (m_prevRowHashes) {
|
|
delete[] m_prevRowHashes;
|
|
m_prevRowHashes = nullptr;
|
|
}
|
|
if (m_currRowHashes) {
|
|
delete[] m_currRowHashes;
|
|
m_currRowHashes = nullptr;
|
|
}
|
|
}
|
|
|
|
// Compute hash for each row of a frame
|
|
void ComputeRowHashes(const uint8_t* frame, uint32_t* hashes)
|
|
{
|
|
for (int row = 0; row < m_height; row++) {
|
|
hashes[row] = ComputeCRC32(frame + row * m_stride, m_stride);
|
|
}
|
|
}
|
|
|
|
// Detect vertical scroll between previous and current frame
|
|
// Returns scroll amount in pixels (positive = scroll down, negative = scroll up)
|
|
// Returns 0 if no scroll detected
|
|
int DetectVerticalScroll(const uint8_t* prevFrame, const uint8_t* currFrame)
|
|
{
|
|
// Compute hashes for current frame
|
|
ComputeRowHashes(currFrame, m_currRowHashes);
|
|
|
|
int maxScroll = m_height / MAX_SCROLL_RATIO;
|
|
int bestScrollAmount = 0;
|
|
int bestMatchCount = 0;
|
|
|
|
// Try different scroll amounts
|
|
for (int scrollAmount = MIN_SCROLL_LINES; scrollAmount <= maxScroll; scrollAmount++) {
|
|
// Check scroll down (content moves up, new content at bottom)
|
|
int matchCount = CountMatchingRows(scrollAmount);
|
|
if (matchCount > bestMatchCount) {
|
|
bestMatchCount = matchCount;
|
|
bestScrollAmount = scrollAmount;
|
|
}
|
|
|
|
// Check scroll up (content moves down, new content at top)
|
|
matchCount = CountMatchingRows(-scrollAmount);
|
|
if (matchCount > bestMatchCount) {
|
|
bestMatchCount = matchCount;
|
|
bestScrollAmount = -scrollAmount;
|
|
}
|
|
}
|
|
|
|
// Check if match quality is good enough
|
|
int comparedRows = m_height - abs(bestScrollAmount);
|
|
int threshold = (comparedRows * MATCH_THRESHOLD) / 100;
|
|
|
|
if (bestMatchCount >= threshold && bestMatchCount >= MIN_SCROLL_LINES) {
|
|
m_lastScrollAmount = bestScrollAmount;
|
|
// Swap hash buffers for next frame
|
|
std::swap(m_prevRowHashes, m_currRowHashes);
|
|
return bestScrollAmount;
|
|
}
|
|
|
|
// No scroll detected, update previous hashes
|
|
std::swap(m_prevRowHashes, m_currRowHashes);
|
|
m_lastScrollAmount = 0;
|
|
return 0;
|
|
}
|
|
|
|
// Update previous frame hashes (call after sending diff frame)
|
|
void UpdatePrevHashes(const uint8_t* frame)
|
|
{
|
|
ComputeRowHashes(frame, m_prevRowHashes);
|
|
}
|
|
|
|
// Get edge region info for scroll frame
|
|
// BMP is bottom-up: row 0 = screen bottom, row height-1 = screen top
|
|
// scrollAmount > 0: scroll down (content moves up), new content at screen bottom = BMP row 0
|
|
// scrollAmount < 0: scroll up (content moves down), new content at screen top = BMP high rows
|
|
void GetEdgeRegion(int scrollAmount, int* outOffset, int* outPixelCount) const
|
|
{
|
|
if (scrollAmount > 0) {
|
|
// Scroll down: new content at screen bottom = BMP row 0 (low address)
|
|
*outOffset = 0;
|
|
*outPixelCount = scrollAmount * m_width;
|
|
} else {
|
|
// Scroll up: new content at screen top = BMP high rows
|
|
*outOffset = (m_height + scrollAmount) * m_stride;
|
|
*outPixelCount = (-scrollAmount) * m_width;
|
|
}
|
|
}
|
|
|
|
int GetLastScrollAmount() const
|
|
{
|
|
return m_lastScrollAmount;
|
|
}
|
|
int GetWidth() const
|
|
{
|
|
return m_width;
|
|
}
|
|
int GetHeight() const
|
|
{
|
|
return m_height;
|
|
}
|
|
int GetStride() const
|
|
{
|
|
return m_stride;
|
|
}
|
|
|
|
private:
|
|
// Count matching rows for a given scroll amount
|
|
// BMP is bottom-up: row 0 = screen bottom, row height-1 = screen top
|
|
// scrollAmount > 0: scroll down (content moves up in screen)
|
|
// - In BMP: new content at row 0, old content shifted to higher rows
|
|
// - curr[scrollAmount + i] should match prev[i]
|
|
// scrollAmount < 0: scroll up (content moves down in screen)
|
|
// - In BMP: new content at high rows, old content shifted to lower rows
|
|
// - curr[i] should match prev[i + absScroll]
|
|
int CountMatchingRows(int scrollAmount) const
|
|
{
|
|
int matchCount = 0;
|
|
int absScroll = abs(scrollAmount);
|
|
|
|
if (scrollAmount > 0) {
|
|
// Scroll down: curr[scrollAmount..height-1] should match prev[0..height-scrollAmount-1]
|
|
for (int i = 0; i < m_height - absScroll; i++) {
|
|
if (m_currRowHashes[i + absScroll] == m_prevRowHashes[i]) {
|
|
matchCount++;
|
|
}
|
|
}
|
|
} else {
|
|
// Scroll up: curr[0..height-scrollAmount-1] should match prev[scrollAmount..height-1]
|
|
for (int i = 0; i < m_height - absScroll; i++) {
|
|
if (m_currRowHashes[i] == m_prevRowHashes[i + absScroll]) {
|
|
matchCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return matchCount;
|
|
}
|
|
};
|
|
|
|
#endif // SCROLL_DETECTOR_H
|