/*
 *  Copyright (c) 2010 The VP8 project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license 
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may 
 *  be found in the AUTHORS file in the root of the source tree.
 */


/****************************************************************************
*
*   Module Title :     preproc.c
*
*   Description  :     Simple pre-processor.
*
****************************************************************************/

/****************************************************************************
*  Header Files
****************************************************************************/

#include "memory.h"
#include "preproc7.h"
#include "vpx_mem/vpx_mem.h"

/****************************************************************************
*  Macros
****************************************************************************/
#define FRAMECOUNT 7
#define ROUNDUP32(X) ( ( ( (unsigned long) X ) + 31 )&( 0xFFFFFFE0 ) )

/****************************************************************************
*  Imports
****************************************************************************/
extern void vp8_get_processor_flags(int *mmx_enabled, int *xmm_enabled, int *wmt_enabled);

/****************************************************************************
*  Exported Global Variables
****************************************************************************/
void (*temp_filter)(pre_proc_instance *ppi, unsigned char *s, unsigned char *d, int bytes, int strength);
void temp_filter_mmx
(
    pre_proc_instance *ppi,
    unsigned char *s,
    unsigned char *d,
    int bytes,
    int strength
);
void temp_filter_wmt
(
    pre_proc_instance *ppi,
    unsigned char *s,
    unsigned char *d,
    int bytes,
    int strength
);

/****************************************************************************
 *
 *  ROUTINE       : temp_filter_c
 *
 *  INPUTS        : pre_proc_instance *ppi : Pointer to pre-processor instance.
 *                  unsigned char *s     : Pointer to source frame.
 *                  unsigned char *d     : Pointer to destination frame.
 *                  int bytes            : Number of bytes to filter.
 *                  int strength         : Strength of filter to apply.
 *
 *  OUTPUTS       : None.
 *
 *  RETURNS       : void
 *
 *  FUNCTION      : Performs a closesness adjusted temporarl blur
 *
 *  SPECIAL NOTES : Destination frame can be same as source frame.
 *
 ****************************************************************************/
void temp_filter_c
(
    pre_proc_instance *ppi,
    unsigned char *s,
    unsigned char *d,
    int bytes,
    int strength
)
{
    int byte = 0;
    unsigned char *frameptr = ppi->frame_buffer;

    if (ppi->frame == 0)
    {
        do
        {
            int frame = 0;

            do
            {
                *frameptr = s[byte];
                ++frameptr;
                ++frame;
            }
            while (frame < FRAMECOUNT);

            d[byte] = s[byte];

            ++byte;
        }
        while (byte < bytes);
    }
    else
    {
        int modifier;
        int offset = (ppi->frame % FRAMECOUNT);

        do
        {
            int accumulator = 0;
            int count = 0;
            int frame = 0;

            frameptr[offset] = s[byte];

            do
            {
                int pixel_value = *frameptr;

                modifier   = s[byte];
                modifier  -= pixel_value;
                modifier  *= modifier;
                modifier >>= strength;
                modifier  *= 3;

                if (modifier > 16)
                    modifier = 16;

                modifier = 16 - modifier;

                accumulator += modifier * pixel_value;

                count += modifier;

                frameptr++;

                ++frame;
            }
            while (frame < FRAMECOUNT);

            accumulator += (count >> 1);
            accumulator *= ppi->fixed_divide[count];
            accumulator >>= 16;

            d[byte] = accumulator;

            ++byte;
        }
        while (byte < bytes);
    }

    ++ppi->frame;
}
/****************************************************************************
 *
 *  ROUTINE       : delete_pre_proc
 *
 *  INPUTS        : pre_proc_instance *ppi : Pointer to pre-processor instance.
 *
 *  OUTPUTS       : None.
 *
 *  RETURNS       : void
 *
 *  FUNCTION      : Deletes a pre-processing instance.
 *
 *  SPECIAL NOTES : None.
 *
 ****************************************************************************/
void delete_pre_proc(pre_proc_instance *ppi)
{
    if (ppi->frame_buffer_alloc)
        vpx_free(ppi->frame_buffer_alloc);

    ppi->frame_buffer_alloc = 0;
    ppi->frame_buffer      = 0;

    if (ppi->fixed_divide_alloc)
        vpx_free(ppi->fixed_divide_alloc);

    ppi->fixed_divide_alloc = 0;
    ppi->fixed_divide      = 0;
}

/****************************************************************************
 *
 *  ROUTINE       : init_pre_proc
 *
 *  INPUTS        : pre_proc_instance *ppi : Pointer to pre-processor instance.
 *                  int frame_size        : Number of bytes in one frame.
 *
 *  OUTPUTS       : None.
 *
 *  RETURNS       : int: 1 if successful, 0 if failed.
 *
 *  FUNCTION      : Initializes prepprocessor instance.
 *
 *  SPECIAL NOTES : None.
 *
 ****************************************************************************/
int init_pre_proc7(pre_proc_instance *ppi, int frame_size)
{
    int i;
    int mmx_enabled;
    int xmm_enabled;
    int wmt_enabled;

    vp8_get_processor_flags(&mmx_enabled, &xmm_enabled, &wmt_enabled);

    if (wmt_enabled)
        temp_filter = temp_filter_wmt;
    else if (mmx_enabled)
        temp_filter = temp_filter_mmx;
    else
        temp_filter = temp_filter_c;


    delete_pre_proc(ppi);

    ppi->frame_buffer_alloc = vpx_malloc(32 + frame_size * FRAMECOUNT * sizeof(unsigned char));

    if (!ppi->frame_buffer_alloc)
    {
        delete_pre_proc(ppi);
        return 0;
    }

    ppi->frame_buffer = (unsigned char *) ROUNDUP32(ppi->frame_buffer_alloc);

    ppi->fixed_divide_alloc = vpx_malloc(32 + 255 * sizeof(unsigned int));

    if (!ppi->fixed_divide_alloc)
    {
        delete_pre_proc(ppi);
        return 0;
    }

    ppi->fixed_divide = (unsigned int *) ROUNDUP32(ppi->fixed_divide_alloc);

    for (i = 1; i < 255; i++)
        ppi->fixed_divide[i] = 0x10000 / i;

    return 1;
}
