修复缩放视界下拖拽与改色卡顿、加载字节进度、光标越界三角与中心轴均值滤波统一
1. 修复选区放大后的拖拽/改色卡顿(先实测再修,不猜)
- 用离屏探针量化:缩放视界下一帧要画约 250 条网格线,GDI+ DrawLine 每条 0.1~0.19ms,
整帧 26.4ms;而包络带填充本身只要 0.36ms,真正的瓶颈是逐条直线调用。
- 网格线、时间轴刻度、绘图区边框、网格分隔线全部改为 GDI 批量 1px 直线绘制
(一次 SaveDC/IntersectClipRect/创建画笔,逐段 MoveToEx/LineTo);
纵轴数值胶囊改为把所有圆角矩形合并成一个路径、只调用一次填充。
- 半透明网格线颜色先用底色预混成不透明色,否则 GDI 无法绘制透明度;
GDI 绘制不受 GDI+ 裁剪约束,批量绘制一律显式设置裁剪矩形。
- 实测:缩放视界下场景缓存失效帧 26.4 -> 8.25ms,改色应用 7.1 -> 3.15ms,
缓存命中帧 2.06 -> 0.30ms。
2. 读取文件时显示"已读取 / 总大小 百分比"
- 解析进度回调扩展为携带真实字节数(已读字节、文件总字节);
列重组等没有字节可计的阶段只显示百分比。
- 新增字节数格式化:单位在 GB / MB / KB 之间自适应,只输出数字与单位;
已读大小随进度自动切换单位。
- 进度浮层按"已读取 / 文件总大小 百分比"显示,两个大小与百分比都保留两位小数,
完全取代原来的整数百分比。
3. 网格编号拖拽重排的绘制修复
- GX 编号悬停时保持普通箭头光标(不再变成四向箭头),按下拖拽时才使用上下箭头。
- 拖拽时先把整幅静态场景(含主时间轴)贴回,再把目标槽位高亮、被拖网格的轮廓与
投影裁剪在绘图区内,因此拖到工具栏甚至更上方、或拖到时间轴及下方都不再出现
残留与错乱。
4. 网格 Y 轴数值只在完全移出可视区时才不绘制
- 原来只要碰到边界就整条不画;现在按可见范围裁剪胶囊与数值,
部分可见时绘制未被遮挡的部分。
5. 边沿计数横向光标的读数与越界指示
- 幅值读数右移,让开横线左端的三角把手,不再遮挡。
- 幅值超出视界时(横线贴边显示)在读数上方/下方绘制方向三角,
并且与读数水平居中对齐。
6. 常规光标读数越界与双光标叠放
- 光标滞留在视界之外时,时间读数同样绘制方向三角。
- 双光标读数重叠时按时间值排序:小值在上、大值在下(原来固定把第二个上移,
左右光标交换后会显示成大的在上)。
- 双光标同时越界到同一侧时,两条时间读数与时间差值读数在同一列垂直对齐,
并为差值读数绘制方向三角;叠放间距按三角深度放宽,三角不会再压在相邻读数上。
7. 中心轴边沿计数与均值滤波统一
- 参数由单一的"平滑点数 N"改为按信号的"均值点数 X + 阶数 N",与实验性功能中的
均值滤波完全同构(同样的命名、同样的作用、同样的曲线实现),
两条曲线逐位一致,光标处的取值标注规则也统一。
- 浮动面板改为自上而下的逐信号条目,每个条目三排:
第一排信号名与复选框,第二排均值点数与阶数输入框,第三排该信号的上升沿/下降沿统计;
取消勾选即不显示该信号的参数编辑与统计结果,启用时默认只勾选第一个可见信号。
- 网格重建继承、拆分网格、信号插入/删除的下标修正与场景缓存指纹都同步覆盖新条目。
测试:离屏自动验证套件新增一组检查(字节数格式化、解析进度字节、编号悬停/拖拽光标、
双光标读数排序与对齐、中心轴三排布局与参数、横向光标越界三角、加载浮层渲染等),
合计 241 项检查全部通过;验证过程不显示窗口、不移动鼠标键盘、不抓屏。
This commit is contained in:
1 parent
bc8db72f18
commit
8801c8f019
12 files changed
+951
-420
No files matched your search
@@ -35,13 +35,26 @@ namespace jdi
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 加载进度信息。
|
||||
*
|
||||
* 解析阶段会携带真实的"已读 / 总字节数";列重组与统计阶段没有字节可计,
|
||||
* 此时 `hasBytes` 为 false,界面应只展示百分比。
|
||||
*/
|
||||
struct CsvProgressInfo
|
||||
{
|
||||
double ratio = 0.0; /**< 0-1 总体完成比例。 */
|
||||
unsigned long long bytesRead = 0; /**< 已读取字节数。 */
|
||||
unsigned long long totalBytes = 0; /**< 文件总字节数。 */
|
||||
bool hasBytes = false; /**< 上述字节数字段是否有效。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 解析进度回调。
|
||||
*
|
||||
* 在解析过程中按块回调,ratio 为 0.0-1.0 的完成比例;
|
||||
* 返回 false 表示请求取消,解析会尽快停止并返回失败。
|
||||
* 在解析过程中按块回调;返回 false 表示请求取消,解析会尽快停止并返回失败。
|
||||
*/
|
||||
using CsvProgress = std::function<bool(double ratio)>;
|
||||
using CsvProgress = std::function<bool(const CsvProgressInfo& info)>;
|
||||
|
||||
/**
|
||||
* @brief 读取并解析一个 CSV 文件。
|
||||
|
||||
@@ -360,6 +360,8 @@ namespace jdi
|
||||
*
|
||||
* 仅参与列表中的信号才会被勾选预览;参数按信号保存,
|
||||
* 因此同一条网格内不同信号可以使用不同的均值点数与阶数。
|
||||
* 「边沿计数(中心轴)」的平滑参数与它**完全同构**(同一套命名与作用),
|
||||
* 见 `EdgeCenterItem`。
|
||||
*/
|
||||
struct MeanFilterItem
|
||||
{
|
||||
@@ -368,6 +370,9 @@ namespace jdi
|
||||
int order = 1; /**< 阶数 N(重复滤波次数,至少 1)。 */
|
||||
};
|
||||
|
||||
/** 中心轴边沿计数的平滑参数:与均值滤波共用同一结构与同一套语义。 */
|
||||
using EdgeCenterItem = MeanFilterItem;
|
||||
|
||||
std::wstring name; /**< 网格名称。 */
|
||||
std::vector<SignalRef> signals; /**< 该网格包含的信号(按加入顺序)。 */
|
||||
bool autoRange = true; /**< 是否依据信号取值范围自动计算 Y 轴。 */
|
||||
@@ -408,14 +413,34 @@ namespace jdi
|
||||
/**
|
||||
* @brief 「实验性功能 → 边沿计数(中心轴)」开关。
|
||||
*
|
||||
* 开启后为本网格内每条信号额外绘制一条"中心平均值曲线"(每个数据点取自身与左右各
|
||||
* `edgeCenterSmooth` 个点的平均值),并按“信号穿越中心平均值曲线”统计上/下边沿。
|
||||
* 开启后为本网格内**被勾选**的信号额外绘制一条均值滤波曲线(与「实验性功能 → 均值滤波」
|
||||
* 用的是同一套参数:均值点数 X + 阶数 N),并按“信号穿越该滤波曲线”统计上/下边沿。
|
||||
* 这条曲线只是叠加显示,不会作为信号加入左侧列表。
|
||||
*/
|
||||
bool edgeCenter = false;
|
||||
|
||||
/** 中心平均值的平滑半径 N(左右各取 N 个数据点,窗口大小 2N+1)。 */
|
||||
int edgeCenterSmooth = 5;
|
||||
/**
|
||||
* @brief 参与中心轴边沿计数的信号及其平滑参数(只包含被勾选的信号)。
|
||||
*
|
||||
* 参数语义与 `meanFilterItems` **完全一致**(`points` = 均值点数 X,
|
||||
* `order` = 阶数 N),因此两条曲线的数值逐位相同。
|
||||
*/
|
||||
std::vector<EdgeCenterItem> edgeCenterItems;
|
||||
|
||||
/** 取某个信号的中心轴平滑参数(未勾选时返回空指针)。 */
|
||||
const EdgeCenterItem* FindEdgeCenterItem(const SignalRef& ref) const;
|
||||
|
||||
/** 取得或创建某信号的中心轴平滑参数(用于勾选)。 */
|
||||
EdgeCenterItem& EnsureEdgeCenterItem(const SignalRef& ref);
|
||||
|
||||
/** 取消某信号的中心轴边沿计数勾选。 */
|
||||
void RemoveEdgeCenterItem(const SignalRef& ref);
|
||||
|
||||
/** 取某信号的中心轴边沿计数勾选状态。 */
|
||||
bool EdgeCenterEnabled(const SignalRef& ref) const
|
||||
{
|
||||
return FindEdgeCenterItem(ref) != nullptr;
|
||||
}
|
||||
|
||||
/** 中心轴统计浮动面板是否展开。 */
|
||||
bool edgeCenterPanelOpen = true;
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
#include <objidl.h>
|
||||
#include <gdiplus.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "TextEdit.hpp"
|
||||
#include "Theme.hpp"
|
||||
@@ -224,6 +226,50 @@ namespace jdi
|
||||
*/
|
||||
void UiDrawPlusIcon(gd::Graphics& g, const gd::RectF& rect, std::uint32_t argb, float strokeWidth);
|
||||
|
||||
/**
|
||||
* @brief 用 GDI 一次性绘制若干条**不透明 1px 实线**(每条线段相互独立)。
|
||||
*
|
||||
* 性能关键:GDI+ 的 `DrawLine` 实测每条约 0.1–0.19 ms(带抗锯齿的长线更慢),
|
||||
* 而一张网格通常要画几十条网格线 —— 逐条调用会让单帧多花十几毫秒。
|
||||
* 这里改用 GDI(一次取 HDC + 一支画笔 + 多个 LineTo),快一两个数量级,
|
||||
* 而且像素级网格线本来就不需要抗锯齿,观感反而更清晰。
|
||||
*
|
||||
* ⚠️ GDI 绘制**不受 GDI+ 裁剪区约束**,因此必须显式传入裁剪矩形
|
||||
* (内部用 `SaveDC` + `IntersectClipRect` 保证不越界)。
|
||||
* ⚠️ 只能用于**不透明**颜色(GDI 不做 alpha 混合);半透明色需调用方先与底色混合。
|
||||
*
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
* @param [IN] clip 裁剪矩形(客户区坐标)。
|
||||
* @param [IN] segments 线段数组,每 4 个 float 为一条(x1,y1,x2,y2)。
|
||||
* @param [IN] segmentCount 线段数量。
|
||||
* @param [IN] argb 颜色(Alpha 必须为 0xFF,否则不做任何绘制)。
|
||||
* @param [IN] width 线宽(像素,>= 1;仅精确支持 1,更宽时按整数宽画)。
|
||||
*/
|
||||
void UiDrawSolidLines(gd::Graphics& g, const gd::RectF& clip, const float* segments,
|
||||
std::size_t segmentCount, std::uint32_t argb, int width = 1);
|
||||
|
||||
/**
|
||||
* @brief 把半透明颜色与已知不透明底色混合,得到可直接用于 GDI 的不透明颜色。
|
||||
* @param [IN] background 不透明底色(0xFFRRGGBB)。
|
||||
* @param [IN] argb 待叠加颜色(含 alpha)。
|
||||
* @return 混合后的不透明颜色。
|
||||
*/
|
||||
std::uint32_t UiBlendOver(std::uint32_t background, std::uint32_t argb);
|
||||
|
||||
/**
|
||||
* @brief 一次性填充多个圆角矩形(同色同圆角)。
|
||||
*
|
||||
* 每帧要画几十个刻度数值底板时,逐个调用 `UiFillRoundRect` 会带来可观的 GDI+ 调用开销;
|
||||
* 合并成一条 GraphicsPath 后只 FillPath 一次(实测约快 3 倍)。
|
||||
*
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
* @param [IN] rects 矩形集合。
|
||||
* @param [IN] radius 圆角半径。
|
||||
* @param [IN] argb 填充色。
|
||||
*/
|
||||
void UiFillRoundRects(gd::Graphics& g, const std::vector<gd::RectF>& rects, float radius,
|
||||
std::uint32_t argb);
|
||||
|
||||
/**
|
||||
* @brief 在矩形中心绘制一个叉号(关闭)图标。
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
|
||||
@@ -64,6 +64,17 @@ namespace jdi
|
||||
*/
|
||||
std::wstring FormatNumber(double value, int decimals);
|
||||
|
||||
/**
|
||||
* @brief 把字节数格式化为自适应单位的文本(GB / MB / KB),保留两位小数。
|
||||
*
|
||||
* 单位阀值:1 GB = 1024 MB、1 MB = 1024 KB;小于 1 KB 时也按 KB 显示。
|
||||
* 只输出"数字 + 单位"(如 "1.69 GB"),便于拼接为"已读取 / 总量"。
|
||||
*
|
||||
* @param [IN] bytes 字节数。
|
||||
* @return 形如 "812.34 MB" 的文本。
|
||||
*/
|
||||
std::wstring FormatByteSize(unsigned long long bytes);
|
||||
|
||||
/**
|
||||
* @brief 以自适应有效位格式化数值(NaN/Inf 会被转换为可读文本)。
|
||||
* @param [IN] value 待格式化数值。
|
||||
|
||||
Reference in new issue
Block a user