Sub-pixel movement and Anti-Aliased rotation
From Directorforum Collaboration Wiki
Moving sprites can be really ugly. Rotating sprites can be really ugly. Nice rotation and movement comes at a CPU cost. For some applications the speed of this code might be good enough, for others it will be too slow. 128x128 image takes 1 ms to be rotated and scaled down to 32x32. {on my computer} It does look nicer than doing the same with a sprite.
--------------------------------------------------------------------- -- Code by Arnold Wuis, 29-jan-2010, Director forum, Collab Wiki -- --------------------------------------------------------------------- Global BigBuffer on FinalSubPixel(HoekRAD, location, SourceImage, HalfBufferDim) -- The Code assumes a 4 times bigger source image than destination image aQuad=RotateRect(SourceImage.Rect, HoekRAD) --rotates the source image; returns a Quad with center at (0,0) -- Gets the Rect enveloping the Quad {center at (0,0)} QuadRect=getQuadRect(aQuad) -- Source Mod, Makes the Source image bigger if it is not a factor 4 ( 4x4 pixels are mapped back to 1x1 pixel ) ModX= QuadRect.width mod 4 > 1 ModY= QuadRect.height mod 4 > 1 -- Dest Mod , Source image can also have 2 pixels outlined resulting in a destination image that has 2 half pixels on each side. -- This is prevented by adding 2 half pixels in the destination (or better 2 pixel outline in Source image) DestModX=2*((QuadRect.width/4) mod 2) DestModY=2*((QuadRect.height/4) mod 2) -- Total Inflate, because the Quad and Rect have their center at 0,0 inflate will keep it that way -- combination of space needed for sub-pixel placement, the Source Modulo and the destination modulo QuadRect=QuadRect.Inflate(2+ModX+DestModX, 2+ModY,DestModY) -- Sub pixel offset -2...2, -2..2 ; aOffset=[integer( ( Location[1] -integer(location[1]) )*4), integer( ( Location[2] -integer(location[2]) )*4)] -- (every 4x4 pixel is a real pixel so by moving inside this you can get sub pixels) aOffset=aOffset+[HalfBufferDim, HalfBufferDim] --half of big buffer size will put the Quad in the center -- copy the rotated image tot the buffer with the center of the image at the center of the buffer BigBuffer.copyPixels(SourceImage, aQuad+[aOffset, aOffset, aOffset, aOffset] , SourceImage.Rect ) -- copy the size of the used buffer to the stage with a scale of 1/4 (the stage).image.copyPixels(BigBuffer, (QuadRect/4).offset(location[1], location[2]) , QuadRect.offset(HalfBufferDim, HalfBufferDim)) -- clean the buffer to be used again with the next image BigBuffer.fill(QuadRect.offset(HalfBufferDim, HalfBufferDim) , rgb(255,255,255) ) end on RotateRect(aRect, AngleRAD) -- center for Rect and for Quad result NewCenter=vector(aRect.Width/2, aRect.Height/2, 0) -- Vector(0,0,0)-NewCenter (leftTop van Rect=0,0) LeftTop=-NewCenter -- RightTop RightTop=Vector(aRect[3],0,0)-NewCenter --rotationMatrix cosAngle=cos(angleRAD) sinAngle=sin(AngleRAD) aQuad=[] -- newleftTop aQuad.add(point(LeftTop[1]*cosAngle-LeftTop[2]*sinAngle, LeftTop[1]*SinAngle+leftTop[2]*cosAngle)) -- newRightTop aQuad.add(point(RightTop[1]*cosAngle-RightTop[2]*sinAngle, RightTop[1]*SinAngle+RightTop[2]*cosAngle)) aQuad.add(-aQuad[1]) --RightBottom aQuad.add(-aQuad[2]) --LeftBottum -- Quad with center at (0,0) return aQuad end on GetQuadRect(aQuad) -- returns the outline Rect of a Quad MinimaleXwaarde=Min([ aQuad[1][1], aQuad[2][1], aQuad[3][1], aQuad[4][1] ]) MinimaleYwaarde=Min([ aQuad[1][2], aQuad[2][2], aQuad[3][2], aQuad[4][2] ]) MaximaleXwaarde=Max([ aQuad[1][1], aQuad[2][1], aQuad[3][1], aQuad[4][1] ]) MaximaleYwaarde=Max([ aQuad[1][2], aQuad[2][2], aQuad[3][2], aQuad[4][2] ]) return rect(minimaleXwaarde, MinimaleYwaarde, MaximaleXWaarde, maximaleYwaarde) end
