Adjustable clipping for pixelrage
This is an experimental clipping routine for pixelrage I wrote long ago. The idea being to have standard helper functions that are universal and that can be used anywhere, in this case it applies as much to pixelrage as it does TBitmap.
When copying graphics (blitting) to a surface that has a cliprect installed, there are several steps that needs to be taken (pixelrage takes care of these things for you naturally):
- If the source rectangle is larger or overlaps the cliprect, it must be adjusted
- the difference between source x1,y1 and target x1,y1 must be added to x1,y1
- the difference between source x2,y2 and target x2,y2 must be subtracted from source
- Once correctly adjusted, the input and output codec’s must be pre-fetched
- Copy over each scanline using codec->read to codec->write
When stretching however, the ordeal becomes somewhat different. You still have to adjust the source and target regions, but if the source is smaller than the target your copy routine must be smart enough to apply a fixed math scale factor (which basically means, some pixels will be read more than once in order for scaling to occur).
And clipping for all
I never really got to finish this one, so I release it into the wild (together with pixelrage and byterage). I use these libraries in various older products but I don’t mind sharing and giving back to the community. Hopefully people can re-cycle some of my work, or perhaps use it as inspiration for something better.
Function StretchClip(var srcRect:TRect;var targetRect:TRect; targetClip:TRect):Boolean; var srcWD: Integer; srcHD: Integer; dstWD: Integer; dstHD: Integer; clpWD: Integer; clpHD: Integer; xOff: Integer; yOff: Integer; Begin result:=False; srcWD:=srcRect.right-srcrect.left; srcHD:=srcRect.bottom-srcRect.Top; if (srcWD>0) and (srcHD>0) then begin dstWD:=targetRect.right-targetRect.left; dstHD:=targetRect.bottom-targetRect.Top; if (dstWD>0) and (dstHD>0) then begin clpWD:=targetClip.right-targetClip.left; clpHD:=targetClip.bottom-targetClip.Top; if (clpWD>0) and (clpHD>0) then Begin inc(srcWD); inc(srcHD); inc(dstWD); inc(dstHD); inc(clpWD); inc(clpHD); (* Check of target is within extreme Right and left of clip *) if (targetRect.left<targetClip.Right) and (targetRect.top<targetClip.Bottom) then Begin (* Clip LEFT *) if targetrect.Left<targetclip.Left then Begin (* X Offset by how much? *) xOff:=abs(targetclip.Left - targetrect.Left); (* Is it off by less than the whole width? *) if xoff<srcWD then Begin srcRect.Left:=srcRect.left + xoff; targetRect.Left:=targetclip.Left; dec(dstWD,xoff); dec(srcWD,xoff); end else exit; end; (* CLIP TOP *) if targetrect.Top<targetclip.top then Begin (* Y offset by how much? *) yOff:=abs(targetclip.Top - targetrect.Top); (* is it off by less than the whole height? *) if yOff<srcHD then Begin srcRect.top:=srcRect.top + yOff; targetRect.top:=targetClip.Top; dec(dstHD,yoff); dec(srcHD,yoff); end else exit; end; (* CLIP RIGHT *) if (targetrect.right > targetClip.right) then Begin xOff:=abs(targetrect.right - targetClip.right); if xoff<srcWD then Begin srcRect.Right := srcRect.Right - xoff; targetRect.right:=targetClip.Right; dec(dstWD,xoff); dec(srcWD,xoff); end else exit; end; (* CLIP BOTTOM *) if (targetrect.bottom > targetClip.bottom) then Begin yOff:=abs(targetrect.bottom - targetClip.bottom); if yoff<srcHD then Begin srcRect.bottom := srcRect.bottom - yOff; targetRect.bottom:=targetClip.bottom; dec(dstHD,yOff); dec(srcHD,yOff); end else exit; end; (* Final verdict *) result:=(srcWD>0) and (srcHD>0) and (dstWD>0) and (dstHD>0) and (clpWD>0) and (clpHD>0); end; end; end; end; end;
When we apply our clipper on two unsuspecting rectangles passing by my lab on their way to a square dance, we see that it calculates the correct offset’s and adjusts the buffer values as expected. Now you can feed the adjusted source and target rectangles to your blitter without having to test each scanline or accidentally writing beyond the memory buffer.
Pixels are fun 🙂
procedure TfrmTest.testClip; var srcRect, dstRect,Clip:TRect; begin Clip:=Rect(10,10,109,109); srcRect:=Rect(10,10,60,60); dstRect:=Rect(0,0,115,40); DrawOriginalCords(srcRect,dstRect,clip); if StretchClip(srcRect,dstRect,Clip) then Begin LogToMemo('Clip successfull:'); LogtoMemo('Source = ' + pxlrage.TPXRRect.toString(srcRect)); LogtoMemo('Target = ' + pxlrage.TPXRRect.toString(dstRect)); DrawClippedCords(srcRect,dstRect,clip); end else logToMemo('Clipping resulted in no activity'); end;