How would one make these screen effects?

Hi @hasanchik I tried to optimize this as below, try it pls

I tested on my Meobit(same mcu with your Xtron Pro). Total time from 5.3s down to about 0.3~0.4s(with pause(0)).

What I changed:

  • As each step(threshold) increase only 1 pixel in 4x4 dither sqr, I saved them into 2 arraies in order.
  • Send prev updated image(bg2) into next dither() calling, so only call setPixel() on dots that really need to update.

It will much more faster if you remove basic.pause(0), almostly can’t see the changing progress, even on device.
Bad side, I didn’t read details for the logic about offx and offy, so just leave them along. Hope it’s not very complicate to add them back for you. :smile:

let ditherStepX=[0,2,0,2,1,1,3,3,0,2,0,2,1,3,1,3]
let ditherStepY=[0,2,2,0,1,3,3,1,1,3,3,1,0,2,2,0]
function dither(imgFrom: Image, threshold: number, color: number = 0, imgTo: Image = null, offx: number = 0, offy: number = 0) {
    let og = imgFrom
    for (let y = 0; y < imgFrom.height; y+=4) {
        for (let x = 0; x < imgFrom.width; x+=4) {
            if (imgTo) {
                og.setPixel(x + ditherStepX[threshold], y + ditherStepY[threshold], imgTo.getPixel(x + ditherStepX[threshold], y + ditherStepY[threshold]))
            }else
                og.setPixel(x + ditherStepX[threshold], y + ditherStepY[threshold], color)
        }
    }
    return og
}

let bgFrom = sprites.background.lilypads
let bgTo = sprites.background.lunarnewyear
let bgDither:Image
doDither()

function doDither(delay:number=0){
    bgDither=bgFrom.clone()
    let msBegin = control.millis()
    for (let index = 0; index <= 15; index++) {
        bgDither = dither(bgDither, index, 1, bgTo, 4, 4)
        scene.setBackgroundImage(bgDither)
        basic.pause(delay)
    }
    info.setScore((control.millis() - msBegin))
}
1 Like

Amazing!!! It has no lag on hardware!!! Not even 1 fps drop!!! Its 38 fps on the highest and 30 on the lowest!

I actually made a more optimized version right before you posted this, but that only has 7 fps instead of 0.4 fps. But its still very laggy. Here’s it to compare:

    const bayerThresholdMap = [
        [1, 49, 13, 61, 4, 52, 16, 64],
        [33, 17, 45, 29, 36, 20, 48, 32],
        [9, 57, 5, 53, 12, 60, 8, 56],
        [41, 25, 37, 21, 44, 28, 40, 24],
        [3, 51, 15, 63, 2, 50, 14, 62],
        [35, 19, 47, 31, 34, 18, 46, 30],
        [11, 59, 7, 55, 10, 58, 6, 54],
        [43, 27, 39, 23, 42, 26, 38, 22]
    ];

    function ditherRow(buff : Buffer, x : number, threshold : number, col : number = 0, buff2 : Buffer = null) : void {
        let y = 0
        let dithering = Math.floor(Math.mod(threshold, 65))
        while (y <= buff.length) {
            let map = bayerThresholdMap[x % 8][y % 8]
            if (map < dithering+1) {
                if (buff2 != null) {
                    buff.setUint8(y, buff2[y])
                } else {
                    buff.setUint8(y, col)
                }
            }
            y += 1
        }
    }

    export function dither(img: Image, threshold: number, color: number = 0, img2: Image = null, offx: number = 0, offy: number = 0) {
        if (prevTransformations[1].n1 != threshold) {
            prevTransformations[1].n1 = threshold
        } else {
            return prevTransformations[1].img
        }
        let w = img.width
        let h = img.height
        //let imageData : number[] = []
        let out = img.clone()
        let buff = Buffer.create(h)
        let buff2 = null
        if (img2 != null) {
            buff2 = Buffer.create(h)
        }
        for (let x = 0; x < w; x++) {
            out.getRows(x, buff)
            if (buff2 != null) {
                img2.getRows(x, buff2)
            } 
            ditherRow(buff, x, threshold, color, buff2)
            out.setRows(x, buff)
        }
        prevTransformations[1].img = out
        return out
    }

1 Like

Hi @hasanchik , so glad to hear it works

As @kwx said, the key of perf is the inner loop.

    for (let y = 0; y < imgFrom.height; y+=4) {
        for (let x = 0; x < imgFrom.width; x+=4) {

Have you noticed “y+=4” and “x+=4” ? My code run inner loop 1/16 times comparing to yours, per dither sqr
Of cause, other optimizing are helped meanwhile.

Im all out of ideas for a screen effect to make. Somebody know a idea?

Maybe a explode, or a screen glass broken?

Thats too “unspecific”. Could you let me see what you mean?

Also, the extension is fully finished, blocks and all. Should probally make a post about that.

ya, that just an idea, just came to my mind when saw your ask.

I dont know what you mean by screen glass broken

@hasanchik @kwx @AqeeAqee (sorry if this notification is annoying) I know this topic is dead, but I’ve been trying to optimize a zoom screen effect for hardware and think this is a relevant topic to my question. Do you have any ideas on optimizations, specifically for hardware, but improvements for the simulator are also appreciated as this will probably become an extension. It seems that the perf loss is from reading from an array in the inner loop, but I can’t use idiv because I want decimal zoom multipliers to still exist. The other perf loss is seemingly from reading the screen image pixels. I’m wondering if there’s faster ways to do what I’m doing. I also want this to affect the whole screen, so that’s why I’m using a renderable. Here’s my code:
https://makecode.com/_Yoc7K6EwdK9o

Hi @Kiwiphoenix364 !

Try helpers.imageBlit() pls, to avoid “2D for” and getPixel inside.
Hope it helps.

1 Like

@AqeeAqee sorry, but I’m not familiar with that function (and I can’t find documentation), can you please specify what the arguements do? I know there’s the description when you hover over it imageBlit(img: Image, xDst: number, yDst: number, wDst: number, hDst: number, src: Image, xSrc: number, ySrc: number, wSrc: number, hSrc: number, transparent: boolean, check: boolean): boolean but that only tells me the arguement types, not what they do. Also, wouldn’t this function just copy/paste segments of images? Idk how this would help unless I was still doing it pixel-by-pixel.

imageBlit() just copy a rect area from Src image to the other rect area of Dst image. x,y,w,h are top, left, width, height of these rects. The transparent can be set true/false as your need, and the check should be always false, otherwise nothing will happen.

1 Like

Thx, I’ll check this out!

I’ve made a dither code that uses a buffer and getRows/setRows, and is ~105-107% faster (~205-207% of the speed) on my pygamer!

I’m most likely going to use this in my screen effects extension (with a slightly changed sequence of dithering, and credit in a code comment for @hasanchik’s idea/coding and @kwx 's/your
(@AqeeAqee’s) contributions to optimizing this code).

1 Like

I know this has been dead for two years, but I need help on making a wavy effect as shown in this forum (Even though I have the extension I just wanna know how to make a heat effect.)

I don’t code anymore, but the general idea goes as follows. You get a row or column of pixels. You then horizontally or vertically shift the row or column based on sin(x) or sin(y) . Sin only goes from -1 to 1, it’ll need to be tweaked. Put the shifted line back in a separate image. Repeat for all rows or columns.

If this is too general for you, it could be worth trying to look at the code to figure out how we did it.

2 Likes

Is there a way to make the wave effect only in a certain area, like under some tiles? I’m trying to make a water effect.

1 Like

I would just use that tile animations extension for that, or maybe look at whatever @Jedi was doing with that capybara game? I don’t think it ever got finished, but I remember seeing a cool water effect.

1 Like