/*===========================================================================*
 * subsample.c                                   *
 *                                       *
 *  Procedures concerned with subsampling                    *
 *                                       *
 * EXPORTED PROCEDURES:                              *
 *  LumMotionErrorA                              *
 *  LumMotionErrorB                              *
 *  LumMotionErrorC                              *
 *  LumMotionErrorD                              *
 *                                       *
 *===========================================================================*/

/*
 * Copyright (c) 1995 The Regents of the University of California.
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose, without fee, and without written agreement is
 * hereby granted, provided that the above copyright notice and the following
 * two paragraphs appear in all copies of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
 * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

/*==============*
 * HEADER FILES *
 *==============*/

#include "pm_c_util.h"
#include "all.h"
#include "mtypes.h"
#include "frames.h"
#include "bitio.h"
#include "prototypes.h"

#include "subsample.h"


static void
computePrevFyFx(MpegFrame * const prevFrame,
                int         const by,
                int         const bx,
                vector      const m,
                uint8 ***   const prevP,
                int *       const fyP,
                int *       const fxP) {

    boolean const xHalf = (ABS(m.x) % 2 == 1);
    boolean const yHalf = (ABS(m.y) % 2 == 1);

    MotionToFrameCoord(by, bx, m.y/2, m.x/2, fyP, fxP);

    if (xHalf) {
        if (m.x < 0)
            --*fxP;

        if (yHalf) {
            if (m.y < 0)
                --*fyP;
        
            *prevP = prevFrame->halfBoth;
        } else
            *prevP = prevFrame->halfX;
    } else if (yHalf) {
        if (m.y < 0)
            --*fyP;
        
        *prevP = prevFrame->halfY;
    } else
        *prevP = prevFrame->ref_y;
}



static int32
evenColDiff(const uint8 * const macross,
            const int32 * const currentRow) {

    return 0
        + ABS(macross[ 0] - currentRow[ 0])
        + ABS(macross[ 2] - currentRow[ 2])
        + ABS(macross[ 4] - currentRow[ 4])
        + ABS(macross[ 6] - currentRow[ 6])
        + ABS(macross[ 8] - currentRow[ 8])
        + ABS(macross[10] - currentRow[10])
        + ABS(macross[12] - currentRow[12])
        + ABS(macross[14] - currentRow[14]);
}



static int32
oddColDiff(const uint8 * const macross,
           const int32 * const currentRow) {

    return 0
        + ABS(macross[ 1] - currentRow[ 1])
        + ABS(macross[ 3] - currentRow[ 3])
        + ABS(macross[ 5] - currentRow[ 5])
        + ABS(macross[ 7] - currentRow[ 7])
        + ABS(macross[ 9] - currentRow[ 9])
        + ABS(macross[11] - currentRow[11])
        + ABS(macross[13] - currentRow[13])
        + ABS(macross[15] - currentRow[15]);
}



/*===========================================================================*
 *
 * LumMotionErrorA
 *
 *  compute the motion error for the A subsampling pattern
 *
 * RETURNS: the error, or some number greater if it is worse
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
int32
LumMotionErrorA(const LumBlock * const currentBlockP,
                MpegFrame *      const prevFrame,
                int              const by,
                int              const bx,
                vector           const m,
                int32            const bestSoFar) {

    int32 diff; /* max value of diff is 255*256 = 65280 */
    uint8 ** prev;
    int fy, fx;
    unsigned int rowNumber;

    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);

    diff = 0;  /* initial value */

    for (rowNumber = 0; rowNumber < 16; rowNumber +=2) {
        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
        const int32 * const currentRow = currentBlockP->l[rowNumber];

        diff += evenColDiff(macross, currentRow);

        if (diff > bestSoFar)
            return diff;
    }
    return diff;
}



/*===========================================================================*
 *
 * LumMotionErrorB
 *
 *  compute the motion error for the B subsampling pattern
 *
 * RETURNS: the error, or some number greater if it is worse
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
int32
LumMotionErrorB(const LumBlock * const currentBlockP,
                MpegFrame *      const prevFrame,
                int              const by,
                int              const bx,
                vector           const m,
                int32            const bestSoFar) {

    int32 diff;  /* max value of diff is 255*256 = 65280 */
    uint8 **prev;
    int fy, fx;
    unsigned int rowNumber;

    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);

    diff = 0;  /* initial value */
    
    for (rowNumber = 0; rowNumber < 16; rowNumber +=2) {
        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
        const int32 * const currentRow = currentBlockP->l[rowNumber];

        diff += oddColDiff(macross, currentRow);

        if (diff > bestSoFar)
            return diff;
    }
    return diff;
}


/*===========================================================================*
 *
 * LumMotionErrorC
 *
 *  compute the motion error for the C subsampling pattern
 *
 * RETURNS: the error, or some number greater if it is worse
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
int32
LumMotionErrorC(const LumBlock * const currentBlockP,
                MpegFrame *      const prevFrame,
                int              const by,
                int              const bx,
                vector           const m,
                int32            const bestSoFar) {

    int32 diff;        /* max value of diff is 255*256 = 65280 */
    uint8 **prev;
    int fy, fx;
    unsigned int rowNumber;

    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);

    diff = 0;  /* initial value */
    
    for (rowNumber = 1; rowNumber < 16; rowNumber +=2) {
        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
        const int32 * const currentRow = currentBlockP->l[rowNumber];

        diff += evenColDiff(macross, currentRow);

        if (diff > bestSoFar)
            return diff;
    }
    return diff;
}


/*===========================================================================*
 *
 * LumMotionErrorD
 *
 *  compute the motion error for the D subsampling pattern
 *
 * RETURNS: the error, or some number greater if it is worse
 *
 * SIDE EFFECTS:    none
 *
 *===========================================================================*/
int32
LumMotionErrorD(const LumBlock * const currentBlockP,
                MpegFrame *      const prevFrame,
                int              const by,
                int              const bx,
                vector           const m,
                int32            const bestSoFar) {

    int32 diff;     /* max value of diff is 255*256 = 65280 */
    uint8 ** prev;
    int fy, fx;
    unsigned int rowNumber;

    computePrevFyFx(prevFrame, by, bx, m, &prev, &fy, &fx);

    diff = 0;  /* initial value */

    for (rowNumber = 1; rowNumber < 16; rowNumber +=2) {
        uint8 *       const macross    = &(prev[fy + rowNumber][fx]);
        const int32 * const currentRow = currentBlockP->l[rowNumber];

        diff += oddColDiff(macross, currentRow);

        if (diff > bestSoFar)
            return diff;
    }
    return diff;
}
