aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-11-01 08:45:59 +0300
committerValentin Popov <valentin@popov.link>2026-11-01 08:45:59 +0300
commitf771a62298dd935a1ec6f746b738a2a82aab352d (patch)
tree313d5f4dab44fa2dfdf4f0d90f0917f00e9be7e3 /tools
parent6c603976d4e368708935ab80e152b495a5c5e47a (diff)
downloadfparkan-f771a62298dd935a1ec6f746b738a2a82aab352d.tar.xz
fparkan-f771a62298dd935a1ec6f746b738a2a82aab352d.zip
fix(tools): preserve opaque framebuffer RGB in captures
Diffstat (limited to 'tools')
-rw-r--r--tools/native-frame-capture/README.md6
-rw-r--r--tools/native-frame-capture/compare-frames.ps126
-rw-r--r--tools/native-frame-capture/convert-viewer-readback.ps114
3 files changed, 34 insertions, 12 deletions
diff --git a/tools/native-frame-capture/README.md b/tools/native-frame-capture/README.md
index d8f621c..1f7166b 100644
--- a/tools/native-frame-capture/README.md
+++ b/tools/native-frame-capture/README.md
@@ -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» без масштабирования и
diff --git a/tools/native-frame-capture/compare-frames.ps1 b/tools/native-frame-capture/compare-frames.ps1
index 2a83bf0..b8d3855 100644
--- a/tools/native-frame-capture/compare-frames.ps1
+++ b/tools/native-frame-capture/compare-frames.ps1
@@ -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() }
}
}
diff --git a/tools/native-frame-capture/convert-viewer-readback.ps1 b/tools/native-frame-capture/convert-viewer-readback.ps1
index 3bafe14..17c136a 100644
--- a/tools/native-frame-capture/convert-viewer-readback.ps1
+++ b/tools/native-frame-capture/convert-viewer-readback.ps1
@@ -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) {