ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
139 lines
5.4 KiB
Python
139 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Counts bright pixels in a PNG region."""
|
|
import struct
|
|
import sys
|
|
import zlib
|
|
|
|
|
|
def read_png(path):
|
|
with open(path, "rb") as png:
|
|
data = png.read()
|
|
if data[:8] != b"\x89PNG\r\n\x1a\n":
|
|
sys.exit(f"FAIL: {path} is not a PNG")
|
|
pos, width, height, color, interlace, idat = 8, 0, 0, 0, 0, b""
|
|
while pos < len(data):
|
|
length, kind = struct.unpack(">I4s", data[pos : pos + 8])
|
|
body = data[pos + 8 : pos + 8 + length]
|
|
if kind == b"IHDR":
|
|
width, height, depth, color, _, _, interlace = struct.unpack(">IIBBBBB", body)
|
|
if depth != 8 or color not in (2, 6) or interlace != 0:
|
|
sys.exit(f"FAIL: {path} needs 8-bit RGB/RGBA, not depth {depth} color {color} interlace {interlace}")
|
|
elif kind == b"IDAT":
|
|
idat += body
|
|
elif kind == b"IEND":
|
|
break
|
|
pos += 12 + length
|
|
return width, height, 4 if color == 6 else 3, zlib.decompress(idat)
|
|
|
|
|
|
def unfilter(width, height, channels, raw):
|
|
stride = width * channels
|
|
out, previous = bytearray(), bytearray(stride)
|
|
pos = 0
|
|
for _ in range(height):
|
|
kind = raw[pos]
|
|
pos += 1
|
|
line = bytearray(raw[pos : pos + stride])
|
|
pos += stride
|
|
for i in range(stride):
|
|
left = line[i - channels] if i >= channels else 0
|
|
up = previous[i]
|
|
up_left = previous[i - channels] if i >= channels else 0
|
|
if kind == 1:
|
|
line[i] = (line[i] + left) & 0xFF
|
|
elif kind == 2:
|
|
line[i] = (line[i] + up) & 0xFF
|
|
elif kind == 3:
|
|
line[i] = (line[i] + (left + up) // 2) & 0xFF
|
|
elif kind == 4:
|
|
p = left + up - up_left
|
|
pa, pb, pc = abs(p - left), abs(p - up), abs(p - up_left)
|
|
line[i] = (line[i] + (left if pa <= pb and pa <= pc else up if pb <= pc else up_left)) & 0xFF
|
|
out += line
|
|
previous = line
|
|
return out
|
|
|
|
|
|
def region_pixels(path, x0, y0, x1, y1):
|
|
width, height, channels, raw = read_png(path)
|
|
pixels = unfilter(width, height, channels, raw)
|
|
xs, xe = int(width * x0), int(width * x1)
|
|
ys, ye = int(height * y0), int(height * y1)
|
|
out = bytearray()
|
|
for y in range(ys, ye):
|
|
for x in range(xs, xe):
|
|
at = (y * width + x) * channels
|
|
out += pixels[at : at + 3]
|
|
return xe - xs, ye - ys, bytes(out)
|
|
|
|
|
|
def region_at(path, xs, ys, width, height):
|
|
shot_w, shot_h, channels, raw = read_png(path)
|
|
pixels = unfilter(shot_w, shot_h, channels, raw)
|
|
out = bytearray()
|
|
for y in range(ys, ys + height):
|
|
for x in range(xs, xs + width):
|
|
at = (y * shot_w + x) * channels
|
|
out += pixels[at : at + 3]
|
|
return bytes(out)
|
|
|
|
|
|
def main():
|
|
if sys.argv[1] == "dist":
|
|
print(bin(int(sys.argv[2], 16) ^ int(sys.argv[3], 16)).count("1"))
|
|
return
|
|
if sys.argv[1] == "dump":
|
|
shot, out, x0, y0, x1, y1 = sys.argv[2], sys.argv[3], *map(float, sys.argv[4:8])
|
|
width, height, rgb = region_pixels(shot, x0, y0, x1, y1)
|
|
head = struct.pack("<4sII4f", b"RGBZ", width, height, x0, y0, x1, y1)
|
|
with open(out, "wb") as fixture:
|
|
fixture.write(head + zlib.compress(rgb, 9))
|
|
return
|
|
if sys.argv[1] == "cmp":
|
|
shot, fixture_path = sys.argv[2], sys.argv[3]
|
|
with open(fixture_path, "rb") as fixture:
|
|
blob = fixture.read()
|
|
magic, width, height, x0, y0, x1, y1 = struct.unpack("<4sII4f", blob[:28])
|
|
if magic != b"RGBZ":
|
|
sys.exit(f"FAIL: {fixture_path} is not an RGBZ fixture")
|
|
shot_w, shot_h, _, _ = read_png(shot)
|
|
mine = region_at(shot, int(shot_w * x0), int(shot_h * y0), width, height)
|
|
theirs = zlib.decompress(blob[28:])
|
|
if len(mine) != len(theirs):
|
|
sys.exit(f"FAIL: {fixture_path} region is {len(theirs)} bytes, the shot's is {len(mine)}")
|
|
print(sum(a != b for a, b in zip(mine, theirs, strict=True)))
|
|
return
|
|
if sys.argv[1] == "hash":
|
|
path, x0, y0, x1, y1 = sys.argv[2], *map(float, sys.argv[3:7])
|
|
width, height, channels, raw = read_png(path)
|
|
pixels = unfilter(width, height, channels, raw)
|
|
cells = []
|
|
for gy in range(16):
|
|
for gx in range(16):
|
|
total = count = 0
|
|
for y in range(int(height * (y0 + (y1 - y0) * gy / 16)), int(height * (y0 + (y1 - y0) * (gy + 1) / 16))):
|
|
for x in range(int(width * (x0 + (x1 - x0) * gx / 16)), int(width * (x0 + (x1 - x0) * (gx + 1) / 16))):
|
|
at = (y * width + x) * channels
|
|
total += (pixels[at] + pixels[at + 1] + pixels[at + 2]) / 3
|
|
count += 1
|
|
cells.append(total / max(count, 1))
|
|
mean = sum(cells) / 256
|
|
bits = 0
|
|
for cell in cells:
|
|
bits = (bits << 1) | (cell >= mean)
|
|
print(f"{bits:064x}")
|
|
return
|
|
path, x0, y0, x1, y1 = sys.argv[1], *map(float, sys.argv[2:6])
|
|
width, height, channels, raw = read_png(path)
|
|
pixels = unfilter(width, height, channels, raw)
|
|
ink = 0
|
|
for y in range(int(height * y0), int(height * y1)):
|
|
for x in range(int(width * x0), int(width * x1)):
|
|
at = (y * width + x) * channels
|
|
if pixels[at] + pixels[at + 1] + pixels[at + 2] > 380:
|
|
ink += 1
|
|
print(ink)
|
|
|
|
|
|
main()
|