I have added two new drawing methods. drawTile and drawScaledToSize, both available as block and masked versions. The first one drawTile will draw an image buffer into another image buffer tiled between x,y,width,height and at an offset so scrolling is possible. The second one drawScaledToSize is an alternative to the drawScaled and the difference is that drawScaled uses floats to decide how the image is scaled, where the drawScaledToSize will draw an image scaled between x,y,width,height. I have also added the circle and oval that JumpMan wrote. A couple of methods has changed names to make things more uniform, but that should be easy to find out.
' The following code is public domain, use it as you like, in parts or as it is. Feel free to modify as you see fit.
' No warranties are provided for the code.
' All code written by Johnny Bremer (aka. Zawran), unless otherwise mentioned
Type zImageBuffer
Global zImageBufferList:TList = CreateList()
Field link:TLink ' link pointer
Field buffer:TPixmap ' buffer where pixmap is stored
Field width:Int ' width of image
Field height:Int ' height of image
Field pitch:Int ' pitch of image
Field bcount:Int ' byte count of each pixel 3/4 depending on alpha
Field mask:Int[3] ' masking color array
' create a new image buffer
Function make:zImageBuffer(width:Int,height:Int,maskrgb:Int=$000000,fillrgb:Int=$FF000000)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = CreatePixmap(width,height,PF_RGBA8888)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask[0] = (maskrgb Shr 16) & 255
tmp.mask[1] = (maskrgb Shr 8) & 255
tmp.mask[2] = maskrgb & 255
ClearPixels(tmp.buffer,fillrgb)
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function
' load an image and create an image buffer from that
Function loadFile:zImageBuffer(path:String,maskrgb:Int=$000000)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = LoadPixmap(path)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask[0] = (maskrgb Shr 16) & 255
tmp.mask[1] = (maskrgb Shr 8) & 255
tmp.mask[2] = maskrgb & 255
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function
' copy an already existing image buffer
Function copy:zImageBuffer(from:zImageBuffer)
Local tmp:zImageBuffer = New zImageBuffer
tmp.buffer = CopyPixmap(from.buffer)
tmp.width = tmp.buffer.width
tmp.height = tmp.buffer.height
tmp.pitch = PixmapPitch(tmp.buffer)
tmp.bcount = tmp.pitch / tmp.width
tmp.mask = from.mask
tmp.link = ListAddLast(zImageBufferList,tmp)
Return tmp
End Function
' make a copy of the image buffer which is rotated at an angle
Function rotateCopy:zImageBuffer(from:zImageBuffer,angle:Int)
Local tmp:zImagebuffer = zImageBuffer.copy(from)
tmp.clear()
Local r:Byte,g:Byte,b:Byte,a:Byte
Local wh:Int = (tmp.width/2)-1
Local hh:Int = (tmp.height/2)-1
For Local y:Int = 0 Until tmp.height
For Local x:Int = 0 Until tmp.width
Local tx:Int = (x-wh) * Cos(angle) - (y-hh) * Sin(angle) + wh
Local ty:Int = (y-hh) * Cos(angle) + (x-wh) * Sin(angle) + hh
If tx > -1 And tx < tmp.width And ty > -1 And ty < tmp.height Then
from.getpixel(tx,ty,r,g,b,a)
tmp.setpixel(x,y,r,g,b,a)
End If
Next
Next
Return tmp
End Function
' clear an image buffer with a set RGBA color
Method clear(rgba:Int=$FF000000)
ClearPixels(Self.buffer,rgba)
End Method
' save an image buffer to file
Method saveImage(path:String="image.png",comp:Int=5)
SavePixmapPNG(Self.buffer,path,comp)
End Method
' draw an image buffer to screen
Method drawToScreen(x:Int=0,y:Int=0)
DrawPixmap(Self.buffer,x,y)
End Method
' remove an image buffer
Method remove()
Self.buffer = Null
RemoveLink(Self.link)
End Method
' set the masking color of the image buffer
Method setMask(maskrgb:Int=$000000)
Self.mask[0] = (maskrgb Shr 16) & 255
Self.mask[1] = (maskrgb Shr 8) & 255
Self.mask[2] = maskrgb & 255
End Method
' set the color of a pixel in the image buffer
Method setPixel(x:Int,y:Int,r:Byte,g:Byte,b:Byte,a:Byte=255)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
If x > -1 And x < Self.width And y > -1 And y < Self.height Then
Local offset:Int = y*Self.pitch+x*Self.bcount
pixPtr[offset] = r
pixPtr[offset+1] = g
pixPtr[offset+2] = b
If Self.bcount = 4 Then pixPtr[offset+3] = a
End If
End Method
' get the color of a pixel in the image buffer
Method getPixel(x:Int,y:Int,r:Byte Var,g:Byte Var,b:Byte Var,a:Byte Var)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
If x > -1 And x < Self.width And y > -1 And y < Self.height Then
Local offset:Int = y*Self.pitch+x*Self.bcount
r = pixPtr[offset]
g = pixPtr[offset+1]
b = pixPtr[offset+2]
If Self.bcount = 4 Then a = pixPtr[offset+3]
End If
End Method
' draw a line into the image buffer using a specific color
Method line(x1:Float,y1:Float,x2:Float,y2:Float,r:Byte,g:Byte,b:Byte,a:Byte=255)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local steps:Float,xI:Float,i:Int
x2 :- x1
y2 :- y1
If Abs(x2) > Abs(y2) Then
steps = Abs(x2)
Else
steps = Abs(y2)
End If
xI = Float(x2 / steps)
y2 = Float(y2 / steps)
For x2 = 0 To steps
If Int(y1) > -1 And Int(y1) < Self.height Then
If Int(x1) > -1 And Int(x1) < Self.width Then
Local offset:Int = Int(y1)*Self.pitch+Int(x1)*Self.bcount
pixPtr[offset] = r
pixPtr[offset+1] = g
pixPtr[offset+2] = b
If Self.bcount = 4 Then pixPtr[offset+3] = a
End If
End If
x1 :+ xI
y1 :+ y2
Next
End Method
' draw a rect into the image buffer using a specific color
Method rect(x1:Int,y1:Int,x2:Int,y2:Int,r:Byte,g:Byte,b:Byte)
For Local i:Int = y1 To y2
Self.line(x1,i,x2,i,r,g,b)
Next
End Method
' draw an oval into the image buffer using a specific color (written by Jumpman from the DBF-Interactive forum)
Method oval(cx:Int,cy:Int,width:Int,height:Int,r:Byte,g:Byte,b:Byte,a:Byte=255,filled:Int = True)
Local Rx:Int,Ry:Int
Local p:Int,px:Int,py:Int,x:Int,y:Int
Local Rx2:Int,Ry2:Int,twoRx2:Int,twoRy2:Int
Local pFloat:Float
Rx=Abs(width Shr 1)
Ry=Abs(height Shr 1)
cx :+rx
cy :+ry
Rx2=Rx*Rx
Ry2=Ry*Ry
twoRx2=Rx2 Shl 1
twoRy2=Ry2 Shl 1
'Region 1
x=0
y=Ry
If filled Then
line cx+x,cy+y,cx-x,cy+y,r,g,b,a
line cx+x,cy-y,cx-x,cy-y,r,g,b,a
Else
setpixel cx+x,cy+y,r,g,b,a
setpixel cx-x,cy+y,r,g,b,a
setpixel cx+x,cy-y,r,g,b,a
setpixel cx-x,cy-y,r,g,b,a
EndIf
pFloat=(Ry2-(Rx2*Ry))+(0.25*Rx2)
p=Int(pFloat)
If pFloat Mod 1.0>=0.5 Then p:+1
px=0
py=twoRx2*y
While px<py
x:+1
px:+twoRy2
If p>=0
y:-1
py:-twoRx2
EndIf
If p<0 Then p:+Ry2+px Else p:+Ry2+px-py
If filled Then
Self.line cx+x,cy+y,cx-x,cy+y,r,g,b,a
Self.line cx+x,cy-y,cx-x,cy-y,r,g,b,a
Else
Self.setPixel cx+x,cy+y,r,g,b,a
Self.setPixel cx-x,cy+y,r,g,b,a
Self.setPixel cx+x,cy-y,r,g,b,a
Self.setPixel cx-x,cy-y,r,g,b,a
EndIf
Wend
'Region 2
pFloat=(Ry2*(x+0.5)*(x+0.5))+(Rx2*(y-1.0)*(y-1.0))-(Rx2*(Float(Ry2)))
p=Int(pFloat)
If pFloat Mod 1.0>=0.5 Then p:+1
While y>0
y:-1
py:-twoRx2
If p<=0
x:+1
px:+twoRy2
EndIf
If p>0 Then p:+Rx2-py Else p:+Rx2-py+px
If filled Then
Self.line cx+x,cy+y,cx-x,cy+y,r,g,b,a
Self.line cx+x,cy-y,cx-x,cy-y,r,g,b,a
Else
Self.setPixel cx+x,cy+y,r,g,b,a
Self.setPixel cx-x,cy+y,r,g,b,a
Self.setPixel cx+x,cy-y,r,g,b,a
Self.setPixel cx-x,cy-y,r,g,b,a
EndIf
Wend
End Method
' draw a circle into the image buffer with a specific color (written by Jumpman from the DBF-Interactive forum)
Method circle(cx:Int,cy:Int,radius:Int,r:Byte,g:Byte,b:Byte,a:Byte=255,filled:Int = True)
If (cx-radius) > Self.width Return
If (cy-radius) > Self.height Return
If (cx+radius) < 0 Then Return
If (cy+radius) < 0 Then Return
Local x:Int = 0
Local d:Int = (2*Radius)
Local y:Int=Radius
While x<y
If d < 0 Then
d = d + (4 * x) + 6
Else
d = d + 4 * (x - y) + 10
y = y - 1
End If
If filled Then
Self.line cx + X, cy + Y,cx + X, cy - Y,r,g,b,a
Self.line cx - X, cy + Y,cx - X, cy - Y,r,g,b,a
Self.line cx + Y, cy + X,cx + Y, cy - X,r,g,b,a
Self.line cx - Y, cy + X,cx - Y, cy - X,r,g,b,a
Else
Self.setPixel cx + X, cy + Y,r,g,b,a
Self.setPixel cx + X, cy - Y,r,g,b,a
Self.setPixel cx - X, cy + Y,r,g,b,a
Self.setPixel cx - X, cy - Y,r,g,b,a
Self.setPixel cx + Y, cy + X,r,g,b,a
Self.setPixel cx + Y, cy - X,r,g,b,a
Self.setPixel cx - Y, cy + X,r,g,b,a
Self.setPixel cx - Y, cy - X,r,g,b,a
EndIf
x=x+1
Wend
End Method
' draw an entire image buffer into another image buffer tiled at an offset and within a set rectangle ignoring masking
Method drawTileBlock(toImg:zImageBuffer,x:Int,y:Int,w:Int,h:Int,offsetX:Int=0,offsetY:Int=0)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
If offsetX < 0 Or offsetX => Self.width Then offsetX = 0 ' if offset is outside image, then use default
If offsetY < 0 Or offsetY => Self.height Then offsetY = 0 ' if offset is outside image, then use default
Local dx:Int = offsetX
Local dy:Int = offsetY
For Local ty:Int = 0 Until h
dx = offsetX
For Local tx:Int = 0 Until w
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
Local offsetS:Int = (dy*Self.pitch) + (dx*Self.bcount)
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
dx :+ 1
If dx = Self.width Then dx = 0
Next
dy :+ 1
If dy = Self.height Then dy = 0
Next
End Method
' draw an entire image buffer into another image buffer tiled at an offset and within a set rectangle using masking
Method drawTileMasked(toImg:zImageBuffer,x:Int,y:Int,w:Int,h:Int,offsetX:Int=0,offsetY:Int=0)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
If offsetX < 0 Or offsetX => Self.width Then offsetX = 0 ' if offset is outside image, then use default
If offsetY < 0 Or offsetY => Self.height Then offsetY = 0 ' if offset is outside image, then use default
Local dx:Int = offsetX
Local dy:Int = offsetY
For Local ty:Int = 0 Until h
dx = offsetX
For Local tx:Int = 0 Until w
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
If pixSourcePtr[dy*Self.pitch+dx*Self.bcount] <> Self.mask[0] Or pixSourcePtr[dy*Self.pitch+dx*Self.bcount+1] <> Self.mask[1] Or pixSourcePtr[dy*Self.pitch+dx*Self.bcount+2] <> Self.mask[2] Then
Local offsetS:Int = (dy*Self.pitch) + (dx*Self.bcount)
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
End If
dx :+ 1
If dx = Self.width Then dx = 0
Next
dy :+ 1
If dy = Self.height Then dy = 0
Next
End Method
' draw the entire image buffer into another image buffer ignoring masking
Method drawBlock(toImg:zImagebuffer,x:Int,y:Int)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
For Local ty:Int = 0 Until Self.height
For Local tx:Int = 0 Until Self.width
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
Local offsetS:Int = ty*Self.pitch+tx*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
Next
Next
End Method
' draw the entire image buffer into another image buffer using the masking color
Method drawMasked(toImg:zImageBuffer,x:Int,y:Int)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
For Local ty:Int = 0 Until Self.height
For Local tx:Int = 0 Until Self.width
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
If pixSourcePtr[ty*Self.pitch+tx*Self.bcount] <> Self.mask[0] Or pixSourcePtr[ty*Self.pitch+tx*Self.bcount+1] <> Self.mask[1] Or pixSourcePtr[ty*Self.pitch+tx*Self.bcount+2] <> Self.mask[2] Then
Local offsetS:Int = ty*Self.pitch+tx*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
End If
Next
Next
End Method
' draw the entire image buffer into another image buffer with additive effect
Method drawAdditiveMasked(toImg:zImageBuffer,x:Int,y:Int)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
Local r:Byte,g:Byte,b:Byte,a:Byte
For Local ty:Int = 0 Until Self.height
For Local tx:Int = 0 Until Self.width
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
If pixSourcePtr[ty*Self.pitch+tx*Self.bcount] <> Self.mask[0] Or pixSourcePtr[ty*Self.pitch+tx*Self.bcount+1] <> Self.mask[1] Or pixSourcePtr[ty*Self.pitch+tx*Self.bcount+2] <> Self.mask[2] Then
Local offsetS:Int = ty*Self.pitch+tx*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
r = pixSourcePtr[offsetS]
g = pixSourcePtr[offsetS+1]
b = pixSourcePtr[offsetS+2]
If Self.bcount = 4 Then a = pixSourcePtr[offsetS+3]
If r + pixDestPtr[offsetD] > 255 Then
r = 255
Else
r :+ pixDestPtr[offsetD]
End If
If g + pixDestPtr[offsetD+1] > 255 Then
g = 255
Else
g :+ pixDestPtr[offsetD+1]
End If
If b + pixDestPtr[offsetD+2] > 255 Then
b = 255
Else
b :+ pixDestPtr[offsetD+2]
End If
If Self.bcount = 4 Then
If a + pixDestPtr[offsetD+3] > 255 Then
a = 255
Else
a :+ pixDestPtr[offsetD+3]
End If
Else
a = 255
End If
pixDestPtr[offsetD] = r
pixDestPtr[offsetD+1] = g
pixDestPtr[offsetD+2] = b
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = a
End If
End If
Next
Next
End Method
' draw the entire image buffer into another image buffer scaled but ignoring masking
Method drawScaledBlock(toImg:zImageBuffer,x:Int,y:Int,scaleX:Float,scaleY:Float)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
Local sw:Int = Self.width * scaleX
Local sh:Int = Self.height * scaleY
Local deltaX:Float = Self.width / Float(sw)
Local deltaY:Float = Self.height / Float(sh)
Local cx:Float=0.0,cy:Float=0.0
For Local ty:Int = 0 Until sh
cx = 0.0
For Local tx:Int = 0 Until sw
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
Local offsetS:Int = Int(cy)*Self.pitch+Int(cx)*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
cx :+ deltaX
Next
cy :+ deltaY
Next
End Method
' draw the entire image buffer into another image buffer scaled using masking
Method drawScaledMasked(toImg:zImageBuffer,x:Int,y:Int,scaleX:Float,scaleY:Float)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
Local sw:Int = Self.width * scaleX
Local sh:Int = Self.height * scaleY
Local deltaX:Float = Self.width / Float(sw)
Local deltaY:Float = Self.height / Float(sh)
Local cx:Float=0.0,cy:Float=0.0
For Local ty:Int = 0 Until sh
cx = 0.0
For Local tx:Int = 0 Until sw
If tx+x > -1 And tx+x < toImg.width And ty+y > -1 And ty+y < toImg.height Then
If pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount] <> Self.mask[0] Or pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount+1] <> Self.mask[1] Or pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount+2] <> Self.mask[2] Then
Local offsetS:Int = Int(cy)*Self.pitch+Int(cx)*Self.bcount
Local offsetD:Int = (ty+y)*toImg.pitch+(tx+x)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
End If
cx :+ deltaX
Next
cy :+ deltaY
Next
End Method
' draw the entire image buffer into another image buffer scaled to size based on x/y coordinate set but ignoring masking
Method drawScaledToSizeBlock(toImg:zImageBuffer,x1:Int,y1:Int,x2:Int,y2:Int)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
Local sw:Int = x2-x1
Local sh:Int = y2-y1+1
Local deltaX:Float = Self.width / Float(sw)
Local deltaY:Float = Self.height / Float(sh)
Local cx:Float=0.0,cy:Float=0.0
For Local ty:Int = 0 Until sh
cx = 0.0
For Local tx:Int = 0 Until sw
If tx+x1 > -1 And tx+x1 < toImg.width And ty+y1 > -1 And ty+y1 < toImg.height Then
Local offsetS:Int = Int(cy)*Self.pitch+Int(cx)*Self.bcount
Local offsetD:Int = (ty+y1)*toImg.pitch+(tx+x1)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
cx :+ deltaX
Next
cy :+ deltaY
Next
End Method
' draw the entire image buffer into another image buffer scaled to size based on x/y coordinate set using masking
Method drawScaledToSizeMasked(toImg:zImageBuffer,x1:Int,y1:Int,x2:Int,y2:Int)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixDestPtr:Byte Ptr = PixmapPixelPtr(toImg.buffer)
Local sw:Int = x2-x1
Local sh:Int = y2-y1+1
Local deltaX:Float = Self.width / Float(sw)
Local deltaY:Float = Self.height / Float(sh)
Local cx:Float=0.0,cy:Float=0.0
For Local ty:Int = 0 Until sh
cx = 0.0
For Local tx:Int = 0 Until sw
If tx+x1 > -1 And tx+x1 < toImg.width And ty+y1 > -1 And ty+y1 < toImg.height Then
If pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount] <> Self.mask[0] Or pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount+1] <> Self.mask[1] Or pixSourcePtr[Int(cy)*Self.pitch+Int(cx)*Self.bcount+2] <> Self.mask[2] Then
Local offsetS:Int = Int(cy)*Self.pitch+Int(cx)*Self.bcount
Local offsetD:Int = (ty+y1)*toImg.pitch+(tx+x1)*toImg.bcount
pixDestPtr[offsetD] = pixSourcePtr[offsetS]
pixDestPtr[offsetD+1] = pixSourcePtr[offsetS+1]
pixDestPtr[offsetD+2] = pixSourcePtr[offsetS+2]
If Self.bcount = 4 And toImg.bcount = 4 Then pixDestPtr[offsetD+3] = pixSourcePtr[offsetS+3]
End If
End If
cx :+ deltaX
Next
cy :+ deltaY
Next
End Method
' copy a rectangle from one image buffer to another as a block ignoring masking
Method copyRectBlock(from:zImageBuffer,x:Int,y:Int,w:Int,h:Int,tx:Int,ty:Int)
Local pixTargetPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(from.buffer)
For Local yy:Int = 0 Until h
For Local xx:Int = 0 Until w
' is target pixel within the target buffer
If tx+xx > -1 And ty+yy > -1 And tx+xx < Self.width And ty+yy < Self.height Then
' is source pixel within the source buffer
If x+xx > -1 And y+yy > -1 And x+xx < from.width And y+yy < from.height Then
Local offsetT:Int = (ty+yy)*Self.pitch+(tx+xx)*Self.bcount
Local offsetS:Int = (y+yy)*from.pitch+(x+xx)*from.bcount
pixTargetPtr[offsetT] = pixSourcePtr[offsetS]
pixTargetPtr[offsetT+1] = pixSourcePtr[offsetS+1]
pixTargetPtr[offsetT+2] = pixSourcePtr[offsetS+2]
' check to see if both images include alpha value that needs to be copied as well
If Self.bcount = 4 And from.bcount = 4 Then pixTargetPtr[offsetT+3] = pixSourcePtr[offsetS+3]
End If
End If
Next
Next
End Method
' copy a rectangle from one image buffer to another using masking
Method copyRect(from:zImageBuffer,x:Int,y:Int,w:Int,h:Int,tx:Int,ty:Int)
Local pixTargetPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local pixSourcePtr:Byte Ptr = PixmapPixelPtr(from.buffer)
For Local yy:Int = 0 Until h
For Local xx:Int = 0 Until w
' is target pixel within the target buffer
If tx+xx > -1 And ty+yy > -1 And tx+xx < Self.width And ty+yy < Self.height Then
' is source pixel within the source buffer
If x+xx > -1 And y+yy > -1 And x+xx < from.width And y+yy < from.height Then
If pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount] <> from.mask[0] Or pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+1] <> from.mask[1] Or pixSourcePtr[(y+yy)*from.pitch+(x+xx)*from.bcount+2] <> from.mask[2] Then
Local offsetT:Int = (ty+yy)*Self.pitch+(tx+xx)*Self.bcount
Local offsetS:Int = (y+yy)*from.pitch+(x+xx)*from.bcount
pixTargetPtr[offsetT] = pixSourcePtr[offsetS]
pixTargetPtr[offsetT+1] = pixSourcePtr[offsetS+1]
pixTargetPtr[offsetT+2] = pixSourcePtr[offsetS+2]
' check to see if both images include alpha value that needs to be copied as well
If Self.bcount = 4 And from.bcount = 4 Then pixTargetPtr[offsetT+3] = pixSourcePtr[offsetS+3]
End If
End If
End If
Next
Next
End Method
' add noise effect to a rectangular area inside the image buffer
Method noise(x:Int,y:Int,width:Int,height:Int,noiselevel:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
Local n:Int = Rnd(-noiselevel,noiselevel)
If pixPtr[offset] + n < 0 Then
pixPtr[offset] = 0
Else
If pixPtr[offset] + n > 255 Then
pixPtr[offset] = 255
Else
pixPtr[offset] :+ n
End If
End If
If pixPtr[offset+1] + n < 0 Then
pixPtr[offset+1] = 0
Else
If pixPtr[offset+1] + n > 255 Then
pixPtr[offset+1] = 255
Else
pixPtr[offset+1] :+ n
End If
End If
If pixPtr[offset+2] + n < 0 Then
pixPtr[offset+2] = 0
Else
If pixPtr[offset+2] + n > 255 Then
pixPtr[offset+2] = 255
Else
pixPtr[offset+2] :+ n
End If
End If
End If
Next
Next
End Method
' subtract color value from the pixels of a rectangular area inside the image buffer
Method colorSubtract(x:Int,y:Int,width:Int,height:Int,r:Byte,g:Byte,b:Byte,a:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
If pixPtr[offset] - r < 0 Then
pixPtr[offset] = 0
Else
pixPtr[offset] = pixPtr[offset]-r
End If
If pixPtr[offset+1] - g < 0 Then
pixPtr[offset+1] = 0
Else
pixPtr[offset+1] = pixPtr[offset+1]-g
End If
If pixPtr[offset+2] - b < 0 Then
pixPtr[offset+2] = 0
Else
pixPtr[offset+2] = pixPtr[offset+2]-b
End If
If Self.bcount = 4 Then
If pixPtr[offset+3] - a < 0 Then
pixPtr[offset+3] = 0
Else
pixPtr[offset+3] = pixPtr[offset+3]-a
End If
End If
End If
Next
Next
End Method
' add color value to the pixels of a rectangular area inside the image buffer
Method colorAddition(x:Int,y:Int,width:Int,height:Int,r:Byte,g:Byte,b:Byte,a:Byte)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
For Local ty:Int = y To y+height-1
For Local tx:Int = x To x+width-1
If tx > -1 And tx < Self.width And ty > -1 And ty < Self.height Then
Local offset:Int = ty*Self.pitch+tx*Self.bcount
If r + pixPtr[offset] > 255 Then
pixPtr[offset] = 255
Else
pixPtr[offset] = pixPtr[offset]+r
End If
If g + pixPtr[offset+1] > 255 Then
pixPtr[offset+1] = 255
Else
pixPtr[offset+1] = pixPtr[offset+1]+g
End If
If b + pixPtr[offset+2] > 255 Then
pixPtr[offset+2] = 255
Else
pixPtr[offset+2] = pixPtr[offset+2]+b
End If
If Self.bcount = 4 Then
If a + pixPtr[offset+3] > 255 Then
pixPtr[offset+3] = 255
Else
pixPtr[offset+3] = pixPtr[offset+3]+a
End If
End If
End If
Next
Next
End Method
' mirror the entire image buffer in various directions
Method mirror(direction:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Local tmp:Byte,x:Int,y:Int
Select direction
Case 0 ' horizontal
For x = 0 To (Self.width/2)-1
For y = 0 Until Self.height
tmp = pixPtr[y*Self.pitch+x*Self.bcount]
pixPtr[y*Self.pitch+x*Self.bcount] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+1]
pixPtr[y*Self.pitch+x*Self.bcount+1] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+1]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+1] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+2]
pixPtr[y*Self.pitch+x*Self.bcount+2] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+2]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+2] = tmp
If Self.bcount = 4 Then
tmp = pixPtr[y*Self.pitch+x*Self.bcount+3]
pixPtr[y*Self.pitch+x*Self.bcount+3] = pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+3]
pixPtr[y*Self.pitch+(Self.width-x-1)*Self.bcount+3] = tmp
End If
Next
Next
Case 1 ' vertical
For y = 0 To (Self.height/2)-1
For x = 0 Until Self.width
tmp = pixPtr[y*Self.pitch+x*Self.bcount]
pixPtr[y*Self.pitch+x*Self.bcount] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+1]
pixPtr[y*Self.pitch+x*Self.bcount+1] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+1]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+1] = tmp
tmp = pixPtr[y*Self.pitch+x*Self.bcount+2]
pixPtr[y*Self.pitch+x*Self.bcount+2] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+2]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+2] = tmp
If Self.bcount = 4 Then
tmp = pixPtr[y*Self.pitch+x*Self.bcount+3]
pixPtr[y*Self.pitch+x*Self.bcount+3] = pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+3]
pixPtr[(Self.height-y-1)*Self.pitch+x*Self.bcount+3] = tmp
End If
Next
Next
Case 2 ' horizontal + vertical
Self.mirror(0)
Self.mirror(1)
End Select
End Method
' shift the entire content of an image buffer in one of four directions: 0=left, 1=right, 2=up, 3=down
Method shift(direction:Int,pixels:Int)
Local pixPtr:Byte Ptr = PixmapPixelPtr(Self.buffer)
Select direction
Case 0 ' left
Local moveSize:Int = (Self.width-pixels)*Self.bcount
Local offset:Int = pixels*Self.bcount
For Local y:Int = 0 Until Self.height
MemCopy(pixPtr,pixPtr+offset,moveSize)
pixPtr :+ pitch
Next
Case 1 ' right
Local pixSize:Int = pitch/Self.width
Local moveSize:Int = (Self.width-pixels)*Self.bcount
Local offset:Int = pixels*Self.bcount
For Local y:Int = 0 Until Self.height
MemCopy(pixPtr+offset,pixPtr,moveSize)
pixPtr :+ pitch
Next
Case 2 ' up
Local pixSize:Int = pitch/Self.width
For Local y:Int = 0 To Self.height-1-pixels
MemCopy(pixPtr,pixPtr+pitch*pixels,pitch)
pixPtr :+ pitch
Next
Case 3 ' down
Local pixSize:Int = pitch/Self.width
pixPtr :+ ((Self.width*Self.bcount)*(Self.height-1))
For Local y:Int = 0 To Self.height-1-pixels
MemCopy(pixPtr,pixPtr-(pitch*pixels),pitch)
pixPtr :- pitch
Next
End Select
End Method
End Type
Type zSpriteSheet
Global zSpriteSheetList:TList = CreateList()
Field link:TLink ' link pointer
Field image:zImageBuffer ' image buffer where sprites pixmap is stored
Field spriteCount:Int ' number of sprites on sheet
Field spriteX:Int[] ' x position of top left pixel in sprite
Field spriteY:Int[] ' y position of top left pixel in sprite
Field spriteW:Int[] ' width of sprite
Field spriteH:Int[] ' height of sprite
' load spritesheet
Function loadsheet:zSpriteSheet(path:String,maskrgb:Int=$000000)
Local tmp:zSpriteSheet = New zSpriteSheet
tmp.image = zImageBuffer.loadfile(path,maskrgb)
Local f:String = Left(path,Len(path)-3)+"zss"
Local filein:TStream = OpenFile(f)
tmp.spriteCount = ReadInt(filein)
tmp.spriteX = New Int[tmp.spriteCount]
tmp.spriteY = New Int[tmp.spriteCount]
tmp.spriteW = New Int[tmp.spriteCount]
tmp.spriteH = New Int[tmp.spriteCount]
For Local i:Int = 0 Until tmp.spriteCount
tmp.spriteX[i] = ReadInt(filein)
tmp.spriteY[i] = ReadInt(filein)
tmp.spriteW[i] = ReadInt(filein)
tmp.spriteH[i] = ReadInt(filein)
Next
tmp.link = ListAddLast(zSpriteSheetList,tmp)
Return tmp
End Function
' create a new image buffer
Function loadfontsheet:zSpriteSheet(path:String,maskrgb:Int=$000000)
Local tmp:zSpriteSheet = New zSpriteSheet
tmp.image = zImageBuffer.loadfile(path,maskrgb)
Local f:String = Left(path,Len(path)-3)+"fxb"
Local filein:TStream = ReadStream(f)
Local t:Int = ReadInt(filein) ' font height not used
tmp.spriteCount = ReadInt(filein)
tmp.spriteX = New Int[tmp.spriteCount]
tmp.spriteY = New Int[tmp.spriteCount]
tmp.spriteW = New Int[tmp.spriteCount]
tmp.spriteH = New Int[tmp.spriteCount]
For Local i:Int = 0 Until tmp.spriteCount
t = ReadInt(filein) ' ascii code not used
tmp.spriteX[i] = ReadInt(filein)
tmp.spriteY[i] = ReadInt(filein)
tmp.spriteW[i] = ReadInt(filein)
tmp.spriteH[i] = ReadInt(filein)
Next
tmp.link = ListAddLast(zSpriteSheetList,tmp)
Return tmp
End Function
Method draw(toImg:zImageBuffer,x:Int,y:Int,sprite:Int)
toImg.copyrect(Self.image,Self.spriteX[sprite],Self.spriteY[sprite],Self.spriteW[sprite],Self.spriteH[sprite],x,y)
End Method
' remove sprite sheet
Method remove()
Self.image.remove() ' remove sprite image
RemoveLink(Self.link) ' remove sprite sheet
End Method
End Type
I will still be adding the optional line methods and I will be testing to see if using memcopy can speed up the drawing of the methods that does not use masking.