Blit 8 bit to 32 bit (later add interpolation/extrapolation for palette)

Hello,

I am new here, I am exploring the possibilities of converting OpenXcom 8 bit color game to 32 bit color game so that it can look a bit better when the game shades the pixels/colors when they in shadow/light and such.

Currently the game uses a palette for battlescape divided into rows of each 16 colors. 0 to 15 basically.

By converting it to true color/32 bit colors it will become possible to choose many more shadows and thus the game should look better than.

Currently the game stores basically all pixel information in 8 bits. I have identified the main blitting routine where the 8 bit pixels are blitting into SDL, this is in Surface.cpp (which is a custom class inside OpenXcom source base which wraps SDL_surface and such):

(I have currently modified it to try and write a custom blitter):

/**

  • Blits this surface onto another one, with its position

  • relative to the top-left corner of the target surface.

  • The cropping rectangle controls the portion of the surface

  • that is blitted.

  • @param surface Pointer to surface to blit onto.
    */
    void Surface::blit(Surface *surface)
    {
    SDL_Surface *SrcSurface;
    SDL_Surface *DstSurface;

    if (_visible && !_hidden)
    {
    if (_redraw)
    draw();

     SDL_Rect* cropper;
     SDL_Rect target;
     if (_crop.w == 0 && _crop.h == 0)
     {
     	cropper = 0;
     }
     else
     {
     	cropper = &_crop;
     }
     target.x = getX();
     target.y = getY();
    
     SrcSurface = _surface;
     DstSurface = surface->getSurface();
    
     if
     (
     	(SrcSurface->format->BitsPerPixel == 8) &&
     	(DstSurface->format->BitsPerPixel == 32)
     )
     {
     	// do something special
    

// SrcSurface->pixels
SpecialBlit(_surface, cropper, surface->getSurface(), &target);

	} else
	{
		// assume 8 bits to 8 bits.
		SDL_BlitSurface(_surface, cropper, surface->getSurface(), &target);
	}
}

}

Currently the custom blitter looks as follows in SpecialBlit.cpp:

#include “SpecialBlit.h”

// Skybuck: re-use clipping from SDL_UpperBlit

int SpecialBlit
(
SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect
)
{
SDL_Rect fulldst;
int srcx, srcy, w, h;

/* Make sure the surfaces aren't locked */
if ( ! src || ! dst )
{
	SDL_SetError("SDL_UpperBlit: passed a NULL surface");
	return(-1);
}
if ( src->locked || dst->locked )
{
	SDL_SetError("Surfaces must not be locked during blit");
	return(-1);
}

/* If the destination rectangle is NULL, use the entire dest surface */
if ( dstrect == NULL )
{
        fulldst.x = fulldst.y = 0;
	dstrect = &fulldst;
}

/* clip the source rectangle to the source surface */
if(srcrect)
{
    int maxw, maxh;

	srcx = srcrect->x;
	w = srcrect->w;
	if(srcx < 0)
	{
	    w += srcx;
		dstrect->x -= srcx;
		srcx = 0;
	}
	maxw = src->w - srcx;
	if(maxw < w) w = maxw;

	srcy = srcrect->y;
	h = srcrect->h;
	if(srcy < 0)
	{
	    h += srcy;
		dstrect->y -= srcy;
		srcy = 0;
	}
	maxh = src->h - srcy;
	if(maxh < h) h = maxh;
    
} else
{
    srcx = srcy = 0;
	w = src->w;
	h = src->h;
}

/* clip the destination rectangle against the clip rectangle */
{
    SDL_Rect *clip = &dst->clip_rect;
	int dx, dy;

	dx = clip->x - dstrect->x;
	if(dx > 0)
	{
		w -= dx;
		dstrect->x += dx;
		srcx += dx;
	}
	dx = dstrect->x + w - clip->x - clip->w;
	if(dx > 0) w -= dx;

	dy = clip->y - dstrect->y;
	if(dy > 0)
	{
		h -= dy;
		dstrect->y += dy;
		srcy += dy;
	}
	dy = dstrect->y + h - clip->y - clip->h;
	if(dy > 0) h -= dy;
}

if(w > 0 && h > 0)
{
    SDL_Rect sr;
    sr.x = srcx;
	sr.y = srcy;
	sr.w = dstrect->w = w;
	sr.h = dstrect->h = h;
	return SDL_LowerBlit(src, &sr, dst, dstrect);
}
dstrect->w = dstrect->h = 0;
return 0;

}

And SpecialBlit.h:

#pragma once

#include <SDL.h>

int SpecialBlit
(
SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect
);

Currently this code compiles but ofcourse I have to modify:

		// do something special
		SpecialBlit(_surface, cropper, surface->getSurface(), &target); 

SpecialBlit is basically a copy of the code of SDL_UpperBlit (from SDL version 1.2). Basically it re-uses the clipping source code so I don’t have to re-write that. Later on it calls SDL_LowerBlit.

SDL_LowerBlit is the function I will most likely need to modify and re-write:

/*

  • Set up a blit between two surfaces – split into three parts:

  • The upper part, SDL_UpperBlit(), performs clipping and rectangle

  • verification. The lower part is a pointer to a low level

  • accelerated blitting function.

  • These parts are separated out and each used internally by this

  • library in the optimimum places. They are exported so that if

  • you know exactly what you are doing, you can optimize your code

  • by calling the one(s) you need.
    */
    int SDL_LowerBlit (SDL_Surface *src, SDL_Rect *srcrect,
    SDL_Surface *dst, SDL_Rect *dstrect)
    {
    SDL_blit do_blit;
    SDL_Rect hw_srcrect;
    SDL_Rect hw_dstrect;

    /* Check to make sure the blit mapping is valid */
    if ( (src->map->dst != dst) ||
    (src->map->dst->format_version != src->map->format_version) ) {
    if ( SDL_MapSurface(src, dst) < 0 ) {
    return(-1);
    }
    }

    /* Figure out which blitter to use */
    if ( (src->flags & SDL_HWACCEL) == SDL_HWACCEL ) {
    if ( src == SDL_VideoSurface ) {
    hw_srcrect = *srcrect;
    hw_srcrect.x += current_video->offset_x;
    hw_srcrect.y += current_video->offset_y;
    srcrect = &hw_srcrect;
    }
    if ( dst == SDL_VideoSurface ) {
    hw_dstrect = *dstrect;
    hw_dstrect.x += current_video->offset_x;
    hw_dstrect.y += current_video->offset_y;
    dstrect = &hw_dstrect;
    }
    do_blit = src->map->hw_blit;
    } else {
    do_blit = src->map->sw_blit;
    }
    return(do_blit(src, srcrect, dst, dstrect));
    }

Now what is interesting to note while exploring this source code there seems to be some mapping functionality and private map data structure in SDL_Surface:

SDL_BlitMap

and a function call:

SDL_MapSurface

So now I am starting to wonder if it’s possible to write a “custom blitter” for SDL version 1.2.

So I have a few questions about this idea of writing a custom blitter:

  1. Is it possible to “plug-in” a custom blitter without having to re-compile/re-build SDL version 1.2 ?

  2. How would I go about doing this ? Which API to use ? Is there any example code for this ?

(If it’s not possible to write a custom blitter without re-compiling/re-building SDL 1.2 then I will try and modify this source code directly and maybe use get pixel/set pixel like techniques).

Ultimately the goal for this custom blitter is to convert 8 bit palette indexes to something higher perhaps 12 bit or 13 bit palette indexes. So it will be like as if there is a bigger VGA palette with more entries available than just 256. The idea is to expand each row from 16 colors all the way up to 512 colors.

(However this special color processing will only be applied to certain Surfaces.cpp which the game uses, so the idea is to avoid most of the conversion work for menus and show and simply use a blitter to convert those from 8 bit to 32 bit as “usual” and then later modify specific surface inherited data structures, such as terrain to use special color processing which is described below, not really relevant to this question, but does explain why this 8 bit to 32 bit conversion is necessary):

So the original VGA palette is 256 colors in total divided by 16 colors per row = 16 rows.

The new advanced palette will be: 16 rows * 512 colors = 8192 colors.

Each new color (/palette index) will then be described by R,G,B (each 8 bits as usual)

Ultimately this new palette index will be looked up and converted into R,G,B the R,G,B is then to be plotted/put onto a 24 or 32 bit surface.

The new advanced palette is currently calculated by cubic spline (or linear) interpolation. (Extrapolation could also be done later).

The original OpenXcom palette has been analyzed with a custom openxcom palette analysis tool which I wrote in Delphi and made two youtube videos about it, this is the short one, where the graph was updated from lines to poinst to more clearly show the improvement that is possible: