📄 sdl_rleaccel.c
字号:
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
#ifdef SAVE_RCSID
static char rcsid =
"@(#) $Id: SDL_RLEaccel.c,v 1.4 2002/04/22 21:38:03 wmay Exp $";
#endif
/*
* RLE encoding for software colorkey and alpha-channel acceleration
*
* Original version by Sam Lantinga
*
* Mattias Engdeg錼d (Yorick): Rewrite. New encoding format, encoder and
* decoder. Added per-surface alpha blitter. Added per-pixel alpha
* format, encoder and blitter.
*
* Many thanks to Xark and johns for hints, benchmarks and useful comments
* leading to this code.
*
* Welcome to Macro Mayhem.
*/
/*
* The encoding translates the image data to a stream of segments of the form
*
* <skip> <run> <data>
*
* where <skip> is the number of transparent pixels to skip,
* <run> is the number of opaque pixels to blit,
* and <data> are the pixels themselves.
*
* This basic structure is used both for colorkeyed surfaces, used for simple
* binary transparency and for per-surface alpha blending, and for surfaces
* with per-pixel alpha. The details differ, however:
*
* Encoding of colorkeyed surfaces:
*
* Encoded pixels always have the same format as the target surface.
* <skip> and <run> are unsigned 8 bit integers, except for 32 bit depth
* where they are 16 bit. This makes the pixel data aligned at all times.
* Segments never wrap around from one scan line to the next.
*
* The end of the sequence is marked by a zero <skip>,<run> pair at the *
* beginning of a line.
*
* Encoding of surfaces with per-pixel alpha:
*
* The sequence begins with a struct RLEDestFormat describing the target
* pixel format, to provide reliable un-encoding.
*
* Each scan line is encoded twice: First all completely opaque pixels,
* encoded in the target format as described above, and then all
* partially transparent (translucent) pixels (where 1 <= alpha <= 254),
* in the following 32-bit format:
*
* For 32-bit targets, each pixel has the target RGB format but with
* the alpha value occupying the highest 8 bits. The <skip> and <run>
* counts are 16 bit.
*
* For 16-bit targets, each pixel has the target RGB format, but with
* the middle component (usually green) shifted 16 steps to the left,
* and the hole filled with the 5 most significant bits of the alpha value.
* i.e. if the target has the format rrrrrggggggbbbbb,
* the encoded pixel will be 00000gggggg00000rrrrr0aaaaabbbbb.
* The <skip> and <run> counts are 8 bit for the opaque lines, 16 bit
* for the translucent lines. Two padding bytes may be inserted
* before each translucent line to keep them 32-bit aligned.
*
* The end of the sequence is marked by a zero <skip>,<run> pair at the
* beginning of an opaque line.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SDL_types.h"
#include "SDL_video.h"
#include "SDL_error.h"
#include "SDL_sysvideo.h"
#include "SDL_blit.h"
#include "SDL_memops.h"
#include "SDL_RLEaccel_c.h"
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#define PIXEL_COPY(to, from, len, bpp) \
do { \
if(bpp == 4) { \
SDL_memcpy4(to, from, (unsigned)(len)); \
} else { \
SDL_memcpy(to, from, (unsigned)(len) * (bpp)); \
} \
} while(0)
/*
* Various colorkey blit methods, for opaque and per-surface alpha
*/
#define OPAQUE_BLIT(to, from, length, bpp, alpha) \
PIXEL_COPY(to, from, length, bpp)
/*
* For 32bpp pixels on the form 0x00rrggbb:
* If we treat the middle component separately, we can process the two
* remaining in parallel. This is safe to do because of the gap to the left
* of each component, so the bits from the multiplication don't collide.
* This can be used for any RGB permutation of course.
*/
#define ALPHA_BLIT32_888(to, from, length, bpp, alpha) \
do { \
int i; \
Uint32 *src = (Uint32 *)(from); \
Uint32 *dst = (Uint32 *)(to); \
for(i = 0; i < (int)(length); i++) { \
Uint32 s = *src++; \
Uint32 d = *dst; \
Uint32 s1 = s & 0xff00ff; \
Uint32 d1 = d & 0xff00ff; \
d1 = (d1 + ((s1 - d1) * alpha >> 8)) & 0xff00ff; \
s &= 0xff00; \
d &= 0xff00; \
d = (d + ((s - d) * alpha >> 8)) & 0xff00; \
*dst++ = d1 | d; \
} \
} while(0)
/*
* For 16bpp pixels we can go a step further: put the middle component
* in the high 16 bits of a 32 bit word, and process all three RGB
* components at the same time. Since the smallest gap is here just
* 5 bits, we have to scale alpha down to 5 bits as well.
*/
#define ALPHA_BLIT16_565(to, from, length, bpp, alpha) \
do { \
int i; \
Uint16 *src = (Uint16 *)(from); \
Uint16 *dst = (Uint16 *)(to); \
for(i = 0; i < (int)(length); i++) { \
Uint32 s = *src++; \
Uint32 d = *dst; \
s = (s | s << 16) & 0x07e0f81f; \
d = (d | d << 16) & 0x07e0f81f; \
d += (s - d) * alpha >> 5; \
d &= 0x07e0f81f; \
*dst++ = d | d >> 16; \
} \
} while(0)
#define ALPHA_BLIT16_555(to, from, length, bpp, alpha) \
do { \
int i; \
Uint16 *src = (Uint16 *)(from); \
Uint16 *dst = (Uint16 *)(to); \
for(i = 0; i < (int)(length); i++) { \
Uint32 s = *src++; \
Uint32 d = *dst; \
s = (s | s << 16) & 0x03e07c1f; \
d = (d | d << 16) & 0x03e07c1f; \
d += (s - d) * alpha >> 5; \
d &= 0x03e07c1f; \
*dst++ = d | d >> 16; \
} \
} while(0)
/*
* The general slow catch-all function, for remaining depths and formats
*/
#define ALPHA_BLIT_ANY(to, from, length, bpp, alpha) \
do { \
int i; \
Uint8 *src = from; \
Uint8 *dst = to; \
for(i = 0; i < (int)(length); i++) { \
Uint32 s, d; \
unsigned rs, gs, bs, rd, gd, bd; \
switch(bpp) { \
case 2: \
s = *(Uint16 *)src; \
d = *(Uint16 *)dst; \
break; \
case 3: \
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { \
s = (src[0] << 16) | (src[1] << 8) | src[2]; \
d = (dst[0] << 16) | (dst[1] << 8) | dst[2]; \
} else { \
s = (src[2] << 16) | (src[1] << 8) | src[0]; \
d = (dst[2] << 16) | (dst[1] << 8) | dst[0]; \
} \
break; \
case 4: \
s = *(Uint32 *)src; \
d = *(Uint32 *)dst; \
break; \
} \
RGB_FROM_PIXEL(s, fmt, rs, gs, bs); \
RGB_FROM_PIXEL(d, fmt, rd, gd, bd); \
rd += (rs - rd) * alpha >> 8; \
gd += (gs - gd) * alpha >> 8; \
bd += (bs - bd) * alpha >> 8; \
PIXEL_FROM_RGB(d, fmt, rd, gd, bd); \
switch(bpp) { \
case 2: \
*(Uint16 *)dst = d; \
break; \
case 3: \
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { \
dst[0] = d >> 16; \
dst[1] = d >> 8; \
dst[2] = d; \
} else { \
dst[0] = d; \
dst[1] = d >> 8; \
dst[2] = d >> 16; \
} \
break; \
case 4: \
*(Uint32 *)dst = d; \
break; \
} \
src += bpp; \
dst += bpp; \
} \
} while(0)
/*
* Special case: 50% alpha (alpha=128)
* This is treated specially because it can be optimized very well, and
* since it is good for many cases of semi-translucency.
* The theory is to do all three components at the same time:
* First zero the lowest bit of each component, which gives us room to
* add them. Then shift right and add the sum of the lowest bits.
*/
#define ALPHA_BLIT32_888_50(to, from, length, bpp, alpha) \
do { \
int i; \
Uint32 *src = (Uint32 *)(from); \
Uint32 *dst = (Uint32 *)(to); \
for(i = 0; i < (int)(length); i++) { \
Uint32 s = *src++; \
Uint32 d = *dst; \
*dst++ = (((s & 0x00fefefe) + (d & 0x00fefefe)) >> 1) \
+ (s & d & 0x00010101); \
} \
} while(0)
/*
* For 16bpp, we can actually blend two pixels in parallel, if we take
* care to shift before we add, not after.
*/
/* helper: blend a single 16 bit pixel at 50% */
#define BLEND16_50(dst, src, mask) \
do { \
Uint32 s = *src++; \
Uint32 d = *dst; \
*dst++ = (((s & mask) + (d & mask)) >> 1) \
+ (s & d & (~mask & 0xffff)); \
} while(0)
/* basic 16bpp blender. mask is the pixels to keep when adding. */
#define ALPHA_BLIT16_50(to, from, length, bpp, alpha, mask) \
do { \
unsigned n = (length); \
Uint16 *src = (Uint16 *)(from); \
Uint16 *dst = (Uint16 *)(to); \
if(((unsigned long)src ^ (unsigned long)dst) & 3) { \
/* source and destination not in phase, blit one by one */ \
while(n--) \
BLEND16_50(dst, src, mask); \
} else { \
if((unsigned long)src & 3) { \
/* first odd pixel */ \
BLEND16_50(dst, src, mask); \
n--; \
} \
for(; n > 1; n -= 2) { \
Uint32 s = *(Uint32 *)src; \
Uint32 d = *(Uint32 *)dst; \
*(Uint32 *)dst = ((s & (mask | mask << 16)) >> 1) \
+ ((d & (mask | mask << 16)) >> 1) \
+ (s & d & (~(mask | mask << 16))); \
src += 2; \
dst += 2; \
} \
if(n) \
BLEND16_50(dst, src, mask); /* last odd pixel */ \
} \
} while(0)
#define ALPHA_BLIT16_565_50(to, from, length, bpp, alpha) \
ALPHA_BLIT16_50(to, from, length, bpp, alpha, 0xf7de)
#define ALPHA_BLIT16_555_50(to, from, length, bpp, alpha) \
ALPHA_BLIT16_50(to, from, length, bpp, alpha, 0xfbde)
#define CHOOSE_BLIT(blitter, alpha, fmt) \
do { \
if(alpha == 255) { \
switch(fmt->BytesPerPixel) { \
case 1: blitter(1, Uint8, OPAQUE_BLIT); break; \
case 2: blitter(2, Uint8, OPAQUE_BLIT); break; \
case 3: blitter(3, Uint8, OPAQUE_BLIT); break; \
case 4: blitter(4, Uint16, OPAQUE_BLIT); break; \
} \
} else { \
switch(fmt->BytesPerPixel) { \
case 1: \
/* No 8bpp alpha blitting */ \
break; \
\
case 2: \
switch(fmt->Rmask | fmt->Gmask | fmt->Bmask) { \
case 0xffff: \
if(fmt->Gmask == 0x07e0 \
|| fmt->Rmask == 0x07e0 \
|| fmt->Bmask == 0x07e0) { \
if(alpha == 128) \
blitter(2, Uint8, ALPHA_BLIT16_565_50); \
else { \
alpha >>= 3; /* use 5 bit alpha */ \
blitter(2, Uint8, ALPHA_BLIT16_565); \
} \
} else \
goto general16; \
break; \
\
case 0x7fff: \
if(fmt->Gmask == 0x03e0 \
|| fmt->Rmask == 0x03e0 \
|| fmt->Bmask == 0x03e0) { \
if(alpha == 128) \
blitter(2, Uint8, ALPHA_BLIT16_555_50); \
else { \
alpha >>= 3; /* use 5 bit alpha */ \
blitter(2, Uint8, ALPHA_BLIT16_555); \
} \
break; \
} \
/* fallthrough */ \
\
default: \
general16: \
blitter(2, Uint8, ALPHA_BLIT_ANY); \
} \
break; \
\
case 3: \
blitter(3, Uint8, ALPHA_BLIT_ANY); \
break; \
\
case 4: \
if((fmt->Rmask | fmt->Gmask | fmt->Bmask) == 0x00ffffff \
&& (fmt->Gmask == 0xff00 || fmt->Rmask == 0xff00 \
|| fmt->Bmask == 0xff00)) { \
if(alpha == 128) \
blitter(4, Uint16, ALPHA_BLIT32_888_50); \
else \
blitter(4, Uint16, ALPHA_BLIT32_888); \
} else \
blitter(4, Uint16, ALPHA_BLIT_ANY); \
break; \
} \
} \
} while(0)
/*
* This takes care of the case when the surface is clipped on the left and/or
* right. Top clipping has already been taken care of.
*/
static void RLEClipBlit(int w, Uint8 *srcbuf, SDL_Surface *dst,
Uint8 *dstbuf, SDL_Rect *srcrect, unsigned alpha)
{
SDL_PixelFormat *fmt = dst->format;
#define RLECLIPBLIT(bpp, Type, do_blit) \
do { \
int linecount = srcrect->h; \
int ofs = 0; \
int left = srcrect->x; \
int right = left + srcrect->w; \
dstbuf -= left * bpp; \
for(;;) { \
int run; \
ofs += *(Type *)srcbuf; \
run = ((Type *)srcbuf)[1]; \
srcbuf += 2 * sizeof(Type); \
if(run) { \
/* clip to left and right borders */ \
if(ofs < right) { \
int start = 0; \
int len = run; \
int startcol; \
if(left - ofs > 0) { \
start = left - ofs; \
len -= start; \
if(len <= 0) \
goto nocopy ## bpp ## do_blit; \
} \
startcol = ofs + start; \
if(len > right - startcol) \
len = right - startcol; \
do_blit(dstbuf + startcol * bpp, srcbuf + start * bpp, \
len, bpp, alpha); \
} \
nocopy ## bpp ## do_blit: \
srcbuf += run * bpp; \
ofs += run; \
} else if(!ofs) \
break; \
if(ofs == w) { \
ofs = 0; \
dstbuf += dst->pitch; \
if(!--linecount) \
break; \
} \
} \
} while(0)
CHOOSE_BLIT(RLECLIPBLIT, alpha, fmt);
#undef RLECLIPBLIT
}
/* blit a colorkeyed RLE surface */
int SDL_RLEBlit(SDL_Surface *src, SDL_Rect *srcrect,
SDL_Surface *dst, SDL_Rect *dstrect)
{
Uint8 *dstbuf;
Uint8 *srcbuf;
int x, y;
int w = src->w;
unsigned alpha;
/* Lock the destination if necessary */
if ( dst->flags & (SDL_HWSURFACE|SDL_ASYNCBLIT) ) {
SDL_VideoDevice *video = current_video;
SDL_VideoDevice *this = current_video;
if ( video->LockHWSurface(this, dst) < 0 ) {
return(-1);
}
}
/* Set up the source and destination pointers */
x = dstrect->x;
y = dstrect->y;
dstbuf = (Uint8 *)dst->pixels + dst->offset
+ y * dst->pitch + x * src->format->BytesPerPixel;
srcbuf = (Uint8 *)src->map->sw_data->aux_data;
{
/* skip lines at the top if neccessary */
int vskip = srcrect->y;
int ofs = 0;
if(vskip) {
#define RLESKIP(bpp, Type) \
for(;;) { \
int run; \
ofs += *(Type *)srcbuf; \
run = ((Type *)srcbuf)[1]; \
srcbuf += sizeof(Type) * 2; \
if(run) { \
srcbuf += run * bpp; \
ofs += run; \
} else if(!ofs) \
goto done; \
if(ofs == w) { \
ofs = 0; \
if(!--vskip) \
break; \
} \
}
switch(src->format->BytesPerPixel) {
case 1: RLESKIP(1, Uint8); break;
case 2: RLESKIP(2, Uint8); break;
case 3: RLESKIP(3, Uint8); break;
case 4: RLESKIP(4, Uint16); break;
}
#undef RLESKIP
}
}
alpha = (src->flags & SDL_SRCALPHA) == SDL_SRCALPHA
? src->format->alpha : 255;
/* if left or right edge clipping needed, call clip blit */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -