/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* gos720 src/gos/2d/XTOP_R7/app/rendercheck/t_gradient.c 1.1             */
/*                                                                        */
/* Licensed Materials - Property of IBM                                   */
/*                                                                        */
/* Restricted Materials of IBM                                            */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 2006                   */
/* All Rights Reserved                                                    */
/*                                                                        */
/* US Government Users Restricted Rights - Use, duplication or            */
/* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.      */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
/* SCCSID_BEGIN_TAG                                                    */
#ifndef lint
static char sccsid[] = "@(#)11  1.1  src/gos/2d/XTOP_R7/app/rendercheck/t_gradient.c, xext, gos720 10/27/06 16:21:34";
#endif
/* SCCSID_END_TAG                                                      */
/*
 * $Id$
 *
 * Copyright © 2006 Lars Knoll
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the copyright holders not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  The copyright holders makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdio.h>
#include <assert.h>
#include "rendercheck.h"

typedef struct _stop {
    double x;
    color4d color;
} stop;

static const stop stop_list[][10] = {
    {
        { 0., {0, 0, 1.0, 1.0} },
        { 1., {1.0, 0, 0, 1.0} },
        { -1, {0, 0, 0, 0} }
    },
    {
        { 0., {0, 0, 1.0, 1.0} },
        { .5, {0, 1.0, 0, 1.0} },
        { 1., {1.0, 0, 0, 1.0} },
        { -1, {0, 0, 0, 0} }
    },
    {
        { 0., {0, 0, 1.0, 0} },
        { 1., {1.0, 0, 0, 1.0} },
        { -1, {0, 0, 0, 0} }
    },
    {
        { 0., {0, 0, 1.0, 0} },
        { .5, {0, 1.0, 0, .75} },
        { 1., {1.0, 0, 0, .5} },
        { -1, {0, 0, 0, 0} }
    }
};
const int n_stop_list = sizeof(stop_list)/(10*sizeof(stop));

typedef struct _point {
    double x;
    double y;
} point;

static const point linear_gradient_points[] = {
    { -5, -5 },  { 5, 5 },
    { 0, 0 },  { 10, 10 },
};

typedef struct _pixel {
    int x;
    int y;
} pixel;

static const pixel test_pixels [] = {
    {0, 0},
    {0, 5},
    {5, 0},
    {5, 5},
    {3, 7},
    {7, 3},
    {20, 20},
    {30, 13},
    {39, 39},
    {-1, -1}
};

const int n_linear_gradient_points = sizeof(linear_gradient_points)/(2*sizeof(point));

static void gradientPixel(const stop *stops, double pos, unsigned int spread, color4d *result)
{
    const int PRECISION = 1<<16;
    int ipos = pos * PRECISION - 1;
    int i;
    double frac;

    /* calculate the actual offset. */
    if (ipos < 0 || ipos >= PRECISION) {
        if (spread == RepeatNormal) {
            ipos = ipos % PRECISION;
            ipos = ipos < 0 ? PRECISION + ipos : ipos;

        } else if (spread == RepeatReflect) {
            const int limit = PRECISION * 2 - 1;
            ipos = ipos % limit;
            ipos = ipos < 0 ? limit + ipos : ipos;
            ipos = ipos >= PRECISION ? limit - ipos : ipos;

        } else if (spread == RepeatPad) {
            if (ipos < 0)
                ipos = 0;
            else if (ipos >= PRECISION)
                ipos = PRECISION-1;
        } else { /* RepeatNone */
            result->r = 0;
            result->g = 0;
            result->b = 0;
            result->a = 0;
            return;
        }
    }

    assert(ipos >= 0);
    assert(ipos < PRECISION);

    pos = ipos/(double)PRECISION;

    if (pos <= stops[0].x) {
        *result = stops[0].color;
        return;
    }

    for (i = 0; i < 10; ++i) {
        if (stops[i].x >= pos)
            break;
        if (stops[i].x < 0) {
            *result = stops[i-1].color;
            result->r *= result->a;
            result->g *= result->a;
            result->b *= result->a;
            return;
        }
    }

    frac = (pos - stops[i-1].x)/(stops[i].x - stops[i-1].x);

    result->r = (1.-frac)*stops[i-1].color.r + frac*stops[i].color.r;
    result->g = (1.-frac)*stops[i-1].color.g + frac*stops[i].color.g;
    result->b = (1.-frac)*stops[i-1].color.b + frac*stops[i].color.b;
    result->a = (1.-frac)*stops[i-1].color.a + frac*stops[i].color.a;

    result->r *= result->a;
    result->g *= result->a;
    result->b *= result->a;

    return;
}

static void calculate_linear_gradient_color(int x, int y,
                                            const point *points,
                                            const stop *stops,
                                            color4d *tgradient, int repeat)
{
    double dx, dy, l, xrel, yrel, pos;
    dx = points[1].x - points[0].x;
    dy = points[1].y - points[0].y;
    l = dx*dx + dy*dy;

    xrel = x - points[0].x;
    yrel = y - points[0].y;

    pos = (dx*xrel + dy*yrel)/l;
    gradientPixel(stops, pos, repeat, tgradient);
}



Bool linear_gradient_test(Display *dpy, picture_info *win,
                          picture_info *dst, int op, picture_info *dst_color)
{
    color4d expected, tested, tdst, tgradient;
    int i, s, p, repeat;
    Picture gradient;
    char testname[40];
    Bool success = True;

    for (s = 0; s < n_stop_list; ++s) {
        for (p = 0; p < n_linear_gradient_points; p += 2) {
            XLinearGradient g;
            XFixed stops[10];
            XRenderColor colors[10];
            const stop *stps = &stop_list[s][0];
            g.p1.x = XDoubleToFixed(linear_gradient_points[p].x);
            g.p1.y = XDoubleToFixed(linear_gradient_points[p].y);
            g.p2.x = XDoubleToFixed(linear_gradient_points[p+1].x);
            g.p2.y = XDoubleToFixed(linear_gradient_points[p+1].y);
            for (i = 0; i < 10; ++i) {
                if (stps[i].x < 0)
                    break;
                stops[i] = XDoubleToFixed(stps[i].x);
                colors[i].red = stps[i].color.r*65535;
                colors[i].green = stps[i].color.g*65535;
                colors[i].blue = stps[i].color.b*65535;
                colors[i].alpha = stps[i].color.a*65535;
            }
            gradient = XRenderCreateLinearGradient(dpy, &g, stops, colors, i);

            for (repeat = 1; repeat < 4; ++repeat) {
                const pixel *pix;
                XRenderPictureAttributes pa;
		pa.repeat = repeat;
		XRenderChangePicture(dpy, gradient, CPRepeat, &pa);

                XRenderComposite(dpy, PictOpSrc, dst_color->pict, 0, dst->pict, 0, 0,
                                 0, 0, 0, 0, win_width, win_height);
                XRenderComposite(dpy, ops[op].op, gradient, 0,
                                 dst->pict, 0, 0, 0, 0, 0, 0, win_width, win_height);

                /* Copy the output to the window, so the user sees something visual. */
                if (win != dst)
                    XRenderComposite(dpy, PictOpSrc, dst->pict, 0, win->pict, 0, 0,
                                     0, 0, 0, 0, win_width, win_height);

                pix = test_pixels;
                while (pix->x >= 0) {

                    get_pixel(dpy, dst, pix->x, pix->y, &tested);

                    calculate_linear_gradient_color(pix->x, pix->y, &linear_gradient_points[p],
                                                    stps, &tgradient, repeat);

                    tdst = dst_color->color;
                    color_correct(dst, &tdst);
                    do_composite(ops[op].op, &tgradient, 0, &tdst,
                                 &expected, False);
                    color_correct(dst, &expected);

                    snprintf(testname, 40, "%s linear gradient", ops[op].name);
                    if (!eval_diff(testname, &expected, &tested, 0, 0, is_verbose)) {
                        printf("gradient: %d stops: %d repeat: %d pos: %d/%d\n"
                               "src color: %.2f %.2f %.2f %.2f\n"
                               "dst color: %.2f %.2f %.2f %.2f\n",
                               p/2, s,
                               repeat, pix->x, pix->y,
                               tgradient.r, tgradient.g,
                               tgradient.b, tgradient.a,
                               dst_color->color.r, dst_color->color.g,
                               dst_color->color.b, dst_color->color.a);
                        success = FALSE;
                    } else if (is_verbose) {
                        printf("src: %d/%d, dst: %s\n", s, p, dst->name);
                    }
                    ++pix;
                }
            }
            XRenderFreePicture(dpy, gradient);
        }
    }
    return success;
}