# SPDX-License-Identifier: GPL-3.0-only
# Green Bean AI: reproducible CPU reference images for lesson 08.
# Formula reference: Comfy-Org/ComfyUI commit 0df64eb242b7c5759c3e86afd5d1846d923b1033,
# comfy_extras/nodes_mask.py. This is not a ComfyUI plugin or a GPU test.
# The official background and derived compositions are distributed under GPL-3.0.
# The separately supplied corner-label.png is an original CC0 asset.
# Dependencies: Python 3, Pillow, NumPy. No network access, model or shell commands.
# Example: python reference-images.py --font /path/to/NotoSansCJK-Regular.ttc --font-index 3
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
import numpy as np
import argparse
parser=argparse.ArgumentParser()
parser.add_argument('--background',default='official-background.jpg')
parser.add_argument('--label',default='corner-label.png')
parser.add_argument('--output',default='reference-results')
parser.add_argument('--font',required=True,help='Local Noto Sans CJK Regular font file')
parser.add_argument('--font-index',type=int,default=3,help='TTC face index; 3 is the TC face in the reference environment')
args=parser.parse_args()
p=Path(args.output);p.mkdir(parents=True,exist_ok=True)
def font(n):return ImageFont.truetype(args.font,n,index=args.font_index)
bg=Image.open(args.background).convert('RGB');badge=Image.open(args.label).convert('RGBA')
if bg.size!=(512,512) or badge.size!=(180,72):raise ValueError('This reference renderer expects the lesson background512x512 and label180x72')
dst=np.array(bg,dtype=np.float32)/255.0;rgba=np.array(badge,dtype=np.float32)/255.0
src=rgba[:,:,:3];alpha=rgba[:,:,3];load_mask=1.0-alpha;correct_mask=1.0-load_mask;x,y=308,416
def compose(mask):
 out=dst.copy();m=np.ones_like(alpha) if mask is None else mask
 out[y:y+72,x:x+180]=m[:,:,None]*src+(1-m[:,:,None])*out[y:y+72,x:x+180]
 return np.clip(255.0*out,0,255).astype(np.uint8)
outs={'no-mask':compose(None),'wrong-mask':compose(load_mask),'correct-mask':compose(correct_mask)}
for name,arr in outs.items():Image.fromarray(arr).save(p/(name+'.png'))
# Actual CPU reference outputs shown side-by-side. Labels distinguish from product screenshots.
can=Image.new('RGB',(1536,640),'#fafbf7');q=ImageDraw.Draw(can)
for n,(name,title) in enumerate([('no-mask','A 未接 MASK'),('wrong-mask','B 直接接 Load Image MASK'),('correct-mask','C 經 Invert Mask 反轉')]):
 q.text((n*512+18,12),title,font=font(26),fill='#173e2b');can.paste(Image.fromarray(outs[name]),(n*512,70))
q.text((22,598),'CPU 合成參考輸出・官方素材＋原創角標，非 ComfyUI GUI／GPU 實跑',font=font(26),fill='#51675b');can.save(p/'overlay-comparison.png')
# Enlarged local crop shows anti-aliased edges and source transparency behavior.
can=Image.new('RGB',(1440,470),'#fafbf7');q=ImageDraw.Draw(can)
for n,(name,title) in enumerate([('no-mask','未接：透明區的 RGB 也貼上'),('wrong-mask','未反轉：主體淡掉、角落保留'),('correct-mask','反轉後：圓角融入背景')]):
 q.text((n*480+12,18),title,font=font(23),fill='#173e2b');crop=Image.fromarray(outs[name]).crop((290,395,506,506)).resize((432,222),Image.Resampling.NEAREST);can.paste(crop,(n*480+24,87))
q.text((24,365),'同一來源、同一位置，只改遮罩接法。局部放大用 nearest 顯示，非銳化或 AI 增強。',font=font(25),fill='#51675b');q.text((24,409),'CPU 數學參考輸出，非產品截圖；不同解碼／版本可能有細微像素差異。',font=font(24),fill='#51675b');can.save(p/'overlay-edge-detail.png')
# Checkerboard only in a DISPLAY preview, never in downloadable label bytes.
can=Image.new('RGB',(1100,470),'#fafbf7');q=ImageDraw.Draw(can)
q.text((24,16),'透明角標與遮罩方向',font=font(36),fill='#173e2b')
for k2,title in enumerate(['PNG alpha','Load Image MASK = 1−alpha','Invert Mask 後 = alpha']):
 xx=24+k2*360;q.text((xx,90),title,font=font(22),fill='#20372b')
 if k2==0:
  board=Image.new('RGBA',(324,130));bq=ImageDraw.Draw(board)
  for yy in range(0,130,13):
   for xxx in range(0,324,13):bq.rectangle((xxx,yy,xxx+12,yy+12),fill=('#d8ded7' if (xxx//13+yy//13)%2 else '#fff'))
  board.alpha_composite(badge.resize((324,130)),(0,0));tile=board.convert('RGB')
 else:
  v=load_mask if k2==1 else correct_mask;tile=Image.fromarray((v*255).astype('uint8')).convert('RGB').resize((324,130))
 can.paste(tile,(xx,145))
q.text((24,310),'Image Composite Masked：白色較採用 source，黑色保留 destination',font=font(26),fill='#20372b');q.text((24,360),'灰階代表部分混合；這不是所有遮罩節點都通用的「白色意思」。',font=font(25),fill='#51675b');q.text((24,410),'原創素材與 CPU 遮罩視覺化。棋盤格只作顯示，下載 PNG 仍是真透明。',font=font(23),fill='#51675b');can.save(p/'mask-direction.png')
