ArdBitmap: Bitmap library & tools

Hi all,
I finally unified all my custom “draw-bitmap functions” in a library, also I improved the image compressor with Sketch autogeneration from images or animated gifs.

Library features:

  • Works with compressed & uncompressed bitmaps.
  • Real-time image resize (downscale).
  • Horizontal/Vertical mirroring (fast).
  • Bitmap alignment.

Bitmap compressor:

  • Compatible with PC/MAC/Linux (made with Java).
  • Good compression (better than Cabi).
  • Supports PNG, GIF (also animated gifs) & JPG.
  • Autogenerate Sketchs from images or animated gifs (great for no-developers).

GitHub:

Sample video:

Notes:

  • This is my first time working with Github, I have to learn a lot about how to use it correctly. :sweat:
  • Please, contact me if you need help to integrate the lib with your demo/game or to use the bitmap compressor.
  • I tried to include bitmap rotation, but performance is not good for unresized images. Anyway, 180º image rotation is supported by double mirroring (MIRROR_HOR_VER).
  • Now, it’s time to work on the voice library, I hope it’s ready before Christmas. :wink:
7 Likes

Very interesting. Compression + mirroring could really help reduce sketch size a lot!

I’m wondering what draw method do you support? Is it always white sprite with transparent (like drawSelfMasked in Arudboy2)? Or can you have white/black sprite with transperent (like drawPlusMask in Arduboy2) ?

1 Like

Fantastic work! With this library, we can really see some great things being developed in 2017!

I’m going to take a look at it over the next few days and see if I can come up with something weird. :smiley:

1 Like

@igvina,

You may wish to consider making the constructor argument a pointer to the screen buffer instead of a pointer to the Arduboy or Arduboy2 library object, since that’s all the functions require. This way, you wouldn’t need the #ifdef Arduboy_2 conditional compile code, and it would work with other libraries with different class names. This would make it better suited to be turned into an actual Arduino library, as well.

Also, we can still discuss integrating these functions into the Arduboy2 library, if you’re interested.

2 Likes

Thanks for the feedback. I hope people can include more graphics & more frames in the sketch to create more complex games.
Draw methods support white/blank drawing, but the lib is not optimized for painting masked bitmaps (you need to make 2 draw calls to paint masked images). Anyway, masks can be compressed a lot, and you can reduce a lot the size of the sketch.

Thanks! I hope to see new games that can take advantage of the lib. Write me if you need help with it. Also, you can save a lot of space compressing the big images that you included on Circuit Dude. :wink:

Thanks! I thought that I need to include Arduboy.h or Arduboy2.h, but I see that I can include Arduino.h and be directly compatible with both libs. I will change it today. :slight_smile:

Sure! You can integrate these functions into your lib. Write me if you want to discuss the integration. Thanks!

@igvina,
Sorry for taking so long getting back to you on this. I’ve spent some time considering the options and experimenting. I was going to send you a private message about it but I think what I have to say may be of interest to others (if they don’t find it TL;DR :wink: )

The ArdBitmaplib functions could be added to Arduboy2 in a number of ways:

  • As new main functions, so you would use them like arduboy.drawBitmapResized(...). This approach would require renaming at least some of the functions because there are already existing drawBitmap() and drawCompressed() functions with different behaviour.
  • As a subclass in the main class, like the audio class, so you would use them like arduboy.ardBitmap.drawBitmapResized(...).
  • As a separate class included in the Arduboy2 library, similar to the Sprites class, so you would use them as you do now:
ArdBitmap ardbitmap;

  ardbitmap.drawBitmapResized(...);

I would likely choose using the separate class approach. It makes things easier to maintain and, personally, I think it makes the code in the sketch a bit clearer and more structured.

The advantages of adding these functions to Arduboy2 are:

  • They would be instantly available to any sketch using the Arduboy2 library. You wouldn’t have to copy the library files into your sketch or include a separate library.
  • The functions could access the screen buffer directly, like the current Sprites class does, instead of using a pointer to the buffer. This would eliminate the need to pass a pointer as an argument to the constructor. It would likely generate a bit less code as well. (More on this later.)
  • The functions could use the defined WIDTH and HEIGHT screen dimensions instead of needing the matching definitions that your separate library requires. (Again, more on this later.)

The disadvantages of adding these functions to Arduboy2 are:

  • Adding more bitmap drawing functions could increase confusion with using the library. There are already the main drawBitmap(), drawSlowXYBitmap() and drawCompressed() functions, plus those in the Sprites class. I kind of regret even adding the Sprites class for this reason.
  • The new functions would be an additional thing that had to be maintained and documented. If bug fixes were required, or you wanted to make enhancements, it might not happen as fast as it could with a separately maintained library under your control.
  • The compressed bitmaps require the use of a separate program to compress them. This would either have to be included and maintained in the library, or it’s location would have to be made known in the documentation. (This is already a problem with the existing bitmap functions in Arduboy2.)

I feel that the above disadvantages outweigh the advantages, so I think you should just keep ArdBitmaplib as a separate library. I have some suggestions for you that would address much of the above advantages.

Make the library compatible with the Arduino 1.5 Library Specification

This would allow you to have it included in the Arduino Library Manager, making it easy for users to install in the Arduino IDE.

I also suggest that you drop lib from the end of ArdBitmaplib and just call it ArdBitmap. It’s not common to put lib at the end of an Arduino library’s name. Almost none of the existing Arduino libraries do.

To make your exiting repository Arduino Library Manager compatible you mainly just need to:

  • Move the files into appropriately named folders.
  • Add library.properties and keywords.txt files.
  • Remove the local copies of the library files from the included example sketches. They will then use the installed library.
  • (I’ve done all this. See below.)

Make the library a template class

This is a bit radical for an Arduino library but it would allow it to directly access the screen buffer. It would also allow you to specify the screen width and height as parameters instead of having to hard code matching values in the library’s header file.

Creating an instance of a template class is a bit more work than a regular class, but it’s still pretty straight forward.

Another advantage of using a template is that code can be conditionally compiled based on macro definitions provided by the sketch. Regular classes in Arduino libraries are logically “compiled” separately, before the sketch code, so a sketch can’t pass a #define to them. However, templates are logically “compiled” when the sketch includes the header file that contains them, so they can see any #define that comes before the #include.

Your library uses two macro values, SPEED_HACK and RESIZE_HACK to optimize for speed or code size. With a template class, a sketch could set these or similar macros to influence the generated code, without the need to edit the library source code.

My tests have shown that generated code size, using a template class in a separate library, is the same or sometimes even smaller than having the class included in the Arduboy2 library.


I’ve already forked your library and made the above changes. My fork is at:

Edit: I’ve deleted my fork because its changes have been merged into @igvina’s original repository.

  • ArdBitmaplib has been renamed ArdBitmap everywhere. You would probably want to rename the repository itself on GitHub, as I did with my fork.
  • I changed the library version number to 2.0.0, in accordance with SemVer rules, since using a template changes the API. Since the library hasn’t been previously released as an official Arduino library, this strictly isn’t necessary. You could leave it at version 1.x.x if you wish.
  • I changed the SPEED_HACK macro to NO_SPEED_HACK and used #ifndef instead of #ifdef, since a sketch can’t undefine a macro that’s been defined in the library. You can change the name or logic if you wish. I didn’t document the use of these macros in README.md because I don’t know if you want them to be “officially” available to the user as part of the API.
  • I changed README.md with instructions on using it as an Arduino library and as a template class.
  • I set architectures=* in library.properties, meaning all CPU architectures are supported, since there’s no AVR specific code in the library. I tried your CompleteTest example sketch on my ARM based prototype and it ran fine. The fps remained above 60 for all the tests :smile:

I can create a pull request from my fork to your repository on GitHub. Or, you could pull my changes to a local working repository, make any desired changes, and then push to your GitHub repository.

If you want to submit the library for inclusion in the Arduboy Library Manager, I can help with that. I’ve already done Arduboy2, ArduboyTones and ArduboyPlaytune.


For anyone else who’s read through this whole thing, I hope you didn’t find it a complete waste of time :disappointed:

1 Like

Hi @MLXXXp,

thanks for all the feedback about how to improve the library. I really appreciate it.
All the changes that you comment seems good to me. As I’m a Github noob I think it’s better that you can modify directly the master repo with your improvements. Also, I would appreciate your help to submit the library to the inclusion in the Arduboy Library Manager. Thanks!

@igvina, I’ve sent you a private message about this.

1 Like

I hate to necro a thread, but are there any tips for making ArdBitmap run more quickly? I get some pretty serious slowdown happening when I get five or six 20x16 sprites on screen at a time.

ArdBitmap will be inherantly slower because of the extra features it supports (e.g. mirroring).

Which features are you using?
Are there ways you could do without them?

Unfortunately, the reason I decided to refactor with ArdBitmap is because it supported mirroring (compression was just a nice side effect). I’ve got some complex rotation animations that I’m mirroring to save program memory. The compression and mirroring are the only features I’m using. Is the limited power of the Arduboy gonna make me choose between mirroring and program space?

(This is the project in question: https://github.com/jessemillar/crates/tree/fb542c8208a89eaa4eae69b77c549494cb7d0b23)

If you’re using both mirroring and compression then that’s probably what’s causing the slowdown.
One on its own would be a large slowdown anyway, both together makes it even slower.

From the looks of it you’ve got plenty of space and could probably find some savings elsewhere.

You have several options:

  • Replace some of the ardbitmap.drawCompressed that doesn’t have mirroring with arduboy.drawCompressed
  • Sacrifice the mirroring and using Arduboy2’s built in compression instead of ArdBitmap’s compression
  • Try @filmote’s mirroring system to see if that helps, but that would mean mirroring and compression are mutually exclusive
  • Attemp to write your own ardbitmap optimisations
  • Learn assembly and rewrite some of the ardbitmap routines to use assembly :P

There’s quite a few places you could probably optimise a bit.
For example your Vector doesn’t need to be using dynamic allocation and some of your types could be optimised (e.g. don’t use int if you don’t need numbers larger than 255).
(Also switching to brads or only using radians instead of mixing radians and degrees may or may not help.)

1 Like

All great tips! Thank you! I’ll experiment.

1 Like

I have a hacked version of the arduboy.drawCompressed() that supports mirroring in my Karateka and Lode Runner games. It added surprisingly little code to the original drawCompressed() but only mirrors horizontally.

Look under the src/utils directory. You will see I have extended the Arduboy2 library and in my main .ino file I declare a reference to the extended version as ahown below:

#include "src/utils/Arduboy2Ext.h"
...
Arduboy2Ext arduboy;
2 Likes

I’ll definitely check this out. Thank you!

1 Like

Hi @jessemillar,

you can use ArdBitmap mirroring feature with uncompressed images, speed should be similar.
Decompression and resizing are a lot more CPU intensive.
I would use compression only on big images (with a good compression ratio). If your game requieres a good frame rate(30/60) it’s recommended to use uncompressed images during the gameplay.

1 Like

@igvina To create an uncompressed image, can I use TEAM A.R.G.'s sprite sheet converter? I didn’t see any options in your Java converter for creating an uncompressed image.

yes, you can use the Arduboy bitmap editor that you prefer. Please, send me a private message if you have more questions. Good luck with your game!

2 Likes

I had a few issues integrating this library into a class based framework I am using.

The sample code …

#include <Arduboy2.h>
Arduboy2 arduboy;

#define ARDBITMAP_SBUF arduboy.getBuffer()
#include <ArdBitmap.h>
ArdBitmap<WIDTH, HEIGHT> ardbitmap;

… works perfectly in a normal INO but the ARDBITMAP_SBUF define was causing grief.

Thanks to @Pharap’s wisdom, I was able to get around the problem by adding the following line of code to the library alongside the other #defines. It removes the need to have an instantiated arduboy instance and uses the already visible sBuffer variable.

#define ARDBITMAP_SBUF Arduboy2Base::sBuffer

1 Like

I think that semicolon is a typo.