修复垂直中键拖拽后再平移的网格纵向跳变/绘制错误
症状:对某网格做一次纵向中键拖拽(幅值窗口上下平移)后,不松开中键、只做一点点
水平(或垂直)位移,该网格波形整体纵向错位(最多 15px),看起来像"纵向跳帧/抽搐"。
松开后再按下中键做同样的微小水平移动同样复现,且与缩放等级无关。
根因:RenderLaneLayerRegion 结束时无条件把 layer.anchorYMax 重置为"当前 yMax"。
该赋值只对"整块重画 / 纵向补条(整宽)"成立(那两种情况整块像素都对齐到当前映射),
对"横向补细条(整高)"不成立 —— 横向滚动只改一条竖带,其余像素仍在原锚点上。
纵向拖动以 kLayerRollQuantum = 16px 量化:|vOffset| < 16px 时不滚动、交给贴图偏移,
因此拖动停下时图层会残留一个 <16px 的纵向偏移且 anchorYMax != 当前 yMax。第一次水平
微移(哪怕只有 3px)就把该残余偏移静默清零(vOffset 变成 0),而像素并未上移 →
整个网格的波形相对正确位置纵向错位,错位量恰好等于那个残余量。
修法:RenderLaneLayerRegion 新增形参 double anchorYMax(本次渲染像素所对应的纵向锚点),
内部以它作为幅值映射基准(yMin = anchorYMax - laneSpan)渲染,并写回
layer.anchorYMax = anchorYMax。各调用点:
* 整块重画 / 纵向补条 / 待重画补齐:传当前 yMax(纵向补条传滚动后的 layer.anchorYMax,
与已滚动像素同一映射);
* 横向补细条:传 layer.anchorYMax(保持不变),细条因此与其余像素纵向同源,
残余偏移继续由贴图偏移承担。
vOffset == 0 时新逻辑与旧逻辑完全等价,故既有"仅水平/仅纵向平移"的像素结果不变。
验证:新增离屏探针 .preflight/perf/pancheck(窗口 SW_HIDE + 直接注入消息 + 读图层内部
anchorYMax/当前 yMax/vOffset + 把持按钮帧与释放后的整幅全质量帧做纵向互相关)。
修复前 A 场景 H step 1 起 vOff 从 -15.00 被清零为 +0.00、lane0 best dy=+15px(差异 39245);
修复后全程保持 -15.00、best dy=0(差异 1220,仅已知 <=0.5px 列相位残差)。
对照/中间网格/长距水平/斜向/松开重按共 7 个场景在缩放 0/8/26 步下全部 dy=0
(中间网格 +1px 为整像素量化残差)。make clean && make all 零警告。
This commit is contained in:
1 parent
7344bbe9c4
commit
de27d9b94f
3 files changed
+93
-12
No files matched your search
@@ -7319,7 +7319,9 @@ namespace jdi
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!RenderLaneLayerRegion(laneIndex, layer, 0, 0, 0, 0, defaultWideMin))
|
||||
// 整块重画:整个图层都对齐到"当前 `yMax`"(锚点 = 当前 yMax)。
|
||||
if (!RenderLaneLayerRegion(laneIndex, layer, 0, 0, 0, 0, defaultWideMin,
|
||||
m_lanes[static_cast<std::size_t>(laneIndex)].yMax))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -7330,7 +7332,7 @@ namespace jdi
|
||||
}
|
||||
|
||||
bool PlotView::RenderLaneLayerRegion(int laneIndex, LaneLayer& layer, int stripX, int stripW,
|
||||
int stripY, int stripH, double wideMin)
|
||||
int stripY, int stripH, double wideMin, double anchorYMax)
|
||||
{
|
||||
if (laneIndex < 0 || laneIndex >= static_cast<int>(m_lanes.size()))
|
||||
{
|
||||
@@ -7410,6 +7412,13 @@ namespace jdi
|
||||
m_lanes[i].yMin = savedLanes[i].yMin;
|
||||
m_lanes[i].yMax = savedLanes[i].yMax;
|
||||
}
|
||||
// ⚠️ **该网格的幅值映射必须以本次渲染的纵向锚点为准**(而不是"当前 `yMax`"):
|
||||
// - 整块重画 / 纵向补条:锚点就是当前 `yMax`(纵向补条是滚动后的值),二者一致;
|
||||
// - **横向补细条**:只重画一条竖带,其余像素仍在**原锚点**上;若这里用当前 `yMax`
|
||||
// 渲染,竖带与其余像素的纵向映射就不同源,贴图后出现撕裂/错位。
|
||||
// 详见 `RenderLaneLayerRegion` 的 `anchorYMax` 形参说明。
|
||||
m_lanes[static_cast<std::size_t>(laneIndex)].yMax = anchorYMax;
|
||||
m_lanes[static_cast<std::size_t>(laneIndex)].yMin = anchorYMax - laneSpan;
|
||||
// 该网格在本图层中的位置:横向铺满整条宽图,纵向铺满整张画布。
|
||||
// 画布 = 「可见网格 + 上下各 `marginY` 行余量」。**上下扩展量按"像素行"精确换算**
|
||||
//(而不是上下各扩展固定的数值量):位图高度是整数(`ceil(laneHeight) + 2*marginY`),
|
||||
@@ -7474,9 +7483,9 @@ namespace jdi
|
||||
layer.wideMin = wideMin;
|
||||
layer.span = span;
|
||||
layer.marginY = marginY;
|
||||
// 纵向锚点 = 渲染时该网格的 `yMax`(可见窗口顶行对应的数值)。
|
||||
// 贴图时按 `(anchorYMax - 当前 yMax) * pxPerValue` 得到纵向偏移像素数。
|
||||
layer.anchorYMax = savedLanes[static_cast<std::size_t>(laneIndex)].yMax;
|
||||
// 纵向锚点 = 本次渲染的像素所对应的 `yMax`(由调用方按"改动了哪部分像素"决定;
|
||||
// 横向补细条必须沿用图层原锚点,否则残余的纵向偏移会被静默清零 —— 见函数说明)。
|
||||
layer.anchorYMax = anchorYMax;
|
||||
layer.pxPerValue = pxPerValue;
|
||||
layer.key = LaneLayerKey(laneIndex);
|
||||
layer.valid = true;
|
||||
@@ -7505,7 +7514,9 @@ namespace jdi
|
||||
bool ok = true;
|
||||
for (std::size_t index = 0; index < m_layerFront.size(); ++index)
|
||||
{
|
||||
if (!RenderLaneLayerRegion(static_cast<int>(index), m_layerFront[index], 0, 0, 0, 0, wideMin))
|
||||
// 整块重画:每个图层都以"当前 `yMax`"为纵向锚点。
|
||||
if (!RenderLaneLayerRegion(static_cast<int>(index), m_layerFront[index], 0, 0, 0, 0, wideMin,
|
||||
m_lanes[index].yMax))
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
@@ -7590,7 +7601,9 @@ namespace jdi
|
||||
{
|
||||
QueueLaneRebuild(laneIndex);
|
||||
}
|
||||
RenderLaneLayerRegion(laneIndex, layer, 0, bitmapWidth, 0, 0, LayerDefaultWideMin());
|
||||
// 整块重画:重新居中 + 纵向锚点取**当前** `yMax`。
|
||||
RenderLaneLayerRegion(laneIndex, layer, 0, bitmapWidth, 0, 0, LayerDefaultWideMin(),
|
||||
m_lanes[static_cast<std::size_t>(laneIndex)].yMax);
|
||||
continue;
|
||||
}
|
||||
if (contentStale)
|
||||
@@ -7603,7 +7616,7 @@ namespace jdi
|
||||
// 再只补画新暴露的那一条(整宽 × 行数)。
|
||||
//
|
||||
// ⚠️ **必须先做纵向再做横向**:横向补条由 `RenderLaneLayerRegion` 渲染,
|
||||
// 而该函数会把图层的纵向锚点重置为"当前 `yMax`";若先补横向、那时锚点还没跟上,
|
||||
// 而该函数按传入的**纵向锚点**渲染细条;若先补横向,那时锚点还没跟上,
|
||||
// 补出来的细条会带着一个错的纵向偏移(贴图后表现为波形错位/撕裂)。
|
||||
// ⚠️ 没有这一步时:幅值一变就超出余量 → 每帧都排队**整块重画**(画布比可见网格高
|
||||
// 3 倍,代价很大)→ 拖动时"抽搐";而且在重画完成前,`BlitWideBuffer` 只能贴出部分
|
||||
@@ -7639,8 +7652,9 @@ namespace jdi
|
||||
}
|
||||
// 锚点随之移动(与横向 `layer.wideMin += shift / scale` 同一套记账方式)。
|
||||
layer.anchorYMax -= static_cast<double>(roll) / layer.pxPerValue;
|
||||
// 补画暴露的那一条(整宽)。
|
||||
RenderLaneLayerRegion(laneIndex, layer, 0, bitmapWidth, stripY, stripH, layer.wideMin);
|
||||
// 补画暴露的那一条(整宽)。锚点用**滚动后**的值 —— 该条与已滚动的像素同一映射。
|
||||
RenderLaneLayerRegion(laneIndex, layer, 0, bitmapWidth, stripY, stripH, layer.wideMin,
|
||||
layer.anchorYMax);
|
||||
}
|
||||
|
||||
if (shift == 0)
|
||||
@@ -7664,9 +7678,14 @@ namespace jdi
|
||||
// 只补画新暴露的细条(每帧代价恒为 O(细条))。
|
||||
// ⚠️ 细条必须用**该图层更新后的映射**渲染(传 `layer.wideMin`);若改用"当前视界"
|
||||
// 的映射,细条与已平移的旧像素之间会有 ≤0.5px 的错位接缝 → 拖动时看起来在抖动。
|
||||
// ⚠️ **纵向锚点必须沿用 `layer.anchorYMax`**(不能传当前 `yMax`):横向补条只改一条
|
||||
// 竖带,其余像素仍在原锚点上;同时该函数按锚点渲染这条竖带,因此竖带与其余像素
|
||||
// 纵向同源。若在这里把锚点重置为当前 `yMax`,残余的纵向偏移会被静默清零、而像素
|
||||
// 并未上移 → 整个网格的波形纵向错位("垂直拖动后再横向平移出现纵向跳变")。
|
||||
const int stripWidth = std::min(std::abs(shift), bitmapWidth);
|
||||
const int stripX = (shift > 0) ? (bitmapWidth - stripWidth) : 0;
|
||||
RenderLaneLayerRegion(laneIndex, layer, stripX, stripWidth, 0, 0, layer.wideMin);
|
||||
RenderLaneLayerRegion(laneIndex, layer, stripX, stripWidth, 0, 0, layer.wideMin,
|
||||
layer.anchorYMax);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user