オーバーサンプリング最適化
前回は、「オーバーサンプリング」によるキャンバス描画を行いましたが、今回は、そのコードを最適化してもう少し高速化させます。
では、まずオーバーサンプリングのループ部分をもう一度見てみましょう。
ソース画像の座標 sx, sy を求める計算を毎ループで行っていますが、sx と sy を別々に見てみると、それぞれの座標は SUBPIXEL 通りの数値 (SUBPIXEL = 4 なら、4通り) であることがわかります。
X, Y のそれぞれのループでは、それぞれの座標値は一定なわけですから、ループ内で毎回計算せずに、X, Y それぞれの座標値をテーブル化して、ループ内ではそれを参照してやれば計算回数を減らせます。
テーブルを使った場合のコードは、以下のようになります。
「SUBPIXEL x SUBPIXEL」回分行っていた計算が、「SUBPIXEL + SUBPIXEL」回分の計算で済みました。
では、まずオーバーサンプリングのループ部分をもう一度見てみましょう。
for(jy = 0, sfy_j = sfy; jy < SUBPIXEL; jy++, sfy_j += incfxy_j) { for(jx = 0, sfx_j = sfx; jx < SUBPIXEL; jx++, sfx_j += incfxy_j) { sx = sfx_j >> 18; sy = sfy_j >> 18; if(sx < 0) sx = 0; else if(sx >= srcimg->w) sx = srcimg->w - 1; if(sy < 0) sy = 0; else if(sy >= srcimg->h) sy = srcimg->h - 1; psrc = srcimg->pixbuf + sy * srcimg->w + sx;
ソース画像の座標 sx, sy を求める計算を毎ループで行っていますが、sx と sy を別々に見てみると、それぞれの座標は SUBPIXEL 通りの数値 (SUBPIXEL = 4 なら、4通り) であることがわかります。
X, Y のそれぞれのループでは、それぞれの座標値は一定なわけですから、ループ内で毎回計算せずに、X, Y それぞれの座標値をテーブル化して、ループ内ではそれを参照してやれば計算回数を減らせます。
テーブルを使った場合のコードは、以下のようになります。
int tblX[SUBPIXEL],tblY[SUBPIXEL]; /* Y のテーブル */ for(jy = 0, sfy_j = sfy; jy < SUBPIXEL; jy++, sfy_j += incfxy_j) { sy = sfy_j >> 18; if(sy < 0) sy = 0; else if(sy >= srcimg->h) sy = srcimg->h - 1; tblY[jy] = sy; } /* X のテーブル */ for(jx = 0, sfx_j = sfx; jx < SUBPIXEL; jx++, sfx_j += incfxy_j) { sx = sfx_j >> 18; if(sx < 0) sx = 0; else if(sx >= srcimg->w) sx = srcimg->w - 1; tblX[jx] = sx; } /* オーバーサンプリング*/ for(jy = 0; jy < SUBPIXEL; jy++) { sy = tblY[jy]; for(jx = 0; jx < SUBPIXEL; jx++) { sy = tblX[jx]; psrc = srcimg->pixbuf + sy * srcimg->w + sx;
「SUBPIXEL x SUBPIXEL」回分行っていた計算が、「SUBPIXEL + SUBPIXEL」回分の計算で済みました。
Y のテーブルをポインタ値に
上記のコードに、もう少し手を加えます。
Y のテーブル値は、ソース座標の Y 位置ですが、これをピクセルバッファのポインタ位置に変えてみます。
ソース画像のバッファ位置は以下の計算で求めていますが、
Y のテーブル値を、バッファの Y 位置のポインタにすれば、ここの計算も省略できます。
これで、だいぶループ内を軽量化できました。
Y のテーブル値は、ソース座標の Y 位置ですが、これをピクセルバッファのポインタ位置に変えてみます。
ソース画像のバッファ位置は以下の計算で求めていますが、
psrc = srcimg->pixbuf + sy * srcimg->w + sx;
Y のテーブル値を、バッファの Y 位置のポインタにすれば、ここの計算も省略できます。
SPTK_PIX_RGBA *tblY[SUBPIXEL]; int tblX[SUBPIXEL]; /* Y のテーブル : ポインタ */ for(jy = 0, sfy_j = sfy; jy < SUBPIXEL; jy++, sfy_j += incfxy_j) { sy = sfy_j >> 18; if(sy < 0) sy = 0; else if(sy >= srcimg->h) sy = srcimg->h - 1; tblY[jy] = srcimg->pixbuf + sy * srcimg->w; } /* X のテーブル : 座標 */ for(jx = 0, sfx_j = sfx; jx < SUBPIXEL; jx++, sfx_j += incfxy_j) { sx = sfx_j >> 18; if(sx < 0) sx = 0; else if(sx >= srcimg->w) sx = srcimg->w - 1; tblX[jx] = sx; } /* オーバーサンプリング*/ for(jy = 0; jy < SUBPIXEL; jy++) { for(jx = 0; jx < SUBPIXEL; jx++) { psrc = tblY[jy] + tblX[jx];
これで、だいぶループ内を軽量化できました。
さらなる最適化
実は、上記のコードにはまだ最適化の余地があります。
上記の、Y テーブル作成部分を見てください。
これは ix (キャンバス座標 X) のループ内で毎回計算されていますが、ix のループ内では、ソース画像の Y 位置は常に一定です。
ということは、Y のテーブルは、ix のループ前に一度だけ計算してやれば OK だということです。
これで、かなり計算数を減らせました。
上記の、Y テーブル作成部分を見てください。
これは ix (キャンバス座標 X) のループ内で毎回計算されていますが、ix のループ内では、ソース画像の Y 位置は常に一定です。
ということは、Y のテーブルは、ix のループ前に一度だけ計算してやれば OK だということです。
for(iy = 0; iy < CANVAS_H; iy++) { /* Y のテーブル */ for(jy = 0, sfy_j = sfy; jy < SUBPIXEL; jy++, sfy_j += incfxy_j) { sy = sfy_j >> 18; if(sy < 0) sy = 0; else if(sy >= srcimg->h) sy = srcimg->h - 1; tblY[jy] = srcimg->pixbuf + sy * srcimg->w; } sfx = sfx_left; for(ix = 0; ix < CANVAS_W; ix++, sfx += incfxy, pdst += winimg->bpp) {
これで、かなり計算数を減らせました。
ソースコード
以下が、最適化後の全コードです。
017_oversamp2.c
017_oversamp2.c
#include <stdio.h> #include "sptk.h" #define SCRW 16 #define CANVAS_W 250 #define CANVAS_H 250 #define SUBPIXEL 4 SPTK_WIDGET *scrh,*scrv; SPTK_IMAGE *winimg; SPTK_IMAGE32 *srcimg; int scrx = 0,scry = 0,zoom = 100; int update_canvas = 0; void draw_canvas() { int ix,iy,jx,jy,pitchd,sfx,sfy,sfx_left,incfxy; int sx,sy,r,g,b,incfxy_j,subf,tblX[SUBPIXEL]; uint8_t *pdst; SPTK_PIX_RGBA *psrc,*tblY[SUBPIXEL]; pdst = winimg->pixbuftop; pitchd = winimg->pitch_dir - CANVAS_W * winimg->bpp; incfxy = (1 << 18) * 100 / zoom; incfxy_j = incfxy / SUBPIXEL; sfx_left = scrx * incfxy; sfy = scry * incfxy; for(iy = 0; iy < CANVAS_H; iy++) { /* Y テーブル作成 */ for(jy = 0, subf = sfy; jy < SUBPIXEL; jy++, subf += incfxy_j) { sy = subf >> 18; if(sy < 0) sy = 0; else if(sy >= srcimg->h) sy = srcimg->h - 1; tblY[jy] = srcimg->pixbuf + sy * srcimg->w; } /*----- ix ループ -----*/ sfx = sfx_left; for(ix = 0; ix < CANVAS_W; ix++, sfx += incfxy, pdst += winimg->bpp) { /* 範囲外 */ sx = sfx >> 18; sy = sfy >> 18; if(sx < 0 || sx >= srcimg->w || sy < 0 || sy >= srcimg->h) { sptk_image_setpixel_buf_rgb(winimg, pdst, 0xcc, 0xcc, 0xcc); continue; } /* X テーブル作成 */ for(jx = 0, subf = sfx; jx < SUBPIXEL; jx++, subf += incfxy_j) { sx = subf >> 18; if(sx < 0) sx = 0; else if(sx >= srcimg->w) sx = srcimg->w - 1; tblX[jx] = sx; } /* オーバーサンプリング */ r = g = b = 0; for(jy = 0; jy < SUBPIXEL; jy++) { for(jx = 0; jx < SUBPIXEL; jx++) { psrc = tblY[jy] + tblX[jx]; r += psrc->r; g += psrc->g; b += psrc->b; } } r /= SUBPIXEL * SUBPIXEL; g /= SUBPIXEL * SUBPIXEL; b /= SUBPIXEL * SUBPIXEL; sptk_image_setpixel_buf_rgb(winimg, pdst, r, g, b); } sfy += incfxy; pdst += pitchd; } } void update_handle() { char m[16]; if(update_canvas) { draw_canvas(); sprintf(m, "%d", zoom); sptk_image_text(winimg, 4, 4, m, -1, 0xffffff); sptk_image_text(winimg, 3, 3, m, -1, 0); update_canvas = 0; } } void update_screen(int time) { update_canvas = 1; sptk_update(NULL, 0, 0, CANVAS_W, CANVAS_H, time); } void change_zoom() { /* スクロール最大値変更 */ sptk_scrollbar_set_status(scrh, 0, srcimg->w * zoom / 100, CANVAS_W); sptk_scrollbar_set_status(scrv, 0, srcimg->h * zoom / 100, CANVAS_H); /* 位置を再取得 */ scrx = sptk_scrollbar_get_pos(scrh); scry = sptk_scrollbar_get_pos(scrv); } void winhandle(SPTK_EVENT *ev) { switch(ev->type) { case SPTK_EVENT_WINDOW_KEYDOWN: if(ev->key.code == 'U') { if(zoom < 100) zoom += 10; else if(zoom != 1000) zoom += 100; change_zoom(); update_screen(0); } else if(ev->key.code == 'D') { if(zoom > 100) zoom -= 100; else if(zoom != 10) zoom -= 10; change_zoom(); update_screen(0); } break; case SPTK_EVENT_WINDOW_CLOSE: sptk_quit(); break; } } void scroll_handle(SPTK_WIDGET *wg,int type,int pos) { if(wg->id == 0) scrx = pos; else scry = pos; update_screen(5); } int main() { sptk_init("test", CANVAS_W + SCRW, CANVAS_H + SCRW); sptk_window_set_handle(winhandle); sptk_set_update_handle(update_handle); winimg = sptk_window_get_image(); srcimg = sptk_image32_load_bitmap("bitmap1.bmp"); if(!srcimg) sptk_errexit("cannot load bitmap file"); scrh = sptk_widget_scrollbar_create(0, 0, CANVAS_H, CANVAS_W, SCRW, 0, scroll_handle); scrv = sptk_widget_scrollbar_create(1, CANVAS_W, 0, SCRW, CANVAS_H, 1, scroll_handle); change_zoom(); update_screen(-1); sptk_run(); sptk_image32_free(srcimg); return 0; }