mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-28 18:06:09 +02:00
156 lines
5.9 KiB
Lua
156 lines
5.9 KiB
Lua
-- Manual check for #1866: black hairlines at the title's SGB zone boundaries
|
|
-- (canvas rows 64 and 80, BlkPacket_Titlescreen, data/sgb/sgb_packets.asm:
|
|
-- 123-127) at window heights that are not a whole multiple of 144.
|
|
-- POKEPORT_DRIVER=tests/drivers/title_seam_bug1866_test.lua POKEPORT_TOUCH=0 SHOT_DIR=/tmp/shots love .
|
|
-- Do not set POKEPORT_SPEED: fast-forward desynchronizes the title music.
|
|
return function(game)
|
|
local U = dofile("tests/drivers/util.lua")
|
|
local PaletteFX = require("src.render.PaletteFX")
|
|
local Renderer = require("src.render.Renderer")
|
|
local SHOT_DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
|
|
|
local function check(label, ok)
|
|
U.log(ok and "PASS" or "FAIL", label)
|
|
return ok
|
|
end
|
|
|
|
local title
|
|
for _ = 1, 120 do
|
|
local top = game.stack:top()
|
|
if top and top.screenId == "TitleState" and top.sgbPalettes then
|
|
title = top
|
|
break
|
|
end
|
|
U.tap(game, "start")
|
|
U.wait(9)
|
|
end
|
|
check("the title screen is on top", title ~= nil)
|
|
if not (title and title.sgbPalettes) then
|
|
U.log("No title state to look at; nothing below can run.")
|
|
while true do coroutine.yield() end
|
|
end
|
|
|
|
local opts = game.save.options
|
|
local mode = opts and opts.colors or PaletteFX.mode
|
|
if PaletteFX.mode ~= "gbc" then
|
|
U.log("COLORS is", tostring(mode) .. "; switching the view to SGB for the check")
|
|
PaletteFX.setMode("gbc")
|
|
U.wait(20)
|
|
end
|
|
local zones = PaletteFX.ensureZones(title:sgbPalettes(game))
|
|
check("the title builds three SGB zones", zones ~= nil and #zones == 3)
|
|
|
|
-- Every height below is odd and none is a multiple of 144, so the picture
|
|
-- never lands on a whole number of screen pixels per GB pixel. "fill"
|
|
-- additionally drops the integer fit scale, which is the shape the report's
|
|
-- screenshot was taken in (about 5.98 screen pixels per GB pixel).
|
|
local HEIGHTS = { 721, 823, 907, 1013 }
|
|
local WIDTH = 1157
|
|
local worstRows, worstTag = 0, "none"
|
|
|
|
-- the control: a zone list with a real hole in it, drawn the way the
|
|
-- per-zone pass alone would draw it. It proves the sampler below can see
|
|
-- a seam at all, and that the underpaint is what closes this one.
|
|
local gapped = {}
|
|
for i, z in ipairs(zones) do
|
|
gapped[i] = { x = z.x, y = z.y, w = z.w, h = z.h, colors = z.colors }
|
|
end
|
|
if gapped[2] then
|
|
gapped[2].y = gapped[2].y + 1
|
|
gapped[2].h = gapped[2].h - 2
|
|
end
|
|
|
|
local function drawZonesOnly(zoneList, Ux, Uy, uox, uoy)
|
|
local shader = PaletteFX.shader()
|
|
love.graphics.setShader(shader)
|
|
for _, z in ipairs(zoneList) do
|
|
PaletteFX.sendColors(shader, z.colors)
|
|
love.graphics.setScissor(uox + z.x * Ux, uoy + z.y * Uy,
|
|
z.w * Ux, z.h * Uy)
|
|
love.graphics.draw(Renderer.canvas, uox, uoy, 0, Ux, Uy)
|
|
end
|
|
love.graphics.setScissor()
|
|
love.graphics.setShader()
|
|
end
|
|
|
|
local function probeAt(ph, fill, zoneList, zonesOnly)
|
|
local dpi = 1
|
|
local probe = love.graphics.newCanvas(WIDTH, ph, { dpiscale = dpi })
|
|
local Up
|
|
if fill then
|
|
Up = math.min(ph / 144, WIDTH / 160)
|
|
else
|
|
Up = math.max(1, math.floor(math.min(WIDTH / 160, ph / 144)))
|
|
end
|
|
local Ux, Uy = Up / dpi, Up / dpi
|
|
local uox = math.floor((WIDTH - 160 * Up) / 2) / dpi
|
|
local uoy = math.floor((ph - 144 * Up) / 2) / dpi
|
|
local uvpw, uvph = 160 * Ux, 144 * Uy
|
|
love.graphics.setCanvas(probe)
|
|
love.graphics.clear(0, 0, 0, 1)
|
|
love.graphics.setColor(1, 1, 1, 1)
|
|
if zonesOnly then
|
|
drawZonesOnly(zoneList, Ux, Uy, uox, uoy)
|
|
else
|
|
Renderer:blitCanvas(Renderer.canvas, Ux, Uy, zoneList, Ux, Uy,
|
|
uox, uoy, uox, uoy, uvpw, uvph, dpi, dpi)
|
|
end
|
|
love.graphics.setCanvas()
|
|
local data = probe:newImageData()
|
|
local top = math.floor(uoy * dpi) + 1
|
|
local bottom = math.min(math.floor((uoy + uvph) * dpi) - 1, data:getHeight() - 1)
|
|
local col = math.floor(uox * dpi) + 2
|
|
local rows = {}
|
|
for py = top, bottom do
|
|
local r, g, b = data:getPixel(col, py)
|
|
if r < 0.02 and g < 0.02 and b < 0.02 then rows[#rows + 1] = py end
|
|
end
|
|
return rows, ("%dx%d %s"):format(WIDTH, ph, fill and "fill" or "fit")
|
|
end
|
|
|
|
for _, ph in ipairs(HEIGHTS) do
|
|
for _, fill in ipairs({ false, true }) do
|
|
local rows, tag = probeAt(ph, fill, zones, false)
|
|
if #rows > worstRows then worstRows, worstTag = #rows, tag end
|
|
check(tag .. ": no letterbox row shows through a zone boundary",
|
|
#rows == 0)
|
|
if #rows > 0 then
|
|
U.log(" black rows:", table.concat(rows, ","))
|
|
end
|
|
end
|
|
end
|
|
U.log("worst case:", worstRows, "black rows at", worstTag)
|
|
|
|
local holeRows = probeAt(HEIGHTS[3], true, gapped, true)
|
|
check("a zone list with a hole in it does show the letterbox clear",
|
|
#holeRows > 0)
|
|
local filledRows = probeAt(HEIGHTS[3], true, gapped, false)
|
|
check("and the same hole drawn through the renderer shows none",
|
|
#filledRows == 0)
|
|
|
|
-- and the live window at one of those heights, for the eye
|
|
local ok = pcall(love.window.setMode, WIDTH, HEIGHTS[3],
|
|
{ resizable = true, vsync = 0 })
|
|
check("the window resized to " .. WIDTH .. "x" .. HEIGHTS[3], ok)
|
|
U.wait(20)
|
|
check("wrote " .. SHOT_DIR .. "/bug1866_title_907.png",
|
|
U.shot(game, SHOT_DIR .. "/bug1866_title_907.png"))
|
|
pcall(love.window.setMode, WIDTH, HEIGHTS[4], { resizable = true, vsync = 0 })
|
|
U.wait(20)
|
|
check("wrote " .. SHOT_DIR .. "/bug1866_title_1013.png",
|
|
U.shot(game, SHOT_DIR .. "/bug1866_title_1013.png"))
|
|
|
|
PaletteFX.setMode(mode)
|
|
U.wait(10)
|
|
|
|
U.log("Look along the title picture where the POKeMON logo meets the red")
|
|
U.log("VERSION ribbon, and where the ribbon meets RED and the copyright: the")
|
|
U.log("off-white background must run through both boundaries unbroken.")
|
|
U.log("The near-miss is a hairline one or two pixels tall that is dark grey")
|
|
U.log("or slightly off-white rather than black; that is still the seam.")
|
|
|
|
while true do
|
|
coroutine.yield()
|
|
end
|
|
end
|