/*
*  This program was created by AXELL CORPORATION.
*  Copyright (C) 2017 AXELL CORPORATION, all rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gfx_sample.h"

/* quality */
#define DEMO_RENDERING_QUALITY   VG_RENDERING_QUALITY_BETTER
#define DEMO_IMAGE_QUALITY       VG_IMAGE_QUALITY_BETTER

#define COVER_WIDTH      320
#define COVER_HEIGHT     240
#define COVER_NUMBER     9

/* number of images in scene */
#define COVER_NUM_RENDER 11

/* image object */
VGImage texobj[COVER_NUMBER];

/* coordinate of rectangles, transform matrices */
VGfloat covMatrix[COVER_NUM_RENDER][9];
VGint covPos[COVER_NUM_RENDER][8];
VGfloat centerMatrix[9];
VGint centerPos[8];

VGfloat covMatrixM[COVER_NUM_RENDER][9];
VGint covPosM[COVER_NUM_RENDER][8];
VGfloat centerMatrixM[9];
VGint centerPosM[8];

#define DEMO_WIN_WIDTH    640
#define DEMO_WIN_HEIGHT   480
#define CENTER_YPOS_L     120
#define CENTER_WIDTH_L    318
#define CENTER_HEIGHT_L   240
#define CENTER_YPOS       150
#define CENTER_WIDTH      240
#define CENTER_HEIGHT     180
#define TRAPEZOID_HEIGHT  82
#define TRAPEZOID_UPPER   120

/*  */
#define OFFSET    8
#define OFFSET_S  16

/* demo mode (0 : left to right, 1 : right to left, 2,3 : center scaling */
static int demo_mode;

/* base image ID */
static int baseid;

/* current position from base position */
static int current_offset;
#define MAX_OFFSET   256
#define MID_OFFSET   (MAX_OFFSET >> 1)

/* background clear color */
static VGfloat clearColor[4] = {0.0F, 0.0F, 0.0F, 1.0F};

/* color transform */
static VGfloat colorTransform[8] = {0.25F, 0.25F, 0.25F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F};

static char filename[COVER_NUMBER][16] = 
{
    COVER_NAME0,
    COVER_NAME1,
    COVER_NAME2,
    COVER_NAME3,
    COVER_NAME4,
    COVER_NAME5,
    COVER_NAME6,
    COVER_NAME7,
    COVER_NAME8,
};

static int ComputeWarpQuadToSquare(VGfloat sx0, VGfloat sy0, VGfloat sx1, VGfloat sy1, VGfloat sx2, VGfloat sy2, VGfloat sx3, VGfloat sy3, VGfloat * matrix);
static int ComputeWarpSquareToQuad(VGfloat dx0, VGfloat dy0, VGfloat dx1, VGfloat dy1, VGfloat dx2, VGfloat dy2, VGfloat dx3, VGfloat dy3, VGfloat * matrix);
static int ComputeWarpQuadToQuad(VGfloat dx0, VGfloat dy0, VGfloat dx1, VGfloat dy1, VGfloat dx2, VGfloat dy2, VGfloat dx3, VGfloat dy3, VGfloat sx0, VGfloat sy0, VGfloat sx1, VGfloat sy1, VGfloat sx2, VGfloat sy2, VGfloat sx3, VGfloat sy3, VGfloat * matrix);


static int InvMtx3x3(float d[3][4], float s[3][4])
{
    float det00 = s[1][1] * s[2][2] - s[2][1] * s[1][2];
    float det01 = s[2][0] * s[1][2] - s[1][0] * s[2][2];
    float det02 = s[1][0] * s[2][1] - s[2][0] * s[1][1];

    float det = s[0][0] * det00 + s[0][1] * det01 + s[0][2] * det02;
    if (det == 0.0) {
        return 0;
    }
    det = 1.0 / det;

    d[0][0] = det * det00;
    d[1][0] = det * det01;
    d[2][0] = det * det02;
    d[0][1] = det * (s[2][1] * s[0][2] - s[0][1] * s[2][2]);
    d[1][1] = det * (s[0][0] * s[2][2] - s[2][0] * s[0][2]);
    d[2][1] = det * (s[2][0] * s[0][1] - s[0][0] * s[2][1]);
    d[0][2] = det * (s[0][1] * s[1][2] - s[1][1] * s[0][2]);
    d[1][2] = det * (s[1][0] * s[0][2] - s[0][0] * s[1][2]);
    d[2][2] = det * (s[0][0] * s[1][1] - s[1][0] * s[0][1]);
    d[0][3] = d[1][3] = d[2][3] = 0.0F;
    return 1;
}

static int ComputeWarpQuadToSquare(VGfloat sx0, VGfloat sy0, VGfloat sx1, VGfloat sy1, VGfloat sx2, VGfloat sy2, VGfloat sx3, VGfloat sy3, VGfloat * matrix)
{
    int nonsingular;
    float m[3][4], im[3][4];
    float mat[9];
    int ret;

	if(!matrix || ((int)matrix) & 3)
		return 1;

	ret = ComputeWarpSquareToQuad(sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3, mat);
	if(ret)
		return 1;

    m[0][0] = mat[0]; m[0][1] = mat[3]; m[0][2] = mat[6];
    m[1][0] = mat[1]; m[1][1] = mat[4]; m[1][2] = mat[7];
    m[2][0] = mat[2]; m[2][1] = mat[5]; m[2][2] = mat[8];

    nonsingular = InvMtx3x3(im, m);

	if(!nonsingular)
		return 1;
	matrix[0] = im[0][0];
	matrix[1] = im[1][0];
	matrix[2] = im[2][0];
	matrix[3] = im[0][1];
	matrix[4] = im[1][1];
	matrix[5] = im[2][1];
	matrix[6] = im[0][2];
	matrix[7] = im[1][2];
	matrix[8] = im[2][2];
	return 0;
}

static int ComputeWarpSquareToQuad(VGfloat dx0, VGfloat dy0, VGfloat dx1, VGfloat dy1, VGfloat dx2, VGfloat dy2, VGfloat dx3, VGfloat dy3, VGfloat * matrix)
{
    float oodet, g, h, sumx, sumy, det;
    
	VGfloat diffx1 = dx1 - dx3;
	VGfloat diffy1 = dy1 - dy3;
	VGfloat diffx2 = dx2 - dx3;
	VGfloat diffy2 = dy2 - dy3;

	if(!matrix || ((int)matrix) & 3)
		return 1;

	det = diffx1*diffy2 - diffx2*diffy1;
	if(det == 0.0f)
		return 1;

	sumx = dx0 - dx1 + dx3 - dx2;
	sumy = dy0 - dy1 + dy3 - dy2;

	if(sumx == 0.0f && sumy == 0.0f) {
		matrix[0] = dx1 - dx0;
		matrix[1] = dy1 - dy0;
		matrix[2] = 0.0f;
		matrix[3] = dx3 - dx1;
		matrix[4] = dy3 - dy1;
		matrix[5] = 0.0f;
		matrix[6] = dx0;
		matrix[7] = dy0;
		matrix[8] = 1.0f;
		return 0;
	}

    oodet = 1.0f / det;
	g = (sumx*diffy2 - diffx2*sumy) * oodet;
	h = (diffx1*sumy - sumx*diffy1) * oodet;

	matrix[0] = dx1-dx0+g*dx1;
	matrix[1] = dy1-dy0+g*dy1;
	matrix[2] = g;
	matrix[3] = dx2-dx0+h*dx2;
	matrix[4] = dy2-dy0+h*dy2;
	matrix[5] = h;
	matrix[6] = dx0;
	matrix[7] = dy0;
	matrix[8] = 1.0f;
	return 0;
}

static int ComputeWarpQuadToQuad(VGfloat dx0, VGfloat dy0, VGfloat dx1, VGfloat dy1, VGfloat dx2, VGfloat dy2, VGfloat dx3, VGfloat dy3, VGfloat sx0, VGfloat sy0, VGfloat sx1, VGfloat sy1, VGfloat sx2, VGfloat sy2, VGfloat sx3, VGfloat sy3, VGfloat * matrix)
{
	VGfloat qtos[9];
	VGfloat stoq[9];
    int ret;
	if(!matrix || ((int)matrix) & 3)
		return 1;

	ret = ComputeWarpQuadToSquare(sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3, qtos);
	if(ret)
		return 1;

	ret = ComputeWarpSquareToQuad(dx0, dy0, dx1, dy1, dx2, dy2, dx3, dy3, stoq);
	if(ret)
		return 0;

	matrix[0] = stoq[0] * qtos[0] + stoq[3] * qtos[1] + stoq[6] * qtos[2];
	matrix[1] = stoq[1] * qtos[0] + stoq[4] * qtos[1] + stoq[7] * qtos[2];
	matrix[2] = stoq[2] * qtos[0] + stoq[5] * qtos[1] + stoq[8] * qtos[2];
	matrix[3] = stoq[0] * qtos[3] + stoq[3] * qtos[4] + stoq[6] * qtos[5];
	matrix[4] = stoq[1] * qtos[3] + stoq[4] * qtos[4] + stoq[7] * qtos[5];
	matrix[5] = stoq[2] * qtos[3] + stoq[5] * qtos[4] + stoq[8] * qtos[5];
	matrix[6] = stoq[0] * qtos[6] + stoq[3] * qtos[7] + stoq[6] * qtos[8];
	matrix[7] = stoq[1] * qtos[6] + stoq[4] * qtos[7] + stoq[7] * qtos[8];
	matrix[8] = stoq[2] * qtos[6] + stoq[5] * qtos[7] + stoq[8] * qtos[8];
	return 0;
}




/**
 * Calculate basic rectangle coordinate.
 */
static void calcBasePosition(void)
{
    int pos, cen, offset;

    /* make center cover position */
    cen = (COVER_NUM_RENDER / 2);
    covPos[cen][0] = covPos[cen][6] = (DEMO_WIN_WIDTH - CENTER_WIDTH) / 2;
    covPos[cen][2] = covPos[cen][4] = covPos[cen][0] + CENTER_WIDTH;
    covPos[cen][1] = covPos[cen][3] = CENTER_YPOS;
    covPos[cen][5] = covPos[cen][7] = CENTER_YPOS + CENTER_HEIGHT;
    /* make side trapezoid position */
    offset = covPos[cen][0] / (cen - 1);
    for (pos = 1; pos <= cen; pos++) {
        covPos[cen - pos][0] = covPos[cen - pos][6] = covPos[cen][0] - offset * (pos);
        covPos[cen - pos][2] = covPos[cen - pos][4] = covPos[cen - pos][0] + TRAPEZOID_HEIGHT;
        covPos[cen - pos][1] = CENTER_YPOS;
        covPos[cen - pos][3] = CENTER_YPOS + (CENTER_HEIGHT - TRAPEZOID_UPPER) / 2;
        covPos[cen - pos][5] = covPos[cen - pos][3] + TRAPEZOID_UPPER;
        covPos[cen - pos][7] = CENTER_YPOS + CENTER_HEIGHT;
        covPos[cen + pos][2] = covPos[cen + pos][4] = covPos[cen][2] + offset * (pos);
        covPos[cen + pos][0] = covPos[cen + pos][6] = covPos[cen + pos][2] - TRAPEZOID_HEIGHT;
        covPos[cen + pos][3] = CENTER_YPOS;
        covPos[cen + pos][1] = CENTER_YPOS + (CENTER_HEIGHT - TRAPEZOID_UPPER) / 2;
        covPos[cen + pos][7] = covPos[cen + pos][1] + TRAPEZOID_UPPER;
        covPos[cen + pos][5] = CENTER_YPOS + CENTER_HEIGHT;
    }

    centerPos[0] = centerPos[6] = (DEMO_WIN_WIDTH - CENTER_WIDTH_L) / 2;
    centerPos[2] = centerPos[4] = centerPos[0] + CENTER_WIDTH_L;
    centerPos[1] = centerPos[3] = CENTER_YPOS_L;
    centerPos[5] = centerPos[7] = CENTER_YPOS_L + CENTER_HEIGHT_L;

    /*** mirrored cover */
    /* make center cover position */
    cen = (COVER_NUM_RENDER / 2);
    covPosM[cen][0] = covPos[cen][0];
    covPosM[cen][1] = covPos[cen][1];
    covPosM[cen][2] = covPos[cen][2];
    covPosM[cen][3] = covPos[cen][3];
    covPosM[cen][4] = covPos[cen][4];
    covPosM[cen][5] = covPos[cen][3] - CENTER_HEIGHT;
    covPosM[cen][6] = covPos[cen][6];
    covPosM[cen][7] = covPos[cen][1] - CENTER_HEIGHT;

    /* make side trapezoid position */
    for (pos = 1; pos <= cen; pos++) {
        covPosM[cen - pos][0] = covPosM[cen - pos][6] = covPos[cen - pos][0];
        covPosM[cen - pos][2] = covPosM[cen - pos][4] = covPos[cen - pos][2];
        covPosM[cen - pos][1] = covPos[cen - pos][1];
        covPosM[cen - pos][3] = covPos[cen - pos][3];
        covPosM[cen - pos][5] = covPos[cen - pos][3] - TRAPEZOID_UPPER;
        covPosM[cen - pos][7] = covPos[cen - pos][1] - CENTER_HEIGHT;
        covPosM[cen + pos][0] = covPosM[cen + pos][6] = covPos[cen + pos][0];
        covPosM[cen + pos][2] = covPosM[cen + pos][4] = covPos[cen + pos][2];
        covPosM[cen + pos][1] = covPos[cen + pos][1];
        covPosM[cen + pos][3] = covPos[cen + pos][3];
        covPosM[cen + pos][7] = covPos[cen + pos][1] - TRAPEZOID_UPPER;
        covPosM[cen + pos][5] = covPos[cen + pos][3] - CENTER_HEIGHT;
    }
    centerPosM[0] = centerPosM[6] = centerPos[0];
    centerPosM[2] = centerPosM[4] = centerPos[2];
    centerPosM[1] = centerPos[1];
    centerPosM[3] = centerPos[3];
    centerPosM[5] = centerPos[3] - CENTER_HEIGHT_L;
    centerPosM[7] = centerPos[1] - CENTER_HEIGHT_L;

}

#if 0
/**
 * Destroy objects.
 */
static void killApp(void)
{
    int i;
    for (i = 0; i < COVER_NUMBER; i++) {
        vgDestroyImage(texobj[i]);
    }
}
#endif

/**
 * Create image objects, context settings.
 */
static void initApp(void)
{
    void *cover_image;
    int i;

    cover_image = GfxGetFreeArea(COVER_WIDTH, COVER_HEIGHT, COVER_BPP<<3);

    /* create image object */
    for (i = 0; i < COVER_NUMBER; i++) {
        GfxFsRead((int8_t*)filename[i], COVER_STRIDE * COVER_HEIGHT, (void*)cover_image);
        /* create image object */
        texobj[i] = vgCreateImage(VG_sRGBA_8888, COVER_WIDTH, COVER_HEIGHT,
                                  VG_IMAGE_QUALITY_BETTER|VG_IMAGE_QUALITY_FASTER|VG_IMAGE_QUALITY_NONANTIALIASED);
        vgImageSubData(texobj[i], cover_image, COVER_WIDTH * 4, VG_sRGBA_8888, 0, 0, COVER_WIDTH, COVER_HEIGHT);
    }

    GfxReleaseFreeArea(cover_image);

    /* context */
    vgSetfv(VG_CLEAR_COLOR, 4, clearColor);
    vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, colorTransform);
    vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
    vgSeti(VG_IMAGE_QUALITY, DEMO_IMAGE_QUALITY);
    vgSeti(VG_RENDERING_QUALITY, DEMO_RENDERING_QUALITY);
    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);

    /* calculate cover position */
    calcBasePosition();

    /* matrix */
    for (i = 0; i < COVER_NUM_RENDER; i++) {
        ComputeWarpQuadToQuad((float)covPos[i][0],          (float)covPos[i][1],
                                 (float)covPos[i][2],          (float)covPos[i][3],
                                 (float)covPos[i][4],          (float)covPos[i][5],
                                 (float)covPos[i][6],          (float)covPos[i][7],
                                 (float)0,                     (float)0,
                                 (float)(COVER_WIDTH - 1),     (float)0,
                                 (float)(COVER_WIDTH - 1),     (float)(COVER_HEIGHT - 1),
                                 (float)0,                     (float)(COVER_HEIGHT - 1),
                                 &covMatrix[i][0]);
    }
    ComputeWarpQuadToQuad((float)centerPos[0],          (float)centerPos[1],
                             (float)centerPos[2],          (float)centerPos[3],
                             (float)centerPos[4],          (float)centerPos[5],
                             (float)centerPos[6],          (float)centerPos[7],
                             (float)0,                     (float)0,
                             (float)(COVER_WIDTH - 1),     (float)0,
                             (float)(COVER_WIDTH - 1),     (float)(COVER_HEIGHT - 1),
                             (float)0,                     (float)(COVER_HEIGHT - 1),
                             &centerMatrix[0]);
    /* matrix */
    for (i = 0; i < COVER_NUM_RENDER; i++) {
        ComputeWarpQuadToQuad((float)covPosM[i][0],         (float)covPosM[i][1],
                                 (float)covPosM[i][2],         (float)covPosM[i][3],
                                 (float)covPosM[i][4],         (float)covPosM[i][5],
                                 (float)covPosM[i][6],         (float)covPosM[i][7],
                                 (float)0,                     (float)0,
                                 (float)(COVER_WIDTH - 1),     (float)0,
                                 (float)(COVER_WIDTH - 1),     (float)(COVER_HEIGHT - 1),
                                 (float)0,                     (float)(COVER_HEIGHT - 1),
                                 &covMatrixM[i][0]);
    }
    ComputeWarpQuadToQuad((float)centerPosM[0],          (float)centerPosM[1],
                             (float)centerPosM[2],          (float)centerPosM[3],
                             (float)centerPosM[4],          (float)centerPosM[5],
                             (float)centerPosM[6],          (float)centerPosM[7],
                             (float)0,                     (float)0,
                             (float)(COVER_WIDTH - 1),     (float)0,
                             (float)(COVER_WIDTH - 1),     (float)(COVER_HEIGHT - 1),
                             (float)0,                     (float)(COVER_HEIGHT - 1),
                             &centerMatrixM[0]);

}


/**
 * Draw covers.
 */
static void repaint(void)
{
    int i;
    int texid;
    VGfloat mtx[9];
    VGfloat lint = current_offset / (float)MAX_OFFSET;
    int center = COVER_NUM_RENDER / 2;

    vgClear(0, 0, DEMO_WIN_WIDTH, DEMO_WIN_HEIGHT);

    if (demo_mode == 1) {  /*** mode 1 : left to right ***/
        /* right side images */
        for (i = COVER_NUM_RENDER - 2; i >= center; i--) {
            if (i == center && current_offset < MID_OFFSET) continue;
            /* make interpolate matrix */
            memcpy(mtx, covMatrix[i], sizeof(covMatrix[i]));
            mtx[0] += (covMatrix[i+1][0] - covMatrix[i][0]) * lint;
            mtx[6] += (covMatrix[i+1][6] - covMatrix[i][6]) * lint;
            if (i == center) {
                mtx[1] += (covMatrix[i+1][1] - covMatrix[i][1]) * lint;
                mtx[2] += (covMatrix[i+1][2] - covMatrix[i][2]) * lint;
                mtx[4] += (covMatrix[i+1][4] - covMatrix[i][4]) * lint;
                mtx[7] += (covMatrix[i+1][7] - covMatrix[i][7]) * lint;
            }
            /* draw image */
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            /* make interpolate matrix */
            memcpy(mtx, covMatrixM[i], sizeof(covMatrixM[i]));
            mtx[0] += (covMatrixM[i+1][0] - covMatrixM[i][0]) * lint;
            mtx[6] += (covMatrixM[i+1][6] - covMatrixM[i][6]) * lint;
            if (i == center) {
                mtx[1] += (covMatrixM[i+1][1] - covMatrixM[i][1]) * lint;
                mtx[2] += (covMatrixM[i+1][2] - covMatrixM[i][2]) * lint;
                mtx[4] += (covMatrixM[i+1][4] - covMatrixM[i][4]) * lint;
                mtx[7] += (covMatrixM[i+1][7] - covMatrixM[i][7]) * lint;
            }
            /* draw image */
            vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
        }
        /* left side images */
        for (i = 0; i <= center; i++) {
            if (i == center && current_offset >= MID_OFFSET) continue;
            /* make interpolate matrix */
            memcpy(mtx, covMatrix[i], sizeof(covMatrix[i]));
            mtx[0] += (covMatrix[i+1][0] - covMatrix[i][0]) * lint;
            mtx[6] += (covMatrix[i+1][6] - covMatrix[i][6]) * lint;
            if (i >= center - 1) {
                mtx[1] += (covMatrix[i+1][1] - covMatrix[i][1]) * lint;
                mtx[2] += (covMatrix[i+1][2] - covMatrix[i][2]) * lint;
                if (i == center) {
                    mtx[4] += (covMatrix[i+1][4] - covMatrix[i][4]) * lint;
                    mtx[7] += (covMatrix[i+1][7] - covMatrix[i][7]) * lint;
                }
            }
            /* draw image */
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            /* make interpolate matrix */
            memcpy(mtx, covMatrixM[i], sizeof(covMatrixM[i]));
            mtx[0] += (covMatrixM[i+1][0] - covMatrixM[i][0]) * lint;
            mtx[6] += (covMatrixM[i+1][6] - covMatrixM[i][6]) * lint;
            if (i >= center - 1) {
                mtx[1] += (covMatrixM[i+1][1] - covMatrixM[i][1]) * lint;
                mtx[2] += (covMatrixM[i+1][2] - covMatrixM[i][2]) * lint;
                if (i == center) {
                    mtx[4] += (covMatrixM[i+1][4] - covMatrixM[i][4]) * lint;
                    mtx[7] += (covMatrixM[i+1][7] - covMatrixM[i][7]) * lint;
                }
            }
            /* draw image */
            vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
        }
    } else if (demo_mode == 2) {  /*** mode 2 : right to left ***/
        /* left side images */
        for (i = 1; i <= center; i++) {
            if (i == center && current_offset < MID_OFFSET) continue;
            /* make interpolate matrix */
            memcpy(mtx, covMatrix[i], sizeof(covMatrix[i]));
            mtx[0] -= (covMatrix[i][0] - covMatrix[i-1][0]) * lint;
            mtx[6] -= (covMatrix[i][6] - covMatrix[i-1][6]) * lint;
            if (i == center) {
                mtx[1] -= (covMatrix[i][1] - covMatrix[i-1][1]) * lint;
                mtx[2] -= (covMatrix[i][2] - covMatrix[i-1][2]) * lint;
            }
            /* draw image */
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            /* make interpolate matrix */
            memcpy(mtx, covMatrixM[i], sizeof(covMatrixM[i]));
            mtx[0] -= (covMatrixM[i][0] - covMatrixM[i-1][0]) * lint;
            mtx[6] -= (covMatrixM[i][6] - covMatrixM[i-1][6]) * lint;
            if (i == center) {
                mtx[1] -= (covMatrixM[i][1] - covMatrixM[i-1][1]) * lint;
                mtx[2] -= (covMatrixM[i][2] - covMatrixM[i-1][2]) * lint;
            }
            /* draw image */
            vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
        }
        /* right side images */
        for (i = COVER_NUM_RENDER - 1; i >= center; i--) {
            if (i == center && current_offset >= MID_OFFSET) continue;
            /* make interpolate matrix */
            memcpy(mtx, covMatrix[i], sizeof(covMatrix[i]));
            mtx[0] -= (covMatrix[i][0] - covMatrix[i-1][0]) * lint;
            mtx[6] -= (covMatrix[i][6] - covMatrix[i-1][6]) * lint;
            if (i <= center + 1) {
                mtx[1] -= (covMatrix[i][1] - covMatrix[i-1][1]) * lint;
                mtx[2] -= (covMatrix[i][2] - covMatrix[i-1][2]) * lint;
                if (i != center) {
                    mtx[4] -= (covMatrix[i][4] - covMatrix[i-1][4]) * lint;
                    mtx[7] -= (covMatrix[i][7] - covMatrix[i-1][7]) * lint;
                }
            }
            /* draw image */
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            /* make interpolate matrix */
            memcpy(mtx, covMatrixM[i], sizeof(covMatrixM[i]));
            mtx[0] -= (covMatrixM[i][0] - covMatrixM[i-1][0]) * lint;
            mtx[6] -= (covMatrixM[i][6] - covMatrixM[i-1][6]) * lint;
            if (i <= center + 1) {
                mtx[1] -= (covMatrixM[i][1] - covMatrixM[i-1][1]) * lint;
                mtx[2] -= (covMatrixM[i][2] - covMatrixM[i-1][2]) * lint;
                if (i != center) {
                    mtx[4] -= (covMatrixM[i][4] - covMatrixM[i-1][4]) * lint;
                    mtx[7] -= (covMatrixM[i][7] - covMatrixM[i-1][7]) * lint;
                }
            }
            /* draw image */
            vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
            vgLoadMatrix(mtx);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
        }
    } else {  /*** mode 0, 3 or 4 : center scaling ***/
        /* left side images */
        for (i = 1; i < center; i++) {
            vgLoadMatrix(covMatrix[i]);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
            vgLoadMatrix(covMatrixM[i]);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
        }
        /* right side images */
        for (i = COVER_NUM_RENDER - 2; i > center; i--) {
            vgLoadMatrix(covMatrix[i]);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
            vgLoadMatrix(covMatrixM[i]);
            texid = (baseid + i) % COVER_NUMBER;
            vgDrawImage(texobj[texid]);
            vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
        }
        /* center image */
        memcpy(mtx, covMatrix[i], sizeof(covMatrix[i]));
        mtx[0] += (centerMatrix[0] - covMatrix[center][0]) * lint;
        mtx[4] += (centerMatrix[4] - covMatrix[center][4]) * lint;
        mtx[6] += (centerMatrix[6] - covMatrix[center][6]) * lint;
        mtx[7] += (centerMatrix[7] - covMatrix[center][7]) * lint;
        vgLoadMatrix(mtx);
        texid = (baseid + center) % COVER_NUMBER;
        vgDrawImage(texobj[texid]);
        /* center image */
        memcpy(mtx, covMatrixM[center], sizeof(covMatrixM[i]));
        mtx[0] += (centerMatrixM[0] - covMatrixM[center][0]) * lint;
        mtx[4] += (centerMatrixM[4] - covMatrixM[center][4]) * lint;
        mtx[6] += (centerMatrixM[6] - covMatrixM[center][6]) * lint;
        mtx[7] += (centerMatrixM[7] - covMatrixM[center][7]) * lint;
        vgSeti(VG_COLOR_TRANSFORM, VG_TRUE);
        vgLoadMatrix(mtx);
        texid = (baseid + center) % COVER_NUMBER;
        vgDrawImage(texobj[texid]);
        vgSeti(VG_COLOR_TRANSFORM, VG_FALSE);
    }

    /* バッファスワップ */
    GfxSwapBuffer();
}


/**
 * Get new demo mode.
 */
static int newmode(void)
{
    static int cnt = 5;
    cnt--;
    if (cnt == 0) {
        cnt = 1 + (rand() & 0xFF) % 5;
        if (demo_mode == 4)
            return 5;
        else if (demo_mode == 0)
            return (rand() & 0xFF) % 4;
        else
            return (rand() & 0xFF) % 3;
    }
    return demo_mode;
}



void MultifuncMain(void)
{
    /***** Coverflow main *****/
        demo_mode = 0;
        baseid = 0;
        current_offset = 0;

        /* initialize */
        initApp();

        /* main loop */
        while(1) {
            /* draw scene */
            repaint();

            /* demo mode & position control */
            if (demo_mode == 0) {
                demo_mode = newmode();
            } else if (demo_mode == 1) {
                current_offset += OFFSET;
                if (current_offset >= MAX_OFFSET) {
                    baseid -= 1;
                    if (baseid == -1) {
                        baseid = COVER_NUMBER - 1;
                    }
                    current_offset = 0;
                    demo_mode = newmode();
                }
            } else if (demo_mode == 2) {
                current_offset += OFFSET;
                if (current_offset >= MAX_OFFSET) {
                    baseid += 1;
                    if (baseid == COVER_NUMBER) {
                        baseid = 0;
                    }
                    current_offset = 0;
                    demo_mode = newmode();
                }
            } else if (demo_mode == 3) {
                current_offset += OFFSET_S;
                if (current_offset >= MAX_OFFSET) {
                    demo_mode = 4;
                    current_offset = MAX_OFFSET;
                }
            } else if (demo_mode == 4) {
                demo_mode = newmode();
            } else if (demo_mode == 5) {
                current_offset -= OFFSET_S;
                if (current_offset <= 0) {
                    current_offset = 0;
                    demo_mode = 0;
                }
            }
        }

        /* termination */
//        killApp();
}

