Scaling up pixel art

I’ve needed to draw pixel art (think Stardew Valley or Terraria) at a larger scale and can think of a few different approaches to this problem, so I decided to run some benchmarks. Project will be included if anybody can think of additional optimizations.

In this test I’m going to draw an 8x8 graphic at 32x, so 256x256. Before timing anything, I generate a two dimensional array of randomized colors. I run each test 1000 times and take the average. All the tests produce the correct results, timing is the only interesting part here. The Xojo version I’m using is 2025r2.1.

The first test uses FillRectangle to draw each “pixel” as a 32px rectangle.
The second test uses RGBSurface to set all 65,536 pixels individually.
The third test creates an 8x8 picture, sets all 64 pixels with RGBSurface, then uses DrawPicture to draw it scaled up on another picture.

These are all pretty fast operations, so measurements are in microseconds. Results are somewhat surprising. Except for test 2, that was always going to be the worst performer.

Platform FillRectangle RGBSurface RGBSurface + DrawPicture
Mac 71.36 5503.65 208.88
Windows 102.98 11839.47 84.12

This topic is really just for discussion / sharing of knowledge. It looks like my best option is a platform-specific implementation.

Pixel Drawing Speed Tests.xojo_binary_project.zip (5.7 KB)

1 Like

Well… nevermind. There’s definitely some sort of performance improvement from running the test multiple times and/or in sequence.

If I retry with a single execution, and each launch only runs a single test, the results are dramatically different:

Platform FillRectangle RGBSurface RGBSurface + DrawPicture
Mac 171.08 5516.58 306.58
Windows 11515.30 10517.40 11839.60

I’m not sure exactly what to attribute the difference to, though I suspect memory allocation. I think a clear takeaway though is that Windows drawing performance is terrible.

Possibly another side effect of Direct2D. Have you tried an old style picture (depth 24 or 32) to see if that helps.

Hello
I always use the 2D object Pixmapshape to enlarge/reduce images. I can quickly modify it with scale or rotation. I’ve expanded the demo program a bit.
Pixel Drawing Speed Tests-Pixmapshape.xojo_binary_project.zip (8.1 KB)

Do you have MBS?

Have you tried the GMIMage Resize function?

sorry // Bogenmass
Pixel Drawing Speed Tests-Pixmapshape 2.xojo_binary_project.zip (8.1 KB)

I do, but I can’t find which function you’re talking about. Google finds the FileMaker version just fine, but nothing for Xojo. I’m sure it’s there, I just don’t know which I’m looking for.

I’ve added a 10ms delay between execution in an attempt to let memory clear out. This seems to produce more accurate results, but I still can’t say I know with confidence exactly what this effect I’m seeing is. The tests are now triggered one at a time using buttons on the window.

I added the PixmapShape version and alternate bit depth. Neither made any meaningful difference.

Platform FillRectangle RGBSurface RGBSurface + DrawPicture PixmapShape
Mac 518.22 6239.92 1101.63 1079.36
Windows 117.87 10520.23 103.82 109.37

Assuming these results are accurate (I have my doubts) it seems like FillRectangle is the best choice. But I doubt these results because the Windows results blow the Mac results out of the water, contrary to the last test. And I’ve spent so much time in Xojo that I know Windows drawing is slower. The 10ms between executions is likely pointless on Windows.

Pixel Drawing Speed Tests v2.xojo_binary_project.zip (8.0 KB)

Here’s the GMImageMBS method you’re looking for:

ETA: You’ll probably want to use GMImageMBS.FilterPoint as the filter for pixel art since it’s the equivalent of nearest neighbor.

Thanks. Looks like I don’t have that plugin loaded, so it’ll have to wait for another time to test its performance. My gut says if I try to create the 8x8 Xojo picture, then create a GMImageMBS from that, resize it, and convert back to the Xojo picture, performance won’t be better. There’s too many conversions there. However, if I learn how to draw the 8x8 to a new GMImageMBS directly instead of converting from the Xojo picture, that has more potential.

The following might work:

1.Create your source Xojo picture with the content.

2.Create an empty destination Xojo picture with the target dimensions.

3.Create PictureMBS objects from the Xojo pictures (this doesn’t copy).

4.Use PictureMBS.Scale to upscale the source PictureMBS into the destination PictureMBS.

Picturembs might help, yes.

As an aside, for some reason Google searches always turn up FileMaker

To get xojo plugins , you need to add

Site:monkeybreadsoftware.net to the search.

I’m unsure why the 8x8 image is generated Inside the loop, or why the scaled size (8*scale) is calculated in the loop Instead of precalculated.

That said, tweaking those things is probably not the kind of optimising that is needed here.

Can all potential images be created once at app startup, or do they arrive unexpected as the app runs?

Good question. Yes, they are dynamic. That’s why I generate that stuff inside the loop. I do use caching but I’m really trying to test the performance of generating a picture to add to the cache.

But that gives me an idea. It may be more performant to built a sprite sheet first and draw pieces of that into the individual tile pictures.