123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165(* Claude Code
*
* Copyright (C) 2026 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* (LGPL) as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*)(* See St_bitblt.mli *)moduleM=St_memorytypeoop=M.oopletcount=ref0letchanges()=!count(* a Form's bits, width, height, and bytes per row *)typeform={bits:Bytes.t;w:int;h:int;stride:int}letint_field(m:M.t)(o:oop)(i:int):intoption=letv=M.fetchmoiinifM.is_intvthenSome(M.int_ofv)elsematchM.bodymvwithM.Floatf->Some(truncate(Float.roundf))|_->Noneletget_form(m:M.t)(o:oop):formoption=ifM.is_into||o=M.nil||M.sizemo<3thenNoneelsematch(M.bodym(M.fetchmo0),int_fieldmo1,int_fieldmo2)with|M.Bytesbits,Somew,Someh->letstride=(w+15)/16*2inifw>=0&&h>=0&&Bytes.lengthbits>=stride*hthenSome{bits;w;h;stride}elseNone|_->Noneletpixel(f:form)(x:int)(y:int):int=ifx<0||y<0||x>=f.w||y>=f.hthen0else(Char.code(Bytes.getf.bits((y*f.stride)+(xlsr3)))lsr(7-(xland7)))land1letset_pixel(f:form)(x:int)(y:int)(v:int):unit=leti=(y*f.stride)+(xlsr3)inletbit=1lsl(7-(xland7))inletc=Char.code(Bytes.getf.bitsi)inBytes.setf.bitsi(Char.chr(ifv=1thenclorbitelseclandlnotbitland255))(*****************************************************************************)(* A pixel at a time *)(*****************************************************************************)(* the definition, as St_bitblt.mli states it: each pixel of the
* rectangle read, combined, written. What [blit] must equal. *)letblit_pixels~(dest:form)~(source:formoption)~(halftone:formoption)~(rule:int)~(dx:int)~(dy:int)~(sx:int)~(sy:int)((x0,y0,x1,y1):int*int*int*int):unit=fory=y0toy1-1doforx=x0tox1-1dolets=matchsourcewithNone->1|Somef->pixelf(sx+x-dx)(sy+y-dy)inlets=matchhalftonewithNone->s|Somef->slandpixelf(xland15)(yland15)inletd=pixeldestxyinset_pixeldestxy((rulelsr(3-((2*s)+d)))land1)donedone(*****************************************************************************)(* A byte at a time *)(*****************************************************************************)(* claude: the same, eight pixels at once: the rule is a function of
* two bits, so it is a function of two bytes, bit by bit, done by the
* machine's and, or and not. What is left is to line the source's bits
* up with the destination's bytes -- the source starts anywhere, so a
* destination byte's eight source bits come from two source bytes,
* shifted -- and to keep the destination's bits outside the rectangle,
* with a mask at each end of a row:
*
* destination |........|........|........| bytes
* rectangle xxxx xxxxxxxx xx
* masks 00001111 11111111 11000000
* source |........|........|........| r bits to the left
*
* St_bench times the two ("blit"). *)(* byte i of row y: 0 outside the Form, the row's padding cleared *)letbyte_at(f:form)(y:int)(i:int):int=ify<0||y>=f.h||i<0||i*8>=f.wthen0elseletc=Char.code(Bytes.getf.bits((y*f.stride)+i))inif(i+1)*8<=f.wthencelsecland(0xFF00lsr(f.wland7))land255(* the eight pixels of row y from x on, x anywhere *)letbits8(f:form)(y:int)(x:int):int=leti=xasr3andr=xland7inifr=0thenbyte_atfyielse((byte_atfyilslr)lor(byte_atfy(i+1)lsr(8-r)))land255letblit_bytes~(dest:form)~(source:formoption)~(halftone:formoption)~(rule:int)~(dx:int)~(dy:int)~(sx:int)~(sy:int)((x0,y0,x1,y1):int*int*int*int):unit=(* the rule's four cases, each all ones or none *)letonbit=ifrulelandbit<>0then255else0inletr00=on8andr01=on4andr10=on2andr11=on1in(* a fill -- no source, no halftone, a rule that does not look at the
* destination (0 white, 15 black) -- writes the same byte everywhere
* but at a row's two ends: the middle of a row at once *)letfill=(match(source,halftone)withNone,None->true|_->false)&&r10=r11inifx1>x0thenfory=y0toy1-1doletfirst=x0lsr3andlast=(x1-1)lsr3inletmiddle=fill&&last-first>=2inifmiddlethenBytes.filldest.bits((y*dest.stride)+first+1)(last-first-1)(Char.chrr11);fori=firsttolastdoifnot(middle&&i>first&&i<last)thenbeginletleft=i*8inletmask=(ifleft<x0then255lsr(x0-left)else255)landifleft+8>x1then(0xFF00lsr(x1-left))land255else255inlets=matchsourcewithNone->255|Somef->bits8f(sy+y-dy)(sx+left-dx)inlets=matchhalftonewithNone->s|Somef->slandbyte_atf(yland15)(iland1)inletat=(y*dest.stride)+iinletd=Char.code(Bytes.getdest.bitsat)inletns=lnotsandnd=lnotdinletr=(r00landnslandnd)lor(r01landnslandd)lor(r10landslandnd)lor(r11landslandd)inBytes.setdest.bitsat(Char.chr((dlandlnotmask)lor(rlandmask)land255))enddonedoneletblit?(simple=false)~(dest:form)~(source:formoption)~(halftone:formoption)~(rule:int)~(dx:int)~(dy:int)~(sx:int)~(sy:int)(area:int*int*int*int):unit=(* a Form copied onto itself (scrolling): from a copy of it, so that
* no pixel is read after it was written (Ingalls chose the direction
* to copy in instead: an exercise) *)letsource=matchsourcewithSomefwhenf.bits==dest.bits->Some{fwithbits=Bytes.copyf.bits}|s->sin(ifsimplethenblit_pixelselseblit_bytes)~dest~source~halftone~rule~dx~dy~sx~syarea(*****************************************************************************)(* The primitive *)(*****************************************************************************)letcopy_bits(m:M.t)(bb:oop):bool=letfieldi=int_fieldmbbiinmatch(get_formm(M.fetchmbb0),(field3,field4,field5,field6,field7),(field8,field9,field10,field11,field12,field13))with|Somedest,(Somerule,Somedx,Somedy,Somew,Someh),(sx,sy,cx,cy,cw,ch)whenrule>=0&&rule<16->letsource=M.fetchmbb1andhalftone=M.fetchmbb2inletsource=ifsource=M.nilthenNoneelseget_formmsourceinlethalftone=ifhalftone=M.nilthenNoneelseget_formmhalftoneinletsx=Option.valuesx~default:0andsy=Option.valuesy~default:0inletcx=Option.valuecx~default:0andcy=Option.valuecy~default:0inletcw=Option.valuecw~default:dest.wandch=Option.valuech~default:dest.hin(* the destination's rectangle clipped: to the clipping rectangle,
* then to the Form *)letx0=maxdx(maxcx0)andy0=maxdy(maxcy0)inletx1=min(dx+w)(min(cx+cw)dest.w)andy1=min(dy+h)(min(cy+ch)dest.h)inblit~dest~source~halftone~rule~dx~dy~sx~sy(x0,y0,x1,y1);incrcount;true|_->falseletform(m:M.t)(o:oop):(int*int*(int->int->bool))option=matchget_formmowithSomef->Some(f.w,f.h,funxy->pixelfxy=1)|None->None