Tag Archives: develop

OpenGL antialiasing in Android and transparent textures

I tried to replace the legacy 2D rendering code of WordMix, which uses the native Android canvas methods, with an OpenGL renderer to allow for fancy effects and animations.

First attempt

simple texture with full bleed image, no border

Because the tiles are simple rectangles with round corners, I created a texture with gimp and rendered a quad in OpenGL. The texture had no mipmaps and was filtered linear for both, minimizing and magnifying. When rotating that quad, I got the typical “staircase” lines, because I did not use anti-aliasing / multisampling. The result looks rather horrible:

no multisampling, no mipmaps, simple texture result in typical “staircase” borders

You can see two effects, one if it being the clear staircase borders, where the texture is not linear filtered, and you see the round corners of the texture with a grayish border, I’ll explain in the next paragraphs.

Multisampling emulation to remove “staircase”

So how to achieve multisampling in OpenGL ES 1.1? The answer I found is quite simple and easy on the hardware: use a texture with a transparent border and linear texture interpolation will do the rest. So I modified the texture to include a transparent border and rendered the quads slightly bigger to fill the same amount of pixels.

texture image with transparent border

The result looked better but I was not satisfied with the borders. I saw the interpolations but there is still a very visible “staircase”. Plus it seems, that the borders are blended with a black color, which can be seen on the overlapping tiles:

no mipmaps, texture with transparent border, still visible “staircase” and dark colored border

This is in fact due to my texture, which had the transparent pixels assigned the color black. The OpenGL interpolation would just average two neighbour pixels, which would calculate like

(argb(1, 1, 1, 1) + argb(0, 0, 0, 0)) / 2 = argb(0.5, 0.5, 0.5, 0.5)

which is a semi transparent gray color tone.

Monkeying with gimp for transparent pixel

So how to create a texture, where the transparent pixels have the color white? Gimp seemed to screw up the color of transparent pixels even though when exporting my work as png file, it offers to keep the color of transparent pixels.

The trick: combine all visible planes, create an alpha channel and change the color layer. If you have uncombined planes, the result is unpredictable and the colors are screwed up.

So now I had a texture with a white but fully transparent border (value 0x00FFFFFF) and I’d expect the calculation to be

(argb(1, 1, 1, 1) + argb(0, 1, 1, 1)) / 2 = argb(0.5, 1, 1, 1)

But I still got the same result:

texture with transparent + white border: still black border in Android

Bitmaps with transparent pixels in Android

So why is my border still black, while the texture has white transparent regions? I checked the loaded Bitmap with this code after loading the png resource:

Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.stone);
Log.d("texture", bmp.getPixel(0, 0)); /* result: 0 */

Why is the result 0?? I’d expect a 0x00FFFFFF, but either Androids Bitmap loader premultiplies the alpha or recompresses the image file on compile, although I did place the image in the res/drawable-nodpi folder.

But apparently Bitmap and Canvas throw away all color information, when drawing with an alpha value of 0. This results in a fully transparent, but black canvas:

canvas.drawColor(Color.argb(0, 255, 255, 255), Mode.SRC);
Log.d("texture", bmp.getPixel(0, 0)); /* result: 0 */

while the following results in a white canvas, which is almost transparent (1/256):

canvas.drawColor(Color.argb(1, 255, 255, 255), Mode.SRC);
Log.d("texture", bmp.getPixel(0, 0)); /* result: 0x01FFFFFF */

Good to know, so now I create my texture with a border that is almost transparent, but not completely (alpha value 1/256) and the color white, which should be hardly visible, calculating like:

(argb(1, 1, 1, 1) + argb(0.01, 1, 1, 1)) / 2 = argb(0.505, 1, 1, 1)

I checked with above Log code and indeed got the value 0x01FFFFFF. So at least the Bitmap was loaded correctly now. But I still get a  black border and the result looks the same. Why?

Creating OpenGL textures with unmultiplied alpha

I found a post and bug report that apparently the GLUtils.glTexImage2D() screws with the alpha and colors too, creating texture values of 0x01010101, which gets blended with the nearby white pixels on linear filtering. What the…?

The post suggests a workaround to not use GLUtils to load the Bitmap into an OpenGL texture but use the original GL10.glTexImage2D(). While the code in that post is not very efficient, it does result in nice and smooth blended borders. Of course the use of mipmaps helps too to make the texture smooth when minified:

texture with 0x01FFFFFF almost-transparent border and use of original GL10.glTexImage2D method and mipmaps

Summary

Several culprits were found to make antialiasing work with an Android App using OpenGL ES 1.1:

  1. Create textures that have transparent borders, so linear filtering emulated oversampling at polygon borders
  2. Make sure the transparent border of your texture contains color values, which will “bleed” into the border pixels of the texture.
  3. If you use mipmaps, make sure you have enough transparent border pixels or set GL_TEXTURE_WRAP to GL_CLAMP.
  4. Double check result, because gimp does screw up when having multiple layers, that are merged when exporting as png image.
  5. Androids Bitmap loader and Canvas code seems to zero out the color values when alpha is 0. The workaround to keep the color values on load: Use colored pixels with alpha value of 1 (of 255).
  6. The GLUtils.glTexImage2D implementation premultiplies alpha values with color values, resulting in very dark color, instead of the white I wanted. Use the GL10.glTexImage2D directly (example in this post).

Using mipmaps and adding a nice shadow texture results in a screen, that looks very similar to the original, but which is much faster:

final result with all workarounds, mipmaps and shadows

WordMix learning Russian

The next WordMix and WordMix Pro release will include support for Russian, Portuguese and Dutch as dictionary languages. I had a lot of fun with the Cyrillic encoding of characters and especially the database for the words as I learned that a lot of Linux tools are still not ready for handling multi byte character sequences correctly.

Mostly the tool tr kept me busy, when I tried to convert lower case letters to upper case. The normal approach of

tr [:lower:] [:upper:]

only seems to work for the ASCII character set. If manually used on UTF-8 data, it screws everything up even more, like in the command:

tr \
  абвгдеёжзийклмнопрстуфхцчшщъыьэюя \
  AБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ

The trick was to use tr on the original KOI8-R encoded data (which is 8 bit), for which I also had to pass KOI8-R encoded parameters to the tool, which was a pain inside an otherwise UTF-8 encoded shell script. So I tried to read the KOI8-R encoded parameters from a file before passing it as arguments so I don’t screw up my shell script.

It took me several hours and attempts to find that out and to get all the encodings right, so now a working Russian dictionary is available. 🙂 It won’t be shipped by default though, so it needs to be fetched from the Internet once by the game, on first use.

Of course the global ranklist is prepared for the new languages as well.

manually enabling deflate filter

Oh my, my webhoster has the deflate output filter disabled by default, that enables gzip compression of outgoing content. This is important for huge xml/json data from webservices that travel over mobile networks and easily reduces used bandwith to up to 10%.

Putting this in my .htaccess did the trick:

AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript

You can analyze the traffic with Wireshark, Firebug or use an online tool:

Make sure, your mobile app sets the Accept-Encoding of the request accordingly.

 

Hardware acceleration on SGS2 with Android 4.0

Starting from Android 3 (API level 11), there is a hardware renderer for 2D graphics, which drastically increases performance. The hardware acceleration was disabled by default and had to be enabled by the developer by declaring in his AndroidManifest.xml file:

<application android:hardwareAccelerated="true" ...>

According to the android documentation, that value changed to true starting with API level 14:

The default value is "true" if you’ve set either minSdkVersion or targetSdkVersion to "14" or higher; otherwise, it’s "false".

This is true for some devices (like the HTC Sensation with Android 4.0.3), but does NOT apply for the Samsung Galaxy S2 with official Android 4.0.3 and 4.0.4.

Applications without above attribute explicitly set to true are not hardware accelerated automatically on that device. On the HTC Sensation they are.

So don’t forget to declare that attribute in your AndroidManifest.xml file, if you want hardware acceleration on all devices.

Users can force-enable the use of GPU rendering in the developer options, which can be used as a workaround with the risk of incompatible applications yielding render errors.

WordMix 2D view must not use hardware acceleration

Currently, the 2D view of my WordMix game uses some features of Canvas, that are incompatible with hardware acceleration and results in display bugs. These glitches did not occur on my Samsung Galaxy S2, because it was not hardware accelerated as stated above, but occured on another device, a HTC Sensation with Android 4. Took me a while to figure out, what exactly was going on, but after declaring

<application android:hardwareAccelerated="false" ...>

fixed it for the time being, until I changed the render code to be hardware accelerated.

android.hardware.faketouch vs. android.hardware.touchscreen

By default, an Android application requires the feature android.hardware.touchscreen, which is then used for filtering in Google Play, so only devices, that have touchscreen capabilities, will be able to install that app.

Besides that, there is a more basic feature, android.hardware.faketouch; android docs state:

If your application requires basic point and click interaction (in other words, it won’t work with only a d-pad controller), you should declare this feature. Because this is the minimum level of touch interaction, your app will also be compatible with devices that offer more complex touch interfaces.

If the application does not require touchscreen features, it is recommended to set android.hardware.touchscreen to not be required, but declare android.hardware.faketouch instead, so I did this for WordMix, which should work with faketouch devices, too:

<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.faketouch" android:required="true" />

If you do that, check the results on Google Play, which shows the number of supported devices:

  • touchscreen required, faketouch not required: 1500
  • touchscreen not required, faketouch required: 860
  • neither required: 1800

That is odd and not according to the documentation. For example a Samsung GT-S5360 seems to support touchscreen, but not faketouch. The Samsung Galaxy S2 supports both. You can include all devices by setting touchscreen to be not required, which then includes all faketouch devices, but also all devices that have even less input capabilities.

First playable version of Freebloks for Android

Freebloks-0.0.1_pre1.apk

I just pushed a first playable version of Freebloks for Android, a port of Freebloks 3D to the Android system. It’s a very early development version and very rough, but you can start a single player game and join a network game and place stones. The user interface still needs a lot of love, but I might push a preview version on Google Play soon. Please get involved on GitHub, if you want to contibute.

Here are two screenshots:

 

Creating a custom LSF email router using exim

The University of Applied Sciences Weingarten, as many others, uses an installation of LSF (Lehre Studium Forschung) to manage students, departments and courses.When working at the datacenter of the university, I was part of a project to make the data of the LSF data available to the email system, to make LSF the primary system to connect user accounts with departments or classes. While accounts were primarily managed using LDAP, the system was not flexible enough to hold information about enrolled courses or roles in LSF, like professor, student, assistant, member of department X, etc.

Connecting LSF to the email system

News article: LSF email router

A custom webservice within the LSF framework however, replied a query with a nice plaintext list of user account names, which easily can be mapped to the canonical email adress of each given account. So we have the data, now how do we route the emails?

The datacenter ran two powerful Cisco Ironport appliances, used as MX, filter and first email router. I decided to put the lsf-email-router part on a separate Linux machine, the lists.hs-weingarten.de server, already running exim4 and an instance of mailman for the faculty. The Ironport appliances already routed every mail for lists.hs-weingarten.de subdomain to the lists server.

Special addresses

The exim4 router should detect special LSF email addresses (starting with lsf-), querying LSF for the data, and forward the email to the received list of user accounts, injecting the emails back into the email system to the main mail routers, that resolved account names to actual mailboxes.

Several adresses and queries were prepared, which can be easily extended, for example:

  • lsf-einrichtung-XXX@… (department XXX)
  • lsf-veranstaltung-xyz@… (all members of course xyz)
  • lsf-einrichtung-XXX-funktion-YYY@… (department XXX, role YYY)
  • lsf-veranstaltung-XYZ-20112@… (members of course xyz of winter semester 2011/12)
  • lsf-studiengang-XY@… (all students of the field XY)

A complete list of available dynamic addresses can be found in the service portal: http://rz-serviceportal.hs-weingarten.de/lsf-email-verteiler

The custom script

Two ninjas created a short perl script to be called from exim4, that did the following tasks:

  1. Separate the parts of the email address and check for validity
  2. Determine cache file for the special list and exit script, if cache file is younger than 6 hours. This is done to not query the webservice more often than necessary and using 6h old data was acceptable in this case.
  3. If the cache file does not exist or is older, the LSF webservice is queried for the list of accounts. On success, the cache file is created or updated.
  4. On failure, the LSF might be down, but the email system shouldn’t. If a cache file exists and is younger than 1 week, use it anyway.
  5. If the cache file is too old, delete it and fail. Do not use cached file that is too old but rather fail routing the email, which will result in a mailer daemon message back to the sender.
  6. The perl script exits with an error code 0 or 1 to indicate success or failure.

Now we have a cache file with valid email addresses, or we don’t.

The exim configuration

Integrating the perl script into exim, using exim4’s splitted config, was demeaningly easy, but was actually the tricky part (file /etc/exim4/conf.d/router/475-lsf-router):

lsf_router:
    driver = redirect
    local_part_prefix = lsf-
    file = ${run{/usr/bin/perl /etc/exim4/lsf-router.pl \
           --lsflist $local_part_prefix$local_part} \
           {/var/cache/lsf-router/$local_part_prefix$local_part} \
           {/var/cache/lsf-router/error}}

This router entry works as a redirect router, giving it a file to expand email adresses from. Like a boss, we used exim’s ${run} string expansion to run our script every time an email is routed that maches the lsf- local part prefix of the address. Depending on the success or failure of the ran command, the expansion returns one of two strings. On success, the used filename is the whole local part of the destination address, on failure it is a non-existing path, which will result in a permanent routing failure.

Voilà, our LSF email router is done and working. Sending an email to lsf-veranstaltung-XYZ@… results in the email being routed to the Linux box, the script will be run to refresh the cache for the special email address, querying LSF at most every 6 hours, expanding the data and exim will forward the email to all user accounts.

It’s like using mailman for managing university courses and departments – except that it’s great.

For further information, feel free to contact me or rechenzentrum@hs-weingarten.de

See also:

kwin-style-crystal repository on GitHub

I prepared a public repository on GitHub with all the code of the crystal window decoration, from crystal-0.5 for KDE-3.2 to the current crystal-2.2.0 for KDE-4.9 beta.

Please feel free to contribute:

There are also ongoing discussions about integrating the crystal window decoration into the official KDE SC, moving all development into the offical KWin tree, next to the current default Oxygen deco.

Currently all major distributions ship optional binary crystal packages. If not, see the official crystal project page on kde-look.org.

Crystal-2.1.1 casting shadows ahead

I just added shadow support in Crystal-2.1.1 (for KDE 4.8) and Crystal-2.2.0 (for KDE 4.9), as well as some minor fixes. Since KDE 4.8 the window decoration itself is responsible for rendering shadows when compositing. Because Crystal uses an unstable kwin API to support tabbing, versions before 2.2.0 are unable to compile, which has the exact same features than 2.1.1.

The shadows aren’t the prettiest but better than none. Hopefully I’ll have time to do further improvements, refactoring, increased speed, stability and graphical improvements.

I hope to get a stable crystal release, which would blend in well with the rest of KDE SC.

Get crystal from the project page on kde-look.org or wait for your distribution to catch up:

WordMix Pro going 3D

WordMix in the Google Play Store

WordMix Pro arrived in the Google Play Store. While WordMix will stay free of charge, selling a pro-version will support the developing of the game and helpkeeping the free version adfree. So far the Pro version does not have much to offer but some more background images for the game. I do not intend to make the gameplay differ between the free and the payed version.

A 3D OpenGL interface for the game is under development and will be available in the pro-version soon.

Check out the first screenshot:

Chicken Tournament source code on GitHub!

I finally published the source code of my biggest game so far Chicken Tournament, which I created about 10 years ago in C++. Starting in 2002, at the age of 18, it took me 13 month to create the game and a lot of my blood went into the game, and I’m still proud of the result. Over the years many people asked me for the source code or parts of it to study, and I never hesitated showing the code around, but never wanted to actually make it OpenSource.

Now the complete source code of the game (excluding source of the models and the video so far) is available on GitHub and contains the resources, the game logic, libraries and the Microsoft Visual C++ 6 project. Along the source, the DirectX7-SDK is needed to successfully create a binary.

The game is not under development anymore and big changed are unlikely to happen. Please respect my work.