/**
 *  @file   pix32_resize.h
 *  @brief  バイリニアサンプリングで拡大縮小.
 *  @author tenk* (Masashi Kitamura)
 *  @note
 *  -   縮小は平均画素法を併用.
 *      x,yサイズを整数で割れるように、一旦(計算上)、拡大する。
 *      拡大は整数倍でなくてよい、あたりはミソかも。
 */

#ifndef PIX32_RESIZE_H
#define PIX32_RESIZE_H

#if !(defined __cplusplus) && !(defined inline) && (defined _WIN32)
#define inline __inline
#endif


#ifndef PIX32_ARGB
/// 8bit値のa,r,g,bを繋げて 32bit の色値にする.
//#define PIX32_ARGB(a,r,g,b)       ((((unsigned char)(a))<<24)|(((unsigned char)(r))<<16)|(((unsigned char)(g))<<8)|((unsigned char)(b)))
#define PIX32_ARGB(a,r,g,b)     ((((a))<<24)|(((r))<<16)|(((g))<<8)|((b)))      // a,r,g,bが予め0..255になっていること前提にする.
#endif

#ifndef PIX32_GET_B
/// argb値中の blueの値を取得.
#define PIX32_GET_B(argb)       ((unsigned char)(argb))
#endif

#ifndef PIX32_GET_G
/// argb値中の greenの値を取得.
#define PIX32_GET_G(argb)       ((unsigned char)((argb)>>8))
#endif

#ifndef PIX32_GET_R
/// argb値中の greenの値を取得.
#define PIX32_GET_R(argb)       ((unsigned char)((argb)>>16))
#endif

#ifndef PIX32_GET_A
/// argb値中の alphaの値を取得.
#define PIX32_GET_A(argb)       (((unsigned)(argb))>>24)
#endif



/** 整数割りで、縮小.
 */
static inline void pix32_reducedsizeI(unsigned *dst, unsigned dstW, unsigned dstH, const unsigned *src, unsigned srcW, unsigned srcH)
{
    unsigned    mw, mh;
    unsigned    msz;
    unsigned    dx, dy, sxx, syy;
    if (srcW == 0 || srcH == 0 || dstW == 0 || dstH == 0)
        return;
    mw = srcW / dstW;
    mh = srcH / dstH;
    if (mw == 0 || mh == 0)
        return;
    //printf("[%d*%d->(%d,%d)->%d*%d]\n", srcW,srcH,mw,mh,dstW,dstH);
    msz = mw * mh;
    syy = 0;
    for (dy = 0; dy < dstH; ++dy) {
        sxx = 0;
        for (dx = 0; dx < dstW; ++dx) {
            double fa=0, fr=0, fg=0, fb=0;
            unsigned    x, y;
            for (y = syy; y < syy+mh; ++y) {
                for (x = sxx; x < sxx+mw; ++x) {
                    unsigned c = src[y*srcW + x];
                    fa += PIX32_GET_A(c);
                    fr += PIX32_GET_R(c);
                    fg += PIX32_GET_G(c);
                    fb += PIX32_GET_B(c);
                }
            }
            {
                unsigned ia,ir,ig,ib;
                ia = (unsigned)(fa / msz); if (ia > 254) ia = 255;  // 誤差で255になるべきものが254になるようなんで、強制して逃げ.
                ir = (unsigned)(fr / msz); if (ir > 255) ir = 255;
                ig = (unsigned)(fg / msz); if (ig > 255) ig = 255;
                ib = (unsigned)(fb / msz); if (ib > 255) ib = 255;
                dst[dy * dstW + dx] = PIX32_ARGB(ia,ir,ig,ib);
            }
            sxx += mw;
        }
        syy += mh;
    }
}



/** バイリニアサンプリングで拡大縮小.
 */
static inline void pix32_resize(unsigned *dst, unsigned dstW, unsigned dstH, const unsigned *src, unsigned srcW, unsigned srcH)
{
    double rw, rh;

    if (srcW == 0 || srcH == 0 || dstW == 0 || dstH == 0)
        return;

    rw = (double)srcW / dstW;
    rh = (double)srcH / dstH;

    if ((rw >= 1.0) | (rh >= 1.0)) {    // 縮小が発生する場合.
        if ((srcW % dstW) == 0 && (srcH % dstH) == 0) {
            // 整数割りですむ場合は、それ専用の処理にして、バイリニアによる滲み(誤差)を減らす.
            pix32_reducedsizeI(dst, dstW, dstH, src, srcW, srcH);
            return;
        }

        {
            // 縦横縮小.
            // 縮小が整数倍で行えるように、計算途中は一旦バイリニアサンプリングで拡大.
            unsigned    msz;
            unsigned    dmw,dmh;
            unsigned    dx, dy;

            unsigned    mw  = (unsigned)rw + 1;     // 何分の一にするか.
            unsigned    mh  = (unsigned)rh + 1;

          #if 1
            mw += (mw < 2);
            mh += (mh < 2);
          #endif

            dmw = dstW * mw;        // 出力幅の整数倍かつ元サイズよりも大きい値.
            dmh = dstH * mh;
            rw  = (double)srcW / dmw;
            rh  = (double)srcH / dmh;

            msz = mw   * mh;

            for (dy = 0; dy < dstH; ++dy) {
                for (dx = 0; dx < dstW; ++dx) {
                    double fa=0, fr=0, fg=0, fb=0;
                    unsigned dmx, dmy ;
                    // 仮想的に一旦tw*thサイズの画像に拡大して,
                    // その中のmw*mh ピクセルのPIX32_ARGBの平均を出力の1ピクセルにする.
                    for (dmy = dy*mh; dmy < (dy+1)*mh; ++dmy) {
                        for (dmx = dx*mw; dmx < (dx+1)*mw; ++dmx) {
                            double  sx = dmx * rw - 0.5;
                            double  sy = dmy * rh - 0.5;
                            if (sx < 0)
                                sx = 0;
                            if (sy < 0)
                                sy = 0;
                            {
                                unsigned    x0 = (unsigned)sx;
                                unsigned    y0 = (unsigned)sy;
                                double      kw = sx - x0;
                                double      kh = sy - y0;
                                double      k0 = (1.0 - kw) * (1.0 - kh);
                                double      k1 = kw * (1.0 - kh);
                                double      k2 = (1.0 - kw) * kh;
                                double      k3 = kw * kh;
                                unsigned    ccX0a1lW = (x0+1 < srcW);
                                unsigned    ccY0a1lH = (y0+1 < srcH);
                                unsigned    c0  = src[y0 * srcW + x0];
                                unsigned    c1  = ccX0a1lW ? src[y0 * srcW + x0 + 1] : c0;
                                unsigned    c2  = ccY0a1lH ? src[(y0+1) * srcW + x0] : c0;
                                unsigned    c3  =(ccX0a1lW & ccY0a1lH) ? src[(y0+1) * srcW + (x0 + 1)]
                                                : ccX0a1lW             ? c1
                                                : ccY0a1lH             ? c2
                                                :                        c0;
                                fa += (PIX32_GET_A(c0)*k0 + PIX32_GET_A(c1)*k1 + PIX32_GET_A(c2)*k2 + PIX32_GET_A(c3)*k3);
                                fr += (PIX32_GET_R(c0)*k0 + PIX32_GET_R(c1)*k1 + PIX32_GET_R(c2)*k2 + PIX32_GET_R(c3)*k3);
                                fg += (PIX32_GET_G(c0)*k0 + PIX32_GET_G(c1)*k1 + PIX32_GET_G(c2)*k2 + PIX32_GET_G(c3)*k3);
                                fb += (PIX32_GET_B(c0)*k0 + PIX32_GET_B(c1)*k1 + PIX32_GET_B(c2)*k2 + PIX32_GET_B(c3)*k3);
                            }
                        }
                    }
                    {
                        unsigned ia,ir,ig,ib;
                        ia = (unsigned)(fa / msz); if (ia > 254) ia = 255;  // 誤差で255になるべきものが254になるようなんで、強制して逃げ.
                        ir = (unsigned)(fr / msz); if (ir > 255) ir = 255;
                        ig = (unsigned)(fg / msz); if (ig > 255) ig = 255;
                        ib = (unsigned)(fb / msz); if (ib > 255) ib = 255;
                        dst[dy * dstW + dx] = PIX32_ARGB(ia,ir,ig,ib);
                    }
                }
            }
        }
    }
  #if 1
    else {  //拡大処理 ※縮小側の処理で、拡大も問題ないが、分かりやすさと速度?のために、このルーチンは残す.
        unsigned dx,dy;
        for (dy = 0; dy < dstH; dy++) {
            for (dx = 0; dx < dstW; dx++) {
                double  sx = dx * rw - 0.5;
                double  sy = dy * rh - 0.5;
                if (sx < 0)
                    sx = 0;
                if (sy < 0)
                    sy = 0;
                {
                    unsigned    x0 = (unsigned)(sx-0.0);
                    unsigned    y0 = (unsigned)(sy-0.0);
                    double      kw = sx - x0;
                    double      kh = sy - y0;
                    double      k0 = (1.0 - kw) * (1.0 - kh);
                    double      k1 = kw * (1.0 - kh);
                    double      k2 = (1.0 - kw) * kh;
                    double      k3 = kw * kh;
                    unsigned    ccX0a1lW = x0+1 < srcW;
                    unsigned    ccY0a1lH = y0+1 < srcH;
                    unsigned    c0 = src[y0 * srcW + x0];
                    unsigned    c1 = ccX0a1lW ? src[y0 * srcW + x0 + 1] : c0;
                    unsigned    c2 = ccY0a1lH ? src[(y0+1) * srcW + x0] : c0;
                    unsigned    c3 = (ccX0a1lW & ccY0a1lH) ? src[(y0+1) * srcW + x0 + 1]
                                   : ccX0a1lW ?              c1
                                   : ccY0a1lH ?              c2
                                   :                         c0;
                    unsigned    ir = (unsigned)( (PIX32_GET_R(c0)*k0 + PIX32_GET_R(c1)*k1 + PIX32_GET_R(c2)*k2 + PIX32_GET_R(c3)*k3) );
                    unsigned    ig = (unsigned)( (PIX32_GET_G(c0)*k0 + PIX32_GET_G(c1)*k1 + PIX32_GET_G(c2)*k2 + PIX32_GET_G(c3)*k3) );
                    unsigned    ib = (unsigned)( (PIX32_GET_B(c0)*k0 + PIX32_GET_B(c1)*k1 + PIX32_GET_B(c2)*k2 + PIX32_GET_B(c3)*k3) );
                    unsigned    ia = (unsigned)( (PIX32_GET_A(c0)*k0 + PIX32_GET_A(c1)*k1 + PIX32_GET_A(c2)*k2 + PIX32_GET_A(c3)*k3) );

                    if (ia > 254) ia = 255; // 誤差で255になるべきものが254になるようなんで、強制して逃げ.
                    if (ir > 255) ir = 255;
                    if (ig > 255) ig = 255;
                    if (ib > 255) ib = 255;

                    dst[dy * dstW + dx] = PIX32_ARGB(ia,ir,ig,ib);
                }
            }
        }
    }
  #endif
}


#endif