把宽缓冲区推广为基底复用:拖弱波增强参数滑块降为 O(1)(55ms 到 5ms)
抽出 PaintLaneDerivedOverlays;宽缓冲只存基底(网格/波形/时间轴,跳过派生曲线与图例/Y轴刻度);基底校验键改为 SceneContentHash(true,false)(含外观样式、排除派生参数),因此拖弱波增强等级/迭代滑块不会让基底失效,可复用基底像素 + 只重画派生曲线叠加层。判定为结构未变且外观样式未变(仅视图或派生参数变化)时启用;外观样式变化仍走样式补丁。 实测 VERY_BIG_DATA 全信号、视界 [40.1756,40.1885]s、5 网格:弱波增强滑块 55ms 到 5.2ms,平移 1.3ms、透明度/粗细/颜色 8ms 不变;静止/结算帧与 HEAD 逐像素 0 差异(LockBits)。
This commit is contained in:
1 parent
fa0d62b016
commit
b04167a9d6
3 files changed
+126
-71
No files matched your search
@@ -1076,6 +1076,18 @@ namespace jdi
|
||||
void PaintLane(gd::Graphics& g, int laneIndex, const LaneVisual& lane,
|
||||
const std::vector<double>& timeTicks);
|
||||
|
||||
/**
|
||||
* @brief 绘制某网格的全部**派生预览曲线**(均值滤波 / 中心轴 / 弱波增强),叠加在原信号之上。
|
||||
*
|
||||
* 从 `PaintLane` 抽出,供「宽缓冲区」前台路径复用:宽缓冲只存"基底"(网格/波形),
|
||||
* 派生曲线每帧作为叠加层单独绘制,因此拖派生参数滑块时无需重画波形。
|
||||
*
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
* @param [IN] laneIndex 网格下标。
|
||||
* @param [IN] lane 网格绘制信息。
|
||||
*/
|
||||
void PaintLaneDerivedOverlays(gd::Graphics& g, int laneIndex, const LaneVisual& lane) const;
|
||||
|
||||
/**
|
||||
* @brief 绘制底部时间轴。
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
@@ -1933,6 +1945,13 @@ namespace jdi
|
||||
float opacity = 1.0f; /**< 不透明度。 */
|
||||
float lineWidth = 1.0f; /**< 线宽。 */
|
||||
int mode = 0; /**< 绘制模式。 */
|
||||
|
||||
/** @brief 相等比较(用于判断"逐信号外观样式是否变化")。 */
|
||||
bool operator==(const SceneStyleEntry& other) const
|
||||
{
|
||||
return color == other.color && opacity == other.opacity &&
|
||||
lineWidth == other.lineWidth && mode == other.mode;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -5562,23 +5562,34 @@ namespace jdi
|
||||
}
|
||||
};
|
||||
|
||||
// 视界平移(左键/中键拖动):画面内容不变、只是横向平移。直接复用「宽缓冲区」
|
||||
// 已渲染的像素,截取子矩形贴图 —— 前台 O(1)(一次 BitBlt),不重画波形,
|
||||
// 因此无论放大到多深的区间,平移都与全局显示时一样流畅。
|
||||
if (m_fastFrame && m_dragMode == PlotDragMode::PAN && m_decorValid && HasDrawableContent())
|
||||
// 视界平移 / 派生预览曲线参数变化:画面**基底**(网格/波形)不变,只是横向平移或
|
||||
// 叠加的派生曲线变化。直接复用「宽缓冲区」已渲染的**基底**像素,只把派生曲线作为
|
||||
// 叠加层重画 —— 前台 O(1),不重画波形,因此无论放大到多深都保持跟手。
|
||||
// 判定:网格结构未变(几何指纹一致)且**逐信号外观样式未变** → 属视图/派生变化,可用宽基底;
|
||||
// 外观样式(颜色/粗细/透明度/模式)变化 → 交给下方"样式补丁"分支局部重画。
|
||||
bool wideUsable = false;
|
||||
if (m_fastFrame && m_decorValid && HasDrawableContent() && m_sceneValid &&
|
||||
m_sceneGeometryHash == SceneGeometryHash())
|
||||
{
|
||||
std::vector<std::vector<SceneStyleEntry>> currentStyles;
|
||||
CollectSceneStyles(currentStyles);
|
||||
wideUsable = (currentStyles == m_sceneStyles);
|
||||
}
|
||||
if (wideUsable)
|
||||
{
|
||||
bool blitted = BlitWideBuffer(g);
|
||||
if (!blitted)
|
||||
if (!blitted && RebuildWideBuffer())
|
||||
{
|
||||
// 视界移出了宽缓冲范围,或内容/跨度变化 → 以当前视界为中心重建一次。
|
||||
if (RebuildWideBuffer())
|
||||
{
|
||||
blitted = BlitWideBuffer(g);
|
||||
}
|
||||
blitted = BlitWideBuffer(g);
|
||||
}
|
||||
if (blitted)
|
||||
{
|
||||
// 宽缓冲不含图例与 Y 轴刻度(二者锚定网格左缘、随视界移动),由装饰缓存贴回。
|
||||
// 宽缓冲只含基底:派生预览曲线作为叠加层重画(很轻)。
|
||||
for (std::size_t i = 0; i < m_lanes.size(); ++i)
|
||||
{
|
||||
PaintLaneDerivedOverlays(g, static_cast<int>(i), m_lanes[i]);
|
||||
}
|
||||
// 图例与 Y 轴刻度(锚定网格左缘、随视界移动)由装饰缓存贴回。
|
||||
BlitDecorCache(g);
|
||||
paintOverlays();
|
||||
return;
|
||||
@@ -6786,8 +6797,10 @@ namespace jdi
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 内容(外观样式 / 派生曲线参数 / 网格结构 / 视界几何)变化 → 需重建。
|
||||
return (m_wideKey == SceneContentHash(true));
|
||||
// 内容(外观样式 / 网格结构 / 视界几何)变化 → 需重建。
|
||||
// ⚠️ **不含派生曲线参数**:宽缓冲只存基底(网格 / 波形),派生曲线是每帧叠加的,
|
||||
// 因此拖弱波增强 / 均值滤波参数滑块不会让基底失效,可直接复用基底像素。
|
||||
return (m_wideKey == SceneContentHash(true, false));
|
||||
}
|
||||
|
||||
bool PlotView::RebuildWideBuffer()
|
||||
@@ -6870,7 +6883,7 @@ namespace jdi
|
||||
|
||||
m_wideMin = wideMin;
|
||||
m_wideSpan = span;
|
||||
m_wideKey = SceneContentHash(true);
|
||||
m_wideKey = SceneContentHash(true, false);
|
||||
m_wideValid = true;
|
||||
return true;
|
||||
}
|
||||
@@ -7152,15 +7165,8 @@ namespace jdi
|
||||
|
||||
// 只有拖拽中的信号光晕可以进入静态场景缓存(悬停光晕见 PaintHoverHighlight)。
|
||||
const SignalRef haloRef = SceneHaloRef();
|
||||
// 「边沿计数(中心轴)」开启时,为**被勾选**的信号各叠加一条均值滤波曲线
|
||||
//(参数 = 该信号自己的条目:均值点数 X + 迭代次数 N,与「均值滤波」完全同一套语义)。
|
||||
const bool centerOn = Tab().Lanes()[static_cast<std::size_t>(laneIndex)].edgeCenter;
|
||||
// 「均值滤波」开启时,为**被勾选**的信号各叠加一条多次迭代滑动平均曲线
|
||||
//(均值点数 X、迭代次数 N 由该信号自己的条目给出)。
|
||||
const bool meanFilterOn = Tab().Lanes()[static_cast<std::size_t>(laneIndex)].meanFilter;
|
||||
// 「弱波增强」开启时同样只为**被勾选**的信号叠加曲线
|
||||
//(增强等级取整个网格共用的 `weakBoostLevel`)。
|
||||
const bool weakBoostOn = Tab().Lanes()[static_cast<std::size_t>(laneIndex)].weakBoost;
|
||||
// 派生预览曲线(中心轴 / 均值滤波 / 弱波增强)在下方"第二遍"统一叠加绘制,
|
||||
// 见 `PaintLaneDerivedOverlays`(抽出以便宽缓冲区前台路径复用)。
|
||||
|
||||
for (const SignalRef& ref : laneSignals)
|
||||
{
|
||||
@@ -7286,55 +7292,12 @@ namespace jdi
|
||||
}
|
||||
|
||||
// 第二遍:把所有**派生预览曲线**统一绘制在**本网格全部原信号之上**。
|
||||
// 若在同一个信号循环里就地叠加,后绘制的原信号会把先前信号的预览曲线盖住
|
||||
//(用户明确要求"预览信号处于原信号上层不要被覆盖")。
|
||||
for (const SignalRef& ref : laneSignals)
|
||||
// 渲染「宽缓冲区」时跳过:宽缓冲只存"基底"(网格/波形,不含派生曲线与装饰),
|
||||
// 派生曲线改由前台叠加层绘制(见 `PaintLaneDerivedOverlays`),
|
||||
// 这样拖派生参数滑块时基底不变、可复用宽缓冲像素。
|
||||
if (!m_renderWide)
|
||||
{
|
||||
const ResolvedSignal resolved = ResolveSignal(ref);
|
||||
if (resolved.data == nullptr || resolved.style == nullptr || !resolved.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// 中心轴均值滤波曲线(「实验性功能 → 边沿计数(中心轴)」):与「均值滤波」同源,
|
||||
// 只有被勾选的信号才绘制。
|
||||
if (centerOn)
|
||||
{
|
||||
const Lane::EdgeCenterItem* centerItem =
|
||||
Tab().Lanes()[static_cast<std::size_t>(laneIndex)].FindEdgeCenterItem(ref);
|
||||
if (centerItem != nullptr)
|
||||
{
|
||||
PaintMeanFilterCurve(g, resolved, lane, centerItem->points, centerItem->iterations);
|
||||
}
|
||||
}
|
||||
// 均值滤波预览曲线:只有被勾选的信号才绘制(参数取自它自己的条目)。
|
||||
if (meanFilterOn)
|
||||
{
|
||||
const Lane::MeanFilterItem* item =
|
||||
Tab().Lanes()[static_cast<std::size_t>(laneIndex)].FindMeanFilterItem(ref);
|
||||
if (item != nullptr)
|
||||
{
|
||||
PaintMeanFilterCurve(g, resolved, lane, item->points, item->iterations);
|
||||
}
|
||||
}
|
||||
// 弱波增强预览曲线:只有被勾选的信号才绘制(增强等级取自整个网格)。
|
||||
if (weakBoostOn)
|
||||
{
|
||||
const Lane::WeakBoostItem* item =
|
||||
Tab().Lanes()[static_cast<std::size_t>(laneIndex)].FindWeakBoostItem(ref);
|
||||
if (item != nullptr)
|
||||
{
|
||||
const int levelPercent = static_cast<int>(std::lround(
|
||||
std::max(0.0, std::min(1.0,
|
||||
Tab().Lanes()[static_cast<std::size_t>(laneIndex)]
|
||||
.weakBoostLevel)) *
|
||||
100.0));
|
||||
const int iterations = std::max(
|
||||
kWeakBoostIterationsMin,
|
||||
std::min(kWeakBoostIterationsMax,
|
||||
Tab().Lanes()[static_cast<std::size_t>(laneIndex)].weakBoostIterations));
|
||||
PaintWeakBoostCurve(g, resolved, lane, levelPercent, iterations);
|
||||
}
|
||||
}
|
||||
PaintLaneDerivedOverlays(g, laneIndex, lane);
|
||||
}
|
||||
|
||||
// g.Restore 也会还原绘制状态,此处直接恢复图形状态即可。
|
||||
@@ -7465,6 +7428,59 @@ namespace jdi
|
||||
return SignalRef();
|
||||
}
|
||||
|
||||
void PlotView::PaintLaneDerivedOverlays(gd::Graphics& g, int laneIndex, const LaneVisual& lane) const
|
||||
{
|
||||
if (m_tab == nullptr || laneIndex < 0 ||
|
||||
laneIndex >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
const Lane& tabLane = Tab().Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
const bool centerOn = tabLane.edgeCenter;
|
||||
const bool meanFilterOn = tabLane.meanFilter;
|
||||
const bool weakBoostOn = tabLane.weakBoost;
|
||||
if (!centerOn && !meanFilterOn && !weakBoostOn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int levelPercent = static_cast<int>(
|
||||
std::lround(std::max(0.0, std::min(1.0, tabLane.weakBoostLevel)) * 100.0));
|
||||
const int boostIterations =
|
||||
std::max(kWeakBoostIterationsMin, std::min(kWeakBoostIterationsMax, tabLane.weakBoostIterations));
|
||||
for (const SignalRef& ref : tabLane.signals)
|
||||
{
|
||||
const ResolvedSignal resolved = ResolveSignal(ref);
|
||||
if (resolved.data == nullptr || resolved.style == nullptr || !resolved.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (centerOn)
|
||||
{
|
||||
const Lane::EdgeCenterItem* centerItem = tabLane.FindEdgeCenterItem(ref);
|
||||
if (centerItem != nullptr)
|
||||
{
|
||||
PaintMeanFilterCurve(g, resolved, lane, centerItem->points, centerItem->iterations);
|
||||
}
|
||||
}
|
||||
if (meanFilterOn)
|
||||
{
|
||||
const Lane::MeanFilterItem* item = tabLane.FindMeanFilterItem(ref);
|
||||
if (item != nullptr)
|
||||
{
|
||||
PaintMeanFilterCurve(g, resolved, lane, item->points, item->iterations);
|
||||
}
|
||||
}
|
||||
if (weakBoostOn)
|
||||
{
|
||||
const Lane::WeakBoostItem* item = tabLane.FindWeakBoostItem(ref);
|
||||
if (item != nullptr)
|
||||
{
|
||||
PaintWeakBoostCurve(g, resolved, lane, levelPercent, boostIterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlotView::PaintLaneLegend(gd::Graphics& g, int laneIndex, const LaneVisual& lane, float visibleTop,
|
||||
float visibleBottom)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user