実用的なキャンバス(3)・左右反転表示

イメージの左右反転表示
アナログで紙に絵を描いている時、絵のバランスを確認するために、紙の裏を覗いて見る、ということがよくあります。

左右反転した状態の絵を確認することで、絵のバランスを確認できるというものですが、これがペイントソフト上でも出来ると便利ですね。

前回はイメージの基準位置を変更できるようにしましたが、これに少し手を加えれば、キャンバス上でイメージを左右反転することは割と簡単に出来ます。
ソースファイル
>> 020_canvas3.c

H キーで左右反転の ON/OFF ができます。
タイトルバーに、現在の ON/OFF 状態が表示されます。
解説
キャンバスの左右反転状態は canvas_hrev に格納し、0 で通常、1 で左右反転状態とします。
H キーで、このフラグを反転します。
キャンバス描画
まずは、キャンバス描画の変更部分を見てみます。

void draw_canvas(SPTK_IMAGE32 *srcimg,SPTK_RECTWH *rc)
{
    int ix,iy,pitchd,sfx,sfy,sfx_left,incfx,incfy,sx,sy;
    uint8_t *pdst;
    SPTK_PIX_RGBA *psrc;
        
    pdst = sptk_image_getptbuf(winimg, rc->x, rc->y);
    pitchd = winimg->pitch_dir - rc->w * winimg->bpp;
    
    incfx = incfy = (1 << 18) * 100 / zoom;
    if(canvas_hrev) incfx = -incfx;
    
    sfx_left = (rc->x - CANVAS_W / 2 + ptScr.x) * incfx + (ptImgBase.x << 18);
    
    sfy = (rc->y - CANVAS_H / 2 + ptScr.y) * incfy + (ptImgBase.y << 18);
    
    for(iy = 0; iy < rc->h; iy++)
    {
        sfx = sfx_left;
        sy  = sfy >> 18;

        for(ix = 0; ix < rc->w; ix++)
        {
            sx = sfx >> 18;
            
            if(sx < 0 || sx >= srcimg->w || sy < 0 || sy >= srcimg->h)
                sptk_image_setpixel_buf_rgb(winimg, pdst, 0xcc, 0xcc, 0xcc);
            else
            {
                psrc = srcimg->pixbuf + sy * srcimg->w + sx;
            
                sptk_image_setpixel_buf_rgb(winimg, pdst, psrc->r, psrc->g, psrc->b);
            }
            
            sfx += incfx;
            pdst += winimg->bpp;
        }
        
        sfy += incfy;
        pdst += pitchd;
    }
}

今までは incfxy を使って X, Y 方向ともに同じ値を加算していましたが、今回はそれを X, Y それぞれに分けて incfx, incfy の2つを用意しています。

左右反転の適用における実質の変更点は、以下の部分だけです。

incfx = incfy = (1 << 18) * 100 / zoom;
if(canvas_hrev) incfx = -incfx;

キャンバスの左右反転が ON の場合、incfx の値を符号反転させています。

「キャンバス座標→イメージ座標」の変換としては、基準位置を元に拡大縮小した後、左右反転する場合はその値を符号反転し、基準位置を元にイメージ座標を左右反転させることになります。

ここでは、incfx を符号反転させることで、その左右反転処理が行えます。

左右反転時の X 方向では、イメージ座標は右から左へ値が減っていくことになるので、ループ時は符号反転させた incfx をそのまま足します。
座標変換
座標変換の計算部分も、少し手を加えます。

void img_to_canvas(int *x,int *y)
{
    int xx;
    
    xx = *x - ptImgBase.x;
    if(canvas_hrev) xx = -xx;

    *x = (int)(xx * zoom / 100.0 + CANVAS_W / 2 - ptScr.x);
    *y = (int)((*y - ptImgBase.y) * zoom / 100.0 + CANVAS_H / 2 - ptScr.y);
}

void canvas_to_img(int *x,int *y)
{
    double dx,dy;
    
    dx = (*x - CANVAS_W / 2 + ptScr.x) * 100.0 / zoom;
    dy = (*y - CANVAS_H / 2 + ptScr.y) * 100.0 / zoom;
    
    if(canvas_hrev) dx = -dx;

    *x = (int)(dx + ptImgBase.x);
    *y = (int)(dy + ptImgBase.y);
}

それぞれ、左右反転時は、各タイミングで X 座標を符号反転します。

なお、今回は、「イメージ範囲→キャンバス範囲」の変換処理にも少し手を加えました。
というのも、範囲の左上と右下の座標を img_to_canvas() で変換した値は、左右反転時には x1 > x2 となるからです。
(x1,y1) - (x2,y2) が正しく左上・右下になるように、sptk_rect_swap() で値を入れ替えています。

また、同じように、スクロールの最小値・最大値の計算時も、左右反転時に合わせた処理が必要になります。
そのほか
今回は、左右反転の変更時にイメージの基準位置などを調整していないので、画面上のイメージ位置が別の場所に飛びますが、実際のペイントソフトでは、左右反転の変更時にも画面上のイメージ位置が移動しないように調整するべきです。