18

Is it possible to embed an image within a program using SDL which can be used at run time.

For example, I have a program which brings up a splash screen on startup containing the logo and copyright information. Rather than having this image in a bitmap file and using SDL_LoadBMP to load it to a SDL_Surface. I would like to have the image embedded in the program binary, to stop someone potentially changing the splash image and copyright name.

Does anyone have any suggestions on ways to do this? Example code would be great.

Scott
  • 438
  • 6
  • 18
  • Just as a side note: If someone wants to change their splash screen, they're going to change your splash screen. That shouldn't be your only motivation for doing this. –  Aug 24 '13 at 19:11
  • 2
    That is true, if someone really wanted to they probably could. However my other motive is the program is a small tool that needs to portable and easy to use for people with fewer computer skills, so having the whole program as a single file would be much more preferable than a folder of small images that have to be in the right location. – Scott Aug 24 '13 at 19:15
  • This should certainly be possible (not *trivial*, but possible). It would seem to me a properly configured [`SDL_RWOps`](http://wiki.libsdl.org/SDL_RWops?highlight=%28%5CbCategoryStruct%5Cb%29%7C%28SDLStructTemplate%29) in conjunction with a call to [`SDL_LoadBMP_RW()`](http://wiki.libsdl.org/SDL_LoadBMP_RW?highlight=%28%5CbCategoryAPI%5Cb%29%7C%28SDLFunctionTemplate%29) can pull an image from just about anywhere you configure it to do so, including a static internal buffer built at compile time. Whether this "solves" the issue you're identifying is another matter. – WhozCraig Aug 24 '13 at 19:15
  • Just had a look at how these work and from my understanding if I turned the bitmap image into a character array in my source code. I could use these commands to make it load the bitmap from the const variable at runtime? – Scott Aug 24 '13 at 19:21
  • @Scott Exactly, and for ways to get that static image, Dietrich has some options below. Once you have the static image in bin-format in the executable, it is simply a matter of setting up a `SDL_RWOps` to use a custom-function set (written by you) that uses that image as its data "source". You get the idea. – WhozCraig Aug 24 '13 at 19:58
  • Indeed I do, Just need to setup SDL_RWOps to retrieve the data into a stream, and SDL_LoadBMP_RW to load the bitmap from the SDL_RWOps stream. Thank you very much. – Scott Aug 24 '13 at 20:08

3 Answers3

19

Embedding a file in an executable is easy but there are some gotchas, there are several ways to do it including some portable and non-portable ways.

Using #embed

This will reportedly be part of C23. It may be on track to appear in C++26 as well. Check whether your compiler supports this feature. In the future, this may be the most portable and straightforward way to embed binary data.

static const unsigned char IMAGE_DATA[] = {
#embed "myimage.bmp
};

See WG14 n2592 for the feature proposal.

Advantages: simplest, easiest

Disadvantages: your compiler probably doesn’t support this yet

Convert the image to C code

Write a script to convert the image to a constant array in C. The script would look something like this in Python:

#!/usr/bin/env python3
print("static const unsigned char IMAGE_DATA[] = {{{}}};".format(
        ",".join(str(b) for b in open("myimage.bmp", "rb").read())))

Just pipe the output to a *.h file and include that file from one other file. You can get the size of the file with sizeof(IMAGE_DATA).

Advantages: portable

Disadvantages: requires Python to be installed, does not work if array is too large for compiler, requires adding a custom step to the build system

Convert the image to an object file

This is more platform-dependent. On platforms with GNU binutils toolchains (e.g. Linux) you can use objcopy, I think bin2obj works on Microsoft toolchains.

Advantages: works everywhere

Disadvantages: non-portable, requires adding a custom step to the build system, the custom step might be tricky to get right

On GNU binutils toolchains, with objcopy

The objcopy program lets you specify binary as the input format, but then you need to specify the architecture explicitly... so you will have to modify the command for i386 and x64 versions of your executable.

$ objcopy --input binary --output elf32-i386 --binary-architecture i386 \
    myimage.bmp myimage.o

You can get the data from C by using the following declarations:

// Ignore the fact that these are char...
extern char _binary_myimage_bmp_start, _binary_myimage_bmp_end;

#define MYIMAGE_DATA ((void *) &_binary_myimage_bmp_start)
#define MYIMAGE_SIZE \
    ((size_t) (&_binary_myimage_bmp_end - &_binary_myimage_bmp_start))

Use an assembler directive

Paradoxically, embedding a static file is fairly easy in assembler. Assemblers often have directives like .incbin (which works with GAS and YASM).

Advantages: works everywhere

Disadvantages: non-portable, assembler syntax is different between platforms

(Windows) Embed the file as a resource

On Windows, you can embed resources in an EXE and then get the resources using library calls.

Advantages: probably easiest if you are on Windows

Disadvantages: only works on Windows

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • I had considered using resources on windows, however my program is cross platform, so I was looking for a more generic solution. As I have a lack of assembler knowledge and the images are small I think I can get away with the "Convert image to C" option. However I wouldn't mind knowing more about the object file method if you could point me in the right direction. My main development platform is linux. – Scott Aug 24 '13 at 19:50
  • Sure, I added some more instructions for how to use `objcopy`. – Dietrich Epp Aug 24 '13 at 19:57
  • Thank you very very much. This question is going to become a very useful resource for my programming. I'm extremely grateful :D – Scott Aug 24 '13 at 20:03
  • ...or you could save your image as ppm, add ppm loader to the project, and embed images as a text string into your code. And you could use Qt 4 resource system which is cross-platform and supports embedding images into application. – SigTerm Aug 25 '13 at 03:53
  • Great! But when I get that `image.h` file, how can I use it in c++? For example, usually in qt, I can just set a string to the image file path, (`string str1 = str0 + "splash.png";QPixmap pixmap(str1.c_str());`). What should I do now with `image.h` file? Thanks – Daniel Feb 22 '14 at 20:22
  • @Daniel: You'll have to read the Qt documentation for how to read an image from memory. – Dietrich Epp Feb 23 '14 at 04:08
  • 2
    GIMP has an export image to C format if anyone is interested. – j0h Sep 24 '17 at 01:45
4

You can export the image as .xpm format (in gimp) and include it to your code. But you will need SDL_Image.h to load it as SDL_Surface.

As it is in this doc, is really simple:

//To create a surface from an XPM image included in C source, use:

SDL_Surface *IMG_ReadXPMFromArray(char **xpm);

A example in C/C++:

#include <SDL/SDL.h>
#include "test.xpm"
#include <SDL/SDL_image.h>

SDL_Surface *image;
SDL_Surface *screen;

int main(int argc, char **argv)
{
    SDL_Init(SDL_INIT_EVERYTHING);
    screen = SDL_SetVideoMode(800,600,32,SDL_SWSURFACE);
    image = IMG_ReadXPMFromArray(test_xpm); //the .xpm image is a char array. "test_xpm" is the name of the char array
    SDL_Rect offset;
    offset.x = 0;
    offset.y = 0;
    SDL_BlitSurface(image,NULL,screen,&offset);
    SDL_Flip(screen);
    SDL_Delay(5000);


    return 0;
}

I hope this helps.

drift3r
  • 41
  • 1
3

With gimp you can save a image as c code.

carlos
  • 1,261
  • 1
  • 12
  • 15
  • 2
    This is true, however gimp only exports the raw pixel data. Whereas the I need to keep the file in correct image format to use in SDL. – Scott Aug 24 '13 at 20:15