Wrote a replacement for SDL_BlitSurface...

...which allows blitting of transparent surfaces on top of
each other. It's not optimized with assembly code like the normal
SDL_BlitSurface so might be a bit slower.
This commit is contained in:
Mark de Wever 2008-04-16 17:21:12 +00:00
parent dcd16ecc47
commit cfc6417f68
5 changed files with 177 additions and 8 deletions

View file

@ -616,7 +616,11 @@ void timage::draw(surface& canvas,
surf = image_;
}
SDL_BlitSurface(surf, &src_clip, canvas, &dst_clip);
// FIXME the scale and strech return an optimized surface which needs
// to be turned into a not optimized surface again, so it would be nice
// to add a parameter whether or not to optimize.
surf = make_neutral_surface(surf);
blit_surface(surf, &src_clip, canvas, &dst_clip);
}
@ -744,7 +748,7 @@ void ttext::draw(surface& canvas,
SDL_SetAlpha(surf, 0, 0);
SDL_Rect dst = { x, y, canvas->w, canvas->h };
SDL_BlitSurface(surf, 0, canvas, &dst);
blit_surface(surf, 0, canvas, &dst);
}
} // namespace

View file

@ -256,7 +256,7 @@ void tcontrol::draw(surface& surface)
// Next time when visible we grab the background again.
if(restorer_) {
DBG_G_D << "Control: drawing setting invisible.\n";
SDL_BlitSurface(restorer_, 0, surface, &rect);
blit_surface(restorer_, 0, surface, &rect);
restorer_ = 0;
}
return;
@ -267,7 +267,7 @@ void tcontrol::draw(surface& surface)
restorer_ = get_surface_portion(surface, rect);
}
if(full_redraw()) {
SDL_BlitSurface(restorer_, 0, surface, &rect);
blit_surface(restorer_, 0, surface, &rect);
rect = get_rect();
}
@ -280,7 +280,7 @@ void tcontrol::draw(surface& surface)
}
canvas(get_state()).draw(true);
SDL_BlitSurface(canvas(get_state()).surf(), 0, surface, &rect);
blit_surface(canvas(get_state()).surf(), 0, surface, &rect);
}
} // namespace gui2

View file

@ -105,5 +105,166 @@ Uint32 decode_colour(const std::string& colour)
return result;
}
namespace {
// Surface locker class based on surface_locker in sdl_utils.hpp.
struct surface_lock
{
surface_lock(SDL_Surface* surf) : surface_(surf), locked_(false)
{
if(SDL_MUSTLOCK(surface_)) {
const int res = SDL_LockSurface(surface_);
if(res == 0) {
locked_ = true;
}
}
}
~surface_lock()
{
if(locked_) {
SDL_UnlockSurface(surface_);
}
}
Uint32* pixels() { return reinterpret_cast<Uint32*>(surface_->pixels); }
private:
SDL_Surface* surface_;
bool locked_;
};
} // namespace
//! Replacement for SDL_BlitSurface.
//!
//! SDL_BlitSurface has problems with blitting partly transparent surfaces so
//! this is a replacement. It ignores the SDL_SRCALPHA and SDL_SRCCOLORKEY
//! flags. src and dst will have the SDL_RLEACCEL flag removed.
//! The return value of SDL_BlistSurface is normally ignored so no return value.
//! The rectangles are const and will not be modified.
//!
//! @param src The surface to blit.
//! @param srcrect The size of the surface to blit, only width and height
//! are used.
//! @param dst The surface to blit on.
//! @param dstrect The offset to blit the surface on, only x and y are used.
void blit_surface(SDL_Surface* src,
const SDL_Rect* srcrect, SDL_Surface* dst, const SDL_Rect* dstrect)
{
assert(src);
assert(dst);
assert((src->flags & SDL_RLEACCEL) == 0);
assert((dst->flags & SDL_RLEACCEL) == 0);
// Get the areas to blit
SDL_Rect dst_rect = { 0, 0, dst->w, dst->h };
if(dstrect) {
dst_rect.x = dstrect->x;
dst_rect.w -= dstrect->x;
dst_rect.y = dstrect->y;
dst_rect.h -= dstrect->y;
assert(dst_rect.x >= 0);
assert(dst_rect.y >= 0);
}
SDL_Rect src_rect = { 0, 0, src->w, src->h };
if(srcrect && srcrect->w && srcrect->h) {
src_rect.w = srcrect->w;
src_rect.h = srcrect->h;
assert(src_rect.w <= src->w);
assert(src_rect.h <= src->h);
}
// Get the blit size limits.
const unsigned width = minimum(src_rect.w, dst_rect.w);
const unsigned height = minimum(src_rect.h, dst_rect.h);
{
// Extra scoping used for the surface_lock.
surface_lock src_lock(src);
surface_lock dst_lock(dst);
Uint32* const src_pixels = reinterpret_cast<Uint32*>(src_lock.pixels());
Uint32* dst_pixels = reinterpret_cast<Uint32*>(dst_lock.pixels());
for(unsigned y = 0; y < height; ++y) {
for(unsigned x = 0; x < width; ++x) {
// We need to do the blitting using some optimizations
// if the src is fully transparent we can ignore this pixel
// if the src is fully opaque we can overwrite the destination with this pixel
// if the destination is fully transparent we replace us with the source
//
// We do these optimizations between the extraction of the variables
// to avoid creating variables not used (it might save us some cycles).
const Uint32 src_pixel = src_pixels[(y + src_rect.y) * src->w + (x + src_rect.x)];
const Uint8 src_a = (src_pixel & 0xFF000000) >> 24;
if(!src_a) {
// Fully transparent source, ignore
continue;
}
const ptrdiff_t dst_offset = (y + dst_rect.y) * dst->w + (x + dst_rect.x);
if(src_a == 255) {
// Fully opaque source, copy
dst_pixels[dst_offset] = src_pixel;
continue;
}
const Uint32 dst_pixel = dst_pixels[dst_offset];
Uint8 dst_a = (dst_pixel & 0xFF000000) >> 24;
if(!dst_a) {
// Fully transparent destination, copy
dst_pixels[dst_offset] = src_pixel;
continue;
}
const Uint8 src_r = (src_pixel & 0x00FF0000) >> 16;
const Uint8 src_g = (src_pixel & 0x0000FF00) >> 8;
const Uint8 src_b = src_pixel & 0x000000FF;
Uint8 dst_r = (dst_pixel & 0x00FF0000) >> 16;
Uint8 dst_g = (dst_pixel & 0x0000FF00) >> 8;
Uint8 dst_b = dst_pixel & 0x000000FF;
if(dst_a == 255) {
// Destination fully opaque blend the source.
dst_r = (((src_r - dst_r) * src_a) >> 8 ) + dst_r;
dst_g = (((src_g - dst_g) * src_a) >> 8 ) + dst_g;
dst_b = (((src_b - dst_b) * src_a) >> 8 ) + dst_b;
} else {
// Destination and source party transparent.
// aquired the data now do the blitting
const unsigned tmp_a = 255 - src_a;
const unsigned tmp_r = 1 + (src_r * src_a) + (dst_r * tmp_a);
dst_r = (tmp_r + (tmp_r >> 8)) >> 8;
const unsigned tmp_g = 1 + (src_g * src_a) + (dst_g * tmp_a);
dst_g = (tmp_g + (tmp_g >> 8)) >> 8;
const unsigned tmp_b = 1 + (src_b * src_a) + (dst_b * tmp_a);
dst_b = (tmp_b + (tmp_b >> 8)) >> 8;
dst_a += (((255 - dst_a) * src_a) >> 8);
}
dst_pixels[dst_offset] = (dst_a << 24) | (dst_r << 16) | (dst_g << 8) | (dst_b);
}
}
}
}
} // namespace gui2

View file

@ -59,6 +59,10 @@ Uint32 decode_colour(const std::string& colour);
int decode_font_style(const std::string& style);
//! Replacement for SDL_BlitSurface.
void blit_surface(SDL_Surface* src,
const SDL_Rect* srcrect, SDL_Surface* dst, const SDL_Rect* dstrect);
} // namespace gui2
#endif

View file

@ -112,11 +112,11 @@ int twindow::show(const bool restore, void* /*flip_function*/)
resolve_definition();
layout(get_client_rect());
screen = create_optimized_surface(restorer_);
screen = make_neutral_surface(restorer_);
canvas_background_.draw();
SDL_Rect blit = {0, 0, screen->w, screen->h};
SDL_BlitSurface(canvas_background_.surf(), 0, screen, &blit);
blit_surface(canvas_background_.surf(), 0, screen, &blit);
}
#if 0
// Darkening for debugging redraw.
@ -136,7 +136,7 @@ int twindow::show(const bool restore, void* /*flip_function*/)
if(draw_foreground) {
canvas_foreground_.draw();
SDL_Rect blit = {0, 0, screen->w, screen->h};
SDL_BlitSurface(canvas_foreground_.surf(), 0, screen, &blit);
blit_surface(canvas_foreground_.surf(), 0, screen, &blit);
}
if(tooltip_.dirty()) {
tooltip_.draw(screen);