fix(tools): preserve opaque framebuffer RGB in captures
Docs Deploy / Build and Deploy MkDocs (push) Successful in 36s

This commit is contained in:
Valentin Popov committed 2026-11-01 09:45:59 +04:00
1 parent 6c603976d4
commit f771a62298
4 files changed
+40 -12

No files matched your search

+6
View File
@@ -348,3 +348,9 @@ D3D7 в `fparkan-render`, а затем один раз меняет знак в
каналов не определяет. Последовательность не содержит структурированных ID
видимых объектов, списка элементов отрисовки, ключей конвейера, матриц или
хэшей промежуточных буферов.
При преобразовании кадра в PNG сохраняется исходный RGB, а прозрачность
выключается: окно показывает готовое непрозрачное изображение. Альфа-канал
GPU может содержать результат смешивания поверхностей; повторное применение
его к готовому цвету затемнило бы кадр ещё раз. Поэтому конвертер и сборщик
сравнительных пар игнорируют эту альфу, а raw-файл сохраняют без изменений.
+6
View File
@@ -117,6 +117,12 @@ the verified value for the run above, not a universal surface format. The
converter uses the installed `System.Drawing` PNG encoder and adds no package
dependency.
PNG exports treat the captured frame as opaque and preserve its stored RGB.
Framebuffer alpha is a rendering result, not image transparency; applying it
again when displaying the PNG would darken surfaces a second time. Raw files
retain all four original channels. The comparison script also ignores alpha
in older PNG exports, without changing the measured RGB values.
## Сравнение областей кадра
`compare-frames.ps1` собирает пару «оригинал / FParkan» без масштабирования и
+21 -5
View File
@@ -41,7 +41,21 @@ if ($SelfTest) {
try { Get-MeanRgb $test ([System.Drawing.Rectangle]::new(1, 0, 2, 1)) | Out-Null }
catch { $rejected = $true }
if (-not $rejected) { throw 'Out-of-bounds region was accepted.' }
Write-Output 'self-test: exact region RGB means and bounds rejection OK'
$test.SetPixel(0, 0, [System.Drawing.Color]::FromArgb(0, 31, 16, 24))
$test.SetPixel(1, 0, [System.Drawing.Color]::FromArgb(64, 31, 16, 24))
$opaque = $test.Clone([System.Drawing.Rectangle]::new(0, 0, 2, 1), [System.Drawing.Imaging.PixelFormat]::Format32bppRgb)
$pair = [System.Drawing.Bitmap]::new(2, 1)
$graphics = [System.Drawing.Graphics]::FromImage($pair)
try {
$graphics.Clear([System.Drawing.Color]::Black)
$graphics.DrawImageUnscaled($opaque, 0, 0)
foreach ($x in @(0, 1)) {
if ($pair.GetPixel($x, 0).ToArgb() -ne [System.Drawing.Color]::FromArgb(31, 16, 24).ToArgb()) {
throw 'Comparison applied framebuffer alpha to stored RGB.'
}
}
} finally { $graphics.Dispose(); $pair.Dispose(); $opaque.Dispose() }
Write-Output 'self-test: exact region RGB means, opaque presentation and bounds rejection OK'
} finally { $test.Dispose() }
return
}
@@ -63,19 +77,21 @@ foreach ($value in $viewport) {
}
$crop = [System.Drawing.Rectangle]::new($viewport[0], $viewport[1], $viewport[2] - $viewport[0], $viewport[3] - $viewport[1])
$native = [System.Drawing.Bitmap]::new((Resolve-Path -LiteralPath $NativePng).Path)
$viewer = $nativeCrop = $pair = $graphics = $null
$viewer = $viewerOpaque = $nativeCrop = $pair = $graphics = $null
try {
$viewer = [System.Drawing.Bitmap]::new((Resolve-Path -LiteralPath $ViewerPng).Path)
if ($crop.Width -le 0 -or $crop.Height -le 0 -or $crop.Right -gt $native.Width -or $crop.Bottom -gt $native.Height -or
$viewer.Width -ne $crop.Width -or $viewer.Height -ne $crop.Height) {
throw 'Native crop and viewer dimensions must match without scaling.'
}
$nativeCrop = $native.Clone($crop, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
# Captured frames are opaque presentations; framebuffer alpha is not PNG opacity.
$nativeCrop = $native.Clone($crop, [System.Drawing.Imaging.PixelFormat]::Format32bppRgb)
$viewerOpaque = $viewer.Clone([System.Drawing.Rectangle]::new(0, 0, $viewer.Width, $viewer.Height), [System.Drawing.Imaging.PixelFormat]::Format32bppRgb)
$pair = [System.Drawing.Bitmap]::new($viewer.Width * 2, $viewer.Height + 24)
$graphics = [System.Drawing.Graphics]::FromImage($pair)
$graphics.Clear([System.Drawing.Color]::Black)
$graphics.DrawImageUnscaled($nativeCrop, 0, 24)
$graphics.DrawImageUnscaled($viewer, $viewer.Width, 24)
$graphics.DrawImageUnscaled($viewerOpaque, $viewer.Width, 24)
$font = [System.Drawing.SystemFonts]::DefaultFont
$graphics.DrawString('Original', $font, [System.Drawing.Brushes]::White, 4, 4)
$graphics.DrawString('FParkan', $font, [System.Drawing.Brushes]::White, $viewer.Width + 4, 4)
@@ -100,7 +116,7 @@ try {
ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $jsonPath -Encoding UTF8
Write-Output "Wrote $pngPath and $jsonPath"
} finally {
foreach ($resource in @($graphics, $pair, $nativeCrop, $viewer, $native)) {
foreach ($resource in @($graphics, $pair, $nativeCrop, $viewerOpaque, $viewer, $native)) {
if ($null -ne $resource) { $resource.Dispose() }
}
}
@@ -118,7 +118,7 @@ function Save-BgraPngToStream {
$bitmap = New-Object System.Drawing.Bitmap(
$Width,
$Height,
[System.Drawing.Imaging.PixelFormat]::Format32bppArgb
[System.Drawing.Imaging.PixelFormat]::Format32bppRgb
)
$rectangle = New-Object System.Drawing.Rectangle(0, 0, $Width, $Height)
$bitmapData = $null
@@ -126,7 +126,7 @@ function Save-BgraPngToStream {
$bitmapData = $bitmap.LockBits(
$rectangle,
[System.Drawing.Imaging.ImageLockMode]::WriteOnly,
[System.Drawing.Imaging.PixelFormat]::Format32bppArgb
[System.Drawing.Imaging.PixelFormat]::Format32bppRgb
)
$rowBytes = $Width * 4
@@ -201,12 +201,12 @@ function Get-CameraExtent {
function Invoke-SelfTest {
$rgba = [byte[]]@(
255, 0, 0, 255, 0, 255, 0, 255,
0, 0, 255, 255, 255, 255, 255, 255
255, 0, 0, 0, 0, 255, 0, 64,
0, 0, 255, 128, 255, 255, 255, 255
)
$expectedBgra = [byte[]]@(
0, 0, 255, 255, 0, 255, 0, 255,
255, 0, 0, 255, 255, 255, 255, 255
0, 0, 255, 0, 0, 255, 0, 64,
255, 0, 0, 128, 255, 255, 255, 255
)
$rgbaResult = Convert-ReadbackToBgra -Source $rgba -Format 37 -Width 2 -Height 2
@@ -303,7 +303,7 @@ function Invoke-SelfTest {
$stream.Dispose()
}
'Self-check passed (2x2 RGBA/BGRA PNG, byte-length/64 MiB limits, viewport-coordinate bounds, and cropped pixel_capture bounds).'
'Self-check passed (opaque 2x2 RGBA/BGRA PNG, byte-length/64 MiB limits, viewport-coordinate bounds, and cropped pixel_capture bounds).'
}
if ($SelfTest) {