"""Rebuild gpe-appmgr's desktop-background.png for the netBook Pro. The genuine file shipped inside Psion's gpe-appmgr CVS module (tag GPE_APPMGR_56) and did not survive; it is in neither the OE tree nor the upstream GPE mirror. What does survive is the artwork it was drawn from: the five Psion "blockletter" glyphs in psion-session's splash_screen.png, which are the same glyphs the fss_88 photograph shows watermarked down the left edge of the desktop. So the glyphs here are Psion's own pixels, taken from Psion's own file; only the layout is reconstructed. """ from PIL import Image, ImageFilter import sys SPLASH = '/Users/Joshua/Downloads/PsionLinux-netBookPro/PsionLX-UI-photos/psion-splash_screen.png' # gpe-appmgr reads DEFAULT_BACKGROUND, which is # /usr/share/pixmaps/gpe/default/gpe-appmgr/desktop-background.png # NOT /usr/share/gpe/desktop-background.png, where a second unused copy of # the same file also sits. Write every path given so the two cannot drift -- # regenerating only the /usr/share/gpe one leaves the desktop unchanged and # looks exactly like the change having had no effect. OUT = sys.argv[1] ALSO = sys.argv[2:] W, H = 800, 600 # The five dark blocks of the PSION logo, and the band holding the white # glyph inside them (the yellow P/S/I/O/N letters sit below, and are not # part of the watermark). BLOCKS = [(215, 265), (270, 320), (325, 358), (363, 416), (422, 472)] GLYPH_TOP, GLYPH_BOT = 204, 237 splash = Image.open(SPLASH).convert('RGB') glyphs = [] for x0, x1 in BLOCKS: tile = splash.crop((x0, GLYPH_TOP, x1 + 1, GLYPH_BOT)) # White glyph on dark block -> 1-bit mask. mask = tile.convert('L').point(lambda v: 255 if v > 170 else 0, mode='1') glyphs.append(mask.convert('L')) # Lay the glyphs down the left edge, reading top to bottom, as the # photograph shows them: each rotated a quarter turn. COL_X, COL_W = 6, 70 # The column sits higher than it used to: measured against the photographs # the glyphs start near the top of the screen and finish well clear of the # 48px dock, rather than running down into it. TOP, GAP = 22, 22 BOTTOM_CLEAR = 96 # keep this much space above the dock scaled = [] for g in glyphs: r = g.rotate(-90, expand=True) w, h = r.size s = COL_W / w scaled.append(r.resize((COL_W, max(1, int(h * s))), Image.LANCZOS)) total = sum(s.size[1] for s in scaled) + GAP * (len(scaled) - 1) avail = H - TOP - BOTTOM_CLEAR if total > avail: # keep the column on screen k = avail / total scaled = [s.resize((int(s.size[0] * k), int(s.size[1] * k)), Image.LANCZOS) for s in scaled] total = sum(s.size[1] for s in scaled) + GAP * (len(scaled) - 1) # The desktop is not flat white: the photographs show the left-hand edge # carrying a pale blue wash that fades out by about a quarter of the way # across. Measured off the fss_88 photograph, where the background reads # #d7dbd5 at 2% of the width against a #ebeae1 mid-screen reference. EDGE = (223, 231, 241) # pale blue at the extreme left FIELD = (255, 255, 255) # white by the time the wash has faded # The wash covers the WHOLE width. It was 0.26, measured off fss_88 by # comparing 2% of the width against a mid-screen reference -- but that only # established where the wash is strongest, not where it ends. In the Pulster # frame the blue runs the full width: taking B/R to cancel the camera's warm # cast, it falls smoothly from 1.000 at the left edge to 0.916 at the right # with no plateau. Fading out by x=208 left five sixths of the desktop flat # white, which is why the program-group boxes sitting on it did not pick up # the blue they have in the photographs. FADE = 1.0 bg = Image.new('RGB', (W, H)) px = bg.load() for x in range(W): t = min(1.0, x / (W * FADE)) t = t * t * (3 - 2 * t) # ease, so there is no seam col = tuple(int(EDGE[i] + (FIELD[i] - EDGE[i]) * t) for i in range(3)) for y in range(H): px[x, y] = col INK = (168, 194, 214) # pale blue, as the photograph shows it y = TOP for s in scaled: # The watermark is drawn as an outline, not a solid: take the # morphological gradient of the glyph mask. solid = s.point(lambda v: 255 if v > 110 else 0) outline = solid.filter(ImageFilter.MaxFilter(5)).point(lambda v: 255 if v > 110 else 0) ring = Image.eval(outline, lambda v: v) ring = Image.composite(Image.new('L', s.size, 0), ring, solid) tint = Image.new('RGB', s.size, INK) bg.paste(tint, (COL_X, y), ring) y += s.size[1] + GAP bg.save(OUT) for a in ALSO: bg.save(a) print(f' wrote {a}') print(f' wrote {OUT} {bg.size[0]}x{bg.size[1]} column {COL_X}..{COL_X+COL_W}, y {TOP}..{y-GAP}')