In my course I'm making a HUD with bars (energy, health, etc.) that are, for once,
not box shaped. Additionally, I wanted the area of these bars (the "background" of it, that isn't covered by the bar-part itself) to maybe scroll a simple faint pattern or something, maybe even just glow or something (how that factors in here, I am yet to know).
(One other methods I've seen involves covering up the whole screen save for the area you want to see; obviously this isn't suitable for this).
Anyway, using
Syncaidius's Memblock Image Manipulation Library, I've come up with a very simple mock/proof of concept (that is, if it's not already been done; a search turned up nothing).
it works by taking two images, one defining the masked area, and the one to display in that area. The RGB of the second is combined with the A of the first to make the desired image.
Animated gif demo (~1mb)
here's the code. It requires two images to work, so they and the code are zipped up and attached to this post. The code itself is a little bit of a mess. Basically, this method is still very slow. Rather than access the memblocks of the source images for every pixel of the output, they're output to an array on loading. That whole thing's a bit rudimentary but I'm looking to make it faster and easier to integrate into thing in the near future.
set display mode desktop width(), desktop height(), 32, 1
sync on
sync rate 60
// SPRITE MASK. A tiled image scrolls while it's mask restricts it's visibility to a single area.
// This requires: the tiled image which is to be scrolled and displayed.
// the mask to apply which defines the area to display.
// Method: begin a new image, the size of the mask.
// get the display image and it's properties (size, scroll x, y, etc.)
// run through every pixel of the new mask image, replace it's colour values with the appropriate values from the corresponding pixel of the display image.
// put the mask values into the alpha channel.
global mx as integer
global my as integer
global dispImage as integer = 1
global maskImage as integer = 2
global drawImage as integer = 3
global dispMem as integer
global maskMem as integer
global drawMem as integer
gosub IMG_GLOBALINIT
IMG_INIT(1, 10, 256, 256)
IMG_LOAD("DisplayImage.png", dispImage, 1, 1)
IMG_LOAD("MaskImageC.png", maskImage, 1, 1)
// convert display and mask images to memblocks for reading from.
`dispMem = IMG_MAKE(128, 128, 0)
`IMG_PASTE_IMAGE(dispMem, dispImage, 0, 0)
`maskMem = IMG_MAKE(256, 256, 0)
`IMG_PASTE_IMAGE(maskMem, maskImage, 0, 0)
// begin the new image as a blank image.
drawMem = IMG_MAKE(256, 256, 0)
do
// update input.
mx = mousex()
my = mousey()
// raw draw of images.
paste image maskImage, 10, 10, 1
paste image dispImage, 276, 10, 1
paste image dispImage, mx, my
// change scroll values
inc scrollX#, 3
inc scrollY#, 1
`if scrollX# < -127 then scrollX# = -128 - scrollX#
`if scrollY# < -127 then scrollY# = -128 - scrollY#
if scrollX# > 127 then scrollX# = 128 - scrollX#
if scrollY# > 127 then scrollY# = 128 - scrollY#
// SPRITE MASK PROCESS START. ************************
// begin the new image as a copy of the mask image.
`drawMem = IMG_MAKE(256, 256, 0)
`IMG_PASTE_IMAGE(drawMem, maskImage, 0, 0)
for n = 0 to IMG_pixelTableMaxImages
if IMG_pixelTableLookup(n) = dispImage then dispEntry = n
if IMG_pixelTableLookup(n) = maskImage then maskEntry = n
next n
print dispEntry
print maskEntry
// copy each color channel pixel from the display image into the new image, taking into account the scroll amount.
dispX = scrollX#
if dispX < 0 then dispX = 0
if dispX > 127 then dispX = 0
for x = 0 to 255
dispY = scrollY#
if dispY < 0 then dispY = 0
for y = 0 to 255
inc dispY
if dispY > 127 then dispY = 0`128 - dispY
dispColor = IMG_pixelTable(dispEntry, dispX, dispY)
maskColor = IMG_pixelTable(maskEntry, int(x / (1+(mx/1000.0))), int(y / (1+(my/1000.0))))
`dispColor = IMG_GET_PIXEL(dispMem, dispX, dispY)
`maskColor = IMG_GET_PIXEL(maskMem, x, y)
`color = rgb(RGBR(dispColor), RGBG(dispColor), RGBB(dispColor))
color = SET ALPHA(dispColor, RGBA(maskColor))
IMG_SET_PIXEL(drawMem, x, y, color)
next y
inc dispX
if dispX > 127 then dispX = 0`128 - dispX
next x
// get the final image.
IMG_UPDATE(drawMem, drawImage)
`IMG_DELETE(drawMem)
`IMG_DELETE(dispMem)
`IMG_DELETE(maskMem)
// SPRITE MASK PROCESS END. **************************
// draw final image.
paste image drawImage, 10, 276, 1
print screen fps()
sync
bgGlow# = 128.0 + (cos(timer() / 10.0) * 32.0)
cls rgb(bgGlow#, bgGlow#, bgGlow#)
loop
remstart
======================================================================================================================
=====================================SYNCAIDIUS' MEMBLOCK IMAGE MANIPULATION LIBRARY V1.1=============================
======================================================================================================================
Feel free to use this code however you wish, whether that be for free or commercial games or programs.
remend
// ADDITIONS ================================================
type t_IMG_pixelTable
r as integer
g as integer
b as integer
a as integer
endtype
IMG_GLOBALINIT:
global IMG_pixelTableMaxImages as integer
return
// Sets up library with a "pixeltable", and an array to match images to their pixeltable entry.
function IMG_INIT(createpixeltable as boolean, maximages as integer, maxw as integer, maxh as integer)
IMG_pixelTableMaxImages = -1
if createpixeltable
IMG_pixelTableMaxImages = maximages
dim IMG_pixelTable(maximages, maxw, maxh)` as t_IMG_pixelTable
dim IMG_pixelTableLookup(maximages)
endif
endfunction
// Loads an image and creates an array of all of it's pixels. Speeds up mask operations because repeated memblock access isn't required.
function IMG_LOAD(filename as string, imagenum as integer, textureflag as boolean, createpixeltable as boolean)
load image filename, imagenum, textureflag
if createpixeltable
// look for free entry
entry = -1
for n = 0 to IMG_pixelTableMaxImages
if IMG_pixelTableLookup(n) = 0 then entry = n : exit
next n
if entry <> -1
IMG_pixelTableLookup(entry) = imagenum
w = image width(imagenum) - 1
h = image height(imagenum) - 1
mem = IMG_MAKE(w + 1, h + 1, 0)
IMG_PASTE_IMAGE(mem, imagenum, 0, 0)
for x = 0 to w
for y = 0 to h
color = IMG_GET_PIXEL(mem, x, y)
color = SET ALPHA(color, RGBA(color))
IMG_pixelTable(entry, x, y) = color
next y
next x
endif
IMG_DELETE(mem)
endif
endfunction
// ==========================================================
//Creates a new memblock image filled with the specified colour.
function IMG_MAKE(width as integer, height as integer, colour as dword)
mem = FREE_MEMBLOCK()
pixels = width*height
bytes = 4+4+4+(pixels*4) //width+height+depth+data
bytePos = 0
make memblock mem,bytes
width = (0<<24) + (0<<16) + (0<<8) + width
height = (0<<24) + (0<<16) + (0<<8) + height
depth = (0<<24) + (0<<16) + (0<<8) + 32
//write image info
write memblock dword mem,bytePos,width : inc bytePos,4
write memblock dword mem,bytePos,height : inc bytePos,4
write memblock dword mem,bytePos,depth : inc bytePos,4
//set every pixel in the image to the specified colour
for pixel = 1 to pixels
write memblock dword mem,bytePos,colour : inc bytePos,4 //set all pixels to the specified 'colour'
next pixel
endfunction mem
//converts a DBP image into a memblock image. The function will also delete the image that was used, if the delete parameter is set to 1.
function IMG_CONVERT(img as integer, delete as boolean)
mem = FREE_MEMBLOCK()
make memblock from image mem,img
//check if the image should be deleted (since its now inside a memblock, the coder may not want the image anymore).
if delete = 1
delete image img
endif
endfunction mem
//sets the colour of the specified pixel on a memblock image
function IMG_SET_PIXEL(memblock as byte, x as integer, y as integer, colour as dword)
//get the width and height of the image
width = memblock byte(memblock,0)+256*memblock byte(memblock,1)
height = memblock byte(memblock,4)+256*memblock byte(memblock,5)
if x>-1 and x<width
if y>-1 and y<height
n as double integer
n = (4*((y*width)+x))+12
write memblock dword memblock,n,colour
endif
endif
endfunction
//same as IMAGE_SET_PIXEL except the memblock image width and height have to be entered. This speeds things up a little if you have many pixels to write and already know the image width/height)
function IMG_SET_PIXEL2(memblock as byte, x as integer, y as integer, width as integer, height as integer, colour as dword)
if x>-1 and x<width
if y>-1 and y<height
n as double integer
n = (4*((y*width)+x))+12
write memblock dword memblock,n,colour
endif
endif
endfunction
//returns the colour of the specified pixel as a dword
function IMG_GET_PIXEL(memblock as byte, x as integer, y as integer)
width = memblock byte(memblock,0)+256*memblock byte(memblock,1)
height = memblock byte(memblock,4)+256*memblock byte(memblock,5)
pixel = IMG_RGBA(255,255,255,255)
if x>-1 and x<width
if y>-1 and y<height
n as double integer
n = (4*((y*width)+x))+12
pixel = memblock dword(memblock,n)
endif
endif
endfunction pixel
//same as IMAGE_GET_PIXEL except the width and height have to be entered. This speeds things up a little if you have many pixels to read and already know the image width/height)
function IMG_GET_PIXEL2(memblock as byte, x as integer, y as integer, width as integer, height as integer)
pixel = IMG_RGBA(255,255,255,255)
if x>-1 and x<width
if y>-1 and y<height
n as double integer
n = (4*((y*width)+x))+12
pixel = memblock dword(memblock,n)
endif
endif
endfunction pixel
//creates or updates a DBP image with the contents of a memblock image
function IMG_UPDATE(memblock as byte, img as integer)
make image from memblock img,memblock
endfunction
//sets the whole image to a single colour
function IMG_CLEAR(mem as byte, colour as dword)
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
for x = 0 to (width-1)
for y = 0 to (height-1)
IMG_SET_PIXEL2(mem,x,y,width,height,colour)
next y
next x
endfunction
//does exactly the same as DELETE MEMBLOCK, just uses the image library naming convention instead.
function IMG_DELETE(mem as byte)
delete memblock mem
endfunction
//Pastes a normal image into a memblock image.
function IMG_PASTE_IMAGE(mem as integer, srcImg as integer, x as integer, y as integer)
tempMem = FREE_MEMBLOCK() //temp memblock for the image that will be pasted
make memblock from image tempMem,srcImg
//width and height of source memblock
imgWidth = memblock byte(tempMem,0)+256*memblock byte(tempMem,1)
imgHeight = memblock byte(tempMem,4)+256*memblock byte(tempMem,5)
//width and height of destination memblock
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
for px = 0 to (imgWidth-1)
for py = 0 to (imgHeight-1)
col = IMG_GET_PIXEL2(tempMem,px,py,imgWidth,imgHeight)
IMG_SET_PIXEL2(mem,x+px,y+py,width,height,col)
next py
next px
//delete the temp memblock
delete memblock tempMem
endfunction
//paste a memblock image into another memblock image at the specified coords. Also create a function to paste part of one memblock image into another memblock image.
function IMG_PASTE_MEMBLOCK(mem as integer, srcMem as integer, x as integer, y as integer)
//width and height of destination memblock
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
//width and height of source memblock (memblock that will be pasted)
widthsrc = memblock byte(srcMem,0)+256*memblock byte(srcMem,1)
heightsrc = memblock byte(srcMem,4)+256*memblock byte(srcMem,5)
for px = 0 to (widthsrc-1)
for py = 0 to (heightsrc-1)
col = IMG_GET_PIXEL2(src,px,py,widthsrc,heightsrc)
IMG_SET_PIXEL2(mem,x+px,y+py,width,height,col)
next py
next px
endfunction
//copies the specified area of one memblock image (source) into another memblock image (destination)
function IMG_PASTE_MEMBLOCK_PART(mem as byte, srcMem as byte, x as integer, y as integer, x2 as integer, y2 as integer, x3 as integer, y3 as integer)
//width and height of destination memblock
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
//width and height of source memblock (memblock to copy from)
widthsrc = memblock byte(srcMem,0)+256*memblock byte(srcMem,1)
heightsrc = memblock byte(srcMem,4)+256*memblock byte(srcMem,5)
//get the width and height of the area to be copied
difX = x3 - x2
difY = y3 - y2
for px = 0 to difX
for py = 0 to difY
col = IMG_GET_PIXEL2(srcMem,x2+px,y2+py,widthsrc,heightsrc)
IMG_SET_PIXEL2(mem,x+px,y+py,width,height,col)
next py
next px
endfunction
//draw a solid colour rectangle inside a memblock image
function IMG_DRAW_RECTANGLE(mem as byte, x as integer, y as integer, width as integer, height as integer, colour as dword)
//width and height of destination memblock
memwidth = memblock byte(mem,0)+256*memblock byte(mem,1)
memheight = memblock byte(mem,4)+256*memblock byte(mem,5)
//set rectangle pixels
for px = 0 to (width-1)
for py = 0 to (height-1)
IMG_SET_PIXEL2(mem,x+px,y+py,memwidth,memheight,colour)
next py
next px
endfunction mem
function IMG_DRAW_RECTANGLE_OUTLINE(mem as integer, x as integer, y as integer, width as integer, height as integer, thickness as integer, colour as dword)
//width and height of destination memblock
memwidth = memblock byte(mem,0)+256*memblock byte(mem,1)
memheight = memblock byte(mem,4)+256*memblock byte(mem,5)
//left side
for px = x to x+thickness
for py = y to (y+height)
IMG_SET_PIXEL2(mem,px,py,memwidth,memheight,colour)
next py
next px
//right side
for px = (x+width) to (x+width)-thickness step -1
for py = y to (y+height)
IMG_SET_PIXEL2(mem,px,py,memwidth,memheight,colour)
next py
next px
//top side
for px = x to (x+width)
for py = y to y+thickness
IMG_SET_PIXEL2(mem,px,py,memwidth,memheight,colour)
next py
next px
//bottom side
for px = x to (x+width)
for py = (y+height) to (y+height)-thickness step -1
IMG_SET_PIXEL2(mem,px,py,memwidth,memheight,colour)
next py
next px
endfunction
//draw a line - memblock, x1, y1, x2, y2, line width/thickness, red, green, blue
function IMG_DRAW_LINE(mem as byte, x1 as integer, y1 as integer, x2 as integer, y2 as integer, colour as dword)
//get width and height of memblock image
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
deltax = abs(x2 - x1) // The difference between the x's
deltay = abs(y2 - y1) // The difference between the y's
x = x1 // Start x off at the first pixel
y = y1 // Start y off at the first pixel
numadd = 0
if x2 >= x1 // The x-values are increasing
xinc1 = 1
xinc2 = 1
else // The x-values are decreasing
xinc1 = -1
xinc2 = -1
endif
if y2 >= y1 // The y-values are increasing
yinc1 = 1
yinc2 = 1
else // The y-values are decreasing
yinc1 = -1
yinc2 = -1
endif
if (deltax >= deltay) // There is at least one x-value for every y-value
xinc1 = 0 // Don't change the x when numerator >= denominator
yinc2 = 0 // Don't change the y for every iteration
den = deltax
num = deltax / 2
numadd = deltay
numpixels = deltax // There are more x-values than y-values
else // There is at least one y-value for every x-value
xinc2 = 0 // Don't change the x for every iteration
yinc1 = 0 // Don't change the y when numerator >= denominator
den = deltay
num = deltay / 2
numadd = deltax
numpixels = deltay // There are more y-values than x-values
endif
for curpixel = 0 to numpixels
IMG_SET_PIXEL2(mem,x,y,width,height,colour)
num = num + numadd // Increase the numerator by the top of the fraction
if num >= den // Check if numerator >= denominator
num = num - den // Calculate the new numerator value
x = x + xinc1 // Change the x as appropriate
y = y + yinc1 // Change the y as appropriate
endif
x = x + xinc2 // Change the x as appropriate
y = y + yinc2 // Change the y as appropriate
next curpixel
endfunction
//draws a filled circle
function IMG_DRAW_CIRCLE(mem as byte, x as integer, y as integer, radius as integer, colour as dword)
//get width and height of memblock image
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
dots = radius*(6.28*2)
deg2dot#=360/(dots*1.0)
for f = 0 to radius
for d = 0 to dots
u = sin(d*deg2dot#)*(radius-f)
v = cos(d*deg2dot#)*(radius-f)
IMG_SET_PIXEL2(mem,x+u,y+v,width,height,colour)
next d
next f
endfunction
//draws a circle outline
function IMG_DRAW_CIRCLE_OUTLINE(mem as byte, x as integer, y as integer, radius as integer, dotRate as integer, thickness as integer, colour as dword)
//get width and height of memblock image
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
radians = (6.28/100)*dotRate //how many dots are allowed to make up the circle's border
dots = radius*radians
deg2dot#=360/(dots*1.0)
for t = 0 to thickness
for d = 0 to dots
u = sin(d*deg2dot#)*(radius-t)
v = cos(d*deg2dot#)*(radius-t)
IMG_SET_PIXEL2(mem,x+u,y+v,width,height,colour)
next d
next t
endfunction
//draws part of a circle
function IMG_DRAW_CIRCLE_PART(mem as byte, x as integer, y as integer, radius as integer, fillPercent as integer, cFill as boolean, angle as float, colour as dword)
//get width and height of memblock image
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
//calculate circle border dot amount and angle
radians = (6.28/100)*fillPercent //how many dots are allowed to make up the circle's border
dots = radius*radians
deg2dot#=angle/(dots*1.0)
if cFill = 0 //check if the circle is to be filled or not
for d = 0 to dots
u = sin(d*deg2dot#)*radius
v = cos(d*deg2dot#)*radius
IMG_SET_PIXEL2(mem,x+u,y+v,width,height,colour)
next d
else
for f = 0 to radius
for d = 0 to dots
u = sin(d*deg2dot#)*(radius-f)
v = cos(d*deg2dot#)*(radius-f)
IMG_SET_PIXEL2(mem,x+u,y+v,width,height,colour)
next d
next f
endif
endfunction
//converts the input memblock image to a greyscale image
function IMG_GREYSCALE(mem as byte)
//get width and height of memblock image
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
for x = 0 to (width-1)
for y = 0 to (height-1)
col = IMG_GET_PIXEL2(mem,x,y,width,height)
//seperate each pixel colour channel from the col dword value
a=rgba(col)
r=rgbr(col)
g=rgbg(col)
b=rgbb(col)
//calculate the black/white/grey to use
col = (r+g+b)/3 //add rgb together and average out (divide by 3)
IMG_SET_PIXEL2(mem,x,y,width,height,IMG_RGBA(col,col,col,255))
next y
next x
endfunction
//flips a memblock image horizontally
function IMG_FLIP_HORIZONTAL(mem as byte)
//retrieve image width and height from memblock
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
//create a temp image memblock of the same size as our image's memblock
temp = IMG_MAKE(width, height, IMG_RGBA(0,0,0,255))
//rewrite the data into the temp memblock, with the x axis flipped
for x = 0 to (width-1)
for y = 0 to (height-1)
nx = width-x
col = IMG_GET_PIXEL2(mem,x,y,width,height)
IMG_SET_PIXEL2(temp,nx,y,width,height,col)
next y
next x
//copy everything from the temp memblock back into our image memblock and delete the temp one.
copy memblock temp,mem,0,0,get memblock size(mem)
delete memblock temp
endfunction
//flips a memblock image vertically
function IMG_FLIP_VERTICAL(mem as byte)
//retrieve image width and height from memblock
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
//create a temp image memblock of the same size as our image's memblock
temp = IMG_MAKE(width, height, IMG_RGBA(0,0,0,255))
//rewrite the data into the temp memblock, with the y axis flipped
for x = 0 to (width-1)
for y = 0 to (height-1)
ny = height-y
col = IMG_GET_PIXEL2(mem,x,y,width,height)
IMG_SET_PIXEL2(temp,x,ny,width,height,col)
next y
next x
//copy everything from the temp memblock back into our image memblock and delete the temp one.
copy memblock temp,mem,0,0,get memblock size(mem)
delete memblock temp
endfunction
function IMG_TINT(mem as byte, colour as dword)
//retrieve image width and height from memblock
width = memblock byte(mem,0)+256*memblock byte(mem,1)
height = memblock byte(mem,4)+256*memblock byte(mem,5)
//seperate each tint colour channel from the dword colour value
ta=(colour && 0xFF000000) >> 24 //retrieves bits 24 - 31 from the colour (byte 4). Alpha
tr=(colour && 0x00FF0000) >> 16 //retrieves bits 16 - 23 from the colour (byte 3). Red
tg=(colour && 0x0000FF00) >> 8 //retrieves bits 8 - 15 from the colour (byte 2). Green
tb=(colour && 0x000000FF) //retrieves bits 0 - 7 from the colour (byte 1). Blue
for x = 0 to (width-1)
for y = 0 to (height-1)
col = IMG_GET_PIXEL2(mem,x,y,width,height)
//seperate each pixel colour channel from the col dword value
a=(col && 0xFF000000) >> 24
r=(col && 0x00FF0000) >> 16
g=(col && 0x0000FF00) >> 8
b=(col && 0x000000FF)
//add the tint and pixel colour together, then divide it by 2 to average it.
col = IMG_RGBA((r+tr)/2, (g+tg)/2, (b+tb)/2,(a+ta)/2)
IMG_SET_PIXEL2(mem,x,y,width,height,col)
next y
next x
endfunction
function FREE_MEMBLOCK()
for find = 1 to 255
if memblock exist(find) = 0
exitfunction find
endif
next find
endfunction 0
//converts 4 seperate colour channels into a single DWORD and returns it.
function IMG_RGBA(r as byte, g as byte, b as byte, a as byte)
col = (a<<24) + (r<<16) + (g<<8) + b
endfunction col
edit:
basic controls: move the mouse to scale the mask (top-left corner for normal size). Switch between files "MaskImageC" and "MaskImageB" for different masks. Also use the mouse to move the image attached to the cursor around; it'll display underneath the effect. Also note, the background pulses colour to show the effect isn't a cutout or such.
further edit:
frame rate for me averages around 30, which is yeah-ok, but I reckon it'd slow pretty fast with more or bigger images.