/*
 * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * This file was originally generated by JSLC
 * and then hand edited for performance.
 */

#include <jni.h>
#include <math.h>
#include "SSEUtils.h"
#include "com_sun_scenario_effect_impl_sw_sse_SSELinearConvolveShadowPeer.h"

#define cmin 1.0f
#define cmax (255.0f - 1.0f/32.0f)

JNIEXPORT void JNICALL
Java_com_sun_scenario_effect_impl_sw_sse_SSELinearConvolveShadowPeer_filterVector
    (JNIEnv *env, jclass klass,
     jintArray dstPixels_arr, jint dstw, jint dsth, jint dstscan,
     jintArray srcPixels_arr, jint srcw, jint srch, jint srcscan,
     jfloatArray weights_arr, jint count,
     jfloat srcx0, jfloat srcy0,
     jfloat offsetx, jfloat offsety,
     jfloat deltax, jfloat deltay,
     jfloatArray shadowColor_arr,
     jfloat dxcol, jfloat dycol, jfloat dxrow, jfloat dyrow)
{
    if (count > 128) return;
    jfloat weights[128];
    env->GetFloatArrayRegion(weights_arr, 0, count, weights);
    jfloat shadowColor[4];
    env->GetFloatArrayRegion(shadowColor_arr, 0, 4, shadowColor);

    jint *srcPixels = (jint *)env->GetPrimitiveArrayCritical(srcPixels_arr, 0);
    if (srcPixels == NULL) return;
    jint *dstPixels = (jint *)env->GetPrimitiveArrayCritical(dstPixels_arr, 0);
    if (dstPixels == NULL) {
        env->ReleasePrimitiveArrayCritical(srcPixels_arr, srcPixels, JNI_ABORT);
        return;
    }

    jint dstrow = 0;
    // srcxy0 point at UL corner, shift them to center of 1st dest pixel:
    srcx0 += (dxrow + dxcol) * 0.5f;
    srcy0 += (dyrow + dycol) * 0.5f;
    for (jint dy = 0; dy < dsth; dy++) {
        jfloat srcx = srcx0;
        jfloat srcy = srcy0;
        for (jint dx = 0; dx < dstw; dx++) {
            jfloat sum = 0.0f;
            jfloat sampx = srcx + offsetx;
            jfloat sampy = srcy + offsety;
            for (jint i = 0; i < count; ++i) {
                if (sampx >= 0 && sampy >= 0) {
                    jint ix = (jint) sampx;
                    jint iy = (jint) sampy;
                    if (ix < srcw && iy < srch) {
                        // TODO: linear interp the alphas... (RT-27407)
                        jint argb = srcPixels[iy * srcscan + ix];
                        sum += ((jfloat) ((argb >> 24) & 0xff)) * weights[i];
                    }
                }
                sampx += deltax;
                sampy += deltay;
            }
            sum = (sum < 0.0f) ? 0.0f : ((sum > 255.0f) ? 255.0f : sum);
            dstPixels[dstrow + dx] = ((jint) (shadowColor[0] * sum) << 16) |
                                     ((jint) (shadowColor[1] * sum) <<  8) |
                                     ((jint) (shadowColor[2] * sum) <<  0) |
                                     ((jint) (shadowColor[3] * sum) << 24);
            srcx += dxcol;
            srcy += dycol;
        }
        srcx0 += dxrow;
        srcy0 += dyrow;
        dstrow += dstscan;
    }

    env->ReleasePrimitiveArrayCritical(dstPixels_arr, dstPixels, 0);
    env->ReleasePrimitiveArrayCritical(srcPixels_arr, srcPixels, JNI_ABORT);
}

/*
 * In the nomenclature of the argument list for this method, "row" refers
 * to the coordinate which increments once for each new stream of single
 * axis data that we are blurring in a single pass.  And "col" refers to
 * the other coordinate that increments along the row.
 * Rows are horizontal in the first pass and vertical in the second pass.
 * Cols are vice versa.
 */
JNIEXPORT void JNICALL
Java_com_sun_scenario_effect_impl_sw_sse_SSELinearConvolveShadowPeer_filterHV
    (JNIEnv *env, jclass klass,
     jintArray dstPixels_arr, jint dstcols, jint dstrows, jint dcolinc, jint drowinc,
     jintArray srcPixels_arr, jint srccols, jint srcrows, jint scolinc, jint srowinc,
     jfloatArray kvals_arr, jfloatArray shadowColor_arr)
{
    jint kernelSize = env->GetArrayLength(kvals_arr) / 2;
    if (kernelSize > 128) return;
    jfloat kvals[256];
    env->GetFloatArrayRegion(kvals_arr, 0, kernelSize * 2, kvals);
    jfloat shadowColor[4];
    env->GetFloatArrayRegion(shadowColor_arr, 0, 4, shadowColor);
    jint shadowRGBs[256];
    for (jint i = 0; i < 256; i++) {
        shadowRGBs[i] = ((int) (shadowColor[0] * i) << 16) |
                        ((int) (shadowColor[1] * i) <<  8) |
                        ((int) (shadowColor[2] * i) <<  0) |
                        ((int) (shadowColor[3] * i) << 24);
    }

    jint *srcPixels = (jint *)env->GetPrimitiveArrayCritical(srcPixels_arr, 0);
    if (srcPixels == NULL) return;
    jint *dstPixels = (jint *)env->GetPrimitiveArrayCritical(dstPixels_arr, 0);
    if (dstPixels == NULL) {
        env->ReleasePrimitiveArrayCritical(srcPixels_arr, srcPixels, JNI_ABORT);
        return;
    }

    // avals stores the alpha values from the surrounding K pixels
    // from x-r to x+r
    jfloat avals[128];
    jint dstrow = 0;
    jint srcrow = 0;
    for (jint r = 0; r < dstrows; r++) {
        jint dstoff = dstrow;
        jint srcoff = srcrow;
        // Must clear out the array at the start of every line
        // Might be able to rely on the fact that the previous line must
        // have run out of data towards the end of the scan line, though.
        for (jint i = 0; i < kernelSize; i++) {
            avals[i] = 0.0f;
        }
        jint koff = kernelSize;
        for (jint c = 0; c < dstcols; c++) {
            // Load the data for this x location into the array.
            jint rgb = (c < srccols) ? srcPixels[srcoff] : 0;
            avals[kernelSize - koff] = (jfloat) ((rgb >> 24) & 0xff);
            // Bump the koff to the next spot to align the coefficients.
            if (--koff <= 0) {
                koff += kernelSize;
            }
            jfloat sum = -0.5f;
            for (jint i = 0; i < kernelSize; i++) {
                sum += avals[i] * kvals[koff + i];
            }
            dstPixels[dstoff] =
                ((sum < 0.0f) ? 0
                 : ((sum >= 254.0f) ? shadowRGBs[255]
                    : shadowRGBs[((jint) sum) + 1]));
            dstoff += dcolinc;
            srcoff += scolinc;
        }
        dstrow += drowinc;
        srcrow += srowinc;
    }

    env->ReleasePrimitiveArrayCritical(dstPixels_arr, dstPixels, 0);
    env->ReleasePrimitiveArrayCritical(srcPixels_arr, srcPixels, JNI_ABORT);
}
