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 カラーモード」を使ってる人は注意。
いねーよそんな奴。