local M={} local P={} do local greys={0x0F0F0F,0x1E1E1E,0x2D2D2D,0x3C3C3C,0x4B4B4B,0x5A5A5A,0x696969,0x787878,0x878787,0x969696,0xA5A5A5, 0xB4B4B4,0xC3C3C3,0xD2D2D2,0xE1E1E1,0xF0F0F0} local gi,n=1,0 for r=0,5 do for g=0,7 do for b=0,4 do P[n]=(r*51)<<16|((g*255+3)//7)<<8|((b*255+2)//4) n=n+1 end end if r<5 then for _=1,(r==2 and 4 or 3)do if greys[gi]then P[n]=greys[gi];gi=gi+1;n=n+1 end end end end end local cache={} function M.load(path,nocache) local c=not nocache and cache[path] if c then return c end local f=io.open(path,"rb") if not f then return nil,"cannot open "..path end local d=f:read("*a") f:close() if not d or d:sub(1,4)~="OCIF"then return nil,"not an OCIF image"end local pos=5 local byte,codepoint=string.byte,utf8.codepoint local function b1()local v=byte(d,pos)pos=pos+1 return v end local function b2()local a,b=byte(d,pos,pos+1)pos=pos+2 return a<<8|b end local function uc() local c0=byte(d,pos) local n=c0>=0xF0 and 4 or c0>=0xE0 and 3 or c0>=0xC0 and 2 or 1 local cp=codepoint(d,pos) pos=pos+n return cp end local m=b1() local w,h,cells=0,0,{} if m==5 then w,h=b2(),b2() for i=1,w*h do local bg,fg,a=P[b1()],P[b1()],b1() local k=i*3 cells[k-2],cells[k-1],cells[k]=uc(),fg,a<128 and bg or-1 end elseif m>=6 and m<=8 then local is7,is8=m>=7 and 1 or 0,m>=8 and 1 or 0 w,h=b1()+is8,b1()+is8 for i=1,w*h do cells[i*3-2],cells[i*3-1],cells[i*3]=32,0,-1 end for _=1,b1()+is7 do local a=b1() for _=1,b2()+is7 do local ch=uc() for _=1,b1()+is7 do local bg=P[b1()] for _=1,b1()+is7 do local fg=P[b1()] for _=1,b1()+is7 do local y=b1()+is8 for _=1,b1()+is7 do local x=b1()+is8 local k=((y-1)*w+x)*3 cells[k-2],cells[k-1],cells[k]=ch,fg,a<128 and bg or-1 end end end end end end else return nil,"unsupported OCIF encoding "..tostring(m) end c={w,h,cells} if not nocache then cache[path]=c end return c end local near={} local function to8(c) local i=near[c] if i then return i end local best,bd=0,math.huge for k=0,255 do local p=P[k] local d=((p>>16)-(c>>16))^2+(((p>>8)&255)-((c>>8)&255))^2+((p&255)-(c&255))^2 if d>8,w&255,h>>8,h&255)} for i=1,w*h do local k=i*3 local bg=d[k] out[#out+1]=string.char(to8(bg>=0 and bg or 0),to8(d[k-1]),bg>=0 and 0 or 255)..utf8.char(d[k-2]) end local f,err=io.open(path,"wb") if not f then return nil,err end f:write(table.concat(out)) f:close() cache[path]=nil return true end function M.forget(path)cache[path]=nil end return M