This is what I got so far:
SpecialBlit.h:
#pragma once
#include <SDL.h>
int SpecialBlit
(
SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect
);
void SpecialGradientTest( SDL_Surface *ParaSurface );
SpecialBlit.cpp
Code copied from SDL and modified:
#include "SpecialBlit.h"
// Skybuck: re-use clipping from SDL_UpperBlit
// maybe a custom blitter is possible, but for now it seems a recompile of SDL would be needed.
/** typedef for private surface blitting functions */
// typedef int (*SDL_blit)(struct SDL_Surface *src, SDL_Rect *srcrect,
// struct SDL_Surface *dst, SDL_Rect *dstrect);
/*
* 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));
}
*/
int SDL_LowerBlitSpecial
(
SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect
)
{
int x, y;
int w, h;
int sp, dp;
Uint8 *SrcPointer;
SDL_Color *DstPointer;
Uint8 SrcColor;
SDL_Color DstColor;
// int SrcOffset;
// int DstOffset;
int offset;
w = src->w;
h = src->h;
sp = src->pitch;
dp = dst->pitch;
for (y=0; y<h; y++)
{
for (x=0; x<w; x++)
{
// SrcOffset = y * sp + x;
// DstOffset = y * dp + x * 4;
// pointers below already multiply by data structure width so don't do it in formulas below =D
// SrcOffset = y * w + x;
// DstOffset = y * w + x;
offset = y * w + x;
SrcPointer = (Uint8*) src->pixels;
DstPointer = (SDL_Color*) dst->pixels;
SrcColor = SrcPointer[offset];
if (SrcColor != 0)
{
// test color should be replaced later with palette lookup, I don't think this is the cause of the problems, since width and height seems off.
DstColor.r = SrcColor;
DstColor.g = SrcColor;
DstColor.b = SrcColor;
DstPointer[offset] = DstColor;
}
}
}
return 1;
}
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_LowerBlitSpecial(src, &sr, dst, dstrect);
}
dstrect->w = dstrect->h = 0;
return 0;
}
void SpecialGradientTest( SDL_Surface *ParaSurface )
{
int x, y;
int w, h, p, b;
int offset;
SDL_Color color;
SDL_Color *pointer;
h = ParaSurface->h;
w = ParaSurface->w;
p = ParaSurface->pitch;
b = ParaSurface->format->BytesPerPixel;
pointer = (SDL_Color*)(ParaSurface->pixels);
SDL_LockSurface( ParaSurface );
for (y=0; y<h; y++)
{
for (x=0; x<w; x++)
{
offset = y * w + x;
color.r = x & 255;
color.b = y & 255;
color.g = 0;
pointer[offset] = color;
// pointer = (SDL_Color *)ParaSurface->pixels + (y * ParaSurface->pitch + x * ParaSurface->format->BytesPerPixel);
// *pointer = color;
}
}
SDL_UnlockSurface( ParaSurface );
}
Trying to get OpenXcom surface to convert from 8 to 32 bit when it's encountered:
/**
* 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;
SDL_Surface *ConvertedSurface;
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
SpecialBlit(_surface, cropper, surface->getSurface(), &target);
} else
{
// assume 8 bits to 8 bits.
SDL_BlitSurface(_surface, cropper, surface->getSurface(), &target);
}
}
}
Tell OpenXcom to use 32 bit surface.
void Screen::resetDisplay(bool resetVideo)
{
int width = Options::displayWidth;
int height = Options::displayHeight;
#ifdef __linux__
Uint32 oldFlags = _flags;
#endif
makeVideoFlags();
if (!_surface || (_surface->getSurface()->format->BitsPerPixel != _bpp ||
_surface->getSurface()->w != _baseWidth ||
_surface->getSurface()->h != _baseHeight)) // don't reallocate _surface if not necessary, it's a waste of CPU cycles
{
if (_surface) delete _surface;
// _surface = new Surface(_baseWidth, _baseHeight, 0, 0, Screen::use32bitScaler() ? 32 : 8); // only HQX/XBRZ needs 32bpp for this surface; the OpenGL class has its own 32bpp buffer
_surface = new Surface(_baseWidth, _baseHeight, 0, 0, 32); // only HQX/XBRZ needs 32bpp for this surface; the OpenGL class has its own 32bpp buffer
if (_surface->getSurface()->format->BitsPerPixel == 8) _surface->setPalette(deferredPalette);
}