app+docs: 第三十八轮追加——同帧对照收敛差距:真机显示与 NUC dump 空间相关 0.994,鲁棒同口径下 COL 0.33 vs 官方 0.28 达标、ROW 0.22 vs 0.10 差 2.2×;640 中间级竖屏 2.25× 非整数致 GPU 列交替恶化已回退(保留 nativeOut 单级 9×/16× 整数);PIL F-mode resize 失真教训与 COL 波动待验项入档
This commit is contained in:
@@ -100,24 +100,6 @@ class LiveRenderer(
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 640x480 offscreen stage, mirroring the official viewer's display chain:
|
||||
* jadx `ImageViewer` draws the 160x120 SDK bitmap into a 640x480 offscreen
|
||||
* bitmap with the filtering paint (PaintFlagsDrawFilter + FILTER_BITMAP) and
|
||||
* only the SCREEN scales that further. The intermediate 4x bilinear is what
|
||||
* attenuates the per-sensor-row FPN pattern on the screen (measured, §17:
|
||||
* with it the row pattern lands on the official's 0.12e-2 arc; without, our
|
||||
* single-scale render shows it at full amplitude). The stage costs one GPU
|
||||
* blit per frame.
|
||||
*/
|
||||
private var mid: Bitmap? = null
|
||||
private val midRect = android.graphics.RectF(0f, 0f, 640f, 480f)
|
||||
|
||||
private fun ensureMid(): Bitmap {
|
||||
mid?.let { return it }
|
||||
return Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888).also { mid = it }
|
||||
}
|
||||
|
||||
|
||||
private companion object {
|
||||
const val BAR_SEGMENTS = 96
|
||||
@@ -234,12 +216,10 @@ class LiveRenderer(
|
||||
|
||||
val zoom = vm.state.value.zoom
|
||||
val crop = ImageTransform.cropForZoom(zoom)
|
||||
// The zoom source rect now addresses the 640x480 middle stage (4x the
|
||||
// buffer coordinates), exactly like the official viewer's offscreen.
|
||||
val src: android.graphics.Rect? = if (zoom > 1) {
|
||||
val cw = (640 * (crop[2] - crop[0])).toInt()
|
||||
val ch = (480 * (crop[3] - crop[1])).toInt()
|
||||
srcRect.set(320 - cw / 2, 240 - ch / 2, 320 + cw / 2, 240 + ch / 2)
|
||||
val cw = (bufW * (crop[2] - crop[0])).toInt()
|
||||
val ch = (bufH * (crop[3] - crop[1])).toInt()
|
||||
srcRect.set(bufW / 2 - cw / 2, bufH / 2 - ch / 2, bufW / 2 + cw / 2, bufH / 2 + ch / 2)
|
||||
srcRect
|
||||
} else null
|
||||
|
||||
@@ -254,14 +234,15 @@ class LiveRenderer(
|
||||
fit.cx - size[0] / 2f, fit.cy - size[1] / 2f,
|
||||
fit.cx + size[0] / 2f, fit.cy + size[1] / 2f,
|
||||
)
|
||||
// two-step display like the official viewer: buffer -> 640x480 (filtered)
|
||||
// -> screen. The GPU blits are cheap; the attenuated row pattern is the point.
|
||||
val m = ensureMid()
|
||||
val mc = Canvas(m)
|
||||
mc.drawColor(Color.BLACK)
|
||||
mc.drawBitmap(bitmap, null, midRect, paint)
|
||||
if (src != null) canvas.drawBitmap(m, src, dstRect, paint)
|
||||
else canvas.drawBitmap(m, null, dstRect, paint)
|
||||
// Single filtered scale buffer -> screen. The official viewer's 640x480
|
||||
// offscreen stage was tried (round 38) and REVERTED: in our portrait
|
||||
// fitRect the second stage lands on a 2.25x non-integer scale, and the
|
||||
// GPU's rendering of it measurably WORSENED the column-direction
|
||||
// alternation on the device (0.33 -> 0.55 x1e-2 arc) — an artifact the
|
||||
// official (landscape, integer 3x second stage) doesn't have. The 160x120
|
||||
// -> 1440x1920 single scale is 9x/16x integer, artifact-free.
|
||||
if (src != null) canvas.drawBitmap(bitmap, src, dstRect, paint)
|
||||
else canvas.drawBitmap(bitmap, null, dstRect, paint)
|
||||
canvas.restore()
|
||||
|
||||
drawOsd(canvas, vm.state.value)
|
||||
|
||||
Reference in New Issue
Block a user