Tilengine - a freeware 2D graphics engine with raster effects


Well, I’ve been tinkering with an OpenGL renderer and to be honest I reckon your software rendering is faster than my “accelerated” rendering. Plus I don’t have any support for palette effects yet.

I’ve been tinkering with Tilengine on my various boxes. I can build and run nicely on my windows box (although install_x64 is attempting to copy the sample binaries from a directory that doesn’t exist. I can build everything just fine in 32 bit though.)

On my laptop, running Linux Mint in 64 bit, I’m getting compatibility errors when I try to build the samples. I’ll tinker some more and let you know when I have something a bit more helpful.

Incidentally, I’m needing to modify “simon.c” to get the examples to build. On line 140:
if (ti.tile.index)
should be:
if (ti.index)


I’ve been quite busy lately polishing the engine and adding new features, but as you have found, sometimes I miss something… Both the Windows x64 version and the API for managing tiles are quite recent, I’ll check the installer and rebuild all the samples to test that everything is consistent. You’re right about the line 140 in “simon.c”.

I don’t have a 64-bit linux build yet, in fact I’m quite foreigner to linux and I don’t know if I can cross compile a 64-bit version from the 32-bit distribution that I have. I’ll research about this topic.

Regarding OpenGL, I did some test to ensure that it provides all the functionality I would need to port it to run in GPU. It does, but there wouldn’t be much performance gain. Tilengine has a very sequential nature and the software is optimized at running sequentially, whereas a GPU is mainly a parallel thing that gets wasted if used sequentially. And I also have some low-end hardware (Intel Atom N270 + GMA950 and Intel Centrino Duo + GMA3100) that run the samples at sustained 60 fps but won’t run the OpenGL version (no 3.0 support), so I’m loosing more than I’m gaining.

Let me know any more info or suggestions you may have


In that case, I’ll work in Windows for the time being. I’ll keep tinkering in Linux and let you know if I find a way to get it to link nicely. (To be fair, I can’t get my video card to vsync on Linux due to driver issues so it’s no big loss.)


And it’s alive! This is cool!


Cool, is this with your own engine or with tilengine?
Regarding linux, I’m trying to rebuild the samples with the new library version but I’m having some trouble. Unfriendly linux permissions and search paths stuff. I’ll tell you as soon as I have the new download available


I’ve been modifying my game code to talk to Tileengine and its looking pretty nice so far. I’ve gutted out my shades stuff and started adding calls to Tilengine. Windows works nicely but I dont have Linux running yet. The Vsync issues I mentioned apply to everything, my code, your samples, other games, it’s a failure of the driver related to the switching graphic card systems used in laptops. Other than that, some questions and thoughts:

Do you have any mechanism for animating map tiles? I think a number of Megadrive games did this by DMAing new graphics to video memory each frame – I had a lookup table of tile indexes (1 entry for each tile, value being the index of the actual tile to show). That way I can easily animate all instances of a tile. Most of my uses could be covered by palette rolls but it would still be useful in cases.

Python bindings are working beautifully in Windows 32 bit and 64 bit, although tileengine.py doesn’t detect 64 bit. I’m not at a computer right now but I can suggest a patch soon.

I notice that the tilemaps wrap if I scroll past the end. Is this behavior I can rely on? Does it work into negative coordinates as well? I’m considering abusing this for ‘infinite scrolling’ applications – rewriting whichever half of the map the player isn’t in, for example.

In full screen and large scaling modes, am I seeing a fake shadowmask effect? Very nice :slight_smile: I’m also seeing one pixel apparently wrapping left to right, but I kinda like that – it gives the impression of slightly quirky hardware.

I found EnableBlur. My first response was “woah, too blurry”, but thinking back, my Megadrive was even blurrier, with coloured noise from the RF mod as well :slight_smile: If I use it it’ll be as an option and probably come with a “getting the damn TV to tune in” mini game.

Tilengine seems to fail silently if a file path doesn’t exist or if the image format is not what it expects. I got a lot of blank screens before I convinced GIMP to output the right format and my current pixel editor (ASE) seems incapable of saving the right format. Could I send you some test images to look at? Passing files through the GIMP is messing up my palettes.

I’m already having fun adjusting the background colour per scan line.

Thanks for writing this – is really cool and fills a niche almost other gfx libraries seen to have ignored. :slightly_smiling:


Yes we have :slightly_smiling: actual games didn’t modify the tilemap, they updated the tileset. You can use the function TLN_SetTilesetPixels().
Another way is to use the tilemap animation feature, to replace sequences of tile indexes. I don’t have any example, but the preferred way is the previous one.

At this moment I don’t know if python runtime can tell the version of Windows running. For the time being you can replace the line 102 in tilengine.py from

tln = cdll.LoadLibrary("Tilengine.dll")


tln = cdll.LoadLibrary("Tilengine_x64.dll")

Many Megadrive games took advantage of the heavy horizontal blurring at the RF output to fake more colors and transparencies by alternating vertical strips of colors that got blended together. This effect is designed to replicate this behavior.

Yes, this is a tiny bmp file with the mask you can pass as a parameter to TLN_CreateWindow(). The one included in the samples folder tries to simulate a combination of scanlines and shadow mask, typical in low-res arcade monitors, but you can replace it with your own (or don’t use any at all).

You can safely rely on it. The intended behavior is infinite scrolling left or right. Internally it is implemented as a modulo operation. The “shooter” example uses this capability.

Maybe you are using an outdated version of tilengine. Please take a look at the TLN_GetLastError() function and the TLN_Error constant to get precise info about what is happening.
Image format must be 8 bpp (with palette) standard png. Of course you can send the images to check what the problem is. Internally I use libpng to load them.

Thanks fou your feedback :slightly_smiling: your appreciation and interest is the best reward I can get. I hope you are having fun tinkering with it and that you can create some nice games with its help


I don’t think the 64-bit dlls should be named differently than the 32-bit ones. At least, I think it’s not great that you renamed 64-bit SDL2.dll to SDL2_x64.dll and linked Tilengine_x64.dll to that, since then I have to load two copies of SDL2 if I want to use both Tilengine and a prebuilt SDL2_mixer (for example). (Or at least as far as I know? If there is a way to tell Windows to have LoadLibrary give the same resource to anyone asking for ‘SDL2.dll’ or ‘SDL2_x64.dll’ that’d be good to know.)

I know it’s still an issue to have both 32-bit and 64-bit dlls coexist, but I prefer to do that by putting them in different directories and putting the directory matching my architecture in my PATH, like so:

import os, platform
from ctypes import cdll

_system = platform.system()
if _system == 'Windows':
    bit, arch = platform.architecture()
    if bit == '64bit':
        dlldir = '.\\dll_x64'
        dlldir = '.\\dll_x86'
# etc for other platforms
os.environ['PATH'] = os.pathsep.join([dlldir, os.environ['PATH']])
sdl = cdll.SDL2

But even if you prefer dlls with separate names in the same directory, hey, now you know how to determine processor bit size in Python. :slight_smile:

(Also, while I’m here: I think this is really rad and I’m kind of a little too excited thinking about putting it in my game.)


Thanks for your suggestions, I guess you’re right. I’m new to providing both 32/64 bit versions of a package and I don’t know the recommended practices. I built them with different names because I perform many tests and it’s convenient for me to have both versions running side by side in the same folder. But I understand that in real world, only one version is used, so having both with the same name eases maintenance and linking.

I’m finishing a new release, and I’ll follow your advice and use the same names for both versions.[quote=“Dezro, post:28, topic:739”]
I think this is really rad and I’m kind of a little too excited thinking about putting it in my game
You’re very welcome to use it in your game, let me know if you do!


I’ve released new version 1.6.5. Among other things, it fixes the non-compiling samples noted by @birdrun and uses th same library names for both 32-bit and 64-bit versions, in separate folders as suggested by @Dezro


So I used Tilengine in the Global Game Jam last weekend:

Gamejolt link
GGJ link

Spent a couple of (panicked) hours plumbing Tilengine’s output into Pyglet’s OpenGL interface to do scaling and the like. This seems like a pretty fast setup – I got frame draw times of 1ms even from Python!

Using the scanline callback from Python is pretty slow – several hundred calls of C->Python->C add up pretty quickly but I can usually at least maintain 60fps without too much bother. It seems to be system dependent too – my 6 year old AMD desktop box spends around 4ms in raster calls. On the laptop (i7-3xxx) it’s closer to 8-9ms. I might be able to improve that by clever use/abuse of ctypes, though long term I’ll probably drop most of my graphics handling code to C like a sane person.


I’m glad you used tilengine for your projectin this contest :slightly_smiling:

I haven’t able to execute it. My main computer has an aging GeForce GTX 275 that supports OpenGL up to 3.30, and it complains about not supporting the required 4.20 profile. So I’ve tried on a new laptop with a Core i7-5500, Windows 10 x64 with 4.20 support, but it complains with this message:

Traceback (most recent call last):
  File "ritual.py", line 16, in <module>
  File "pyglet\window\win32\__init__.pyc", line 131, in __init__
  File "pyglet\window\__init__.pyc", line 559, in __init__
  File "pyglet\window\win32\__init__.pyc", line 261, in _create
  File "pyglet\gl\win32.pyc", line 263, in attach
  File "pyglet\gl\win32.pyc", line 208, in attach
pyglet.gl.ContextException: Unable to share contexts

Regarding callbacks for the raster effects, I’ve noticed that is the most difficult and weak part when dealing with wrappers. So I’m planning on implementing a secondary rendering method where instead of callbacks, the host application actively draws each scanline. It will look something like this:

#for each frame
line = 0
while tln.DrawNextScanline():
    #do your raster stuff here depending on line
    line = line + 1

This should cut all the performance loss in the callbacks and also make easier to implement wrappers. Does it sound interesting?

I’m also expanding the built-in window, with support for two players and key remapping…


I’ve completed the secondary drawing method for wrappers that doesn’t use callbacks. This is a working python snippet on how to draw to your render target:

# draw frame line by line doing raster effects
tln.BeginFrame (frame)
line = 0
drawing = True
while drawing:
    raster_effect (line)
    line += 1
    drawing = tln.DrawNextScanline ()
frame += 1

raster_effect() is a normal python function with the same internal structure as the old callback mechanism. As you can see, the rendering is now performed actively from the application, without the performance-eating callback mechanism.

There are also the new functions tln.BeginWindowFrame(int) and tln.EndWindowFrame() in case you want to draw to the built-in window.

I haven’t yet published the release, but if you’re interested in this feature for your project, please let me know and I’ll send you the new dll and wrapper.


Hmmm, I haven’t seen the OpenGL context error before but I have seen some odd results from gdebugger which might make sense of it. Guess I’m getting the blue book back out :sightly_smiling:

As for the raster method, it looks feasible. If the ctypes wrapping is as significant as i think it is, it should be faster. I’ll get some performance figures when I’ve got the update. I’ll send you my email address; if you’re happy to send your code-in-progress I’ll be happy to beta test :smile: