I chose ARGB8888 because most renderers can support this format natively. If you create a texture with a different format than the ones listed in the SDL_RendererInfo.texture_formats
array, SDL may automatically create an intermediate surface and convert the data for you.
Paletted formats are not supported, though, so you have to go with two surfaces instead. The conversion from a 8-bit surface to a 32-bit surface could look something like this.
palettetorgba.c
#include <SDL.h>
static SDL_Window * window;
static SDL_Renderer * renderer;
static SDL_Texture * texture_background;
static SDL_Texture * texture_rgba;
static SDL_Surface * surface_8bit;
static SDL_Surface * surface_32bit;
static void SetRect(SDL_Rect * r, int x, int y, int w, int h)
{
r->x = x;
r->y = y;
r->w = w;
r->h = h;
}
static double GetNow()
{
return (double)SDL_GetPerformanceCounter() / (double)SDL_GetPerformanceFrequency();
}
static int LoadAndDisplay(int argc, char**argv)
{
int i, done = 0, window_width, window_height;
SDL_RendererInfo info;
double lastupdate = 0;
int surfacebpp;
Uint32 Rmask, Gmask, Bmask, Amask, texformat;
window = SDL_CreateWindow("Palette to RGBA", 100, 100, 200, 200, 0);
if (window == NULL) {
return 100;
}
renderer = SDL_CreateRenderer(window, -1, 0);
if (renderer == NULL) {
return 110;
}
SDL_GetRendererInfo(renderer, &info);
surface_8bit = SDL_CreateRGBSurface(0, 20, 20, 8, 0, 0, 0, 0);
if (surface_8bit == NULL) {
SDL_Log("Could not create 8-bit surface");
return 120;
}
SDL_PixelFormatEnumToMasks(SDL_PIXELFORMAT_ARGB8888, &surfacebpp, &Rmask, &Gmask, &Bmask, &Amask);
surface_32bit = SDL_CreateRGBSurface(0, 20, 20, 32, Rmask, Gmask, Bmask, Amask);
if (surface_32bit == NULL) {
SDL_Log("Could not create 32-bit surface");
return 130;
}
texture_rgba = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 20, 20);
if (texture_rgba == NULL) {
SDL_Log("Could not create RGBA texture");
return 140;
}
SDL_SetTextureBlendMode(texture_rgba, SDL_BLENDMODE_BLEND);
texture_background = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, 200, 200);
if (texture_background == NULL) {
SDL_Log("Could not create background RGBA texture");
return 150;
} else {
Uint8 * data = SDL_malloc(200 * 200 * 4);
if (data == NULL) {
SDL_Log("Memory allocation error");
return 160;
}
for (i = 0; i < 200 * 200 * 4; i += 4) {
data[i] = 0xff;
data[i + 1] = (i % 800) / 4;
data[i + 2] = 0xff - (i % 800) / 4;
data[i + 3] = 0xff;
}
SDL_UpdateTexture(texture_background, NULL, data, 200 * 4);
SDL_free(data);
}
SDL_QueryTexture(texture_rgba, &texformat, NULL, NULL, NULL);
if (texformat != surface_32bit->format->format) {
SDL_Log("Texture format (%s) didn't match surface format (%s)", SDL_GetPixelFormatName(texformat), SDL_GetPixelFormatName(surface_32bit->format->format));
return 170;
}
SDL_GetWindowSize(window, &window_width, &window_height);
while (!done) {
SDL_Event e;
SDL_Rect dst;
double now = GetNow();
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
done = 1;
} else if (e.type == SDL_KEYUP) {
Uint32 sym = e.key.keysym.sym;
if (sym == SDLK_ESCAPE) {
done = 1;
}
} else if (e.type == SDL_WINDOWEVENT) {
if (e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
window_width = e.window.data1;
window_height = e.window.data2;
}
}
}
if (lastupdate + 1.5 < now) {
int x, y;
Uint8 * data = surface_8bit->pixels;
SDL_Palette * palette = surface_8bit->format->palette;
Uint32 state = (Uint32)(now * 1000000.);
lastupdate = now;
/* Scrambling palette */
for (i = 0; i < palette->ncolors; i++) {
palette->colors[i].r = state & 0xff;
palette->colors[i].g = (state >> 8) & 0xff;
palette->colors[i].b = (state >> 16) & 0xff;
palette->colors[i].a = (state >> 24) & 0xff;
state = (state * 1103515245) + 12345;
}
/* Scrambling image */
for (y = 0; y < 20; y++) {
for (x = 0; x < 20; x++) {
data[x] = state % palette->ncolors;
state = (state * 1103515245) + 12345;
}
data += surface_8bit->pitch;
}
/* Converting to RGBA */
SDL_BlitSurface(surface_8bit, NULL, surface_32bit, NULL);
/* Uploading RGBA */
SDL_UpdateTexture(texture_rgba, NULL, surface_32bit->pixels, surface_32bit->pitch);
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SetRect(&dst, -50, -50, 300, 300);
SDL_RenderCopyEx(renderer, texture_background, NULL, &dst, (Sint32)(now * 360) % 360, NULL, SDL_FLIP_NONE);
SetRect(&dst, 50, 50, 100, 100);
SDL_RenderCopy(renderer, texture_rgba, NULL, &dst);
SDL_RenderPresent(renderer);
SDL_Delay(1);
}
return 0;
}
int main(int argc, char* argv[])
{
int result;
window = NULL;
renderer = NULL;
texture_background = NULL;
texture_rgba = NULL;
surface_8bit = NULL;
surface_32bit = NULL;
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) < 0) {
SDL_Log("Could not initialize SDL");
return 1;
}
result = LoadAndDisplay(argc, argv);
if (surface_8bit != NULL)
SDL_FreeSurface(surface_8bit);
if (surface_32bit != NULL)
SDL_FreeSurface(surface_32bit);
if (texture_background != NULL)
SDL_DestroyTexture(texture_background);
if (texture_rgba != NULL)
SDL_DestroyTexture(texture_rgba);
if (renderer != NULL)
SDL_DestroyRenderer(renderer);
if (window != NULL)
SDL_DestroyWindow(window);
SDL_Quit();
return result;
}
I can’t say anything regarding the performance of this conversion. I’m guessing it’s just a simple loop that copies the palette values into the RGBA surface. If you spot a pattern in the overlay data, you might be able to create your own, faster algorithm for the 8-bit to 32-bit conversion.
I can’t think of a way to somehow make the renderer display the 8-bit data directly. With shaders sure, but they’re not available with the SDL2 renderers. There are other projects like SDL_gpu that provide access to them, if the surface conversion solution is not adequate.