今日の雑記

生きることでいっぱいいっぱい

GP2X での libSDL_gfx の注意点

正確には「GP2Xでの」では無くて「8bit カラーモード」な訳だが。
GP2X」では、カラーモードというかプライマリサーフェスは「8bit」が推奨となっている。単純に「それ以外のモードが重い」せいなのだが。
んで、GP2X に標準で入っている(んだが Wiki では標準扱いになっていないみたいな)「libSDL_gfx」なのだが、これがまた、とんでもない事をしでかいてた。
ちなみにこの「libSDL_gfx」だが、こんな機能がある。

  • プリミティブ(線や四角三角や円)が描ける
  • 拡大縮小ができる
  • 回転ができる

まあこんな感じ。今回問題となったのは、このうち「回転ができる」部分。
問題となったコードを見てみよう。今回挙げるコードは「SDL_gfx-2.0.16」の「SDL_rotozoom.c」の641行目くらい?

void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos)
{
    int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
    tColorY *pc, *sp;
    int gap;

    /*
     * Variable setup 
     */
    xd = ((src->w - dst->w) << 15);
    yd = ((src->h - dst->h) << 15);
    ax = (cx << 16) - (icos * cx);
    ay = (cy << 16) - (isin * cx);
    sw = src->w - 1;
    sh = src->h - 1;
    pc = dst->pixels;
    gap = dst->pitch - dst->w;

    /*
     * Clear surface to colorkey 
     */
    memset(pc, (unsigned char) (src->format->colorkey & 0xff), dst->pitch * dst->h);
    /*
     * Iterate through destination surface 
     */
    for (y = 0; y < dst->h; y++) {
	dy = cy - y;
	sdx = (ax + (isin * dy)) + xd;
	sdy = (ay - (icos * dy)) + yd;
	for (x = 0; x < dst->w; x++) {
	    dx = (short) (sdx >> 16);
	    dy = (short) (sdy >> 16);
	    if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
		sp = (tColorY *) (src->pixels);
		sp += (src->pitch * dy + dx);
		*pc = *sp;
	    }
	    sdx += icos;
	    sdy += isin;
	    pc++;
	}
	pc += gap;
    }
}

これをこうした。新しく付いてるコメント部分が問題の個所。

void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos)
{
    int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
//  tColorY *pc, *sp;	// これは構造体なので(↓後述)でポインタ加算すると +4
    Uint8 *pc, *sp;	// なのでこうする
    int gap;

    /*
     * Variable setup 
     */
    xd = ((src->w - dst->w) << 15);
    yd = ((src->h - dst->h) << 15);
    ax = (cx << 16) - (icos * cx);
    ay = (cy << 16) - (isin * cx);
    sw = src->w - 1;
    sh = src->h - 1;
    pc = dst->pixels;
    gap = dst->pitch - dst->w;

    /*
     * Clear surface to colorkey 
     */
    memset(pc, (unsigned char) (src->format->colorkey & 0xff), dst->pitch * dst->h);
    /*
     * Iterate through destination surface 
     */
    for (y = 0; y < dst->h; y++) {
	dy = cy - y;
	sdx = (ax + (isin * dy)) + xd;
	sdy = (ay - (icos * dy)) + yd;
	for (x = 0; x < dst->w; x++) {
	    dx = (short) (sdx >> 16);
	    dy = (short) (sdy >> 16);
	    if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
//		sp = (tColorY *) (src->pixels);	// どこ指すんだという話
		sp = (Uint8 *) (src->pixels);	// きめうちだけど 8bit カラーなので
		sp += (src->pitch * dy + dx);	// 凄いところを指すポインタに
		*pc = *sp;			// オーバーしてたら凄いところに書き込みを…(実際してたし)
	    }
	    sdx += icos;
	    sdy += isin;
	    pc++;				// ここで +1 ではなくて +4 に
	}
	pc += gap;				// ここも同様だが、実際の値は「0」なので弊害無し
    }
}

まあ、これも移植性というかポータビリティ的に問題はあるので誉められたもんではないです。
元の原因となった「tColorY」という型ですが、見てみると…、

typedef struct tColorY {
	Uint8 y;
} tColorY;

これはないだろ…。「typedef」でいいだろうに…。
というわけで、GP2X 以外で「8bit カラーモード」を使ってる人は注意。
いねーよそんな奴。