"""Keep native UI ink while making the cleared world transparent.""" import numpy as np def clear_world_alpha(pixels): black = ~np.any(pixels[:, :, :3], axis=2) height, width = black.shape parents, edge, runs = [], [], [] previous = [] def root(i): while parents[i] != i: parents[i] = parents[parents[i]] i = parents[i] return i for y in range(height): changes = np.flatnonzero(np.diff(np.pad(black[y].astype(np.int8), (1, 1)))) current = [] cursor = 0 for x0, x1 in zip(changes[::2], changes[1::2]): i = len(parents) parents.append(i) edge.append(y in (0, height-1) or x0 == 0 or x1 == width) current.append((int(x0), int(x1), i)) runs.append((y, int(x0), int(x1), i)) while cursor < len(previous) and previous[cursor][1] <= x0: cursor += 1 k = cursor while k < len(previous) and previous[k][0] < x1: a, b = root(i), root(previous[k][2]) if a != b: parents[b] = a edge[a] = edge[a] or edge[b] k += 1 previous = current result = pixels.copy() result[:, :, 3] = 255 for y, x0, x1, i in runs: if edge[root(i)]: result[y, x0:x1, 3] = 0 return result def clear_supplemental_hud_alpha(pixels): """Correct native warning fringes in the independently cleared HUD pass. FTL paints faint warning-text shadows with nonzero RGB and fractional alpha. The legacy screen conversion makes those almost-black pixels opaque. Use their actual coverage in the warning band only; framed HUD controls still need the contour fill to retain their original black ink. Full screens and native windows do not use this supplemental conversion. """ result = clear_world_alpha(pixels) # At the native 1280x720 HUD coordinates, warnings sit between the top # controls and the weapon bar, to the right of crew and left of target UI. # They shift horizontally when an enemy arrives, so cover both positions. height, width = pixels.shape[:2] left, right = round(400 * width / 1280), round(872 * width / 1280) top, bottom = round(160 * height / 720), round(360 * height / 720) source = pixels[top:bottom,left:right] warning = result[top:bottom,left:right] if not source.size: return result # RGB already includes FTL's warning blink/fade. Do not multiply faded red # glyphs by their raw FBO alpha a second time: only recover coverage for # the almost-black texture fringe, relative to the current glyph peak. colors = source[:, :, :3].astype(np.int16) reddish = (colors[:, :, 0] > colors[:, :, 1] * 2) & (colors[:, :, 0] > colors[:, :, 2] * 2) glyphs = reddish & (colors[:, :, 0] > 8) if not np.any(glyphs): return result brightness = np.max(colors, axis=2) fringe_limit = max(1.0, float(colors[:, :, 0][glyphs].max()) * 0.35) faint_coverage = (source[:, :, 3] <= 8) & (brightness <= fringe_limit) & reddish warning[:, :, 3][faint_coverage] = source[:, :, 3][faint_coverage] return result