/* Portions Copyright (C) 2001 artofcode LLC.
   Portions Copyright (C) 1996, 2001 Artifex Software Inc.
   Portions Copyright (C) 1988, 2000 Aladdin Enterprises.
   This software is based in part on the work of the Independent JPEG Group.
   All Rights Reserved.

   This software is distributed under license and may not be copied, modified
   or distributed except as expressly authorized under the terms of that
   license.  Refer to licensing information at http://www.artifex.com/ or
   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
   San Rafael, CA  94903, (415)492-9861, for further information. */
/*$Id: pcmtx3.c,v 1.5 2001/03/23 16:01:13 stefan Exp $ */

/* pcmtx3.c - 3 x 3 matrix utilities for PCL device independent color spaces */
#include "string_.h"
#include "math_.h"
#include "gx.h"
#include "gstypes.h"
#include "pcommand.h"
#include "pcmtx3.h"


/*
 * Calculate the co-factor for the (i, j) component of a 3 x 3 matrix,
 * including the sign. Note that i and j range from 0 to 2.
 *
 * The cofactor of the (i, j) entry in an n-dimensional matrix is the
 * determinant of the (n - 1) dimensional matrix formed by dropping the
 * ith row and jth column of the given matrix, multiplied by -1 if
 * (i + j) is odd.
 */
  private floatp
calc_cofactor(
    int                 i,
    int                 j,
    const pcl_mtx3_t *  pmtx
)
{
    int                 i1 = (i == 0 ? 1 : 0);
    int                 i2 = (i == 2 ? 1 : 2);
    int                 j1 = (j == 0 ? 1 : 0);
    int                 j2 = (j == 2 ? 1 : 2);
    floatp              cf = pmtx->a[3 * i1 + j1] * pmtx->a[3 * i2 + j2]
                             - pmtx->a[3 * i1 + j2] * pmtx->a[3 * i2 + j1];

    return (((i + j) & 0x1) != 0) ? -cf : cf;
}

/*
 * Form the "cofactor matrix" corresponding to the given matrix.
 *
 * For this routine, pinmtx and poutmtx must be different.
 */
  private void
make_cofactor_mtx(
    const pcl_mtx3_t *  pinmtx,
    pcl_mtx3_t *        poutmtx
)
{
    int                 i;

    for (i = 0; i < 3; i++) {
        int     j;

        for (j = 0; j < 3; j++)
            poutmtx->a[3 * i + j] = calc_cofactor(i, j, pinmtx);
    }
}


/*
 * Add and subtract 3 dimensional vectors. These are not currently needed,
 * but are included for completeness. No inner-product is provided because
 * color spaces do not, in general, have geometric or even metric properties
 * (the CID L*a*b* space is an exception, and then only approximately).
 *
 * Any of the three pointer operands may be the same.
 */
  void
pcl_vec3_add(
    const pcl_vec3_t *  pivec1,
    const pcl_vec3_t *  pivec2,
    pcl_vec3_t *        poutvec
)
{
    poutvec->vc.v1 = pivec1->vc.v1 + pivec2->vc.v1;
    poutvec->vc.v2 = pivec1->vc.v2 + pivec2->vc.v2;
    poutvec->vc.v3 = pivec1->vc.v3 + pivec2->vc.v3;
}

  void
pcl_vec3_sub(
    const pcl_vec3_t *  pivec1,
    const pcl_vec3_t *  pivec2,
    pcl_vec3_t *        poutvec
)
{
    poutvec->vc.v1 = pivec1->vc.v1 - pivec2->vc.v1;
    poutvec->vc.v2 = pivec1->vc.v2 - pivec2->vc.v2;
    poutvec->vc.v3 = pivec1->vc.v3 - pivec2->vc.v3;
}

/*
 * Apply a matrix to a color component vector. Note that vectors are inter-
 * preted as row vectors, and matrices are specified in a row-first order.
 *
 * The code will properly handle the case pinvec == poutvec.
 */
  void
pcl_vec3_xform(
    const pcl_vec3_t *  pinvec,
    pcl_vec3_t *        poutvec,
    const pcl_mtx3_t *  pmtx
)
{
    pcl_vec3_t          tmp_vec;
    int                 i;

    for (i = 0; i < 3; i++)
        tmp_vec.va[i] = pinvec->vc.v1 * pmtx->a[i]
                        + pinvec->vc.v2 * pmtx->a[i + 3]
                        + pinvec->vc.v3 * pmtx->a[i + 6];
    poutvec->vc = tmp_vec.vc;
}

/*
 * Invert a 3 x 3 matrix.
 *
 * This will properly handle the case of pinmtx == poutmtx.
 *
 * Returns 0 on success, e_Range if the matrix provided is singular.
 */
  int
pcl_mtx3_invert(
    const pcl_mtx3_t *  pinmtx,
    pcl_mtx3_t *        poutmtx
)
{
    pcl_mtx3_t          cf_mtx;
    floatp              det;
    int                 i;

    make_cofactor_mtx(pinmtx, &cf_mtx);
    det = pinmtx->c.a11 * cf_mtx.c.a11
          + pinmtx->c.a12 * cf_mtx.c.a12
          + pinmtx->c.a13 * cf_mtx.c.a13;
    if (det == 0.0)
        return e_Range;
    for (i = 0; i < 3; i++) {
        int     j;

        for (j = 0; j < 3; j++)
            poutmtx->a[3 * i + j] = cf_mtx.a[3 * j + i] / det;
    }

    return 0;
}

/*
 * Add, subtract, and multiply two 3 x 3 matrices. These are not currently
 * needed, but are included for completenese.
 *
 * In all cases, any of the three pointers provided may be identical.
 */
  void
pcl_mtx3_add(
    const pcl_mtx3_t *  pinmtx1,
    const pcl_mtx3_t *  pinmtx2,
    pcl_mtx3_t *        poutmtx
)
{
    int                 i;

    for (i = 0; i < 9; i++)
        poutmtx->a[i] = pinmtx1->a[i] + pinmtx2->a[i];
}

  void
pcl_mtx3_sub(
    const pcl_mtx3_t *  pinmtx1,
    const pcl_mtx3_t *  pinmtx2,
    pcl_mtx3_t *        poutmtx
)
{
    int                 i;

    for (i = 0; i < 9; i++)
        poutmtx->a[i] = pinmtx1->a[i] - pinmtx2->a[i];
}

  void
pcl_mtx3_mul(
    const pcl_mtx3_t *  pinmtx1,
    const pcl_mtx3_t *  pinmtx2,
    pcl_mtx3_t *        poutmtx
)
{
    pcl_mtx3_t          tmp_mtx;
    int                 i;

    for (i = 0; i < 3; i++) {
        int     j;

        for (j = 0; j < 3; j++) {
            int     k;
            floatp  val = 0.0;

            for (k = 0; k < 3; k++)
                val += pinmtx1->a[3 * i + k] * pinmtx2->a[3 * k + j];
            tmp_mtx.a[3 * i + j] = val;
        }
    }
    *poutmtx = tmp_mtx;
}

/*
 * Convert a pcl_mtx3_t structure to and from a gs_matrix3 struct. Identity
 * transformations are rare in PCL, so no attempt is made to identify them.
 *
 * Note that the conversion transposes the matrix in interpretation, though
 * physically entries remain in the same order. This is due to the use of
 * a "column vector" interpretation of color components in the graphic library.
 */
  void
pcl_mtx3_convert_to_gs(
    const pcl_mtx3_t *  pinmtx,
    gs_matrix3 *        pgsmtx
)
{
    pgsmtx->cu.u = pinmtx->c.a11;
    pgsmtx->cu.v = pinmtx->c.a12;
    pgsmtx->cu.w = pinmtx->c.a13;
    pgsmtx->cv.u = pinmtx->c.a21;
    pgsmtx->cv.v = pinmtx->c.a22;
    pgsmtx->cv.w = pinmtx->c.a23;
    pgsmtx->cw.u = pinmtx->c.a31;
    pgsmtx->cw.v = pinmtx->c.a32;
    pgsmtx->cw.w = pinmtx->c.a33;
    pgsmtx->is_identity = false;
}

  void
pcl_mtx3_convert_from_gs(
    pcl_mtx3_t *        poutmtx,
    const gs_matrix3 *  pgsmtx
)
{
    if (pgsmtx->is_identity) {
        memset(poutmtx, 0, sizeof(pcl_mtx3_t));
        poutmtx->c.a11 = 1.0;
        poutmtx->c.a22 = 1.0;
        poutmtx->c.a22 = 1.0;
    } else {
        poutmtx->c.a11 = pgsmtx->cu.u;
        poutmtx->c.a12 = pgsmtx->cu.v;
        poutmtx->c.a13 = pgsmtx->cu.w;
        poutmtx->c.a21 = pgsmtx->cv.u;
        poutmtx->c.a22 = pgsmtx->cv.v;
        poutmtx->c.a23 = pgsmtx->cw.v;
        poutmtx->c.a31 = pgsmtx->cw.u;
        poutmtx->c.a32 = pgsmtx->cw.v;
        poutmtx->c.a33 = pgsmtx->cw.w;
    }
}
