I fixed the problem. It was a simple typo. If you look at this line:
                if getPixelMatrixValue(p[a], aPos)=1 and getPixelMatrixValue(p[a], bPos)=1 then exitfunction 1
I was comparing a bit to itself. One of those function calls should using p[
b] but I used 'a' for both instead. Oops!
This code should work. I brought my laptop to work and it's running fine but I can't get it online so I'm just updating the text file here and posting it. So provided I didn't make any more dumb typos, this should be pixel perfect collision with low overhead. With the bug fixed, I'll have to see if it still crashes with large images or not.
I'm getting around 500 fps on my i7 laptop. It dips to 350 on an intersection, but the code has a lot of needless loops in it for drawing for demo purposes.
Ok, I took out all the needless stuff and it's twice as fast. About 1200 fps without collision, 600-900 once the sprites' bounding boxes overlap. Still seems like too large of a hit to me, maybe there's still room to optimize it.
// Project: pixelCollision
// Created: 2017-04-04
Â
// show all errors
SetErrorMode(2)
Â
// set window properties
SetWindowTitle( "pixelCollision" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
Â
// set display properties
SetVirtualResolution( 1024, 768 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 0, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 ) // since version 2.0.22 we can use nicer default fonts
Â
Â
Â
Type Rectangle
    x as integer
    y as integer
    width as integer
    height as integer
EndType
 Â
 Â
Type PixelCollision
    spriteId as integer
    matrix as integer[]
    m as integer
EndType
 Â
 Â
pixelSprites as PixelCollision[]
 Â
 Â
 Â
dude1 = createSprite(loadImage("GameArtPartners_1_FeatureImage2.png"))
p1 = addPixelCollision(pixelSprites, dude1)
setSpritePosition(dude1, 300, 200)
Â
dude2 = createSprite(loadImage("pirate-royalty-free-game-art2.png"))
p2 = addPixelCollision(pixelSprites, dude2)
Â
Â
Â
c = 0xFFFFFF
Â
Â
Â
do
    Â
    Â
Â
    Â
    Â
    setSpritePosition(dude2, getRawMouseX(), getRawMouseY())
    Â
    Â
Â
    bmax = pixelSprites[p1].matrix.length*8
    for i = 0 to bmax
        y = i / 64
        x = i - y*64
        Â
        `if getPixelMatrixValue(pixelSprites[p1], i) > 0 then drawBox(x+100, y+100, x+101, y+101,c,c,c,c,1)
    next i
    Â
    Â
    bmax = pixelSprites[p2].matrix.length*8
    for i = 0 to bmax
        y = i / 64
        x = i - y*64
        Â
        `if getPixelMatrixValue(pixelSprites[p2], i) > 0 then drawBox(x+200, y+100, x+201, y+101,c,c,c,c,1)
    next i
    Â
    Â
    Â
    result = pixelCollide(pixelSprites, p1, p2)
    print(result)
Â
    Print( ScreenFPS() )
    Sync()
loop
Â
Â
Â
Â
Â
 Â
 Â
 Â
 Â
function pixelCollide(p as PixelCollision[], a as integer, b as integer)
    x1 = getSpriteX(p[a].spriteId)
    y1 = getSpriteY(p[a].spriteId)
    x2 = x1 + getSpriteWidth(p[a].spriteId)
    y2 = y1 + getSpriteHeight(p[a].spriteId)
 Â
    x3 = getSpriteX(p[b].spriteId)
    y3 = getSpriteY(p[b].spriteId)
    x4 = x3 + getSpriteWidth(p[b].spriteId)
    y4 = y3 + getSpriteHeight(p[b].spriteId)
    Â
// bounding boxes around sprites
    c = 0xFFFFFF
    `drawBox(x1,y1,x2,y2,c,c,c,c,0)
    `drawBox(x3,y3,x4,y4,c,c,c,c,0)
 Â
    // check bounding box overlap
    if x3 < x2 and x4 > x1 and y3 < y2 and y4 > y1
        // Get intersecting rectangle
        r as Rectangle
        r = getIntersection(x1,y1,x2,y2,x3,y3,x4,y4)
         Â
        x1 = r.x-x1
        y1 = r.y-y1
         Â
        x3 = r.x - x3
        y3 = r.y - y3
         Â
        aWidth = getSpriteWidth(p[a].spriteId)
        bWidth = getSpriteWidth(p[b].spriteId)
        Â
        // show intersection box
        c = 0xFFFF00
        //drawBox(r.x, r.y, r.x+r.width, r.y+r.height,c,c,c,c,0)
       Â
c1 = 0xFFFFFF
c2 = 0xFF0000
c3 = 0x0000FF
// Show what parts of the image are being checked
        for y = 0 to r.height-1
            for x = 0 to r.width-1
                aPos = (y1+y)*aWidth + x1+x
                bPos = (y3+y)*bWidth + x3+x
                if getPixelMatrixValue(p[a], aPos)=1 then drawBox(x+100, y+100, x+101, y+101,c1,c1,c1,c1,1)
if getPixelMatrixValue(p[b], bPos)=1 then drawBox(x+100, y+100, x+101, y+101,c2,c2,c2,c2,1)
cx = r.x+x
cy = r.y+y
if getPixelMatrixValue(p[a], aPos)=1 and getPixelMatrixValue(p[b], bPos)=1 then drawBox(cx,cy,cx+1,cy+1,c3,c3,c3,c3,1)
            next x
        next y
       Â
// Check for overlapping collisionÂ
        for y = 0 to r.height-1
            for x = 0 to r.width-1
                aPos = (y1+y)*aWidth + x1+x
                bPos = (y3+y)*bWidth + x3+x
                if getPixelMatrixValue(p[a], aPos)=1 and getPixelMatrixValue(p[b], bPos)=1 then exitfunction 1
            next x
        next y
         Â
    endif
 Â
endfunction 0
Â
Â
 Â
 Â
function getIntersection(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2)
    left  = max(ax1, bx1)
    top   = max(ay1, by1)
    right = min(ax2, bx2)
    bottom = min(ay2, by2)
 Â
Â
    r as Rectangle
 Â
    r.x = left
    r.y = top
    r.width = 0
    r.height = 0
    if right > left then r.width = right - left
    if bottom > top then r.height = bottom - top
     Â
endfunction r
 Â
 Â
 Â
function addPixelCollision(p ref as PixelCollision[], sprite as integer)
     Â
    img = getSpriteImageID(sprite)
 Â
    collisionMatrixSize = ceil((getImageWidth(img) * getImageHeight(img)) / 8)
 Â
    x as PixelCollision
    x.spriteId = sprite
    x.matrix.length = collisionMatrixSize
 Â
 Â
    m = createMemblockFromImage(img)
    Â
    Â
    Â
Â
    Â
    bit = 0
    for i = 12 to getMemblockSize(m)-4 step 4
        if getMemblockByte(m, i+3) > 0
            setPixelMatrixValue(x, bit, 1)
        endif
        inc bit
    next i
 Â
    x.m = m
    Â
    `deleteMemblock(m)
 Â
 Â
    p.insert(x)
endfunction p.length
 Â
 Â
 Â
 Â
 Â
function setPixelMatrixValue(p ref as PixelCollision, bit, bool)
    i = floor(bit / 8)
    b = bit - i*8
 Â
    p.matrix[i] = p.matrix[i] || (1 << b)
endfunction
 Â
 Â
 Â
 Â
 Â
function getPixelMatrixValue(p as PixelCollision, bit)
    i = floor(bit / 8)
    b = bit - i*8
    value = (p.matrix[i] >> b) && 1
endfunction value
Â
Â
Â
function max(a, b)
    if a > b then exitfunction a
endfunction b
Â
function min(a, b)
    if a < b then exitfunction a
endfunction b
"I like offending people, because I think people who get offended should be offended." - Linus Torvalds